as long as it has cow bells, I’m in…
Initially I was quite deterred and frustrated as my project broke; now that it is somewhat fixed I’m enjoying it though EA was more stable
Thank You Epic for the In-Level-Viewport Modelling System
and a very special thank you for
Mesh Boolean!!!
Oh I have always wanted this!
You’re the best Epic!!!
For everyone who’s curious, here is my demo of the features I just mentioned,
And I give you the whole UE5 project that you see in this video!
UE5 is The Best Thing That Has Ever Happened To The Game Industry
Rama
PS: I really mean it, In-Level-Viewport Modelling shall change the gaming industry forever, watch as indie devs and AAA studios both produce more imaginative games now that the core developers can ALL be modellers!
PSS: Direct Video Link!
You’re the best Epic!!!
UE5 Is Changing This World,
Giving a measurable percentage of the planetary population reasons to smile!
Rama
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: