diff --git a/.vsconfig b/.vsconfig new file mode 100644 index 00000000..7f1e056e --- /dev/null +++ b/.vsconfig @@ -0,0 +1,13 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.14.34.17.4.x86.x64", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.Windows10SDK", + "Microsoft.VisualStudio.Workload.CoreEditor", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame" + ] +} diff --git a/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset b/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset index 4cc6c685..9b9e6aea 100644 Binary files a/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset and b/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset differ diff --git a/Content/StarterContent/Materials/M_Basic_Wall.uasset b/Content/StarterContent/Materials/M_Basic_Wall.uasset index 61ef19aa..94cc0b42 100644 Binary files a/Content/StarterContent/Materials/M_Basic_Wall.uasset and b/Content/StarterContent/Materials/M_Basic_Wall.uasset differ diff --git a/Content/StarterContent/Shapes/Shape_Cone.uasset b/Content/StarterContent/Shapes/Shape_Cone.uasset index 8879ee79..bc685dfe 100644 Binary files a/Content/StarterContent/Shapes/Shape_Cone.uasset and b/Content/StarterContent/Shapes/Shape_Cone.uasset differ diff --git a/MassAITesting.uproject b/MassAITesting.uproject index 43218440..98d3dc6b 100644 --- a/MassAITesting.uproject +++ b/MassAITesting.uproject @@ -46,6 +46,10 @@ { "Name": "MassGameplay", "Enabled": true + }, + { + "Name": "TDxUnrealEditor", + "Enabled": true } ] } \ No newline at end of file diff --git a/Plugins/AnimToTexture/.gitignore b/Plugins/AnimToTexture/.gitignore new file mode 100644 index 00000000..6582eaf9 --- /dev/null +++ b/Plugins/AnimToTexture/.gitignore @@ -0,0 +1,74 @@ +# Visual Studio 2015 user specific files +.vs/ + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.ipa + +# These project files can be generated by the engine +*.xcodeproj +*.xcworkspace +*.sln +*.suo +*.opensdf +*.sdf +*.VC.db +*.VC.opendb + +# Precompiled Assets +SourceArt/**/*.png +SourceArt/**/*.tga + +# Binary Files +Binaries/* +Plugins/*/Binaries/* + +# Builds +Build/* + +# Whitelist PakBlacklist-.txt files +!Build/*/ +Build/*/** +!Build/*/PakBlacklist*.txt + +# Don't ignore icon files in Build +!Build/**/*.ico + +# Built data for maps +*_BuiltData.uasset + +# Configuration files generated by the Editor +Saved/* + +# Compiled source files for the engine to use +Intermediate/* +Plugins/*/Intermediate/* + +# Cache files for the editor to use +DerivedDataCache/* diff --git a/Plugins/AnimToTexture/AnimToTexture.uplugin b/Plugins/AnimToTexture/AnimToTexture.uplugin new file mode 100644 index 00000000..3df91472 --- /dev/null +++ b/Plugins/AnimToTexture/AnimToTexture.uplugin @@ -0,0 +1,29 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "AnimToTexture", + "Description": "Converts SkeletalMesh Animations into Textures", + "Category": "Other", + "CreatedBy": "Epic Games", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": true, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "AnimToTexture", + "Type": "Runtime", + "LoadingPhase": "PreDefault" + }, + { + "Name": "AnimToTextureEditor", + "Type": "Editor", + "LoadingPhase": "PreLoadingScreen" + } + ] +} \ No newline at end of file diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Non_Combat_Idle.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Non_Combat_Idle.uasset new file mode 100644 index 00000000..bb04a266 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Non_Combat_Idle.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Walk_Fwd.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Walk_Fwd.uasset new file mode 100644 index 00000000..7deb8530 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/Walk_Fwd.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/run_fwd.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/run_fwd.uasset new file mode 100644 index 00000000..1826b064 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Animations/run_fwd.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/BP_AnimToTexture.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/BP_AnimToTexture.uasset new file mode 100644 index 00000000..2a3d3a9e Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/BP_AnimToTexture.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_BoneAnimation.uasset new file mode 100644 index 00000000..9d95a257 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_VertexAnimation.uasset new file mode 100644 index 00000000..71d4fad0 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Data/DA_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_Body_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_Body_BoneAnimation.uasset new file mode 100644 index 00000000..bf87665e Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_Body_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_ChestLog_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_ChestLog_BoneAnimation.uasset new file mode 100644 index 00000000..6a8c7af5 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/MI_ChestLog_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/M_Body_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/M_Body_BoneAnimation.uasset new file mode 100644 index 00000000..05ed298c Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/BoneAnimation/M_Body_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_Body.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_Body.uasset new file mode 100644 index 00000000..e3d6773d Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_Body.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_ChestLogo.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_ChestLogo.uasset new file mode 100644 index 00000000..0ce65bcc Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/M_ChestLogo.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_GlossyBlack_Latex.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_GlossyBlack_Latex.uasset new file mode 100644 index 00000000..9bd013b7 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_GlossyBlack_Latex.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 00000000..fec0be5d Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige_LOGO.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 00000000..0627d93f Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_Plastic_Shiny_Beige_LOGO.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_SoftMetal.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_SoftMetal.uasset new file mode 100644 index 00000000..b5f6c71a Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/MaterialFunctions/ML_SoftMetal.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_Body_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_Body_VertexAnimation.uasset new file mode 100644 index 00000000..c09c59a4 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_Body_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_ChestLog_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_ChestLog_VertexAnimation.uasset new file mode 100644 index 00000000..2d89e104 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/MI_ChestLog_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/M_Body_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/M_Body_VertexAnimation.uasset new file mode 100644 index 00000000..72cede9c Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Materials/VertexAnimation/M_Body_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SKM_Mannequin.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SKM_Mannequin.uasset new file mode 100644 index 00000000..0f58d4d1 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SKM_Mannequin.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SK_Mannequin.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SK_Mannequin.uasset new file mode 100644 index 00000000..a361bdc1 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Meshes/SK_Mannequin.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_BoneAnimation.uasset new file mode 100644 index 00000000..3d13ea6c Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_VertexAnimation.uasset new file mode 100644 index 00000000..43321ce1 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/SM_Mannequin_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BonePosition.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BonePosition.uasset new file mode 100644 index 00000000..cbdf9960 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BonePosition.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneRotation.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneRotation.uasset new file mode 100644 index 00000000..0377c80a Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneRotation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneWeight.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneWeight.uasset new file mode 100644 index 00000000..17124a33 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/BoneAnimation/TX_BoneWeight.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01.uasset new file mode 100644 index 00000000..ee8bee42 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01_N.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01_N.uasset new file mode 100644 index 00000000..b4f23f4c Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Aluminum01_N.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_D.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 00000000..dc2c3a17 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_D.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_N.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 00000000..b21027e2 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/T_ML_Rubber_Blue_01_N.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4Man_Logo_N.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4Man_Logo_N.uasset new file mode 100644 index 00000000..691e6023 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4Man_Logo_N.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_LOGO_CARD.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_LOGO_CARD.uasset new file mode 100644 index 00000000..c80a7f00 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_LOGO_CARD.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin_MAT_MASKA.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin_MAT_MASKA.uasset new file mode 100644 index 00000000..78f70d10 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin_MAT_MASKA.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin__normals.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin__normals.uasset new file mode 100644 index 00000000..8119537d Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/UE4_Mannequin__normals.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexNormal.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexNormal.uasset new file mode 100644 index 00000000..2b9feb20 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexNormal.uasset differ diff --git a/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexPosition.uasset b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexPosition.uasset new file mode 100644 index 00000000..7a5ee2f7 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Characters/Mannequin/Textures/VertexAnimation/TX_VertexPosition.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/ML_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Materials/ML_BoneAnimation.uasset new file mode 100644 index 00000000..d2eede02 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/ML_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/ML_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Materials/ML_VertexAnimation.uasset new file mode 100644 index 00000000..c5caede7 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/ML_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/M_BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Materials/M_BoneAnimation.uasset new file mode 100644 index 00000000..766f10ad Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/M_BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/M_BoneIndicesDebug.uasset b/Plugins/AnimToTexture/Content/Materials/M_BoneIndicesDebug.uasset new file mode 100644 index 00000000..d3fa9b2a Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/M_BoneIndicesDebug.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/M_VertexAnimation.uasset b/Plugins/AnimToTexture/Content/Materials/M_VertexAnimation.uasset new file mode 100644 index 00000000..ef775b7f Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/M_VertexAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendFourIfluences.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendFourIfluences.uasset new file mode 100644 index 00000000..4f20eb44 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendFourIfluences.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendTwoInfluences.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendTwoInfluences.uasset new file mode 100644 index 00000000..10bf8961 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BlendTwoInfluences.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneAnimation.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneAnimation.uasset new file mode 100644 index 00000000..0615b7c9 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneAnimation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneIndexUVs.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneIndexUVs.uasset new file mode 100644 index 00000000..13d35a13 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneIndexUVs.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneXformPositionAndNormal.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneXformPositionAndNormal.uasset new file mode 100644 index 00000000..5e7494dc Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/BoneXformPositionAndNormal.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/GetFrame.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/GetFrame.uasset new file mode 100644 index 00000000..3881c651 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/GetFrame.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeFourWeights.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeFourWeights.uasset new file mode 100644 index 00000000..88bfe538 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeFourWeights.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeTwoWeights.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeTwoWeights.uasset new file mode 100644 index 00000000..fd89d999 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/NormalizeTwoWeights.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/TexCoord.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/TexCoord.uasset new file mode 100644 index 00000000..ba3facbe Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/TexCoord.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBonePosition.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBonePosition.uasset new file mode 100644 index 00000000..f3eb315a Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBonePosition.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneRotation.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneRotation.uasset new file mode 100644 index 00000000..230912ef Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneRotation.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneWeights.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneWeights.uasset new file mode 100644 index 00000000..5ee25a52 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackBoneWeights.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexNormal.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexNormal.uasset new file mode 100644 index 00000000..b0f37023 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexNormal.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexPosition.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexPosition.uasset new file mode 100644 index 00000000..13a1818b Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/UnpackVertexPosition.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexPositionAndNormal.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexPositionAndNormal.uasset new file mode 100644 index 00000000..ee60b2e6 Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexPositionAndNormal.uasset differ diff --git a/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexUVs.uasset b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexUVs.uasset new file mode 100644 index 00000000..842fcc1f Binary files /dev/null and b/Plugins/AnimToTexture/Content/Materials/MaterialFunctions/VertexUVs.uasset differ diff --git a/Plugins/AnimToTexture/LICENSE b/Plugins/AnimToTexture/LICENSE new file mode 100644 index 00000000..52d1e0f2 --- /dev/null +++ b/Plugins/AnimToTexture/LICENSE @@ -0,0 +1,3 @@ +UE-Only Content - Licensed for Use Only with Unreal Engine-based Products + +https://www.unrealengine.com/marketplace/en-US/product/city-sample \ No newline at end of file diff --git a/Plugins/AnimToTexture/README.md b/Plugins/AnimToTexture/README.md new file mode 100644 index 00000000..a61552ed --- /dev/null +++ b/Plugins/AnimToTexture/README.md @@ -0,0 +1,2 @@ +# AnimToTexture +Unreal Engine 5 plugin from Epic's [City Sample project](https://www.unrealengine.com/marketplace/en-US/product/city-sample) to convert animations to textures. \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/AnimToTexture.Build.cs b/Plugins/AnimToTexture/Source/AnimToTexture/AnimToTexture.Build.cs new file mode 100644 index 00000000..17384782 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/AnimToTexture.Build.cs @@ -0,0 +1,54 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class AnimToTexture : ModuleRules +{ + public AnimToTexture(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + // ... add private dependencies that you statically link with here ... + "MeshDescription", + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTexture.cpp b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTexture.cpp new file mode 100644 index 00000000..dc9a2e1a --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTexture.cpp @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTexture.h" + +#define LOCTEXT_NAMESPACE "FAnimToTextureModule" + +void FAnimToTextureModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + +} + +void FAnimToTextureModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FAnimToTextureModule, AnimToTexture) \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureDataAsset.cpp b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureDataAsset.cpp new file mode 100644 index 00000000..312f8332 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureDataAsset.cpp @@ -0,0 +1,55 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTextureDataAsset.h" + +int32 UAnimToTextureDataAsset::GetIndexFromAnimSequence(const UAnimSequence* Sequence) +{ + int32 OutIndex = 0; + + int32 NumSequences = AnimSequences.Num(); + + // We can store a sequence to index map for a faster search + for (int32 CurrentIndex = 0; CurrentIndex < NumSequences; ++CurrentIndex) + { + const FAnimSequenceInfo& SequenceInfo = AnimSequences[CurrentIndex]; + if (SequenceInfo.AnimSequence == Sequence) + { + OutIndex = CurrentIndex; + break; + } + } + + return OutIndex; +} + +namespace AnimToTextureParamNames +{ + static const FName BoundingBoxMin = TEXT("MinBBox"); + static const FName BoundingBoxScale = TEXT("SizeBBox"); + static const FName RowsPerFrame = TEXT("RowsPerFrame"); + static const FName BoneWeightRowsPerFrame = TEXT("BoneWeightsRowsPerFrame"); + static const FName NumFrames = TEXT("NumFrames (S)"); + //static const FName UVChannel = TEXT("UVChannel"); + //static const FName AnimateSwitch = TEXT("Animate (B)"); + static const FName VertexPositionTexture = TEXT("PositionTexture"); + static const FName VertexNormalTexture = TEXT("NormalTexture"); + static const FName BonePositionTexture = TEXT("BonePositionTexture"); + static const FName BoneRotationTexture = TEXT("BoneRotationTexture"); + static const FName BoneWeightsTexture = TEXT("BoneWeightsTexture"); +} + +FAnimToTextureMaterialParamNames::FAnimToTextureMaterialParamNames() +{ + BoundingBoxMin = AnimToTextureParamNames::BoundingBoxMin; + BoundingBoxScale = AnimToTextureParamNames::BoundingBoxScale; + RowsPerFrame = AnimToTextureParamNames::RowsPerFrame; + BoneWeightRowsPerFrame = AnimToTextureParamNames::BoneWeightRowsPerFrame; + NumFrames = AnimToTextureParamNames::NumFrames; + //UVChannel = AnimToTextureParamNames::UVChannel; + //AnimateSwitch = AnimToTextureParamNames::AnimateSwitch; + VertexPositionTexture = AnimToTextureParamNames::VertexPositionTexture; + VertexNormalTexture = AnimToTextureParamNames::VertexNormalTexture; + BonePositionTexture = AnimToTextureParamNames::BonePositionTexture; + BoneRotationTexture = AnimToTextureParamNames::BoneRotationTexture; + BoneWeightsTexture = AnimToTextureParamNames::BoneWeightsTexture; +} diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureInstancePlaybackHelpers.cpp b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureInstancePlaybackHelpers.cpp new file mode 100644 index 00000000..6a68c898 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Private/AnimToTextureInstancePlaybackHelpers.cpp @@ -0,0 +1,83 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTextureInstancePlaybackHelpers.h" +#include "AnimToTextureDataAsset.h" + +void UAnimToTextureInstancePlaybackLibrary::SetupInstancedMeshComponent(UInstancedStaticMeshComponent* InstancedMeshComponent, FAnimToTextureInstanceData& InstanceData, int32 NumInstances) +{ + if (InstancedMeshComponent) + { + InstancedMeshComponent->NumCustomDataFloats = InstanceData.PlaybackData.GetTypeSize() / sizeof(float); + InstancedMeshComponent->PerInstanceSMData.Reset(); + InstancedMeshComponent->PerInstanceSMData.AddDefaulted(NumInstances); + InstancedMeshComponent->PreAllocateInstancesMemory(NumInstances); + InstancedMeshComponent->PerInstanceSMCustomData.SetNumZeroed(NumInstances * InstancedMeshComponent->NumCustomDataFloats); + AllocateInstanceData(InstanceData, NumInstances); + } +} + +void UAnimToTextureInstancePlaybackLibrary::BatchUpdateInstancedMeshComponent(UInstancedStaticMeshComponent* InstancedMeshComponent, FAnimToTextureInstanceData& InstanceData) +{ + SIZE_T CustomDataSizeToCopy = FMath::Min(InstanceData.PlaybackData.Num() * InstanceData.PlaybackData.GetTypeSize(), InstancedMeshComponent->PerInstanceSMCustomData.Num() * InstancedMeshComponent->PerInstanceSMCustomData.GetTypeSize()); + FMemory::Memcpy(InstancedMeshComponent->PerInstanceSMCustomData.GetData(), InstanceData.PlaybackData.GetData(), CustomDataSizeToCopy); + + int32 TransformsToCopy = FMath::Min(InstancedMeshComponent->GetNumRenderInstances(), InstanceData.StaticMeshInstanceData.Num()); + InstancedMeshComponent->BatchUpdateInstancesData(0, TransformsToCopy, InstanceData.StaticMeshInstanceData.GetData(), true, false); +} + +void UAnimToTextureInstancePlaybackLibrary::AllocateInstanceData(FAnimToTextureInstanceData& InstanceData, int32 Count) +{ + InstanceData.StaticMeshInstanceData.AddDefaulted(Count); + InstanceData.PlaybackData.AddDefaulted(Count); +} + +bool UAnimToTextureInstancePlaybackLibrary::UpdateInstanceData(FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, const FAnimToTextureInstancePlaybackData& PlaybackData, const FTransform& Transform) +{ + if (InstanceData.PlaybackData.IsValidIndex(InstanceIndex) && InstanceData.StaticMeshInstanceData.IsValidIndex(InstanceIndex)) + { + InstanceData.PlaybackData[InstanceIndex] = PlaybackData; + InstanceData.StaticMeshInstanceData[InstanceIndex].Transform = Transform.ToMatrixWithScale(); + return true; + } + + return false; +} + +bool UAnimToTextureInstancePlaybackLibrary::GetInstancePlaybackData(const FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, FAnimToTextureInstancePlaybackData& InstancePlaybackData) +{ + if (InstanceData.PlaybackData.IsValidIndex(InstanceIndex)) + { + InstancePlaybackData = InstanceData.PlaybackData[InstanceIndex]; + return true; + } + + return false; +} + +bool UAnimToTextureInstancePlaybackLibrary::GetInstanceTransform(const FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, FTransform& InstanceTransform) +{ + if (InstanceData.StaticMeshInstanceData.IsValidIndex(InstanceIndex)) + { + InstanceTransform = FTransform(InstanceData.StaticMeshInstanceData[InstanceIndex].Transform); + return true; + } + + return false; +} + +bool UAnimToTextureInstancePlaybackLibrary::AnimStateFromDataAsset(const UAnimToTextureDataAsset* DataAsset, int32 StateIndex, FAnimToTextureAnimState& AnimState) +{ + if (DataAsset && DataAsset->Animations.IsValidIndex(StateIndex)) + { + const FAnimInfo& AnimInfo = DataAsset->Animations[StateIndex]; + AnimState.StartFrame = AnimInfo.AnimStart; + AnimState.NumFrames = AnimInfo.NumFrames; + AnimState.bLooping = AnimInfo.bLooping; + + return true; + } + + AnimState = FAnimToTextureAnimState(); + return false; +} + diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Private/EvaluateSequenceAnimInstance.cpp b/Plugins/AnimToTexture/Source/AnimToTexture/Private/EvaluateSequenceAnimInstance.cpp new file mode 100644 index 00000000..492124d6 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Private/EvaluateSequenceAnimInstance.cpp @@ -0,0 +1,10 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "EvaluateSequenceAnimInstance.h" + + +UEvaluateSequenceAnimInstance::UEvaluateSequenceAnimInstance(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + +} \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Private/LightweightMontageInstance.cpp b/Plugins/AnimToTexture/Source/AnimToTexture/Private/LightweightMontageInstance.cpp new file mode 100644 index 00000000..896ab76d --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Private/LightweightMontageInstance.cpp @@ -0,0 +1,258 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "LightweightMontageInstance.h" +#include "Animation/AnimMontage.h" + +EMontageSubStepResult UE::VertexAnimation::FLightweightMontageSubstepper::Advance(float& InOut_P_Original, UAnimMontage* Montage) +{ + // Copied from MontageSubstepper::Advance + // @todo : Clean up + optimize when we do profiling + + DeltaMove = 0.f; + + if (Montage == nullptr) + { + return EMontageSubStepResult::InvalidMontage; + } + + bReachedEndOfSection = false; + + float PositionInSection; + CurrentSectionIndex = Montage->GetAnimCompositeSectionIndexFromPos(InOut_P_Original, PositionInSection); + if (!Montage->IsValidSectionIndex(CurrentSectionIndex)) + { + return EMontageSubStepResult::InvalidSection; + } + + const FCompositeSection& CurrentSection = Montage->GetAnimCompositeSection(CurrentSectionIndex); + CurrentSectionStartTime = CurrentSection.GetTime(); + + // Find end of current section. We only update one section at a time. + CurrentSectionLength = Montage->GetSectionLength(CurrentSectionIndex); + + if (FMath::IsNearlyZero(TimeRemaining)) + { + return EMontageSubStepResult::NotMoved; + } + + const float PlayRate = Montage->RateScale; + + if (FMath::IsNearlyZero(PlayRate)) + { + return EMontageSubStepResult::NotMoved; + } + + DeltaMove = TimeRemaining * PlayRate; + + // Finally clamp DeltaMove by section markers. + { + const float OldDeltaMove = DeltaMove; + + // Clamp DeltaMove based on move allowed within current section + // We stop at each section marker to evaluate whether we should jump to another section marker or not. + // Test is inclusive, so we know if we've reached marker or not. + const float MaxSectionMove = CurrentSectionLength - PositionInSection; + if (DeltaMove >= MaxSectionMove) + { + DeltaMove = MaxSectionMove; + bReachedEndOfSection = true; + } + } + + // DeltaMove is now final, see if it has any effect on our position. + if (FMath::Abs(DeltaMove) > 0.f) + { + // Note that we don't worry about looping and wrapping around here. + // We step per section to simplify code to extract notifies/root motion/etc. + InOut_P_Original += DeltaMove; + + // Decrease RemainingTime with actual time elapsed + // So we can take more substeps as needed. + const float TimeStep = DeltaMove / PlayRate; + ensure(TimeStep >= 0.f); + TimeRemaining = FMath::Max(TimeRemaining - TimeStep, 0.f); + + return EMontageSubStepResult::Moved; + } + else + { + return EMontageSubStepResult::NotMoved; + } +} + +void UE::VertexAnimation::FLightweightMontageInstance::Initialize(UAnimMontage* InMontage, float StartTime) +{ + Terminate(); + + MontageWeak = InMontage; + Montage = InMontage; + if (Montage) + { + CachedLength = InMontage->GetPlayLength(); + bInitialized = true; + Position = StartTime; + RefreshNextPrevSections(Montage); + MontageSubStepper.Initialize(this); + MontageSubStepper.Advance(Position, Montage); + SetSequence(FindSequenceAtCurrentTime(SectionStartTimeTransient)); + } + + Montage = nullptr; +} + +void UE::VertexAnimation::FLightweightMontageInstance::RefreshNextPrevSections(UAnimMontage* InMontage) +{ + // initialize next section + if (InMontage->CompositeSections.Num() > 0) + { + NextSections.Empty(InMontage->CompositeSections.Num()); + NextSections.AddUninitialized(InMontage->CompositeSections.Num()); + + for (int32 I = 0; I < InMontage->CompositeSections.Num(); ++I) + { + FCompositeSection& Section = InMontage->CompositeSections[I]; + int32 NextSectionIdx = InMontage->GetSectionIndex(Section.NextSectionName); + NextSections[I] = NextSectionIdx; + } + } +} + +const UAnimSequence* UE::VertexAnimation::FLightweightMontageInstance::FindSequenceAtCurrentTime(float& SequenceStartTime) +{ + SequenceStartTime = 0.0f; + const FAnimTrack& AnimTrack = Montage->SlotAnimTracks[0].AnimTrack; + if (const FAnimSegment* Segment = AnimTrack.GetSegmentAtTime(Position)) + { + SequenceStartTime = Segment->StartPos - Segment->AnimStartTime; + return Cast(Segment->AnimReference); + } + + return nullptr; +} + +UAnimMontage* UE::VertexAnimation::FLightweightMontageInstance::GetMontage() const +{ + return bInitialized ? MontageWeak.Get() : nullptr; +} + +const UAnimSequence* UE::VertexAnimation::FLightweightMontageInstance::GetSequence() const +{ + return bInitialized ? SequenceWeak.Get() : nullptr; +} + +void UE::VertexAnimation::FLightweightMontageInstance::SetSequence(const UAnimSequence* InSequence) +{ + bSequenceChangedThisFrame = false; + const UAnimSequence* PrevSequence = SequenceWeak.Get(); + if (PrevSequence != InSequence) + { + bSequenceChangedThisFrame = true; + SequenceWeak = InSequence; + } +} + +float UE::VertexAnimation::FLightweightMontageInstance::GetPositionInSection() const +{ + return Position - SectionStartTimeTransient; +} + +void UE::VertexAnimation::FLightweightMontageInstance::Terminate() +{ + *this = FLightweightMontageInstance(); +} + +bool UE::VertexAnimation::FLightweightMontageInstance::Advance_Internal(float DeltaTime, FRootMotionMovementParams& OutRootMotionParams, const FLightWeightMontageExtractionSettings& ExtractionSettings) +{ + // Copied from FAnimMontageInstance::Advance + // @todo : Clean up + optimize when we do profiling + + const bool bExtractRootMotion = Montage->HasRootMotion() && ExtractionSettings.bExtractRootMotion; + + /** + Limit number of iterations for performance. + This can get out of control if PlayRate is set really high, or there is a hitch, and Montage is looping for example. + */ + const int32 MaxIterations = 10; + int32 NumIterations = 0; + + /** + If we're hitting our max number of iterations for whatever reason, + make sure we're not accumulating too much time, and go out of range. + */ + if (MontageSubStepper.GetRemainingTime() < 10.f) + { + MontageSubStepper.AddEvaluationTime(DeltaTime); + } + + while (MontageSubStepper.HasTimeRemaining() && (++NumIterations < MaxIterations)) + { + const float PreviousSubStepPosition = Position; + EMontageSubStepResult SubStepResult = MontageSubStepper.Advance(Position, Montage); + + if (SubStepResult != EMontageSubStepResult::Moved) + { + // stop and leave this loop + break; + } + + // Extract Root Motion for this time slice, and accumulate it. + if (bExtractRootMotion) + { + OutRootMotionParams.Accumulate(Montage->ExtractRootMotionFromTrackRange(PreviousSubStepPosition, Position)); + } + + // if we reached end of section, and we were not processing a branching point, and no events has messed with out current position.. + // .. Move to next section. + // (this also handles looping, the same as jumping to a different section). + if (MontageSubStepper.HasReachedEndOfSection()) + { + const int32 CurrentSectionIndex = MontageSubStepper.GetCurrentSectionIndex(); + + // Get recent NextSectionIndex in case it's been changed by previous events. + const int32 RecentNextSectionIndex = NextSections[CurrentSectionIndex]; + if (RecentNextSectionIndex != INDEX_NONE) + { + float LatestNextSectionStartTime; + float LatestNextSectionEndTime; + Montage->GetSectionStartAndEndTime(RecentNextSectionIndex, LatestNextSectionStartTime, LatestNextSectionEndTime); + + // Jump to next section's appropriate starting point (start or end). + Position = LatestNextSectionStartTime; + } + else + { + // Reached end of last section. Exit. + return false; + break; + } + } + } + + return true; +} + +bool UE::VertexAnimation::FLightweightMontageInstance::IsValid() const +{ + return bInitialized && MontageWeak.IsValid() && SequenceWeak.IsValid(); +} + +void UE::VertexAnimation::FLightweightMontageInstance::Advance(float DeltaTime, float InGlobalTime, FRootMotionMovementParams& OutRootMotionParams, const FLightWeightMontageExtractionSettings& ExtractionSettings) +{ + Montage = GetMontage(); + if (Montage == nullptr) + { + return; + } + + const bool bIsPlaying = Advance_Internal(DeltaTime, OutRootMotionParams, ExtractionSettings); + const UAnimSequence* CurrentSequence = FindSequenceAtCurrentTime(SectionStartTimeTransient); + + SetSequence(CurrentSequence); + + Montage = nullptr; + + if (!bIsPlaying) + { + Terminate(); + } +} \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTexture.h b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTexture.h new file mode 100644 index 00000000..58a361fb --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTexture.h @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FAnimToTextureModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureDataAsset.h b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureDataAsset.h new file mode 100644 index 00000000..a4c50d14 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureDataAsset.h @@ -0,0 +1,358 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Engine/DataAsset.h" +#include "Animation/AnimInstance.h" +#include "Engine/StaticMesh.h" +#include "AnimToTextureDataAsset.generated.h" + +class USkeletalMesh; +class UStaticMesh; +class UTexture2D; + +USTRUCT(Blueprintable) +struct ANIMTOTEXTURE_API FAnimToTextureMaterialParamNames +{ + GENERATED_BODY() + + // + // Static Switch Parameters + // + + // UPROPERTY(BlueprintReadWrite, EditAnywhere) + // FName AnimateSwitch; + + // + // Scalar Parameters + // + + //UPROPERTY(EditAnywhere) + //FName UVChannel; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName RowsPerFrame; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BoneWeightRowsPerFrame; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName NumFrames; + + // + // Vector Parameters + // + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BoundingBoxMin; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BoundingBoxScale; + + // + // Texture Parameters + // + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName VertexPositionTexture; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName VertexNormalTexture; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BonePositionTexture; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BoneRotationTexture; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FName BoneWeightsTexture; + + // Initialize Names + FAnimToTextureMaterialParamNames(); +}; + +UENUM(Blueprintable) +enum class EAnimToTextureMode : uint8 +{ + /* Position and Normal Per-Vertex */ + Vertex, + /* Linear Blending Skinnin */ + Bone, +}; + +//UENUM(Blueprintable) +//enum class EAnimToTextureNumInfluences : uint8 +//{ +// One, Two, Four, +//}; + +UENUM(Blueprintable) +enum class EAnimToTextureBonePrecision : uint8 +{ + /* Bone positions and rotations stored in 8 bits */ + EightBits, + /* Bone positions and rotations stored in 16 bits */ + SixteenBits, +}; + + +//USTRUCT(Blueprintable) +//struct FSkeletalMeshInfo +//{ +// GENERATED_BODY() +// +// UPROPERTY(EditAnywhere) +// USkeletalMesh* SkeletalMesh = nullptr; +// +// UPROPERTY(EditAnywhere) +// int32 LODIndex = 0; +//}; + +//USTRUCT(Blueprintable) +//struct FStaticMeshInfo +//{ +// GENERATED_BODY() +// +// UPROPERTY(EditAnywhere) +// UStaticMesh* StaticMesh = nullptr; +// +// UPROPERTY(EditAnywhere) +// int32 LODIndex = 0; +// +// UPROPERTY(EditAnywhere) +// int32 UVChannel = 1; +//}; + +USTRUCT(Blueprintable) +struct FAnimSequenceInfo +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bEnabled = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UAnimSequence* AnimSequence = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bLooping = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bUseCustomRange = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bUseCustomRange")) + int32 StartFrame = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bUseCustomRange")) + int32 EndFrame = 1; + +}; + +USTRUCT(Blueprintable) +struct FAnimInfo +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere) + int32 NumFrames = 0; + + UPROPERTY(VisibleAnywhere) + int32 AnimStart = 0; + + UPROPERTY(EditAnywhere) + bool bLooping = true; +}; + +UCLASS(MinimalAPI, Blueprintable) +class UAnimToTextureDataAsset : public UPrimaryDataAsset +{ +public: + GENERATED_BODY() + + // ------------------------------------------------------ + // Skeletal Mesh + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalMesh", meta = (AssetBundles = "Client")) + TSoftObjectPtr SkeletalMesh; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalMesh", Meta = (DisplayName = "LODIndex")) + int32 SkeletalLODIndex = 0; + + // ------------------------------------------------------ + // Static Mesh + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StaticMesh", meta = (AssetBundles = "Client")) + TSoftObjectPtr StaticMesh; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StaticMesh", Meta = (DisplayName = "LODIndex")) + int32 StaticLODIndex = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StaticMesh") + int32 UVChannel = 1; + + // ------------------------------------------------------ + // Texture + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture") + int32 MaxHeight = 4096; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture") + int32 MaxWidth = 4096; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture") + bool bEnforcePowerOfTwo = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture") + EAnimToTextureMode Mode; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Vertex", meta = (EditCondition = "Mode == EAnimToTextureMode::Vertex")) + TSoftObjectPtr VertexPositionTexture; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Vertex", meta = (EditCondition = "Mode == EAnimToTextureMode::Vertex")) + TSoftObjectPtr VertexNormalTexture; + + // UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Bone", meta = (EditCondition = "Mode == EAnimToTextureMode::Bone")) + // EAnimToTextureNumInfluences NumInfluences; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Bone", meta = (EditCondition = "Mode == EAnimToTextureMode::Bone")) + TSoftObjectPtr BonePositionTexture; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Bone", meta = (EditCondition = "Mode == EAnimToTextureMode::Bone")) + TSoftObjectPtr BoneRotationTexture; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Bone", meta = (EditCondition = "Mode == EAnimToTextureMode::Bone")) + TSoftObjectPtr BoneWeightTexture; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Texture|Bone", meta = (EditCondition = "Mode == EAnimToTextureMode::Bone")) + EAnimToTextureBonePrecision PositionAndRotationPrecision = EAnimToTextureBonePrecision::EightBits; + + // ------------------------------------------------------ + // Animation + + /** This Mesh will be used as MasterPose. Animations must use same Skeleton than this SkeletalMesh */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TSoftObjectPtr MasterSkeletalMesh; + + /** Bone used for Rigid Binding. The bone needs to be part of the RawBones. + * Sockets and VirtualBones are not supported. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + FName AttachToSocket; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TSubclassOf AnimInstanceClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + float SampleRate = 30.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TArray AnimSequences; + + // ------------------------------------------------------ + // Info + + /* Total Number of Frames in all animations */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info") + int32 NumFrames = 0; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Vertex", Meta = (DisplayName = "RowsPerFrame")) + int32 VertexRowsPerFrame = 1; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Vertex", Meta = (DisplayName = "MinBBox")) + FVector VertexMinBBox; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Vertex", Meta = (DisplayName = "SizeBBox")) + FVector VertexSizeBBox; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Bone") + int32 BoneWeightRowsPerFrame = 1; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Bone") + int32 BoneRowsPerFrame = 1; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Bone", Meta = (DisplayName = "MinBBox")) + FVector BoneMinBBox; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info|Bone", Meta = (DisplayName = "SizeBBox")) + FVector BoneSizeBBox; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Info") + TArray Animations; + + UFUNCTION(BlueprintCallable) + ANIMTOTEXTURE_API int32 GetIndexFromAnimSequence(const UAnimSequence* Sequence); + + UFUNCTION() + void Reset(); + + // If we weren't in a plugin, we could unify this in a base class + template + static AssetType* GetAsset(const TSoftObjectPtr& AssetPointer) + { + AssetType* ReturnVal = nullptr; + if (AssetPointer.ToSoftObjectPath().IsValid()) + { + ReturnVal = AssetPointer.Get(); + if (!ReturnVal) + { + AssetType* LoadedAsset = Cast(AssetPointer.ToSoftObjectPath().TryLoad()); + if (ensureMsgf(LoadedAsset, TEXT("Failed to load asset pointer %s"), *AssetPointer.ToString())) + { + ReturnVal = LoadedAsset; + } + } + } + return ReturnVal; + } + +#define AnimToTextureDataAsset_ASSET_ACCESSOR(ClassName, PropertyName) \ + FORCEINLINE ClassName* Get##PropertyName() const { return GetAsset(PropertyName); } + + AnimToTextureDataAsset_ASSET_ACCESSOR(UStaticMesh, StaticMesh); + AnimToTextureDataAsset_ASSET_ACCESSOR(USkeletalMesh, SkeletalMesh); + AnimToTextureDataAsset_ASSET_ACCESSOR(USkeletalMesh, MasterSkeletalMesh); + AnimToTextureDataAsset_ASSET_ACCESSOR(UTexture2D, VertexPositionTexture); + AnimToTextureDataAsset_ASSET_ACCESSOR(UTexture2D, VertexNormalTexture); + AnimToTextureDataAsset_ASSET_ACCESSOR(UTexture2D, BonePositionTexture); + AnimToTextureDataAsset_ASSET_ACCESSOR(UTexture2D, BoneRotationTexture); + AnimToTextureDataAsset_ASSET_ACCESSOR(UTexture2D, BoneWeightTexture); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Static Mesh")) + UStaticMesh* BP_GetStaticMesh() { return GetStaticMesh(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Skeletal Mesh")) + USkeletalMesh* BP_GetSkeletalMesh() { return GetSkeletalMesh(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Master Skeletal Mesh")) + USkeletalMesh* BP_GetMasterSkeletalMesh() { return GetMasterSkeletalMesh(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Bone Position Texture")) + UTexture2D* BP_GetBonePositionTexture() { return GetBonePositionTexture(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Bone Rotation Texture")) + UTexture2D* BP_GetBoneRotationTexture() { return GetBoneRotationTexture(); } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Bone Weight Texture")) + UTexture2D* BP_GetBoneWeightTexture() { return GetBoneWeightTexture(); } +}; + +FORCEINLINE void UAnimToTextureDataAsset::Reset() +{ + // Common Info. + //this->NumVertices = 0; + this->NumFrames = 0; + this->Animations.Reset(); + + // Vertex Info + this->VertexRowsPerFrame = 1; + this->VertexMinBBox = FVector::ZeroVector; // { TNumericLimits::Max(), TNumericLimits::Max(), TNumericLimits::Max() }; + this->VertexSizeBBox = FVector::ZeroVector; + + // Bone Info + //this->NumBones = 0; + this->BoneRowsPerFrame = 1; + this->BoneWeightRowsPerFrame = 1; + this->BoneMinBBox = FVector::ZeroVector; // { TNumericLimits::Max(), TNumericLimits::Max(), TNumericLimits::Max() }; + this->BoneSizeBBox = FVector::ZeroVector; +}; \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureInstancePlaybackHelpers.h b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureInstancePlaybackHelpers.h new file mode 100644 index 00000000..d1895945 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Public/AnimToTextureInstancePlaybackHelpers.h @@ -0,0 +1,98 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Components/InstancedStaticMeshComponent.h" + +#include "AnimToTextureInstancePlaybackHelpers.generated.h" + +// Use floats to match custom floats of instanced static mesh +// We could pack a float w/ more parameters if desired +USTRUCT(BlueprintType) +struct FAnimToTextureAnimState +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + float StartFrame = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + float NumFrames = 60.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + float PlayRate = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + float bLooping = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + float GlobalStartTime = 0.0f; +}; + +USTRUCT(BlueprintType) +struct FAnimToTextureInstancePlaybackData +{ + GENERATED_USTRUCT_BODY() + + // Store prev state to allow blending of prev->current state in material + // Uncomment this if we start blending states + //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + //FAnimToTextureAnimState PrevState; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AnimToTexture") + FAnimToTextureAnimState CurrentState; +}; + +USTRUCT(BlueprintType) +struct FAnimToTextureAnimationSyncData +{ + GENERATED_USTRUCT_BODY() + + // The time used for sync when transitioning from skeletal mesh to material animated static mesh. + // World real time at the time of the transition + float SyncTime; +}; + +USTRUCT(BlueprintType) +struct FAnimToTextureInstanceData +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + TArray PlaybackData; + + UPROPERTY() + TArray StaticMeshInstanceData; +}; + +class UAnimToTextureDataAsset; + +UCLASS() +class ANIMTOTEXTURE_API UAnimToTextureInstancePlaybackLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static void SetupInstancedMeshComponent(UInstancedStaticMeshComponent* InstancedMeshComponent, UPARAM(ref) FAnimToTextureInstanceData& InstanceData, int32 NumInstances); + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static void BatchUpdateInstancedMeshComponent(UInstancedStaticMeshComponent* InstancedMeshComponent, UPARAM(ref) FAnimToTextureInstanceData& InstanceData); + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static void AllocateInstanceData(UPARAM(ref) FAnimToTextureInstanceData& InstanceData, int32 Count); + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static bool UpdateInstanceData(UPARAM(ref) FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, const FAnimToTextureInstancePlaybackData& PlaybackData, const FTransform& Transform); + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static bool GetInstancePlaybackData(UPARAM(ref) const FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, FAnimToTextureInstancePlaybackData& InstancePlaybackData); + + UFUNCTION(BlueprintCallable, Category = "Anim Texture Playback") + static bool GetInstanceTransform(UPARAM(ref) const FAnimToTextureInstanceData& InstanceData, int32 InstanceIndex, FTransform& InstanceTransform); + + UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Anim Texture Playback") + static bool AnimStateFromDataAsset(const UAnimToTextureDataAsset* DataAsset, int32 StateIndex, FAnimToTextureAnimState& AnimState); +}; diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Public/EvaluateSequenceAnimInstance.h b/Plugins/AnimToTexture/Source/AnimToTexture/Public/EvaluateSequenceAnimInstance.h new file mode 100644 index 00000000..7feac7b9 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Public/EvaluateSequenceAnimInstance.h @@ -0,0 +1,27 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Animation/AnimInstance.h" +#include "Animation/AnimSequence.h" +#include "EvaluateSequenceAnimInstance.generated.h" + +/** + * Simple anim instance with a few parameters to be used as inputs when driving it + * from UAnimToTextureBPLibrary::AnimationToTexture() + */ +UCLASS() +class ANIMTOTEXTURE_API UEvaluateSequenceAnimInstance : public UAnimInstance +{ + GENERATED_BODY() + +public: + UEvaluateSequenceAnimInstance(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + + UPROPERTY(Transient, EditAnywhere, BlueprintReadOnly) + UAnimSequence* SequenceToEvaluate = nullptr; + + UPROPERTY(Transient, EditAnywhere, BlueprintReadOnly) + float TimeToEvaluate = 0.0f; +}; + diff --git a/Plugins/AnimToTexture/Source/AnimToTexture/Public/LightweightMontageInstance.h b/Plugins/AnimToTexture/Source/AnimToTexture/Public/LightweightMontageInstance.h new file mode 100644 index 00000000..c5e67287 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTexture/Public/LightweightMontageInstance.h @@ -0,0 +1,94 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "UObject/WeakObjectPtrTemplates.h" + +class UAnimMontage; +class UAnimSequence; +struct FLightweightMontageInstance; + +enum class EMontageSubStepResult : uint8; + +namespace UE +{ + namespace VertexAnimation + { + struct FLightWeightMontageExtractionSettings + { + bool bExtractRootMotion = false; + }; + + struct FLightweightMontageInstance; + + struct FLightweightMontageSubstepper + { + void AddEvaluationTime(float InDeltaTime) { TimeRemaining += InDeltaTime; } + bool HasTimeRemaining() const { return (TimeRemaining > SMALL_NUMBER); } + bool HasReachedEndOfSection() const { return bReachedEndOfSection; } + float GetRemainingTime() const { return TimeRemaining; } + EMontageSubStepResult Advance(float& InOutTime, UAnimMontage* Montage); + int32 GetCurrentSectionIndex() const { return CurrentSectionIndex; } + float GetCurrentSectionLength() const { return CurrentSectionLength; } + float GetCurrentSectionStartTime() const { return CurrentSectionStartTime; } + float GetCurrentDeltaMove() const { return DeltaMove; } + + void Initialize(UE::VertexAnimation::FLightweightMontageInstance* InMontageInstance) + { + } + + float TimeRemaining = 0.0f; + float DeltaMove = 0.0f; + float CurrentSectionLength = 0.0f; + float CurrentSectionStartTime = 0.0f; + int32 CurrentSectionIndex = 0; + bool bReachedEndOfSection = false; + }; + + struct ANIMTOTEXTURE_API FLightweightMontageInstance + { + bool SequenceChangedThisFrame() const { return bSequenceChangedThisFrame; } + bool WasInitialized() const { return bInitialized; } + bool IsValid() const; + void Advance(float DeltaTime, float InGlobalTime, struct FRootMotionMovementParams& OutRootMotionParams, const FLightWeightMontageExtractionSettings& ExtractionSettings); + void Initialize(UAnimMontage* InMontage, float StartTime = 0.0f); + UAnimMontage* GetMontage() const; + const UAnimSequence* GetSequence() const; + float GetPositionInSection() const; + float GetPosition() const { return Position; } + float GetLength() const { return CachedLength; } + void Terminate(); + + private: + bool Advance_Internal(float DeltaTime, struct FRootMotionMovementParams& OutRootMotionParams, const FLightWeightMontageExtractionSettings& ExtractionSettings); + void RefreshNextPrevSections(UAnimMontage* InMontage); + void SetSequence(const UAnimSequence* InSequence); + const UAnimSequence* FindSequenceAtCurrentTime(float& SequenceStartTime); + + public: + + FRootMotionMovementParams RootMotionDelta; + + private: + + UE::VertexAnimation::FLightweightMontageSubstepper MontageSubStepper; + + // @todo : Handle asset references externally to avoid paying the cost of the weak ptr + TWeakObjectPtr MontageWeak = nullptr; + TWeakObjectPtr SequenceWeak = nullptr; + + // Transient. Gets cleared at the end of update + UAnimMontage* Montage = nullptr; + + TArray> NextSections; + float Position = 0.0f; + float CachedLength = 0.0f; + + // We need this since GetSegmentIndexAtTime and GetAnimCompositeSectionIndexFromPos can return different sections at the same frame + float SectionStartTimeTransient = 0.0f; + + bool bSequenceChangedThisFrame = false; + bool bInitialized = false; + }; + } +} \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/AnimToTextureEditor.Build.cs b/Plugins/AnimToTexture/Source/AnimToTextureEditor/AnimToTextureEditor.Build.cs new file mode 100644 index 00000000..1ec50541 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/AnimToTextureEditor.Build.cs @@ -0,0 +1,57 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class AnimToTextureEditor : ModuleRules +{ + public AnimToTextureEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "MaterialEditor" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "AnimToTexture", + "RawMesh", + "MeshDescription", + "UnrealEd", + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureBPLibrary.cpp b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureBPLibrary.cpp new file mode 100644 index 00000000..7b0f4325 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureBPLibrary.cpp @@ -0,0 +1,999 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTextureBPLibrary.h" +#include "AnimToTexture.h" +#include "AnimToTextureUtils.h" +#include "AnimToTextureSkeletalMesh.h" +#include "AnimToTextureDataAsset.h" +#include "EvaluateSequenceAnimInstance.h" + +#include "Editor.h" +#include "LevelEditor.h" +#include "RawMesh.h" +#include "MeshUtilities.h" +#include "AssetRegistryModule.h" +#include "Components/SkeletalMeshComponent.h" +#include "Components/SkinnedMeshComponent.h" +#include "Rendering/SkeletalMeshRenderData.h" +#include "Rendering/SkeletalMeshModel.h" +#include "Animation/AnimBlueprint.h" +#include "Animation/AnimSequence.h" +#include "Math/Vector.h" +#include "Math/NumericLimits.h" +#include "Engine/SkeletalMeshSocket.h" +#include "MeshDescription.h" +#include "Materials/MaterialInstanceConstant.h" +#include "MaterialEditingLibrary.h" +#include "EditorSupportDelegates.h" + +UAnimToTextureBPLibrary::UAnimToTextureBPLibrary(const FObjectInitializer& ObjectInitializer) +: Super(ObjectInitializer) +{ + +} + +void UAnimToTextureBPLibrary::AnimationToTexture(UAnimToTextureDataAsset* DataAsset, + const FTransform RootTransform, const bool bCreateTextures, const bool bCreateUVChannel) +{ + if (!DataAsset) + { + return; + } + + // Reset DataAsset Values + DataAsset->Reset(); + + if (!DataAsset->GetSkeletalMesh() || !DataAsset->GetStaticMesh()) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid Meshes")); + return; + } + + // Check LODs + if (!DataAsset->GetSkeletalMesh()->IsValidLODIndex(DataAsset->SkeletalLODIndex) || + DataAsset->StaticLODIndex >= DataAsset->GetStaticMesh()->GetNumLODs()) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid LOD Indices")); + return; + } + + // Check Socket. + bool bValidSocket = false; + if (DataAsset->AttachToSocket.IsValid() && !DataAsset->AttachToSocket.IsNone()) + { + if (AnimToTexture_Private::HasBone(DataAsset->GetSkeletalMesh(), DataAsset->AttachToSocket)) + { + bValidSocket = true; + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Invalid Socket: %s"), *DataAsset->AttachToSocket.ToString()); + return; + } + } + + if (bValidSocket && DataAsset->Mode == EAnimToTextureMode::Vertex) + { + UE_LOG(LogTemp, Warning, TEXT("Unable to use Socket in Vertex Mode. Use Bone Mode instead.")); + return; + } + + // --------------------------------------------------------------------------- + // Get Meshes Vertices and Mapping. + // NOTE: We need to create a Mapping between the StaticMesh|MeshDescription and the SkeletalMesh + // Since they dont have same number of points. + // + TArray Vertices; + TArray SkelVertices; + TArray StaticToSkelMapping; + + AnimToTexture_Private::GetStaticToSkeletalMapping( + *DataAsset->GetStaticMesh(), DataAsset->StaticLODIndex, + *DataAsset->GetSkeletalMesh(), DataAsset->SkeletalLODIndex, + Vertices, SkelVertices, + StaticToSkelMapping); + + // Set Static Number of Vertices + // NOTE: these are the MeshDescription Vertices. + const int32 NumVertices = Vertices.Num(); + + // -------------------------------------------------------------------------- + + // Create Temp Actor + UWorld* World = GEditor->GetEditorWorldContext().World(); + AActor* Actor = World->SpawnActor(); + + // Create Temp MasterMeshComponent + USkeletalMeshComponent* MasterMeshComponent = nullptr; + if (DataAsset->GetMasterSkeletalMesh()) + { + MasterMeshComponent = NewObject(Actor); + MasterMeshComponent->SetSkeletalMesh(DataAsset->GetMasterSkeletalMesh()); + MasterMeshComponent->SetForcedLOD(1); // Force to LOD0 + MasterMeshComponent->SetAnimationMode(EAnimationMode::AnimationSingleNode); + MasterMeshComponent->SetUpdateAnimationInEditor(true); + MasterMeshComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones; + MasterMeshComponent->RegisterComponent(); + }; + + // Create Temp SkeletalMesh Component + USkeletalMeshComponent* MeshComponent = NewObject(Actor); + MeshComponent->SetSkeletalMesh(DataAsset->GetSkeletalMesh()); + MeshComponent->SetForcedLOD(1); // Force to LOD0; + MeshComponent->RegisterComponent(); + + USkeletalMeshComponent* BaseMeshComponent = nullptr; + if (MasterMeshComponent) + { + MeshComponent->SetupAttachment(MasterMeshComponent); + MeshComponent->SetMasterPoseComponent(MasterMeshComponent); + + BaseMeshComponent = MasterMeshComponent; + } + else + { + MeshComponent->SetAnimationMode(EAnimationMode::AnimationSingleNode); + MeshComponent->SetUpdateAnimationInEditor(true); + MeshComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones; + + BaseMeshComponent = MeshComponent; + } + + // -------------------------------------------------------------------------- + // Set AnimBlueprint + UEvaluateSequenceAnimInstance* EvaluationAnimInstance = nullptr; + if (DataAsset->AnimInstanceClass != nullptr) + { + // Set Anim Blueprint + BaseMeshComponent->SetAnimInstanceClass(DataAsset->AnimInstanceClass); + EvaluationAnimInstance = Cast(BaseMeshComponent->GetAnimInstance()); + } + + // Get Mapping between Slave and Master + TArray SlaveBoneMap; + if (DataAsset->GetMasterSkeletalMesh() && + DataAsset->Mode == EAnimToTextureMode::Bone) + { + AnimToTexture_Private::GetSlaveBoneMap(DataAsset->GetMasterSkeletalMesh(), DataAsset->GetSkeletalMesh(), SlaveBoneMap); + } + + // --------------------------------------------------------------------------- + // Get Reference Skeleton Transforms + // + int32 NumBones = INDEX_NONE; + int32 SocketIndex = INDEX_NONE; + TArray BoneNames; + TArray BoneRefPositions; + TArray BoneRefRotations; + TArray BonePositions; + TArray BoneRotations; + + if (DataAsset->Mode == EAnimToTextureMode::Bone) + { + // Get Number of RawBones (no virtual) + NumBones = AnimToTexture_Private::GetNumBones(BaseMeshComponent->SkeletalMesh); + + // Get Raw Ref Bone (no virtual) + TArray RefBoneTransforms; + AnimToTexture_Private::GetRefBoneTransforms(BaseMeshComponent->SkeletalMesh, RefBoneTransforms); + AnimToTexture_Private::DecomposeTransformations(RefBoneTransforms, BoneRefPositions, BoneRefRotations); + + // Get Bone Names (no virtual) + AnimToTexture_Private::GetBoneNames(BaseMeshComponent->SkeletalMesh, BoneNames); + + // Make sure array sizes are correct. + check(RefBoneTransforms.Num() == NumBones && BoneNames.Num() == NumBones); + + // Check if Socket is in BoneNames + if (bValidSocket && !BoneNames.Find(DataAsset->AttachToSocket, SocketIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("Socket: %s not found in Raw Bone List"), *DataAsset->AttachToSocket.ToString()); + return; + } + + // Add RefPose + // Note: this is added in the first frame of the Bone Position and Rotation Textures + BonePositions.Append(BoneRefPositions); + BoneRotations.Append(BoneRefRotations); + } + + // --------------------------------------------------------------------------- + // Get Vertex Data (for all frames) + // + TArray VertexDeltas; + TArray VertexNormals; + + // Get Animation Frames Data + // + const float SampleInterval = 1.f / DataAsset->SampleRate; + + for (const FAnimSequenceInfo& AnimSequenceInfo : DataAsset->AnimSequences) + { + UAnimSequence* AnimSequence = AnimSequenceInfo.AnimSequence; + + if (!AnimSequenceInfo.bEnabled || !AnimSequence) + { + continue; + } + + // Make sure SkelMesh and AnimSequence use same Skeleton + if (AnimSequence->GetSkeleton() != BaseMeshComponent->SkeletalMesh->GetSkeleton()) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid AnimSequence: %s for given SkeletalMesh: %s"), *AnimSequence->GetFName().ToString(), *BaseMeshComponent->SkeletalMesh->GetFName().ToString()); + continue; + } + // Set AnimSequence + else + { + if (IsValid(EvaluationAnimInstance)) + { + EvaluationAnimInstance->SequenceToEvaluate = AnimSequence; + EvaluationAnimInstance->TimeToEvaluate = 0.0f; + } + else + { + BaseMeshComponent->SetAnimation(AnimSequence); + } + } + + // ----------------------------------------------------------------------------------- + // Get Number of Frames + // + int32 StartFrame; + int32 EndFrame; + + // Get Range from AnimSequence + if (!AnimSequenceInfo.bUseCustomRange) + { + StartFrame = 0; + EndFrame = AnimSequence->GetNumberOfSampledKeys() - 1; // AnimSequence->GetNumberOfFrames(); + } + // Get Range from DataAsset + else + { + StartFrame = AnimSequenceInfo.StartFrame; + EndFrame = AnimSequenceInfo.EndFrame; + } + if (EndFrame - StartFrame <= 0) + { + continue; + } + + // --------------------------------------------------------------------------- + // + float Time = 0.f; + float EndTime = AnimSequence->GetTimeAtFrame(EndFrame); + + int32 SampleIndex = 0; + + while (Time < EndTime) + { + Time = FMath::Clamp(SampleIndex * SampleInterval, 0.f, EndTime); + SampleIndex++; + + // Go To Time + if (IsValid(EvaluationAnimInstance)) + { + EvaluationAnimInstance->TimeToEvaluate = Time; + } + else + { + BaseMeshComponent->SetPosition(Time); + } + + // Update SkelMesh Animation. + BaseMeshComponent->TickAnimation(0.0f, false /*bNeedsValidRootMotion*/); + // BaseMeshComponent->TickComponent(0.0f, ELevelTick::LEVELTICK_All, nullptr); + BaseMeshComponent->RefreshBoneTransforms(nullptr /*TickFunction*/); + BaseMeshComponent->RefreshSlaveComponents(); + + // --------------------------------------------------------------------------- + // Store Vertex Deltas & Normals. + // + if (DataAsset->Mode == EAnimToTextureMode::Vertex) + { + TArray Positions; + TArray Normals; + AnimToTexture_Private::GetSkinnedVertices(MeshComponent, DataAsset->SkeletalLODIndex, + Positions, Normals); + + // Loop thru static vertices and find the mapped SkeletalMesh Vertex + for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex) + { + const int32 SkelVertexIndex = StaticToSkelMapping[VertexIndex]; + + // Delta & Normal + const FVector3f VertexDelta = (FVector3f)RootTransform.TransformPosition((FVector)Positions[SkelVertexIndex]) - SkelVertices[SkelVertexIndex]; + const FVector3f VertexNormal = (FVector3f)RootTransform.TransformVector((FVector)Normals[SkelVertexIndex]); + + VertexDeltas.Add(VertexDelta); + VertexNormals.Add(VertexNormal); + } + } // End Vertex Mode + + // --------------------------------------------------------------------------- + // Store Bone Positions & Rotations + // + else if (DataAsset->Mode == EAnimToTextureMode::Bone) + { + // Get Relative Transforms + // Note: Size is of Raw bones in SkeletalMesh. These are the original/raw bones of the asset, without Virtual Bones. + // Note: You cannot call CacheRefToLocalMatrices on a SlaveComponent + TArray RefToLocals; + BaseMeshComponent->CacheRefToLocalMatrices(RefToLocals); + + // check size + check(RefToLocals.Num() == NumBones); + + // Get Component Space Transforms + // Note returns all transforms, including VirtualBones + // Note: You cannot call GetComponentSpaceTransforms on a SlaveComponent + const TArray& CompSpaceTransforms = BaseMeshComponent->GetComponentSpaceTransforms(); + check(CompSpaceTransforms.Num() >= RefToLocals.Num()); + + for (int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex) + { + // Decompose Transformation (ComponentSpace) + const FTransform& CompSpaceTransform = CompSpaceTransforms[BoneIndex]; + FVector3f BonePosition; + FVector4 BoneRotation; + AnimToTexture_Private::DecomposeTransformation(CompSpaceTransform, BonePosition, BoneRotation); + + // Position Delta (from RefPose) + const FVector3f Delta = BonePosition - BoneRefPositions[BoneIndex]; + + // Decompose Transformation (Relative to RefPose) + FVector3f BoneRelativePosition; + FVector4 BoneRelativeRotation; + FMatrix RefToLocalMatrix = FMatrix(RefToLocals[BoneIndex]); + const FTransform RelativeTransform(RefToLocalMatrix); + AnimToTexture_Private::DecomposeTransformation(RelativeTransform, BoneRelativePosition, BoneRelativeRotation); + + BonePositions.Add(Delta); + BoneRotations.Add(BoneRelativeRotation); + } + } // End Bone Mode + } // End Frame + + // Store Anim Info Data + FAnimInfo AnimInfo; + AnimInfo.NumFrames = SampleIndex; + AnimInfo.AnimStart = DataAsset->NumFrames; + AnimInfo.bLooping = AnimSequenceInfo.bLooping; + DataAsset->Animations.Add(AnimInfo); + + // Accumulate Frames + DataAsset->NumFrames += AnimInfo.NumFrames; + } // End Anim + + // Destroy Temp Component + MeshComponent->UnregisterComponent(); + MeshComponent->DestroyComponent(); + + // Destroy Master Component + if (MasterMeshComponent) + { + MasterMeshComponent->UnregisterComponent(); + MasterMeshComponent->DestroyComponent(); + } + + Actor->Destroy(); + + // --------------------------------------------------------------------------- + // Nothing to do here ... + // + if (!DataAsset->NumFrames || !NumVertices) + { + return; + } + + // --------------------------------------------------------------------------- + if (DataAsset->Mode == EAnimToTextureMode::Vertex) + { + // Find Best Resolution for Vertex Data + int32 Height, Width; + if (!FindBestResolution(DataAsset->NumFrames, NumVertices, + Height, Width, DataAsset->VertexRowsPerFrame, + DataAsset->MaxHeight, DataAsset->MaxWidth, DataAsset->bEnforcePowerOfTwo)) + { + UE_LOG(LogTemp, Warning, TEXT("Vertex Animation data cannot be fit in a %ix%i texture."), DataAsset->MaxHeight, DataAsset->MaxWidth); + return; + } + + // Normalize Vertex Data + TArray NormalizedVertexDeltas; + TArray NormalizedVertexNormals; + NormalizeVertexData( + VertexDeltas, VertexNormals, + DataAsset->VertexMinBBox, DataAsset->VertexSizeBBox, + NormalizedVertexDeltas, NormalizedVertexNormals); + + // Write Textures + if (bCreateTextures) + { + AnimToTexture_Private::WriteVectorsToTexture(NormalizedVertexDeltas, DataAsset->NumFrames, DataAsset->VertexRowsPerFrame, Height, Width, DataAsset->GetVertexPositionTexture()); + AnimToTexture_Private::WriteVectorsToTexture(NormalizedVertexNormals, DataAsset->NumFrames, DataAsset->VertexRowsPerFrame, Height, Width, DataAsset->GetVertexNormalTexture()); + } + + // Add Vertex UVChannel + if (bCreateUVChannel) + { + CreateUVChannel(DataAsset->GetStaticMesh(), DataAsset->StaticLODIndex, DataAsset->UVChannel, + Height, Width); + } + } + + // --------------------------------------------------------------------------- + + if (DataAsset->Mode == EAnimToTextureMode::Bone) + { + // Find Best Resolution for Bone Data + int32 Height, Width; + + // NOTE: If NumBones are > 256, you might need to use MasterPose + + // Note we are adding +1 frame for the ref pose + if (!FindBestResolution(DataAsset->NumFrames + 1, NumBones, + Height, Width, DataAsset->BoneRowsPerFrame, + DataAsset->MaxHeight, DataAsset->MaxWidth, DataAsset->bEnforcePowerOfTwo)) + { + UE_LOG(LogTemp, Warning, TEXT("Bone Animation data cannot be fit in a %ix%i texture."), DataAsset->MaxHeight, DataAsset->MaxWidth); + return; + } + + // Write Bone Position and Rotation Textures + if (bCreateTextures) + { + // Normalize Bone Data + TArray NormalizedBonePositions; + TArray NormalizedBoneRotations; + NormalizeBoneData( + BonePositions, BoneRotations, + DataAsset->BoneMinBBox, DataAsset->BoneSizeBBox, + NormalizedBonePositions, NormalizedBoneRotations); + + // Write Textures + if (DataAsset->PositionAndRotationPrecision == EAnimToTextureBonePrecision::SixteenBits) + { + AnimToTexture_Private::WriteVectorsToTexture(NormalizedBonePositions, DataAsset->NumFrames + 1, DataAsset->BoneRowsPerFrame, Height, Width, DataAsset->GetBonePositionTexture()); + AnimToTexture_Private::WriteVectorsToTexture(NormalizedBoneRotations, DataAsset->NumFrames + 1, DataAsset->BoneRowsPerFrame, Height, Width, DataAsset->GetBoneRotationTexture()); + } + else + { + AnimToTexture_Private::WriteVectorsToTexture(NormalizedBonePositions, DataAsset->NumFrames + 1, DataAsset->BoneRowsPerFrame, Height, Width, DataAsset->GetBonePositionTexture()); + AnimToTexture_Private::WriteVectorsToTexture(NormalizedBoneRotations, DataAsset->NumFrames + 1, DataAsset->BoneRowsPerFrame, Height, Width, DataAsset->GetBoneRotationTexture()); + } + } + + // --------------------------------------------------------------------------- + + // Find Best Resolution for Weights Data + if (!FindBestResolution(2, NumVertices, + Height, Width, DataAsset->BoneWeightRowsPerFrame, + DataAsset->MaxHeight, DataAsset->MaxWidth, DataAsset->bEnforcePowerOfTwo)) + { + UE_LOG(LogTemp, Warning, TEXT("Weights Data cannot be fit in a %ix%i texture."), DataAsset->MaxHeight, DataAsset->MaxWidth); + return; + } + + // Write Weights Texture + if (bCreateTextures) + { + TArray> SkinWeights; + + // Store Influence Weights + if (!bValidSocket) + { + // Get SkinWeights mapped to StaticMesh + AnimToTexture_Private::GetReducedSkinWeightsData( + *DataAsset->GetSkeletalMesh(), DataAsset->SkeletalLODIndex, + StaticToSkelMapping, + SkinWeights); + + // Remap MeshBoneIndices to SlaveBoneMap + if (DataAsset->GetMasterSkeletalMesh()) + { + for (AnimToTexture_Private::TVertexSkinWeight<4>& SkinWeight : SkinWeights) + { + SkinWeight.MeshBoneIndices[0] = SlaveBoneMap[SkinWeight.MeshBoneIndices[0]]; + SkinWeight.MeshBoneIndices[1] = SlaveBoneMap[SkinWeight.MeshBoneIndices[1]]; + SkinWeight.MeshBoneIndices[2] = SlaveBoneMap[SkinWeight.MeshBoneIndices[2]]; + SkinWeight.MeshBoneIndices[3] = SlaveBoneMap[SkinWeight.MeshBoneIndices[3]]; + } + } + } + // If Valid Socket, set all influences to same index. + else + { + // Set all indices and weights to same SocketIndex + SkinWeights.SetNumUninitialized(NumVertices); + for (AnimToTexture_Private::TVertexSkinWeight<4>& SkinWeight : SkinWeights) + { + SkinWeight.BoneWeights = TStaticArray(InPlace, 255); + SkinWeight.MeshBoneIndices = TStaticArray(InPlace, SocketIndex); + } + } + + // Write Bone Weights Texture + AnimToTexture_Private::WriteSkinWeightsToTexture(SkinWeights, + DataAsset->BoneWeightRowsPerFrame, Height, Width, DataAsset->GetBoneWeightTexture()); + } + + // Add Vertex UVChannel + if (bCreateUVChannel) + { + CreateUVChannel(DataAsset->GetStaticMesh(), DataAsset->StaticLODIndex, DataAsset->UVChannel, + Height, Width); + } + } + + // --------------------------------------------------------------------------- + // Mark DataAsset as Dirty. + // + DataAsset->MarkPackageDirty(); +} + + +void UAnimToTextureBPLibrary::UpdateMaterialInstanceFromDataAsset(UAnimToTextureDataAsset* DataAsset, UMaterialInstanceConstant* MaterialInstance, + const FAnimToTextureMaterialParamNames& ParamNames, const EMaterialParameterAssociation MaterialParameterAssociation) +{ + if (!MaterialInstance || !DataAsset) + { + return; + } + + // Set Preview Mesh + if (DataAsset->GetStaticMesh()) + { + MaterialInstance->PreviewMesh = DataAsset->GetStaticMesh(); + } + + if (DataAsset->Mode == EAnimToTextureMode::Vertex) + { + FLinearColor VectorParameter; + VectorParameter = FLinearColor(DataAsset->VertexMinBBox); + UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(MaterialInstance, ParamNames.BoundingBoxMin, VectorParameter, MaterialParameterAssociation); + + VectorParameter = FLinearColor(DataAsset->VertexSizeBBox); + UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(MaterialInstance, ParamNames.BoundingBoxScale, VectorParameter, MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(MaterialInstance, ParamNames.NumFrames, DataAsset->NumFrames, MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(MaterialInstance, ParamNames.RowsPerFrame, DataAsset->VertexRowsPerFrame, MaterialParameterAssociation); + + UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(MaterialInstance, ParamNames.VertexPositionTexture, DataAsset->GetVertexPositionTexture(), MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(MaterialInstance, ParamNames.VertexNormalTexture, DataAsset->GetVertexNormalTexture(), MaterialParameterAssociation); + + } + else if (DataAsset->Mode == EAnimToTextureMode::Bone) + { + FLinearColor VectorParameter; + VectorParameter = FLinearColor(DataAsset->BoneMinBBox); + UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(MaterialInstance, ParamNames.BoundingBoxMin, VectorParameter, MaterialParameterAssociation); + + VectorParameter = FLinearColor(DataAsset->BoneSizeBBox); + UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(MaterialInstance, ParamNames.BoundingBoxScale, VectorParameter, MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(MaterialInstance, ParamNames.NumFrames, DataAsset->NumFrames, MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(MaterialInstance, ParamNames.RowsPerFrame, DataAsset->BoneRowsPerFrame, MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(MaterialInstance, ParamNames.BoneWeightRowsPerFrame, DataAsset->BoneWeightRowsPerFrame, MaterialParameterAssociation); + + UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(MaterialInstance, ParamNames.BonePositionTexture, DataAsset->GetBonePositionTexture(), MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(MaterialInstance, ParamNames.BoneRotationTexture, DataAsset->GetBoneRotationTexture(), MaterialParameterAssociation); + UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(MaterialInstance, ParamNames.BoneWeightsTexture, DataAsset->GetBoneWeightTexture(), MaterialParameterAssociation); + } + + // Update Material + UMaterialEditingLibrary::UpdateMaterialInstance(MaterialInstance); + + // Rebuild Material + UMaterialEditingLibrary::RebuildMaterialInstanceEditors(MaterialInstance->GetMaterial()); +} + +void UAnimToTextureBPLibrary::UpdateMaterialLayerFunction( + UMaterialInstanceConstant* MaterialInstance, + UMaterialFunctionInterface* OldMaterialFunction, + UMaterialFunctionInterface* NewMaterialFunction) +{ + if (!IsValid(MaterialInstance)) + { + return; + } + + const FMaterialParameterInfo ParameterInfo(TEXT("BoneAnimation"), EMaterialParameterAssociation::GlobalParameter); + + FMaterialLayersFunctions LayersValue; + FGuid TempGuid(0, 0, 0, 0); + + // TODO: Ben.Ingram / Cesar.Castro verify this. + bool bMaterialLayerReplaced = false; + if (MaterialInstance->GetMaterialLayers(LayersValue)) + { + for (int32 LayerIdx = 0; LayerIdx < LayersValue.Layers.Num(); ++LayerIdx) + { + TObjectPtr& MaterialFunction = LayersValue.Layers[LayerIdx]; + if (MaterialFunction.Get() == OldMaterialFunction) + { + MaterialFunction = NewMaterialFunction; + LayersValue.LayerLinkStates[LayerIdx] = EMaterialLayerLinkState::UnlinkedFromParent; + bMaterialLayerReplaced = true; + break; + } + + } + } + + if (bMaterialLayerReplaced) + { + // TODO: Ben.Ingram / Cesar.Castro verify this. + MaterialInstance->SetMaterialLayers(LayersValue); + + // Update Material + UMaterialEditingLibrary::UpdateMaterialInstance(MaterialInstance); + + // Rebuild Material + UMaterialEditingLibrary::RebuildMaterialInstanceEditors(MaterialInstance->GetMaterial()); + } +} + + +UStaticMesh* UAnimToTextureBPLibrary::ConvertSkeletalMeshToStaticMesh(USkeletalMesh* SkeletalMesh, const FString PackageName, const int32 LODIndex) +{ + if (!SkeletalMesh || PackageName.IsEmpty()) + { + return nullptr; + } + + if (!FPackageName::IsValidObjectPath(PackageName)) + { + return nullptr; + } + + if (LODIndex >= 0 && !SkeletalMesh->IsValidLODIndex(LODIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid LODIndex: %i"), LODIndex); + return nullptr; + } + + // Create Temp Actor + UWorld* World = GEditor->GetEditorWorldContext().World(); + AActor* Actor = World->SpawnActor(); + + // Create Temp SkeletalMesh Component + USkeletalMeshComponent* MeshComponent = NewObject(Actor); + MeshComponent->RegisterComponent(); + MeshComponent->SetSkeletalMesh(SkeletalMesh); + TArray MeshComponents = { MeshComponent }; + + UStaticMesh* OutStaticMesh = nullptr; + bool bGeneratedCorrectly = true; + + // Create New StaticMesh + if (!FPackageName::DoesPackageExist(PackageName)) + { + IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked("MeshUtilities"); + OutStaticMesh = MeshUtilities.ConvertMeshesToStaticMesh(MeshComponents, FTransform::Identity, PackageName); + } + // Update Existing StaticMesh + else + { + // Load Existing Mesh + OutStaticMesh = LoadObject(nullptr, *PackageName); + } + + if (OutStaticMesh) + { + // Create Temp Package. + // because + UPackage* TransientPackage = GetTransientPackage(); + + // Create Temp Mesh. + IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked("MeshUtilities"); + UStaticMesh* TempMesh = MeshUtilities.ConvertMeshesToStaticMesh(MeshComponents, FTransform::Identity, TransientPackage->GetPathName()); + + // make sure transactional flag is on + TempMesh->SetFlags(RF_Transactional); + + // Copy All LODs + if (LODIndex < 0) + { + const int32 NumSourceModels = TempMesh->GetNumSourceModels(); + OutStaticMesh->SetNumSourceModels(NumSourceModels); + + for (int32 Index = 0; Index < NumSourceModels; ++Index) + { + // Get RawMesh + FRawMesh RawMesh; + TempMesh->GetSourceModel(Index).LoadRawMesh(RawMesh); + + // Set RawMesh + OutStaticMesh->GetSourceModel(Index).SaveRawMesh(RawMesh); + }; + } + + // Copy Single LOD + else + { + if (LODIndex >= TempMesh->GetNumSourceModels()) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid Source Model Index: %i"), LODIndex); + bGeneratedCorrectly = false; + } + else + { + OutStaticMesh->SetNumSourceModels(1); + + // Get RawMesh + FRawMesh RawMesh; + TempMesh->GetSourceModel(LODIndex).LoadRawMesh(RawMesh); + + // Set RawMesh + OutStaticMesh->GetSourceModel(0).SaveRawMesh(RawMesh); + } + } + + // Copy Materials + const TArray& Materials = TempMesh->GetStaticMaterials(); + OutStaticMesh->SetStaticMaterials(Materials); + + // Done + TArray OutErrors; + OutStaticMesh->Build(true, &OutErrors); + OutStaticMesh->MarkPackageDirty(); + } + + // Destroy Temp Component and Actor + MeshComponent->UnregisterComponent(); + MeshComponent->DestroyComponent(); + Actor->Destroy(); + + return bGeneratedCorrectly ? OutStaticMesh : nullptr; +} + + +void UAnimToTextureBPLibrary::NormalizeVertexData( + const TArray& Deltas, const TArray& Normals, + FVector& MinBBox, FVector& SizeBBox, + TArray& NormalizedDeltas, TArray& NormalizedNormals) +{ + check(Deltas.Num() == Normals.Num()); + + // --------------------------------------------------------------------------- + // Compute Bounding Box + // + MinBBox = { TNumericLimits::Max(), TNumericLimits::Max(), TNumericLimits::Max() }; + FVector3f MaxBBox = { TNumericLimits::Min(), TNumericLimits::Min(), TNumericLimits::Min() }; + + for (const FVector3f& Delta: Deltas) + { + // Find Min/Max BoundingBox + MinBBox.X = FMath::Min(Delta.X, MinBBox.X); + MinBBox.Y = FMath::Min(Delta.Y, MinBBox.Y); + MinBBox.Z = FMath::Min(Delta.Z, MinBBox.Z); + + MaxBBox.X = FMath::Max(Delta.X, MaxBBox.X); + MaxBBox.Y = FMath::Max(Delta.Y, MaxBBox.Y); + MaxBBox.Z = FMath::Max(Delta.Z, MaxBBox.Z); + } + + SizeBBox = (FVector)MaxBBox - MinBBox; + + // --------------------------------------------------------------------------- + // Normalize Vertex Position Deltas + // Basically we want all deltas to be between [0, 1] + + // Compute Normalization Factor per-axis. + const FVector NormFactor = { + 1.0f / static_cast(SizeBBox.X), + 1.0f / static_cast(SizeBBox.Y), + 1.0f / static_cast(SizeBBox.Z) }; + + NormalizedDeltas.SetNumUninitialized(Deltas.Num()); + for (int32 Index = 0; Index < Deltas.Num(); ++Index) + { + NormalizedDeltas[Index] = (FVector3f)(((FVector)Deltas[Index] - MinBBox) * NormFactor); + } + + // --------------------------------------------------------------------------- + // Normalize Vertex Normals + // And move them to [0, 1] + + NormalizedNormals.SetNumUninitialized(Normals.Num()); + for (int32 Index = 0; Index < Normals.Num(); ++Index) + { + NormalizedNormals[Index] = (Normals[Index].GetSafeNormal() + FVector3f::OneVector) * 0.5f; + } + +} + +void UAnimToTextureBPLibrary::NormalizeBoneData( + const TArray& Positions, const TArray& Rotations, + FVector& MinBBox, FVector& SizeBBox, + TArray& NormalizedPositions, TArray& NormalizedRotations) +{ + check(Positions.Num() == Rotations.Num()); + + // --------------------------------------------------------------------------- + // Compute Position Bounding Box + // + MinBBox = { TNumericLimits::Max(), TNumericLimits::Max(), TNumericLimits::Max() }; + FVector3f MaxBBox = { TNumericLimits::Min(), TNumericLimits::Min(), TNumericLimits::Min() }; + + for (const FVector3f& Position : Positions) + { + // Find Min/Max BoundingBox + MinBBox.X = FMath::Min(Position.X, MinBBox.X); + MinBBox.Y = FMath::Min(Position.Y, MinBBox.Y); + MinBBox.Z = FMath::Min(Position.Z, MinBBox.Z); + + MaxBBox.X = FMath::Max(Position.X, MaxBBox.X); + MaxBBox.Y = FMath::Max(Position.Y, MaxBBox.Y); + MaxBBox.Z = FMath::Max(Position.Z, MaxBBox.Z); + } + + SizeBBox = (FVector)MaxBBox - MinBBox; + + // --------------------------------------------------------------------------- + // Normalize Bone Position. + // Basically we want all positions to be between [0, 1] + + // Compute Normalization Factor per-axis. + const FVector NormFactor = { + 1.0f / static_cast(SizeBBox.X), + 1.0f / static_cast(SizeBBox.Y), + 1.0f / static_cast(SizeBBox.Z) }; + + NormalizedPositions.SetNumUninitialized(Positions.Num()); + for (int32 Index = 0; Index < Positions.Num(); ++Index) + { + NormalizedPositions[Index] = FVector3f(((FVector)Positions[Index] - MinBBox) * NormFactor); + } + + // --------------------------------------------------------------------------- + // Normalize Rotations + // And move them to [0, 1] + NormalizedRotations.SetNumUninitialized(Rotations.Num()); + for (int32 Index = 0; Index < Rotations.Num(); ++Index) + { + const FVector4 Axis = Rotations[Index]; + const float Angle = Rotations[Index].W; // Angle are returned in radians and they go from [0-pi*2] + + NormalizedRotations[Index] = (Axis.GetSafeNormal() + FVector::OneVector) * 0.5f; + NormalizedRotations[Index].W = Angle / (PI * 2.f); + } +} + + +bool UAnimToTextureBPLibrary::CreateUVChannel( + UStaticMesh* StaticMesh, const int32 LODIndex, const int32 UVChannelIndex, + const int32 Height, const int32 Width) +{ + if (!StaticMesh) + { + return false; + } + + // Get Render Data + const FStaticMeshRenderData* RenderData = StaticMesh->GetRenderData(); + check(RenderData); + + // Get LOD Data + if (!RenderData->LODResources.IsValidIndex(LODIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid LODIndex: %i"), LODIndex); + return false; + } + + // ---------------------------------------------------------------------------- + // Get Mesh Description. + // This is needed for Inserting UVChannel + FMeshDescription* MeshDescription = StaticMesh->GetMeshDescription(LODIndex); + check(MeshDescription); + + // Check if UVChannel is being used by the Lightmap UV + /*if (StaticMesh->GetLightMapCoordinateIndex() == UVChannelIndex) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid UVChannel: %i. Already used by LightMap"), UVChannelIndex); + return false; + }*/ + + // Add New UVChannel. + // UE_LOG(LogTemp, Warning, TEXT("UVChannel: %i. Number of existing UVChannels: %i"), UVChannelIndex, StaticMesh->GetNumUVChannels(LODIndex)); + if (UVChannelIndex == StaticMesh->GetNumUVChannels(LODIndex)) + { + if (!StaticMesh->InsertUVChannel(LODIndex, UVChannelIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("Unable to Add UVChannel")); + return false; + } + } + else if (UVChannelIndex > StaticMesh->GetNumUVChannels(LODIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("UVChannel: %i Out of Range. Number of existing UVChannels: %i"), UVChannelIndex, StaticMesh->GetNumUVChannels(LODIndex)); + return false; + } + + // ----------------------------------------------------------------------------- + + TMap TexCoords; + + for (const FVertexInstanceID VertexInstanceID : MeshDescription->VertexInstances().GetElementIDs()) + { + const FVertexID VertexID = MeshDescription->GetVertexInstanceVertex(VertexInstanceID); + const int32 VertexIndex = VertexID.GetValue(); + + float U = (0.5f / (float)Width) + (VertexIndex % Width) / (float)Width; + float V = (0.5f / (float)Height) + (VertexIndex / Width) / (float)Height; + + TexCoords.Add(VertexInstanceID, FVector2D(U, V)); + } + + // Set Full Precision UVs + SetFullPrecisionUVs(StaticMesh, true); + + if (StaticMesh->SetUVChannel(LODIndex, UVChannelIndex, TexCoords)) + { + // Update and Mark to Save. + StaticMesh->MarkPackageDirty(); + + return true; + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Unable to Set UVChannel: %i. TexCoords: %i"), UVChannelIndex, TexCoords.Num()); + return false; + }; + + return false; +} + +bool UAnimToTextureBPLibrary::FindBestResolution( + const int32 NumFrames, const int32 NumElements, + int32& OutHeight, int32& OutWidth, int32& OutRowsPerFrame, + const int32 MaxHeight, const int32 MaxWidth, bool bEnforcePowerOfTwo) +{ + if (bEnforcePowerOfTwo) + { + OutWidth = 2; + while (OutWidth < NumElements && OutWidth < MaxWidth) + { + OutWidth *= 2; + } + OutRowsPerFrame = FMath::CeilToInt(NumElements / (float)OutWidth); + + const int32 TargetHeight = NumFrames * OutRowsPerFrame; + OutHeight = 2; + while (OutHeight < TargetHeight) + { + OutHeight *= 2; + } + } + else + { + OutRowsPerFrame = FMath::CeilToInt(NumElements / (float)MaxWidth); + OutWidth = FMath::CeilToInt(NumElements / (float)OutRowsPerFrame); + OutHeight = NumFrames * OutRowsPerFrame; + } + + const bool bValidResolution = OutWidth <= MaxWidth && OutHeight <= MaxHeight; + return bValidResolution; +}; + +void UAnimToTextureBPLibrary::SetFullPrecisionUVs(UStaticMesh* StaticMesh, bool bFullPrecision) +{ + int32 NumSourceModels = StaticMesh->GetNumSourceModels(); + for (int32 LodIndex = 0; LodIndex < NumSourceModels; LodIndex++) + { + FStaticMeshSourceModel& SourceModel = StaticMesh->GetSourceModel(LodIndex); + SourceModel.BuildSettings.bUseFullPrecisionUVs = bFullPrecision; + } +} + +void UAnimToTextureBPLibrary::SetStaticMeshBoundsExtensions( + UStaticMesh* StaticMesh, + const FVector& PositiveBoundsExtension, + const FVector& NegativeBoundsExtension) +{ + if (IsValid(StaticMesh)) + { + StaticMesh->SetPositiveBoundsExtension(PositiveBoundsExtension); + StaticMesh->SetNegativeBoundsExtension(NegativeBoundsExtension); + } +} diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureEditor.cpp b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureEditor.cpp new file mode 100644 index 00000000..2de19458 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureEditor.cpp @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTextureEditor.h" + +#define LOCTEXT_NAMESPACE "FAnimToTextureEditorModule" + +void FAnimToTextureEditorModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + +} + +void FAnimToTextureEditorModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FAnimToTextureEditorModule, AnimToTextureEditor) \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureSkeletalMesh.cpp b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureSkeletalMesh.cpp new file mode 100644 index 00000000..ed0c83d0 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureSkeletalMesh.cpp @@ -0,0 +1,446 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#include "AnimToTextureSkeletalMesh.h" +#include "Rendering/SkeletalMeshModel.h" +#include "Rendering/SkeletalMeshRenderData.h" +#include "Engine/StaticMesh.h" +#include "Engine/SkeletalMesh.h" +#include "Components/SkeletalMeshComponent.h" +#include "MeshDescription.h" +#include "BoneIndices.h" + +namespace AnimToTexture_Private +{ + +int32 GetVertices(const USkeletalMesh& Mesh, const int32 LODIndex, + TArray& OutPositions, TArray& OutNormals) +{ + OutPositions.Reset(); + OutNormals.Reset(); + + const FSkeletalMeshRenderData* RenderData = Mesh.GetResourceForRendering(); + check(RenderData); + + if (!RenderData->LODRenderData.IsValidIndex(LODIndex)) + { + return INDEX_NONE; + } + + // Get LOD Data + const FSkeletalMeshLODRenderData& LODRenderData = RenderData->LODRenderData[LODIndex]; + + // Get Total Num of Vertices (for all sections) + const int32 NumVertices = LODRenderData.GetNumVertices(); + OutPositions.SetNumUninitialized(NumVertices); + OutNormals.SetNumUninitialized(NumVertices); + + for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex) + { + OutPositions[VertexIndex] = LODRenderData.StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex); + OutNormals[VertexIndex] = LODRenderData.StaticVertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(VertexIndex); + }; + + return NumVertices; +} + +int32 FindClosestVertex(const FVector3f& Point, const TArray& Vertices) +{ + float MinDistance = TNumericLimits::Max(); + int32 ClosestIndex = INDEX_NONE; + + // Find Closest SkeletalMesh Vertex. + for (int32 VertexIndex = 0; VertexIndex < Vertices.Num(); ++VertexIndex) + { + const float Distance = FVector3f::Dist(Point, Vertices[VertexIndex]); + if (Distance < MinDistance) + { + MinDistance = Distance; + ClosestIndex = VertexIndex; + } + } + + return ClosestIndex; +} + +void GetStaticToSkeletalMapping( + const UStaticMesh& StaticMesh, const int32 StaticLODIndex, + const USkeletalMesh& SkeletalMesh, const int32 SkeletalLODIndex, + TArray& OutStaticVertices, TArray& OutSkeletalVertices, + TArray& OutStaticToSkeletalMapping) +{ + // Reset + OutStaticVertices.Reset(); + OutSkeletalVertices.Reset(); + OutStaticToSkeletalMapping.Reset(); + + // Get Mesh Description + const FMeshDescription* MeshDescription = StaticMesh.GetMeshDescription(StaticLODIndex); + check(MeshDescription); + + // Get SkeletalMesh Vertices + TArray Normals; + const int32 SkelNumVertices = AnimToTexture_Private::GetVertices(SkeletalMesh, SkeletalLODIndex, OutSkeletalVertices, Normals); + + // Get MeshDescription Vertices + const FVertexArray& Vertices = MeshDescription->Vertices(); + const int32 NumVertices = Vertices.Num(); + OutStaticVertices.SetNum(NumVertices); + OutStaticToSkeletalMapping.SetNum(NumVertices); + + for (const FVertexID VertexID : Vertices.GetElementIDs()) + { + const int32 VertexIndex = VertexID.GetValue(); + + // Get Mesh Description Vertex Position + const FVector3f Vertex = MeshDescription->GetVertexPosition(VertexID); + OutStaticVertices[VertexIndex] = Vertex; + + // Find Closest SkeletalMesh Vertex + OutStaticToSkeletalMapping[VertexIndex] = FindClosestVertex(Vertex, OutSkeletalVertices); + } +} + +int32 GetNumBones(const USkeletalMesh* SkeletalMesh) +{ + check(SkeletalMesh); + + const FReferenceSkeleton& RefSkeleton = SkeletalMesh->GetRefSkeleton(); + return RefSkeleton.GetRawBoneNum(); +} + +/** +* Copied from FAnimationRuntime::GetComponentSpaceTransform +*/ +void GetRefBoneTransforms(const USkeletalMesh* SkeletalMesh, TArray& Transforms) +{ + check(SkeletalMesh); + Transforms.Reset(); + + // Get Reference Skeleton + const FReferenceSkeleton& RefSkeleton = SkeletalMesh->GetRefSkeleton(); + const TArray& BoneSpaceTransforms = RefSkeleton.GetRawRefBonePose(); // Get only raw bones (no virtual) + + const int32 NumTransforms = BoneSpaceTransforms.Num(); + Transforms.SetNumUninitialized(NumTransforms); + + for (int32 BoneIndex = 0; BoneIndex < NumTransforms; ++BoneIndex) + { + // initialize to identity since some of them don't have tracks + int32 IterBoneIndex = BoneIndex; + FTransform ComponentSpaceTransform = BoneSpaceTransforms[BoneIndex]; + + do + { + //const int32 ParentIndex = RefSkeleton.GetParentIndex(IterBoneIndex); + const int32 ParentIndex = RefSkeleton.GetRawParentIndex(IterBoneIndex); // Get only raw bones (no virtual) + if (ParentIndex != INDEX_NONE) + { + ComponentSpaceTransform *= BoneSpaceTransforms[ParentIndex]; + } + + IterBoneIndex = ParentIndex; + } while (RefSkeleton.IsValidIndex(IterBoneIndex)); + + // + Transforms[BoneIndex] = ComponentSpaceTransform; + } +} + +bool HasBone(const USkeletalMesh* SkeletalMesh, const FName& Bone) +{ + check(SkeletalMesh); + + const FReferenceSkeleton& RefSkeleton = SkeletalMesh->GetRefSkeleton(); + + for (const FMeshBoneInfo& BoneInfo: RefSkeleton.GetRawRefBoneInfo()) + { + if (BoneInfo.Name == Bone) + { + return true; + } + } + return false; +} + + +void GetBoneNames(const USkeletalMesh* SkeletalMesh, TArray& OutNames) +{ + check(SkeletalMesh); + OutNames.Reset(); + + const FReferenceSkeleton& RefSkeleton = SkeletalMesh->GetRefSkeleton(); + + const int32 NumRawBones = RefSkeleton.GetRawBoneNum(); + OutNames.SetNumUninitialized(NumRawBones); + + for (int32 BoneIndex = 0; BoneIndex < NumRawBones; ++BoneIndex) + { + OutNames[BoneIndex] = RefSkeleton.GetBoneName(BoneIndex); + } +}; + +void GetSkinnedVertices(USkeletalMeshComponent* MeshComponent, const int32 LODIndex, + TArray& OutPositions, TArray& OutNormals) +{ + OutPositions.Reset(); + OutNormals.Reset(); + + if (!MeshComponent) + { + return; + } + + // Get SkeletalMesh + USkeletalMesh* SkeletalMesh = MeshComponent->SkeletalMesh; + if (!SkeletalMesh) + { + return; + } + + // Get Render Data + FSkeletalMeshRenderData* RenderData = SkeletalMesh->GetResourceForRendering(); + if (!RenderData->LODRenderData.IsValidIndex(LODIndex)) + { + return; + }; + + // Get Matrices + TArray RefToLocals; + MeshComponent->CacheRefToLocalMatrices(RefToLocals); + + // --------------------------------------------------------------------------------------------- + + // Get Ref-Pose Vertices + TArray Vertices; + TArray Normals; + const int32 NumVertices = GetVertices(*SkeletalMesh, LODIndex, Vertices, Normals); + + // TODO: Add Morph Deltas to Vertices. + + // Get Weights + TArray> SkinWeights; + GetSkinWeightsData(*SkeletalMesh, LODIndex, SkinWeights); + + OutPositions.SetNumUninitialized(NumVertices); + OutNormals.SetNumUninitialized(NumVertices); + + for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex) + { + const FVector3f& Vertex = Vertices[VertexIndex]; + const FVector3f& Normal = Normals[VertexIndex]; + const TVertexSkinWeight& Weights = SkinWeights[VertexIndex]; + + FVector4f SkinnedVertex(0); + FVector4f SkinnedNormal(0); + + for (int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; InfluenceIndex++) + { + const uint8& BoneWeight = Weights.BoneWeights[InfluenceIndex]; + const uint16& MeshBoneIndex = Weights.MeshBoneIndices[InfluenceIndex]; + + // Get Matrix + const FMatrix44f& RefToLocal = RefToLocals[MeshBoneIndex]; + + const float Weight = (float)BoneWeight / 255.f; + SkinnedVertex += RefToLocal.TransformPosition(Vertex) * Weight; + SkinnedNormal += RefToLocal.TransformVector(Normal) * Weight; + } + + OutPositions[VertexIndex] = SkinnedVertex; + OutNormals[VertexIndex] = SkinnedNormal; + }; + +}; + +void ReduceSkinWeightsData(const TArray>& InSkinWeights, TArray>& OutSkinWeights) +{ + OutSkinWeights.SetNumUninitialized(InSkinWeights.Num()); + + for (int32 VertexIndex = 0; VertexIndex < InSkinWeights.Num(); ++VertexIndex) + { + GetMaxFourInfluences(InSkinWeights[VertexIndex], OutSkinWeights[VertexIndex]); + } +} + +void GetSlaveBoneMap(const USkeletalMesh* MasterSkeletalMesh, const USkeletalMesh* SlaveSkeletalMesh, TArray& SlaveBoneMap) +{ + const TArray& MasterBoneInfo = MasterSkeletalMesh->GetRefSkeleton().GetRawRefBoneInfo(); + const TArray& SlaveBoneInfo = SlaveSkeletalMesh->GetRefSkeleton().GetRawRefBoneInfo(); + + // Initialize Array + const int32 MasterNumBones = MasterBoneInfo.Num(); + const int32 SlaveNumBones = SlaveBoneInfo.Num(); + SlaveBoneMap.Init(INDEX_NONE, SlaveNumBones); + + for (int32 SlaveBoneIndex = 0; SlaveBoneIndex < SlaveNumBones; ++SlaveBoneIndex) + { + // Search by name-matching + for (int32 MasterBoneIndex = 0; MasterBoneIndex < MasterNumBones; ++MasterBoneIndex) + { + // found by name-matching + if (SlaveBoneInfo[SlaveBoneIndex].Name == MasterBoneInfo[MasterBoneIndex].Name) + { + SlaveBoneMap[SlaveBoneIndex] = MasterBoneIndex; + break; + } + } + + // Search in upstream hierarchy + if (SlaveBoneMap[SlaveBoneIndex] == INDEX_NONE) + { + bool FoundMasterBone = false; + int32 ParentIndex = SlaveBoneInfo[SlaveBoneIndex].ParentIndex; + while (!FoundMasterBone && ParentIndex != INDEX_NONE) + { + for (int32 MasterBoneIndex = 0; MasterBoneIndex < MasterNumBones; ++MasterBoneIndex) + { + if (SlaveBoneInfo[ParentIndex].Name == MasterBoneInfo[MasterBoneIndex].Name) + { + SlaveBoneMap[SlaveBoneIndex] = MasterBoneIndex; + FoundMasterBone = true; + break; + } + } + + ParentIndex = SlaveBoneInfo[ParentIndex].ParentIndex; + } + } + + // SlaveBone not found in Master + if (SlaveBoneMap[SlaveBoneIndex] == INDEX_NONE) + { + UE_LOG(LogTemp, Warning, TEXT("Unable to find BoneMapping for: %s"), *SlaveBoneInfo[SlaveBoneIndex].Name.ToString()) + } + } +} + +void DecomposeTransformation(const FTransform& Transform, FVector3f& Translation, FVector4& Rotation) +{ + // Get Translation + Translation = (FVector3f)Transform.GetTranslation(); + + // Get Rotation + const FQuat Quat = Transform.GetRotation(); + + FVector Axis; + float Angle; + Quat.ToAxisAndAngle(Axis, Angle); + + Rotation = FVector4(Axis, Angle); +} + +void DecomposeTransformations(const TArray& Transforms, TArray& Translations, TArray& Rotations) +{ + const int32 NumTransforms = Transforms.Num(); + Translations.SetNumUninitialized(NumTransforms); + Rotations.SetNumUninitialized(NumTransforms); + + for (int32 Index = 0; Index < NumTransforms; ++Index) + { + DecomposeTransformation(Transforms[Index], Translations[Index], Rotations[Index]); + } +}; + + +void GetSkinWeightsData(const USkeletalMesh& SkeletalMesh, const int32 LODIndex, TArray>& SkinWeights) +{ + // Reset Weights + SkinWeights.Reset(); + + // Get Render Data + const FSkeletalMeshRenderData* RenderData = SkeletalMesh.GetResourceForRendering(); + check(RenderData); + + if (!RenderData->LODRenderData.IsValidIndex(LODIndex)) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid LODIndex: %i"), LODIndex) + return; + } + + // Get LOD Data + const FSkeletalMeshLODRenderData& LODRenderData = RenderData->LODRenderData[LODIndex]; + + // Get Weights + const FSkinWeightVertexBuffer* SkinWeightVertexBuffer = LODRenderData.GetSkinWeightVertexBuffer(); + check(SkinWeightVertexBuffer); + + // Get Weights from Buffer. + // this is size of number of vertices. + TArray SkinWeightsInfo; + SkinWeightVertexBuffer->GetSkinWeights(SkinWeightsInfo); + + // Allocated SkinWeightData + SkinWeights.SetNumUninitialized(SkinWeightsInfo.Num()); + + // Loop thru vertices + for (int32 VertexIndex = 0; VertexIndex < SkinWeightsInfo.Num(); ++VertexIndex) + { + // Find Section From Global Vertex Index + // NOTE: BoneMap is stored by Section. + int32 OutSectionIndex; + int32 OutSectionVertexIndex; + LODRenderData.GetSectionFromVertexIndex(VertexIndex, OutSectionIndex, OutSectionVertexIndex); + + // Get Section for Vertex. + const FSkelMeshRenderSection& RenderSection = LODRenderData.RenderSections[OutSectionIndex]; + + // Get Vertex Weights + const FSkinWeightInfo& SkinWeightInfo = SkinWeightsInfo[VertexIndex]; + + // Store Weights + for (int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex) + { + const uint8& BoneWeight = SkinWeightInfo.InfluenceWeights[InfluenceIndex]; + const uint16& BoneIndex = SkinWeightInfo.InfluenceBones[InfluenceIndex]; + const uint16& MeshBoneIndex = RenderSection.BoneMap[BoneIndex]; + + SkinWeights[VertexIndex].BoneWeights[InfluenceIndex] = BoneWeight; + SkinWeights[VertexIndex].BoneIndices[InfluenceIndex] = BoneIndex; + SkinWeights[VertexIndex].MeshBoneIndices[InfluenceIndex] = MeshBoneIndex; + } + } +} + +void GetReducedSkinWeightsData( + const USkeletalMesh& SkeletalMesh, const int32 SkeletalLODIndex, + const TArray StaticToSkelMapping, + TArray>& OutSkinWeights) +{ + // Get SkeletalMesh Weights + TArray> SkinWeights; + AnimToTexture_Private::GetSkinWeightsData(SkeletalMesh, SkeletalLODIndex, SkinWeights); + + // Reduce SkeletalMesh Weights to 4 highest influences. + TArray> ReducedSkinWeights; + AnimToTexture_Private::ReduceSkinWeightsData(SkinWeights, ReducedSkinWeights); + + // Allocate StaticMesh Weights + OutSkinWeights.SetNumUninitialized(StaticToSkelMapping.Num()); + + // Loop thru SkeletalMesh Weights + //uint16 InvalidMeshBoneIndex; + //bool bIsValidMeshBoneIndex = true; + for (int32 VertexIndex = 0; VertexIndex < StaticToSkelMapping.Num(); ++VertexIndex) + { + const int32 SkelVertexIndex = StaticToSkelMapping[VertexIndex]; + OutSkinWeights[VertexIndex] = ReducedSkinWeights[SkelVertexIndex]; + + // MeshBoneIndex can not be higher than 255 + /*for (uint16 MeshBoneIndex: OutSkinWeights[VertexIndex].MeshBoneIndices) + { + if (MeshBoneIndex > 255) + { + InvalidMeshBoneIndex = MeshBoneIndex; + bIsValidMeshBoneIndex = false; + break; + } + } + + if (!bIsValidMeshBoneIndex) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid SkinWeights on Vertex: %i. MeshBoneIndex: %i > 255"), VertexIndex, InvalidMeshBoneIndex) + break; + }*/ + } +} + +} // end namespace AnimToTexture_Private \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureUtils.cpp b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureUtils.cpp new file mode 100644 index 00000000..1fde7661 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Private/AnimToTextureUtils.cpp @@ -0,0 +1,83 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "AnimToTextureUtils.h" +#include "AnimToTextureSkeletalMesh.h" + +#include "Rendering/SkeletalMeshRenderData.h" + +namespace AnimToTexture_Private +{ + bool CheckSkinWeightsToTexture(const TArray>& SkinWeights) + { + bool bHasInvalidIndices = false; + int16 InvalidMeshBoneIndex = INDEX_NONE; + for (int32 VertexIndex = 0; VertexIndex < SkinWeights.Num(); ++VertexIndex) + { + // MeshBoneIndex can not be higher than 255 + for (uint16 MeshBoneIndex : SkinWeights[VertexIndex].MeshBoneIndices) + { + if (MeshBoneIndex > 255) + { + InvalidMeshBoneIndex = MeshBoneIndex; + bHasInvalidIndices = true; + break; + } + } + + if (InvalidMeshBoneIndex != INDEX_NONE) + { + UE_LOG(LogTemp, Warning, TEXT("Invalid SkinWeights on Vertex: %i. MeshBoneIndex: %i > 255"), VertexIndex, InvalidMeshBoneIndex) + } + } + + if (bHasInvalidIndices) + { + return false; + } + + return true; + } + + bool WriteSkinWeightsToTexture(const TArray>& SkinWeights, + const int32 RowsPerFrame, const int32 Height, const int32 Width, UTexture2D* Texture) + { + if (!Texture) + { + return false; + } + + // Sanity check for MeshBoneIndices > 256 + if (!CheckSkinWeightsToTexture(SkinWeights)) + { + return false; + } + + const int32 NumVertices = SkinWeights.Num(); + + // Allocate PixelData. + TArray Pixels; + Pixels.Init(FColor::Black, Height * Width); + + for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex) + { + const TVertexSkinWeight<4>& VertexSkinWeight = SkinWeights[VertexIndex]; + + // Write Influence + // NOTE: we are assuming the bone index is under < 256 + Pixels[VertexIndex].R = (uint8)VertexSkinWeight.MeshBoneIndices[0]; + Pixels[VertexIndex].G = (uint8)VertexSkinWeight.MeshBoneIndices[1]; + Pixels[VertexIndex].B = (uint8)VertexSkinWeight.MeshBoneIndices[2]; + Pixels[VertexIndex].A = (uint8)VertexSkinWeight.MeshBoneIndices[3]; + + // Write Weight + Pixels[Width * RowsPerFrame + VertexIndex].R = VertexSkinWeight.BoneWeights[0]; + Pixels[Width * RowsPerFrame + VertexIndex].G = VertexSkinWeight.BoneWeights[1]; + Pixels[Width * RowsPerFrame + VertexIndex].B = VertexSkinWeight.BoneWeights[2]; + Pixels[Width * RowsPerFrame + VertexIndex].A = VertexSkinWeight.BoneWeights[3]; + }; + + // Write to Texture + return WriteToTexture(Texture, Height, Width, Pixels); + } + +} \ No newline at end of file diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureBPLibrary.h b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureBPLibrary.h new file mode 100644 index 00000000..f29f864a --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureBPLibrary.h @@ -0,0 +1,86 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "AnimToTextureDataAsset.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Materials/MaterialLayersFunctions.h" + +#include "AnimToTextureBPLibrary.generated.h" + +class UStaticMesh; +class USkeletalMesh; + +/* +* TODO: +* - Right now it is saving data per-vertex instead of per-point. +* This will require more pixels if the mesh has lots of material slots or uv-shells. +* FStaticMeshOperations::FindOverlappingCorners(FOverlappingCorners& OutOverlappingCorners, const FMeshDescription& MeshDescription, float ComparisonThreshold) +*/ +UCLASS() +class UAnimToTextureBPLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_UCLASS_BODY() + +#if WITH_EDITOR + + /** + * Bakes Animation Data into Textures. + * Position & Normals. + */ + UFUNCTION(BlueprintCallable, meta = (Category = "VertexAnimationdTesting")) + static void AnimationToTexture(UAnimToTextureDataAsset* DataAsset, + const FTransform RootTransform, const bool bCreateTextures=true, const bool bCreateUVChannel=true); + + /* Utility for converting SkeletalMesh into a StaticMesh */ + UFUNCTION(BlueprintCallable, Category = "VertexAnimationdTesting") + static UStaticMesh* ConvertSkeletalMeshToStaticMesh(USkeletalMesh* SkeletalMesh, const FString PackageName, const int32 LODIndex = -1); + + /** + * Updates a material's parameters to match those of an animToTexture data asset + */ + UFUNCTION(BlueprintCallable, meta = (Category = "VertexAnimationdTesting")) + static void UpdateMaterialInstanceFromDataAsset(UAnimToTextureDataAsset* DataAsset, class UMaterialInstanceConstant* MaterialInstance, + const FAnimToTextureMaterialParamNames& ParamNames, const EMaterialParameterAssociation MaterialParameterAssociation = EMaterialParameterAssociation::GlobalParameter); + + /** + * Replaces material layer function + */ + UFUNCTION(BlueprintCallable, meta = (Category = "VertexAnimationdTesting")) + static void UpdateMaterialLayerFunction( + class UMaterialInstanceConstant* MaterialInstance, + class UMaterialFunctionInterface* OldMaterialFunction, + class UMaterialFunctionInterface* NewMaterialFunction); + + UFUNCTION(BlueprintCallable, meta = (Category = "VertexAnimationdTesting")) + static void SetStaticMeshBoundsExtensions(UStaticMesh* StaticMesh, const FVector& PositiveBoundsExtension, const FVector& NegativeBoundsExtension); + +#endif // WITH_EDITOR + +private: + + static void NormalizeVertexData( + const TArray& Deltas, const TArray& Normals, + FVector& MinBBox, FVector& SizeBBox, + TArray& NormalizedDeltas, TArray& NormalizedNormals); + + static void NormalizeBoneData( + const TArray& Positions, const TArray& Rotations, + FVector& MinBBox, FVector& SizeBBox, + TArray& NormalizedPositions, TArray& NormalizedRotations); + + /* Returns best resolution for the given data. + * Returns false if data wont fit in the the max range. + */ + static bool FindBestResolution(const int32 NumFrames, const int32 NumElements, + int32& OutHeight, int32& OutWidth, int32& OutRowsPerFrame, + const int32 MaxHeight = 4096, const int32 MaxWidth = 4096, bool bEnforcePowerOfTwo = false); + + /* Sets Static Mesh FullPrecisionUVs Property*/ + static void SetFullPrecisionUVs(UStaticMesh* StaticMesh, bool bFullPrecision); + + /* Creates UV Coord with vertices */ + static bool CreateUVChannel(UStaticMesh* StaticMesh, const int32 LODIndex, const int32 UVChannelIndex, + const int32 Height, const int32 Width); + +}; diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureEditor.h b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureEditor.h new file mode 100644 index 00000000..6211baa2 --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureEditor.h @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FAnimToTextureEditorModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureSkeletalMesh.h b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureSkeletalMesh.h new file mode 100644 index 00000000..af76c3ae --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureSkeletalMesh.h @@ -0,0 +1,224 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "GPUSkinPublicDefs.h" +#include "Containers/StaticArray.h" + +#include "AnimToTextureDataAsset.h" + +namespace AnimToTexture_Private +{ + +template +struct TVertexSkinWeight +{ + TStaticArray MeshBoneIndices; + TStaticArray BoneIndices; + TStaticArray BoneWeights; +}; + +/* Returns RefPose Vertex Positions */ +int32 GetVertices(const USkeletalMesh& Mesh, const int32 LODIndex, + TArray& OutPositions, TArray& OutNormals); + +/* Finds closest vertex to point */ +int32 FindClosestVertex(const FVector3f& Point, const TArray& Vertices); + +/* VertexMapping between StaticMesh and SkeletalMesh Vertices. +* Note: Size of Mapping is Number of MeshDescription Vertices. +*/ +void GetStaticToSkeletalMapping( + const UStaticMesh& StaticMesh, const int32 StaticLODIndex, + const USkeletalMesh& SkeletalMesh, const int32 SkeletalLODIndex, + TArray& OutStaticVertices, TArray& OutSkeletalVertices, + TArray& OutStaticToSkeletalMapping); + +/* Computes CPUSkinning at Pose */ +void GetSkinnedVertices(USkeletalMeshComponent* MeshComponent, const int32 LODIndex, + TArray& OutPositions, TArray& OutNormals); + +/** Gets Skin Weights Data from SkeletalMeshComponent */ +void GetSkinWeightsData(const USkeletalMesh& SkeletalMesh, const int32 LODIndex, TArray>& SkinWeights); + +/** Gets Skin Weights Data from SkeletalMeshComponent, Reduce it to NumInfluences and Maps it to the StaticMesh */ +void GetReducedSkinWeightsData( + const USkeletalMesh& SkeletalMesh, const int32 SkeletalLODIndex, + const TArray StaticToSkelMapping, + TArray>& SkinWeights); + +/** Reduce Weights */ +void ReduceSkinWeightsData(const TArray>& InSkinWeights, TArray>& OutSkinWeights); + +/* Returns the 4-Higher Influences and Weights */ +void GetMaxFourInfluences(const TVertexSkinWeight& InSkinWeights, TVertexSkinWeight<4>& OutSkinWeights); + +/* Returns Number of RawBones (no virtual bones)*/ +int32 GetNumBones(const USkeletalMesh* SkeletalMesh); + +/* Returns RefPose Bone Transforms. + Only the RawBones are returned (no virtual bones) + The returned transforms are in ComponentSpace */ +void GetRefBoneTransforms(const USkeletalMesh* SkeletalMesh, TArray& OutTransforms); + +/* Returns Bone exist in the RawBone list (no virtual bones) */ +bool HasBone(const USkeletalMesh* SkeletalMesh, const FName& Bone); + +/* Returns Bone Names. + Only the RawBones are returned (no virtual bones) */ +void GetBoneNames(const USkeletalMesh* SkeletalMesh, TArray& OutNames); + +/** Returns a Mapping between Slave and Master. The hierarchy is traversed upstream until a matching Bone is found. */ +void GetSlaveBoneMap(const USkeletalMesh* MasterSkeletalMesh, const USkeletalMesh* SlaveSkeletalMesh, TArray& SlaveBoneMap); + +/* Decomposes Transform in Translation and AxisAndAngle */ +void DecomposeTransformation(const FTransform& Transform, FVector3f& Translation, FVector4& Rotation); +void DecomposeTransformations(const TArray& Transforms, TArray& Translations, TArray& Rotations); + +// ---------------------------------------------------------------------------- + +//template<> +//FORCEINLINE void GetMaxInfluences<1>( +// const TVertexSkinWeight& InSkinWeights, TVertexSkinWeight<4>& OutSkinWeights) +//{ +// // Reset Values +// for (int32 Index = 0; Index < 4; ++Index) +// { +// OutSkinWeights.BoneWeights[Index] = 0; +// OutSkinWeights.BoneIndices[Index] = 0; +// OutSkinWeights.MeshBoneIndices[Index] = 0; +// } +// +// // Set first influence to 1 +// OutSkinWeights.BoneWeights[0] = 255; // TNumericLimits::Max(); +// +// // Find 1st Influence (Highest) +// uint8 MaxWeight = TNumericLimits::Min(); +// for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) +// { +// if (InSkinWeights.BoneWeights[Index] > MaxWeight) +// { +// MaxWeight = InSkinWeights.BoneWeights[Index]; +// OutSkinWeights.BoneIndices[0] = InSkinWeights.BoneIndices[Index]; +// OutSkinWeights.MeshBoneIndices[0] = InSkinWeights.MeshBoneIndices[Index]; +// } +// } +//} +// +//template<> +//FORCEINLINE void GetMaxInfluences<2>( +// const TVertexSkinWeight& InSkinWeights, TVertexSkinWeight<4>& OutSkinWeights) +//{ +// // Reset Values +// for (int32 Index = 0; Index < 4; ++Index) +// { +// OutSkinWeights.BoneIndices[Index] = 0; +// OutSkinWeights.BoneWeights[Index] = 0; +// OutSkinWeights.MeshBoneIndices[Index] = 0; +// } +// +// // Find 1st Influence (Highest) +// for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) +// { +// if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[0]) +// { +// OutSkinWeights.BoneWeights[0] = InSkinWeights.BoneWeights[Index]; +// OutSkinWeights.BoneIndices[0] = InSkinWeights.BoneIndices[Index]; +// OutSkinWeights.MeshBoneIndices[0] = InSkinWeights.MeshBoneIndices[Index]; +// } +// } +// +// // Find 2nd Influence +// for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) +// { +// if (InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[0]) +// { +// if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[1]) +// { +// OutSkinWeights.BoneWeights[1] = InSkinWeights.BoneWeights[Index]; +// OutSkinWeights.BoneIndices[1] = InSkinWeights.BoneIndices[Index]; +// OutSkinWeights.MeshBoneIndices[1] = InSkinWeights.MeshBoneIndices[Index]; +// } +// } +// } +// +// // Normalize Weighs +// const float NormFactor = 255.0f / (OutSkinWeights.BoneWeights[0] + OutSkinWeights.BoneWeights[1]); +// OutSkinWeights.BoneWeights[0] = FMath::RoundToInt(OutSkinWeights.BoneWeights[0] * NormFactor); +// OutSkinWeights.BoneWeights[1] = FMath::RoundToInt(OutSkinWeights.BoneWeights[1] * NormFactor); +//} + +FORCEINLINE void GetMaxFourInfluences( + const TVertexSkinWeight& InSkinWeights, TVertexSkinWeight<4>& OutSkinWeights) +{ + // Reset Values + for (int32 Index = 0; Index < 4; ++Index) + { + OutSkinWeights.BoneWeights[Index] = 0; + OutSkinWeights.BoneIndices[Index] = 0; + OutSkinWeights.MeshBoneIndices[Index] = 0; + } + + // Find 1st Influence (Highest) + for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) + { + if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[0]) + { + OutSkinWeights.BoneWeights[0] = InSkinWeights.BoneWeights[Index]; + OutSkinWeights.BoneIndices[0] = InSkinWeights.BoneIndices[Index]; + OutSkinWeights.MeshBoneIndices[0] = InSkinWeights.MeshBoneIndices[Index]; + } + } + + // Find 2nd Influence + for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) + { + if (InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[0]) + { + if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[1]) + { + OutSkinWeights.BoneWeights[1] = InSkinWeights.BoneWeights[Index]; + OutSkinWeights.BoneIndices[1] = InSkinWeights.BoneIndices[Index]; + OutSkinWeights.MeshBoneIndices[1] = InSkinWeights.MeshBoneIndices[Index]; + } + } + } + + // Find 3rd Influence + for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) + { + if (InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[0] && InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[1]) + { + if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[2]) + { + OutSkinWeights.BoneWeights[2] = InSkinWeights.BoneWeights[Index]; + OutSkinWeights.BoneIndices[2] = InSkinWeights.BoneIndices[Index]; + OutSkinWeights.MeshBoneIndices[2] = InSkinWeights.MeshBoneIndices[Index]; + } + } + } + + // Find 4th Influence + for (int32 Index = 0; Index < MAX_TOTAL_INFLUENCES; ++Index) + { + if (InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[0] && InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[1] && InSkinWeights.BoneIndices[Index] != OutSkinWeights.BoneIndices[2]) + { + if (InSkinWeights.BoneWeights[Index] > OutSkinWeights.BoneWeights[3]) + { + OutSkinWeights.BoneWeights[3] = InSkinWeights.BoneWeights[Index]; + OutSkinWeights.BoneIndices[3] = InSkinWeights.BoneIndices[Index]; + OutSkinWeights.MeshBoneIndices[3] = InSkinWeights.MeshBoneIndices[Index]; + } + } + } + + // Normalize Weighs + const float NormFactor = 255.0f / (OutSkinWeights.BoneWeights[0] + OutSkinWeights.BoneWeights[1] + OutSkinWeights.BoneWeights[2] + OutSkinWeights.BoneWeights[3]); + for (int32 Index = 0; Index < 4; ++Index) + { + OutSkinWeights.BoneWeights[Index] = FMath::RoundToInt(OutSkinWeights.BoneWeights[Index] * NormFactor); + } +} + +} // end namespace AnimToTexture_Private diff --git a/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureUtils.h b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureUtils.h new file mode 100644 index 00000000..be04347f --- /dev/null +++ b/Plugins/AnimToTexture/Source/AnimToTextureEditor/Public/AnimToTextureUtils.h @@ -0,0 +1,309 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Texture.h" +#include "Engine/Texture2D.h" + +namespace AnimToTexture_Private +{ + // Forward declaration + template struct TVertexSkinWeight; + + struct FVector4u16 + { + uint16 X; + uint16 Y; + uint16 Z; + uint16 W; + }; + + struct FLowPrecision + { + using ColorType = FColor; + static constexpr EPixelFormat PixelFormat = EPixelFormat::PF_B8G8R8A8; + static constexpr ETextureSourceFormat TextureSourceFormat = ETextureSourceFormat::TSF_BGRA8; + static constexpr TextureCompressionSettings CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap; + static constexpr ColorType DefaultColor = FColor(0, 0, 0); + }; + + struct FHighPrecision + { + using ColorType = FVector4u16; + static constexpr EPixelFormat PixelFormat = EPixelFormat::PF_R16G16B16A16_UNORM; + static constexpr ETextureSourceFormat TextureSourceFormat = ETextureSourceFormat::TSF_RGBA16; + static constexpr TextureCompressionSettings CompressionSettings = TextureCompressionSettings::TC_HDR; + static constexpr ColorType DefaultColor = { 0, 0, 0, 0 }; + }; + + + /** Writes list of vectors into texture + * Note: They must be pre-normalized. */ + template + bool WriteVectorsToTexture(const TArray& Vectors, + const int32 NumFrames, const int32 RowsPerFrame, + const int32 Height, const int32 Width, + UTexture2D* Texture); + + /* Writes list of skinweights into texture. + * The SkinWeights data is already in uint8 & uint16 format, no need for normalizing it. + */ + bool WriteSkinWeightsToTexture(const TArray>& SkinWeights, + const int32 RowsPerFrame, + const int32 Height, const int32 Width, + UTexture2D* Texture); + + /* Helper utility for writing 8 or 16 bits textures */ + template + bool WriteToTexture(UTexture2D* Texture, const uint32 Height, const uint32 Width, const TArray& Data); + + template + void VectorToColor(const V& Vector, C& Color); + + //template + //void ColorArrayToData(const TArray& Colors, TArray& Data); +} + +// ---------------------------------------------------------------------------- + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector3f& Vector, FColor& Color) +{ + const float ClampedX = FMath::Clamp(Vector.X, 0.0f, 1.0f); + const float ClampedY = FMath::Clamp(Vector.Y, 0.0f, 1.0f); + const float ClampedZ = FMath::Clamp(Vector.Z, 0.0f, 1.0f); + + Color.R = (uint8)FMath::RoundToInt(ClampedX * 255.0f); + Color.G = (uint8)FMath::RoundToInt(ClampedY * 255.0f); + Color.B = (uint8)FMath::RoundToInt(ClampedZ * 255.0f); + Color.A = 255; +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector3d& Vector, FColor& Color) +{ + VectorToColor((FVector3f)Vector, Color); +} + + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector4& Vector, FColor& Color) +{ + const float ClampedX = FMath::Clamp(Vector.X, 0.0f, 1.0f); + const float ClampedY = FMath::Clamp(Vector.Y, 0.0f, 1.0f); + const float ClampedZ = FMath::Clamp(Vector.Z, 0.0f, 1.0f); + const float ClampedW = FMath::Clamp(Vector.W, 0.0f, 1.0f); + + Color.R = (uint8)FMath::RoundToInt(ClampedX * 255.0f); + Color.G = (uint8)FMath::RoundToInt(ClampedY * 255.0f); + Color.B = (uint8)FMath::RoundToInt(ClampedZ * 255.0f); + Color.A = (uint8)FMath::RoundToInt(ClampedW * 255.0f); +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FIntVector& Vector, FColor& Color) +{ + const int32 ClampedX = FMath::Clamp(Vector.X, 0, 1); + const int32 ClampedY = FMath::Clamp(Vector.Y, 0, 1); + const int32 ClampedZ = FMath::Clamp(Vector.Z, 0, 1); + + Color.R = (uint8)(ClampedX * 255); + Color.G = (uint8)(ClampedY * 255); + Color.B = (uint8)(ClampedZ * 255); + Color.A = 255; +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FIntVector4& Vector, FColor& Color) +{ + const int32 ClampedX = FMath::Clamp(Vector.X, 0, 1); + const int32 ClampedY = FMath::Clamp(Vector.Y, 0, 1); + const int32 ClampedZ = FMath::Clamp(Vector.Z, 0, 1); + const int32 ClampedW = FMath::Clamp(Vector.W, 0, 1); + + Color.R = (uint8)(ClampedX * 255); + Color.G = (uint8)(ClampedY * 255); + Color.B = (uint8)(ClampedZ * 255); + Color.A = (uint8)(ClampedW * 255); +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector3f& Vector, FLinearColor& Color) +{ + Color.R = Vector.X; + Color.G = Vector.Y; + Color.B = Vector.Z; + Color.A = 1.0f; +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector4& Vector, FLinearColor& Color) +{ + Color.R = Vector.X; + Color.G = Vector.Y; + Color.B = Vector.Z; + Color.A = Vector.W; +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector3f& Vector, FVector4u16& Color) +{ + Color.X = FMath::RoundToInt(FMath::Clamp(Vector.X, 0.0f, 1.0f) * MAX_uint16); + Color.Y = FMath::RoundToInt(FMath::Clamp(Vector.Y, 0.0f, 1.0f) * MAX_uint16); + Color.Z = FMath::RoundToInt(FMath::Clamp(Vector.Z, 0.0f, 1.0f) * MAX_uint16); + Color.W = MAX_uint16; +} + +template<> +FORCEINLINE void AnimToTexture_Private::VectorToColor(const FVector4& Vector, FVector4u16& Color) +{ + Color.X = FMath::RoundToInt(FMath::Clamp(Vector.X, 0.0f, 1.0f) * MAX_uint16); + Color.Y = FMath::RoundToInt(FMath::Clamp(Vector.Y, 0.0f, 1.0f) * MAX_uint16); + Color.Z = FMath::RoundToInt(FMath::Clamp(Vector.Z, 0.0f, 1.0f) * MAX_uint16); + Color.W = FMath::RoundToInt(FMath::Clamp(Vector.W, 0.0f, 1.0f) * MAX_uint16); +} + +/* +template<> +FORCEINLINE void AnimToTexture_Private::ColorArrayToData(const TArray& Colors, TArray& Data) +{ + // Resize Array + Data.Init(0, Colors.Num() * 4); + + for (int32 Index = 0; Index < Colors.Num(); ++Index) + { + // NOTE: format is BGRA (swaping R <-> B) + Data[Index * 4 + 0] = Colors[Index].B; // B + Data[Index * 4 + 1] = Colors[Index].G; // G + Data[Index * 4 + 2] = Colors[Index].R; // R + Data[Index * 4 + 3] = Colors[Index].A; // A + } +} + +template<> +FORCEINLINE void AnimToTexture_Private::ColorArrayToData(const TArray& Colors, TArray& Data) +{ + // Resize Array + Data.Init(0.0f, Colors.Num() * 4); + + for (int32 Index = 0; Index < Colors.Num(); ++Index) + { + // NOTE: format is RGBA + Data[Index * 4 + 0] = Colors[Index].R; // R + Data[Index * 4 + 1] = Colors[Index].G; // G + Data[Index * 4 + 2] = Colors[Index].B; // B + Data[Index * 4 + 3] = Colors[Index].A; // A + } +} +*/ + + +template +FORCEINLINE bool AnimToTexture_Private::WriteVectorsToTexture(const TArray& Vectors, + const int32 NumFrames, const int32 RowsPerFrame, + const int32 Height, const int32 Width, UTexture2D* Texture) +{ + if (!Texture || !NumFrames) + { + return false; + } + + // NumElements Per-Frame + const int32 NumElements = Vectors.Num() / NumFrames; + + // Allocate PixelData. + TArray Pixels; + Pixels.Init(TextureSettings::DefaultColor, Height * Width); + + // Fillout Frame Data + for (int32 Frame = 0; Frame < NumFrames; ++Frame) + { + const int32 BlockStart = RowsPerFrame * Width * Frame; + + // Set Data. + for (int32 Index = 0; Index < NumElements; ++Index) + { + const V& Vector = Vectors[NumElements * Frame + Index]; + typename TextureSettings::ColorType& Pixel = Pixels[BlockStart + Index]; + + VectorToColor(Vector, Pixel); + } + } + + // Write to Texture + return WriteToTexture(Texture, Height, Width, Pixels); +} + +template +FORCEINLINE bool AnimToTexture_Private::WriteToTexture( + UTexture2D* Texture, + const uint32 Height, + const uint32 Width, + const TArray& Pixels) +{ + if (!Texture) + { + return false; + } + + // ------------------------------------------------------------------------ + // Get Texture Platform + FTexturePlatformData* PlatformData = Texture->GetPlatformData(); + if (!PlatformData) + { + PlatformData = new FTexturePlatformData(); + Texture->SetPlatformData(PlatformData); + } + PlatformData->SizeX = Width; + PlatformData->SizeY = Height; + PlatformData->SetNumSlices(1); + PlatformData->PixelFormat = TextureSettings::PixelFormat; + + // ------------------------------------------------------------------------ + // Get First MipMap + // + FTexture2DMipMap* Mip; + if (PlatformData->Mips.IsEmpty()) + { + Mip = new FTexture2DMipMap(); + PlatformData->Mips.Add(Mip); + } + else + { + Mip = &PlatformData->Mips[0]; + } + Mip->SizeX = Width; + Mip->SizeY = Height; + + // ------------------------------------------------------------------------ + // Lock the Mipmap data so it can be modified + Mip->BulkData.Lock(LOCK_READ_WRITE); + + // Reallocate MipMap + uint8* TextureData = (uint8*)Mip->BulkData.Realloc(Width * Height * sizeof(typename TextureSettings::ColorType)); + + // Copy the pixel data into the Texture data + const uint8* PixelsData = (uint8*)Pixels.GetData(); + FMemory::Memcpy(TextureData, PixelsData, Width * Height * sizeof(typename TextureSettings::ColorType)); + + // Unlock data + Mip->BulkData.Unlock(); + + // Initialize a new texture + Texture->Source.Init(Width, Height, 1, 1, TextureSettings::TextureSourceFormat, PixelsData); + + // Set parameters + Texture->SRGB = 0; + Texture->Filter = TextureFilter::TF_Nearest; + Texture->CompressionSettings = TextureSettings::CompressionSettings; + Texture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; + + // Update and Mark to Save. + Texture->UpdateResource(); + Texture->MarkPackageDirty(); + + return true; +} + + diff --git a/Source/MassAITesting/Mass/RTSAgentTrait.h b/Source/MassAITesting/Mass/RTSAgentTrait.h index c46cebe6..298dc3f5 100644 --- a/Source/MassAITesting/Mass/RTSAgentTrait.h +++ b/Source/MassAITesting/Mass/RTSAgentTrait.h @@ -127,6 +127,7 @@ struct MASSAITESTING_API FRTSAgentParameters : public FMassSharedFragment // Since all rts agents current share animation data, its easiest to setup in a shared fragment for now UPROPERTY(EditAnywhere) TSoftObjectPtr AnimData; + }; /** diff --git a/Source/MassAITesting/StateTree/Evaluators/MassStateTreeSmartObjectEvaluatorPlus.h b/Source/MassAITesting/StateTree/Evaluators/MassStateTreeSmartObjectEvaluatorPlus.h index d229de6a..063fc694 100644 --- a/Source/MassAITesting/StateTree/Evaluators/MassStateTreeSmartObjectEvaluatorPlus.h +++ b/Source/MassAITesting/StateTree/Evaluators/MassStateTreeSmartObjectEvaluatorPlus.h @@ -19,7 +19,7 @@ struct MASSAITESTING_API FMassStateTreeSmartObjectEvaluatorPlusInstanceData /** The identifier of the search request send by the evaluator to find candidates */ UPROPERTY(EditAnywhere, Category = Output) - FMassSmartObjectRequestResult SearchRequestResult; + FMassSmartObjectRequestResultFragment SearchRequestResult; /** Indicates that the result of the candidates search is ready and contains some candidates */ UPROPERTY(EditAnywhere, Category = Output) diff --git a/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.cpp b/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.cpp index 4e85f6e8..fd9b8ad6 100644 --- a/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.cpp +++ b/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.cpp @@ -2,9 +2,9 @@ #include "MassStateTreeClaimSmartObjectTaskPlus.h" - #include "MassSmartObjectBehaviorDefinition.h" #include "MassSmartObjectFragments.h" +#include "MassSmartObjectTypes.h" #include "SmartObjectSubsystem.h" #include "StateTreeExecutionContext.h" @@ -12,7 +12,7 @@ bool FMassStateTreeClaimSmartObjectTaskPlus::Link(FStateTreeLinker& Linker) { Linker.LinkInstanceDataProperty(SmartObjectHandle, STATETREE_INSTANCEDATA_PROPERTY(FMassStateTreeClaimSmartObjectTaskInstanceData, SOHandle)); Linker.LinkInstanceDataProperty(ClaimResultHandle, STATETREE_INSTANCEDATA_PROPERTY(FMassStateTreeClaimSmartObjectTaskInstanceData, ClaimResult)); - + Linker.LinkExternalData(SmartObjectUserHandle); Linker.LinkExternalData(SmartObjectSubsystemHandle); diff --git a/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.h b/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.h index 00e946a0..b1dc562c 100644 --- a/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.h +++ b/Source/MassAITesting/StateTree/Tasks/MassStateTreeClaimSmartObjectTaskPlus.h @@ -9,6 +9,7 @@ #include "SmartObjectSubsystem.h" #include "MassStateTreeClaimSmartObjectTaskPlus.generated.h" + class UMassSignalSubsystem; struct FMassSmartObjectUserFragment; USTRUCT() @@ -44,3 +45,4 @@ struct MASSAITESTING_API FMassStateTreeClaimSmartObjectTaskPlus : public FMassSt TStateTreeExternalDataHandle SmartObjectSubsystemHandle; }; +