The Joy of UE5 Is Growing
Dear World,
And Dear Epic,
And Dear Community,
Oh How the Joy of Unreal Engine is Growing!
zap: Look what We can do in UE5, World!
We can create our own static mesh assets in Editor, using BP nodes!
Unreal Engine is a Modelling Tool ! <~~~
We Can Truly Make Our Own Worlds Now
Thank You Epic!
I Hope You Enjoy The Video:
With Me Talking About Unreal Engine Modelling Suite!
The Relevant C++ Code
Thank You Epic Devs!
//~~~ CreateStaticMeshAssetFromDynamicMesh ~~~
#include "GeometryFramework/Public/Components/DynamicMeshComponent.h"
//Runtime
//Engine\Plugins\Runtime\MeshModelingToolset\Source\ModelingComponents\Public\ModelingObjectsCreationAPI.h
#include "ModelingObjectsCreationAPI.h"
//Editor
#if WITH_EDITOR
//"ModelingComponentsEditorOnly" in build.cs ♥
#include "AssetUtils/CreateStaticMeshUtil.h"
#endif
//~~~~ End CreateStaticMeshAssetFromDynamicMesh ~~~
UStaticMesh* UVictoryBPFunctionLibrary::CreateStaticMeshAssetFromDynamicMesh(
FString ContentFolderPath,
UDynamicMeshComponent* DynamicMeshComp,
FString& Status,
FString& NewAssetFilePath,
bool& Success
){
NewAssetFilePath = "";
#if WITH_EDITOR
//Comp?
if(!DynamicMeshComp || !DynamicMeshComp->GetDynamicMesh())
{
Status = "No valid Dynamic Mesh Component was supplied!";
Success = false;
return nullptr;
}
//No Triangles?
if(DynamicMeshComp->GetDynamicMesh()->IsEmpty())
{
Status = "Dynamic Mesh has no triangles!";
Success = false;
return nullptr;
}
//Make sure to remove extension if user added it ♥
//File without any extension
ContentFolderPath.ReplaceInline(TEXT(".uasset"),TEXT(""));
//~~~ Create possibly numbered new filename, if supplied exists! ♥
FString FinalRelativePath = "";
bool FolderTreeCreated = UVictoryBPFunctionLibrary::GenerateUniqueContentRelativeFileName(ContentFolderPath + ".uasset",FinalRelativePath,NewAssetFilePath);
if(!FolderTreeCreated)
{
Status = "Could not create the specified directory tree";
Success = false;
return false;
}
//Remove ext before sending to AssetUtils
//// Path is now Relative with no extension, possibly with 1,2,3 added for each create event in editor! <3
FinalRelativePath.ReplaceInline(TEXT(".uasset"),TEXT(""));
//~~~ End of file path input handling ♥ ~~~
//Create Mesh Base Params
FCreateMeshObjectParams CreateMeshParams;
CreateMeshParams.BaseName = FinalRelativePath;
//~~~ Materials ~~~
DynamicMeshComp->ValidateMaterialSlots();
for(int32 v = 0; v < DynamicMeshComp->GetNumMaterials() ; v++)
{
CreateMeshParams.Materials.Add(DynamicMeshComp->GetMaterial(v));
}
//Set from FDynamicMesh3 (the actual mesh herself, not from a MeshDescription)
CreateMeshParams.SetMesh(DynamicMeshComp->GetMesh());
//Ensure Set to DynamicMesh
CreateMeshParams.MeshType = ECreateMeshObjectSourceMeshType::DynamicMesh;
//~~~
//~~~
//~~~
//~~~~~~~~~~~~~~~~~
// Code from UE_5.0\Engine\Plugins\Runtime\MeshModelingToolset\Source\ModelingComponentsEditorOnly\PublicEditorModelingObjectsCreationAPI.cpp
//Static Mesh!
//CreateMeshObjectResult = EditorCreateMeshAPI->CreateStaticMeshAsset(CreateMeshParams);
//Static Asset Options
UE::AssetUtils::FStaticMeshAssetOptions AssetOptions;
AssetOptions.NewAssetPath = "/Game/" + CreateMeshParams.BaseName;
//Ensure no //
FPaths::RemoveDuplicateSlashes(AssetOptions.NewAssetPath);
AssetOptions.NumSourceModels = 1;
AssetOptions.NumMaterialSlots = CreateMeshParams.Materials.Num();
//Got rid of FilterMaterials part <3
AssetOptions.AssetMaterials = (CreateMeshParams.AssetMaterials.Num() == AssetOptions.NumMaterialSlots)
? CreateMeshParams.AssetMaterials
: CreateMeshParams.Materials;
AssetOptions.bEnableRecomputeNormals = CreateMeshParams.bEnableRecomputeNormals;
AssetOptions.bEnableRecomputeTangents = CreateMeshParams.bEnableRecomputeTangents;
AssetOptions.bGenerateNaniteEnabledMesh = CreateMeshParams.bEnableNanite;
AssetOptions.NaniteProxyTrianglePercent = CreateMeshParams.NaniteProxyTrianglePercent;
AssetOptions.bCreatePhysicsBody = CreateMeshParams.bEnableCollision;
AssetOptions.CollisionType = CreateMeshParams.CollisionMode;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Dynamic Mesh! ♥
FDynamicMesh3* DynamicMesh = &CreateMeshParams.DynamicMesh.GetValue();
AssetOptions.SourceMeshes.DynamicMeshes.Add(DynamicMesh);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Static Mesh Result
UE::AssetUtils::FStaticMeshResults ResultData;
//==========
//CREATE!!!
UE::AssetUtils::ECreateStaticMeshResult AssetResult = UE::AssetUtils::CreateStaticMeshAsset(AssetOptions, ResultData);
//==========
if (AssetResult != UE::AssetUtils::ECreateStaticMeshResult::Ok)
{
Status = "UE::AssetUtils::ECreateStaticMeshResult is ECreateModelingObjectResult::Failed_AssetCreationFailed";
Success = false;
return false;
}
// End of code from PublicEditorModelingObjectsCreationAPI.cpp
//~~~~~~~~~~~~~~~~~
Status = "Victory!";
Success = true;
return ResultData.StaticMesh;
#endif
Status = "This node is for Editor Builds only, but does create static mesh assets that can ship with your packaged game! ♥ ";
Success = false;
return nullptr;
}
bool UVictoryBPFunctionLibrary::GenerateUniqueContentRelativeFileName(FString ContentRelativeFilePath, FString& ContentRelativeNewFileName, FString& AbsolutePath, bool CreateFolderTree)
{
//UE User-Input Assistance (inline) ♥
FPaths::NormalizeFilename(ContentRelativeFilePath);
FPaths::RemoveDuplicateSlashes(ContentRelativeFilePath);
FString AbsContentPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
//Extension
FString FileExt = FPaths::GetExtension(ContentRelativeFilePath, true); //include .
//File without any extension
FString AssetFile = FPaths::GetBaseFilename(ContentRelativeFilePath);
//Everything but the file
FString BasePath = FPaths::GetPath(ContentRelativeFilePath);
if(ContentRelativeFilePath.Contains("/"))
{
//Absolute Path
BasePath = AbsContentPath + BasePath;
if(CreateFolderTree)
{
//Folder?
if(!FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*BasePath))
{
//Info out to user about what was attempted
AbsolutePath = BasePath;
return false;
//~~~~~~~~~~~~~~~~~~~~~~
}
}
}
//~~~
// Make path with extension
if(BasePath != "")
{
AbsolutePath = BasePath + "/" + AssetFile;
}
else
{
AbsolutePath = AbsContentPath + AssetFile;
}
//Check if file exists already, increment int as needed, ♥
//Absolute Path + File, Still No Extension yet
BasePath = AbsolutePath;
int32 FileNameInt = 1;
AbsolutePath = BasePath + FileExt;
while(FPlatformFileManager::Get().GetPlatformFile().FileExists( *AbsolutePath))
{
FileNameInt++;
AbsolutePath = BasePath + FString::FromInt(FileNameInt) + FileExt;
}
FString Left;
//Make Relative
AbsolutePath.Split(TEXT("/Content/"),&Left,&ContentRelativeNewFileName);
return true;
}
Have Fun Creating Static Mesh Assets Using Your Own Custom Modeling Tools
And Modeling to Your Creative Heart’s Content in the Level Viewport!
PS: You will want to enable this plugin for use with the new content in the Victory Plugin Content Folder:
(The content demoed in the video is zipped inside Victory Plugin Content Folder so you can make sure you have Geometry Script Plugin enabled first)
PSS: More info: