diff --git a/voxel_cpp_test/Config/DefaultEditor.ini b/voxel_cpp_test/Config/DefaultEditor.ini index 8a839899..5ff90e89 100644 --- a/voxel_cpp_test/Config/DefaultEditor.ini +++ b/voxel_cpp_test/Config/DefaultEditor.ini @@ -5,4 +5,5 @@ SimpleMapName=/Game/TopDown/Maps/TopDownExampleMap bAllowClassAndBlueprintPinMatching=true bReplaceBlueprintWithClass= true bDontLoadBlueprintOutsideEditor= true -bBlueprintIsNotBlueprintType= true \ No newline at end of file +bBlueprintIsNotBlueprintType= true + diff --git a/voxel_cpp_test/Content/NewVoxelWorldSaveObject1.uasset b/voxel_cpp_test/Content/NewVoxelWorldSaveObject1.uasset new file mode 100644 index 00000000..53b5cc59 Binary files /dev/null and b/voxel_cpp_test/Content/NewVoxelWorldSaveObject1.uasset differ diff --git a/voxel_cpp_test/Content/Worlds/emonds_field_heightmap1.uasset b/voxel_cpp_test/Content/Worlds/emonds_field_heightmap1.uasset new file mode 100644 index 00000000..863b567d Binary files /dev/null and b/voxel_cpp_test/Content/Worlds/emonds_field_heightmap1.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Config/BaseVoxelPro.ini b/voxel_cpp_test/Plugins/Voxel/Config/BaseVoxelPro.ini new file mode 100644 index 00000000..a3d34a7f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Config/BaseVoxelPro.ini @@ -0,0 +1,388 @@ +[CoreRedirects] ++EnumRedirects=(OldName="EVoxelMaterialConfig", ValueChanges=(("IndexWithMultipleMaterials", "SingleIndex"), ("DoubleIndexWithMultipleMaterials", "DoubleIndex"))) ++EnumRedirects=(OldName="EVoxelPinCategoryDataOnly", NewName="EVoxelPortalNodePinCategory") ++EnumRedirects=(OldName="EVoxelSpawnerScaling", NewName="EVoxelMeshSpawnerScaling") ++EnumRedirects=(OldName="EVoxelSpawnerRotation", NewName="EVoxelMeshSpawnerRotation") + ++FunctionRedirects=(OldName="VoxelWorld.ClearCache", NewName="VoxelBlueprintLibrary.ClearCache") ++FunctionRedirects=(OldName="VoxelWorld.ClearAllCache", NewName="VoxelBlueprintLibrary.ClearAllCache") ++FunctionRedirects=(OldName="VoxelWorld.Cache", NewName="VoxelBlueprintLibrary.Cache") ++FunctionRedirects=(OldName="VoxelWorld.Undo", NewName="VoxelBlueprintLibrary.Undo") ++FunctionRedirects=(OldName="VoxelWorld.Redo", NewName="VoxelBlueprintLibrary.Redo") ++FunctionRedirects=(OldName="VoxelWorld.SaveFrame", NewName="VoxelBlueprintLibrary.SaveFrame") ++FunctionRedirects=(OldName="VoxelWorld.ClearFrames", NewName="VoxelBlueprintLibrary.ClearFrames") ++FunctionRedirects=(OldName="VoxelWorld.GetValue", NewName="VoxelBlueprintLibrary.GetValue") ++FunctionRedirects=(OldName="VoxelWorld.SetValue", NewName="VoxelBlueprintLibrary.SetValue") ++FunctionRedirects=(OldName="VoxelWorld.GetMaterial", NewName="VoxelBlueprintLibrary.GetMaterial") ++FunctionRedirects=(OldName="VoxelWorld.SetMaterial", NewName="VoxelBlueprintLibrary.SetMaterial") ++FunctionRedirects=(OldName="VoxelWorld.GetSave", NewName="VoxelBlueprintLibrary.GetSave") ++FunctionRedirects=(OldName="VoxelWorld.GetCompressedSave", NewName="VoxelBlueprintLibrary.GetCompressedSave") ++FunctionRedirects=(OldName="VoxelWorld.LoadFromSave", NewName="VoxelBlueprintLibrary.LoadFromSave") ++FunctionRedirects=(OldName="VoxelWorld.LoadFromCompressedSave", NewName="VoxelBlueprintLibrary.LoadFromCompressedSave") ++FunctionRedirects=(OldName="VoxelWorld.GetNormal", NewName="VoxelBlueprintLibrary.GetNormal") ++FunctionRedirects=(OldName="VoxelWorld.GetFloatOutput", NewName="VoxelBlueprintLibrary.GetFloatOutput") ++FunctionRedirects=(OldName="VoxelWorld.GetBounds", NewName="VoxelBlueprintLibrary.GetBounds") ++FunctionRedirects=(OldName="VoxelWorld.UpdatePosition", NewName="VoxelBlueprintLibrary.UpdatePosition") ++FunctionRedirects=(OldName="VoxelWorld.UpdateBounds", NewName="VoxelBlueprintLibrary.UpdateBounds") ++FunctionRedirects=(OldName="VoxelWorld.UpdateAll", NewName="VoxelBlueprintLibrary.UpdateAll") ++FunctionRedirects=(OldName="VoxelWorld.GetTaskCount", NewName="VoxelBlueprintLibrary.GetTaskCount") ++FunctionRedirects=(OldName="VoxelWorld.ConnectClient", NewName="VoxelBlueprintLibrary.ConnectToServer") ++FunctionRedirects=(OldName="VoxelWorld.StartServer", NewName="VoxelBlueprintLibrary.StartServer") ++FunctionRedirects=(OldName="VoxelWorld.EnableFloatingActorsInArea", NewName="VoxelBlueprintLibrary.EnableFloatingActorsInArea") ++FunctionRedirects=(OldName="VoxelWorld.EnableActorsInArea", NewName="VoxelBlueprintLibrary.EnableActorsInArea") ++FunctionRedirects=(OldName="VoxelWorld.EnableInstanceInArea", NewName="VoxelBlueprintLibrary.EnableInstanceInArea") ++FunctionRedirects=(OldName="VoxelWorld.GetActorClassFromHISM", NewName="VoxelBlueprintLibrary.GetActorClassFromHISM") ++FunctionRedirects=(OldName="VoxelWorld.AddInstance", NewName="VoxelBlueprintLibrary.AddInstance") ++FunctionRedirects=(OldName="VoxelWorld.RemoveInstance", NewName="VoxelBlueprintLibrary.RemoveInstance") ++FunctionRedirects=(OldName="VoxelPoolManager.CreateGlobalVoxelThreadPool", NewName="VoxelBlueprintLibrary.CreateGlobalVoxelThreadPool") ++FunctionRedirects=(OldName="VoxelPoolManager.DestroyGlobalVoxelThreadPool", NewName="VoxelBlueprintLibrary.DestroyGlobalVoxelThreadPool") ++FunctionRedirects=(OldName="VoxelPoolManager.IsGlobalVoxelPoolCreated", NewName="VoxelBlueprintLibrary.IsGlobalVoxelPoolCreated") ++FunctionRedirects=(OldName="VoxelBPUtilities.MakeBoxFromGlobalPositionAndRadius", NewName="VoxelBlueprintLibrary.MakeBoxFromGlobalPositionAndRadius") ++FunctionRedirects=(OldName="VoxelBPUtilities.Add_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Add_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Substract_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Substract_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Multiply_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Divide_IntVectorInt", NewName="VoxelBlueprintLibrary.Divide_IntVectorInt") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntVectorInt", NewName="VoxelBlueprintLibrary.Multiply_IntVectorInt") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntIntVector", NewName="VoxelBlueprintLibrary.Multiply_IntIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateRGBPaintMaterial", NewName="VoxelBlueprintLibrary.CreateRGBPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateIndexPaintMaterial", NewName="VoxelBlueprintLibrary.CreateIndexPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateDoubleIndexSetPaintMaterial", NewName="VoxelBlueprintLibrary.CreateDoubleIndexSetPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateDoubleIndexBlendPaintMaterial", NewName="VoxelBlueprintLibrary.CreateDoubleIndexBlendPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateGrassPaintMaterial", NewName="VoxelBlueprintLibrary.CreateGrassPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateActorPaintMaterial", NewName="VoxelBlueprintLibrary.CreateActorPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.ApplyPaintMaterial", NewName="VoxelBlueprintLibrary.ApplyPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetColor", NewName="VoxelBlueprintLibrary.GetColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetColor", NewName="VoxelBlueprintLibrary.SetColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromColor", NewName="VoxelBlueprintLibrary.CreateMaterialFromColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndex", NewName="VoxelBlueprintLibrary.GetIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndex", NewName="VoxelBlueprintLibrary.SetIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromIndex", NewName="VoxelBlueprintLibrary.CreateMaterialFromIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndexA", NewName="VoxelBlueprintLibrary.GetIndexA") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndexB", NewName="VoxelBlueprintLibrary.GetIndexB") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetBlend", NewName="VoxelBlueprintLibrary.GetBlend") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndexA", NewName="VoxelBlueprintLibrary.SetIndexA") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndexB", NewName="VoxelBlueprintLibrary.SetIndexB") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetBlend", NewName="VoxelBlueprintLibrary.SetBlend") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromDoubleIndex", NewName="VoxelBlueprintLibrary.CreateMaterialFromDoubleIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetVoxelActorId", NewName="VoxelBlueprintLibrary.GetVoxelActorId") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetVoxelActorId", NewName="VoxelBlueprintLibrary.SetVoxelActorId") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetVoxelGrassId", NewName="VoxelBlueprintLibrary.GetVoxelGrassId") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetVoxelGrassId", NewName="VoxelBlueprintLibrary.SetVoxelGrassId") ++FunctionRedirects=(OldName="VoxelBPUtilities.TranslateBox", NewName="IntBoxLibrary.TranslateBox") ++FunctionRedirects=(OldName="VoxelBPUtilities.Conv_IntVectorToString", NewName="IntBoxLibrary.Conv_IntVectorToString") ++FunctionRedirects=(OldName="VoxelBPUtilities.MakeBoxFromLocalPositionAndRadius", NewName="IntBoxLibrary.MakeBoxFromLocalPositionAndRadius") ++FunctionRedirects=(OldName="VoxelWorld.AddWorms", NewName="VoxelPlaceableItemsUtilities.AddWorms") + ++ClassRedirects=(OldName="VoxelGraph", NewName="VoxelEdGraph") + ++ClassRedirects=(OldName="/Script/Voxel.VoxelColorWheel", NewName="/Script/VoxelHelpers.VoxelColorWheel") ++ClassRedirects=(OldName="/Script/Voxel.MaterialExpressionBlendMaterialAttributesBarycentric", NewName="/Script/VoxelHelpers.MaterialExpressionBlendMaterialAttributesBarycentric") + ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode", NewName="/Script/VoxelGraph.VoxelNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNodeHelper", NewName="/Script/VoxelGraph.VoxelNodeHelper") ++ClassRedirects=(OldName="/Script/Voxel.VoxelMaterialNode", NewName="/Script/VoxelGraph.VoxelMaterialNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelFloatNode", NewName="/Script/VoxelGraph.VoxelFloatNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelIntNode", NewName="/Script/VoxelGraph.VoxelIntNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_XF", NewName="/Script/VoxelGraph.VoxelNode_XF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_YF", NewName="/Script/VoxelGraph.VoxelNode_YF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ZF", NewName="/Script/VoxelGraph.VoxelNode_ZF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_XI", NewName="/Script/VoxelGraph.VoxelNode_XI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_YI", NewName="/Script/VoxelGraph.VoxelNode_YI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ZI", NewName="/Script/VoxelGraph.VoxelNode_ZI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetColor", NewName="/Script/VoxelGraph.VoxelNode_GetColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetVoxelSpawnedActorId", NewName="/Script/VoxelGraph.VoxelNode_GetVoxelSpawnedActorId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetVoxelGrassId", NewName="/Script/VoxelGraph.VoxelNode_GetVoxelGrassId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetIndex", NewName="/Script/VoxelGraph.VoxelNode_GetIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetDoubleIndex", NewName="/Script/VoxelGraph.VoxelNode_GetDoubleIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMax", NewName="/Script/VoxelGraph.VoxelNode_FMax") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMin", NewName="/Script/VoxelGraph.VoxelNode_FMin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMax", NewName="/Script/VoxelGraph.VoxelNode_IMax") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMin", NewName="/Script/VoxelGraph.VoxelNode_IMin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FLess", NewName="/Script/VoxelGraph.VoxelNode_FLess") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FLessEqual", NewName="/Script/VoxelGraph.VoxelNode_FLessEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FGreater", NewName="/Script/VoxelGraph.VoxelNode_FGreater") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FGreaterEqual", NewName="/Script/VoxelGraph.VoxelNode_FGreaterEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FEqual", NewName="/Script/VoxelGraph.VoxelNode_FEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FNotEqual", NewName="/Script/VoxelGraph.VoxelNode_FNotEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILess", NewName="/Script/VoxelGraph.VoxelNode_ILess") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILessEqual", NewName="/Script/VoxelGraph.VoxelNode_ILessEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IGreater", NewName="/Script/VoxelGraph.VoxelNode_IGreater") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IGreaterEqual", NewName="/Script/VoxelGraph.VoxelNode_IGreaterEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IEqual", NewName="/Script/VoxelGraph.VoxelNode_IEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_INotEqual", NewName="/Script/VoxelGraph.VoxelNode_INotEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FAdd", NewName="/Script/VoxelGraph.VoxelNode_FAdd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMultiply", NewName="/Script/VoxelGraph.VoxelNode_FMultiply") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FSubstract", NewName="/Script/VoxelGraph.VoxelNode_FSubstract") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FDivide", NewName="/Script/VoxelGraph.VoxelNode_FDivide") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IAdd", NewName="/Script/VoxelGraph.VoxelNode_IAdd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMultiply", NewName="/Script/VoxelGraph.VoxelNode_IMultiply") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ISubstract", NewName="/Script/VoxelGraph.VoxelNode_ISubstract") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IDivide", NewName="/Script/VoxelGraph.VoxelNode_IDivide") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILeftBitShift", NewName="/Script/VoxelGraph.VoxelNode_ILeftBitShift") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IRightBitShift", NewName="/Script/VoxelGraph.VoxelNode_IRightBitShift") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FloatOfInt", NewName="/Script/VoxelGraph.VoxelNode_FloatOfInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Round", NewName="/Script/VoxelGraph.VoxelNode_Round") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Lerp", NewName="/Script/VoxelGraph.VoxelNode_Lerp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Clamp", NewName="/Script/VoxelGraph.VoxelNode_Clamp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BAnd", NewName="/Script/VoxelGraph.VoxelNode_BAnd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BOr", NewName="/Script/VoxelGraph.VoxelNode_BOr") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BNot", NewName="/Script/VoxelGraph.VoxelNode_BNot") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SwitchInt", NewName="/Script/VoxelGraph.VoxelNode_SwitchInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SwitchFloat", NewName="/Script/VoxelGraph.VoxelNode_SwitchFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_1MinusX", NewName="/Script/VoxelGraph.VoxelNode_1MinusX") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sqrt", NewName="/Script/VoxelGraph.VoxelNode_Sqrt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Pow", NewName="/Script/VoxelGraph.VoxelNode_Pow") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMod", NewName="/Script/VoxelGraph.VoxelNode_IMod") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FAbs", NewName="/Script/VoxelGraph.VoxelNode_FAbs") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IAbs", NewName="/Script/VoxelGraph.VoxelNode_IAbs") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Ceil", NewName="/Script/VoxelGraph.VoxelNode_Ceil") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Floor", NewName="/Script/VoxelGraph.VoxelNode_Floor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VectorLength", NewName="/Script/VoxelGraph.VoxelNode_VectorLength") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Fraction", NewName="/Script/VoxelGraph.VoxelNode_Fraction") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FSign", NewName="/Script/VoxelGraph.VoxelNode_FSign") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ISign", NewName="/Script/VoxelGraph.VoxelNode_ISign") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_InvSqrt", NewName="/Script/VoxelGraph.VoxelNode_InvSqrt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Loge", NewName="/Script/VoxelGraph.VoxelNode_Loge") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Exp", NewName="/Script/VoxelGraph.VoxelNode_Exp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sin", NewName="/Script/VoxelGraph.VoxelNode_Sin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Asin", NewName="/Script/VoxelGraph.VoxelNode_Asin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sinh", NewName="/Script/VoxelGraph.VoxelNode_Sinh") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Cos", NewName="/Script/VoxelGraph.VoxelNode_Cos") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Acos", NewName="/Script/VoxelGraph.VoxelNode_Acos") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Tan", NewName="/Script/VoxelGraph.VoxelNode_Tan") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Atan", NewName="/Script/VoxelGraph.VoxelNode_Atan") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Atan2", NewName="/Script/VoxelGraph.VoxelNode_Atan2") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VectorRotateAngleAxis", NewName="/Script/VoxelGraph.VoxelNode_VectorRotateAngleAxis") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_PerlinWormDistance", NewName="/Script/VoxelGraph.VoxelNode_PerlinWormDistance") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_LOD", NewName="/Script/VoxelGraph.VoxelNode_LOD") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RandomFloat", NewName="/Script/VoxelGraph.VoxelNode_RandomFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RandomInt", NewName="/Script/VoxelGraph.VoxelNode_RandomInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VoxelSize", NewName="/Script/VoxelGraph.VoxelNode_VoxelSize") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_If", NewName="/Script/VoxelGraph.VoxelNode_If") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetValue", NewName="/Script/VoxelGraph.VoxelNode_SetValue") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetColor", NewName="/Script/VoxelGraph.VoxelNode_SetColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetDoubleIndex", NewName="/Script/VoxelGraph.VoxelNode_SetDoubleIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetVoxelSpawnedActorId", NewName="/Script/VoxelGraph.VoxelNode_SetVoxelSpawnedActorId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetVoxelGrassId", NewName="/Script/VoxelGraph.VoxelNode_SetVoxelGrassId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetIndex", NewName="/Script/VoxelGraph.VoxelNode_SetIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetNode", NewName="/Script/VoxelGraph.VoxelNode_SetNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FunctionSeparator", NewName="/Script/VoxelGraph.VoxelNode_FunctionSeparator") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FlowMerge", NewName="/Script/VoxelGraph.VoxelNode_FlowMerge") ++ClassRedirects=(OldName="/Script/Voxel.VoxelExposedNode", NewName="/Script/VoxelGraph.VoxelExposedNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FConstant", NewName="/Script/VoxelGraph.VoxelNode_FConstant") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IConstant", NewName="/Script/VoxelGraph.VoxelNode_IConstant") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_WorldGeneratorSampler", NewName="/Script/VoxelGraph.VoxelNode_WorldGeneratorSampler") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Curve", NewName="/Script/VoxelGraph.VoxelNode_Curve") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_CurveColor", NewName="/Script/VoxelGraph.VoxelNode_CurveColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphBlueprintTools", NewName="/Script/VoxelGraph.VoxelGraphBlueprintTools") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroInputOutputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroInputOutputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroInputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroInputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroOutputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroOutputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphGenerator", NewName="/Script/VoxelGraph.VoxelGraphGenerator") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacro", NewName="/Script/VoxelGraph.VoxelGraphMacro") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroNode", NewName="/Script/VoxelGraph.VoxelGraphMacroNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphPreviewSettings", NewName="/Script/VoxelGraph.VoxelGraphPreviewSettings") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_NoiseNode", NewName="/Script/VoxelGraph.VoxelNode_NoiseNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_NoiseNodeFractal", NewName="/Script/VoxelGraph.VoxelNode_NoiseNodeFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DValueNoise", NewName="/Script/VoxelGraph.VoxelNode_2DValueNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DValueNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DValueNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DPerlinNoise", NewName="/Script/VoxelGraph.VoxelNode_2DPerlinNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DPerlinNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DPerlinNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DSimplexNoise", NewName="/Script/VoxelGraph.VoxelNode_2DSimplexNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DSimplexNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DSimplexNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCubicNoise", NewName="/Script/VoxelGraph.VoxelNode_2DCubicNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCubicNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DCubicNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DWhiteNoise", NewName="/Script/VoxelGraph.VoxelNode_2DWhiteNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DValueNoise", NewName="/Script/VoxelGraph.VoxelNode_3DValueNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DValueNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DValueNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DPerlinNoise", NewName="/Script/VoxelGraph.VoxelNode_3DPerlinNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DPerlinNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DPerlinNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DSimplexNoise", NewName="/Script/VoxelGraph.VoxelNode_3DSimplexNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DSimplexNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DSimplexNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCubicNoise", NewName="/Script/VoxelGraph.VoxelNode_3DCubicNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCubicNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DCubicNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DWhiteNoise", NewName="/Script/VoxelGraph.VoxelNode_3DWhiteNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_CellularNoise", NewName="/Script/VoxelGraph.VoxelNode_CellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCellularNoise", NewName="/Script/VoxelGraph.VoxelNode_2DCellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCellularNoise", NewName="/Script/VoxelGraph.VoxelNode_3DCellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_GradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_GradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DGradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_2DGradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DGradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_2DGradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DGradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_3DGradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DGradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_3DGradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_LODSwitch", NewName="/Script/VoxelGraph.VoxelNode_LODSwitch") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_StaticClampFloat", NewName="/Script/VoxelGraph.VoxelNode_StaticClampFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RangeAnalysisDebuggerFloat", NewName="/Script/VoxelGraph.VoxelNode_RangeAnalysisDebuggerFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Cache2D", NewName="/Script/VoxelGraph.VoxelNode_Cache2D") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sleep", NewName="/Script/VoxelGraph.VoxelNode_Sleep") ++ClassRedirects=(OldName="/Script/Voxel.VoxelPortalNodeInput", NewName="/Script/VoxelGraph.VoxelPortalNodeInput") ++ClassRedirects=(OldName="/Script/Voxel.VoxelPortalNodeOutput", NewName="/Script/VoxelGraph.VoxelPortalNodeOutput") ++ClassRedirects=(OldName="/Script/Voxel.VoxelSeedNode", NewName="/Script/VoxelGraph.VoxelSeedNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Seed", NewName="/Script/VoxelGraph.VoxelNode_Seed") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_AddSeeds", NewName="/Script/VoxelGraph.VoxelNode_AddSeeds") + ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelDebugNode", NewName="/Script/VoxelGraphEditor.VoxelDebugNode") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraph", NewName="/Script/VoxelGraphEditor.VoxelGraph") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphShortcuts", NewName="/Script/VoxelGraphEditor.VoxelGraphShortcuts") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Base", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Base") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode", NewName="/Script/VoxelGraphEditor.VoxelGraphNode") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Knot", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Knot") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Root", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Root") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphSchema", NewName="/Script/VoxelGraphEditor.VoxelGraphSchema") + ++FunctionRedirects=(OldName="VoxelTools.SetValueSphere", NewName="VoxelSphereTools.SetValueSphere") ++FunctionRedirects=(OldName="VoxelTools.AddSphere", NewName="VoxelSphereTools.AddSphere") ++FunctionRedirects=(OldName="VoxelTools.RemoveSphere", NewName="VoxelSphereTools.RemoveSphere") ++FunctionRedirects=(OldName="VoxelTools.SetMaterialSphere", NewName="VoxelSphereTools.SetMaterialSphere") + ++FunctionRedirects=(OldName="VoxelTools.SetValueBox", NewName="VoxelBoxTools.SetValueBox") ++FunctionRedirects=(OldName="VoxelTools.AddBox", NewName="VoxelBoxTools.AddBox") ++FunctionRedirects=(OldName="VoxelTools.RemoveBox", NewName="VoxelBoxTools.RemoveBox") ++FunctionRedirects=(OldName="VoxelTools.SetMaterialBox", NewName="VoxelBoxTools.SetMaterialBox") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetValue", NewName="VoxelDataTools.GetValue") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SetValue", NewName="VoxelDataTools.SetValue") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetMaterial", NewName="VoxelDataTools.GetMaterial") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SetMaterial", NewName="VoxelDataTools.SetMaterial") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetSave", NewName="VoxelDataTools.GetSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetCompressedSave", NewName="VoxelDataTools.GetCompressedSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.LoadFromSave", NewName="VoxelDataTools.LoadFromSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.LoadFromCompressedSave", NewName="VoxelDataTools.LoadFromCompressedSave") + ++PropertyRedirects=(OldName="VoxelDataAsset.Offset",NewName="PositionOffset") ++PropertyRedirects=(OldName="VoxelWorld.MeshThreadCount",NewName="NumberOfThreads") + ++ClassRedirects=(OldName="VoxelActorWithAutoDisable", NewName="VoxelActorWithStaticMeshAndAutoDisable") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.ConnectToServer", NewName="VoxelBlueprintLibrary.ConnectToServerTCP") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.StartServer", NewName="VoxelBlueprintLibrary.StartServerTCP") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateIndexPaintMaterial", NewName="VoxelBlueprintLibrary.CreateSingleIndexPaintMaterial") + ++ClassRedirects=(OldName="VoxelEditorTool_SculptPaintBase", NewName="VoxelBPEditorTool") ++FunctionRedirects=(OldName="VoxelBPEditorTool.EndEdit", NewName="VoxelBPEditorTool.SaveFrame") + ++EnumRedirects=(OldName="EVoxelEditorTool_SculptPaint", NewName="EVoxelEditorToolMode") + ++FunctionRedirects=(OldName="VoxelGraphBlueprintTools.SetVoxelGraphFloatConstantValue", NewName="VoxelGraphBlueprintTools.SetVoxelGraphFloatParameter") ++FunctionRedirects=(OldName="VoxelGraphBlueprintTools.SetVoxelGraphIntConstantValue", NewName="VoxelGraphBlueprintTools.SetVoxelGraphIntParameter") ++ClassRedirects=(OldName="VoxelNode_FConstant", NewName="VoxelNode_FloatParameter") ++ClassRedirects=(OldName="VoxelNode_IConstant", NewName="VoxelNode_IntParameter") ++ClassRedirects=(OldName="VoxelNode_ColorConstant", NewName="VoxelNode_ColorParameter") + ++ClassRedirects=(OldName="VoxelNode_SetIndex", NewName="VoxelNode_SetSingleIndex") ++ClassRedirects=(OldName="VoxelPortalNodeInput", NewName="VoxelLocalVariableDeclaration") ++ClassRedirects=(OldName="VoxelPortalNodeOutput", NewName="VoxelLocalVariableUsage") + ++ClassRedirects=(OldName="VoxelExclusionBox", NewName="VoxelDisableEditsBox") ++ClassRedirects=(OldName="EmptyWorldGenerator", NewName="VoxelEmptyWorldGenerator") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.EnableVoxelActorsInArea", NewName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea") ++FunctionRedirects=(OldName="VoxelPhysicsTools.RemoveFloatingBlocks", NewName="VoxelPhysicsTools.ApplyVoxelPhysics") + ++PropertyRedirects=(OldName="VoxelGraphGenerator.FirstNodePindId",NewName="FirstNodePinId") + ++StructRedirects=(OldName="VoxelPinNameAndType", NewName="VoxelGraphMacroPin") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SpawnFloatingVoxelActors", NewName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea") + ++StructRedirects=(OldName="VoxelSpawnerConfigElementAdvanced", NewName="VoxelSpawnerConfigElementAdvanced_Base") ++StructRedirects=(OldName="VoxelSpawnerConfigElement", NewName="VoxelSpawnerConfigElement_Height") ++StructRedirects=(OldName="VoxelSpawnerConfigHeightElement", NewName="VoxelSpawnerConfigHeightGroup") + ++ClassRedirects=(OldName="VoxelActor", NewName="VoxelSpawnerActor") ++ClassRedirects=(OldName="VoxelActorWithStaticMesh", NewName="VoxelSpawnerActorWithStaticMesh") ++ClassRedirects=(OldName="VoxelActorWithStaticMeshAndAutoDisable", NewName="VoxelSpawnerActorWithStaticMeshAndAutoDisable") ++EnumRedirects=(OldName="EVoxelActorSpawnType", NewName="EVoxelSpawnerActorSpawnType") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea", NewName="VoxelBlueprintLibrary.SpawnVoxelSpawnerActorsInArea") + ++ClassRedirects=(OldName="VoxelInvokerComponent", NewName="VoxelSimpleInvokerComponent") + ++PropertyRedirects=(OldName="VoxelWorld.OctreeDepth", NewName="RenderOctreeDepth") + ++ClassRedirects=(OldName="VoxelAutoDisableComponent", NewName="VoxelPhysicsRelevancyComponent") ++ClassRedirects=(OldName="VoxelSpawnerActorWithStaticMeshAndAutoDisable", NewName="VoxelMeshWithPhysicsRelevancySpawnerActor") ++ClassRedirects=(OldName="VoxelSpawnerActorWithStaticMesh", NewName="VoxelMeshSpawnerActor") + ++PropertyRedirects=(OldName="VoxelTransformableWorldGeneratorPicker.WorldGeneratorObject", NewName="Object") ++PropertyRedirects=(OldName="VoxelTransformableWorldGeneratorPicker.WorldGeneratorClass", NewName="Class") + ++PropertyRedirects=(OldName="VoxelWorldGeneratorPicker.WorldGeneratorObject", NewName="Object") ++PropertyRedirects=(OldName="VoxelWorldGeneratorPicker.WorldGeneratorClass", NewName="Class") + ++PropertyRedirects=(OldName="VoxelBasicSpawner.PositionOffset", NewName="GlobalPositionOffset") ++PropertyRedirects=(OldName="VoxelBasicSpawner.RotationOffset", NewName="LocalRotationOffset") + ++FunctionRedirects=(OldName="VoxelWorld.GlobalToLocalFloat", NewName="VoxelWorld.GlobalToLocalFloatBP") ++FunctionRedirects=(OldName="VoxelWorld.LocalToGlobalFloat", NewName="VoxelWorld.LocalToGlobalFloatBP") + ++EnumRedirects=(OldName="EVoxelBasicSpawnerRotation", ValueChanges=(("DoNotAlign", "RandomAlign"))) + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateOrUpdateTextureFromVoxelTexture", NewName="VoxelBlueprintLibrary.CreateOrUpdateTextureFromVoxelFloatTexture") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateTextureFromVoxelTexture", NewName="VoxelBlueprintLibrary.CreateTextureFromVoxelFloatTexture") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateVoxelTextureFromTextureChannel", NewName="VoxelBlueprintLibrary.CreateVoxelFloatTextureFromTextureChannel") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetVoxelTextureSize", NewName="VoxelBlueprintLibrary.GetVoxelFloatTextureSize") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.IsVoxelTextureValid", NewName="VoxelBlueprintLibrary.IsVoxelFloatTextureValid") + ++FunctionRedirects=(OldName="VoxelWorldGeneratorTools.CreateTextureFromWorldGenerator", NewName="VoxelWorldGeneratorTools.CreateFloatTextureFromWorldGenerator") ++FunctionRedirects=(OldName="VoxelWorldGeneratorTools.CreateTextureFromWorldGeneratorAsync", NewName="VoxelWorldGeneratorTools.CreateFloatTextureFromWorldGeneratorAsync") + ++ClassRedirects=(OldName="IntBoxLibrary", NewName="VoxelIntBoxLibrary") ++StructRedirects=(OldName="IntBox", NewName="VoxelIntBox") + ++EnumRedirects=(OldName="EVoxelMaterialConfig", ValueChanges=(("DoubleIndex", "MultiIndex"))) + ++EnumRedirects=(OldName="EVoxelToolManagerFalloff", NewName="EVoxelFalloff") ++EnumRedirects=(OldName="EVoxelToolManagerAlignment", NewName="EVoxelToolAlignment") + ++FunctionRedirects=(OldName="VoxelTool.AdvancedTick", NewName="VoxelTool.K2_AdvancedTick") ++FunctionRedirects=(OldName="VoxelTool.SimpleTick", NewName="VoxelTool.K2_SimpleTick") + ++EnumRedirects=(OldName="EFractalType", NewName="EVoxelNoiseFractalType") ++EnumRedirects=(OldName="EInterp", NewName="EVoxelNoiseInterpolation") ++EnumRedirects=(OldName="ECellularDistanceFunction", NewName="EVoxelCellularDistanceFunction") ++EnumRedirects=(OldName="ECellularReturnType", NewName="EVoxelCellularReturnType") + ++EnumRedirects=(OldName="EVoxelDataItemSampleCombineMode", NewName="EVoxelDataItemCombineMode") + ++ClassRedirects=(OldName="VoxelWorldGenerator", NewName="VoxelGenerator") ++ClassRedirects=(OldName="VoxelTransformableWorldGenerator", NewName="VoxelTransformableGenerator") ++ClassRedirects=(OldName="VoxelTransformableWorldGeneratorWithBounds", NewName="VoxelTransformableGeneratorWithBounds") + ++StructRedirects=(OldName="VoxelWorldGeneratorPicker", NewName="VoxelGeneratorPicker") ++StructRedirects=(OldName="VoxelTransformableWorldGeneratorPicker", NewName="VoxelTransformableGeneratorPicker") + ++PropertyRedirects=(OldName="VoxelWorld.WorldGenerator",NewName="Generator") + ++FunctionRedirects=(OldName="VoxelWorld.SetWorldGeneratorObject", NewName="SetGeneratorObject") ++FunctionRedirects=(OldName="VoxelWorld.SetWorldGeneratorClass", NewName="SetGeneratorClass") + ++ClassRedirects=(OldName="VoxelFlatWorldGenerator", NewName="VoxelFlatGenerator") ++ClassRedirects=(OldName="VoxelEmptyWorldGenerator", NewName="VoxelEmptyGenerator") + ++ClassRedirects=(OldName="VoxelWorldGeneratorTools", NewName="VoxelGeneratorTools") + ++EnumRedirects=(OldName="EVoxelWorldGeneratorPickerType", NewName="EVoxelGeneratorPickerType") + ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateFloatTextureFromWorldGenerator", NewName="CreateFloatTextureFromGenerator") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateFloatTextureFromWorldGeneratorAsync", NewName="CreateFloatTextureFromGeneratorAsync") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateColorTextureFromWorldGenerator", NewName="CreateColorTextureFromGenerator") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateColorTextureFromWorldGeneratorAsync", NewName="CreateColorTextureFromGeneratorAsync") + ++PropertyRedirects=(OldName="VoxelAssetActor.WorldGenerator",NewName="Generator") + ++PropertyRedirects=(OldName="VoxelSurfaceToolMask.WorldGenerator",NewName="Generator") ++EnumRedirects=(OldName="EVoxelSurfaceToolMaskType", ValueChanges=(("WorldGenerator", "Generator"))) + ++PropertyRedirects=(OldName="VoxelSpawnerConfig.WorldGeneratorOutputs",NewName="GeneratorOutputs") + ++PropertyRedirects=(OldName="VoxelGraphAssetNode.DefaultWorldGenerator",NewName="DefaultGenerator") ++ClassRedirects=(OldName="VoxelNode_WorldGeneratorMerge", NewName="VoxelNode_GeneratorMerge") ++ClassRedirects=(OldName="VoxelNode_WorldGeneratorSamplerBase", NewName="VoxelNode_GeneratorSamplerBase") ++ClassRedirects=(OldName="VoxelNode_SingleWorldGeneratorSamplerBase", NewName="VoxelNode_SingleGeneratorSamplerBase") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorValue", NewName="VoxelNode_GetGeneratorValue") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorMaterial", NewName="VoxelNode_GetGeneratorMaterial") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorCustomOutput", NewName="VoxelNode_GetGeneratorCustomOutput") ++PropertyRedirects=(OldName="VoxelNode_GeneratorMerge.WorldGenerators",NewName="Generators") ++PropertyRedirects=(OldName="VoxelNode_SingleGeneratorSamplerBase.WorldGenerator",NewName="Generator") \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Config/DefaultVoxelPro.ini b/voxel_cpp_test/Plugins/Voxel/Config/DefaultVoxelPro.ini new file mode 100644 index 00000000..a3d34a7f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Config/DefaultVoxelPro.ini @@ -0,0 +1,388 @@ +[CoreRedirects] ++EnumRedirects=(OldName="EVoxelMaterialConfig", ValueChanges=(("IndexWithMultipleMaterials", "SingleIndex"), ("DoubleIndexWithMultipleMaterials", "DoubleIndex"))) ++EnumRedirects=(OldName="EVoxelPinCategoryDataOnly", NewName="EVoxelPortalNodePinCategory") ++EnumRedirects=(OldName="EVoxelSpawnerScaling", NewName="EVoxelMeshSpawnerScaling") ++EnumRedirects=(OldName="EVoxelSpawnerRotation", NewName="EVoxelMeshSpawnerRotation") + ++FunctionRedirects=(OldName="VoxelWorld.ClearCache", NewName="VoxelBlueprintLibrary.ClearCache") ++FunctionRedirects=(OldName="VoxelWorld.ClearAllCache", NewName="VoxelBlueprintLibrary.ClearAllCache") ++FunctionRedirects=(OldName="VoxelWorld.Cache", NewName="VoxelBlueprintLibrary.Cache") ++FunctionRedirects=(OldName="VoxelWorld.Undo", NewName="VoxelBlueprintLibrary.Undo") ++FunctionRedirects=(OldName="VoxelWorld.Redo", NewName="VoxelBlueprintLibrary.Redo") ++FunctionRedirects=(OldName="VoxelWorld.SaveFrame", NewName="VoxelBlueprintLibrary.SaveFrame") ++FunctionRedirects=(OldName="VoxelWorld.ClearFrames", NewName="VoxelBlueprintLibrary.ClearFrames") ++FunctionRedirects=(OldName="VoxelWorld.GetValue", NewName="VoxelBlueprintLibrary.GetValue") ++FunctionRedirects=(OldName="VoxelWorld.SetValue", NewName="VoxelBlueprintLibrary.SetValue") ++FunctionRedirects=(OldName="VoxelWorld.GetMaterial", NewName="VoxelBlueprintLibrary.GetMaterial") ++FunctionRedirects=(OldName="VoxelWorld.SetMaterial", NewName="VoxelBlueprintLibrary.SetMaterial") ++FunctionRedirects=(OldName="VoxelWorld.GetSave", NewName="VoxelBlueprintLibrary.GetSave") ++FunctionRedirects=(OldName="VoxelWorld.GetCompressedSave", NewName="VoxelBlueprintLibrary.GetCompressedSave") ++FunctionRedirects=(OldName="VoxelWorld.LoadFromSave", NewName="VoxelBlueprintLibrary.LoadFromSave") ++FunctionRedirects=(OldName="VoxelWorld.LoadFromCompressedSave", NewName="VoxelBlueprintLibrary.LoadFromCompressedSave") ++FunctionRedirects=(OldName="VoxelWorld.GetNormal", NewName="VoxelBlueprintLibrary.GetNormal") ++FunctionRedirects=(OldName="VoxelWorld.GetFloatOutput", NewName="VoxelBlueprintLibrary.GetFloatOutput") ++FunctionRedirects=(OldName="VoxelWorld.GetBounds", NewName="VoxelBlueprintLibrary.GetBounds") ++FunctionRedirects=(OldName="VoxelWorld.UpdatePosition", NewName="VoxelBlueprintLibrary.UpdatePosition") ++FunctionRedirects=(OldName="VoxelWorld.UpdateBounds", NewName="VoxelBlueprintLibrary.UpdateBounds") ++FunctionRedirects=(OldName="VoxelWorld.UpdateAll", NewName="VoxelBlueprintLibrary.UpdateAll") ++FunctionRedirects=(OldName="VoxelWorld.GetTaskCount", NewName="VoxelBlueprintLibrary.GetTaskCount") ++FunctionRedirects=(OldName="VoxelWorld.ConnectClient", NewName="VoxelBlueprintLibrary.ConnectToServer") ++FunctionRedirects=(OldName="VoxelWorld.StartServer", NewName="VoxelBlueprintLibrary.StartServer") ++FunctionRedirects=(OldName="VoxelWorld.EnableFloatingActorsInArea", NewName="VoxelBlueprintLibrary.EnableFloatingActorsInArea") ++FunctionRedirects=(OldName="VoxelWorld.EnableActorsInArea", NewName="VoxelBlueprintLibrary.EnableActorsInArea") ++FunctionRedirects=(OldName="VoxelWorld.EnableInstanceInArea", NewName="VoxelBlueprintLibrary.EnableInstanceInArea") ++FunctionRedirects=(OldName="VoxelWorld.GetActorClassFromHISM", NewName="VoxelBlueprintLibrary.GetActorClassFromHISM") ++FunctionRedirects=(OldName="VoxelWorld.AddInstance", NewName="VoxelBlueprintLibrary.AddInstance") ++FunctionRedirects=(OldName="VoxelWorld.RemoveInstance", NewName="VoxelBlueprintLibrary.RemoveInstance") ++FunctionRedirects=(OldName="VoxelPoolManager.CreateGlobalVoxelThreadPool", NewName="VoxelBlueprintLibrary.CreateGlobalVoxelThreadPool") ++FunctionRedirects=(OldName="VoxelPoolManager.DestroyGlobalVoxelThreadPool", NewName="VoxelBlueprintLibrary.DestroyGlobalVoxelThreadPool") ++FunctionRedirects=(OldName="VoxelPoolManager.IsGlobalVoxelPoolCreated", NewName="VoxelBlueprintLibrary.IsGlobalVoxelPoolCreated") ++FunctionRedirects=(OldName="VoxelBPUtilities.MakeBoxFromGlobalPositionAndRadius", NewName="VoxelBlueprintLibrary.MakeBoxFromGlobalPositionAndRadius") ++FunctionRedirects=(OldName="VoxelBPUtilities.Add_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Add_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Substract_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Substract_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntVectorIntVector", NewName="VoxelBlueprintLibrary.Multiply_IntVectorIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.Divide_IntVectorInt", NewName="VoxelBlueprintLibrary.Divide_IntVectorInt") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntVectorInt", NewName="VoxelBlueprintLibrary.Multiply_IntVectorInt") ++FunctionRedirects=(OldName="VoxelBPUtilities.Multiply_IntIntVector", NewName="VoxelBlueprintLibrary.Multiply_IntIntVector") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateRGBPaintMaterial", NewName="VoxelBlueprintLibrary.CreateRGBPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateIndexPaintMaterial", NewName="VoxelBlueprintLibrary.CreateIndexPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateDoubleIndexSetPaintMaterial", NewName="VoxelBlueprintLibrary.CreateDoubleIndexSetPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateDoubleIndexBlendPaintMaterial", NewName="VoxelBlueprintLibrary.CreateDoubleIndexBlendPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateGrassPaintMaterial", NewName="VoxelBlueprintLibrary.CreateGrassPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateActorPaintMaterial", NewName="VoxelBlueprintLibrary.CreateActorPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.ApplyPaintMaterial", NewName="VoxelBlueprintLibrary.ApplyPaintMaterial") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetColor", NewName="VoxelBlueprintLibrary.GetColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetColor", NewName="VoxelBlueprintLibrary.SetColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromColor", NewName="VoxelBlueprintLibrary.CreateMaterialFromColor") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndex", NewName="VoxelBlueprintLibrary.GetIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndex", NewName="VoxelBlueprintLibrary.SetIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromIndex", NewName="VoxelBlueprintLibrary.CreateMaterialFromIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndexA", NewName="VoxelBlueprintLibrary.GetIndexA") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetIndexB", NewName="VoxelBlueprintLibrary.GetIndexB") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetBlend", NewName="VoxelBlueprintLibrary.GetBlend") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndexA", NewName="VoxelBlueprintLibrary.SetIndexA") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetIndexB", NewName="VoxelBlueprintLibrary.SetIndexB") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetBlend", NewName="VoxelBlueprintLibrary.SetBlend") ++FunctionRedirects=(OldName="VoxelBPUtilities.CreateMaterialFromDoubleIndex", NewName="VoxelBlueprintLibrary.CreateMaterialFromDoubleIndex") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetVoxelActorId", NewName="VoxelBlueprintLibrary.GetVoxelActorId") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetVoxelActorId", NewName="VoxelBlueprintLibrary.SetVoxelActorId") ++FunctionRedirects=(OldName="VoxelBPUtilities.GetVoxelGrassId", NewName="VoxelBlueprintLibrary.GetVoxelGrassId") ++FunctionRedirects=(OldName="VoxelBPUtilities.SetVoxelGrassId", NewName="VoxelBlueprintLibrary.SetVoxelGrassId") ++FunctionRedirects=(OldName="VoxelBPUtilities.TranslateBox", NewName="IntBoxLibrary.TranslateBox") ++FunctionRedirects=(OldName="VoxelBPUtilities.Conv_IntVectorToString", NewName="IntBoxLibrary.Conv_IntVectorToString") ++FunctionRedirects=(OldName="VoxelBPUtilities.MakeBoxFromLocalPositionAndRadius", NewName="IntBoxLibrary.MakeBoxFromLocalPositionAndRadius") ++FunctionRedirects=(OldName="VoxelWorld.AddWorms", NewName="VoxelPlaceableItemsUtilities.AddWorms") + ++ClassRedirects=(OldName="VoxelGraph", NewName="VoxelEdGraph") + ++ClassRedirects=(OldName="/Script/Voxel.VoxelColorWheel", NewName="/Script/VoxelHelpers.VoxelColorWheel") ++ClassRedirects=(OldName="/Script/Voxel.MaterialExpressionBlendMaterialAttributesBarycentric", NewName="/Script/VoxelHelpers.MaterialExpressionBlendMaterialAttributesBarycentric") + ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode", NewName="/Script/VoxelGraph.VoxelNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNodeHelper", NewName="/Script/VoxelGraph.VoxelNodeHelper") ++ClassRedirects=(OldName="/Script/Voxel.VoxelMaterialNode", NewName="/Script/VoxelGraph.VoxelMaterialNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelFloatNode", NewName="/Script/VoxelGraph.VoxelFloatNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelIntNode", NewName="/Script/VoxelGraph.VoxelIntNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_XF", NewName="/Script/VoxelGraph.VoxelNode_XF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_YF", NewName="/Script/VoxelGraph.VoxelNode_YF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ZF", NewName="/Script/VoxelGraph.VoxelNode_ZF") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_XI", NewName="/Script/VoxelGraph.VoxelNode_XI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_YI", NewName="/Script/VoxelGraph.VoxelNode_YI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ZI", NewName="/Script/VoxelGraph.VoxelNode_ZI") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetColor", NewName="/Script/VoxelGraph.VoxelNode_GetColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetVoxelSpawnedActorId", NewName="/Script/VoxelGraph.VoxelNode_GetVoxelSpawnedActorId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetVoxelGrassId", NewName="/Script/VoxelGraph.VoxelNode_GetVoxelGrassId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetIndex", NewName="/Script/VoxelGraph.VoxelNode_GetIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GetDoubleIndex", NewName="/Script/VoxelGraph.VoxelNode_GetDoubleIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMax", NewName="/Script/VoxelGraph.VoxelNode_FMax") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMin", NewName="/Script/VoxelGraph.VoxelNode_FMin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMax", NewName="/Script/VoxelGraph.VoxelNode_IMax") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMin", NewName="/Script/VoxelGraph.VoxelNode_IMin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FLess", NewName="/Script/VoxelGraph.VoxelNode_FLess") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FLessEqual", NewName="/Script/VoxelGraph.VoxelNode_FLessEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FGreater", NewName="/Script/VoxelGraph.VoxelNode_FGreater") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FGreaterEqual", NewName="/Script/VoxelGraph.VoxelNode_FGreaterEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FEqual", NewName="/Script/VoxelGraph.VoxelNode_FEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FNotEqual", NewName="/Script/VoxelGraph.VoxelNode_FNotEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILess", NewName="/Script/VoxelGraph.VoxelNode_ILess") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILessEqual", NewName="/Script/VoxelGraph.VoxelNode_ILessEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IGreater", NewName="/Script/VoxelGraph.VoxelNode_IGreater") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IGreaterEqual", NewName="/Script/VoxelGraph.VoxelNode_IGreaterEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IEqual", NewName="/Script/VoxelGraph.VoxelNode_IEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_INotEqual", NewName="/Script/VoxelGraph.VoxelNode_INotEqual") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FAdd", NewName="/Script/VoxelGraph.VoxelNode_FAdd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FMultiply", NewName="/Script/VoxelGraph.VoxelNode_FMultiply") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FSubstract", NewName="/Script/VoxelGraph.VoxelNode_FSubstract") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FDivide", NewName="/Script/VoxelGraph.VoxelNode_FDivide") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IAdd", NewName="/Script/VoxelGraph.VoxelNode_IAdd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMultiply", NewName="/Script/VoxelGraph.VoxelNode_IMultiply") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ISubstract", NewName="/Script/VoxelGraph.VoxelNode_ISubstract") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IDivide", NewName="/Script/VoxelGraph.VoxelNode_IDivide") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ILeftBitShift", NewName="/Script/VoxelGraph.VoxelNode_ILeftBitShift") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IRightBitShift", NewName="/Script/VoxelGraph.VoxelNode_IRightBitShift") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FloatOfInt", NewName="/Script/VoxelGraph.VoxelNode_FloatOfInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Round", NewName="/Script/VoxelGraph.VoxelNode_Round") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Lerp", NewName="/Script/VoxelGraph.VoxelNode_Lerp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Clamp", NewName="/Script/VoxelGraph.VoxelNode_Clamp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BAnd", NewName="/Script/VoxelGraph.VoxelNode_BAnd") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BOr", NewName="/Script/VoxelGraph.VoxelNode_BOr") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_BNot", NewName="/Script/VoxelGraph.VoxelNode_BNot") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SwitchInt", NewName="/Script/VoxelGraph.VoxelNode_SwitchInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SwitchFloat", NewName="/Script/VoxelGraph.VoxelNode_SwitchFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_1MinusX", NewName="/Script/VoxelGraph.VoxelNode_1MinusX") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sqrt", NewName="/Script/VoxelGraph.VoxelNode_Sqrt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Pow", NewName="/Script/VoxelGraph.VoxelNode_Pow") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IMod", NewName="/Script/VoxelGraph.VoxelNode_IMod") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FAbs", NewName="/Script/VoxelGraph.VoxelNode_FAbs") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IAbs", NewName="/Script/VoxelGraph.VoxelNode_IAbs") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Ceil", NewName="/Script/VoxelGraph.VoxelNode_Ceil") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Floor", NewName="/Script/VoxelGraph.VoxelNode_Floor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VectorLength", NewName="/Script/VoxelGraph.VoxelNode_VectorLength") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Fraction", NewName="/Script/VoxelGraph.VoxelNode_Fraction") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FSign", NewName="/Script/VoxelGraph.VoxelNode_FSign") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_ISign", NewName="/Script/VoxelGraph.VoxelNode_ISign") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_InvSqrt", NewName="/Script/VoxelGraph.VoxelNode_InvSqrt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Loge", NewName="/Script/VoxelGraph.VoxelNode_Loge") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Exp", NewName="/Script/VoxelGraph.VoxelNode_Exp") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sin", NewName="/Script/VoxelGraph.VoxelNode_Sin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Asin", NewName="/Script/VoxelGraph.VoxelNode_Asin") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sinh", NewName="/Script/VoxelGraph.VoxelNode_Sinh") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Cos", NewName="/Script/VoxelGraph.VoxelNode_Cos") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Acos", NewName="/Script/VoxelGraph.VoxelNode_Acos") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Tan", NewName="/Script/VoxelGraph.VoxelNode_Tan") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Atan", NewName="/Script/VoxelGraph.VoxelNode_Atan") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Atan2", NewName="/Script/VoxelGraph.VoxelNode_Atan2") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VectorRotateAngleAxis", NewName="/Script/VoxelGraph.VoxelNode_VectorRotateAngleAxis") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_PerlinWormDistance", NewName="/Script/VoxelGraph.VoxelNode_PerlinWormDistance") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_LOD", NewName="/Script/VoxelGraph.VoxelNode_LOD") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RandomFloat", NewName="/Script/VoxelGraph.VoxelNode_RandomFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RandomInt", NewName="/Script/VoxelGraph.VoxelNode_RandomInt") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_VoxelSize", NewName="/Script/VoxelGraph.VoxelNode_VoxelSize") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_If", NewName="/Script/VoxelGraph.VoxelNode_If") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetValue", NewName="/Script/VoxelGraph.VoxelNode_SetValue") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetColor", NewName="/Script/VoxelGraph.VoxelNode_SetColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetDoubleIndex", NewName="/Script/VoxelGraph.VoxelNode_SetDoubleIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetVoxelSpawnedActorId", NewName="/Script/VoxelGraph.VoxelNode_SetVoxelSpawnedActorId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetVoxelGrassId", NewName="/Script/VoxelGraph.VoxelNode_SetVoxelGrassId") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetIndex", NewName="/Script/VoxelGraph.VoxelNode_SetIndex") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_SetNode", NewName="/Script/VoxelGraph.VoxelNode_SetNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FunctionSeparator", NewName="/Script/VoxelGraph.VoxelNode_FunctionSeparator") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FlowMerge", NewName="/Script/VoxelGraph.VoxelNode_FlowMerge") ++ClassRedirects=(OldName="/Script/Voxel.VoxelExposedNode", NewName="/Script/VoxelGraph.VoxelExposedNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_FConstant", NewName="/Script/VoxelGraph.VoxelNode_FConstant") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_IConstant", NewName="/Script/VoxelGraph.VoxelNode_IConstant") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_WorldGeneratorSampler", NewName="/Script/VoxelGraph.VoxelNode_WorldGeneratorSampler") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Curve", NewName="/Script/VoxelGraph.VoxelNode_Curve") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_CurveColor", NewName="/Script/VoxelGraph.VoxelNode_CurveColor") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphBlueprintTools", NewName="/Script/VoxelGraph.VoxelGraphBlueprintTools") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroInputOutputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroInputOutputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroInputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroInputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroOutputNode", NewName="/Script/VoxelGraph.VoxelGraphMacroOutputNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphGenerator", NewName="/Script/VoxelGraph.VoxelGraphGenerator") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacro", NewName="/Script/VoxelGraph.VoxelGraphMacro") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphMacroNode", NewName="/Script/VoxelGraph.VoxelGraphMacroNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelGraphPreviewSettings", NewName="/Script/VoxelGraph.VoxelGraphPreviewSettings") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_NoiseNode", NewName="/Script/VoxelGraph.VoxelNode_NoiseNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_NoiseNodeFractal", NewName="/Script/VoxelGraph.VoxelNode_NoiseNodeFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DValueNoise", NewName="/Script/VoxelGraph.VoxelNode_2DValueNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DValueNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DValueNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DPerlinNoise", NewName="/Script/VoxelGraph.VoxelNode_2DPerlinNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DPerlinNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DPerlinNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DSimplexNoise", NewName="/Script/VoxelGraph.VoxelNode_2DSimplexNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DSimplexNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DSimplexNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCubicNoise", NewName="/Script/VoxelGraph.VoxelNode_2DCubicNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCubicNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_2DCubicNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DWhiteNoise", NewName="/Script/VoxelGraph.VoxelNode_2DWhiteNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DValueNoise", NewName="/Script/VoxelGraph.VoxelNode_3DValueNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DValueNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DValueNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DPerlinNoise", NewName="/Script/VoxelGraph.VoxelNode_3DPerlinNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DPerlinNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DPerlinNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DSimplexNoise", NewName="/Script/VoxelGraph.VoxelNode_3DSimplexNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DSimplexNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DSimplexNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCubicNoise", NewName="/Script/VoxelGraph.VoxelNode_3DCubicNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCubicNoiseFractal", NewName="/Script/VoxelGraph.VoxelNode_3DCubicNoiseFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DWhiteNoise", NewName="/Script/VoxelGraph.VoxelNode_3DWhiteNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_CellularNoise", NewName="/Script/VoxelGraph.VoxelNode_CellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DCellularNoise", NewName="/Script/VoxelGraph.VoxelNode_2DCellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DCellularNoise", NewName="/Script/VoxelGraph.VoxelNode_3DCellularNoise") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_GradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_GradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_GradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DGradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_2DGradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_2DGradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_2DGradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DGradientPerturb", NewName="/Script/VoxelGraph.VoxelNode_3DGradientPerturb") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_3DGradientPerturbFractal", NewName="/Script/VoxelGraph.VoxelNode_3DGradientPerturbFractal") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_LODSwitch", NewName="/Script/VoxelGraph.VoxelNode_LODSwitch") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_StaticClampFloat", NewName="/Script/VoxelGraph.VoxelNode_StaticClampFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_RangeAnalysisDebuggerFloat", NewName="/Script/VoxelGraph.VoxelNode_RangeAnalysisDebuggerFloat") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Cache2D", NewName="/Script/VoxelGraph.VoxelNode_Cache2D") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Sleep", NewName="/Script/VoxelGraph.VoxelNode_Sleep") ++ClassRedirects=(OldName="/Script/Voxel.VoxelPortalNodeInput", NewName="/Script/VoxelGraph.VoxelPortalNodeInput") ++ClassRedirects=(OldName="/Script/Voxel.VoxelPortalNodeOutput", NewName="/Script/VoxelGraph.VoxelPortalNodeOutput") ++ClassRedirects=(OldName="/Script/Voxel.VoxelSeedNode", NewName="/Script/VoxelGraph.VoxelSeedNode") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_Seed", NewName="/Script/VoxelGraph.VoxelNode_Seed") ++ClassRedirects=(OldName="/Script/Voxel.VoxelNode_AddSeeds", NewName="/Script/VoxelGraph.VoxelNode_AddSeeds") + ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelDebugNode", NewName="/Script/VoxelGraphEditor.VoxelDebugNode") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraph", NewName="/Script/VoxelGraphEditor.VoxelGraph") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphShortcuts", NewName="/Script/VoxelGraphEditor.VoxelGraphShortcuts") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Base", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Base") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode", NewName="/Script/VoxelGraphEditor.VoxelGraphNode") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Knot", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Knot") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphNode_Root", NewName="/Script/VoxelGraphEditor.VoxelGraphNode_Root") ++ClassRedirects=(OldName="/Script/VoxelEditor.VoxelGraphSchema", NewName="/Script/VoxelGraphEditor.VoxelGraphSchema") + ++FunctionRedirects=(OldName="VoxelTools.SetValueSphere", NewName="VoxelSphereTools.SetValueSphere") ++FunctionRedirects=(OldName="VoxelTools.AddSphere", NewName="VoxelSphereTools.AddSphere") ++FunctionRedirects=(OldName="VoxelTools.RemoveSphere", NewName="VoxelSphereTools.RemoveSphere") ++FunctionRedirects=(OldName="VoxelTools.SetMaterialSphere", NewName="VoxelSphereTools.SetMaterialSphere") + ++FunctionRedirects=(OldName="VoxelTools.SetValueBox", NewName="VoxelBoxTools.SetValueBox") ++FunctionRedirects=(OldName="VoxelTools.AddBox", NewName="VoxelBoxTools.AddBox") ++FunctionRedirects=(OldName="VoxelTools.RemoveBox", NewName="VoxelBoxTools.RemoveBox") ++FunctionRedirects=(OldName="VoxelTools.SetMaterialBox", NewName="VoxelBoxTools.SetMaterialBox") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetValue", NewName="VoxelDataTools.GetValue") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SetValue", NewName="VoxelDataTools.SetValue") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetMaterial", NewName="VoxelDataTools.GetMaterial") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SetMaterial", NewName="VoxelDataTools.SetMaterial") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetSave", NewName="VoxelDataTools.GetSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetCompressedSave", NewName="VoxelDataTools.GetCompressedSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.LoadFromSave", NewName="VoxelDataTools.LoadFromSave") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.LoadFromCompressedSave", NewName="VoxelDataTools.LoadFromCompressedSave") + ++PropertyRedirects=(OldName="VoxelDataAsset.Offset",NewName="PositionOffset") ++PropertyRedirects=(OldName="VoxelWorld.MeshThreadCount",NewName="NumberOfThreads") + ++ClassRedirects=(OldName="VoxelActorWithAutoDisable", NewName="VoxelActorWithStaticMeshAndAutoDisable") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.ConnectToServer", NewName="VoxelBlueprintLibrary.ConnectToServerTCP") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.StartServer", NewName="VoxelBlueprintLibrary.StartServerTCP") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateIndexPaintMaterial", NewName="VoxelBlueprintLibrary.CreateSingleIndexPaintMaterial") + ++ClassRedirects=(OldName="VoxelEditorTool_SculptPaintBase", NewName="VoxelBPEditorTool") ++FunctionRedirects=(OldName="VoxelBPEditorTool.EndEdit", NewName="VoxelBPEditorTool.SaveFrame") + ++EnumRedirects=(OldName="EVoxelEditorTool_SculptPaint", NewName="EVoxelEditorToolMode") + ++FunctionRedirects=(OldName="VoxelGraphBlueprintTools.SetVoxelGraphFloatConstantValue", NewName="VoxelGraphBlueprintTools.SetVoxelGraphFloatParameter") ++FunctionRedirects=(OldName="VoxelGraphBlueprintTools.SetVoxelGraphIntConstantValue", NewName="VoxelGraphBlueprintTools.SetVoxelGraphIntParameter") ++ClassRedirects=(OldName="VoxelNode_FConstant", NewName="VoxelNode_FloatParameter") ++ClassRedirects=(OldName="VoxelNode_IConstant", NewName="VoxelNode_IntParameter") ++ClassRedirects=(OldName="VoxelNode_ColorConstant", NewName="VoxelNode_ColorParameter") + ++ClassRedirects=(OldName="VoxelNode_SetIndex", NewName="VoxelNode_SetSingleIndex") ++ClassRedirects=(OldName="VoxelPortalNodeInput", NewName="VoxelLocalVariableDeclaration") ++ClassRedirects=(OldName="VoxelPortalNodeOutput", NewName="VoxelLocalVariableUsage") + ++ClassRedirects=(OldName="VoxelExclusionBox", NewName="VoxelDisableEditsBox") ++ClassRedirects=(OldName="EmptyWorldGenerator", NewName="VoxelEmptyWorldGenerator") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.EnableVoxelActorsInArea", NewName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea") ++FunctionRedirects=(OldName="VoxelPhysicsTools.RemoveFloatingBlocks", NewName="VoxelPhysicsTools.ApplyVoxelPhysics") + ++PropertyRedirects=(OldName="VoxelGraphGenerator.FirstNodePindId",NewName="FirstNodePinId") + ++StructRedirects=(OldName="VoxelPinNameAndType", NewName="VoxelGraphMacroPin") + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SpawnFloatingVoxelActors", NewName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea") + ++StructRedirects=(OldName="VoxelSpawnerConfigElementAdvanced", NewName="VoxelSpawnerConfigElementAdvanced_Base") ++StructRedirects=(OldName="VoxelSpawnerConfigElement", NewName="VoxelSpawnerConfigElement_Height") ++StructRedirects=(OldName="VoxelSpawnerConfigHeightElement", NewName="VoxelSpawnerConfigHeightGroup") + ++ClassRedirects=(OldName="VoxelActor", NewName="VoxelSpawnerActor") ++ClassRedirects=(OldName="VoxelActorWithStaticMesh", NewName="VoxelSpawnerActorWithStaticMesh") ++ClassRedirects=(OldName="VoxelActorWithStaticMeshAndAutoDisable", NewName="VoxelSpawnerActorWithStaticMeshAndAutoDisable") ++EnumRedirects=(OldName="EVoxelActorSpawnType", NewName="EVoxelSpawnerActorSpawnType") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.SpawnVoxelActorsInArea", NewName="VoxelBlueprintLibrary.SpawnVoxelSpawnerActorsInArea") + ++ClassRedirects=(OldName="VoxelInvokerComponent", NewName="VoxelSimpleInvokerComponent") + ++PropertyRedirects=(OldName="VoxelWorld.OctreeDepth", NewName="RenderOctreeDepth") + ++ClassRedirects=(OldName="VoxelAutoDisableComponent", NewName="VoxelPhysicsRelevancyComponent") ++ClassRedirects=(OldName="VoxelSpawnerActorWithStaticMeshAndAutoDisable", NewName="VoxelMeshWithPhysicsRelevancySpawnerActor") ++ClassRedirects=(OldName="VoxelSpawnerActorWithStaticMesh", NewName="VoxelMeshSpawnerActor") + ++PropertyRedirects=(OldName="VoxelTransformableWorldGeneratorPicker.WorldGeneratorObject", NewName="Object") ++PropertyRedirects=(OldName="VoxelTransformableWorldGeneratorPicker.WorldGeneratorClass", NewName="Class") + ++PropertyRedirects=(OldName="VoxelWorldGeneratorPicker.WorldGeneratorObject", NewName="Object") ++PropertyRedirects=(OldName="VoxelWorldGeneratorPicker.WorldGeneratorClass", NewName="Class") + ++PropertyRedirects=(OldName="VoxelBasicSpawner.PositionOffset", NewName="GlobalPositionOffset") ++PropertyRedirects=(OldName="VoxelBasicSpawner.RotationOffset", NewName="LocalRotationOffset") + ++FunctionRedirects=(OldName="VoxelWorld.GlobalToLocalFloat", NewName="VoxelWorld.GlobalToLocalFloatBP") ++FunctionRedirects=(OldName="VoxelWorld.LocalToGlobalFloat", NewName="VoxelWorld.LocalToGlobalFloatBP") + ++EnumRedirects=(OldName="EVoxelBasicSpawnerRotation", ValueChanges=(("DoNotAlign", "RandomAlign"))) + ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateOrUpdateTextureFromVoxelTexture", NewName="VoxelBlueprintLibrary.CreateOrUpdateTextureFromVoxelFloatTexture") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateTextureFromVoxelTexture", NewName="VoxelBlueprintLibrary.CreateTextureFromVoxelFloatTexture") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.CreateVoxelTextureFromTextureChannel", NewName="VoxelBlueprintLibrary.CreateVoxelFloatTextureFromTextureChannel") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.GetVoxelTextureSize", NewName="VoxelBlueprintLibrary.GetVoxelFloatTextureSize") ++FunctionRedirects=(OldName="VoxelBlueprintLibrary.IsVoxelTextureValid", NewName="VoxelBlueprintLibrary.IsVoxelFloatTextureValid") + ++FunctionRedirects=(OldName="VoxelWorldGeneratorTools.CreateTextureFromWorldGenerator", NewName="VoxelWorldGeneratorTools.CreateFloatTextureFromWorldGenerator") ++FunctionRedirects=(OldName="VoxelWorldGeneratorTools.CreateTextureFromWorldGeneratorAsync", NewName="VoxelWorldGeneratorTools.CreateFloatTextureFromWorldGeneratorAsync") + ++ClassRedirects=(OldName="IntBoxLibrary", NewName="VoxelIntBoxLibrary") ++StructRedirects=(OldName="IntBox", NewName="VoxelIntBox") + ++EnumRedirects=(OldName="EVoxelMaterialConfig", ValueChanges=(("DoubleIndex", "MultiIndex"))) + ++EnumRedirects=(OldName="EVoxelToolManagerFalloff", NewName="EVoxelFalloff") ++EnumRedirects=(OldName="EVoxelToolManagerAlignment", NewName="EVoxelToolAlignment") + ++FunctionRedirects=(OldName="VoxelTool.AdvancedTick", NewName="VoxelTool.K2_AdvancedTick") ++FunctionRedirects=(OldName="VoxelTool.SimpleTick", NewName="VoxelTool.K2_SimpleTick") + ++EnumRedirects=(OldName="EFractalType", NewName="EVoxelNoiseFractalType") ++EnumRedirects=(OldName="EInterp", NewName="EVoxelNoiseInterpolation") ++EnumRedirects=(OldName="ECellularDistanceFunction", NewName="EVoxelCellularDistanceFunction") ++EnumRedirects=(OldName="ECellularReturnType", NewName="EVoxelCellularReturnType") + ++EnumRedirects=(OldName="EVoxelDataItemSampleCombineMode", NewName="EVoxelDataItemCombineMode") + ++ClassRedirects=(OldName="VoxelWorldGenerator", NewName="VoxelGenerator") ++ClassRedirects=(OldName="VoxelTransformableWorldGenerator", NewName="VoxelTransformableGenerator") ++ClassRedirects=(OldName="VoxelTransformableWorldGeneratorWithBounds", NewName="VoxelTransformableGeneratorWithBounds") + ++StructRedirects=(OldName="VoxelWorldGeneratorPicker", NewName="VoxelGeneratorPicker") ++StructRedirects=(OldName="VoxelTransformableWorldGeneratorPicker", NewName="VoxelTransformableGeneratorPicker") + ++PropertyRedirects=(OldName="VoxelWorld.WorldGenerator",NewName="Generator") + ++FunctionRedirects=(OldName="VoxelWorld.SetWorldGeneratorObject", NewName="SetGeneratorObject") ++FunctionRedirects=(OldName="VoxelWorld.SetWorldGeneratorClass", NewName="SetGeneratorClass") + ++ClassRedirects=(OldName="VoxelFlatWorldGenerator", NewName="VoxelFlatGenerator") ++ClassRedirects=(OldName="VoxelEmptyWorldGenerator", NewName="VoxelEmptyGenerator") + ++ClassRedirects=(OldName="VoxelWorldGeneratorTools", NewName="VoxelGeneratorTools") + ++EnumRedirects=(OldName="EVoxelWorldGeneratorPickerType", NewName="EVoxelGeneratorPickerType") + ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateFloatTextureFromWorldGenerator", NewName="CreateFloatTextureFromGenerator") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateFloatTextureFromWorldGeneratorAsync", NewName="CreateFloatTextureFromGeneratorAsync") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateColorTextureFromWorldGenerator", NewName="CreateColorTextureFromGenerator") ++FunctionRedirects=(OldName="VoxelGeneratorTools.CreateColorTextureFromWorldGeneratorAsync", NewName="CreateColorTextureFromGeneratorAsync") + ++PropertyRedirects=(OldName="VoxelAssetActor.WorldGenerator",NewName="Generator") + ++PropertyRedirects=(OldName="VoxelSurfaceToolMask.WorldGenerator",NewName="Generator") ++EnumRedirects=(OldName="EVoxelSurfaceToolMaskType", ValueChanges=(("WorldGenerator", "Generator"))) + ++PropertyRedirects=(OldName="VoxelSpawnerConfig.WorldGeneratorOutputs",NewName="GeneratorOutputs") + ++PropertyRedirects=(OldName="VoxelGraphAssetNode.DefaultWorldGenerator",NewName="DefaultGenerator") ++ClassRedirects=(OldName="VoxelNode_WorldGeneratorMerge", NewName="VoxelNode_GeneratorMerge") ++ClassRedirects=(OldName="VoxelNode_WorldGeneratorSamplerBase", NewName="VoxelNode_GeneratorSamplerBase") ++ClassRedirects=(OldName="VoxelNode_SingleWorldGeneratorSamplerBase", NewName="VoxelNode_SingleGeneratorSamplerBase") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorValue", NewName="VoxelNode_GetGeneratorValue") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorMaterial", NewName="VoxelNode_GetGeneratorMaterial") ++ClassRedirects=(OldName="VoxelNode_GetWorldGeneratorCustomOutput", NewName="VoxelNode_GetGeneratorCustomOutput") ++PropertyRedirects=(OldName="VoxelNode_GeneratorMerge.WorldGenerators",NewName="Generators") ++PropertyRedirects=(OldName="VoxelNode_SingleGeneratorSamplerBase.WorldGenerator",NewName="Generator") \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Config/FilterPlugin.ini b/voxel_cpp_test/Plugins/Voxel/Config/FilterPlugin.ini new file mode 100644 index 00000000..d5690088 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Config/FilterPlugin.ini @@ -0,0 +1,8 @@ +[FilterPlugin] +/Config/ +/Content/ +/Resources/ +/Shaders/ +/Source/ +/LICENSE.txt +/README.md \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelFunctionLibrary.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelFunctionLibrary.uasset new file mode 100644 index 00000000..5810b432 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelFunctionLibrary.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelMacroLibrary.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelMacroLibrary.uasset new file mode 100644 index 00000000..37558a81 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelMacroLibrary.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelSpline.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelSpline.uasset new file mode 100644 index 00000000..acff302d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Blueprints/VoxelSpline.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_16x.png new file mode 100644 index 00000000..c70773d7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_64x.png new file mode 100644 index 00000000..91eb2735 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/DataAsset_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_16x.png new file mode 100644 index 00000000..9395f6bc Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.pdn b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.pdn new file mode 100644 index 00000000..c3db6e52 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.pdn differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.png new file mode 100644 index 00000000..ef7af9c7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Generator_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_16x.png new file mode 100644 index 00000000..8852d36e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_64x.png new file mode 100644 index 00000000..8f056695 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Import_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_16x.png new file mode 100644 index 00000000..052c1261 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_64x.png new file mode 100644 index 00000000..92d40699 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Landscape_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_16x.png new file mode 100644 index 00000000..a41027f5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_64x.png new file mode 100644 index 00000000..32382613 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/MaterialCollection_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_16x.png new file mode 100644 index 00000000..b81ff8f1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_64x.png new file mode 100644 index 00000000..2dd7a4b0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerConfig_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_16x.png new file mode 100644 index 00000000..19535abb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_64x.png new file mode 100644 index 00000000..d28435d7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/SpawnerGroup_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_16x.png new file mode 100644 index 00000000..f48b1d1d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_64x.png new file mode 100644 index 00000000..f592aa1a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/Spawner_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_16x.png new file mode 100644 index 00000000..cb1b60e1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.pdn b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.pdn new file mode 100644 index 00000000..8b222936 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.pdn differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.png new file mode 100644 index 00000000..a4ecab18 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelAsset_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_16x.png new file mode 100644 index 00000000..bc4709cb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_64x.png new file mode 100644 index 00000000..34efe9ea Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelGraph_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png new file mode 100644 index 00000000..046d06a5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png new file mode 100644 index 00000000..2fdc36f6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_16x.png new file mode 100644 index 00000000..90563bc4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_64x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_64x.png new file mode 100644 index 00000000..310483e2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/AssetIcons/World_64x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_16x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_16x.png new file mode 100644 index 00000000..43d91070 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_16x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_40x.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_40x.png new file mode 100644 index 00000000..357595b6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/InvertDataAsset_40x.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/FlattenTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/FlattenTool_40.png new file mode 100644 index 00000000..2178eed1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/FlattenTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/LevelTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/LevelTool_40.png new file mode 100644 index 00000000..2178eed1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/LevelTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/MeshTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/MeshTool_40.png new file mode 100644 index 00000000..38e77364 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/MeshTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/RevertTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/RevertTool_40.png new file mode 100644 index 00000000..36e693e0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/RevertTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SmoothTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SmoothTool_40.png new file mode 100644 index 00000000..c0b6cb59 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SmoothTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SphereTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SphereTool_40.png new file mode 100644 index 00000000..3c59742c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SphereTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SurfaceTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SurfaceTool_40.png new file mode 100644 index 00000000..aa986695 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/SurfaceTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/TrimTool_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/TrimTool_40.png new file mode 100644 index 00000000..d1e2eb92 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/Tools/TrimTool_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/mode_40.png b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/mode_40.png new file mode 100644 index 00000000..71297bba Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Editor/UIIcons/mode_40.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController.uasset new file mode 100644 index 00000000..6a8a3cea Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset new file mode 100644 index 00000000..103c1d31 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset new file mode 100644 index 00000000..8e4ca61e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset new file mode 100644 index 00000000..15786556 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI.uasset new file mode 100644 index 00000000..e513e49e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset new file mode 100644 index 00000000..f9547f8a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexGameMode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexGameMode.uasset new file mode 100644 index 00000000..043e109f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ComplexController/VoxelComplexGameMode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset new file mode 100644 index 00000000..500fe7d4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset new file mode 100644 index 00000000..5f893712 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset new file mode 100644 index 00000000..40231637 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset new file mode 100644 index 00000000..3b5ef5f4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset new file mode 100644 index 00000000..ca63bfcb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset new file mode 100644 index 00000000..63d0b0df Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset new file mode 100644 index 00000000..89efa351 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset new file mode 100644 index 00000000..f94d2350 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset new file mode 100644 index 00000000..cf78814c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset new file mode 100644 index 00000000..82a10b52 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset new file mode 100644 index 00000000..2fdadb25 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset new file mode 100644 index 00000000..b0619fb1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset new file mode 100644 index 00000000..3ae74b80 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset new file mode 100644 index 00000000..5d035e36 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset new file mode 100644 index 00000000..c55ffad7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset new file mode 100644 index 00000000..ab29130c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset new file mode 100644 index 00000000..cd278eff Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap new file mode 100644 index 00000000..38c082bb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap new file mode 100644 index 00000000..1cd14cb5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap new file mode 100644 index 00000000..93574666 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap new file mode 100644 index 00000000..95521543 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap new file mode 100644 index 00000000..6490bc4c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap new file mode 100644 index 00000000..83fbed68 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap new file mode 100644 index 00000000..90bca159 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap new file mode 100644 index 00000000..63b0b47f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap new file mode 100644 index 00000000..f37af984 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap new file mode 100644 index 00000000..b0d4cc81 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap new file mode 100644 index 00000000..97cda924 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap new file mode 100644 index 00000000..caeb650c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap new file mode 100644 index 00000000..fb2d581e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap new file mode 100644 index 00000000..168da6ae Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap new file mode 100644 index 00000000..fc42676a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap new file mode 100644 index 00000000..c9a3f792 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap new file mode 100644 index 00000000..04177399 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap new file mode 100644 index 00000000..e737cfac Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap new file mode 100644 index 00000000..0532b240 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap new file mode 100644 index 00000000..757806bf Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap new file mode 100644 index 00000000..90054cd1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap new file mode 100644 index 00000000..45babd51 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Tools/HighResolutionDigging.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Tools/HighResolutionDigging.umap new file mode 100644 index 00000000..184d0356 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Maps/Tools/HighResolutionDigging.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset new file mode 100644 index 00000000..5c8ffb8f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset new file mode 100644 index 00000000..edfc7fc6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel.uasset new file mode 100644 index 00000000..05a1c600 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset new file mode 100644 index 00000000..8db66153 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel.uasset new file mode 100644 index 00000000..faffe6a1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset new file mode 100644 index 00000000..7db5fa02 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset new file mode 100644 index 00000000..3b488522 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset new file mode 100644 index 00000000..250a525e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset new file mode 100644 index 00000000..5f7583c7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset new file mode 100644 index 00000000..a1fc0b8a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset new file mode 100644 index 00000000..9dd8c025 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset new file mode 100644 index 00000000..b5c942d2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset new file mode 100644 index 00000000..8a4b0c05 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset new file mode 100644 index 00000000..1306992b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset new file mode 100644 index 00000000..8f84b64e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset new file mode 100644 index 00000000..adaf40ad Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset new file mode 100644 index 00000000..9acd37de Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset new file mode 100644 index 00000000..e4b132e4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset new file mode 100644 index 00000000..7f75a3ed Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset new file mode 100644 index 00000000..b2c3cd5c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset new file mode 100644 index 00000000..e45e6b57 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset new file mode 100644 index 00000000..d7137441 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset new file mode 100644 index 00000000..36500d58 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset new file mode 100644 index 00000000..28a1c983 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/T_Palette.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/T_Palette.uasset new file mode 100644 index 00000000..488e3e36 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Materials/T_Palette.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset new file mode 100644 index 00000000..a6a636fd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset new file mode 100644 index 00000000..7927caba Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset new file mode 100644 index 00000000..10913f24 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset new file mode 100644 index 00000000..61954dac Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset new file mode 100644 index 00000000..6daadc2e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset new file mode 100644 index 00000000..9c786ec6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset new file mode 100644 index 00000000..fbe56ac1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset new file mode 100644 index 00000000..7b4b6033 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset new file mode 100644 index 00000000..e426b69b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset new file mode 100644 index 00000000..ab58f2f3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap new file mode 100644 index 00000000..1cc48875 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset new file mode 100644 index 00000000..ae154ead Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset new file mode 100644 index 00000000..5bb94278 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap new file mode 100644 index 00000000..43dafce4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset new file mode 100644 index 00000000..35fa42b1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset new file mode 100644 index 00000000..d2b5e9fd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeCopyPaste.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeCopyPaste.uasset new file mode 100644 index 00000000..d235acf0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeCopyPaste.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeMeshImportExample.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeMeshImportExample.uasset new file mode 100644 index 00000000..e2c282b5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/RuntimeMeshImportExample.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset new file mode 100644 index 00000000..1ccba87b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset new file mode 100644 index 00000000..a7831ee5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset new file mode 100644 index 00000000..334fb504 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset new file mode 100644 index 00000000..7b342a9f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset new file mode 100644 index 00000000..ffc34ae5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset new file mode 100644 index 00000000..6fbf25fc Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset new file mode 100644 index 00000000..535f5670 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset new file mode 100644 index 00000000..8251ab1b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset new file mode 100644 index 00000000..24f2af0a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset new file mode 100644 index 00000000..9b27e325 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset new file mode 100644 index 00000000..e72926f4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 00000000..e90177cf Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 00000000..9e1a5586 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset new file mode 100644 index 00000000..5417fb87 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset new file mode 100644 index 00000000..2f3076d3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset new file mode 100644 index 00000000..ad48d07d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 00000000..30c509db Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 00000000..9923d789 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset new file mode 100644 index 00000000..af130e42 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset new file mode 100644 index 00000000..f6e36d07 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset new file mode 100644 index 00000000..da423ed0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset new file mode 100644 index 00000000..19ae3c9a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset new file mode 100644 index 00000000..6a7a448f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset new file mode 100644 index 00000000..e6435790 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset new file mode 100644 index 00000000..cde0b1ca Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset new file mode 100644 index 00000000..fb68fe98 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 00000000..67cf1901 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 00000000..f7e9577a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset new file mode 100644 index 00000000..0ea4af68 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset new file mode 100644 index 00000000..c6ba6319 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset new file mode 100644 index 00000000..5bafc8e0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset new file mode 100644 index 00000000..661cd5b0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset new file mode 100644 index 00000000..737d18bb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 00000000..1733c68e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 00000000..ead5a034 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset new file mode 100644 index 00000000..c4f452d9 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset new file mode 100644 index 00000000..637bb9e3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset new file mode 100644 index 00000000..2cecc986 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset new file mode 100644 index 00000000..ec2c5271 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset new file mode 100644 index 00000000..de4e2ff1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset new file mode 100644 index 00000000..14210195 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset new file mode 100644 index 00000000..6f80e827 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/M_Shovel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/M_Shovel.uasset new file mode 100644 index 00000000..b852bf06 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/M_Shovel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Shovel.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Shovel.uasset new file mode 100644 index 00000000..f4cd54c4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Shovel.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset new file mode 100644 index 00000000..54bf0aa6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset new file mode 100644 index 00000000..1db6345d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset new file mode 100644 index 00000000..c84a2a21 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset new file mode 100644 index 00000000..7dbf0748 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset new file mode 100644 index 00000000..a7c51d12 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset new file mode 100644 index 00000000..f1993c70 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset new file mode 100644 index 00000000..1b398e57 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset new file mode 100644 index 00000000..434ca03c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset new file mode 100644 index 00000000..f181fe24 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset new file mode 100644 index 00000000..1f09e604 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset new file mode 100644 index 00000000..cf2bde29 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset new file mode 100644 index 00000000..1c1ed5c7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset new file mode 100644 index 00000000..dcecba8a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset new file mode 100644 index 00000000..b7c87994 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset new file mode 100644 index 00000000..bf626751 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset new file mode 100644 index 00000000..11e52346 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset new file mode 100644 index 00000000..88a56f3e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset new file mode 100644 index 00000000..c411c73c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset new file mode 100644 index 00000000..99938533 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset new file mode 100644 index 00000000..90466cf2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset new file mode 100644 index 00000000..344637b2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset new file mode 100644 index 00000000..2ee151ef Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset new file mode 100644 index 00000000..722586e8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset new file mode 100644 index 00000000..1041eba6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset new file mode 100644 index 00000000..651cfeb6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset new file mode 100644 index 00000000..5b665629 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset new file mode 100644 index 00000000..90974297 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset new file mode 100644 index 00000000..54b541e7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset new file mode 100644 index 00000000..6cc86629 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset new file mode 100644 index 00000000..71242132 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset new file mode 100644 index 00000000..481acadf Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset new file mode 100644 index 00000000..94c27601 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset new file mode 100644 index 00000000..a4d19723 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset new file mode 100644 index 00000000..93a96ce5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset new file mode 100644 index 00000000..c7c7ac8c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset new file mode 100644 index 00000000..5ac98669 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset new file mode 100644 index 00000000..e96b946d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset new file mode 100644 index 00000000..c58e91e1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset new file mode 100644 index 00000000..bc45695f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset new file mode 100644 index 00000000..adf12a81 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset new file mode 100644 index 00000000..1b7ae05c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset new file mode 100644 index 00000000..ac284e70 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset new file mode 100644 index 00000000..133ba751 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset new file mode 100644 index 00000000..f203c1c7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset new file mode 100644 index 00000000..e807660a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset new file mode 100644 index 00000000..a1811a8b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset new file mode 100644 index 00000000..2b421753 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset new file mode 100644 index 00000000..9e940cea Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset new file mode 100644 index 00000000..5a15c84a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset new file mode 100644 index 00000000..10aa2c32 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset new file mode 100644 index 00000000..a72891ea Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset new file mode 100644 index 00000000..9fe7378b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset new file mode 100644 index 00000000..3d47d2c8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset new file mode 100644 index 00000000..d565e1c3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset new file mode 100644 index 00000000..b8ce2d6b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset new file mode 100644 index 00000000..c5f3eaf1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset new file mode 100644 index 00000000..9756b05b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset new file mode 100644 index 00000000..0c2bc6da Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/fork.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/fork.uasset new file mode 100644 index 00000000..9aa3147e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Shared/fork.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleController.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleController.uasset new file mode 100644 index 00000000..cf8813ea Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleController.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset new file mode 100644 index 00000000..543fb540 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset new file mode 100644 index 00000000..d1d72f96 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset new file mode 100644 index 00000000..169f5c6c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset new file mode 100644 index 00000000..5ba460b7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset new file mode 100644 index 00000000..0ebcccec Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset new file mode 100644 index 00000000..8815b00c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset new file mode 100644 index 00000000..481bfbf3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset new file mode 100644 index 00000000..788015e4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset new file mode 100644 index 00000000..5d3773fc Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset new file mode 100644 index 00000000..1fa253b9 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset new file mode 100644 index 00000000..d24f2c9e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset new file mode 100644 index 00000000..28f6d879 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset new file mode 100644 index 00000000..faaf05e9 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools.umap b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools.umap new file mode 100644 index 00000000..f72ce4a3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools.umap differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset new file mode 100644 index 00000000..35e8074b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset new file mode 100644 index 00000000..0cb3c253 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/BoxTool.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/BoxTool.uasset new file mode 100644 index 00000000..1636306d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/BoxTool.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/GraphTool.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/GraphTool.uasset new file mode 100644 index 00000000..4e876e7f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/GraphTool.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/MySphereTool.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/MySphereTool.uasset new file mode 100644 index 00000000..0419169c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/CustomTools/MySphereTool.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset new file mode 100644 index 00000000..022508ce Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelDefaultBrushMask.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelDefaultBrushMask.uasset new file mode 100644 index 00000000..6ad50c85 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelDefaultBrushMask.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosionBP.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosionBP.uasset new file mode 100644 index 00000000..e56672d5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosionBP.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset new file mode 100644 index 00000000..3b5580d1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset new file mode 100644 index 00000000..add82b40 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset new file mode 100644 index 00000000..875622fd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset new file mode 100644 index 00000000..56d2e3dd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset new file mode 100644 index 00000000..0ca7e9fc Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset new file mode 100644 index 00000000..8417582f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset new file mode 100644 index 00000000..393e5004 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset new file mode 100644 index 00000000..82e97764 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset new file mode 100644 index 00000000..fd9bfccf Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset new file mode 100644 index 00000000..7015a4ef Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VoxelExample_Craters_ItemManager.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VoxelExample_Craters_ItemManager.uasset new file mode 100644 index 00000000..1048d2d1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Craters/VoxelExample_Craters_ItemManager.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset new file mode 100644 index 00000000..946b3933 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset new file mode 100644 index 00000000..5d96518e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset new file mode 100644 index 00000000..63499691 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset new file mode 100644 index 00000000..e5f0f30e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset new file mode 100644 index 00000000..d68b257a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset new file mode 100644 index 00000000..fca58251 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset new file mode 100644 index 00000000..1dc40592 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset new file mode 100644 index 00000000..c87c5314 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset new file mode 100644 index 00000000..d444c8ee Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset new file mode 100644 index 00000000..a1f14428 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset new file mode 100644 index 00000000..f6739d9d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset new file mode 100644 index 00000000..2538e360 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset new file mode 100644 index 00000000..ae484c57 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet_ItemManager.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet_ItemManager.uasset new file mode 100644 index 00000000..02848009 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet_ItemManager.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset new file mode 100644 index 00000000..cd2561bd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset new file mode 100644 index 00000000..3a5cb5b4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_ItemManager.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_ItemManager.uasset new file mode 100644 index 00000000..3a2a51ad Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_ItemManager.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset new file mode 100644 index 00000000..d7d259a5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset new file mode 100644 index 00000000..6e62f7f5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset new file mode 100644 index 00000000..9b205499 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset new file mode 100644 index 00000000..fe6ab483 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset new file mode 100644 index 00000000..ce3c607b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset new file mode 100644 index 00000000..9753e8f3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset new file mode 100644 index 00000000..d862f0b2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset new file mode 100644 index 00000000..dba4a0db Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset new file mode 100644 index 00000000..967aa081 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset new file mode 100644 index 00000000..8e21bfca Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset new file mode 100644 index 00000000..83ff77ed Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Examples/alpha_brush.png b/voxel_cpp_test/Plugins/Voxel/Content/Examples/alpha_brush.png new file mode 100644 index 00000000..7aa9712d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Examples/alpha_brush.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Difference.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Difference.uasset new file mode 100644 index 00000000..bffa3f2d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Difference.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/FastLerp.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/FastLerp.uasset new file mode 100644 index 00000000..45e42e34 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/FastLerp.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/FlowControl/FourWayFlowMerge.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/FlowControl/FourWayFlowMerge.uasset new file mode 100644 index 00000000..dd1e460f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/FlowControl/FourWayFlowMerge.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetSlopeFromDerivatives.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetSlopeFromDerivatives.uasset new file mode 100644 index 00000000..23ccdf00 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetSlopeFromDerivatives.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphas.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphas.uasset new file mode 100644 index 00000000..8cfcb524 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphas.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphasUsingDistances.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphasUsingDistances.uasset new file mode 100644 index 00000000..0875ce12 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GetVoronoiAlphasUsingDistances.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMax.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMax.uasset new file mode 100644 index 00000000..2135a976 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMax.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMin.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMin.uasset new file mode 100644 index 00000000..49d0d73a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/GraphAssetMin.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsPreview.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsPreview.uasset new file mode 100644 index 00000000..71fa4f56 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsPreview.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsRangeAnalysis.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsRangeAnalysis.uasset new file mode 100644 index 00000000..2035f34c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/IsRangeAnalysis.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/LerpColors.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/LerpColors.uasset new file mode 100644 index 00000000..ac0a8c7a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/LerpColors.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Map.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Map.uasset new file mode 100644 index 00000000..d12cd914 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Map.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/DegreesToRadians.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/DegreesToRadians.uasset new file mode 100644 index 00000000..bed36f00 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/DegreesToRadians.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/RadiansToDegrees.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/RadiansToDegrees.uasset new file mode 100644 index 00000000..a9ca2349 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Math/RadiansToDegrees.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Misc/CaveLayer.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Misc/CaveLayer.uasset new file mode 100644 index 00000000..7544dec8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Misc/CaveLayer.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Modulate.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Modulate.uasset new file mode 100644 index 00000000..0bceb858 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Modulate.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset new file mode 100644 index 00000000..adcd23a6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlotToPreview.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlotToPreview.uasset new file mode 100644 index 00000000..de4c266c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PlotToPreview.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviewSize.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviewSize.uasset new file mode 100644 index 00000000..fc23e596 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviewSize.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviousGeneratorValueMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviousGeneratorValueMaterial.uasset new file mode 100644 index 00000000..cf527828 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/PreviousGeneratorValueMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Ramp.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Ramp.uasset new file mode 100644 index 00000000..4ce2fe52 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Ramp.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/RandomColor.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/RandomColor.uasset new file mode 100644 index 00000000..5033405e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/RandomColor.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SCurve3.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SCurve3.uasset new file mode 100644 index 00000000..f1477e75 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SCurve3.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/2DNoiseSDF.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/2DNoiseSDF.uasset new file mode 100644 index 00000000..3b23d90d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/2DNoiseSDF.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Elongate.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Elongate.uasset new file mode 100644 index 00000000..72e1b4e8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Elongate.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/MirrorAxis.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/MirrorAxis.uasset new file mode 100644 index 00000000..2103d67d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/MirrorAxis.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/RoundSDF.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/RoundSDF.uasset new file mode 100644 index 00000000..1b7f5e9b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/RoundSDF.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Shell.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Shell.uasset new file mode 100644 index 00000000..ff723e79 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SDF/Shell.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SafeLerp.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SafeLerp.uasset new file mode 100644 index 00000000..3a5e17a0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SafeLerp.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Select.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Select.uasset new file mode 100644 index 00000000..0688331a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Select.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetHighQualityValue.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetHighQualityValue.uasset new file mode 100644 index 00000000..60a43c49 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetHighQualityValue.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetSphereUpVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetSphereUpVector.uasset new file mode 100644 index 00000000..8c4b06d1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SetSphereUpVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothIntersection.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothIntersection.uasset new file mode 100644 index 00000000..904a71b3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothIntersection.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothSubtract.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothSubtract.uasset new file mode 100644 index 00000000..8b8cfc25 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothSubtract.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothUnion.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothUnion.uasset new file mode 100644 index 00000000..6f2ef0f3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SmoothOps/SmoothUnion.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SphereNormalizeWithPreview.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SphereNormalizeWithPreview.uasset new file mode 100644 index 00000000..14688383 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SphereNormalizeWithPreview.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Step.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Step.uasset new file mode 100644 index 00000000..cfca2bd0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Step.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/SwitchPreview.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SwitchPreview.uasset new file mode 100644 index 00000000..85171a0b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/SwitchPreview.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Terrace.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Terrace.uasset new file mode 100644 index 00000000..2509e8bb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Terrace.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AbsVector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AbsVector2.uasset new file mode 100644 index 00000000..d9c92069 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AbsVector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Float.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Float.uasset new file mode 100644 index 00000000..b17d8325 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Float.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Vector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Vector2.uasset new file mode 100644 index 00000000..52c616de Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/AddVector2Vector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Float.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Float.uasset new file mode 100644 index 00000000..8f817044 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Float.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset new file mode 100644 index 00000000..dac8f604 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DotProductVector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DotProductVector2.uasset new file mode 100644 index 00000000..3d89efce Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/DotProductVector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/LerpVector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/LerpVector2.uasset new file mode 100644 index 00000000..b9496436 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/LerpVector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset new file mode 100644 index 00000000..d9121843 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset new file mode 100644 index 00000000..9a038f97 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Normalize.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Normalize.uasset new file mode 100644 index 00000000..40b175ad Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Normalize.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Float.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Float.uasset new file mode 100644 index 00000000..7ccf222c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Float.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset new file mode 100644 index 00000000..a4df0d88 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SwitchVector2.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SwitchVector2.uasset new file mode 100644 index 00000000..4e6be917 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/SwitchVector2.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Distance.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Distance.uasset new file mode 100644 index 00000000..a2f37f4b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Distance.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Length.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Length.uasset new file mode 100644 index 00000000..2634e17d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/Vector2Ops/Vector2Length.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AbsVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AbsVector.uasset new file mode 100644 index 00000000..ba88a05a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AbsVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorFloat.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorFloat.uasset new file mode 100644 index 00000000..c22a96ec Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorFloat.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorVector.uasset new file mode 100644 index 00000000..39c38caa Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/AddVectorVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/BreakVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/BreakVector.uasset new file mode 100644 index 00000000..9e5b27db Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/BreakVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/ClampVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/ClampVector.uasset new file mode 100644 index 00000000..00ed0d4b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/ClampVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorFloat.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorFloat.uasset new file mode 100644 index 00000000..db380665 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorFloat.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorVector.uasset new file mode 100644 index 00000000..2d5d5b07 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DivideVectorVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DotProductVector3.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DotProductVector3.uasset new file mode 100644 index 00000000..70366b81 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/DotProductVector3.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/LerpVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/LerpVector.uasset new file mode 100644 index 00000000..ebb11c51 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/LerpVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MakeVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MakeVector.uasset new file mode 100644 index 00000000..993e78d2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MakeVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MaxVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MaxVector.uasset new file mode 100644 index 00000000..d1c35707 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MaxVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MinVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MinVector.uasset new file mode 100644 index 00000000..f01d77db Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MinVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorFloat.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorFloat.uasset new file mode 100644 index 00000000..4db88aa4 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorFloat.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorVector.uasset new file mode 100644 index 00000000..953f7aeb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/MultiplyVectorVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/Normalize.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/Normalize.uasset new file mode 100644 index 00000000..9ecc84f8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/Normalize.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorFloat.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorFloat.uasset new file mode 100644 index 00000000..4000694e Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorFloat.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorVector.uasset new file mode 100644 index 00000000..0a631b07 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SubtractVectorVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SwitchVector.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SwitchVector.uasset new file mode 100644 index 00000000..603b4e14 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/SwitchVector.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/XYZ.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/XYZ.uasset new file mode 100644 index 00000000..219bfc4c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VectorOps/XYZ.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp.uasset new file mode 100644 index 00000000..2ed708c9 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp_2Data.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp_2Data.uasset new file mode 100644 index 00000000..dc3aafc0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Macros/VoronoiLerp_2Data.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset new file mode 100644 index 00000000..37169cd3 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset new file mode 100644 index 00000000..b9d6a590 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset new file mode 100644 index 00000000..fb5a059c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset new file mode 100644 index 00000000..26f48c17 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset new file mode 100644 index 00000000..16e7055c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/MeshImporterMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/MeshImporterMaterial.uasset new file mode 100644 index 00000000..b03ad9d0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/MeshImporterMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/VoxelOpacity.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/VoxelOpacity.uasset new file mode 100644 index 00000000..15e2a2c6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/VoxelOpacity.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset new file mode 100644 index 00000000..0290c5b1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset new file mode 100644 index 00000000..c73fd1aa Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset new file mode 100644 index 00000000..9a74d9fe Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_HeightBlend.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_HeightBlend.uasset new file mode 100644 index 00000000..a3089cc8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_HeightBlend.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalNormal.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalNormal.uasset new file mode 100644 index 00000000..801c1261 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalNormal.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalPosition.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalPosition.uasset new file mode 100644 index 00000000..ca52165f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_LocalPosition.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_PlanetBlend.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_PlanetBlend.uasset new file mode 100644 index 00000000..17f8f001 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_PlanetBlend.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset new file mode 100644 index 00000000..79c48e87 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset new file mode 100644 index 00000000..ea301536 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset new file mode 100644 index 00000000..60860c99 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset new file mode 100644 index 00000000..7eefc227 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset new file mode 100644 index 00000000..3527d02f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/CA_Gradient.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/CA_Gradient.uasset new file mode 100644 index 00000000..3293d110 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/CA_Gradient.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientNoWater.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientNoWater.uasset new file mode 100644 index 00000000..c142f9b6 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientNoWater.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientWater.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientWater.uasset new file mode 100644 index 00000000..fe16ae14 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/C_GradientWater.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/DefaultVoxelPreview.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/DefaultVoxelPreview.uasset new file mode 100644 index 00000000..f9c812fd Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/DefaultVoxelPreview.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_2DPreviewMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_2DPreviewMaterial.uasset new file mode 100644 index 00000000..bd242e0c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_2DPreviewMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_PreviewMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_PreviewMaterial.uasset new file mode 100644 index 00000000..838ccf8a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/M_PreviewMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/Plane.fbx b/voxel_cpp_test/Plugins/Voxel/Content/Preview/Plane.fbx new file mode 100644 index 00000000..4321d5fb Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/Plane.fbx differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/SM_Plane.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/SM_Plane.uasset new file mode 100644 index 00000000..0ac6e304 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/SM_Plane.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/Preview/VoxelRayMarchHeightMap.uasset b/voxel_cpp_test/Plugins/Voxel/Content/Preview/VoxelRayMarchHeightMap.uasset new file mode 100644 index 00000000..77b1525d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/Preview/VoxelRayMarchHeightMap.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial.uasset new file mode 100644 index 00000000..6cf90845 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Level.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Level.uasset new file mode 100644 index 00000000..f4b0daee Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Level.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset new file mode 100644 index 00000000..660a3c20 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset new file mode 100644 index 00000000..908087b7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial.uasset new file mode 100644 index 00000000..ed1079e0 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset new file mode 100644 index 00000000..23045d94 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset new file mode 100644 index 00000000..dcbe3681 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset new file mode 100644 index 00000000..1f92e98a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset new file mode 100644 index 00000000..d2be231f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset new file mode 100644 index 00000000..fda0f03c Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ViewportPlaneMaterial.uasset b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ViewportPlaneMaterial.uasset new file mode 100644 index 00000000..8432ab22 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/ToolMaterials/ViewportPlaneMaterial.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule.uasset new file mode 100644 index 00000000..2fcc4cc8 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule_Graph.uasset new file mode 100644 index 00000000..22e548df Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Capsule_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Ravine_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Ravine_Graph.uasset new file mode 100644 index 00000000..e16653c7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Ravine_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere.uasset new file mode 100644 index 00000000..f4aad72b Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere_Graph.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere_Graph.uasset new file mode 100644 index 00000000..9cfc5634 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VDI_Sphere_Graph.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset new file mode 100644 index 00000000..5e831495 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset differ diff --git a/voxel_cpp_test/Plugins/Voxel/Content/palette.tga b/voxel_cpp_test/Plugins/Voxel/Content/palette.tga new file mode 100644 index 00000000..dda09ad9 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Content/palette.tga differ diff --git a/voxel_cpp_test/Plugins/Voxel/Resources/Icon128.png b/voxel_cpp_test/Plugins/Voxel/Resources/Icon128.png new file mode 100644 index 00000000..20a4d525 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Resources/Icon128.png differ diff --git a/voxel_cpp_test/Plugins/Voxel/Shaders/Private/DistanceField.usf b/voxel_cpp_test/Plugins/Voxel/Shaders/Private/DistanceField.usf new file mode 100644 index 00000000..8b362cfe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Shaders/Private/DistanceField.usf @@ -0,0 +1,93 @@ +// Copyright 2020 Phyronnaz + +#include "/Engine/Public/Platform.ush" +#include "/Engine/Generated/GeneratedUniformBuffers.ush" + +#if __RSCPP_VERSION +struct FConstants +{ + uint SizeX; + uint SizeY; + uint SizeZ; + uint Step; +}; +FConstants VoxelDistanceFieldParameters; +#endif + +RWBuffer RWSrc; +RWBuffer RWDst; + +float3 GetCoord(uint3 Position) +{ + const int Index = Position.x + VoxelDistanceFieldParameters.SizeX * Position.y + VoxelDistanceFieldParameters.SizeX * VoxelDistanceFieldParameters.SizeY * Position.z; + return float3( + RWSrc[3 * Index + 0], + RWSrc[3 * Index + 1], + RWSrc[3 * Index + 2]); +} +void SetCoord(uint3 Position, float3 Coord) +{ + const int Index = Position.x + VoxelDistanceFieldParameters.SizeX * Position.y + VoxelDistanceFieldParameters.SizeX * VoxelDistanceFieldParameters.SizeY * Position.z; + RWDst[3 * Index + 0] = Coord.x; + RWDst[3 * Index + 1] = Coord.y; + RWDst[3 * Index + 2] = Coord.z; +} + +bool IsCoordValid(float3 Coord) +{ + return Coord.x <= 1e9; +} + +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, NUM_THREADS_CS)] +void ExpandDistanceField(uint3 ThreadId : SV_DispatchThreadID) +{ + const uint3 Position = ThreadId.xyz; + + if (Position.x >= VoxelDistanceFieldParameters.SizeX || + Position.y >= VoxelDistanceFieldParameters.SizeY || + Position.z >= VoxelDistanceFieldParameters.SizeZ) + { + return; + } + + float BestDistance = 1e9; + float3 BestCoord = float3(1e9, 1e9, 1e9); + + for (int x = -1; x <= 1; ++x) + { + for (int y = -1; y <= 1; ++y) + { + for (int z = -1; z <= 1; ++z) + { + const int3 NeighborPosition = Position + int3(x, y, z) * VoxelDistanceFieldParameters.Step; + + if (NeighborPosition.x < 0 || + NeighborPosition.y < 0 || + NeighborPosition.z < 0 || + NeighborPosition.x >= int(VoxelDistanceFieldParameters.SizeX) || + NeighborPosition.y >= int(VoxelDistanceFieldParameters.SizeY) || + NeighborPosition.z >= int(VoxelDistanceFieldParameters.SizeZ)) + { + // We could clamp the index below instead of jumping, but it probably has the same cost anyways + // and produce less nice patterns in the first passes + continue; + } + + const float3 NeighborCoord = GetCoord(NeighborPosition); + + if (IsCoordValid(NeighborCoord)) + { + const float Dist = length(NeighborCoord - float3(Position)); + + if (Dist < BestDistance) + { + BestDistance = Dist; + BestCoord = NeighborCoord; + } + } + } + } + } + + SetCoord(Position, BestCoord); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Shaders/Private/Erosion.usf b/voxel_cpp_test/Plugins/Voxel/Shaders/Private/Erosion.usf new file mode 100644 index 00000000..4b9e1b1b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Shaders/Private/Erosion.usf @@ -0,0 +1,288 @@ +// Copyright 2020 Phyronnaz + +// From https://hal.inria.fr/file/index/docid/402079/filename/FastErosion_PG07.pdf + +#include "/Engine/Public/Platform.ush" +#include "/Engine/Generated/GeneratedUniformBuffers.ush" + +RWTexture2D RainMap; +RWTexture2D TerrainHeight; +RWTexture2D WaterHeight; +RWTexture2D Sediment; +RWTexture2D Outflow; +RWTexture2D Velocity; + +RWTexture2D WaterHeight1; +RWTexture2D WaterHeight2; + +RWTexture2D Sediment1; + +// Required for tilt angle +RWTexture2D TerrainHeight1; + +#if __INTELLISENSE__ +struct FConstants +{ + uint size; + float dt; + + // Size of a pipe + float l; + // Gravity + float g; + + // Sediment capacity + float Kc; + // Sediment dissolving + float Ks; + // Sediment deposition + float Kd; + + // Rain strength + float Kr; + // Evaporation + float Ke; +}; +FConstants VoxelErosionParameters; +#endif + +/** + * Getter/Setters helpers as UAV must have a single component + */ +inline bool IsIn(int2 Pos) { return Pos.x >= 0 && Pos.y >= 0 && Pos.x < VoxelErosionParameters.size && Pos.y < VoxelErosionParameters.size; } +inline float GetLeft (int2 Pos) { return IsIn(Pos) ? Outflow[uint2(Pos.x * 4 + 0, Pos.y)] : 0; } +inline float GetRight (int2 Pos) { return IsIn(Pos) ? Outflow[uint2(Pos.x * 4 + 1, Pos.y)] : 0; } +inline float GetBottom(int2 Pos) { return IsIn(Pos) ? Outflow[uint2(Pos.x * 4 + 2, Pos.y)] : 0; } +inline float GetTop (int2 Pos) { return IsIn(Pos) ? Outflow[uint2(Pos.x * 4 + 3, Pos.y)] : 0; } +inline void SetLeft (int2 Pos, float Value) { Outflow[uint2(Pos.x * 4 + 0, Pos.y)] = Value; } +inline void SetRight (int2 Pos, float Value) { Outflow[uint2(Pos.x * 4 + 1, Pos.y)] = Value; } +inline void SetBottom(int2 Pos, float Value) { Outflow[uint2(Pos.x * 4 + 2, Pos.y)] = Value; } +inline void SetTop (int2 Pos, float Value) { Outflow[uint2(Pos.x * 4 + 3, Pos.y)] = Value; } + +inline float2 GetVelocity(uint2 Pos) +{ + return float2( + Velocity[uint2(Pos.x * 2 + 0, Pos.y)], + Velocity[uint2(Pos.x * 2 + 1, Pos.y)]); +} +inline void SetVelocity(uint2 Pos, float2 Value) +{ + Velocity[uint2(Pos.x * 2 + 0, Pos.y)] = Value.x; + Velocity[uint2(Pos.x * 2 + 1, Pos.y)] = Value.y; +} + +/** + * WaterIncrement + */ + +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, 1)] +void WaterIncrement(uint3 ThreadId : SV_DispatchThreadID) +{ + const uint2 Pos = ThreadId.xy; + WaterHeight1[Pos] = WaterHeight[Pos] + VoxelErosionParameters.dt * VoxelErosionParameters.Kr * RainMap[Pos]; +} + +/** + * FlowSimulation + */ + +inline float HeightDifference(uint2 a, uint2 b) +{ + return TerrainHeight[a] + WaterHeight1[a] - TerrainHeight[b] - WaterHeight1[b]; +} +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, 1)] + void FlowSimulation(uint3 ThreadId : SV_DispatchThreadID) +{ + const int2 Pos = ThreadId.xy; + const int2 Left = Pos + int2(-1, 0); + const int2 Right = Pos + int2(+1, 0); + const int2 Bottom = Pos + int2(0, -1); + const int2 Top = Pos + int2(0, +1); + + float4 OutflowValue = float4(GetLeft(Pos), GetRight(Pos), GetBottom(Pos), GetTop(Pos)); + + // Flow update + { + // Compute the flow from height differences + OutflowValue += VoxelErosionParameters.dt * VoxelErosionParameters.g / VoxelErosionParameters.l * + float4( + HeightDifference(Pos, Left ), + HeightDifference(Pos, Right ), + HeightDifference(Pos, Bottom), + HeightDifference(Pos, Top )); + OutflowValue = max(OutflowValue, 0); + + // Scale the flow down if needed + const float OutflowSum = OutflowValue.x + OutflowValue.y + OutflowValue.z + OutflowValue.w; + if (OutflowSum != 0) + { + const float K = WaterHeight1[Pos] * VoxelErosionParameters.l * VoxelErosionParameters.l / (VoxelErosionParameters.dt * OutflowSum); + OutflowValue *= min(K, 1); + } + + SetLeft (Pos, OutflowValue.x); + SetRight (Pos, OutflowValue.y); + SetBottom(Pos, OutflowValue.z); + SetTop (Pos, OutflowValue.w); + } + + // Update water height + { + const float DeltaVolume = VoxelErosionParameters.dt * ( + GetRight(Left) + + GetLeft(Right) + + GetTop(Bottom) + + GetBottom(Top) + + -OutflowValue.x + + -OutflowValue.y + + -OutflowValue.z + + -OutflowValue.w); + WaterHeight2[Pos] = WaterHeight1[Pos] + DeltaVolume / (VoxelErosionParameters.l * VoxelErosionParameters.l); + } + + // Boundaries + if(Pos.x == 0) + { + SetLeft(Pos, 0); + } + else if (Pos.x == VoxelErosionParameters.size - 1) + { + SetRight(Pos, 0); + } + if (Pos.y == 0) + { + SetBottom(Pos, 0); + } + else if (Pos.y == VoxelErosionParameters.size - 1) + { + SetTop(Pos, 0); + } + + // Update velocity + { + float2 DeltaWaterA; + float2 DeltaWaterB; + DeltaWaterA.x = GetRight(Left) - GetLeft(Pos); + DeltaWaterB.x = GetRight(Pos) - GetLeft(Right); + DeltaWaterA.y = GetTop(Bottom) - GetBottom(Pos); + DeltaWaterB.y = GetTop(Pos) - GetBottom(Top); + + float2 DeltaWater; + // Avoid spikes + if (abs(DeltaWaterA.x + DeltaWaterB.x) < 0.001) + { + DeltaWater.x = DeltaWaterA.x; + } + else + { + DeltaWater.x = (DeltaWaterA.x + DeltaWaterB.x) / 2; + } + if (abs(DeltaWaterA.y + DeltaWaterB.y) < 0.001) + { + DeltaWater.y = DeltaWaterA.y; + } + else + { + DeltaWater.y = (DeltaWaterA.y + DeltaWaterB.y) / 2; + } + + const float MeanWater = (WaterHeight1[Pos] + WaterHeight2[Pos]) / 2; + const float Divisor = VoxelErosionParameters.l * MeanWater; + if (Divisor == 0) + { + SetVelocity(Pos, DeltaWater); + } + else + { + SetVelocity(Pos, DeltaWater / Divisor); + } + } +} + +/** + * ErosionDeposition + */ + +// Return sin(tilt angle) +inline float TiltAngle(int2 Pos) +{ + // https://math.stackexchange.com/questions/1044044/local-tilt-angle-based-on-height-field + + const int2 Left = Pos + int2(-1, 0); + const int2 Right = Pos + int2(+1, 0); + const int2 Bottom = Pos + int2(0, -1); + const int2 Top = Pos + int2(0, +1); + + if (!IsIn(Left) || + !IsIn(Right) || + !IsIn(Bottom) || + !IsIn(Top)) + { + // We don't want the sediments to be stuck there + return 0.5; + } + + const float DeltaXL = (TerrainHeight[Left ] - TerrainHeight[Pos]) / 2; + const float DeltaXR = (TerrainHeight[Right ] - TerrainHeight[Pos]) / 2; + const float DeltaYB = (TerrainHeight[Bottom] - TerrainHeight[Pos]) / 2; + const float DeltaYT = (TerrainHeight[Top ] - TerrainHeight[Pos]) / 2; + + const float Sum = max(DeltaXL * DeltaXL, DeltaXR * DeltaXR) + max(DeltaYB * DeltaYB, DeltaYT * DeltaYT); + + return sqrt(Sum / (1 + Sum)); +} + +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, 1)] + void ErosionDeposition(uint3 ThreadId : SV_DispatchThreadID) +{ + const uint2 Pos = ThreadId.xy; + + float C = VoxelErosionParameters.Kc * TiltAngle(Pos) * length(GetVelocity(Pos)); + const float S = Sediment[Pos]; + + const float K = C > S ? VoxelErosionParameters.Ks : VoxelErosionParameters.Kd; + float Diff = K * (C - S); + if (Diff > 0) + { + Diff = min(Diff, TerrainHeight[Pos]); + } + else + { + Diff = -min(-Diff, Sediment[Pos]); + } + + TerrainHeight1[Pos] = TerrainHeight[Pos] - Diff; + Sediment1[Pos] = Sediment[Pos] + Diff; +} + +/** + * SedimentTransportation + */ + +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, 1)] + void SedimentTransportation(uint3 ThreadId : SV_DispatchThreadID) +{ + const uint2 Pos = ThreadId.xy; + const float2 SamplePos = Pos - GetVelocity(Pos) * VoxelErosionParameters.dt; + const uint2 Floor = floor(SamplePos); + const uint2 Ceil = ceil(SamplePos); + const float2 Frac = SamplePos - Floor; + Sediment[Pos] = lerp( + lerp(Sediment1[uint2(Floor.x, Floor.y)], Sediment1[uint2(Ceil.x, Floor.y)], Frac.x), + lerp(Sediment1[uint2(Floor.x, Ceil .y)], Sediment1[uint2(Ceil.x, Ceil .y)], Frac.x), + Frac.y); +} + +/** + * Evaporation + */ + +[numthreads(NUM_THREADS_CS, NUM_THREADS_CS, 1)] + void Evaporation(uint3 ThreadId : SV_DispatchThreadID) +{ + const uint2 Pos = ThreadId.xy; + WaterHeight[Pos] = WaterHeight2[Pos] * (1 - VoxelErosionParameters.Ke * VoxelErosionParameters.dt); + + // Also copy the height data + TerrainHeight[Pos] = TerrainHeight1[Pos]; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/.DS_Store b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/.DS_Store new file mode 100644 index 00000000..8074518a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/.DS_Store differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.Build.cs new file mode 100644 index 00000000..e59c981b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.Build.cs @@ -0,0 +1,89 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class Embree3 : ModuleRules +{ + public Embree3(ReadOnlyTargetRules Target) : base(Target) + { + Type = ModuleType.External; + +#if true + if (Target.Platform == UnrealTargetPlatform.Win64) + { + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Win64", "include")); + + PublicDelayLoadDLLs.Add("embree3.dll"); + PublicDelayLoadDLLs.Add("tbb.dll"); + PublicDelayLoadDLLs.Add("tbbmalloc.dll"); + + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "Win64", "lib", "embree3.lib")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "Win64", "lib", "tbb.lib")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "Win64", "lib", "tbbmalloc.lib")); + + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/Win64/lib/embree3.dll"); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/Win64/lib/tbb.dll"); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.dll"); + + PublicDefinitions.Add("USE_EMBREE_VOXEL=1"); + } + else if (Target.Platform == UnrealTargetPlatform.Mac) + { + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "MacOSX", "include")); + + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "MacOSX", "lib", "libembree3.3.dylib")); + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "MacOSX", "lib", "libtbb.dylib")); + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "MacOSX", "lib", "libtbbmalloc.dylib")); + + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.3.dylib"); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.dylib"); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/Embree3/MacOSX/lib/libtbbmalloc.dylib"); + + PublicDefinitions.Add("USE_EMBREE_VOXEL=1"); + } + else if (Target.Platform == UnrealTargetPlatform.Linux) + { +#if UE_4_24_OR_LATER + // TODO + // throw new System.ArgumentException("Embree Linux Error: Need to fix the code below for 4.24. Some tricky stuff was happening with UE removing the .3 at the end, so can't directly fix it"); + PublicDefinitions.Add("USE_EMBREE_VOXEL=0"); +#else + string SDKDir = Path.Combine(ModuleDirectory, "Linux"); + string IncludeDir = SDKDir + "include/"; + string LibDir = SDKDir + "lib/"; + + PublicIncludePaths.Add(IncludeDir); + + ///////////////////////////////////////// + // The following are needed for linking: + + PublicLibraryPaths.Add(LibDir); + + PublicAdditionalLibraries.Add(":libembree3.so.3"); + PublicAdditionalLibraries.Add(":libtbb.so.2"); + PublicAdditionalLibraries.Add(":libtbbmalloc.so.2"); + + ////////////////////////////////////////////////////// + // The following are needed for runtime dependencies: + + // Adds the library path to LD_LIBRARY_PATH + PublicRuntimeLibraryPaths.Add(LibDir); + + // Tells UBT to copy the .so to the packaged game directory + RuntimeDependencies.Add(LibDir + "libembree3.so.3"); + RuntimeDependencies.Add(LibDir + "libtbb.so.2"); + RuntimeDependencies.Add(LibDir + "libtbbmalloc.so.2"); + + PublicDefinitions.Add("USE_EMBREE_VOXEL=1"); +#endif + } + else + { + PublicDefinitions.Add("USE_EMBREE_VOXEL=0"); + } +#else + PublicDefinitions.Add("USE_EMBREE_VOXEL=0"); +#endif + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.tps b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.tps new file mode 100644 index 00000000..3f5d71fc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Embree3.tps @@ -0,0 +1,15 @@ + + + Embree3 + 2019-05-18T00:00:00+02:00 + High performance ray tracing kernels + Accelerates lightmass allowing lighting builds to take much less time + http://www.apache.org/licenses/LICENSE-2.0 + + Licensees + Git + P4 + + + None + \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.h new file mode 100644 index 00000000..8d74ea77 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.h @@ -0,0 +1,26 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_version.h" +#include "rtcore_common.h" +#include "rtcore_device.h" +#include "rtcore_buffer.h" +#include "rtcore_ray.h" +#include "rtcore_geometry.h" +#include "rtcore_scene.h" +#include "rtcore_builder.h" diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.isph new file mode 100644 index 00000000..ce0043d2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore.isph @@ -0,0 +1,28 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_ISPH__ +#define __RTC_ISPH__ + +#include "rtcore_version.h" +#include "rtcore_common.isph" +#include "rtcore_device.isph" +#include "rtcore_buffer.isph" +#include "rtcore_ray.isph" +#include "rtcore_geometry.isph" +#include "rtcore_scene.isph" + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.h new file mode 100644 index 00000000..bc0824b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.h @@ -0,0 +1,64 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef struct RTCBufferTy* RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, size_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* ptr, size_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.isph new file mode 100644 index 00000000..e4824025 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_buffer.isph @@ -0,0 +1,63 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_BUFFER_ISPH__ +#define __RTC_BUFFER_ISPH__ + +#include "rtcore_device.isph" + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef uniform struct RTCBufferTy* uniform RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, uniform uintptr_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* uniform ptr, uniform uintptr_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* uniform rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer handle (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_builder.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_builder.h new file mode 100644 index 00000000..057ad05c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_builder.h @@ -0,0 +1,133 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_scene.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque BVH type */ +typedef struct RTCBVHTy* RTCBVH; + +/* Input build primitives for the builder */ +struct RTC_ALIGN(32) RTCBuildPrimitive +{ + float lower_x, lower_y, lower_z; + unsigned int geomID; + float upper_x, upper_y, upper_z; + unsigned int primID; +}; + +/* Opaque thread local allocator type */ +typedef struct RTCThreadLocalAllocatorTy* RTCThreadLocalAllocator; + +/* Callback to create a node */ +typedef void* (*RTCCreateNodeFunction) (RTCThreadLocalAllocator allocator, unsigned int childCount, void* userPtr); + +/* Callback to set the pointer to all children */ +typedef void (*RTCSetNodeChildrenFunction) (void* nodePtr, void** children, unsigned int childCount, void* userPtr); + +/* Callback to set the bounds of all children */ +typedef void (*RTCSetNodeBoundsFunction) (void* nodePtr, const struct RTCBounds** bounds, unsigned int childCount, void* userPtr); + +/* Callback to create a leaf node */ +typedef void* (*RTCCreateLeafFunction) (RTCThreadLocalAllocator allocator, const struct RTCBuildPrimitive* primitives, size_t primitiveCount, void* userPtr); + +/* Callback to split a build primitive */ +typedef void (*RTCSplitPrimitiveFunction) (const struct RTCBuildPrimitive* primitive, unsigned int dimension, float position, struct RTCBounds* leftBounds, struct RTCBounds* rightBounds, void* userPtr); + +/* Build flags */ +enum RTCBuildFlags +{ + RTC_BUILD_FLAG_NONE = 0, + RTC_BUILD_FLAG_DYNAMIC = (1 << 0), +}; + +/* Input for builders */ +struct RTCBuildArguments +{ + size_t byteSize; + + enum RTCBuildQuality buildQuality; + enum RTCBuildFlags buildFlags; + unsigned int maxBranchingFactor; + unsigned int maxDepth; + unsigned int sahBlockSize; + unsigned int minLeafSize; + unsigned int maxLeafSize; + float traversalCost; + float intersectionCost; + + RTCBVH bvh; + struct RTCBuildPrimitive* primitives; + size_t primitiveCount; + size_t primitiveArrayCapacity; + + RTCCreateNodeFunction createNode; + RTCSetNodeChildrenFunction setNodeChildren; + RTCSetNodeBoundsFunction setNodeBounds; + RTCCreateLeafFunction createLeaf; + RTCSplitPrimitiveFunction splitPrimitive; + RTCProgressMonitorFunction buildProgress; + void* userPtr; +}; + +/* Returns the default build settings. */ +RTC_FORCEINLINE struct RTCBuildArguments rtcDefaultBuildArguments() +{ + struct RTCBuildArguments args; + args.byteSize = sizeof(args); + args.buildQuality = RTC_BUILD_QUALITY_MEDIUM; + args.buildFlags = RTC_BUILD_FLAG_NONE; + args.maxBranchingFactor = 2; + args.maxDepth = 32; + args.sahBlockSize = 1; + args.minLeafSize = 1; + args.maxLeafSize = 32; + args.traversalCost = 1.0f; + args.intersectionCost = 1.0f; + args.bvh = NULL; + args.primitives = NULL; + args.primitiveCount = 0; + args.primitiveArrayCapacity = 0; + args.createNode = NULL; + args.setNodeChildren = NULL; + args.setNodeBounds = NULL; + args.createLeaf = NULL; + args.splitPrimitive = NULL; + args.buildProgress = NULL; + args.userPtr = NULL; + return args; +} + +/* Creates a new BVH. */ +RTC_API RTCBVH rtcNewBVH(RTCDevice device); + +/* Builds a BVH. */ +RTC_API void* rtcBuildBVH(const struct RTCBuildArguments* args); + +/* Allocates memory using the thread local allocator. */ +RTC_API void* rtcThreadLocalAlloc(RTCThreadLocalAllocator allocator, size_t bytes, size_t align); + +/* Retains the BVH (increments reference count). */ +RTC_API void rtcRetainBVH(RTCBVH bvh); + +/* Releases the BVH (decrements reference count). */ +RTC_API void rtcReleaseBVH(RTCBVH bvh); + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.h new file mode 100644 index 00000000..eccde8a1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.h @@ -0,0 +1,224 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include "rtcore_version.h" + +RTC_NAMESPACE_BEGIN + +#if defined(_WIN32) +#if defined(_M_X64) +typedef long long ssize_t; +#else +typedef int ssize_t; +#endif +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) __declspec(align(__VA_ARGS__)) +#else +# define RTC_ALIGN(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#if !defined (RTC_DEPRECATED) +#ifdef __GNUC__ + #define RTC_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define RTC_DEPRECATED __declspec(deprecated) +#else + #define RTC_DEPRECATED +#endif +#endif + +#if defined(_WIN32) +# define RTC_FORCEINLINE __forceinline +#else +# define RTC_FORCEINLINE inline __attribute__((always_inline)) +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + struct RTCBounds bounds0; + struct RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + const struct RTCIntersectContext* context; + struct RTCRayN* ray; + struct RTCHitN* hit; + unsigned int N; +}; + +/* Filter callback function */ +typedef void (*RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* args); + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + enum RTCIntersectContextFlags flags; // intersection flags + RTCFilterFunctionN filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(struct RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.isph new file mode 100644 index 00000000..6d13ce9c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_common.isph @@ -0,0 +1,205 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_COMMON_ISPH__ +#define __RTC_COMMON_ISPH__ + +#if !defined(RTC_API) +#define RTC_API extern "C" unmasked +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#else +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#endif + +#if !defined(RTC_DEPRECATED) +#define RTC_DEPRECATED // FIXME: deprecation not supported by ISPC +#endif + +#if !defined(RTC_FORCEINLINE) +#define RTC_FORCEINLINE inline +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((uniform unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + RTCBounds bounds0; + RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + RTCIntersectContextFlags flags; // intersection flags + void* filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(uniform RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + const RTCIntersectContext* uniform context; + struct RTCRayN* uniform ray; + struct RTCHitN* uniform hit; + uniform unsigned int N; +}; + +/* Filter callback function */ +typedef unmasked void (*uniform RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* uniform args); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.h new file mode 100644 index 00000000..ed6c8e98 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.h @@ -0,0 +1,97 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque device type */ +typedef struct RTCDeviceTy* RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const char* config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + RTC_DEVICE_PROPERTY_POINT_GEOMETRY_SUPPORTED = 101, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API ssize_t rtcGetDeviceProperty(RTCDevice device, enum RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const enum RTCDeviceProperty prop, ssize_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API enum RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef void (*RTCErrorFunction)(void* userPtr, enum RTCError code, const char* str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, RTCErrorFunction error, void* userPtr); + +/* Memory monitor callback function */ +typedef bool (*RTCMemoryMonitorFunction)(void* ptr, ssize_t bytes, bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* userPtr); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.isph new file mode 100644 index 00000000..9d2c326a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_device.isph @@ -0,0 +1,95 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_DEVICE_ISPH__ +#define __RTC_DEVICE_ISPH__ + +#include "rtcore_common.isph" + +/* Opaque device type */ +typedef uniform struct RTCDeviceTy* uniform RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const uniform int8* uniform config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API uniform intptr_t rtcGetDeviceProperty(RTCDevice device, uniform RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const uniform RTCDeviceProperty prop, uniform intptr_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API uniform RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef unmasked void (*uniform RTCErrorFunction)(void* uniform userPtr, uniform RTCError code, const uniform int8* uniform str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, uniform RTCErrorFunction error, void* uniform userPtr); + +/* Memory monitor callback function */ +typedef uniform bool (*uniform RTCMemoryMonitorFunction)(uniform intptr_t bytes, uniform bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* uniform userPtr); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.h new file mode 100644 index 00000000..ce3e832a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.h @@ -0,0 +1,379 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_buffer.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque scene type */ +typedef struct RTCSceneTy* RTCScene; + +/* Opaque geometry type */ +typedef struct RTCGeometryTy* RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* geometryUserPtr; + unsigned int primID; + unsigned int timeStep; + struct RTCBounds* bounds_o; +}; + +/* Bounding callback function */ +typedef void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayHitN* rayhit; + unsigned int N; +}; + +/* Intersection callback function */ +typedef void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayN* ray; + unsigned int N; +}; + +/* Occlusion callback function */ +typedef void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* geometryUserPtr; + RTCGeometry geometry; + unsigned int primID; + unsigned int timeStep; + const float* u; + const float* v; + const float* Ng_x; + const float* Ng_y; + const float* Ng_z; + float* P_x; + float* P_y; + float* P_z; + unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, enum RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count) */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, float startTime, float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, enum RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, RTCBuffer buffer, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, const void* ptr, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* rtcSetNewGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, size_t byteStride, size_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* rtcGetGeometryBufferData(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, RTCBoundsFunction bounds, void* userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const struct RTCIntersectFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const struct RTCOccludedFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, unsigned int timeStep, enum RTCFormat format, const void* xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, float time, enum RTCFormat format, void* xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, unsigned int topologyID, enum RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, unsigned int vertexAttributeID, unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API unsigned int rtcGetGeometryFace(RTCGeometry geometry, unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, unsigned int topologyID, unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const struct RTCInterpolateArguments* args); + +/* Interpolates vertex data to some u/v location. */ +RTC_FORCEINLINE void rtcInterpolate0(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, float* P, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate1(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate2(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = ddPdudu; + args.ddPdvdv = ddPdvdv; + args.ddPdudv = ddPdudv; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_API void rtcInterpolateN(const struct RTCInterpolateNArguments* args); + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + unsigned short width,height; // max is a 32k x 32k grid +}; + +RTC_NAMESPACE_END + + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.isph new file mode 100644 index 00000000..f536bc7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_geometry.isph @@ -0,0 +1,402 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __EMBREE_GEOMETRY_ISPH__ +#define __EMBREE_GEOMETRY_ISPH__ + +#include "rtcore_buffer.isph" + +/* Opaque scene type */ +typedef uniform struct RTCSceneTy* uniform RTCScene; + +/* Opaque geometry type */ +typedef uniform struct RTCGeometryTy* uniform RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform RTCBounds* uniform bounds_o; +}; + +/* Bounding callback function */ +typedef unmasked void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* uniform args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayHitN* uniform rayhit; + uniform unsigned int N; +}; + +/* Intersection callback function */ +typedef unmasked void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* uniform args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayN* uniform ray; + uniform unsigned int N; +}; + +/* Occlusion callback function */ +typedef unmasked void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* uniform args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* uniform geometryUserPtr; + RTCGeometry geometry; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform const float* uniform u; + uniform const float* uniform v; + uniform const float* uniform Ng_x; + uniform const float* uniform Ng_y; + uniform const float* uniform Ng_z; + uniform float* uniform P_x; + uniform float* uniform P_y; + uniform float* uniform P_z; + uniform unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef unmasked void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* uniform args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, uniform RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count). */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, uniform unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, uniform float startTime, uniform float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, uniform unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, uniform unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, uniform RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform RTCBuffer buffer, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, const void* uniform ptr, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* uniform rtcSetNewGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* uniform rtcGetGeometryBufferData(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* uniform ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* uniform rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, uniform unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, uniform RTCBoundsFunction bounds, void* uniform userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, uniform RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, uniform RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const uniform struct RTCIntersectFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const uniform struct RTCOccludedFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, uniform unsigned int timeStep, uniform RTCFormat format, const void* uniform xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, uniform float time, uniform RTCFormat format, void* uniform xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, uniform float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, uniform unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, uniform unsigned int topologyID, uniform RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, uniform unsigned int vertexAttributeID, uniform unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, uniform RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API uniform unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, uniform unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API uniform unsigned int rtcGetGeometryFace(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API uniform unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API uniform unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API uniform unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, uniform unsigned int topologyID, uniform unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const RTCInterpolateArguments* uniform args); + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations and calculates all derivatives. */ +RTC_API void rtcInterpolateN(const RTCInterpolateNArguments* uniform args); + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_FORCEINLINE void rtcInterpolateV0(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV1(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV2(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + varying float* uniform ddPdudu, varying float* uniform ddPdvdv, varying float* uniform ddPdudv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = (uniform float* uniform)ddPdudu; + args.ddPdvdv = (uniform float* uniform)ddPdvdv; + args.ddPdudv = (uniform float* uniform)ddPdudv; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + int16 width,height; // max is a 32k x 32k grid +}; + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.h new file mode 100644 index 00000000..d95f6d29 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.h @@ -0,0 +1,391 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Ray structure for a single ray */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure for a single ray */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure for a single ray */ +struct RTCRayHit +{ + struct RTCRay ray; + struct RTCHit hit; +}; + +/* Ray structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCRay4 +{ + float org_x[4]; + float org_y[4]; + float org_z[4]; + float tnear[4]; + + float dir_x[4]; + float dir_y[4]; + float dir_z[4]; + float time[4]; + + float tfar[4]; + unsigned int mask[4]; + unsigned int id[4]; + unsigned int flags[4]; +}; + +/* Hit structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCHit4 +{ + float Ng_x[4]; + float Ng_y[4]; + float Ng_z[4]; + + float u[4]; + float v[4]; + + unsigned int primID[4]; + unsigned int geomID[4]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][4]; +}; + +/* Combined ray/hit structure for a packet of 4 rays */ +struct RTCRayHit4 +{ + struct RTCRay4 ray; + struct RTCHit4 hit; +}; + +/* Ray structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCRay8 +{ + float org_x[8]; + float org_y[8]; + float org_z[8]; + float tnear[8]; + + float dir_x[8]; + float dir_y[8]; + float dir_z[8]; + float time[8]; + + float tfar[8]; + unsigned int mask[8]; + unsigned int id[8]; + unsigned int flags[8]; +}; + +/* Hit structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCHit8 +{ + float Ng_x[8]; + float Ng_y[8]; + float Ng_z[8]; + + float u[8]; + float v[8]; + + unsigned int primID[8]; + unsigned int geomID[8]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][8]; +}; + +/* Combined ray/hit structure for a packet of 8 rays */ +struct RTCRayHit8 +{ + struct RTCRay8 ray; + struct RTCHit8 hit; +}; + +/* Ray structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCRay16 +{ + float org_x[16]; + float org_y[16]; + float org_z[16]; + float tnear[16]; + + float dir_x[16]; + float dir_y[16]; + float dir_z[16]; + float time[16]; + + float tfar[16]; + unsigned int mask[16]; + unsigned int id[16]; + unsigned int flags[16]; +}; + +/* Hit structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCHit16 +{ + float Ng_x[16]; + float Ng_y[16]; + float Ng_z[16]; + + float u[16]; + float v[16]; + + unsigned int primID[16]; + unsigned int geomID[16]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; +}; + +/* Combined ray/hit structure for a packet of 16 rays */ +struct RTCRayHit16 +{ + struct RTCRay16 ray; + struct RTCHit16 hit; +}; + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + float* org_x; + float* org_y; + float* org_z; + float* tnear; + + float* dir_x; + float* dir_y; + float* dir_z; + float* time; + + float* tfar; + unsigned int* mask; + unsigned int* id; + unsigned int* flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + float* Ng_x; + float* Ng_y; + float* Ng_z; + + float* u; + float* v; + + unsigned int* primID; + unsigned int* geomID; + unsigned int* instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + struct RTCRayNp ray; + struct RTCHitNp hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +#if defined(__cplusplus) + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE float& RTCRayN_org_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[0*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[1*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[2*N+i]; } +RTC_FORCEINLINE float& RTCRayN_tnear(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[3*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_dir_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[4*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[5*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[6*N+i]; } +RTC_FORCEINLINE float& RTCRayN_time (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[7*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_tfar (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[8*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_mask (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[9*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_id (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[10*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_flags(RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[11*N+i]; } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE float& RTCHitN_Ng_x(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[0*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_y(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[1*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_z(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[2*N+i]; } + +RTC_FORCEINLINE float& RTCHitN_u(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[3*N+i]; } +RTC_FORCEINLINE float& RTCHitN_v(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[4*N+i]; } + +RTC_FORCEINLINE unsigned int& RTCHitN_primID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[5*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_geomID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[6*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_instID(RTCHitN* hit, unsigned int N, unsigned int i, unsigned int l) { return ((unsigned*)hit)[7*N+i+N*l]; } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* RTCRayHitN_RayN(RTCRayHitN* rayhit, unsigned int N) { return (RTCRayN*)&((float*)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* RTCRayHitN_HitN(RTCRayHitN* rayhit, unsigned int N) { return (RTCHitN*)&((float*)rayhit)[12*N]; } + +/* Helper structure for a ray packet of compile-time size N */ +template +struct RTCRayNt +{ + float org_x[N]; + float org_y[N]; + float org_z[N]; + float tnear[N]; + + float dir_x[N]; + float dir_y[N]; + float dir_z[N]; + float time[N]; + + float tfar[N]; + unsigned int mask[N]; + unsigned int id[N]; + unsigned int flags[N]; +}; + +/* Helper structure for a hit packet of compile-time size N */ +template +struct RTCHitNt +{ + float Ng_x[N]; + float Ng_y[N]; + float Ng_z[N]; + + float u[N]; + float v[N]; + + unsigned int primID[N]; + unsigned int geomID[N]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][N]; +}; + +/* Helper structure for a combined ray/hit packet of compile-time size N */ +template +struct RTCRayHitNt +{ + RTCRayNt ray; + RTCHitNt hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* rayN, unsigned int N, unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* hitN, unsigned int N, unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* hitN, const RTCHit* hit, unsigned int N, unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* rayhitN, unsigned int N, unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.isph new file mode 100644 index 00000000..e01f0049 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_ray.isph @@ -0,0 +1,216 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_RAY_ISPH__ +#define __RTC_RAY_ISPH__ + +#include "rtcore_common.isph" + +/* Ray structure */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure */ +struct RTCRayHit +{ + RTCRay ray; + RTCHit hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCRayN_org_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_tnear(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[3*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_dir_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[4*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[5*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[6*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_time (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[7*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_tfar (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[8*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_mask (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[9*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_id (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[10*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_flags(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[11*N+i]); } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCHitN_Ng_x(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_y(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_z(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_u (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[3*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_v (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[4*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_primID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[5*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_geomID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[6*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_instID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i, uniform unsigned int l) { return *((varying unsigned int* uniform) &((unsigned int* uniform)hit)[7*N+i+l*N]); } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* uniform RTCRayHitN_RayN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCRayN* uniform)&((uniform float* uniform)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* uniform RTCRayHitN_HitN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCHitN* uniform)&((uniform float* uniform)rayhit)[12*N]; } + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + uniform float* uniform org_x; + uniform float* uniform org_y; + uniform float* uniform org_z; + uniform float* uniform tnear; + + uniform float* uniform dir_x; + uniform float* uniform dir_y; + uniform float* uniform dir_z; + uniform float* uniform time; + + uniform float* uniform tfar; + uniform unsigned int* uniform mask; + uniform unsigned int* uniform id; + uniform unsigned int* uniform flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + uniform float* uniform Ng_x; + uniform float* uniform Ng_y; + uniform float* uniform Ng_z; + + uniform float* uniform u; + uniform float* uniform v; + + uniform unsigned int* uniform primID; + uniform unsigned int* uniform geomID; + uniform unsigned int* uniform instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + RTCRayNp ray; + RTCHitNp hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* uniform rayN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* uniform hitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* uniform hitN, const varying RTCHit* uniform hit, uniform unsigned int N, uniform unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* uniform rayhitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* uniform ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* uniform hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.h new file mode 100644 index 00000000..2bda59cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.h @@ -0,0 +1,149 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHit4; +struct RTCRayHit8; +struct RTCRayHit16; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef bool (*RTCProgressMonitorFunction)(void* ptr, double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, enum RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, enum RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API enum RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, struct RTCBounds* bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, struct RTCLinearBounds* bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit4* rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit8* rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit16* rayhit); + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit, unsigned int M, size_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit** rayhit, unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHitN* rayhit, unsigned int N, unsigned int M, size_t byteStride); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayHitNp* rayhit, unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay4* ray); + +/* Tests a packet of 8 rays for occlusion with the scene. */ +RTC_API void rtcOccluded8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay8* ray); + +/* Tests a packet of 16 rays for occlusion with the scene. */ +RTC_API void rtcOccluded16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay16* ray); + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray, unsigned int M, size_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay** ray, unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayN* ray, unsigned int N, unsigned int M, size_t byteStride); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayNp* ray, unsigned int N); + +#if defined(__cplusplus) + +/* Helper for easily combining scene flags */ +inline RTCSceneFlags operator|(RTCSceneFlags a, RTCSceneFlags b) { + return (RTCSceneFlags)((size_t)a | (size_t)b); +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.isph new file mode 100644 index 00000000..5579d7e3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_scene.isph @@ -0,0 +1,176 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_SCENE_ISPH__ +#define __RTC_SCENE_ISPH__ + +#include "rtcore_device.isph" + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API uniform unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, uniform unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, uniform unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, uniform unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef unmasked uniform bool (*uniform RTCProgressMonitorFunction)(void* uniform ptr, uniform double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* uniform ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, uniform RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, uniform RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API uniform RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, uniform RTCBounds* uniform bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, uniform RTCLinearBounds* uniform bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a varying ray with the scene. */ +RTC_FORCEINLINE void rtcIntersectV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + if (sizeof(varying float) == 16) + rtcIntersect4((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 32) + rtcIntersect8((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 64) + rtcIntersect16((uniform int* uniform)&imask, scene, context, rayhit); +} + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit** uniform rayhit, uniform unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayHitN* uniform rayhit, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of M ray packets of native packet size with the scene. */ +RTC_FORCEINLINE void rtcIntersectVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcIntersectNM(scene, context, (struct RTCRayHitN*)rayhit, sizeof(varying float)/4, M, byteStride); +} + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHitNp* uniform rayhit, uniform unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 8 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded8(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 16 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded16(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a varying ray for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + + if (sizeof(varying float) == 16) + rtcOccluded4((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 32) + rtcOccluded8((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 64) + rtcOccluded16((uniform int* uniform)&imask, scene, context, ray); +} + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay** uniform ray, uniform unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of M ray packets of native size in SOA format for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcOccludedNM(scene, context, (struct RTCRayN*)ray, sizeof(varying float)/4, M, byteStride); +} + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayNp* uniform ray, uniform unsigned int N); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_version.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_version.h new file mode 100644 index 00000000..43a69754 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/include/embree3/rtcore_version.h @@ -0,0 +1,62 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#define RTC_VERSION_MAJOR 3 +#define RTC_VERSION_MINOR 5 +#define RTC_VERSION_PATCH 2 +#define RTC_VERSION 30502 +#define RTC_VERSION_STRING "3.5.2" + +/* #undef EMBREE_STATIC_LIB */ +/* #undef EMBREE_API_NAMESPACE */ + +#if defined(EMBREE_API_NAMESPACE) +# define RTC_NAMESPACE +# define RTC_NAMESPACE_BEGIN namespace { +# define RTC_NAMESPACE_END } +# define RTC_NAMESPACE_OPEN using namespace ; +# define RTC_API_EXTERN_C +# undef EMBREE_API_NAMESPACE +#else +# define RTC_NAMESPACE_BEGIN +# define RTC_NAMESPACE_END +# define RTC_NAMESPACE_OPEN +# if defined(__cplusplus) +# define RTC_API_EXTERN_C extern "C" +# else +# define RTC_API_EXTERN_C +# endif +#endif + +#if defined(ISPC) +# define RTC_API_IMPORT extern "C" unmasked +# define RTC_API_EXPORT extern "C" unmasked +#elif defined(EMBREE_STATIC_LIB) +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C +#elif defined(_WIN32) +# define RTC_API_IMPORT RTC_API_EXTERN_C __declspec(dllimport) +# define RTC_API_EXPORT RTC_API_EXTERN_C __declspec(dllexport) +#else +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C __attribute__ ((visibility ("default"))) +#endif + +#if defined(RTC_EXPORT_API) +# define RTC_API RTC_API_EXPORT +#else +# define RTC_API RTC_API_IMPORT +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libembree3.so.3 b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libembree3.so.3 new file mode 100644 index 00000000..698354a5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libembree3.so.3 differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbb.so.2 b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbb.so.2 new file mode 100644 index 00000000..6a2bf32a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbb.so.2 differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbbmalloc.so.2 b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbbmalloc.so.2 new file mode 100644 index 00000000..3a6cbd0f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Linux/lib/libtbbmalloc.so.2 differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/.DS_Store b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/.DS_Store new file mode 100644 index 00000000..f9f60d0a Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/.DS_Store differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.h new file mode 100644 index 00000000..8d74ea77 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.h @@ -0,0 +1,26 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_version.h" +#include "rtcore_common.h" +#include "rtcore_device.h" +#include "rtcore_buffer.h" +#include "rtcore_ray.h" +#include "rtcore_geometry.h" +#include "rtcore_scene.h" +#include "rtcore_builder.h" diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.isph new file mode 100644 index 00000000..ce0043d2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore.isph @@ -0,0 +1,28 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_ISPH__ +#define __RTC_ISPH__ + +#include "rtcore_version.h" +#include "rtcore_common.isph" +#include "rtcore_device.isph" +#include "rtcore_buffer.isph" +#include "rtcore_ray.isph" +#include "rtcore_geometry.isph" +#include "rtcore_scene.isph" + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.h new file mode 100644 index 00000000..bc0824b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.h @@ -0,0 +1,64 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef struct RTCBufferTy* RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, size_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* ptr, size_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.isph new file mode 100644 index 00000000..e4824025 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_buffer.isph @@ -0,0 +1,63 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_BUFFER_ISPH__ +#define __RTC_BUFFER_ISPH__ + +#include "rtcore_device.isph" + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef uniform struct RTCBufferTy* uniform RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, uniform uintptr_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* uniform ptr, uniform uintptr_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* uniform rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer handle (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_builder.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_builder.h new file mode 100644 index 00000000..057ad05c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_builder.h @@ -0,0 +1,133 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_scene.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque BVH type */ +typedef struct RTCBVHTy* RTCBVH; + +/* Input build primitives for the builder */ +struct RTC_ALIGN(32) RTCBuildPrimitive +{ + float lower_x, lower_y, lower_z; + unsigned int geomID; + float upper_x, upper_y, upper_z; + unsigned int primID; +}; + +/* Opaque thread local allocator type */ +typedef struct RTCThreadLocalAllocatorTy* RTCThreadLocalAllocator; + +/* Callback to create a node */ +typedef void* (*RTCCreateNodeFunction) (RTCThreadLocalAllocator allocator, unsigned int childCount, void* userPtr); + +/* Callback to set the pointer to all children */ +typedef void (*RTCSetNodeChildrenFunction) (void* nodePtr, void** children, unsigned int childCount, void* userPtr); + +/* Callback to set the bounds of all children */ +typedef void (*RTCSetNodeBoundsFunction) (void* nodePtr, const struct RTCBounds** bounds, unsigned int childCount, void* userPtr); + +/* Callback to create a leaf node */ +typedef void* (*RTCCreateLeafFunction) (RTCThreadLocalAllocator allocator, const struct RTCBuildPrimitive* primitives, size_t primitiveCount, void* userPtr); + +/* Callback to split a build primitive */ +typedef void (*RTCSplitPrimitiveFunction) (const struct RTCBuildPrimitive* primitive, unsigned int dimension, float position, struct RTCBounds* leftBounds, struct RTCBounds* rightBounds, void* userPtr); + +/* Build flags */ +enum RTCBuildFlags +{ + RTC_BUILD_FLAG_NONE = 0, + RTC_BUILD_FLAG_DYNAMIC = (1 << 0), +}; + +/* Input for builders */ +struct RTCBuildArguments +{ + size_t byteSize; + + enum RTCBuildQuality buildQuality; + enum RTCBuildFlags buildFlags; + unsigned int maxBranchingFactor; + unsigned int maxDepth; + unsigned int sahBlockSize; + unsigned int minLeafSize; + unsigned int maxLeafSize; + float traversalCost; + float intersectionCost; + + RTCBVH bvh; + struct RTCBuildPrimitive* primitives; + size_t primitiveCount; + size_t primitiveArrayCapacity; + + RTCCreateNodeFunction createNode; + RTCSetNodeChildrenFunction setNodeChildren; + RTCSetNodeBoundsFunction setNodeBounds; + RTCCreateLeafFunction createLeaf; + RTCSplitPrimitiveFunction splitPrimitive; + RTCProgressMonitorFunction buildProgress; + void* userPtr; +}; + +/* Returns the default build settings. */ +RTC_FORCEINLINE struct RTCBuildArguments rtcDefaultBuildArguments() +{ + struct RTCBuildArguments args; + args.byteSize = sizeof(args); + args.buildQuality = RTC_BUILD_QUALITY_MEDIUM; + args.buildFlags = RTC_BUILD_FLAG_NONE; + args.maxBranchingFactor = 2; + args.maxDepth = 32; + args.sahBlockSize = 1; + args.minLeafSize = 1; + args.maxLeafSize = 32; + args.traversalCost = 1.0f; + args.intersectionCost = 1.0f; + args.bvh = NULL; + args.primitives = NULL; + args.primitiveCount = 0; + args.primitiveArrayCapacity = 0; + args.createNode = NULL; + args.setNodeChildren = NULL; + args.setNodeBounds = NULL; + args.createLeaf = NULL; + args.splitPrimitive = NULL; + args.buildProgress = NULL; + args.userPtr = NULL; + return args; +} + +/* Creates a new BVH. */ +RTC_API RTCBVH rtcNewBVH(RTCDevice device); + +/* Builds a BVH. */ +RTC_API void* rtcBuildBVH(const struct RTCBuildArguments* args); + +/* Allocates memory using the thread local allocator. */ +RTC_API void* rtcThreadLocalAlloc(RTCThreadLocalAllocator allocator, size_t bytes, size_t align); + +/* Retains the BVH (increments reference count). */ +RTC_API void rtcRetainBVH(RTCBVH bvh); + +/* Releases the BVH (decrements reference count). */ +RTC_API void rtcReleaseBVH(RTCBVH bvh); + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.h new file mode 100644 index 00000000..eccde8a1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.h @@ -0,0 +1,224 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include "rtcore_version.h" + +RTC_NAMESPACE_BEGIN + +#if defined(_WIN32) +#if defined(_M_X64) +typedef long long ssize_t; +#else +typedef int ssize_t; +#endif +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) __declspec(align(__VA_ARGS__)) +#else +# define RTC_ALIGN(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#if !defined (RTC_DEPRECATED) +#ifdef __GNUC__ + #define RTC_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define RTC_DEPRECATED __declspec(deprecated) +#else + #define RTC_DEPRECATED +#endif +#endif + +#if defined(_WIN32) +# define RTC_FORCEINLINE __forceinline +#else +# define RTC_FORCEINLINE inline __attribute__((always_inline)) +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + struct RTCBounds bounds0; + struct RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + const struct RTCIntersectContext* context; + struct RTCRayN* ray; + struct RTCHitN* hit; + unsigned int N; +}; + +/* Filter callback function */ +typedef void (*RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* args); + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + enum RTCIntersectContextFlags flags; // intersection flags + RTCFilterFunctionN filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(struct RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.isph new file mode 100644 index 00000000..6d13ce9c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_common.isph @@ -0,0 +1,205 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_COMMON_ISPH__ +#define __RTC_COMMON_ISPH__ + +#if !defined(RTC_API) +#define RTC_API extern "C" unmasked +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#else +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#endif + +#if !defined(RTC_DEPRECATED) +#define RTC_DEPRECATED // FIXME: deprecation not supported by ISPC +#endif + +#if !defined(RTC_FORCEINLINE) +#define RTC_FORCEINLINE inline +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((uniform unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + RTCBounds bounds0; + RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + RTCIntersectContextFlags flags; // intersection flags + void* filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(uniform RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + const RTCIntersectContext* uniform context; + struct RTCRayN* uniform ray; + struct RTCHitN* uniform hit; + uniform unsigned int N; +}; + +/* Filter callback function */ +typedef unmasked void (*uniform RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* uniform args); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.h new file mode 100644 index 00000000..ed6c8e98 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.h @@ -0,0 +1,97 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque device type */ +typedef struct RTCDeviceTy* RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const char* config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + RTC_DEVICE_PROPERTY_POINT_GEOMETRY_SUPPORTED = 101, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API ssize_t rtcGetDeviceProperty(RTCDevice device, enum RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const enum RTCDeviceProperty prop, ssize_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API enum RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef void (*RTCErrorFunction)(void* userPtr, enum RTCError code, const char* str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, RTCErrorFunction error, void* userPtr); + +/* Memory monitor callback function */ +typedef bool (*RTCMemoryMonitorFunction)(void* ptr, ssize_t bytes, bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* userPtr); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.isph new file mode 100644 index 00000000..9d2c326a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_device.isph @@ -0,0 +1,95 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_DEVICE_ISPH__ +#define __RTC_DEVICE_ISPH__ + +#include "rtcore_common.isph" + +/* Opaque device type */ +typedef uniform struct RTCDeviceTy* uniform RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const uniform int8* uniform config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API uniform intptr_t rtcGetDeviceProperty(RTCDevice device, uniform RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const uniform RTCDeviceProperty prop, uniform intptr_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API uniform RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef unmasked void (*uniform RTCErrorFunction)(void* uniform userPtr, uniform RTCError code, const uniform int8* uniform str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, uniform RTCErrorFunction error, void* uniform userPtr); + +/* Memory monitor callback function */ +typedef uniform bool (*uniform RTCMemoryMonitorFunction)(uniform intptr_t bytes, uniform bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* uniform userPtr); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.h new file mode 100644 index 00000000..ce3e832a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.h @@ -0,0 +1,379 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_buffer.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque scene type */ +typedef struct RTCSceneTy* RTCScene; + +/* Opaque geometry type */ +typedef struct RTCGeometryTy* RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* geometryUserPtr; + unsigned int primID; + unsigned int timeStep; + struct RTCBounds* bounds_o; +}; + +/* Bounding callback function */ +typedef void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayHitN* rayhit; + unsigned int N; +}; + +/* Intersection callback function */ +typedef void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayN* ray; + unsigned int N; +}; + +/* Occlusion callback function */ +typedef void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* geometryUserPtr; + RTCGeometry geometry; + unsigned int primID; + unsigned int timeStep; + const float* u; + const float* v; + const float* Ng_x; + const float* Ng_y; + const float* Ng_z; + float* P_x; + float* P_y; + float* P_z; + unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, enum RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count) */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, float startTime, float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, enum RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, RTCBuffer buffer, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, const void* ptr, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* rtcSetNewGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, size_t byteStride, size_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* rtcGetGeometryBufferData(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, RTCBoundsFunction bounds, void* userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const struct RTCIntersectFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const struct RTCOccludedFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, unsigned int timeStep, enum RTCFormat format, const void* xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, float time, enum RTCFormat format, void* xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, unsigned int topologyID, enum RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, unsigned int vertexAttributeID, unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API unsigned int rtcGetGeometryFace(RTCGeometry geometry, unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, unsigned int topologyID, unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const struct RTCInterpolateArguments* args); + +/* Interpolates vertex data to some u/v location. */ +RTC_FORCEINLINE void rtcInterpolate0(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, float* P, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate1(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate2(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = ddPdudu; + args.ddPdvdv = ddPdvdv; + args.ddPdudv = ddPdudv; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_API void rtcInterpolateN(const struct RTCInterpolateNArguments* args); + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + unsigned short width,height; // max is a 32k x 32k grid +}; + +RTC_NAMESPACE_END + + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.isph new file mode 100644 index 00000000..f536bc7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_geometry.isph @@ -0,0 +1,402 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __EMBREE_GEOMETRY_ISPH__ +#define __EMBREE_GEOMETRY_ISPH__ + +#include "rtcore_buffer.isph" + +/* Opaque scene type */ +typedef uniform struct RTCSceneTy* uniform RTCScene; + +/* Opaque geometry type */ +typedef uniform struct RTCGeometryTy* uniform RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform RTCBounds* uniform bounds_o; +}; + +/* Bounding callback function */ +typedef unmasked void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* uniform args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayHitN* uniform rayhit; + uniform unsigned int N; +}; + +/* Intersection callback function */ +typedef unmasked void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* uniform args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayN* uniform ray; + uniform unsigned int N; +}; + +/* Occlusion callback function */ +typedef unmasked void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* uniform args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* uniform geometryUserPtr; + RTCGeometry geometry; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform const float* uniform u; + uniform const float* uniform v; + uniform const float* uniform Ng_x; + uniform const float* uniform Ng_y; + uniform const float* uniform Ng_z; + uniform float* uniform P_x; + uniform float* uniform P_y; + uniform float* uniform P_z; + uniform unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef unmasked void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* uniform args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, uniform RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count). */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, uniform unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, uniform float startTime, uniform float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, uniform unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, uniform unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, uniform RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform RTCBuffer buffer, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, const void* uniform ptr, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* uniform rtcSetNewGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* uniform rtcGetGeometryBufferData(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* uniform ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* uniform rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, uniform unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, uniform RTCBoundsFunction bounds, void* uniform userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, uniform RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, uniform RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const uniform struct RTCIntersectFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const uniform struct RTCOccludedFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, uniform unsigned int timeStep, uniform RTCFormat format, const void* uniform xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, uniform float time, uniform RTCFormat format, void* uniform xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, uniform float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, uniform unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, uniform unsigned int topologyID, uniform RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, uniform unsigned int vertexAttributeID, uniform unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, uniform RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API uniform unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, uniform unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API uniform unsigned int rtcGetGeometryFace(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API uniform unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API uniform unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API uniform unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, uniform unsigned int topologyID, uniform unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const RTCInterpolateArguments* uniform args); + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations and calculates all derivatives. */ +RTC_API void rtcInterpolateN(const RTCInterpolateNArguments* uniform args); + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_FORCEINLINE void rtcInterpolateV0(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV1(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV2(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + varying float* uniform ddPdudu, varying float* uniform ddPdvdv, varying float* uniform ddPdudv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = (uniform float* uniform)ddPdudu; + args.ddPdvdv = (uniform float* uniform)ddPdvdv; + args.ddPdudv = (uniform float* uniform)ddPdudv; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + int16 width,height; // max is a 32k x 32k grid +}; + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.h new file mode 100644 index 00000000..d95f6d29 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.h @@ -0,0 +1,391 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Ray structure for a single ray */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure for a single ray */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure for a single ray */ +struct RTCRayHit +{ + struct RTCRay ray; + struct RTCHit hit; +}; + +/* Ray structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCRay4 +{ + float org_x[4]; + float org_y[4]; + float org_z[4]; + float tnear[4]; + + float dir_x[4]; + float dir_y[4]; + float dir_z[4]; + float time[4]; + + float tfar[4]; + unsigned int mask[4]; + unsigned int id[4]; + unsigned int flags[4]; +}; + +/* Hit structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCHit4 +{ + float Ng_x[4]; + float Ng_y[4]; + float Ng_z[4]; + + float u[4]; + float v[4]; + + unsigned int primID[4]; + unsigned int geomID[4]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][4]; +}; + +/* Combined ray/hit structure for a packet of 4 rays */ +struct RTCRayHit4 +{ + struct RTCRay4 ray; + struct RTCHit4 hit; +}; + +/* Ray structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCRay8 +{ + float org_x[8]; + float org_y[8]; + float org_z[8]; + float tnear[8]; + + float dir_x[8]; + float dir_y[8]; + float dir_z[8]; + float time[8]; + + float tfar[8]; + unsigned int mask[8]; + unsigned int id[8]; + unsigned int flags[8]; +}; + +/* Hit structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCHit8 +{ + float Ng_x[8]; + float Ng_y[8]; + float Ng_z[8]; + + float u[8]; + float v[8]; + + unsigned int primID[8]; + unsigned int geomID[8]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][8]; +}; + +/* Combined ray/hit structure for a packet of 8 rays */ +struct RTCRayHit8 +{ + struct RTCRay8 ray; + struct RTCHit8 hit; +}; + +/* Ray structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCRay16 +{ + float org_x[16]; + float org_y[16]; + float org_z[16]; + float tnear[16]; + + float dir_x[16]; + float dir_y[16]; + float dir_z[16]; + float time[16]; + + float tfar[16]; + unsigned int mask[16]; + unsigned int id[16]; + unsigned int flags[16]; +}; + +/* Hit structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCHit16 +{ + float Ng_x[16]; + float Ng_y[16]; + float Ng_z[16]; + + float u[16]; + float v[16]; + + unsigned int primID[16]; + unsigned int geomID[16]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; +}; + +/* Combined ray/hit structure for a packet of 16 rays */ +struct RTCRayHit16 +{ + struct RTCRay16 ray; + struct RTCHit16 hit; +}; + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + float* org_x; + float* org_y; + float* org_z; + float* tnear; + + float* dir_x; + float* dir_y; + float* dir_z; + float* time; + + float* tfar; + unsigned int* mask; + unsigned int* id; + unsigned int* flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + float* Ng_x; + float* Ng_y; + float* Ng_z; + + float* u; + float* v; + + unsigned int* primID; + unsigned int* geomID; + unsigned int* instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + struct RTCRayNp ray; + struct RTCHitNp hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +#if defined(__cplusplus) + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE float& RTCRayN_org_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[0*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[1*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[2*N+i]; } +RTC_FORCEINLINE float& RTCRayN_tnear(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[3*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_dir_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[4*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[5*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[6*N+i]; } +RTC_FORCEINLINE float& RTCRayN_time (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[7*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_tfar (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[8*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_mask (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[9*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_id (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[10*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_flags(RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[11*N+i]; } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE float& RTCHitN_Ng_x(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[0*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_y(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[1*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_z(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[2*N+i]; } + +RTC_FORCEINLINE float& RTCHitN_u(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[3*N+i]; } +RTC_FORCEINLINE float& RTCHitN_v(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[4*N+i]; } + +RTC_FORCEINLINE unsigned int& RTCHitN_primID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[5*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_geomID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[6*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_instID(RTCHitN* hit, unsigned int N, unsigned int i, unsigned int l) { return ((unsigned*)hit)[7*N+i+N*l]; } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* RTCRayHitN_RayN(RTCRayHitN* rayhit, unsigned int N) { return (RTCRayN*)&((float*)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* RTCRayHitN_HitN(RTCRayHitN* rayhit, unsigned int N) { return (RTCHitN*)&((float*)rayhit)[12*N]; } + +/* Helper structure for a ray packet of compile-time size N */ +template +struct RTCRayNt +{ + float org_x[N]; + float org_y[N]; + float org_z[N]; + float tnear[N]; + + float dir_x[N]; + float dir_y[N]; + float dir_z[N]; + float time[N]; + + float tfar[N]; + unsigned int mask[N]; + unsigned int id[N]; + unsigned int flags[N]; +}; + +/* Helper structure for a hit packet of compile-time size N */ +template +struct RTCHitNt +{ + float Ng_x[N]; + float Ng_y[N]; + float Ng_z[N]; + + float u[N]; + float v[N]; + + unsigned int primID[N]; + unsigned int geomID[N]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][N]; +}; + +/* Helper structure for a combined ray/hit packet of compile-time size N */ +template +struct RTCRayHitNt +{ + RTCRayNt ray; + RTCHitNt hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* rayN, unsigned int N, unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* hitN, unsigned int N, unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* hitN, const RTCHit* hit, unsigned int N, unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* rayhitN, unsigned int N, unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.isph new file mode 100644 index 00000000..e01f0049 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_ray.isph @@ -0,0 +1,216 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_RAY_ISPH__ +#define __RTC_RAY_ISPH__ + +#include "rtcore_common.isph" + +/* Ray structure */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure */ +struct RTCRayHit +{ + RTCRay ray; + RTCHit hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCRayN_org_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_tnear(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[3*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_dir_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[4*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[5*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[6*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_time (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[7*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_tfar (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[8*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_mask (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[9*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_id (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[10*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_flags(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[11*N+i]); } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCHitN_Ng_x(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_y(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_z(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_u (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[3*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_v (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[4*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_primID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[5*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_geomID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[6*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_instID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i, uniform unsigned int l) { return *((varying unsigned int* uniform) &((unsigned int* uniform)hit)[7*N+i+l*N]); } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* uniform RTCRayHitN_RayN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCRayN* uniform)&((uniform float* uniform)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* uniform RTCRayHitN_HitN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCHitN* uniform)&((uniform float* uniform)rayhit)[12*N]; } + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + uniform float* uniform org_x; + uniform float* uniform org_y; + uniform float* uniform org_z; + uniform float* uniform tnear; + + uniform float* uniform dir_x; + uniform float* uniform dir_y; + uniform float* uniform dir_z; + uniform float* uniform time; + + uniform float* uniform tfar; + uniform unsigned int* uniform mask; + uniform unsigned int* uniform id; + uniform unsigned int* uniform flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + uniform float* uniform Ng_x; + uniform float* uniform Ng_y; + uniform float* uniform Ng_z; + + uniform float* uniform u; + uniform float* uniform v; + + uniform unsigned int* uniform primID; + uniform unsigned int* uniform geomID; + uniform unsigned int* uniform instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + RTCRayNp ray; + RTCHitNp hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* uniform rayN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* uniform hitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* uniform hitN, const varying RTCHit* uniform hit, uniform unsigned int N, uniform unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* uniform rayhitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* uniform ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* uniform hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.h new file mode 100644 index 00000000..2bda59cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.h @@ -0,0 +1,149 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHit4; +struct RTCRayHit8; +struct RTCRayHit16; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef bool (*RTCProgressMonitorFunction)(void* ptr, double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, enum RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, enum RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API enum RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, struct RTCBounds* bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, struct RTCLinearBounds* bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit4* rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit8* rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit16* rayhit); + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit, unsigned int M, size_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit** rayhit, unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHitN* rayhit, unsigned int N, unsigned int M, size_t byteStride); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayHitNp* rayhit, unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay4* ray); + +/* Tests a packet of 8 rays for occlusion with the scene. */ +RTC_API void rtcOccluded8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay8* ray); + +/* Tests a packet of 16 rays for occlusion with the scene. */ +RTC_API void rtcOccluded16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay16* ray); + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray, unsigned int M, size_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay** ray, unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayN* ray, unsigned int N, unsigned int M, size_t byteStride); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayNp* ray, unsigned int N); + +#if defined(__cplusplus) + +/* Helper for easily combining scene flags */ +inline RTCSceneFlags operator|(RTCSceneFlags a, RTCSceneFlags b) { + return (RTCSceneFlags)((size_t)a | (size_t)b); +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.isph new file mode 100644 index 00000000..5579d7e3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_scene.isph @@ -0,0 +1,176 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_SCENE_ISPH__ +#define __RTC_SCENE_ISPH__ + +#include "rtcore_device.isph" + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API uniform unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, uniform unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, uniform unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, uniform unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef unmasked uniform bool (*uniform RTCProgressMonitorFunction)(void* uniform ptr, uniform double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* uniform ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, uniform RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, uniform RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API uniform RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, uniform RTCBounds* uniform bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, uniform RTCLinearBounds* uniform bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a varying ray with the scene. */ +RTC_FORCEINLINE void rtcIntersectV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + if (sizeof(varying float) == 16) + rtcIntersect4((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 32) + rtcIntersect8((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 64) + rtcIntersect16((uniform int* uniform)&imask, scene, context, rayhit); +} + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit** uniform rayhit, uniform unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayHitN* uniform rayhit, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of M ray packets of native packet size with the scene. */ +RTC_FORCEINLINE void rtcIntersectVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcIntersectNM(scene, context, (struct RTCRayHitN*)rayhit, sizeof(varying float)/4, M, byteStride); +} + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHitNp* uniform rayhit, uniform unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 8 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded8(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 16 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded16(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a varying ray for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + + if (sizeof(varying float) == 16) + rtcOccluded4((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 32) + rtcOccluded8((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 64) + rtcOccluded16((uniform int* uniform)&imask, scene, context, ray); +} + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay** uniform ray, uniform unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of M ray packets of native size in SOA format for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcOccludedNM(scene, context, (struct RTCRayN*)ray, sizeof(varying float)/4, M, byteStride); +} + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayNp* uniform ray, uniform unsigned int N); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_version.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_version.h new file mode 100644 index 00000000..43a69754 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/include/embree3/rtcore_version.h @@ -0,0 +1,62 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#define RTC_VERSION_MAJOR 3 +#define RTC_VERSION_MINOR 5 +#define RTC_VERSION_PATCH 2 +#define RTC_VERSION 30502 +#define RTC_VERSION_STRING "3.5.2" + +/* #undef EMBREE_STATIC_LIB */ +/* #undef EMBREE_API_NAMESPACE */ + +#if defined(EMBREE_API_NAMESPACE) +# define RTC_NAMESPACE +# define RTC_NAMESPACE_BEGIN namespace { +# define RTC_NAMESPACE_END } +# define RTC_NAMESPACE_OPEN using namespace ; +# define RTC_API_EXTERN_C +# undef EMBREE_API_NAMESPACE +#else +# define RTC_NAMESPACE_BEGIN +# define RTC_NAMESPACE_END +# define RTC_NAMESPACE_OPEN +# if defined(__cplusplus) +# define RTC_API_EXTERN_C extern "C" +# else +# define RTC_API_EXTERN_C +# endif +#endif + +#if defined(ISPC) +# define RTC_API_IMPORT extern "C" unmasked +# define RTC_API_EXPORT extern "C" unmasked +#elif defined(EMBREE_STATIC_LIB) +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C +#elif defined(_WIN32) +# define RTC_API_IMPORT RTC_API_EXTERN_C __declspec(dllimport) +# define RTC_API_EXPORT RTC_API_EXTERN_C __declspec(dllexport) +#else +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C __attribute__ ((visibility ("default"))) +#endif + +#if defined(RTC_EXPORT_API) +# define RTC_API RTC_API_EXPORT +#else +# define RTC_API RTC_API_IMPORT +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/.DS_Store b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/.DS_Store new file mode 100644 index 00000000..51a4d8e2 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/.DS_Store differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.3.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.3.dylib new file mode 100755 index 00000000..9c8adb4f Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.3.dylib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.dylib new file mode 120000 index 00000000..89d9a2d7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libembree3.dylib @@ -0,0 +1 @@ +libembree3.3.dylib \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.5.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.5.dylib new file mode 100644 index 00000000..9f3a4224 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.5.dylib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.dylib new file mode 120000 index 00000000..539553f5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.12.dylib @@ -0,0 +1 @@ +libtbb.12.5.dylib \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.dylib new file mode 120000 index 00000000..6bfd44c6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbb.dylib @@ -0,0 +1 @@ +libtbb.12.dylib \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbbmalloc.dylib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbbmalloc.dylib new file mode 100644 index 00000000..90799522 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/MacOSX/lib/libtbbmalloc.dylib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.h new file mode 100644 index 00000000..8d74ea77 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.h @@ -0,0 +1,26 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_version.h" +#include "rtcore_common.h" +#include "rtcore_device.h" +#include "rtcore_buffer.h" +#include "rtcore_ray.h" +#include "rtcore_geometry.h" +#include "rtcore_scene.h" +#include "rtcore_builder.h" diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.isph new file mode 100644 index 00000000..ce0043d2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore.isph @@ -0,0 +1,28 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_ISPH__ +#define __RTC_ISPH__ + +#include "rtcore_version.h" +#include "rtcore_common.isph" +#include "rtcore_device.isph" +#include "rtcore_buffer.isph" +#include "rtcore_ray.isph" +#include "rtcore_geometry.isph" +#include "rtcore_scene.isph" + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.h new file mode 100644 index 00000000..bc0824b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.h @@ -0,0 +1,64 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef struct RTCBufferTy* RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, size_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* ptr, size_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.isph new file mode 100644 index 00000000..e4824025 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_buffer.isph @@ -0,0 +1,63 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_BUFFER_ISPH__ +#define __RTC_BUFFER_ISPH__ + +#include "rtcore_device.isph" + +/* Types of buffers */ +enum RTCBufferType +{ + RTC_BUFFER_TYPE_INDEX = 0, + RTC_BUFFER_TYPE_VERTEX = 1, + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE = 2, + RTC_BUFFER_TYPE_NORMAL = 3, + RTC_BUFFER_TYPE_TANGENT = 4, + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE = 5, + + RTC_BUFFER_TYPE_GRID = 8, + + RTC_BUFFER_TYPE_FACE = 16, + RTC_BUFFER_TYPE_LEVEL = 17, + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX = 18, + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT = 19, + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX = 20, + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT = 21, + RTC_BUFFER_TYPE_HOLE = 22, + + RTC_BUFFER_TYPE_FLAGS = 32 +}; + +/* Opaque buffer type */ +typedef uniform struct RTCBufferTy* uniform RTCBuffer; + +/* Creates a new buffer. */ +RTC_API RTCBuffer rtcNewBuffer(RTCDevice device, uniform uintptr_t byteSize); + +/* Creates a new shared buffer. */ +RTC_API RTCBuffer rtcNewSharedBuffer(RTCDevice device, void* uniform ptr, uniform uintptr_t byteSize); + +/* Returns a pointer to the buffer data. */ +RTC_API void* uniform rtcGetBufferData(RTCBuffer buffer); + +/* Retains the buffer (increments the reference count). */ +RTC_API void rtcRetainBuffer(RTCBuffer buffer); + +/* Releases the buffer handle (decrements the reference count). */ +RTC_API void rtcReleaseBuffer(RTCBuffer buffer); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_builder.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_builder.h new file mode 100644 index 00000000..057ad05c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_builder.h @@ -0,0 +1,133 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_scene.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque BVH type */ +typedef struct RTCBVHTy* RTCBVH; + +/* Input build primitives for the builder */ +struct RTC_ALIGN(32) RTCBuildPrimitive +{ + float lower_x, lower_y, lower_z; + unsigned int geomID; + float upper_x, upper_y, upper_z; + unsigned int primID; +}; + +/* Opaque thread local allocator type */ +typedef struct RTCThreadLocalAllocatorTy* RTCThreadLocalAllocator; + +/* Callback to create a node */ +typedef void* (*RTCCreateNodeFunction) (RTCThreadLocalAllocator allocator, unsigned int childCount, void* userPtr); + +/* Callback to set the pointer to all children */ +typedef void (*RTCSetNodeChildrenFunction) (void* nodePtr, void** children, unsigned int childCount, void* userPtr); + +/* Callback to set the bounds of all children */ +typedef void (*RTCSetNodeBoundsFunction) (void* nodePtr, const struct RTCBounds** bounds, unsigned int childCount, void* userPtr); + +/* Callback to create a leaf node */ +typedef void* (*RTCCreateLeafFunction) (RTCThreadLocalAllocator allocator, const struct RTCBuildPrimitive* primitives, size_t primitiveCount, void* userPtr); + +/* Callback to split a build primitive */ +typedef void (*RTCSplitPrimitiveFunction) (const struct RTCBuildPrimitive* primitive, unsigned int dimension, float position, struct RTCBounds* leftBounds, struct RTCBounds* rightBounds, void* userPtr); + +/* Build flags */ +enum RTCBuildFlags +{ + RTC_BUILD_FLAG_NONE = 0, + RTC_BUILD_FLAG_DYNAMIC = (1 << 0), +}; + +/* Input for builders */ +struct RTCBuildArguments +{ + size_t byteSize; + + enum RTCBuildQuality buildQuality; + enum RTCBuildFlags buildFlags; + unsigned int maxBranchingFactor; + unsigned int maxDepth; + unsigned int sahBlockSize; + unsigned int minLeafSize; + unsigned int maxLeafSize; + float traversalCost; + float intersectionCost; + + RTCBVH bvh; + struct RTCBuildPrimitive* primitives; + size_t primitiveCount; + size_t primitiveArrayCapacity; + + RTCCreateNodeFunction createNode; + RTCSetNodeChildrenFunction setNodeChildren; + RTCSetNodeBoundsFunction setNodeBounds; + RTCCreateLeafFunction createLeaf; + RTCSplitPrimitiveFunction splitPrimitive; + RTCProgressMonitorFunction buildProgress; + void* userPtr; +}; + +/* Returns the default build settings. */ +RTC_FORCEINLINE struct RTCBuildArguments rtcDefaultBuildArguments() +{ + struct RTCBuildArguments args; + args.byteSize = sizeof(args); + args.buildQuality = RTC_BUILD_QUALITY_MEDIUM; + args.buildFlags = RTC_BUILD_FLAG_NONE; + args.maxBranchingFactor = 2; + args.maxDepth = 32; + args.sahBlockSize = 1; + args.minLeafSize = 1; + args.maxLeafSize = 32; + args.traversalCost = 1.0f; + args.intersectionCost = 1.0f; + args.bvh = NULL; + args.primitives = NULL; + args.primitiveCount = 0; + args.primitiveArrayCapacity = 0; + args.createNode = NULL; + args.setNodeChildren = NULL; + args.setNodeBounds = NULL; + args.createLeaf = NULL; + args.splitPrimitive = NULL; + args.buildProgress = NULL; + args.userPtr = NULL; + return args; +} + +/* Creates a new BVH. */ +RTC_API RTCBVH rtcNewBVH(RTCDevice device); + +/* Builds a BVH. */ +RTC_API void* rtcBuildBVH(const struct RTCBuildArguments* args); + +/* Allocates memory using the thread local allocator. */ +RTC_API void* rtcThreadLocalAlloc(RTCThreadLocalAllocator allocator, size_t bytes, size_t align); + +/* Retains the BVH (increments reference count). */ +RTC_API void rtcRetainBVH(RTCBVH bvh); + +/* Releases the BVH (decrements reference count). */ +RTC_API void rtcReleaseBVH(RTCBVH bvh); + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.h new file mode 100644 index 00000000..eccde8a1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.h @@ -0,0 +1,224 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include "rtcore_version.h" + +RTC_NAMESPACE_BEGIN + +#if defined(_WIN32) +#if defined(_M_X64) +typedef long long ssize_t; +#else +typedef int ssize_t; +#endif +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) __declspec(align(__VA_ARGS__)) +#else +# define RTC_ALIGN(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#if !defined (RTC_DEPRECATED) +#ifdef __GNUC__ + #define RTC_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define RTC_DEPRECATED __declspec(deprecated) +#else + #define RTC_DEPRECATED +#endif +#endif + +#if defined(_WIN32) +# define RTC_FORCEINLINE __forceinline +#else +# define RTC_FORCEINLINE inline __attribute__((always_inline)) +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + struct RTCBounds bounds0; + struct RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + const struct RTCIntersectContext* context; + struct RTCRayN* ray; + struct RTCHitN* hit; + unsigned int N; +}; + +/* Filter callback function */ +typedef void (*RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* args); + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + enum RTCIntersectContextFlags flags; // intersection flags + RTCFilterFunctionN filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(struct RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.isph new file mode 100644 index 00000000..6d13ce9c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_common.isph @@ -0,0 +1,205 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_COMMON_ISPH__ +#define __RTC_COMMON_ISPH__ + +#if !defined(RTC_API) +#define RTC_API extern "C" unmasked +#endif + +#ifdef _WIN32 +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#else +# define RTC_ALIGN(...) // FIXME: need to specify alignment +#endif + +#if !defined(RTC_DEPRECATED) +#define RTC_DEPRECATED // FIXME: deprecation not supported by ISPC +#endif + +#if !defined(RTC_FORCEINLINE) +#define RTC_FORCEINLINE inline +#endif + +/* Invalid geometry ID */ +#define RTC_INVALID_GEOMETRY_ID ((uniform unsigned int)-1) + +/* Maximum number of time steps */ +#define RTC_MAX_TIME_STEP_COUNT 129 + +/* Maximum number of instancing levels */ +#define RTC_MAX_INSTANCE_LEVEL_COUNT 1 + +/* Formats of buffers and other data structures */ +enum RTCFormat +{ + RTC_FORMAT_UNDEFINED = 0, + + /* 8-bit unsigned integer */ + RTC_FORMAT_UCHAR = 0x1001, + RTC_FORMAT_UCHAR2, + RTC_FORMAT_UCHAR3, + RTC_FORMAT_UCHAR4, + + /* 8-bit signed integer */ + RTC_FORMAT_CHAR = 0x2001, + RTC_FORMAT_CHAR2, + RTC_FORMAT_CHAR3, + RTC_FORMAT_CHAR4, + + /* 16-bit unsigned integer */ + RTC_FORMAT_USHORT = 0x3001, + RTC_FORMAT_USHORT2, + RTC_FORMAT_USHORT3, + RTC_FORMAT_USHORT4, + + /* 16-bit signed integer */ + RTC_FORMAT_SHORT = 0x4001, + RTC_FORMAT_SHORT2, + RTC_FORMAT_SHORT3, + RTC_FORMAT_SHORT4, + + /* 32-bit unsigned integer */ + RTC_FORMAT_UINT = 0x5001, + RTC_FORMAT_UINT2, + RTC_FORMAT_UINT3, + RTC_FORMAT_UINT4, + + /* 32-bit signed integer */ + RTC_FORMAT_INT = 0x6001, + RTC_FORMAT_INT2, + RTC_FORMAT_INT3, + RTC_FORMAT_INT4, + + /* 64-bit unsigned integer */ + RTC_FORMAT_ULLONG = 0x7001, + RTC_FORMAT_ULLONG2, + RTC_FORMAT_ULLONG3, + RTC_FORMAT_ULLONG4, + + /* 64-bit signed integer */ + RTC_FORMAT_LLONG = 0x8001, + RTC_FORMAT_LLONG2, + RTC_FORMAT_LLONG3, + RTC_FORMAT_LLONG4, + + /* 32-bit float */ + RTC_FORMAT_FLOAT = 0x9001, + RTC_FORMAT_FLOAT2, + RTC_FORMAT_FLOAT3, + RTC_FORMAT_FLOAT4, + RTC_FORMAT_FLOAT5, + RTC_FORMAT_FLOAT6, + RTC_FORMAT_FLOAT7, + RTC_FORMAT_FLOAT8, + RTC_FORMAT_FLOAT9, + RTC_FORMAT_FLOAT10, + RTC_FORMAT_FLOAT11, + RTC_FORMAT_FLOAT12, + RTC_FORMAT_FLOAT13, + RTC_FORMAT_FLOAT14, + RTC_FORMAT_FLOAT15, + RTC_FORMAT_FLOAT16, + + /* 32-bit float matrix (row-major order) */ + RTC_FORMAT_FLOAT2X2_ROW_MAJOR = 0x9122, + RTC_FORMAT_FLOAT2X3_ROW_MAJOR = 0x9123, + RTC_FORMAT_FLOAT2X4_ROW_MAJOR = 0x9124, + RTC_FORMAT_FLOAT3X2_ROW_MAJOR = 0x9132, + RTC_FORMAT_FLOAT3X3_ROW_MAJOR = 0x9133, + RTC_FORMAT_FLOAT3X4_ROW_MAJOR = 0x9134, + RTC_FORMAT_FLOAT4X2_ROW_MAJOR = 0x9142, + RTC_FORMAT_FLOAT4X3_ROW_MAJOR = 0x9143, + RTC_FORMAT_FLOAT4X4_ROW_MAJOR = 0x9144, + + /* 32-bit float matrix (column-major order) */ + RTC_FORMAT_FLOAT2X2_COLUMN_MAJOR = 0x9222, + RTC_FORMAT_FLOAT2X3_COLUMN_MAJOR = 0x9223, + RTC_FORMAT_FLOAT2X4_COLUMN_MAJOR = 0x9224, + RTC_FORMAT_FLOAT3X2_COLUMN_MAJOR = 0x9232, + RTC_FORMAT_FLOAT3X3_COLUMN_MAJOR = 0x9233, + RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR = 0x9234, + RTC_FORMAT_FLOAT4X2_COLUMN_MAJOR = 0x9242, + RTC_FORMAT_FLOAT4X3_COLUMN_MAJOR = 0x9243, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR = 0x9244, + + /* special 12-byte format for grids */ + RTC_FORMAT_GRID = 0xA001 +}; + +/* Build quality levels */ +enum RTCBuildQuality +{ + RTC_BUILD_QUALITY_LOW = 0, + RTC_BUILD_QUALITY_MEDIUM = 1, + RTC_BUILD_QUALITY_HIGH = 2, + RTC_BUILD_QUALITY_REFIT = 3, +}; + +/* Axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCBounds +{ + float lower_x, lower_y, lower_z, align0; + float upper_x, upper_y, upper_z, align1; +}; + +/* Linear axis-aligned bounding box representation */ +struct RTC_ALIGN(16) RTCLinearBounds +{ + RTCBounds bounds0; + RTCBounds bounds1; +}; + +/* Intersection context flags */ +enum RTCIntersectContextFlags +{ + RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, + RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0), // optimize for incoherent rays + RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) // optimize for coherent rays +}; + +/* Intersection context passed to intersect/occluded calls */ +struct RTCIntersectContext +{ + RTCIntersectContextFlags flags; // intersection flags + void* filter; // filter function to execute + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // will be set to geomID of instance when instance is entered +}; + +/* Initializes an intersection context. */ +RTC_FORCEINLINE void rtcInitIntersectContext(uniform RTCIntersectContext* context) +{ + context->flags = RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT; + context->filter = NULL; + context->instID[0] = RTC_INVALID_GEOMETRY_ID; +} + +/* Arguments for RTCFilterFunctionN */ +struct RTCFilterFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + const RTCIntersectContext* uniform context; + struct RTCRayN* uniform ray; + struct RTCHitN* uniform hit; + uniform unsigned int N; +}; + +/* Filter callback function */ +typedef unmasked void (*uniform RTCFilterFunctionN)(const struct RTCFilterFunctionNArguments* uniform args); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.h new file mode 100644 index 00000000..ed6c8e98 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.h @@ -0,0 +1,97 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque device type */ +typedef struct RTCDeviceTy* RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const char* config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + RTC_DEVICE_PROPERTY_POINT_GEOMETRY_SUPPORTED = 101, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API ssize_t rtcGetDeviceProperty(RTCDevice device, enum RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const enum RTCDeviceProperty prop, ssize_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API enum RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef void (*RTCErrorFunction)(void* userPtr, enum RTCError code, const char* str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, RTCErrorFunction error, void* userPtr); + +/* Memory monitor callback function */ +typedef bool (*RTCMemoryMonitorFunction)(void* ptr, ssize_t bytes, bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* userPtr); + +RTC_NAMESPACE_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.isph new file mode 100644 index 00000000..9d2c326a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_device.isph @@ -0,0 +1,95 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_DEVICE_ISPH__ +#define __RTC_DEVICE_ISPH__ + +#include "rtcore_common.isph" + +/* Opaque device type */ +typedef uniform struct RTCDeviceTy* uniform RTCDevice; + +/* Creates a new Embree device. */ +RTC_API RTCDevice rtcNewDevice(const uniform int8* uniform config); + +/* Retains the Embree device (increments the reference count). */ +RTC_API void rtcRetainDevice(RTCDevice device); + +/* Releases an Embree device (decrements the reference count). */ +RTC_API void rtcReleaseDevice(RTCDevice device); + +/* Device properties */ +enum RTCDeviceProperty +{ + RTC_DEVICE_PROPERTY_VERSION = 0, + RTC_DEVICE_PROPERTY_VERSION_MAJOR = 1, + RTC_DEVICE_PROPERTY_VERSION_MINOR = 2, + RTC_DEVICE_PROPERTY_VERSION_PATCH = 3, + + RTC_DEVICE_PROPERTY_NATIVE_RAY4_SUPPORTED = 32, + RTC_DEVICE_PROPERTY_NATIVE_RAY8_SUPPORTED = 33, + RTC_DEVICE_PROPERTY_NATIVE_RAY16_SUPPORTED = 34, + RTC_DEVICE_PROPERTY_RAY_STREAM_SUPPORTED = 35, + + RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED = 64, + RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED = 65, + RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66, + RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67, + + RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96, + RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97, + RTC_DEVICE_PROPERTY_SUBDIVISION_GEOMETRY_SUPPORTED = 98, + RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED = 99, + RTC_DEVICE_PROPERTY_USER_GEOMETRY_SUPPORTED = 100, + + RTC_DEVICE_PROPERTY_TASKING_SYSTEM = 128, + RTC_DEVICE_PROPERTY_JOIN_COMMIT_SUPPORTED = 129 +}; + +/* Gets a device property. */ +RTC_API uniform intptr_t rtcGetDeviceProperty(RTCDevice device, uniform RTCDeviceProperty prop); + +/* Sets a device property. */ +RTC_API void rtcSetDeviceProperty(RTCDevice device, const uniform RTCDeviceProperty prop, uniform intptr_t value); + +/* Error codes */ +enum RTCError +{ + RTC_ERROR_NONE = 0, + RTC_ERROR_UNKNOWN = 1, + RTC_ERROR_INVALID_ARGUMENT = 2, + RTC_ERROR_INVALID_OPERATION = 3, + RTC_ERROR_OUT_OF_MEMORY = 4, + RTC_ERROR_UNSUPPORTED_CPU = 5, + RTC_ERROR_CANCELLED = 6 +}; + +/* Returns the error code. */ +RTC_API uniform RTCError rtcGetDeviceError(RTCDevice device); + +/* Error callback function */ +typedef unmasked void (*uniform RTCErrorFunction)(void* uniform userPtr, uniform RTCError code, const uniform int8* uniform str); + +/* Sets the error callback function. */ +RTC_API void rtcSetDeviceErrorFunction(RTCDevice device, uniform RTCErrorFunction error, void* uniform userPtr); + +/* Memory monitor callback function */ +typedef uniform bool (*uniform RTCMemoryMonitorFunction)(uniform intptr_t bytes, uniform bool post); + +/* Sets the memory monitor callback function. */ +RTC_API void rtcSetDeviceMemoryMonitorFunction(RTCDevice device, RTCMemoryMonitorFunction memoryMonitor, void* uniform userPtr); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.h new file mode 100644 index 00000000..ce3e832a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.h @@ -0,0 +1,379 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_buffer.h" + +RTC_NAMESPACE_BEGIN + +/* Opaque scene type */ +typedef struct RTCSceneTy* RTCScene; + +/* Opaque geometry type */ +typedef struct RTCGeometryTy* RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* geometryUserPtr; + unsigned int primID; + unsigned int timeStep; + struct RTCBounds* bounds_o; +}; + +/* Bounding callback function */ +typedef void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayHitN* rayhit; + unsigned int N; +}; + +/* Intersection callback function */ +typedef void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + int* valid; + void* geometryUserPtr; + unsigned int primID; + struct RTCIntersectContext* context; + struct RTCRayN* ray; + unsigned int N; +}; + +/* Occlusion callback function */ +typedef void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* geometryUserPtr; + RTCGeometry geometry; + unsigned int primID; + unsigned int timeStep; + const float* u; + const float* v; + const float* Ng_x; + const float* Ng_y; + const float* Ng_z; + float* P_x; + float* P_y; + float* P_z; + unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, enum RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count) */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, float startTime, float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, enum RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, RTCBuffer buffer, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, const void* ptr, size_t byteOffset, size_t byteStride, size_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* rtcSetNewGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot, enum RTCFormat format, size_t byteStride, size_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* rtcGetGeometryBufferData(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, enum RTCBufferType type, unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, RTCBoundsFunction bounds, void* userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const struct RTCIntersectFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const struct RTCOccludedFunctionNArguments* args, const struct RTCFilterFunctionNArguments* filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, unsigned int timeStep, enum RTCFormat format, const void* xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, float time, enum RTCFormat format, void* xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, unsigned int topologyID, enum RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, unsigned int vertexAttributeID, unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API unsigned int rtcGetGeometryFace(RTCGeometry geometry, unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, unsigned int topologyID, unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const struct RTCInterpolateArguments* args); + +/* Interpolates vertex data to some u/v location. */ +RTC_FORCEINLINE void rtcInterpolate0(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, float* P, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate1(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Interpolates vertex data to some u/v location and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolate2(RTCGeometry geometry, unsigned int primID, float u, float v, enum RTCBufferType bufferType, unsigned int bufferSlot, + float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, unsigned int valueCount) +{ + struct RTCInterpolateArguments args; + args.geometry = geometry; + args.primID = primID; + args.u = u; + args.v = v; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = P; + args.dPdu = dPdu; + args.dPdv = dPdv; + args.ddPdudu = ddPdudu; + args.ddPdvdv = ddPdvdv; + args.ddPdudv = ddPdudv; + args.valueCount = valueCount; + rtcInterpolate(&args); +} + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + enum RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_API void rtcInterpolateN(const struct RTCInterpolateNArguments* args); + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + unsigned short width,height; // max is a 32k x 32k grid +}; + +RTC_NAMESPACE_END + + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.isph new file mode 100644 index 00000000..f536bc7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_geometry.isph @@ -0,0 +1,402 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __EMBREE_GEOMETRY_ISPH__ +#define __EMBREE_GEOMETRY_ISPH__ + +#include "rtcore_buffer.isph" + +/* Opaque scene type */ +typedef uniform struct RTCSceneTy* uniform RTCScene; + +/* Opaque geometry type */ +typedef uniform struct RTCGeometryTy* uniform RTCGeometry; + +/* Types of geometries */ +enum RTCGeometryType +{ + RTC_GEOMETRY_TYPE_TRIANGLE = 0, // triangle mesh + RTC_GEOMETRY_TYPE_QUAD = 1, // quad (triangle pair) mesh + RTC_GEOMETRY_TYPE_GRID = 2, // grid mesh + + RTC_GEOMETRY_TYPE_SUBDIVISION = 8, // Catmull-Clark subdivision surface + + RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE = 17, // flat (ribbon-like) linear curves + + RTC_GEOMETRY_TYPE_ROUND_BEZIER_CURVE = 24, // round (tube-like) Bezier curves + RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE = 25, // flat (ribbon-like) Bezier curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BEZIER_CURVE = 26, // flat normal-oriented Bezier curves + + RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE = 32, // round (tube-like) B-spline curves + RTC_GEOMETRY_TYPE_FLAT_BSPLINE_CURVE = 33, // flat (ribbon-like) B-spline curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_BSPLINE_CURVE = 34, // flat normal-oriented B-spline curves + + RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE = 40, // round (tube-like) Hermite curves + RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE = 41, // flat (ribbon-like) Hermite curves + RTC_GEOMETRY_TYPE_NORMAL_ORIENTED_HERMITE_CURVE = 42, // flat normal-oriented Hermite curves + + RTC_GEOMETRY_TYPE_SPHERE_POINT = 50, + RTC_GEOMETRY_TYPE_DISC_POINT = 51, + RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT = 52, + + RTC_GEOMETRY_TYPE_USER = 120, // user-defined geometry + RTC_GEOMETRY_TYPE_INSTANCE = 121 // scene instance +}; + +/* Interpolation modes for subdivision surfaces */ +enum RTCSubdivisionMode +{ + RTC_SUBDIVISION_MODE_NO_BOUNDARY = 0, + RTC_SUBDIVISION_MODE_SMOOTH_BOUNDARY = 1, + RTC_SUBDIVISION_MODE_PIN_CORNERS = 2, + RTC_SUBDIVISION_MODE_PIN_BOUNDARY = 3, + RTC_SUBDIVISION_MODE_PIN_ALL = 4, +}; + +/* Curve segment flags */ +enum RTCCurveFlags +{ + RTC_CURVE_FLAG_NEIGHBOR_LEFT = (1 << 0), + RTC_CURVE_FLAG_NEIGHBOR_RIGHT = (1 << 1) +}; + +/* Arguments for RTCBoundsFunction */ +struct RTCBoundsFunctionArguments +{ + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform RTCBounds* uniform bounds_o; +}; + +/* Bounding callback function */ +typedef unmasked void (*RTCBoundsFunction)(const struct RTCBoundsFunctionArguments* uniform args); + +/* Arguments for RTCIntersectFunctionN */ +struct RTCIntersectFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayHitN* uniform rayhit; + uniform unsigned int N; +}; + +/* Intersection callback function */ +typedef unmasked void (*RTCIntersectFunctionN)(const struct RTCIntersectFunctionNArguments* uniform args); + +/* Arguments for RTCOccludedFunctionN */ +struct RTCOccludedFunctionNArguments +{ + uniform int* uniform valid; + void* uniform geometryUserPtr; + uniform unsigned int primID; + uniform RTCIntersectContext* uniform context; + RTCRayN* uniform ray; + uniform unsigned int N; +}; + +/* Occlusion callback function */ +typedef unmasked void (*RTCOccludedFunctionN)(const struct RTCOccludedFunctionNArguments* uniform args); + +/* Arguments for RTCDisplacementFunctionN */ +struct RTCDisplacementFunctionNArguments +{ + void* uniform geometryUserPtr; + RTCGeometry geometry; + uniform unsigned int primID; + uniform unsigned int timeStep; + uniform const float* uniform u; + uniform const float* uniform v; + uniform const float* uniform Ng_x; + uniform const float* uniform Ng_y; + uniform const float* uniform Ng_z; + uniform float* uniform P_x; + uniform float* uniform P_y; + uniform float* uniform P_z; + uniform unsigned int N; +}; + +/* Displacement mapping callback function */ +typedef unmasked void (*RTCDisplacementFunctionN)(const struct RTCDisplacementFunctionNArguments* uniform args); + +/* Creates a new geometry of specified type. */ +RTC_API RTCGeometry rtcNewGeometry(RTCDevice device, uniform RTCGeometryType type); + +/* Retains the geometry (increments the reference count). */ +RTC_API void rtcRetainGeometry(RTCGeometry geometry); + +/* Releases the geometry (decrements the reference count). */ +RTC_API void rtcReleaseGeometry(RTCGeometry geometry); + +/* Commits the geometry. */ +RTC_API void rtcCommitGeometry(RTCGeometry geometry); + + +/* Enables the geometry. */ +RTC_API void rtcEnableGeometry(RTCGeometry geometry); + +/* Disables the geometry. */ +RTC_API void rtcDisableGeometry(RTCGeometry geometry); + + +/* Sets the number of motion blur time steps of the geometry. */ +RTC_API void rtcSetGeometryTimeStepCount(RTCGeometry geometry, uniform unsigned int timeStepCount); + +/* Sets the motion blur time range of the geometry. */ +RTC_API void rtcSetGeometryTimeRange(RTCGeometry geometry, uniform float startTime, uniform float endTime); + +/* Sets the number of vertex attributes of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeCount(RTCGeometry geometry, uniform unsigned int vertexAttributeCount); + +/* Sets the ray mask of the geometry. */ +RTC_API void rtcSetGeometryMask(RTCGeometry geometry, uniform unsigned int mask); + +/* Sets the build quality of the geometry. */ +RTC_API void rtcSetGeometryBuildQuality(RTCGeometry geometry, uniform RTCBuildQuality quality); + + +/* Sets a geometry buffer. */ +RTC_API void rtcSetGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform RTCBuffer buffer, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Sets a shared geometry buffer. */ +RTC_API void rtcSetSharedGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, const void* uniform ptr, uniform uintptr_t byteOffset, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Creates and sets a new geometry buffer. */ +RTC_API void* uniform rtcSetNewGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot, uniform RTCFormat format, uniform uintptr_t byteStride, uniform uintptr_t itemCount); + +/* Returns the pointer to the data of a buffer. */ +RTC_API void* uniform rtcGetGeometryBufferData(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + +/* Updates a geometry buffer. */ +RTC_API void rtcUpdateGeometryBuffer(RTCGeometry geometry, uniform RTCBufferType type, uniform unsigned int slot); + + +/* Sets the intersection filter callback function of the geometry. */ +RTC_API void rtcSetGeometryIntersectFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the occlusion filter callback function of the geometry. */ +RTC_API void rtcSetGeometryOccludedFilterFunction(RTCGeometry geometry, uniform RTCFilterFunctionN filter); + +/* Sets the user-defined data pointer of the geometry. */ +RTC_API void rtcSetGeometryUserData(RTCGeometry geometry, void* uniform ptr); + +/* Gets the user-defined data pointer of the geometry. */ +RTC_API void* uniform rtcGetGeometryUserData(RTCGeometry geometry); + + +/* Sets the number of primitives of a user geometry. */ +RTC_API void rtcSetGeometryUserPrimitiveCount(RTCGeometry geometry, uniform unsigned int userPrimitiveCount); + +/* Sets the bounding callback function to calculate bounding boxes for user primitives. */ +RTC_API void rtcSetGeometryBoundsFunction(RTCGeometry geometry, uniform RTCBoundsFunction bounds, void* uniform userPtr); + +/* Set the intersect callback function of a user geometry. */ +RTC_API void rtcSetGeometryIntersectFunction(RTCGeometry geometry, uniform RTCIntersectFunctionN intersect); + +/* Set the occlusion callback function of a user geometry. */ +RTC_API void rtcSetGeometryOccludedFunction(RTCGeometry geometry, uniform RTCOccludedFunctionN occluded); + +/* Invokes the intersection filter from the intersection callback function. */ +RTC_API void rtcFilterIntersection(const uniform struct RTCIntersectFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + +/* Invokes the occlusion filter from the occlusion callback function. */ +RTC_API void rtcFilterOcclusion(const uniform struct RTCOccludedFunctionNArguments* uniform args, const uniform RTCFilterFunctionNArguments* uniform filterArgs); + + +/* Sets the instanced scene of an instance geometry. */ +RTC_API void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene); + +/* Sets the transformation of an instance for the specified time step. */ +RTC_API void rtcSetGeometryTransform(RTCGeometry geometry, uniform unsigned int timeStep, uniform RTCFormat format, const void* uniform xfm); + +/* Returns the interpolated transformation of an instance for the specified time. */ +RTC_API void rtcGetGeometryTransform(RTCGeometry geometry, uniform float time, uniform RTCFormat format, void* uniform xfm); + + +/* Sets the uniform tessellation rate of the geometry. */ +RTC_API void rtcSetGeometryTessellationRate(RTCGeometry geometry, uniform float tessellationRate); + +/* Sets the number of topologies of a subdivision surface. */ +RTC_API void rtcSetGeometryTopologyCount(RTCGeometry geometry, uniform unsigned int topologyCount); + +/* Sets the subdivision interpolation mode. */ +RTC_API void rtcSetGeometrySubdivisionMode(RTCGeometry geometry, uniform unsigned int topologyID, uniform RTCSubdivisionMode mode); + +/* Binds a vertex attribute to a topology of the geometry. */ +RTC_API void rtcSetGeometryVertexAttributeTopology(RTCGeometry geometry, uniform unsigned int vertexAttributeID, uniform unsigned int topologyID); + +/* Sets the displacement callback function of a subdivision surface. */ +RTC_API void rtcSetGeometryDisplacementFunction(RTCGeometry geometry, uniform RTCDisplacementFunctionN displacement); + +/* Returns the first half edge of a face. */ +RTC_API uniform unsigned int rtcGetGeometryFirstHalfEdge(RTCGeometry geometry, uniform unsigned int faceID); + +/* Returns the face the half edge belongs to. */ +RTC_API uniform unsigned int rtcGetGeometryFace(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns next half edge. */ +RTC_API uniform unsigned int rtcGetGeometryNextHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns previous half edge. */ +RTC_API uniform unsigned int rtcGetGeometryPreviousHalfEdge(RTCGeometry geometry, uniform unsigned int edgeID); + +/* Returns opposite half edge. */ +RTC_API uniform unsigned int rtcGetGeometryOppositeHalfEdge(RTCGeometry geometry, uniform unsigned int topologyID, uniform unsigned int edgeID); + + +/* Arguments for rtcInterpolate */ +struct RTCInterpolateArguments +{ + RTCGeometry geometry; + unsigned int primID; + float u; + float v; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to some u/v location and optionally calculates all derivatives. */ +RTC_API void rtcInterpolate(const RTCInterpolateArguments* uniform args); + +/* Arguments for rtcInterpolateN */ +struct RTCInterpolateNArguments +{ + RTCGeometry geometry; + const void* valid; + const unsigned int* primIDs; + const float* u; + const float* v; + unsigned int N; + RTCBufferType bufferType; + unsigned int bufferSlot; + float* P; + float* dPdu; + float* dPdv; + float* ddPdudu; + float* ddPdvdv; + float* ddPdudv; + unsigned int valueCount; +}; + +/* Interpolates vertex data to an array of u/v locations and calculates all derivatives. */ +RTC_API void rtcInterpolateN(const RTCInterpolateNArguments* uniform args); + +/* Interpolates vertex data to an array of u/v locations. */ +RTC_FORCEINLINE void rtcInterpolateV0(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = NULL; + args.dPdv = NULL; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV1(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = NULL; + args.ddPdvdv = NULL; + args.ddPdudv = NULL; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* Interpolates vertex data to an array of u/v locations and calculates first and second order derivatives. */ +RTC_FORCEINLINE void rtcInterpolateV2(RTCGeometry geometry, varying unsigned int primID, varying float u, varying float v, + uniform RTCBufferType bufferType, uniform unsigned int bufferSlot, + varying float* uniform P, varying float* uniform dPdu, varying float* uniform dPdv, + varying float* uniform ddPdudu, varying float* uniform ddPdvdv, varying float* uniform ddPdudv, + uniform unsigned int valueCount) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + uniform RTCInterpolateNArguments args; + args.geometry = geometry; + args.valid = (const void* uniform)&imask; + args.primIDs = (const uniform unsigned int* uniform)&primID; + args.u = (const uniform float* uniform)&u; + args.v = (const uniform float* uniform)&v; + args.N = sizeof(varying float)/4; + args.bufferType = bufferType; + args.bufferSlot = bufferSlot; + args.P = (uniform float* uniform)P; + args.dPdu = (uniform float* uniform)dPdu; + args.dPdv = (uniform float* uniform)dPdv; + args.ddPdudu = (uniform float* uniform)ddPdudu; + args.ddPdvdv = (uniform float* uniform)ddPdvdv; + args.ddPdudv = (uniform float* uniform)ddPdudv; + args.valueCount = valueCount; + rtcInterpolateN(&args); +} + +/* RTCGrid primitive for grid mesh */ +struct RTCGrid +{ + unsigned int startVertexID; + unsigned int stride; + int16 width,height; // max is a 32k x 32k grid +}; + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.h new file mode 100644 index 00000000..d95f6d29 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.h @@ -0,0 +1,391 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_common.h" + +RTC_NAMESPACE_BEGIN + +/* Ray structure for a single ray */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure for a single ray */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure for a single ray */ +struct RTCRayHit +{ + struct RTCRay ray; + struct RTCHit hit; +}; + +/* Ray structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCRay4 +{ + float org_x[4]; + float org_y[4]; + float org_z[4]; + float tnear[4]; + + float dir_x[4]; + float dir_y[4]; + float dir_z[4]; + float time[4]; + + float tfar[4]; + unsigned int mask[4]; + unsigned int id[4]; + unsigned int flags[4]; +}; + +/* Hit structure for a packet of 4 rays */ +struct RTC_ALIGN(16) RTCHit4 +{ + float Ng_x[4]; + float Ng_y[4]; + float Ng_z[4]; + + float u[4]; + float v[4]; + + unsigned int primID[4]; + unsigned int geomID[4]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][4]; +}; + +/* Combined ray/hit structure for a packet of 4 rays */ +struct RTCRayHit4 +{ + struct RTCRay4 ray; + struct RTCHit4 hit; +}; + +/* Ray structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCRay8 +{ + float org_x[8]; + float org_y[8]; + float org_z[8]; + float tnear[8]; + + float dir_x[8]; + float dir_y[8]; + float dir_z[8]; + float time[8]; + + float tfar[8]; + unsigned int mask[8]; + unsigned int id[8]; + unsigned int flags[8]; +}; + +/* Hit structure for a packet of 8 rays */ +struct RTC_ALIGN(32) RTCHit8 +{ + float Ng_x[8]; + float Ng_y[8]; + float Ng_z[8]; + + float u[8]; + float v[8]; + + unsigned int primID[8]; + unsigned int geomID[8]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][8]; +}; + +/* Combined ray/hit structure for a packet of 8 rays */ +struct RTCRayHit8 +{ + struct RTCRay8 ray; + struct RTCHit8 hit; +}; + +/* Ray structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCRay16 +{ + float org_x[16]; + float org_y[16]; + float org_z[16]; + float tnear[16]; + + float dir_x[16]; + float dir_y[16]; + float dir_z[16]; + float time[16]; + + float tfar[16]; + unsigned int mask[16]; + unsigned int id[16]; + unsigned int flags[16]; +}; + +/* Hit structure for a packet of 16 rays */ +struct RTC_ALIGN(64) RTCHit16 +{ + float Ng_x[16]; + float Ng_y[16]; + float Ng_z[16]; + + float u[16]; + float v[16]; + + unsigned int primID[16]; + unsigned int geomID[16]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; +}; + +/* Combined ray/hit structure for a packet of 16 rays */ +struct RTCRayHit16 +{ + struct RTCRay16 ray; + struct RTCHit16 hit; +}; + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + float* org_x; + float* org_y; + float* org_z; + float* tnear; + + float* dir_x; + float* dir_y; + float* dir_z; + float* time; + + float* tfar; + unsigned int* mask; + unsigned int* id; + unsigned int* flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + float* Ng_x; + float* Ng_y; + float* Ng_z; + + float* u; + float* v; + + unsigned int* primID; + unsigned int* geomID; + unsigned int* instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + struct RTCRayNp ray; + struct RTCHitNp hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +#if defined(__cplusplus) + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE float& RTCRayN_org_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[0*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[1*N+i]; } +RTC_FORCEINLINE float& RTCRayN_org_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[2*N+i]; } +RTC_FORCEINLINE float& RTCRayN_tnear(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[3*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_dir_x(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[4*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_y(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[5*N+i]; } +RTC_FORCEINLINE float& RTCRayN_dir_z(RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[6*N+i]; } +RTC_FORCEINLINE float& RTCRayN_time (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[7*N+i]; } + +RTC_FORCEINLINE float& RTCRayN_tfar (RTCRayN* ray, unsigned int N, unsigned int i) { return ((float*)ray)[8*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_mask (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[9*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_id (RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[10*N+i]; } +RTC_FORCEINLINE unsigned int& RTCRayN_flags(RTCRayN* ray, unsigned int N, unsigned int i) { return ((unsigned*)ray)[11*N+i]; } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE float& RTCHitN_Ng_x(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[0*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_y(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[1*N+i]; } +RTC_FORCEINLINE float& RTCHitN_Ng_z(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[2*N+i]; } + +RTC_FORCEINLINE float& RTCHitN_u(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[3*N+i]; } +RTC_FORCEINLINE float& RTCHitN_v(RTCHitN* hit, unsigned int N, unsigned int i) { return ((float*)hit)[4*N+i]; } + +RTC_FORCEINLINE unsigned int& RTCHitN_primID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[5*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_geomID(RTCHitN* hit, unsigned int N, unsigned int i) { return ((unsigned*)hit)[6*N+i]; } +RTC_FORCEINLINE unsigned int& RTCHitN_instID(RTCHitN* hit, unsigned int N, unsigned int i, unsigned int l) { return ((unsigned*)hit)[7*N+i+N*l]; } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* RTCRayHitN_RayN(RTCRayHitN* rayhit, unsigned int N) { return (RTCRayN*)&((float*)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* RTCRayHitN_HitN(RTCRayHitN* rayhit, unsigned int N) { return (RTCHitN*)&((float*)rayhit)[12*N]; } + +/* Helper structure for a ray packet of compile-time size N */ +template +struct RTCRayNt +{ + float org_x[N]; + float org_y[N]; + float org_z[N]; + float tnear[N]; + + float dir_x[N]; + float dir_y[N]; + float dir_z[N]; + float time[N]; + + float tfar[N]; + unsigned int mask[N]; + unsigned int id[N]; + unsigned int flags[N]; +}; + +/* Helper structure for a hit packet of compile-time size N */ +template +struct RTCHitNt +{ + float Ng_x[N]; + float Ng_y[N]; + float Ng_z[N]; + + float u[N]; + float v[N]; + + unsigned int primID[N]; + unsigned int geomID[N]; + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT][N]; +}; + +/* Helper structure for a combined ray/hit packet of compile-time size N */ +template +struct RTCRayHitNt +{ + RTCRayNt ray; + RTCHitNt hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* rayN, unsigned int N, unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* hitN, unsigned int N, unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* hitN, const RTCHit* hit, unsigned int N, unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* rayhitN, unsigned int N, unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.isph new file mode 100644 index 00000000..e01f0049 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_ray.isph @@ -0,0 +1,216 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_RAY_ISPH__ +#define __RTC_RAY_ISPH__ + +#include "rtcore_common.isph" + +/* Ray structure */ +struct RTC_ALIGN(16) RTCRay +{ + float org_x; // x coordinate of ray origin + float org_y; // y coordinate of ray origin + float org_z; // z coordinate of ray origin + float tnear; // start of ray segment + + float dir_x; // x coordinate of ray direction + float dir_y; // y coordinate of ray direction + float dir_z; // z coordinate of ray direction + float time; // time of this ray for motion blur + + float tfar; // end of ray segment (set to hit distance) + unsigned int mask; // ray mask + unsigned int id; // ray ID + unsigned int flags; // ray flags +}; + +/* Hit structure */ +struct RTCHit +{ + float Ng_x; // x coordinate of geometry normal + float Ng_y; // y coordinate of geometry normal + float Ng_z; // z coordinate of geometry normal + + float u; // barycentric u coordinate of hit + float v; // barycentric v coordinate of hit + + unsigned int primID; // primitive ID + unsigned int geomID; // geometry ID + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; // instance ID +}; + +/* Combined ray/hit structure */ +struct RTCRayHit +{ + RTCRay ray; + RTCHit hit; +}; + +struct RTCRayN; +struct RTCHitN; +struct RTCRayHitN; + +/* Helper functions to access ray packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCRayN_org_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_org_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_tnear(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[3*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_dir_x(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[4*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_y(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[5*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_dir_z(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[6*N+i]); } +RTC_FORCEINLINE varying float& RTCRayN_time (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[7*N+i]); } + +RTC_FORCEINLINE varying float& RTCRayN_tfar (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((uniform float*)ray)[8*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_mask (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[9*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_id (RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[10*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCRayN_flags(RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((uniform unsigned int*)ray)[11*N+i]); } + +/* Helper functions to access hit packets of runtime size N */ +RTC_FORCEINLINE varying float& RTCHitN_Ng_x(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[0*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_y(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[1*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_Ng_z(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[2*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_u (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[3*N+i]); } +RTC_FORCEINLINE varying float& RTCHitN_v (const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying float* uniform) &((float* uniform)hit)[4*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_primID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[5*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_geomID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i) { return *((varying unsigned int* uniform) &((unsigned int* uniform )hit)[6*N+i]); } +RTC_FORCEINLINE varying unsigned int& RTCHitN_instID(const RTCHitN* uniform hit, uniform unsigned int N, uniform unsigned int i, uniform unsigned int l) { return *((varying unsigned int* uniform) &((unsigned int* uniform)hit)[7*N+i+l*N]); } + +/* Helper functions to extract RTCRayN and RTCHitN from RTCRayHitN */ +RTC_FORCEINLINE RTCRayN* uniform RTCRayHitN_RayN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCRayN* uniform)&((uniform float* uniform)rayhit)[0*N]; } +RTC_FORCEINLINE RTCHitN* uniform RTCRayHitN_HitN(RTCRayHitN* uniform rayhit, uniform unsigned int N) { return (RTCHitN* uniform)&((uniform float* uniform)rayhit)[12*N]; } + +/* Ray structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayNp +{ + uniform float* uniform org_x; + uniform float* uniform org_y; + uniform float* uniform org_z; + uniform float* uniform tnear; + + uniform float* uniform dir_x; + uniform float* uniform dir_y; + uniform float* uniform dir_z; + uniform float* uniform time; + + uniform float* uniform tfar; + uniform unsigned int* uniform mask; + uniform unsigned int* uniform id; + uniform unsigned int* uniform flags; +}; + +/* Hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCHitNp +{ + uniform float* uniform Ng_x; + uniform float* uniform Ng_y; + uniform float* uniform Ng_z; + + uniform float* uniform u; + uniform float* uniform v; + + uniform unsigned int* uniform primID; + uniform unsigned int* uniform geomID; + uniform unsigned int* uniform instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; +}; + +/* Combined ray/hit structure for a packet/stream of N rays in pointer SOA layout */ +struct RTCRayHitNp +{ + RTCRayNp ray; + RTCHitNp hit; +}; + +RTC_FORCEINLINE RTCRay rtcGetRayFromRayN(RTCRayN* uniform rayN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRay ray; + ray.org_x = RTCRayN_org_x(rayN,N,i); + ray.org_y = RTCRayN_org_y(rayN,N,i); + ray.org_z = RTCRayN_org_z(rayN,N,i); + ray.tnear = RTCRayN_tnear(rayN,N,i); + ray.dir_x = RTCRayN_dir_x(rayN,N,i); + ray.dir_y = RTCRayN_dir_y(rayN,N,i); + ray.dir_z = RTCRayN_dir_z(rayN,N,i); + ray.time = RTCRayN_time(rayN,N,i); + ray.tfar = RTCRayN_tfar(rayN,N,i); + ray.mask = RTCRayN_mask(rayN,N,i); + ray.id = RTCRayN_id(rayN,N,i); + ray.flags = RTCRayN_flags(rayN,N,i); + return ray; +} + +RTC_FORCEINLINE RTCHit rtcGetHitFromHitN(RTCHitN* uniform hitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCHit hit; + hit.Ng_x = RTCHitN_Ng_x(hitN,N,i); + hit.Ng_y = RTCHitN_Ng_y(hitN,N,i); + hit.Ng_z = RTCHitN_Ng_z(hitN,N,i); + hit.u = RTCHitN_u(hitN,N,i); + hit.v = RTCHitN_v(hitN,N,i); + hit.primID = RTCHitN_primID(hitN,N,i); + hit.geomID = RTCHitN_geomID(hitN,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + hit.instID[l] = RTCHitN_instID(hitN,N,i,l); + return hit; +} + +RTC_FORCEINLINE void rtcCopyHitToHitN(RTCHitN* uniform hitN, const varying RTCHit* uniform hit, uniform unsigned int N, uniform unsigned int i) +{ + RTCHitN_Ng_x(hitN,N,i) = hit->Ng_x; + RTCHitN_Ng_y(hitN,N,i) = hit->Ng_y; + RTCHitN_Ng_z(hitN,N,i) = hit->Ng_z; + RTCHitN_u(hitN,N,i) = hit->u; + RTCHitN_v(hitN,N,i) = hit->v; + RTCHitN_primID(hitN,N,i) = hit->primID; + RTCHitN_geomID(hitN,N,i) = hit->geomID; + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + RTCHitN_instID(hitN,N,i,l) = hit->instID[l]; +} + +RTC_FORCEINLINE RTCRayHit rtcGetRayHitFromRayHitN(RTCRayHitN* uniform rayhitN, uniform unsigned int N, uniform unsigned int i) +{ + RTCRayHit rh; + + RTCRayN* uniform ray = RTCRayHitN_RayN(rayhitN,N); + rh.ray.org_x = RTCRayN_org_x(ray,N,i); + rh.ray.org_y = RTCRayN_org_y(ray,N,i); + rh.ray.org_z = RTCRayN_org_z(ray,N,i); + rh.ray.tnear = RTCRayN_tnear(ray,N,i); + rh.ray.dir_x = RTCRayN_dir_x(ray,N,i); + rh.ray.dir_y = RTCRayN_dir_y(ray,N,i); + rh.ray.dir_z = RTCRayN_dir_z(ray,N,i); + rh.ray.time = RTCRayN_time(ray,N,i); + rh.ray.tfar = RTCRayN_tfar(ray,N,i); + rh.ray.mask = RTCRayN_mask(ray,N,i); + rh.ray.id = RTCRayN_id(ray,N,i); + rh.ray.flags = RTCRayN_flags(ray,N,i); + + RTCHitN* uniform hit = RTCRayHitN_HitN(rayhitN,N); + rh.hit.Ng_x = RTCHitN_Ng_x(hit,N,i); + rh.hit.Ng_y = RTCHitN_Ng_y(hit,N,i); + rh.hit.Ng_z = RTCHitN_Ng_z(hit,N,i); + rh.hit.u = RTCHitN_u(hit,N,i); + rh.hit.v = RTCHitN_v(hit,N,i); + rh.hit.primID = RTCHitN_primID(hit,N,i); + rh.hit.geomID = RTCHitN_geomID(hit,N,i); + for (uniform unsigned int l = 0; l < RTC_MAX_INSTANCE_LEVEL_COUNT; l++) + rh.hit.instID[l] = RTCHitN_instID(hit,N,i,l); + + return rh; +} + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.h new file mode 100644 index 00000000..2bda59cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.h @@ -0,0 +1,149 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "rtcore_device.h" + +RTC_NAMESPACE_BEGIN + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHit4; +struct RTCRayHit8; +struct RTCRayHit16; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef bool (*RTCProgressMonitorFunction)(void* ptr, double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, enum RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, enum RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API enum RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, struct RTCBounds* bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, struct RTCLinearBounds* bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit4* rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit8* rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit16* rayhit); + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit* rayhit, unsigned int M, size_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHit** rayhit, unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayHitN* rayhit, unsigned int N, unsigned int M, size_t byteStride); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayHitNp* rayhit, unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay4* ray); + +/* Tests a packet of 8 rays for occlusion with the scene. */ +RTC_API void rtcOccluded8(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay8* ray); + +/* Tests a packet of 16 rays for occlusion with the scene. */ +RTC_API void rtcOccluded16(const int* valid, RTCScene scene, struct RTCIntersectContext* context, struct RTCRay16* ray); + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay* ray, unsigned int M, size_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, struct RTCIntersectContext* context, struct RTCRay** ray, unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, struct RTCIntersectContext* context, struct RTCRayN* ray, unsigned int N, unsigned int M, size_t byteStride); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, struct RTCIntersectContext* context, const struct RTCRayNp* ray, unsigned int N); + +#if defined(__cplusplus) + +/* Helper for easily combining scene flags */ +inline RTCSceneFlags operator|(RTCSceneFlags a, RTCSceneFlags b) { + return (RTCSceneFlags)((size_t)a | (size_t)b); +} + +#endif + +RTC_NAMESPACE_END + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.isph b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.isph new file mode 100644 index 00000000..5579d7e3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_scene.isph @@ -0,0 +1,176 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#ifndef __RTC_SCENE_ISPH__ +#define __RTC_SCENE_ISPH__ + +#include "rtcore_device.isph" + +/* Forward declarations for ray structures */ +struct RTCRayHit; +struct RTCRayHitNp; + +/* Scene flags */ +enum RTCSceneFlags +{ + RTC_SCENE_FLAG_NONE = 0, + RTC_SCENE_FLAG_DYNAMIC = (1 << 0), + RTC_SCENE_FLAG_COMPACT = (1 << 1), + RTC_SCENE_FLAG_ROBUST = (1 << 2), + RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) +}; + +/* Creates a new scene. */ +RTC_API RTCScene rtcNewScene(RTCDevice device); + +/* Retains the scene (increments the reference count). */ +RTC_API void rtcRetainScene(RTCScene scene); + +/* Releases the scene (decrements the reference count). */ +RTC_API void rtcReleaseScene(RTCScene scene); + + +/* Attaches the geometry to a scene. */ +RTC_API uniform unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry); + +/* Attaches the geometry to a scene using the specified geometry ID. */ +RTC_API void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, uniform unsigned int geomID); + +/* Detaches the geometry from the scene. */ +RTC_API void rtcDetachGeometry(RTCScene scene, uniform unsigned int geomID); + +/* Gets a geometry handle from the scene. */ +RTC_API RTCGeometry rtcGetGeometry(RTCScene scene, uniform unsigned int geomID); + + +/* Commits the scene. */ +RTC_API void rtcCommitScene(RTCScene scene); + +/* Commits the scene from multiple threads. */ +RTC_API void rtcJoinCommitScene(RTCScene scene); + + +/* Progress monitor callback function */ +typedef unmasked uniform bool (*uniform RTCProgressMonitorFunction)(void* uniform ptr, uniform double n); + +/* Sets the progress monitor callback function of the scene. */ +RTC_API void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunction progress, void* uniform ptr); + +/* Sets the build quality of the scene. */ +RTC_API void rtcSetSceneBuildQuality(RTCScene scene, uniform RTCBuildQuality quality); + +/* Sets the scene flags. */ +RTC_API void rtcSetSceneFlags(RTCScene scene, uniform RTCSceneFlags flags); + +/* Returns the scene flags. */ +RTC_API uniform RTCSceneFlags rtcGetSceneFlags(RTCScene scene); + +/* Returns the axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneBounds(RTCScene scene, uniform RTCBounds* uniform bounds_o); + +/* Returns the linear axis-aligned bounds of the scene. */ +RTC_API void rtcGetSceneLinearBounds(RTCScene scene, uniform RTCLinearBounds* uniform bounds_o); + +/* Intersects a single ray with the scene. */ +RTC_API void rtcIntersect1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit); + +/* Intersects a packet of 4 rays with the scene. */ +RTC_API void rtcIntersect4(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 8 rays with the scene. */ +RTC_API void rtcIntersect8(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a packet of 16 rays with the scene. */ +RTC_API void rtcIntersect16(const int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform rayhit); + +/* Intersects a varying ray with the scene. */ +RTC_FORCEINLINE void rtcIntersectV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + if (sizeof(varying float) == 16) + rtcIntersect4((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 32) + rtcIntersect8((uniform int* uniform)&imask, scene, context, rayhit); + else if (sizeof(varying float) == 64) + rtcIntersect16((uniform int* uniform)&imask, scene, context, rayhit); +} + +/* Intersects a stream of M rays with the scene. */ +RTC_API void rtcIntersect1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of pointers to M rays with the scene. */ +RTC_API void rtcIntersect1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHit** uniform rayhit, uniform unsigned int M); + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayHitN* uniform rayhit, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Intersects a stream of M ray packets of native packet size with the scene. */ +RTC_FORCEINLINE void rtcIntersectVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRayHit* uniform rayhit, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcIntersectNM(scene, context, (struct RTCRayHitN*)rayhit, sizeof(varying float)/4, M, byteStride); +} + +/* Intersects a stream of M ray packets of size N in SOA format with the scene. */ +RTC_API void rtcIntersectNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayHitNp* uniform rayhit, uniform unsigned int N); + +/* Tests a single ray for occlusion with the scene. */ +RTC_API void rtcOccluded1(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray); + +/* Tests a packet of 4 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded4(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 8 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded8(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a packet of 16 rays for occlusion occluded with the scene. */ +RTC_API void rtcOccluded16(const uniform int* uniform valid, RTCScene scene, const RTCIntersectContext* uniform context, void* uniform ray); + +/* Tests a varying ray for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedV(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray) +{ + varying bool mask = __mask; + unmasked { + varying int imask = mask ? -1 : 0; + } + + if (sizeof(varying float) == 16) + rtcOccluded4((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 32) + rtcOccluded8((uniform int* uniform)&imask, scene, context, ray); + else if (sizeof(varying float) == 64) + rtcOccluded16((uniform int* uniform)&imask, scene, context, ray); +} + +/* Tests a stream of M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1M(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of pointers to M rays for occlusion with the scene. */ +RTC_API void rtcOccluded1Mp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRay** uniform ray, uniform unsigned int M); + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNM(RTCScene scene, uniform RTCIntersectContext* uniform context, struct RTCRayN* uniform ray, uniform unsigned int N, uniform unsigned int M, uniform uintptr_t byteStride); + +/* Tests a stream of M ray packets of native size in SOA format for occlusion with the scene. */ +RTC_FORCEINLINE void rtcOccludedVM(RTCScene scene, uniform RTCIntersectContext* uniform context, varying RTCRay* uniform ray, uniform unsigned int M, uniform uintptr_t byteStride) { + rtcOccludedNM(scene, context, (struct RTCRayN*)ray, sizeof(varying float)/4, M, byteStride); +} + +/* Tests a stream of M ray packets of size N in SOA format for occlusion with the scene. */ +RTC_API void rtcOccludedNp(RTCScene scene, uniform RTCIntersectContext* uniform context, uniform RTCRayNp* uniform ray, uniform unsigned int N); + +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_version.h b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_version.h new file mode 100644 index 00000000..43a69754 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/include/embree3/rtcore_version.h @@ -0,0 +1,62 @@ +// ======================================================================== // +// Copyright 2009-2018 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#define RTC_VERSION_MAJOR 3 +#define RTC_VERSION_MINOR 5 +#define RTC_VERSION_PATCH 2 +#define RTC_VERSION 30502 +#define RTC_VERSION_STRING "3.5.2" + +/* #undef EMBREE_STATIC_LIB */ +/* #undef EMBREE_API_NAMESPACE */ + +#if defined(EMBREE_API_NAMESPACE) +# define RTC_NAMESPACE +# define RTC_NAMESPACE_BEGIN namespace { +# define RTC_NAMESPACE_END } +# define RTC_NAMESPACE_OPEN using namespace ; +# define RTC_API_EXTERN_C +# undef EMBREE_API_NAMESPACE +#else +# define RTC_NAMESPACE_BEGIN +# define RTC_NAMESPACE_END +# define RTC_NAMESPACE_OPEN +# if defined(__cplusplus) +# define RTC_API_EXTERN_C extern "C" +# else +# define RTC_API_EXTERN_C +# endif +#endif + +#if defined(ISPC) +# define RTC_API_IMPORT extern "C" unmasked +# define RTC_API_EXPORT extern "C" unmasked +#elif defined(EMBREE_STATIC_LIB) +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C +#elif defined(_WIN32) +# define RTC_API_IMPORT RTC_API_EXTERN_C __declspec(dllimport) +# define RTC_API_EXPORT RTC_API_EXTERN_C __declspec(dllexport) +#else +# define RTC_API_IMPORT RTC_API_EXTERN_C +# define RTC_API_EXPORT RTC_API_EXTERN_C __attribute__ ((visibility ("default"))) +#endif + +#if defined(RTC_EXPORT_API) +# define RTC_API RTC_API_EXPORT +#else +# define RTC_API RTC_API_IMPORT +#endif diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.dll b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.dll new file mode 100644 index 00000000..85b4b69d Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.dll differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.lib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.lib new file mode 100644 index 00000000..6c73c1d5 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/embree3.lib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.dll b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.dll new file mode 100644 index 00000000..cd1625f7 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.dll differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.lib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.lib new file mode 100644 index 00000000..af2a24e1 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbb.lib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.dll b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.dll new file mode 100644 index 00000000..c41f8e94 Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.dll differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.lib b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.lib new file mode 100644 index 00000000..923b57af Binary files /dev/null and b/voxel_cpp_test/Plugins/Voxel/Source/ThirdParty/Embree3/Win64/lib/tbbmalloc.lib differ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel.natvis b/voxel_cpp_test/Plugins/Voxel/Source/Voxel.natvis new file mode 100644 index 00000000..5d17146b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel.natvis @@ -0,0 +1,15 @@ + + + + + + Num={$T2} + + + $T2 + (TVoxelStaticArray<$T1,$T2,$T3>::ElementType*)Data + + + + + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/LICENSE b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/LICENSE new file mode 100644 index 00000000..e29965b7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Jordan Peck + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/VoxelFastNoiseLUT.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/VoxelFastNoiseLUT.cpp new file mode 100644 index 00000000..dae18892 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/FastNoise/VoxelFastNoiseLUT.cpp @@ -0,0 +1,40 @@ +// Copyright 2020 Phyronnaz + +#include "FastNoise/VoxelFastNoiseLUT.h" +#include "FastNoise/CrossPlatformSTD.h" + +#include + +void FVoxelFastNoiseLUT::SetSeed(int32 NewSeed) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + Seed = NewSeed; + + std::mt19937 Generator(NewSeed); + + // There is a bug below: + // 256 - j should be 255 - j + // However, fixing it would break all existing generators + // Instead, make sure it's deterministic + Perm.Memzero(); + Perm12.Memzero(); + + for (int32 Index = 0; Index < 256; Index++) + { + Perm[Index] = Index; + } + + for (int32 j = 0; j < 256; j++) + { + cross_platform_std::uniform_int_distribution<> Distribution(0, 256 - j); // <- bug here + // K should be max 255 + // Due to the bug it's max 256 + // m_perm.Num() = 512, so it's fine to do m_perm[k] + int32 k = Distribution(Generator) + j; + int32 l = Perm[j]; + Perm[j] = Perm[j + 256] = Perm[k]; + Perm[k] = l; + Perm12[j] = Perm12[j + 256] = Perm[j] % 12; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/IVoxelPool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/IVoxelPool.cpp new file mode 100644 index 00000000..c0201766 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/IVoxelPool.cpp @@ -0,0 +1,75 @@ +// Copyright 2020 Phyronnaz + +#include "IVoxelPool.h" +#include "VoxelMinimal.h" +#include "Engine/World.h" + +TMap, TVoxelSharedPtr> IVoxelPool::WorldsPools; +TVoxelSharedPtr IVoxelPool::GlobalPool; + +TVoxelSharedPtr IVoxelPool::GetWorldPool(UWorld* World) +{ + return WorldsPools.FindRef(World); +} + +TVoxelSharedPtr IVoxelPool::GetGlobalPool() +{ + return GlobalPool; +} + +TVoxelSharedPtr IVoxelPool::GetPoolForWorld(UWorld* World) +{ + if (auto Pool = GetWorldPool(World)) + { + return Pool; + } + return GetGlobalPool(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void IVoxelPool::SetWorldPool(UWorld* World, const TVoxelSharedRef& Pool, const FString& Creator) +{ + ensure(World); + ensure(!WorldsPools.Contains(World)); + + WorldsPools.Add(World, Pool); + + LOG_VOXEL(Log, TEXT("Voxel Pool created by %s for world %s"), *Creator, *World->GetName()); +} + +void IVoxelPool::SetGlobalPool(const TVoxelSharedRef& Pool, const FString& Creator) +{ + ensure(!GlobalPool.IsValid()); + + GlobalPool = Pool; + + LOG_VOXEL(Log, TEXT("Global Voxel Pool created by %s"), *Creator); +} + +/////////////////////////////////////////////////////////////////////////////// + +void IVoxelPool::DestroyWorldPool(UWorld* World) +{ + if (ensure(WorldsPools.Contains(World))) + { + WorldsPools.Remove(World); + LOG_VOXEL(Log, TEXT("Voxel Pool destroyed for %s"), *World->GetName()); + } +} + +void IVoxelPool::DestroyGlobalPool() +{ + if (ensure(GlobalPool.IsValid())) + { + GlobalPool.Reset(); + LOG_VOXEL(Log, TEXT("Global Voxel Pool destroyed")); + } +} + +void IVoxelPool::Shutdown() +{ + // Make sure to delete them cleanly here, as else issues can arise with ReturnSynchEventToPool + GlobalPool.Reset(); + WorldsPools.Reset(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAsset.cpp new file mode 100644 index 00000000..80c14c75 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAsset.cpp @@ -0,0 +1,206 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.h" +#include "VoxelAssets/VoxelDataAssetInstance.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelGenerators/VoxelTransformableGeneratorHelper.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelFeedbackContext.h" +#include "VoxelMessages.h" + +#include "Engine/Texture2D.h" +#include "Serialization/LargeMemoryReader.h" +#include "Serialization/LargeMemoryWriter.h" + +UVoxelDataAsset::UVoxelDataAsset() +{ + Data = MakeVoxelShared(); +} + +TVoxelSharedRef UVoxelDataAsset::GetInstance() +{ + return GetInstanceImpl(); +} + +TVoxelSharedRef UVoxelDataAsset::GetTransformableInstance() +{ + return MakeVoxelShared>(GetInstanceImpl(), bSubtractiveAsset); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelDataAsset::GetData() +{ + TryLoad(); + return Data.ToSharedRef(); +} + +void UVoxelDataAsset::SetData(const TVoxelSharedRef& InData) +{ + Data = InData; + Save(); +} + +TVoxelSharedRef UVoxelDataAsset::GetInstanceImpl() +{ + TryLoad(); + return MakeVoxelShared(*this); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataAsset::Save() +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelScopedSlowTask Saving(2.f); + + Modify(); + + VoxelCustomVersion = FVoxelDataAssetDataVersion::LatestVersion; + ValueConfigFlag = GVoxelValueConfigFlag; + MaterialConfigFlag = GVoxelMaterialConfigFlag; + + Saving.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing")); + + FLargeMemoryWriter MemoryWriter(Data->GetAllocatedSize()); + Data->Serialize(MemoryWriter, ValueConfigFlag, MaterialConfigFlag, FVoxelDataAssetDataVersion::Type(VoxelCustomVersion)); + + Saving.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Compressing")); + FVoxelSerializationUtilities::CompressData(MemoryWriter, CompressedData); + + SyncProperties(); +} + + +void UVoxelDataAsset::Load() +{ + VOXEL_FUNCTION_COUNTER(); + + if (CompressedData.Num() == 0) + { + // Nothing to load + return; + } + + TArray64 UncompressedData; + if (!FVoxelSerializationUtilities::DecompressData(CompressedData, UncompressedData)) + { + FVoxelMessages::Error("Decompression failed, data is corrupted", this); + return; + } + + FLargeMemoryReader MemoryReader(UncompressedData.GetData(), UncompressedData.Num()); + Data->Serialize(MemoryReader, ValueConfigFlag, MaterialConfigFlag, FVoxelDataAssetDataVersion::Type(VoxelCustomVersion)); + + if (!ensure(!MemoryReader.IsError())) + { + FVoxelMessages::Error("Serialization failed, data is corrupted", this); + } + ensure(MemoryReader.AtEnd()); + + SyncProperties(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataAsset::TryLoad() +{ + if (Data->IsEmpty()) + { + // Seems invalid, try to load + Load(); + } +} + +void UVoxelDataAsset::SyncProperties() +{ + // To access those properties without loading the asset + Size = Data->GetSize(); + UncompressedSizeInMB = + Data->GetRawValues().Num() * sizeof(FVoxelValue) / double(1 << 20) + + Data->GetRawMaterials().Num() * sizeof(FVoxelMaterial) / double(1 << 20); + CompressedSizeInMB = CompressedData.Num() / double(1 << 20); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataAsset::Serialize(FArchive& Ar) +{ + VOXEL_FUNCTION_COUNTER(); + + Super::Serialize(Ar); + + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (VoxelCustomVersion == FVoxelDataAssetDataVersion::BeforeCustomVersionWasAdded) + { + Ar << MaterialConfigFlag; + Ar << CompressedData; + } + else + { + CompressedData.BulkSerialize(Ar); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelDataAsset::SetThumbnail(TArray&& Colors) +{ + VOXEL_FUNCTION_COUNTER(); + + ThumbnailSave = MoveTemp(Colors); + ThumbnailTexture = nullptr; + + if (ThumbnailSave.Num() != Colors.Num()) + { + MarkPackageDirty(); + return; + } + + for (int32 Index = 0; Index < Colors.Num(); Index++) + { + if (ThumbnailSave[Index] != Colors[Index]) + { + MarkPackageDirty(); + return; + } + } +} + +UTexture2D* UVoxelDataAsset::GetThumbnail() +{ + if (!ThumbnailTexture) + { + ThumbnailTexture = UTexture2D::CreateTransient(DATA_ASSET_THUMBNAIL_RES, DATA_ASSET_THUMBNAIL_RES); + ThumbnailTexture->CompressionSettings = TC_HDR; + ThumbnailTexture->SRGB = false; + + ThumbnailSave.SetNumZeroed(DATA_ASSET_THUMBNAIL_RES * DATA_ASSET_THUMBNAIL_RES); + + FTexture2DMipMap& Mip = ThumbnailTexture->PlatformData->Mips[0]; + + void* TextureData = Mip.BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, ThumbnailSave.GetData(), ThumbnailSave.Num() * sizeof(FColor)); + + Mip.BulkData.Unlock(); + ThumbnailTexture->UpdateResource(); + } + + return ThumbnailTexture; +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAssetData.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAssetData.cpp new file mode 100644 index 00000000..a6bc3d12 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelDataAssetData.cpp @@ -0,0 +1,66 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAssets/VoxelDataAssetData.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelFeedbackContext.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataAssetMemory); + +void FVoxelDataAssetData::SetSize(const FIntVector& NewSize, bool bCreateMaterials) +{ + VOXEL_FUNCTION_COUNTER(); + check(int64(NewSize.X) * int64(NewSize.Y) * int64(NewSize.Z) < MAX_int32); + + const int32 Num = NewSize.X * NewSize.Y * NewSize.Z; + + // Somewhat thread safe + Values.Empty(Num); + Values.SetNumUninitialized(Num); + + Materials.Empty(bCreateMaterials ? Num : 0); + Materials.SetNumUninitialized(bCreateMaterials ? Num : 0); + + Size = NewSize; + + ensure(Size.GetMin() > 0); + ensure(Size.GetMax() > 1); // Else it'll be considered empty + UpdateStats(); +} + +void FVoxelDataAssetData::Serialize(FArchive& Ar, uint32 ValueConfigFlag, uint32 MaterialConfigFlag, FVoxelDataAssetDataVersion::Type Version) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelScopedSlowTask Serializing(2.f); + + Ar << Size; + + const auto SerializationVersion = + Version >= FVoxelDataAssetDataVersion::ValueConfigFlagAndSaveGUIDs + ? FVoxelSerializationVersion::ValueConfigFlagAndSaveGUIDs + : Version >= FVoxelDataAssetDataVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass + ? FVoxelSerializationVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass + : FVoxelSerializationVersion::BeforeCustomVersionWasAdded; + + static_assert(FVoxelSerializationVersion::LatestVersion == FVoxelSerializationVersion::SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, "Need to add a new FVoxelDataAssetDataVersion"); + + Serializing.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing values")); + FVoxelSerializationUtilities::SerializeValues(Ar, Values, ValueConfigFlag, SerializationVersion); + + Serializing.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing materials")); + FVoxelSerializationUtilities::SerializeMaterials(Ar, Materials, MaterialConfigFlag, SerializationVersion); + + if (Size.X * Size.Y * Size.Z != Values.Num() || (Materials.Num() > 0 && Size.X * Size.Y * Size.Z != Materials.Num())) + { + Ar.SetError(); + } + + UpdateStats(); +} + +void FVoxelDataAssetData::UpdateStats() const +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataAssetMemory, AllocatedSize); + AllocatedSize = Values.GetAllocatedSize() + Materials.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataAssetMemory, AllocatedSize); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelHeightmapAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelHeightmapAsset.cpp new file mode 100644 index 00000000..3088fe95 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAssets/VoxelHeightmapAsset.cpp @@ -0,0 +1,322 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelHeightmapAssetInstance.h" +#include "VoxelIntBox.h" +#include "VoxelMessages.h" +#include "VoxelFeedbackContext.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelGenerators/VoxelTransformableGeneratorHelper.h" + +#include "Serialization/LargeMemoryReader.h" +#include "Serialization/LargeMemoryWriter.h" + +#include "Engine/Texture2D.h" +#include "Misc/ScopedSlowTask.h" + +#define LANDSCAPE_ASSET_THUMBNAIL_RES 128 + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelHeightmapAssetMemory); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template<> +VOXEL_API TVoxelHeightmapAssetSamplerWrapper::TVoxelHeightmapAssetSamplerWrapper(UVoxelHeightmapAsset* Asset) + : Scale(Asset ? FMath::Max(SMALL_NUMBER, Asset->Scale) : 1) + , HeightScale(Asset ? Asset->HeightScale : 1) + , HeightOffset(Asset ? Asset->HeightOffset : 0) + , Data(Asset ? CastChecked(Asset)->GetDataSharedPtr() : MakeVoxelShared>()) +{ +} +template<> +VOXEL_API TVoxelHeightmapAssetSamplerWrapper::TVoxelHeightmapAssetSamplerWrapper(UVoxelHeightmapAsset* Asset) + : Scale(Asset ? FMath::Max(SMALL_NUMBER, Asset->Scale) : 1) + , HeightScale(Asset ? Asset->HeightScale : 1) + , HeightOffset(Asset ? Asset->HeightOffset : 0) + , Data(Asset ? CastChecked(Asset)->GetDataSharedPtr() : MakeVoxelShared>()) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void UVoxelHeightmapAsset::TryLoad(TVoxelHeightmapAssetData& Data) +{ + if (Data.IsEmpty()) + { + // Seems invalid, try to load + LoadData(Data); + } +} + +template +void UVoxelHeightmapAsset::SaveData(const TVoxelHeightmapAssetData& Data) +{ + Modify(); + + FVoxelScopedSlowTask Saving(2.f); + + VoxelCustomVersion = FVoxelHeightmapAssetDataVersion::LatestVersion; + MaterialConfigFlag = GVoxelMaterialConfigFlag; + + Saving.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing")); + + FLargeMemoryWriter MemoryWriter(Data.GetAllocatedSize()); + + bool bNeedToSave = false; + const_cast&>(Data).Serialize(MemoryWriter, MaterialConfigFlag, FVoxelHeightmapAssetDataVersion::Type(VoxelCustomVersion), bNeedToSave); + ensure(!bNeedToSave); + + Saving.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Compressing")); + FVoxelSerializationUtilities::CompressData(MemoryWriter, CompressedData); + + SyncProperties(Data); + +#if WITH_EDITOR + // Clear thumbnail + ThumbnailSave.Reset(); + ThumbnailTexture = nullptr; +#endif +} + +template +void UVoxelHeightmapAsset::LoadData(TVoxelHeightmapAssetData& Data) +{ + if (CompressedData.Num() == 0) + { + // Nothing to load + return; + } + TArray64 UncompressedData; + if (!FVoxelSerializationUtilities::DecompressData(CompressedData, UncompressedData)) + { + FVoxelMessages::Error("Decompression failed, data is corrupted", this); + return; + } + + FLargeMemoryReader MemoryReader(UncompressedData.GetData(), UncompressedData.Num()); + + bool bNeedToSave = false; + Data.Serialize(MemoryReader, MaterialConfigFlag, FVoxelHeightmapAssetDataVersion::Type(VoxelCustomVersion), bNeedToSave); + + if (!ensure(!MemoryReader.IsError())) + { + FVoxelMessages::Error("Serialization failed, data is corrupted", this); + Data.ClearData(); + return; + } + ensure(MemoryReader.AtEnd()); + + if (bNeedToSave) + { + SaveData(Data); + } + + SyncProperties(Data); +} + +template +void UVoxelHeightmapAsset::SyncProperties(const TVoxelHeightmapAssetData& Data) +{ + // To access those properties without loading the asset + Width = Data.GetWidth(); + Height = Data.GetHeight(); +} + +template +FVoxelIntBox UVoxelHeightmapAsset::GetBoundsImpl() const +{ + // const cast to load + auto Wrapper = TVoxelHeightmapAssetSamplerWrapper(const_cast(this)); + + return FVoxelIntBox( + FIntVector( + 0, + 0, + FMath::FloorToInt(-Precision + Wrapper.GetMinHeight() - AdditionalThickness)), + FIntVector( + Wrapper.GetWidth(), + Wrapper.GetHeight(), + FMath::CeilToInt(Precision + Wrapper.GetMaxHeight()))).Translate( + FIntVector( + -Wrapper.GetWidth() / 2, + -Wrapper.GetHeight() / 2, + 0)); +} + +void UVoxelHeightmapAsset::Serialize(FArchive& Ar) +{ + Super::Serialize(Ar); + + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (VoxelCustomVersion == FVoxelHeightmapAssetDataVersion::BeforeCustomVersionWasAdded) + { + Ar << MaterialConfigFlag; + Ar << CompressedData; + } + else + { + CompressedData.BulkSerialize(Ar); + } + } +} + +#if WITH_EDITOR +template +UTexture2D* UVoxelHeightmapAsset::GetThumbnailInternal() +{ + if (ThumbnailSave.Num() == 0) + { + const TVoxelHeightmapAssetData& Data = CastChecked(this)->GetData(); + ThumbnailSave.SetNumUninitialized(LANDSCAPE_ASSET_THUMBNAIL_RES * LANDSCAPE_ASSET_THUMBNAIL_RES); + for (int32 X = 0; X < LANDSCAPE_ASSET_THUMBNAIL_RES; X++) + { + for (int32 Y = 0; Y < LANDSCAPE_ASSET_THUMBNAIL_RES; Y++) + { + const float HeightValue = Data.GetHeight( + float(X) / LANDSCAPE_ASSET_THUMBNAIL_RES * Data.GetWidth(), + float(Y) / LANDSCAPE_ASSET_THUMBNAIL_RES * Data.GetHeight(), + EVoxelSamplerMode::Clamp); + const float Value = (HeightValue - Data.GetMinHeight()) / float(Data.GetMaxHeight() - Data.GetMinHeight()); + const uint8 Byte = FVoxelUtilities::FloatToUINT8(Value); + ThumbnailSave[X + LANDSCAPE_ASSET_THUMBNAIL_RES * Y] = FColor(Byte, Byte, Byte, 255); + } + } + } + if (!ThumbnailTexture) + { + ThumbnailTexture = UTexture2D::CreateTransient(LANDSCAPE_ASSET_THUMBNAIL_RES, LANDSCAPE_ASSET_THUMBNAIL_RES); + ThumbnailTexture->CompressionSettings = TC_HDR; + ThumbnailTexture->SRGB = false; + + ThumbnailSave.SetNumZeroed(LANDSCAPE_ASSET_THUMBNAIL_RES * LANDSCAPE_ASSET_THUMBNAIL_RES); + + FTexture2DMipMap& Mip = ThumbnailTexture->PlatformData->Mips[0]; + + void* TextureData = Mip.BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, ThumbnailSave.GetData(), ThumbnailSave.Num() * sizeof(FColor)); + + Mip.BulkData.Unlock(); + ThumbnailTexture->UpdateResource(); + } + + return ThumbnailTexture; +} + +UTexture2D* UVoxelHeightmapAsset::GetThumbnail() +{ + if (IsA()) + { + return GetThumbnailInternal(); + } + else if (IsA()) + { + return GetThumbnailInternal(); + } + else + { + check(false); + return nullptr; + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelHeightmapAssetFloat::UVoxelHeightmapAssetFloat() + : Data(MakeVoxelShared>()) +{ + HeightScale = 0.01f; +} + +TVoxelHeightmapAssetData& UVoxelHeightmapAssetFloat::GetData() +{ + return *GetDataSharedPtr(); +} + +TVoxelSharedRef> UVoxelHeightmapAssetFloat::GetDataSharedPtr() +{ + TryLoad(*Data); + return Data.ToSharedRef(); +} + +void UVoxelHeightmapAssetFloat::Save() +{ + SaveData(*Data); +} + +TVoxelSharedRef> UVoxelHeightmapAssetFloat::GetInstanceImpl() +{ + return MakeVoxelShared>(*this); +} + +TVoxelSharedRef UVoxelHeightmapAssetFloat::GetInstance() +{ + return GetInstanceImpl(); +} + +TVoxelSharedRef UVoxelHeightmapAssetFloat::GetTransformableInstance() +{ + return MakeVoxelShared>>(GetInstanceImpl(), false); +} + +FVoxelIntBox UVoxelHeightmapAssetFloat::GetBounds() const +{ + return GetBoundsImpl(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelHeightmapAssetUINT16::UVoxelHeightmapAssetUINT16() + : Data(MakeVoxelShared>()) +{ + HeightOffset = -32768 / 128.f; + HeightScale = 1.f / 128.f; + AdditionalThickness = -HeightOffset; +} + +TVoxelHeightmapAssetData& UVoxelHeightmapAssetUINT16::GetData() +{ + return *GetDataSharedPtr(); +} + +TVoxelSharedRef> UVoxelHeightmapAssetUINT16::GetDataSharedPtr() +{ + TryLoad(*Data); + return Data.ToSharedRef(); +} + +void UVoxelHeightmapAssetUINT16::Save() +{ + SaveData(*Data); +} + +TVoxelSharedRef> UVoxelHeightmapAssetUINT16::GetInstanceImpl() +{ + return MakeVoxelShared>(*this); +} + +TVoxelSharedRef UVoxelHeightmapAssetUINT16::GetInstance() +{ + return GetInstanceImpl(); +} + +TVoxelSharedRef UVoxelHeightmapAssetUINT16::GetTransformableInstance() +{ + return MakeVoxelShared>>(GetInstanceImpl(), false); +} + +FVoxelIntBox UVoxelHeightmapAssetUINT16::GetBounds() const +{ + return GetBoundsImpl(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAsyncWork.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAsyncWork.cpp new file mode 100644 index 00000000..8ac0020d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelAsyncWork.cpp @@ -0,0 +1,145 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAsyncWork.h" +#include "VoxelMinimal.h" +#include "HAL/Event.h" +#include "VoxelUtilities/VoxelStatsUtilities.h" + +FVoxelAsyncWork::~FVoxelAsyncWork() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + // DO NOT call WaitForDoThreadedWorkToExit here, as the child class destructor has already be run + // It's too late to wait + + if (!IsDone()) + { + LOG_VOXEL(Fatal, TEXT("VoxelAsyncWork %s is being deleted while still in the thread pool!"), *Name.ToString()); + } + if (DoneSection.IsLocked.GetValue() != 0) + { + LOG_VOXEL(Fatal, TEXT("VoxelAsyncWork %s is being deleted while still in DoThreadedWork!"), *Name.ToString()); + } +} + +void FVoxelAsyncWork::DoThreadedWork() +{ + VOXEL_ASYNC_VERBOSE_FUNCTION_COUNTER(); + + check(!IsDone()); + + if (!IsCanceled()) + { + VOXEL_SCOPE_COUNTER_FORMAT("DoWork: %s", *Name.ToString()); + DoWork(); + } + + DoneSection.Lock(); + + IsDoneCounter.Increment(); + + if (!IsCanceled()) + { + VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER("PostDoWork"); + check(IsDone()); + PostDoWork(); + } + + if (bAutodelete) + { + DoneSection.Unlock(); + delete this; + return; + } + + DoneSection.Unlock(); + // Might be deleted right after this +} + +void FVoxelAsyncWork::Abandon() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(!IsDone()); + + DoneSection.Lock(); + + IsDoneCounter.Increment(); + WasAbandonedCounter.Increment(); + + if (bAutodelete) + { + DoneSection.Unlock(); + delete this; + } + else + { + DoneSection.Unlock(); + } +} + +bool FVoxelAsyncWork::CancelAndAutodelete() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + DoneSection.Lock(); + + check(!bAutodelete); + + bAutodelete = true; + CanceledCounter.Increment(); + + if (IsDone()) + { + DoneSection.Unlock(); + delete this; + return true; + } + else + { + DoneSection.Unlock(); + return false; + } +} + +void FVoxelAsyncWork::WaitForDoThreadedWorkToExit() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + DoneSection.Lock(); + DoneSection.Unlock(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelAsyncWorkWithWait::FVoxelAsyncWorkWithWait(FName Name, double PriorityDuration, bool bAutoDelete) + : FVoxelAsyncWork(Name, PriorityDuration, bAutoDelete) +{ + DoneEvent = FPlatformProcess::GetSynchEventFromPool(true); + DoneEvent->Reset(); +} + +FVoxelAsyncWorkWithWait::~FVoxelAsyncWorkWithWait() +{ + if (DoneEvent) + { + FPlatformProcess::ReturnSynchEventToPool(DoneEvent); + DoneEvent = nullptr; + } +} + +void FVoxelAsyncWorkWithWait::PostDoWork() +{ + PostDoWorkBeforeTrigger(); + DoneEvent->Trigger(); +} + +void FVoxelAsyncWorkWithWait::WaitForCompletion() +{ + DoneEvent->Wait(); + // Else the thread that called WaitForCompletion might delete us before DoThreadedWork finishes + WaitForDoThreadedWorkToExit(); + check(IsDone()); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelInvokerComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelInvokerComponent.cpp new file mode 100644 index 00000000..70d65b9a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelInvokerComponent.cpp @@ -0,0 +1,254 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelComponents/VoxelInvokerComponent.h" +#include "VoxelWorldInterface.h" +#include "VoxelMessages.h" +#include "GameFramework/Pawn.h" +#include "Engine/World.h" +#include "Kismet/GameplayStatics.h" +#include "Components/BrushComponent.h" + +FIntVector UVoxelInvokerComponentBase::GetInvokerVoxelPosition(const AVoxelWorldInterface* VoxelWorld) const +{ + return GetInvokerVoxelPosition(const_cast(VoxelWorld)); +} + +FVoxelInvokerSettings UVoxelInvokerComponentBase::GetInvokerSettings(const AVoxelWorldInterface* VoxelWorld) const +{ + return GetInvokerSettings(const_cast(VoxelWorld)); +} + +bool UVoxelInvokerComponentBase::IsLocalInvoker_Implementation() const +{ + auto* Owner = Cast(GetOwner()); + return !Owner || Owner->IsLocallyControlled(); +} + +FIntVector UVoxelInvokerComponentBase::GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + return FIntVector(0); +} + +FVoxelInvokerSettings UVoxelInvokerComponentBase::GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + return {}; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelInvokerComponentBase::EnableInvoker() +{ + if (bIsInvokerEnabled) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invoker already enabled"), this); + return; + } + bIsInvokerEnabled = true; + + auto& Array = Components.FindOrAdd(GetWorld()); + Array.AddUnique(MakeWeakObjectPtr(this)); + Array.RemoveAllSwap([](auto& X) { return !X.IsValid(); }); + + LOG_VOXEL(Log, TEXT("Voxel Invoker enabled; Name: %s; Owner: %s"), *GetName(), *GetOwner()->GetName()); +} + +void UVoxelInvokerComponentBase::DisableInvoker() +{ + if (!bIsInvokerEnabled) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invoker already disabled"), this); + return; + } + bIsInvokerEnabled = false; + + auto& Array = Components.FindOrAdd(GetWorld()); + Array.RemoveAllSwap([this](auto& X) { return !X.IsValid() || X == this; }); + + LOG_VOXEL(Log, TEXT("Voxel Invoker disabled; Name: %s"), *GetName()); +} + +bool UVoxelInvokerComponentBase::IsInvokerEnabled() const +{ + return bIsInvokerEnabled; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelInvokerComponentBase::OnRegister() +{ + Super::OnRegister(); + if (bStartsEnabled && ensure(!bIsInvokerEnabled)) + { + EnableInvoker(); + } +} + +void UVoxelInvokerComponentBase::OnUnregister() +{ + Super::OnUnregister(); + if (bIsInvokerEnabled) + { + DisableInvoker(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelInvokerComponentBase::RefreshAllVoxelInvokers() +{ + VOXEL_FUNCTION_COUNTER(); + OnForceRefreshInvokers.Broadcast(); +} + +const TArray>& UVoxelInvokerComponentBase::GetInvokers(UWorld* World) +{ + auto& Result = Components.FindOrAdd(World); + Result.RemoveAllSwap([](auto& X) { return !X.IsValid(); }); + return Result; +} + +FSimpleMulticastDelegate UVoxelInvokerComponentBase::OnForceRefreshInvokers; +TMap, TArray>> UVoxelInvokerComponentBase::Components; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FIntVector UVoxelSimpleInvokerComponent::GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + return VoxelWorld->GlobalToLocal(GetInvokerGlobalPosition()); +} + +FVoxelInvokerSettings UVoxelSimpleInvokerComponent::GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + const FVector InvokerGlobalPosition = GetInvokerGlobalPosition(); + const auto GetVoxelBounds = [&](float Distance) + { + return FVoxelIntBox::SafeConstruct( + VoxelWorld->GlobalToLocal(InvokerGlobalPosition - Distance, EVoxelWorldCoordinatesRounding::RoundDown), + VoxelWorld->GlobalToLocal(InvokerGlobalPosition + Distance, EVoxelWorldCoordinatesRounding::RoundUp) + ); + }; + + FVoxelInvokerSettings Settings; + + Settings.bUseForLOD = bUseForLOD; + Settings.LODToSet = LODToSet; + Settings.LODBounds = GetVoxelBounds(LODRange); + + Settings.bUseForCollisions = bUseForCollisions; + Settings.CollisionsBounds = GetVoxelBounds(CollisionsRange); + + Settings.bUseForNavmesh = bUseForNavmesh; + Settings.NavmeshBounds = GetVoxelBounds(NavmeshRange); + + return Settings; +} + +FVector UVoxelSimpleInvokerComponent::GetInvokerGlobalPosition_Implementation() const +{ + return GetComponentLocation(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVector UVoxelInvokerWithPredictionComponent::GetInvokerGlobalPosition_Implementation() const +{ + FVector Position = GetComponentLocation(); + if (bEnablePrediction) + { + Position += GetOwner()->GetVelocity() * PredictionTime; + } + return Position; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVector UVoxelInvokerAutoCameraComponent::GetInvokerGlobalPosition_Implementation() const +{ + APlayerCameraManager* CameraManager = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0); + if (ensure(CameraManager)) + { + return CameraManager->GetCameraLocation(); + } + else + { + return FVector::ZeroVector; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelLODVolumeInvokerComponent::UVoxelLODVolumeInvokerComponent() +{ + bUseForEvents = false; + bUseForPriorities = false; +} + +bool UVoxelLODVolumeInvokerComponent::IsLocalInvoker_Implementation() const +{ + // Always true for a volume + return true; +} + +FIntVector UVoxelLODVolumeInvokerComponent::GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + return VoxelWorld->GlobalToLocal(GetComponentLocation()); +} + +FVoxelInvokerSettings UVoxelLODVolumeInvokerComponent::GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const +{ + auto* Volume = CastChecked(GetOwner()); + const FBox VolumeWorldBounds = Volume->GetBounds().GetBox(); + const FVoxelIntBox VolumeBounds = FVoxelIntBox::SafeConstruct( + VoxelWorld->GlobalToLocal(VolumeWorldBounds.Min, EVoxelWorldCoordinatesRounding::RoundDown), + VoxelWorld->GlobalToLocal(VolumeWorldBounds.Max, EVoxelWorldCoordinatesRounding::RoundUp) + ); + + FVoxelInvokerSettings Settings; + + Settings.bUseForLOD = bUseForLOD; + Settings.LODToSet = LODToSet; + Settings.LODBounds = VolumeBounds; + + Settings.bUseForCollisions = bUseForCollisions; + Settings.CollisionsBounds = VolumeBounds; + + Settings.bUseForNavmesh = bUseForNavmesh; + Settings.NavmeshBounds = VolumeBounds; + + return Settings; +} + +/////////////////////////////////////////////////////////////////////////////// + +AVoxelLODVolume::AVoxelLODVolume() +{ + GetBrushComponent()->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); + GetBrushComponent()->bAlwaysCreatePhysicsState = true; + GetBrushComponent()->SetMobility(EComponentMobility::Movable); + + InvokerComponent = CreateDefaultSubobject("Invoker Component"); + InvokerComponent->SetupAttachment(RootComponent); +} + +#if WITH_EDITOR +void AVoxelLODVolume::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + UVoxelInvokerComponentBase::RefreshAllVoxelInvokers(); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelNoClippingComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelNoClippingComponent.cpp new file mode 100644 index 00000000..0dd4c9c5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelNoClippingComponent.cpp @@ -0,0 +1,345 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelComponents/VoxelNoClippingComponent.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" + +#include "Async/Async.h" +#include "EngineUtils.h" +#include "GameFramework/Character.h" +#include "GameFramework/CharacterMovementComponent.h" + +UVoxelNoClippingComponent::UVoxelNoClippingComponent() +{ + PrimaryComponentTick.bCanEverTick = true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelNoClippingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + VOXEL_FUNCTION_COUNTER(); + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + const double Time = FPlatformTime::Seconds(); + + if (!AsyncResult.IsValid() && LastTickTime + TickRate < Time) + { + // Time to start a new search + LastTickTime = Time; + StartAsyncTask(); + } + + if (!AsyncResult.IsValid() || !AsyncResult.IsReady()) + { + // Search is not started/not complete + if (bIsInsideSurface) + { + // We should have a search in progress already + ensure(AsyncResult.IsValid()); + // Always fire the event on tick + BroadcastMoveTowardsSurface(); + } + return; + } + + const TArray Results = AsyncResult.Get(); + const TArray> VoxelWorlds = MoveTemp(PendingVoxelWorlds); + + AsyncResult.Reset(); + PendingVoxelWorlds.Reset(); + + if (!ensure(Results.Num() == VoxelWorlds.Num()) || !ensure(Results.Num() > 0)) + { + return; + } + + FAsyncResult WorstResult; + TWeakObjectPtr WorstVoxelWorld; + for (int32 Index = 0; Index < Results.Num(); Index++) + { + const FAsyncResult& Result = Results[Index]; + const TWeakObjectPtr& VoxelWorld = VoxelWorlds[Index]; + + if (!Result.bInsideSurface) + { + continue; + } + + WorstResult = Result; + WorstVoxelWorld = VoxelWorld; + + // TODO smarter selection when we are inside multiple voxel worlds? + break; + } + + if (!WorstResult.bInsideSurface) + { + // We're safe + if (bIsInsideSurface) + { + bIsInsideSurface = false; + BroadcastStopMovingTowardsSurface(); + } + return; + } + + if (!WorstVoxelWorld.IsValid() || !WorstVoxelWorld->IsCreated()) + { + LOG_VOXEL(Warning, TEXT("NoClippingComponent: Clearing task result as voxel world is now invalid")); + WorstVoxelWorld = nullptr; + WorstResult.ClosestSafeLocation.Reset(); + } + + // We're not safe! + if (WorstResult.ClosestSafeLocation) + { + // We found a safe location: teleport there + + const FVector NewPosition = WorstVoxelWorld->LocalToGlobal(WorstResult.ClosestSafeLocation.GetValue()); + const FVector Delta = NewPosition - GetComponentLocation(); + + AActor& Owner = *GetOwner(); + + auto* Root = Owner.GetRootComponent(); + if (!ensure(Root)) + { + return; + } + + LOG_VOXEL(Log, TEXT("NoClippingComponent: teleporting %s to %s (delta: %s)"), *Owner.GetName(), *(Root->GetComponentLocation() + Delta).ToString(), *Delta.ToString()); + Root->MoveComponent(Delta, Owner.GetActorQuat(), false, nullptr, MOVECOMP_NoFlags, ETeleportType::ResetPhysics); + + BroadcastOnTeleported(); + + // We're safe + if (bIsInsideSurface) + { + bIsInsideSurface = false; + BroadcastStopMovingTowardsSurface(); + } + } + else + { + bIsInsideSurface = true; + + // Start a task now, so we can read the result as soon as possible + StartAsyncTask(); + + LOG_VOXEL(Log, TEXT("NoClippingComponent: could not find a safe location, firing MoveTowardsSurface. SearchRange: %d"), SearchRange); + BroadcastMoveTowardsSurface(); + } + + ensure(AsyncResult.IsValid() || !bIsInsideSurface); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelNoClippingComponent::StartAsyncTask() +{ + if (!ensure(!AsyncResult.IsValid())) + { + return; + } + ensure(PendingVoxelWorlds.Num() == 0); + + struct FVoxelWorldInfo + { + AVoxelWorld* VoxelWorld = nullptr; + TVoxelSharedPtr Data; + FVoxelVector Location{ ForceInit }; + }; + TArray VoxelWorldInfos; + + for (auto* VoxelWorld : TActorRange(GetWorld())) + { + if (!VoxelWorld->IsCreated() || !ShouldUseVoxelWorld(VoxelWorld)) + { + continue; + } + + const auto Location = VoxelWorld->GlobalToLocalFloat(GetComponentLocation()); + if (VoxelWorld->GetWorldBounds().ContainsFloat(Location)) + { + VoxelWorldInfos.Add(FVoxelWorldInfo{ VoxelWorld, VoxelWorld->GetDataSharedPtr(), Location }); + } + } + + if (VoxelWorldInfos.Num() > 0) + { + PendingVoxelWorlds.Reset(); + for (auto& It : VoxelWorldInfos) + { + PendingVoxelWorlds.Add(It.VoxelWorld); + } + + AsyncResult = Async(EAsyncExecution::TaskGraph, [VoxelWorldInfos, SearchRange = SearchRange]() + { + TArray Results; + for (auto& It : VoxelWorldInfos) + { + Results.Add(AsyncTask(*It.Data, It.Location, SearchRange)); + } + return Results; + }); + } + else + { + // No voxel world found, stop moving towards surface if needed + if (bIsInsideSurface) + { + LOG_VOXEL(Log, TEXT("NoClippingComponent: went outside of the voxel world bounds, stopping moving towards the surface")); + bIsInsideSurface = false; + BroadcastStopMovingTowardsSurface(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelNoClippingComponent::BroadcastMoveTowardsSurface() const +{ + if (bEnableDefaultBehavior) + { + auto* CharacterMovement = GetCharacterMovement(); + if (CharacterMovement) + { + ResetVelocity(*CharacterMovement); + + FVector Direction; + if (bIsPlanet) + { + Direction = (CharacterMovement->GetActorLocation() - PlanetCenter).GetSafeNormal(); + } + else + { + Direction = FVector::UpVector; + } + CharacterMovement->AddImpulse(Direction * Speed, true); + } + } + + MoveTowardsSurface.Broadcast(); +} + +void UVoxelNoClippingComponent::BroadcastStopMovingTowardsSurface() const +{ + if (bEnableDefaultBehavior) + { + auto* CharacterMovement = GetCharacterMovement(); + if (CharacterMovement) + { + ResetVelocity(*CharacterMovement); + } + } + + StopMovingTowardsSurface.Broadcast(); +} + +void UVoxelNoClippingComponent::BroadcastOnTeleported() const +{ + if (bEnableDefaultBehavior) + { + auto* CharacterMovement = GetCharacterMovement(); + if (CharacterMovement) + { + ResetVelocity(*CharacterMovement); + } + } + + OnTeleported.Broadcast(); +} + +UCharacterMovementComponent* UVoxelNoClippingComponent::GetCharacterMovement() const +{ + auto* Character = Cast(GetOwner()); + if (Character) + { + auto* CharacterMovement = Character->GetCharacterMovement(); + ensure(CharacterMovement); + return CharacterMovement; + } + else + { + return nullptr; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelNoClippingComponent::ResetVelocity(UCharacterMovementComponent& CharacterMovement) +{ + CharacterMovement.Velocity = FVector::ZeroVector; + CharacterMovement.ClearAccumulatedForces(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNoClippingComponent::FAsyncResult UVoxelNoClippingComponent::AsyncTask(const FVoxelData& Data, const FVoxelVector& ComponentLocation, int32 SearchRange) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const FVoxelIntBox Bounds = FVoxelIntBox(ComponentLocation).Extend(FMath::Max(1, SearchRange)); + FVoxelReadScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + + const FVoxelConstDataAccelerator Accelerator(Data); + for (auto& Neighbor : FVoxelUtilities::GetNeighbors(ComponentLocation)) + { + if (Accelerator.GetValue(Neighbor, 0).IsEmpty()) + { + // At least one of the voxel near us is not empty: consider ourselves safe + return {}; + } + } + + const double StartTime = FPlatformTime::Seconds(); + + FAsyncResult Result; + Result.bInsideSurface = true; + + float BestDistance = 1e9; + + // Somewhat suboptimal: we could query the voxels in "circles" instead + // It's so fast that it's probably not worth the hassle + const auto Values = Data.GetValues(Bounds); + const FIntVector Size = Bounds.Size(); + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + if (!Values[X + Size.X * Y + Size.X * Size.Y * Z].IsEmpty()) + { + continue; + } + + const FIntVector Position = Bounds.Min + FIntVector(X, Y, Z); + const float Distance = FVoxelVector::Distance(FVoxelVector(Position), ComponentLocation); + if (Distance < BestDistance) + { + BestDistance = Distance; + Result.ClosestSafeLocation = Position; + } + } + } + } + + const double EndTime = FPlatformTime::Seconds(); + + LOG_VOXEL(Verbose, TEXT("NoClippingComponent search took %.3fms. Success: %s"), (EndTime - StartTime) * 1000, *LexToString(Result.ClosestSafeLocation.IsSet())); + + return Result; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelPhysicsRelevancyComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelPhysicsRelevancyComponent.cpp new file mode 100644 index 00000000..cdfbc963 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelComponents/VoxelPhysicsRelevancyComponent.cpp @@ -0,0 +1,110 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelComponents/VoxelPhysicsRelevancyComponent.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelWorld.h" + +#include "EngineUtils.h" +#include "TimerManager.h" +#include "Components/PrimitiveComponent.h" + +UVoxelPhysicsRelevancyComponent::UVoxelPhysicsRelevancyComponent() +{ + PrimaryComponentTick.bCanEverTick = true; +} + +void UVoxelPhysicsRelevancyComponent::BeginPlay() +{ + VOXEL_FUNCTION_COUNTER(); + + Super::BeginPlay(); + + for (UActorComponent* Component : GetOwner()->GetComponents()) + { + if (UPrimitiveComponent* PrimitiveComponent = Cast(Component)) + { + if (PrimitiveComponent->BodyInstance.bSimulatePhysics) + { + PrimitiveComponentsWithPhysicsEnabled.Add(PrimitiveComponent); + } + } + } + + if (PrimitiveComponentsWithPhysicsEnabled.Num() == 0) + { + SetComponentTickEnabled(false); + } + + SetComponentTickInterval(TickInterval); +} + +void UVoxelPhysicsRelevancyComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + VOXEL_FUNCTION_COUNTER(); + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + if (PrimitiveComponentsWithPhysicsEnabled.Num() == 0) + { + return; + } + + bool bShouldSimulatePhysics = false; + const FVector Position = GetOwner()->GetActorLocation(); + for (TActorIterator It(GetWorld()); It; ++It) + { + auto* World = *It; + if (World->IsCreated()) + { + auto LocalPosition = World->GlobalToLocal(Position); + auto& LODManager = World->GetLODManager(); + if (LODManager.Settings.WorldBounds.Contains(LocalPosition)) + { + uint8 LOD; + if (LODManager.AreCollisionsEnabled(LocalPosition, LOD) && LOD <= MaxVoxelChunksLODForPhysics) + { + bShouldSimulatePhysics = true; + break; + } + } + } + } + if (bArePhysicsEnabled != bShouldSimulatePhysics) + { + if (bShouldSimulatePhysics) + { + if (!bWaitingToBeActivated) + { + auto& TimerManager = GetWorld()->GetTimerManager(); + TimerManager.SetTimer(Handle, this, &UVoxelPhysicsRelevancyComponent::ActivatePhysics, TimeToWaitBeforeActivating); + bWaitingToBeActivated = true; + } + } + else + { + if (bWaitingToBeActivated) + { + auto& TimerManager = GetWorld()->GetTimerManager(); + TimerManager.ClearTimer(Handle); + bWaitingToBeActivated = false; + } + bArePhysicsEnabled = false; + for (auto& Component : PrimitiveComponentsWithPhysicsEnabled) + { + Component->SetSimulatePhysics(false); + } + } + } +} + +void UVoxelPhysicsRelevancyComponent::ActivatePhysics() +{ + VOXEL_FUNCTION_COUNTER(); + + bWaitingToBeActivated = false; + bArePhysicsEnabled = true; + for (auto& Component : PrimitiveComponentsWithPhysicsEnabled) + { + Component->SetSimulatePhysics(true); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookedData.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookedData.cpp new file mode 100644 index 00000000..d27b474a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookedData.cpp @@ -0,0 +1,44 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelCooking/VoxelCookedData.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelCookedDataMemory); + +FVoxelCookedDataImpl::~FVoxelCookedDataImpl() +{ +} + +bool FVoxelCookedDataImpl::Serialize(FArchive& Ar) +{ + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (Ar.IsSaving()) + { + Version = FVoxelCookedDataVersion::LatestVersion; + } + + Ar << Version; + Ar << Guid; + Ar << Chunks; + + UpdateAllocatedSize(); + } + + return true; +} + +void FVoxelCookedDataImpl::UpdateAllocatedSize() const +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelCookedDataMemory, AllocatedSize); + AllocatedSize = Chunks.GetAllocatedSize(); + for (auto& Chunk : Chunks) + { + AllocatedSize += Chunk.Data.GetAllocatedSize(); + } + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelCookedDataMemory, AllocatedSize); +} + +void FVoxelCookedDataImpl::RemoveEmptyChunks() +{ + Chunks.RemoveAllSwap([](auto& Chunk) { return Chunk.Data.Num() == 0; }); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookingLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookingLibrary.cpp new file mode 100644 index 00000000..7cbc43c2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelCooking/VoxelCookingLibrary.cpp @@ -0,0 +1,316 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelCooking/VoxelCookingLibrary.h" + +#include "VoxelWorld.h" +#include "VoxelMessages.h" +#include "VoxelDefaultPool.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelRender/Renderers/VoxelDefaultRenderer.h" +#include "VoxelWorldRootComponent.h" + +#include "HAL/Event.h" + +#include "IPhysXCooking.h" +#include "IPhysXCookingModule.h" + +#include "PhysXIncludes.h" +#include "PhysicsEngine/PhysicsSettings.h" +#include "Interface_CollisionDataProviderCore.h" +#include "Engine/Private/PhysicsEngine/PhysXSupport.h" // For FPhysXInputStream + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +struct FVoxelCookingTaskData +{ + IVoxelRenderer& Renderer; + FVoxelCookedDataImpl& CookedData; + IPhysXCooking& PhysXCooking; + + const int32 NumChunksToBuild; + const FVoxelCookingSettings CookingSettings; + + FEvent* const DoneEvent; + FThreadSafeCounter NumChunksBuilt; + + FThreadSafeCounter64 MeshingTime; + FThreadSafeCounter64 CollisionTime; + + FVoxelCookingTaskData(IVoxelRenderer& Renderer, FVoxelCookedDataImpl& CookedData, int32 NumChunksToBuild, const FVoxelCookingSettings& CookingSettings) + : Renderer(Renderer) + , CookedData(CookedData) + , PhysXCooking(*GetPhysXCookingModule()->GetPhysXCooking()) + , NumChunksToBuild(NumChunksToBuild) + , CookingSettings(CookingSettings) + , DoneEvent(FPlatformProcess::GetSynchEventFromPool()) + { + CookedData.SetNumChunks(NumChunksToBuild); + } + ~FVoxelCookingTaskData() + { + check(NumChunksBuilt.GetValue() == NumChunksToBuild); + FPlatformProcess::ReturnSynchEventToPool(DoneEvent); + } + + void ChunkDone(TArray&& Data) + { + const int32 NumBuilt = NumChunksBuilt.Increment(); + if (CookingSettings.bLogProgress) + { + LOG_VOXEL(Log, TEXT("VOXEL COOKING: %d/%d"), NumBuilt, NumChunksToBuild); + } + + CookedData.GetChunk(NumBuilt - 1).Data = MoveTemp(Data); + + if (NumBuilt == NumChunksToBuild) + { + DoneEvent->Trigger(); + } + } +}; + +class FVoxelCookingTask : public IVoxelQueuedWork +{ +public: + const FIntVector ChunkPosition; + FVoxelCookingTaskData& TaskData; + + FVoxelCookingTask(const FIntVector& ChunkPosition, FVoxelCookingTaskData& TaskData) + : IVoxelQueuedWork(STATIC_FNAME("Cooking Task"), 0) + , ChunkPosition(ChunkPosition) + , TaskData(TaskData) + { + } + + virtual void DoThreadedWork() override + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray Indices; + TArray Vertices; + { + VOXEL_ASYNC_SCOPE_COUNTER("Creating geometry"); + + const uint64 StartTime = FPlatformTime::Cycles64(); + TaskData.Renderer.CreateGeometry_AnyThread(0, ChunkPosition, Indices, Vertices); + const uint64 EndTime = FPlatformTime::Cycles64(); + TaskData.MeshingTime.Add(EndTime - StartTime); + } + + TArray Buffer; + if (Indices.Num() > 0) + { + static const FName PhysXFormat = FPlatformProperties::GetPhysicsFormat(); + + EPhysXMeshCookFlags CookFlags = EPhysXMeshCookFlags::Default; + if (!TaskData.CookingSettings.bCleanCollisionMesh) + { + CookFlags |= EPhysXMeshCookFlags::DeformableMesh; + } + if (TaskData.CookingSettings.bFastCollisionCook) + { + CookFlags |= EPhysXMeshCookFlags::FastCook; + } + + check(Indices.Num() % 3 == 0); + + TArray TriIndices; + TriIndices.SetNumUninitialized(Indices.Num() / 3); + FMemory::Memcpy(TriIndices.GetData(), Indices.GetData(), Indices.Num() * sizeof(int32)); + + // Put the chunk in global space, as tri meshes don't support individual transforms + for (auto& Vertex : Vertices) + { + Vertex = (Vertex + FVector(ChunkPosition)) * TaskData.CookingSettings.VoxelSize; + } + + constexpr bool bFlipNormals = true; // Always true due to the order of the vertices (clock wise vs not) + + bool bResult; + { + VOXEL_ASYNC_SCOPE_COUNTER("Cooking collision"); + + const uint64 StartTime = FPlatformTime::Cycles64(); + bResult = TaskData.PhysXCooking.CookTriMesh(PhysXFormat, CookFlags, Vertices, TriIndices, {}, bFlipNormals, Buffer); + const uint64 EndTime = FPlatformTime::Cycles64(); + TaskData.CollisionTime.Add(EndTime - StartTime); + } + + if (!bResult) + { + Buffer.Reset(); + LOG_VOXEL(Warning, TEXT("VOXEL COOKING: Failed to cook chunk at %s with %d indices"), *ChunkPosition.ToString(), Indices.Num()); + } + } + + TaskData.ChunkDone(MoveTemp(Buffer)); + delete this; + } + virtual void Abandon() override + { + check(false); + } + virtual uint32 GetPriority() const override + { + return 0; + } +}; +#endif + +FVoxelCookedData UVoxelCookingLibrary::CookVoxelDataImpl(const FVoxelCookingSettings& Settings, const FVoxelUncompressedWorldSaveImpl* Save) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!Settings.Generator.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid generator")); + return {}; + } + + AVoxelWorld* VoxelWorld = NewObject(); + VoxelWorld->RenderOctreeDepth = Settings.RenderOctreeDepth; + VoxelWorld->VoxelSize = Settings.VoxelSize; + VoxelWorld->RenderType = Settings.RenderType; + VoxelWorld->Generator = Settings.Generator; + + const auto Pool = FVoxelDefaultPool::Create(Settings.ThreadCount, true, {}, {}); + const auto Data = FVoxelData::Create(FVoxelDataSettings(VoxelWorld, EVoxelPlayType::Game)); + const auto DebugManager = FVoxelDebugManager::Create(FVoxelDebugManagerSettings(VoxelWorld, EVoxelPlayType::Game, Pool, Data)); + + if (Save) + { + Data->LoadFromSave(*Save, {}); + } + + const auto Renderer = FVoxelDefaultRenderer::Create(FVoxelRendererSettings( + VoxelWorld, + EVoxelPlayType::Game, + nullptr, + Data, + Pool, + nullptr, + DebugManager, + false)); + + const FIntVector Min = Data->WorldBounds.Min; + const FIntVector Max = Data->WorldBounds.Max; + + const FIntVector NumChunksPerAxis = (Max - Min) / RENDER_CHUNK_SIZE; + const int64 TotalNumChunks = int64(NumChunksPerAxis.X) * int64(NumChunksPerAxis.Y) * int64(NumChunksPerAxis.Z); + + if (TotalNumChunks > MAX_int32) + { + FVoxelMessages::Error(FUNCTION_ERROR("Depth too high")); + return {}; + } + + const double StartTime = FPlatformTime::Seconds(); + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Starting cooking with %lld tasks"), TotalNumChunks); + + FVoxelCookedData CookedData; + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + FVoxelCookingTaskData TaskData(*Renderer, CookedData.Mutable(), TotalNumChunks, Settings); + + for (int32 X = Min.X; X < Max.X; X += RENDER_CHUNK_SIZE) + { + for (int32 Y = Min.Y; Y < Max.Y; Y += RENDER_CHUNK_SIZE) + { + for (int32 Z = Min.Z; Z < Max.Z; Z += RENDER_CHUNK_SIZE) + { + const FIntVector ChunkPosition = FIntVector(X, Y, Z); + auto* Task = new FVoxelCookingTask(ChunkPosition, TaskData); + Pool->QueueTask({}, Task); + } + } + } + + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Waiting for tasks")); + TaskData.DoneEvent->Wait(); + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Done")); + + const double EndTime = FPlatformTime::Seconds(); + + TaskData.CookedData.RemoveEmptyChunks(); + TaskData.CookedData.UpdateAllocatedSize(); + + const double GameThreadTime = EndTime - StartTime; + const double MeshingTime = TaskData.MeshingTime.GetValue() * FPlatformTime::GetSecondsPerCycle64(); + const double CollisionTime = TaskData.CollisionTime.GetValue() * FPlatformTime::GetSecondsPerCycle64(); + const double OverheadTime = GameThreadTime - (MeshingTime + CollisionTime) / Settings.ThreadCount; + + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Game Thread time: %fs"), GameThreadTime); + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Async Thread meshing time: %fs"), MeshingTime); + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Async Thread collision time: %fs"), CollisionTime); + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Overhead time: %fs (%f%%)"), OverheadTime, 100. * OverheadTime / GameThreadTime); +#else + ensure(false); +#endif + + Renderer->Destroy(); + DebugManager->Destroy(); + + return CookedData; +} + +FVoxelCookingSettings UVoxelCookingLibrary::MakeVoxelCookingSettingsFromVoxelWorld(AVoxelWorld* World, int32 ThreadCount) +{ + if (!World) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Voxel World")); + return {}; + } + + FVoxelCookingSettings Settings; + Settings.ThreadCount = ThreadCount; + Settings.RenderOctreeDepth = World->RenderOctreeDepth; + Settings.VoxelSize = World->VoxelSize; + Settings.RenderType = World->RenderType; + Settings.Generator = World->Generator; + + return Settings; +} + +void UVoxelCookingLibrary::LoadCookedVoxelData(FVoxelCookedData CookedData, AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!World) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid voxel world!")); + return; + } + if (World->IsCreated()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Voxel world is already created!")); + return; + } + + UVoxelWorldRootComponent& WorldRoot = World->GetWorldRoot(); + + const auto& Chunks = CookedData.Const().GetChunks(); + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + TArray TriMeshes; + TriMeshes.Reserve(Chunks.Num()); + + for (auto& Chunk : Chunks) + { + if (ensure(Chunk.Data.Num() > 0)) + { + FPhysXInputStream Buffer(Chunk.Data.GetData(), Chunk.Data.Num()); + physx::PxTriangleMesh* CookedMesh = GPhysXSDK->createTriangleMesh(Buffer); + TriMeshes.Add(CookedMesh); + } + } + + WorldRoot.SetCookedTriMeshes(TriMeshes); +#else + ensure(false); +#endif + + World->ApplyCollisionSettingsToRoot(); + + LOG_VOXEL(Log, TEXT("VOXEL COOKING: Loaded cooked data")); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelData.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelData.cpp new file mode 100644 index 00000000..bcf26ea3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelData.cpp @@ -0,0 +1,1133 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelData.inl" +#include "VoxelData/VoxelSave.h" +#include "VoxelData/VoxelDataLock.h" +#include "VoxelData/VoxelDataOctree.h" +#include "VoxelData/VoxelSaveUtilities.h" +#include "VoxelData/VoxelDataUtilities.h" + +#include "VoxelDiff.h" +#include "VoxelEnums.h" +#include "VoxelWorld.h" +#include "VoxelQueryZone.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" + +#include "Misc/ScopeLock.h" +#include "Async/Async.h" + +VOXEL_API TAutoConsoleVariable CVarMaxPlaceableItemsPerOctree( + TEXT("voxel.data.MaxPlaceableItemsPerOctree"), + 1, + TEXT("Max number of placeable items per data octree node. If more placeable items are added, the node is split. Low = fast generation, High = very slightly lower memory usage"), + ECVF_Default); + +VOXEL_API TAutoConsoleVariable CVarStoreSpecialValueForGeneratorValuesInSaves( + TEXT("voxel.data.StoreSpecialValueForGeneratorValuesInSaves"), + 1, + TEXT("If true, will store FVoxelValue::Special() instead of the value if it's equal to the generator value when saving. Reduces save size a lot, but increases save time a lot too.\n") + TEXT("Important: must be the same when saving & loading!"), + ECVF_Default); + +DEFINE_STAT(STAT_NumVoxelAssetItems); +DEFINE_STAT(STAT_NumVoxelDisableEditsItems); +DEFINE_STAT(STAT_NumVoxelDataItems); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline auto CreateGenerator(const AVoxelWorld* World) +{ + auto GeneratorInstance = World->Generator.GetInstance(true); + GeneratorInstance->Init(World->GetGeneratorInit()); + return GeneratorInstance; +} + +inline int32 ClampDataDepth(int32 Depth) +{ + return FMath::Max(1, FVoxelUtilities::ClampDepth(Depth)); +} + +FVoxelDataSettings::FVoxelDataSettings(const AVoxelWorld* World, EVoxelPlayType PlayType) + : Depth(ClampDataDepth(FVoxelUtilities::ConvertDepth(World->RenderOctreeDepth))) + , WorldBounds(World->GetWorldBounds()) + , Generator(CreateGenerator(World)) + , bEnableMultiplayer(PlayType == EVoxelPlayType::Game ? World->bEnableMultiplayer : false) + , bEnableUndoRedo(PlayType == EVoxelPlayType::Game ? World->bEnableUndoRedo : true) +{ +} + +FVoxelDataSettings::FVoxelDataSettings( + int32 Depth, + const TVoxelSharedRef& Generator, + bool bEnableMultiplayer, + bool bEnableUndoRedo) + : Depth(ClampDataDepth(Depth)) + , WorldBounds(FVoxelUtilities::GetBoundsFromDepth(this->Depth)) + , Generator(Generator) + , bEnableMultiplayer(bEnableMultiplayer) + , bEnableUndoRedo(bEnableUndoRedo) +{ + +} + +FVoxelDataSettings::FVoxelDataSettings( + const FVoxelIntBox& WorldBounds, + const TVoxelSharedRef& Generator, + bool bEnableMultiplayer, + bool bEnableUndoRedo) + : Depth(ClampDataDepth(FVoxelUtilities::GetOctreeDepthContainingBounds(WorldBounds))) + , WorldBounds(WorldBounds) + , Generator(Generator) + , bEnableMultiplayer(bEnableMultiplayer) + , bEnableUndoRedo(bEnableUndoRedo) +{ + +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelData::FVoxelData(const FVoxelDataSettings& Settings) + : IVoxelData(Settings.Depth, Settings.WorldBounds, Settings.bEnableMultiplayer, Settings.bEnableUndoRedo, Settings.Generator) + , Octree(MakeUnique(Depth)) +{ + check(Depth > 0); + check(Octree->GetBounds().Contains(WorldBounds)); +} + +TVoxelSharedRef FVoxelData::Create(const FVoxelDataSettings& Settings, int32 DataOctreeInitialSubdivisionDepth) +{ + auto* Data = new FVoxelData(Settings); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Subdivide Data"); + // Subdivide tree a few levels on start to avoid having update tasks locking the entire octree + FVoxelOctreeUtilities::IterateEntireTree(Data->GetOctree(), [&](FVoxelDataOctreeBase& Chunk) + { + if (!Chunk.IsLeaf() && Settings.Depth - Chunk.Height < DataOctreeInitialSubdivisionDepth) + { + Chunk.AsParent().CreateChildren(); + } + }); + } + return MakeShareable(Data); +} + +TVoxelSharedRef FVoxelData::Clone() const +{ + return MakeShareable(new FVoxelData(FVoxelDataSettings(WorldBounds, Generator, bEnableMultiplayer, bEnableUndoRedo))); +} + +FVoxelData::~FVoxelData() +{ + ClearData(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelDataOctreeLocker +{ +public: + const EVoxelLockType LockType; + const FVoxelIntBox Bounds; + const FName Name; + + FVoxelDataOctreeLocker(EVoxelLockType LockType, const FVoxelIntBox& Bounds, FName Name) + : LockType(LockType) + , Bounds(Bounds) + , Name(Name) + { + } + + TArray Lock(FVoxelDataOctreeBase& Octree) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (!Octree.GetBounds().Intersect(Bounds)) + { + return {}; + } + + LockImpl(Octree); + +#if VOXEL_DEBUG + FVoxelOctreeUtilities::IterateTreeInBounds(Octree, Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeafOrHasNoChildren()) + { + if (LockType == EVoxelLockType::Read) + { + ensureThreadSafe(Tree.IsLockedForRead()); + } + else + { + ensureThreadSafe(Tree.IsLockedForWrite()); + } + } + }); +#endif + + return MoveTemp(LockedOctrees); + } + +private: + TArray LockedOctrees; + + void LockImpl(FVoxelDataOctreeBase& Octree) + { + checkVoxelSlow(Bounds.Intersect(Octree.GetBounds())); + + Octree.Mutex.Lock(LockType); + + // Need to be locked to check IsLeafOrHasNoChildren + if (Octree.IsLeafOrHasNoChildren()) + { + LockedOctrees.Add(Octree.GetId()); + } + else + { + Octree.Mutex.Unlock(LockType); + + auto& Parent = Octree.AsParent(); + for (auto& Child : Parent.GetChildren()) + { + if (Child.GetBounds().Intersect(Bounds)) + { + LockImpl(Child); + } + } + } + } +}; +class FVoxelDataOctreeUnlocker +{ +public: + const EVoxelLockType LockType; + const TArray& LockedOctrees; + + FVoxelDataOctreeUnlocker(EVoxelLockType LockType, const TArray& LockedOctrees) + : LockType(LockType) + , LockedOctrees(LockedOctrees) + { + } + + void Unlock(FVoxelDataOctreeBase& Octree) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + UnlockImpl(Octree); + check(LockedOctreesIndex == LockedOctrees.Num()); + } + +private: + int32 LockedOctreesIndex = 0; + + void UnlockImpl(FVoxelDataOctreeBase& Octree) + { + if (LockedOctreesIndex == LockedOctrees.Num()) + { + return; + } + + if (LockedOctrees[LockedOctreesIndex] == Octree.GetId()) + { + LockedOctreesIndex++; + + ensure( + !LockedOctrees.IsValidIndex(LockedOctreesIndex) || + !Octree.IsInOctree(LockedOctrees[LockedOctreesIndex].Position)); + + Octree.Mutex.Unlock(LockType); + } + else if (Octree.IsInOctree(LockedOctrees[LockedOctreesIndex].Position)) + { + checkVoxelSlow(LockedOctrees[LockedOctreesIndex].Height < Octree.Height); + checkVoxelSlow(!Octree.IsLeaf()); + auto& Parent = Octree.AsParent(); + for (auto& Child : Parent.GetChildren()) + { + UnlockImpl(Child); + } + } + } +}; + +TUniquePtr FVoxelData::Lock(EVoxelLockType LockType, const FVoxelIntBox& Bounds, FName Name) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + ensure(Bounds.IsValid()); + + MainLock.Lock(EVoxelLockType::Read); + + auto LockInfo = TUniquePtr(new FVoxelDataLockInfo()); + LockInfo->Name = Name; + LockInfo->LockType = LockType; + LockInfo->LockedOctrees = FVoxelDataOctreeLocker(LockType, Bounds, Name).Lock(GetOctree()); + return LockInfo; +} + +void FVoxelData::Unlock(TUniquePtr LockInfo) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(LockInfo.IsValid()); + + FVoxelDataOctreeUnlocker(LockInfo->LockType, LockInfo->LockedOctrees).Unlock(GetOctree()); + + MainLock.Unlock(EVoxelLockType::Read); + + LockInfo->LockedOctrees.Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelData::ClearData() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + MainLock.Lock(EVoxelLockType::Write); + { + // Clear the data to have clean memory reports + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + Leaf.GetData().ClearData(*this); + Leaf.GetData().ClearData(*this); + }); + + ensure(GetDirtyMemory().Values.GetValue() == 0); + ensure(GetDirtyMemory().Materials.GetValue() == 0); + + ensure(GetCachedMemory().Values.GetValue() == 0); + ensure(GetCachedMemory().Materials.GetValue() == 0); + + Octree = MakeUnique(Depth); + } + MainLock.Unlock(EVoxelLockType::Write); + + UndoRedo = {}; + MarkAsDirty(); + +#define CLEAR(Type, Stat) \ + { \ + auto& ItemsData = GetItemsData(); \ + FScopeLock Lock(&ItemsData.Section); \ + DEC_DWORD_STAT_BY(Stat, ItemsData.Items.Num()); \ + ItemsData.Items.Reset(); \ + } + + CLEAR(FVoxelAssetItem, STAT_NumVoxelAssetItems); + CLEAR(FVoxelDisableEditsBoxItem, STAT_NumVoxelDisableEditsItems); + CLEAR(FVoxelDataItem, STAT_NumVoxelDataItems); + +#undef CLEAR +} + +void FVoxelData::ClearOctreeData(TArray& OutBoundsToUpdate) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForWrite()); + bool bUpdate = false; + if (Leaf.GetData().IsDirty()) + { + bUpdate = true; + Leaf.GetData().ClearData(*this); + } + if (Leaf.GetData().IsDirty()) + { + bUpdate = true; + Leaf.GetData().ClearData(*this); + } + if (bUpdate) + { + OutBoundsToUpdate.Add(Leaf.GetBounds()); + } + }); +} + +template +void FVoxelData::CheckIsSingle(const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelOctreeUtilities::IterateLeavesInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForWrite()); + Leaf.GetData().Compress(*this); + }); +} + +template VOXEL_API void FVoxelData::CheckIsSingle(const FVoxelIntBox&); +template VOXEL_API void FVoxelData::CheckIsSingle(const FVoxelIntBox&); + +template +void FVoxelData::Get(TVoxelQueryZone& GlobalQueryZone, int32 LOD) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + // TODO this is very inefficient for high LODs as we don't early exit when we already know we won't be reading any data in the chunk + // TODO BUG: this is also querying data multiple times if we have edited data! + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), GlobalQueryZone.Bounds, [&](FVoxelDataOctreeBase& InOctree) + { + if (!InOctree.IsLeafOrHasNoChildren()) return; + ensureThreadSafe(InOctree.IsLockedForRead()); + + auto QueryZone = GlobalQueryZone.ShrinkTo(InOctree.GetBounds()); + + if (InOctree.IsLeaf()) + { + auto& Data = InOctree.AsLeaf().GetData(); + if (Data.HasData()) + { + VOXEL_SLOW_SCOPE_COUNTER("Copy Data"); + const FIntVector Min = InOctree.GetMin(); + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + const int32 Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(Min, X, Y, Z); + QueryZone.Set(X, Y, Z, Data.Get(Index)); + } + } + } + return; + } + } + + InOctree.GetFromGeneratorAndAssets(*Generator, QueryZone, LOD); + }); + + // Handle data outside of the world bounds + // Can happen on edges with marching cubes, as it's querying N + 1 voxels with N a power of 2 + // Note that we should probably use WorldBounds here, but doing so with a correct handling of Step is quite complex + const FVoxelIntBox OctreeBounds = Octree->GetBounds(); + check(OctreeBounds.IsMultipleOf(GlobalQueryZone.Step)); + if (!OctreeBounds.Contains(GlobalQueryZone.Bounds)) + { + for (auto& LocalBounds : GlobalQueryZone.Bounds.Difference(OctreeBounds)) + { + check(LocalBounds.IsMultipleOf(GlobalQueryZone.Step)); + auto LocalQueryZone = GlobalQueryZone.ShrinkTo(LocalBounds); + for (VOXEL_QUERY_ZONE_ITERATE(LocalQueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(LocalQueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(LocalQueryZone, Z)) + { + // Get will handle clamping to the world bounds + LocalQueryZone.Set(X, Y, Z, Get(X, Y, Z, LOD)); + } + } + } + } + } +} + +template VOXEL_API void FVoxelData::Get(TVoxelQueryZone&, int32) const; +template VOXEL_API void FVoxelData::Get(TVoxelQueryZone&, int32) const; + +TVoxelRange FVoxelData::GetValueRange(const FVoxelIntBox& InBounds, int32 LOD) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + ensure(InBounds.IsValid()); + + const auto Apply = [&](FVoxelDataOctreeBase& Tree) + { + const auto TreeBounds = Tree.GetBounds(); + ensureVoxelSlowNoSideEffects(InBounds.Intersect(TreeBounds)); + + const auto QueryBounds = InBounds.Overlap(TreeBounds); + ensureVoxelSlowNoSideEffects(QueryBounds.IsValid()); + + if (Tree.IsLeaf()) + { + auto& Data = Tree.AsLeaf().GetData(); + if (Data.IsSingleValue()) + { + return TVoxelRange(Data.GetSingleValue()); + } + if (Data.IsDirty()) + { + // Could also store the data bounds, but that would require to track it when editing. Probably not worth the added cost. + return TVoxelRange::Infinite(); + } + } + + auto& ItemHolder = Tree.GetItemHolder(); + + TOptional> Range; + for (int32 Index = ItemHolder.GetAssetItems().Num() - 1; Index >= 0; Index--) + { + auto& Asset = *ItemHolder.GetAssetItems()[Index]; + + if (!Asset.Bounds.Intersect(QueryBounds)) continue; + + const auto AssetRangeFlt = Asset.Generator->GetValueRange_Transform( + Asset.LocalToWorld, + Asset.Bounds.Overlap(QueryBounds), + LOD, + FVoxelItemStack(ItemHolder, *Generator, Index)); + const auto AssetRange = TVoxelRange(AssetRangeFlt); + + if (!Range.IsSet()) + { + Range = AssetRange; + } + else + { + Range = TVoxelRange::Union(Range.GetValue(), AssetRange); + } + + if (Asset.Bounds.Contains(QueryBounds)) + { + // This one is covering everything, no need to continue deeper in the stack nor to check the generator + return Range.GetValue(); + } + } + + // Note: need to query individual bounds as ItemHolder might be different + const auto GeneratorRangeFlt = Generator->GetValueRange(QueryBounds, LOD, FVoxelItemStack(ItemHolder)); + const auto GeneratorRange = TVoxelRange(GeneratorRangeFlt); + if (!Range.IsSet()) + { + return GeneratorRange; + } + else + { + return TVoxelRange::Union(Range.GetValue(), GeneratorRange); + } + }; + const auto Reduction = [](auto RangeA, auto RangeB) + { + return TVoxelRange::Union(RangeA, RangeB); + }; + + // Note: even if WorldBounds doesn't contain InBounds, we don't need to check other values are the queries are always clamped to world bounds + const auto Result = FVoxelOctreeUtilities::ReduceInBounds>(GetOctree(), WorldBounds.Clamp(InBounds), Apply, Reduction); + + ensure(Result.IsSet()); + return Result.Get(FVoxelValue::Empty()); +} + +TVoxelRange FVoxelData::GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& InBounds, int32 LOD) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + ensure(InBounds.IsValid()); + + const auto Apply = [&](FVoxelDataOctreeBase& Tree) + { + const auto QueryBounds = InBounds.Overlap(Tree.GetBounds()); + + auto& ItemHolder = Tree.GetItemHolder(); + + TOptional> Range; + for (int32 Index = ItemHolder.GetAssetItems().Num() - 1; Index >= 0; Index--) + { + auto& Asset = *ItemHolder.GetAssetItems()[Index]; + + if (!Asset.Bounds.Intersect(InBounds)) continue; + + const auto AssetRange = Asset.Generator->GetCustomOutputRange_Transform( + Asset.LocalToWorld, + DefaultValue, + Name, + InBounds, + LOD, + FVoxelItemStack(ItemHolder, *Generator, Index)); + + if (!Range.IsSet()) + { + Range = AssetRange; + } + else + { + Range = TVoxelRange::Union(Range.GetValue(), AssetRange); + } + + if (Asset.Bounds.Contains(QueryBounds)) + { + // This one is covering everything, no need to continue deeper in the stack nor to check the generator + return Range.GetValue(); + } + } + + // Note: need to query individual bounds as ItemHolder might be different + const auto GeneratorRange = Generator->GetCustomOutputRange(DefaultValue, Name, QueryBounds, LOD, FVoxelItemStack(ItemHolder)); + if (!Range.IsSet()) + { + return GeneratorRange; + } + else + { + return TVoxelRange::Union(Range.GetValue(), GeneratorRange); + } + }; + const auto Reduction = [](auto RangeA, auto RangeB) + { + return TVoxelRange::Union(RangeA, RangeB); + }; + + const auto Result = FVoxelOctreeUtilities::ReduceInBounds>(GetOctree(), InBounds, Apply, Reduction); + // TODO: check outside of octree as well? + return Result.Get(DefaultValue); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelData::GetSave(FVoxelUncompressedWorldSaveImpl& OutSave, TArray& OutObjects) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelReadScopeLock Lock(*this, FVoxelIntBox::Infinite, "GetSave"); + + FVoxelSaveBuilder Builder(Depth); + + TArray>> BuffersToDelete; + + FVoxelOctreeUtilities::IterateAllLeaves(*Octree, [&](FVoxelDataOctreeLeaf& Leaf) + { + TVoxelDataOctreeLeafData* ValuesPtr = &Leaf.Values; + + if (CVarStoreSpecialValueForGeneratorValuesInSaves.GetValueOnGameThread() != 0) + { + VOXEL_ASYNC_SCOPE_COUNTER("Diffing with generator"); + + // Only if dirty and not compressed to a single value + if (Leaf.Values.IsDirty() && !Leaf.Values.IsSingleValue()) + { + auto UniquePtr = MakeUnique>(); + UniquePtr->CreateData(*this); + UniquePtr->SetIsDirty(true, *this); + + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + LeafBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(LeafBounds.Min, X, Y, Z); + const FVoxelValue Value = Leaf.Values.Get(Index); + // Empty stack: items not loaded when loading in LoadFromSave + const FVoxelValue GeneratorValue = Generator->Get(X, Y, Z, 0, FVoxelItemStack::Empty); + + if (GeneratorValue == Value) + { + UniquePtr->GetRef(Index) = FVoxelValue::Special(); + } + else + { + UniquePtr->GetRef(Index) = Value; + } + }); + + UniquePtr->TryCompressToSingleValue(*this); + ValuesPtr = UniquePtr.Get(); + BuffersToDelete.Emplace(MoveTemp(UniquePtr)); + } + } + + Builder.AddChunk(Leaf.Position, *ValuesPtr, Leaf.Materials); + }); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Items"); + + for (auto& Item : AssetItemsData.Items) + { + Builder.AddAssetItem(Item->Item); + } + } + + Builder.Save(OutSave, OutObjects); + + VOXEL_ASYNC_SCOPE_COUNTER("ClearData"); + for (auto& Buffer : BuffersToDelete) + { + // For correct memory reports + Buffer->ClearData(*this); + } +} + +bool FVoxelData::LoadFromSave(const FVoxelUncompressedWorldSaveImpl& Save, const FVoxelPlaceableItemLoadInfo& LoadInfo, TArray* OutBoundsToUpdate) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (OutBoundsToUpdate) + { + FVoxelWriteScopeLock Lock(*this, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateEntireTree(*Octree, [&](auto& Tree) + { + if (Tree.IsLeafOrHasNoChildren()) + { + OutBoundsToUpdate->Add(Tree.GetBounds()); + } + }); + } + + // Will replace the octree + ClearData(); + ClearDirtyFlag(); // Set by ClearData + + FVoxelWriteScopeLock Lock(*this, FVoxelIntBox::Infinite, FUNCTION_FNAME); + + FVoxelSaveLoader Loader(Save); + + int32 ChunkIndex = 0; + FVoxelOctreeUtilities::IterateEntireTree(*Octree, [&](FVoxelDataOctreeBase& Tree) + { + if (ChunkIndex == Loader.NumChunks()) + { + return; + } + + const FVoxelIntBox OctreeBounds = Tree.GetBounds(); + const FIntVector CurrentPosition = Loader.GetChunkPosition(ChunkIndex); + if (Tree.IsLeaf()) + { + auto& Leaf = Tree.AsLeaf(); + if (CurrentPosition == Tree.Position) + { + Loader.ExtractChunk(ChunkIndex, *this, Leaf.Values, Leaf.Materials); + + if (CVarStoreSpecialValueForGeneratorValuesInSaves.GetValueOnGameThread() != 0) + { + VOXEL_ASYNC_SCOPE_COUNTER("Loading generator values"); + + // If we are dirty and we are not a single value, or if we are a single special value + if (Leaf.Values.IsDirty() && (!Leaf.Values.IsSingleValue() || Leaf.Values.GetSingleValue() == FVoxelValue::Special())) + { + if (Leaf.Values.IsSingleValue()) + { + Leaf.Values.ExpandSingleValue(*this); + } + + OctreeBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(OctreeBounds.Min, X, Y, Z); + FVoxelValue& Value = Leaf.Values.GetRef(Index); + + if (Value == FVoxelValue::Special()) + { + // Use the generator value, ignoring all assets and items as they are not loaded + // The same is done when checking on save + Value = Generator->Get(X, Y, Z, 0, FVoxelItemStack::Empty); + } + }); + + Leaf.Values.TryCompressToSingleValue(*this); + } + } + + ChunkIndex++; + if (OutBoundsToUpdate) + { + OutBoundsToUpdate->Add(OctreeBounds); + } + } + } + else + { + auto& Parent = Tree.AsParent(); + if (OctreeBounds.Contains(CurrentPosition) && !Parent.HasChildren()) + { + Parent.CreateChildren(); + } + } + }); + check(ChunkIndex == Loader.NumChunks() || Save.GetDepth() > Depth); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Load items"); + TArray AssetItems; + Loader.GetPlaceableItems(LoadInfo, AssetItems); + for (auto& AssetItem : AssetItems) + { + AddItem(AssetItem); + } + } + + return !Loader.GetError(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelData::GetDiffs(TArray>& OutValueDiffQueue, TArray>& OutMaterialDiffQueue) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelReadScopeLock Lock(*this, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + // TODO: array of dirty chunks instead of whole octree iteration + ensureThreadSafe(Leaf.IsLockedForRead()); + if (Leaf.Multiplayer.IsValid()) + { + if (Leaf.Multiplayer->IsNetworkDirty()) + { + auto& Diff = OutValueDiffQueue.Emplace_GetRef(Leaf.Position); + Leaf.Multiplayer->AddToDiffQueueAndReset(Leaf.Values, Diff.Diffs); + } + if (Leaf.Multiplayer->IsNetworkDirty()) + { + auto& Diff = OutMaterialDiffQueue.Emplace_GetRef(Leaf.Position); + Leaf.Multiplayer->AddToDiffQueueAndReset(Leaf.Materials, Diff.Diffs); + } + } + }); +} + +template +inline void LoadFromDiffsImpl(FVoxelData& Data, TArray>& Queue, TArray& OutModifiedBounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (auto& ChunkDiff : Queue) + { + auto Bounds = FVoxelIntBox(ChunkDiff.Position - FIntVector(DATA_CHUNK_SIZE / 2), ChunkDiff.Position + FIntVector(DATA_CHUNK_SIZE / 2)); + FVoxelWriteScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + + FVoxelDataOctreeLeaf& Leaf = + *FVoxelOctreeUtilities::GetLeaf( + Data.GetOctree(), + ChunkDiff.Position); + + ensureThreadSafe(Leaf.IsLockedForWrite()); + check(Leaf.Position == ChunkDiff.Position); + check(ChunkDiff.Diffs.Num() > 0); + + Leaf.InitForEdit(Data); + + auto& DataHolder = Leaf.GetData(); + DataHolder.SetIsDirty(true, Data); + for (auto& Diff : ChunkDiff.Diffs) + { + checkVoxelSlow(0 <= Diff.Index && Diff.Index < VOXELS_PER_DATA_CHUNK); + DataHolder.GetRef(Diff.Index) = Diff.Value; + } + + OutModifiedBounds.Emplace(Leaf.GetBounds()); + } +} + +void FVoxelData::LoadFromDiffs(TArray>& ValueDiffQueue, TArray>& MaterialDiffQueue, TArray& OutModifiedBounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + LoadFromDiffsImpl(*this, ValueDiffQueue, OutModifiedBounds); + LoadFromDiffsImpl(*this, MaterialDiffQueue, OutModifiedBounds); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_UNDO_REDO_IMPL(Return, Dummy) check(IsInGameThread()); if(!ensure(bEnableUndoRedo)) { return Return; } +#define CHECK_UNDO_REDO() CHECK_UNDO_REDO_IMPL({},) +#define CHECK_UNDO_REDO_VOID() CHECK_UNDO_REDO_IMPL(,) + +static TAutoConsoleVariable CVarResetDataChunksWhenUndoingAddItem( + TEXT("voxel.data.ResetDataChunksWhenUndoingAddItem"), + 0, + TEXT("If true, will reset all data chunks affected by AddItem when undoing it. If false, these chunks will be left untouched. In both cases, undo is imperfect"), + ECVF_Default); + +bool FVoxelData::Undo(TArray& OutBoundsToUpdate) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_UNDO_REDO(); + + if (UndoRedo.HistoryPosition <= 0) + { + return false; + } + + MarkAsDirty(); + UndoRedo.HistoryPosition--; + + UndoRedo.RedoUniqueIds.Add(UndoRedo.CurrentFrameUniqueId); + UndoRedo.CurrentFrameUniqueId = UndoRedo.UndoUniqueIds.Pop(false); + + const auto Bounds = UndoRedo.UndoFramesBounds.Pop(); + UndoRedo.RedoFramesBounds.Add(Bounds); + + auto& LeavesWithRedoStack = UndoRedo.LeavesWithRedoStackStack.Emplace_GetRef(); + + FVoxelWriteScopeLock Lock(*this, Bounds, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateLeavesInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.UndoRedo.IsValid() && Leaf.UndoRedo->CanUndoRedo(UndoRedo.HistoryPosition)) + { + if (Leaf.UndoRedo->GetFramesStack().Num() == 0) + { + // Only add if this is the first redo to avoid duplicates +#if VOXEL_DEBUG + for (auto& It : UndoRedo.LeavesWithRedoStackStack) + { + ensure(!It.Contains(&Leaf)); + } +#endif + LeavesWithRedoStack.Add(&Leaf); + } + Leaf.UndoRedo->UndoRedo(*this, Leaf, UndoRedo.HistoryPosition); + OutBoundsToUpdate.Add(Leaf.GetBounds()); + } + }); + + return true; +} + +bool FVoxelData::Redo(TArray& OutBoundsToUpdate) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_UNDO_REDO(); + + if (UndoRedo.HistoryPosition >= UndoRedo.MaxHistoryPosition) + { + return false; + } + + MarkAsDirty(); + UndoRedo.HistoryPosition++; + + UndoRedo.UndoUniqueIds.Add(UndoRedo.CurrentFrameUniqueId); + UndoRedo.CurrentFrameUniqueId = UndoRedo.RedoUniqueIds.Pop(false); + + const auto Bounds = UndoRedo.RedoFramesBounds.Pop(); + UndoRedo.UndoFramesBounds.Add(Bounds); + + // We are redoing: pop redo stacks added by the last undo + if (ensure(UndoRedo.LeavesWithRedoStackStack.Num() > 0)) UndoRedo.LeavesWithRedoStackStack.Pop(false); + + FVoxelWriteScopeLock Lock(*this, Bounds, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateLeavesInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.UndoRedo.IsValid() && Leaf.UndoRedo->CanUndoRedo(UndoRedo.HistoryPosition)) + { + Leaf.UndoRedo->UndoRedo(*this, Leaf, UndoRedo.HistoryPosition); + OutBoundsToUpdate.Add(Leaf.GetBounds()); + } + }); + + return true; +} + +void FVoxelData::ClearFrames() +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_UNDO_REDO_VOID(); + + UndoRedo = {}; + + FVoxelWriteScopeLock Lock(*this, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.UndoRedo.IsValid()) + { + Leaf.UndoRedo->ClearFrames(Leaf); + } + }); +} + +void FVoxelData::SaveFrame(const FVoxelIntBox& Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_UNDO_REDO_VOID(); + + { +#if VOXEL_DEBUG + // Not thread safe, but for debug only so should be ok + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.UndoRedo.IsValid() && !Leaf.UndoRedo->IsCurrentFrameEmpty() && !Leaf.GetBounds().Intersect(Bounds)) + { + ensureMsgf(false, TEXT("Save Frame called on too small bounds! Input Bounds: %s; Leaf Bounds: %s"), *Bounds.ToString(), *Leaf.GetBounds().ToString()); + } + }); +#endif + + // Call SaveFrame on the leaves + { + FVoxelReadScopeLock Lock(*this, Bounds, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateLeavesInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForRead()); + if (Leaf.UndoRedo.IsValid()) + { + Leaf.UndoRedo->SaveFrame(Leaf, UndoRedo.HistoryPosition); + } + }); + } + + // Clear redo histories + for (auto& LeavesWithRedoStack : UndoRedo.LeavesWithRedoStackStack) + { + for (auto* Leaf : LeavesWithRedoStack) + { + if (ensure(Leaf->UndoRedo.IsValid())) + { + // Note: might be empty if we called Redo already + // Note: no need to lock, frame stacks are game thread only and a leaf cannot be destroyed without a global lock + Leaf->UndoRedo->GetFramesStack().Reset(); + } + } + } + UndoRedo.LeavesWithRedoStackStack.Reset(); + +#if VOXEL_DEBUG + // Not thread safe, but for debug only so should be ok + FVoxelOctreeUtilities::IterateAllLeaves(GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.UndoRedo.IsValid()) + { + ensure(Leaf.UndoRedo->GetFramesStack().Num() == 0); + ensure(Leaf.UndoRedo->IsCurrentFrameEmpty()); + } + }); +#endif + } + + // Important: do all that at the end as HistoryPosition is used above + + MarkAsDirty(); + + UndoRedo.HistoryPosition++; + UndoRedo.MaxHistoryPosition = UndoRedo.HistoryPosition; + + UndoRedo.UndoFramesBounds.Add(Bounds); + UndoRedo.RedoFramesBounds.Reset(); + + UndoRedo.UndoUniqueIds.Add(UndoRedo.CurrentFrameUniqueId); + UndoRedo.RedoUniqueIds.Reset(); + // Assign new unique id to this frame + UndoRedo.CurrentFrameUniqueId = UndoRedo.FrameUniqueIdCounter++; + + ensure(UndoRedo.UndoFramesBounds.Num() == UndoRedo.HistoryPosition); + ensure(UndoRedo.UndoUniqueIds.Num() == UndoRedo.HistoryPosition); +} + +bool FVoxelData::IsCurrentFrameEmpty() +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_UNDO_REDO(); + + FVoxelReadScopeLock Lock(*this, FVoxelIntBox::Infinite, "IsCurrentFrameEmpty"); + bool bValue = true; + FVoxelOctreeUtilities::IterateLeavesByPred(GetOctree(), [&](auto&) { return bValue; }, [&](auto& Leaf) + { + if (Leaf.UndoRedo.IsValid()) + { + bValue &= Leaf.UndoRedo->IsCurrentFrameEmpty(); + } + }); + return bValue; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelDataGeneratorInstance_AddAssetItem : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + const FVoxelData& Data; + explicit FVoxelDataGeneratorInstance_AddAssetItem(const FVoxelData& Data) + : Super(nullptr) + , Data(Data) + { + } + + virtual FVector GetUpVector(v_flt, v_flt, v_flt) const override final { return {}; } + + FORCEINLINE v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack&) const + { + // Not ideal, but w/e + return FVoxelDataUtilities::MakeBilinearInterpolatedData(Data).GetValue(X, Y, Z, LOD); + } + FORCEINLINE FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack&) const + { + return Data.GetMaterial(FMath::RoundToInt(X), FMath::RoundToInt(Y), FMath::RoundToInt(Z), LOD); + } + FORCEINLINE TVoxelRange GetValueRangeImpl(const FVoxelIntBox&, int32, const FVoxelItemStack&) const + { + return TVoxelRange::Infinite(); + } +}; + +void FVoxelDataUtilities::AddAssetItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelTransformableGeneratorInstance& Generator, + const FVoxelIntBox& Bounds, + const FTransform& LocalToWorld, + bool bModifyValues, + bool bModifyMaterials) +{ + if (bModifyValues) + { + Leaf.InitForEdit(Data); + } + if (bModifyMaterials) + { + Leaf.InitForEdit(Data); + } + + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + const FVoxelIntBox BoundsToEdit = LeafBounds.Overlap(Bounds); + + const FVoxelDataGeneratorInstance_AddAssetItem PtrGenerator(Data); + const FVoxelItemStack ItemStack(Leaf.GetItemHolder(), PtrGenerator, 0); + + TArray ValuesBuffer; + TArray MaterialsBuffer; + + const auto WriteAssetDataToBuffer = [&](auto& Buffer) + { + using T = typename TDecay::Type::ElementType; + + Buffer.SetNumUninitialized(BoundsToEdit.Count()); + + Leaf.GetData().SetIsDirty(true, Data); + + TVoxelQueryZone QueryZone(BoundsToEdit, Buffer.GetData()); + Generator.Get_Transform( + LocalToWorld, + QueryZone, + 0, + ItemStack); + }; + if (bModifyValues) WriteAssetDataToBuffer(ValuesBuffer); + if (bModifyMaterials) WriteAssetDataToBuffer(MaterialsBuffer); + + // Need to first write both of them, as the item stack is referencing the data + + const FIntVector Size = BoundsToEdit.Size(); + FVoxelDataOctreeSetter::Set(Data, Leaf, [&](auto Lambda) { BoundsToEdit.Iterate(Lambda); }, + [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material) + { + const int32 Index = FVoxelUtilities::Get3DIndex(Size, X, Y, Z, BoundsToEdit.Min); + if (bModifyValues) + { + Value = FVoxelUtilities::Get(ValuesBuffer, Index); + } + if (bModifyMaterials) + { + Material = FVoxelUtilities::Get(MaterialsBuffer, Index); + } + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataAccelerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataAccelerator.cpp new file mode 100644 index 00000000..782a5db4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataAccelerator.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelDataAccelerator.h" +#include "HAL/IConsoleManager.h" + +static TAutoConsoleVariable CVarCacheSize( + TEXT("voxel.data.DataAccelerator.CacheSize"), + 8, + TEXT("Size of the data accelerator cache"), + ECVF_Default); +static TAutoConsoleVariable CVarUseAcceleratorMap( + TEXT("voxel.data.DataAccelerator.UseMap"), + 1, + TEXT("Whether to cache the leaves in a map"), + ECVF_Default); +static TAutoConsoleVariable CVarShowStats( + TEXT("voxel.data.DataAccelerator.LogStats"), + 0, + TEXT("Log stats about accelerators hit/misses"), + ECVF_Default); + +int32 FVoxelDataAcceleratorParameters::GetDefaultCacheSize() +{ + return CVarCacheSize.GetValueOnAnyThread(); +} +bool FVoxelDataAcceleratorParameters::GetUseAcceleratorMap() +{ + return CVarUseAcceleratorMap.GetValueOnAnyThread() != 0; +} +bool FVoxelDataAcceleratorParameters::GetShowStats() +{ + return CVarShowStats.GetValueOnAnyThread() != 0; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctree.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctree.cpp new file mode 100644 index 00000000..87977ec3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctree.cpp @@ -0,0 +1,166 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelDataOctree.h" +#include "VoxelData/VoxelDataUtilities.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataOctreesMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelUndoRedoMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelMultiplayerMemory); +DEFINE_STAT(STAT_VoxelDataOctreesCount); + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataOctreeDirtyValuesMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataOctreeDirtyMaterialsMemory); + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataOctreeCachedValuesMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelDataOctreeCachedMaterialsMemory); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +T FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, U X, U Y, U Z, int32 LOD) const +{ + ensureThreadSafe(IsLockedForRead()); + check(IsLeafOrHasNoChildren()); + + const auto& Assets = ItemHolder->GetAssetItems(); + for (int32 Index = Assets.Num() - 1; Index >= 0; Index--) + { + auto& Asset = *Assets[Index]; + if (Asset.Bounds.ContainsTemplate(X, Y, Z)) + { + return Asset.Generator->Get_Transform(Asset.LocalToWorld, X, Y, Z, LOD, FVoxelItemStack(*ItemHolder, Generator, Index)); + } + } + return Generator.Get(X, Y, Z, LOD, FVoxelItemStack(*ItemHolder)); +} + +template VOXEL_API v_flt FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, v_flt X, v_flt Y, v_flt Z, int32 LOD) const; +template VOXEL_API v_flt FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, int32 X, int32 Y, int32 Z, int32 LOD) const; +template VOXEL_API FVoxelValue FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, int32 X, int32 Y, int32 Z, int32 LOD) const; +template VOXEL_API FVoxelMaterial FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, int32 X, int32 Y, int32 Z, int32 LOD) const; + +template +void FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, TVoxelQueryZone& QueryZone, int32 LOD) const +{ + ensureThreadSafe(IsLockedForRead()); + check(IsLeafOrHasNoChildren()); + + const auto& Assets = ItemHolder->GetAssetItems(); + + if (Assets.Num() == 0) + { + VOXEL_SLOW_SCOPE_COUNTER("Query Generator"); + Generator.Get(QueryZone, LOD, FVoxelItemStack(*ItemHolder)); + return; + } + + for (int32 Index = Assets.Num() - 1; Index >= 0; Index--) + { + auto& Asset = *Assets[Index]; + if (QueryZone.Bounds.Contains(Asset.Bounds)) + { + VOXEL_SLOW_SCOPE_COUNTER("Query Asset"); + Asset.Generator->Get_Transform(Asset.LocalToWorld, QueryZone, LOD, FVoxelItemStack(*ItemHolder, Generator, Index)); + return; + } + if (QueryZone.Bounds.Intersect(Asset.Bounds)) + { + break; + } + } + + VOXEL_SLOW_SCOPE_COUNTER("Individual Asset & Generator Queries"); + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + T Value; + for (int32 Index = Assets.Num() - 1; Index >= 0; Index--) + { + auto& Asset = *Assets[Index]; + if (Asset.Bounds.Contains(X, Y, Z)) + { + Value = Asset.Generator->Get_Transform(Asset.LocalToWorld, X, Y, Z, LOD, FVoxelItemStack(*ItemHolder, Generator, Index)); + break; + } + if (Index == 0) + { + Value = Generator.Get(X, Y, Z, LOD, FVoxelItemStack(*ItemHolder)); + } + } + QueryZone.Set(X, Y, Z, Value); + } + } + } +} + +template VOXEL_API void FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, TVoxelQueryZone& QueryZone, int32 LOD) const; +template VOXEL_API void FVoxelDataOctreeBase::GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, TVoxelQueryZone& QueryZone, int32 LOD) const; + +template +T FVoxelDataOctreeBase::GetCustomOutput(const FVoxelGeneratorInstance& Generator, T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const +{ + ensureThreadSafe(IsLockedForRead()); + check(IsLeafOrHasNoChildren()); + + const auto& Assets = ItemHolder->GetAssetItems(); + for (int32 Index = Assets.Num() - 1; Index >= 0; Index--) + { + auto& Asset = *Assets[Index]; + if (Asset.Bounds.ContainsTemplate(X, Y, Z)) + { + return Asset.Generator->GetCustomOutput_Transform(Asset.LocalToWorld, DefaultValue, Name, X, Y, Z, LOD, FVoxelItemStack(*ItemHolder, Generator, Index)); + } + } + return Generator.GetCustomOutput(DefaultValue, Name, X, Y, Z, LOD, FVoxelItemStack(*ItemHolder)); +} + +template VOXEL_API v_flt FVoxelDataOctreeBase::GetCustomOutput(const FVoxelGeneratorInstance&, v_flt, FName, v_flt, v_flt, v_flt, int32) const; +template VOXEL_API int32 FVoxelDataOctreeBase::GetCustomOutput(const FVoxelGeneratorInstance&, int32, FName, v_flt, v_flt, v_flt, int32) const; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataOctreeParent::CreateChildren() +{ + TVoxelOctreeParent::CreateChildren(); + +#if DO_THREADSAFE_CHECKS + for (auto& Child : AsParent().GetChildren()) + { + Child.Parent = this; + } +#endif + + if (ItemHolder->NumItems() > 0) + { + for (auto& Child : AsParent().GetChildren()) + { + const FVoxelIntBox ChildBounds = Child.GetBounds(); + ItemHolder->ApplyToAllItems([&](auto& Item) + { + if (Item.Bounds.Intersect(ChildBounds)) + { + Child.ItemHolder->AddItem(Item); + } + }); + } + } + ItemHolder.Reset(); +} + +void FVoxelDataOctreeParent::DestroyChildren() +{ + TVoxelOctreeParent::DestroyChildren(); + + check(!ItemHolder.IsValid()); + // Always valid on a node with no children + ItemHolder = MakeUnique(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctreeLeafUndoRedo.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctreeLeafUndoRedo.cpp new file mode 100644 index 00000000..e01cf6ad --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelDataOctreeLeafUndoRedo.cpp @@ -0,0 +1,157 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelDataOctreeLeafUndoRedo.h" +#include "VoxelData/VoxelDataOctree.h" + +FVoxelDataOctreeLeafUndoRedo::FVoxelDataOctreeLeafUndoRedo(const FVoxelDataOctreeLeaf& Leaf) + : CurrentFrame(MakeUnique(Leaf)) +{ + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, sizeof(FVoxelDataOctreeLeafUndoRedo)); +} + +FVoxelDataOctreeLeafUndoRedo::~FVoxelDataOctreeLeafUndoRedo() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, sizeof(FVoxelDataOctreeLeafUndoRedo)); +} + +void FVoxelDataOctreeLeafUndoRedo::ClearFrames(const FVoxelDataOctreeLeaf& Leaf) +{ + CurrentFrame = MakeUnique(Leaf); + UndoFramesStack.Empty(); + RedoFramesStack.Empty(); +} + +void FVoxelDataOctreeLeafUndoRedo::SaveFrame(const FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition) +{ + VOXEL_SLOW_FUNCTION_COUNTER(); + + if (!CurrentFrame->IsEmpty()) + { + CurrentFrame->HistoryPosition = HistoryPosition; + AddFrameToStack(CurrentFrame); + check(!CurrentFrame); + + CurrentFrame = MakeUnique(Leaf); + + AlreadyModified.Values.Clear(); + AlreadyModified.Materials.Clear(); + } + if (RedoFramesStack.Num() > 0) + { + RedoFramesStack.Empty(); + } +} + +template +void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType() +{ + const auto ClearFrame = [](FFrame& Frame) + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Frame).Empty(); + }; + + ClearFrame(*CurrentFrame); + for (auto& Frame : UndoFramesStack) + { + ClearFrame(*Frame); + } + for (auto& Frame : RedoFramesStack) + { + ClearFrame(*Frame); + } +} + +template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType(); +template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType(); + +template +void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData& Data, FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition) +{ + check(CurrentFrame->IsEmpty()); + check(CanUndoRedo(HistoryPosition)); + + const TUniquePtr Frame = GetFramesStack().Pop(false); + check(Frame->HistoryPosition == HistoryPosition); + + TUniquePtr NewFrame = MakeUnique(Leaf); + // If Type is Undo NewFrame is a redo frame, so + 1. Else it's an undo frame so -1 + NewFrame->HistoryPosition = HistoryPosition + (Type == EVoxelUndoRedo::Undo ? 1 : -1); + + check(!Frame->IsEmpty()); + + const auto Apply = [&](auto TypeInst) + { + using T = decltype(TypeInst); + + const TArray>& FrameData = FVoxelUtilities::TValuesMaterialsSelector::Get(*Frame); + TArray>& NewFrameData = FVoxelUtilities::TValuesMaterialsSelector::Get(*NewFrame); + TVoxelDataOctreeLeafData& DataHolder = FVoxelUtilities::TValuesMaterialsSelector::Get(Leaf); + + if (FrameData.Num() == 0) return; + + if (!DataHolder.HasData()) + { + // Data was reverted to generator value + + DataHolder.CreateData(Data, [&](T* RESTRICT DataPtr) + { + TVoxelQueryZone QueryZone(Leaf.GetBounds(), DataPtr); + Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + }); + } + DataHolder.PrepareForWrite(Data); + + NewFrameData.SetNumUninitialized(FrameData.Num()); + + const TModifiedValue* RESTRICT const FrameDataPtr = FrameData.GetData(); + TModifiedValue* RESTRICT const NewFrameDataPtr = NewFrameData.GetData(); + + checkVoxelSlow(FrameDataPtr); + checkVoxelSlow(NewFrameDataPtr); + + for (int32 Index = 0; Index < FrameData.Num(); Index++) + { + checkVoxelSlow(FrameData.IsValidIndex(Index)); + checkVoxelSlow(NewFrameData.IsValidIndex(Index)); + + const auto ModifiedValue = FrameDataPtr[Index]; + auto& ValueRef = DataHolder.GetRef(ModifiedValue.Index); + + NewFrameDataPtr[Index] = TModifiedValue(ModifiedValue.Index, ValueRef); + ValueRef = ModifiedValue.Value; + } + + if (TIsSame::Value) DataHolder.SetIsDirty(Frame->bValuesDirty, Data); + if (TIsSame::Value) DataHolder.SetIsDirty(Frame->bMaterialsDirty, Data); + }; + + Apply(FVoxelValue()); + Apply(FVoxelMaterial()); + + AddFrameToStack(NewFrame); +} + +template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData&, FVoxelDataOctreeLeaf&, int32); +template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData&, FVoxelDataOctreeLeaf&, int32); + +void FVoxelDataOctreeLeafUndoRedo::FFrame::UpdateStats() const +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); + AllocatedSize = sizeof(FFrame) + Values.GetAllocatedSize() + Materials.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); +} + +template +void FVoxelDataOctreeLeafUndoRedo::AddFrameToStack(TUniquePtr& Frame) +{ + { + VOXEL_SLOW_SCOPE_COUNTER("Shrink"); + Frame->Values.Shrink(); + Frame->Materials.Shrink(); + } + + Frame->UpdateStats(); + + GetFramesStack().Add(MoveTemp(Frame)); + check(!Frame); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSave.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSave.cpp new file mode 100644 index 00000000..addc4324 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSave.cpp @@ -0,0 +1,387 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelSave.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelMessages.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelUncompressedSavesMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelCompressedSavesMemory); + +struct FVoxelChunkSave32Bits +{ + FIntVector Position; + int32 ValuesIndex = -1; + int32 MaterialsIndex = -1; + int32 FoliageIndex = -1; + + friend FArchive& operator<<(FArchive& Ar, FVoxelChunkSave32Bits& Save) + { + Ar << Save.Position; + Ar << Save.ValuesIndex; + Ar << Save.MaterialsIndex; + Ar << Save.FoliageIndex; + return Ar; + } +}; + +struct FVoxelChunkSaveWithoutFoliage +{ + FIntVector Position; + int32 ValuesIndex; + int32 MaterialsIndex; + + FORCEINLINE friend FArchive& operator<<(FArchive& Ar, FVoxelChunkSaveWithoutFoliage& Save) + { + Ar << Save.Position; + Ar << Save.ValuesIndex; + Ar << Save.MaterialsIndex; + + return Ar; + } + + FORCEINLINE operator FVoxelChunkSave32Bits() const + { + return { Position, ValuesIndex, MaterialsIndex, -1 }; + } +}; + +struct FVoxelFoliage +{ + uint8 R; + uint8 G; + uint8 B; + uint8 A; + + inline friend FArchive& operator<<(FArchive& Ar, FVoxelFoliage& Foliage) + { + Ar << Foliage.R; + Ar << Foliage.G; + Ar << Foliage.B; + Ar << Foliage.A; + return Ar; + } +}; + +void FVoxelUncompressedWorldSaveImpl::UpdateAllocatedSize() const +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUncompressedSavesMemory, AllocatedSize); + AllocatedSize = + Chunks.GetAllocatedSize() + + ValueBuffers.GetAllocatedSize() + + MaterialBuffers.GetAllocatedSize() + + PlaceableItems.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUncompressedSavesMemory, AllocatedSize); +} + +bool FVoxelUncompressedWorldSaveImpl::Serialize(FArchive& Ar) +{ + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (Ar.IsSaving()) + { + Version = FVoxelSaveVersion::LatestVersion; + } + + // Serialize version & depth + { + int32 Dummy = 42; + Ar << Dummy; + if (Dummy == 42) // Trick to know the version, as Depth is always smaller than 42 + { + Ar << Version; + Ar << Depth; + } + else + { + Version = FVoxelSaveVersion::BeforeCustomVersionWasAdded; + Depth = Dummy; + } + } + + const auto SerializationVersion = + Version >= FVoxelSaveVersion::ValueConfigFlagAndSaveGUIDs + ? FVoxelSerializationVersion::ValueConfigFlagAndSaveGUIDs + : Version >= FVoxelSaveVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass + ? FVoxelSerializationVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass + : FVoxelSerializationVersion::BeforeCustomVersionWasAdded; + + static_assert(FVoxelSerializationVersion::LatestVersion == FVoxelSerializationVersion::SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, "Need to add a new FVoxelSaveVersion"); + + // Serialize GUID + if (Version >= FVoxelSaveVersion::ValueConfigFlagAndSaveGUIDs) + { + Ar << Guid; + } + else + { + Guid = FGuid::NewGuid(); + } + + // Serialize UserFlags + if (Version >= FVoxelSaveVersion::AddUserFlagsToSaves) + { + Ar << UserFlags; + } + else + { + UserFlags = 0; + } + + // Serialize value config + uint32 ValueConfigFlag = GVoxelValueConfigFlag; + if (Version >= FVoxelSaveVersion::ValueConfigFlagAndSaveGUIDs) + { + Ar << ValueConfigFlag; + } + + // Serialize material config + uint32 MaterialConfigFlag = GVoxelMaterialConfigFlag; + Ar << MaterialConfigFlag; + + // Serialize buffers + if (Version >= FVoxelSaveVersion::StoreMaterialChannelsIndividuallyAndRemoveFoliage) + { + // Serialize value buffers + FVoxelSerializationUtilities::SerializeValues(Ar, ValueBuffers, ValueConfigFlag, SerializationVersion); + FVoxelSerializationUtilities::SerializeValues(Ar, SingleValues, ValueConfigFlag, SerializationVersion); + + // Serialize material buffers + FVoxelSerializationUtilities::SerializeMaterials(Ar, MaterialsIndices, MaterialConfigFlag); + MaterialBuffers.BulkSerialize(Ar); + SingleMaterials.BulkSerialize(Ar); + + // Serialize chunks indices + // Note: make sure to not use BulkSerialize as data isn't aligned + Ar << Chunks; + } + else + { + TNoGrowArray OldMaterialBuffers; + TNoGrowArray OldSingleMaterials; + + // Serialize value buffers + FVoxelSerializationUtilities::SerializeValues(Ar, ValueBuffers, ValueConfigFlag, SerializationVersion); + + // Serialize material buffers + FVoxelSerializationUtilities::SerializeMaterials(Ar, OldMaterialBuffers, MaterialConfigFlag, SerializationVersion); + + // Serialize foliage buffers + if (Version >= FVoxelSaveVersion::FoliagePaint) + { + TArray FoliageBuffers; + FoliageBuffers.BulkSerialize(Ar); + } + + // Serialize single values buffers + if (Version >= FVoxelSaveVersion::SingleValues) + { + FVoxelSerializationUtilities::SerializeValues(Ar, SingleValues, ValueConfigFlag, SerializationVersion); + FVoxelSerializationUtilities::SerializeMaterials(Ar, OldSingleMaterials, MaterialConfigFlag, SerializationVersion); + + TArray SingleFoliage; + SingleFoliage.BulkSerialize(Ar); + } + + // Serialize chunks indices + struct FVoxelChunkSaveWithSingleMaterial + { + FIntVector Position; + + int32 ValuesIndex = -1; + int32 MaterialsIndex = -1; + + bool bSingleValue = false; + // Makes life easier when loading legacy files + bool bSingleMaterial_Unused = false; + }; + TNoGrowArray NewChunks; + { + TArray OldChunks; + if (Version < FVoxelSaveVersion::FoliagePaint) + { + TArray ChunksWithoutFoliage; + if (Version == FVoxelSaveVersion::BeforeCustomVersionWasAdded) + { + Ar << ChunksWithoutFoliage; + } + else + { + ChunksWithoutFoliage.BulkSerialize(Ar); + } + OldChunks = TArray(ChunksWithoutFoliage); + } + else + { + OldChunks.BulkSerialize(Ar); + } + + NewChunks.Empty(OldChunks.Num()); + for (auto& OldChunk : OldChunks) + { + constexpr int32 SingleValueIndexFlag = 1 << 30; + + FVoxelChunkSaveWithSingleMaterial& NewChunk = NewChunks.Emplace_GetRef(); + + NewChunk.Position = OldChunk.Position; + + if (OldChunk.ValuesIndex != -1) + { + NewChunk.ValuesIndex = OldChunk.ValuesIndex & (~SingleValueIndexFlag); + NewChunk.bSingleValue = OldChunk.ValuesIndex & SingleValueIndexFlag; + } + if (OldChunk.MaterialsIndex != -1) + { + NewChunk.MaterialsIndex = OldChunk.MaterialsIndex & (~SingleValueIndexFlag); + NewChunk.bSingleMaterial_Unused = OldChunk.MaterialsIndex & SingleValueIndexFlag; + } + } + ensure(NewChunks.GetSlack() == 0); + } + + // Fixup material indices, as they are now referencing the MaterialsIndices array and not MaterialBuffers/SingleMaterials + { + check(OldMaterialBuffers.Num() % VOXELS_PER_DATA_CHUNK == 0); + + MaterialsIndices.Empty(OldMaterialBuffers.Num() / VOXELS_PER_DATA_CHUNK + OldSingleMaterials.Num()); + MaterialBuffers.Empty(OldMaterialBuffers.Num() * FVoxelMaterial::NumChannels); + SingleMaterials.Empty(OldSingleMaterials.Num() * FVoxelMaterial::NumChannels); + + Chunks.Empty(NewChunks.Num()); + + // Fixup chunks + for (auto& Chunk : NewChunks) + { + if (Chunk.MaterialsIndex != -1) + { + if (Chunk.bSingleMaterial_Unused) + { + const int32 OldIndex = Chunk.MaterialsIndex; + + Chunk.MaterialsIndex = MaterialsIndices.AddUninitialized(1); + auto& MaterialIndices = MaterialsIndices[Chunk.MaterialsIndex]; + + const FVoxelMaterial& Material = OldSingleMaterials[OldIndex]; + + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + MaterialIndices.GetRaw(Channel) = SingleMaterials.Add(Material.GetRaw(Channel)) | MaterialIndexSingleValueFlag; + } + } + else + { + check(Chunk.MaterialsIndex % VOXELS_PER_DATA_CHUNK == 0); + + const int32 OldIndex = Chunk.MaterialsIndex; + + Chunk.MaterialsIndex = MaterialsIndices.AddUninitialized(1); + auto& MaterialIndices = MaterialsIndices[Chunk.MaterialsIndex]; + + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + MaterialIndices.GetRaw(Channel) = MaterialBuffers.AddUninitialized(VOXELS_PER_DATA_CHUNK); + } + + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + const FVoxelMaterial& Material = OldMaterialBuffers[OldIndex + Index]; + + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + MaterialBuffers[MaterialIndices.GetRaw(Channel) + Index] = Material.GetRaw(Channel); + } + } + } + } + FVoxelChunkSave NewChunk; + NewChunk.Position = Chunk.Position; + NewChunk.ValuesIndex = Chunk.ValuesIndex; + NewChunk.MaterialsIndex = Chunk.MaterialsIndex; + NewChunk.bSingleValue = Chunk.bSingleValue; + Chunks.Add(NewChunk); + } + + ensure(MaterialsIndices.GetSlack() == 0); + ensure(MaterialBuffers.GetSlack() == 0); + ensure(SingleMaterials.GetSlack() == 0); + ensure(Chunks.GetSlack() == 0); + } + } + + // Serialize placeable items + if (Version >= FVoxelSaveVersion::PlaceableItemsInSave) + { + Ar << PlaceableItems; + } + + if (Ar.IsLoading() && Ar.IsError()) + { + FVoxelMessages::Error("VoxelSave: Serialization failed, data is corrupted"); + *this = FVoxelUncompressedWorldSaveImpl(); + } + + UpdateAllocatedSize(); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCompressedWorldSaveImpl::~FVoxelCompressedWorldSaveImpl() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelCompressedSavesMemory, AllocatedSize); +} + +bool FVoxelCompressedWorldSaveImpl::Serialize(FArchive& Ar) +{ + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (Ar.IsSaving()) + { + Version = FVoxelSaveVersion::LatestVersion; + } + + Ar << Depth; + Ar << Version; + if (Version < FVoxelSaveVersion::ValueConfigFlagAndSaveGUIDs) + { + uint32 ConfigFlags; + Ar << ConfigFlags; + Guid = FGuid::NewGuid(); + } + else + { + Ar << Guid; + } + Ar << CompressedData; + + UpdateAllocatedSize(); + } + + return true; +} + +void FVoxelCompressedWorldSaveImpl::UpdateAllocatedSize() const +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelCompressedSavesMemory, AllocatedSize); + AllocatedSize = CompressedData.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelCompressedSavesMemory, AllocatedSize); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelWorldSaveObject::PostLoad() +{ + Super::PostLoad(); + CopyDepthFromSave(); +} + +void UVoxelWorldSaveObject::CopyDepthFromSave() +{ + Depth = Save.Const().GetDepth(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSaveUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSaveUtilities.cpp new file mode 100644 index 00000000..579e3af7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelData/VoxelSaveUtilities.cpp @@ -0,0 +1,333 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelData/VoxelSaveUtilities.h" +#include "VoxelData/VoxelDataOctreeLeafData.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" +#include "VoxelMessages.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" + +#include "Serialization/LargeMemoryReader.h" +#include "Serialization/LargeMemoryWriter.h" +#include "Serialization/MemoryReader.h" +#include "Serialization/MemoryWriter.h" + +FVoxelSaveBuilder::FVoxelSaveBuilder(int32 Depth) + : Depth(Depth) +{ +} + +void FVoxelSaveBuilder::Save(FVoxelUncompressedWorldSaveImpl& OutSave, TArray& OutObjects) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(Depth >= 0); + OutSave.Guid = FGuid::NewGuid(); + OutSave.Depth = Depth; + OutSave.Chunks.Empty(ChunksToSave.Num()); + + { + uint32 NumValueBuffers = 0; + uint32 NumSingleValues = 0; + + uint32 NumMaterialsIndices = 0; + uint32 NumMaterialBuffers = 0; + uint32 NumSingleMaterials = 0; + + for (auto& Chunk : ChunksToSave) + { + if (Chunk.Values->IsDirty()) + { + NumValueBuffers += Chunk.Values->DataPtr != nullptr; + NumSingleValues += Chunk.Values->DataPtr == nullptr; + } + + if (Chunk.Materials->IsDirty()) + { + NumMaterialsIndices++; + if (Chunk.Materials->bUseChannels) + { + for (auto& DataPtr : Chunk.Materials->Channels_DataPtr) + { + NumMaterialBuffers += DataPtr != nullptr; + NumSingleMaterials += DataPtr == nullptr; + } + } + else + { + NumMaterialBuffers += FVoxelMaterial::NumChannels; + } + } + } + + OutSave.ValueBuffers.Empty(NumValueBuffers * VOXELS_PER_DATA_CHUNK); + OutSave.SingleValues.Empty(NumSingleValues); + + OutSave.MaterialsIndices.Empty(NumMaterialsIndices); + OutSave.MaterialBuffers.Empty(NumMaterialBuffers * VOXELS_PER_DATA_CHUNK); + OutSave.SingleMaterials.Empty(NumSingleMaterials); + } + + for (auto& Chunk : ChunksToSave) + { + FVoxelUncompressedWorldSaveImpl::FVoxelChunkSave NewChunk; + NewChunk.Position = Chunk.Position; + + if (Chunk.Values->IsDirty()) + { + if (Chunk.Values->DataPtr) + { + NewChunk.ValuesIndex = OutSave.ValueBuffers.AddUninitialized(VOXELS_PER_DATA_CHUNK); + FMemory::Memcpy(&OutSave.ValueBuffers[NewChunk.ValuesIndex], Chunk.Values->DataPtr, sizeof(FVoxelValue) * VOXELS_PER_DATA_CHUNK); + } + else + { + check(Chunk.Values->bIsSingleValue); + + NewChunk.ValuesIndex = OutSave.SingleValues.Add(Chunk.Values->SingleValue); + NewChunk.bSingleValue = true; + } + } + else + { + NewChunk.ValuesIndex = -1; + } + + if (Chunk.Materials->IsDirty()) + { + TVoxelMaterialStorage MaterialIndices; + + if (Chunk.Materials->bUseChannels) + { + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + if (auto& DataPtr = Chunk.Materials->Channels_DataPtr[Channel]) + { + const int32 Index = OutSave.MaterialBuffers.AddUninitialized(VOXELS_PER_DATA_CHUNK); + FMemory::Memcpy(&OutSave.MaterialBuffers[Index], DataPtr, sizeof(uint8) * VOXELS_PER_DATA_CHUNK); + + MaterialIndices.GetRaw(Channel) = Index; + } + else + { + MaterialIndices.GetRaw(Channel) = OutSave.SingleMaterials.Add(Chunk.Materials->Channels_SingleValue[Channel]); + MaterialIndices.GetRaw(Channel) |= FVoxelUncompressedWorldSaveImpl::MaterialIndexSingleValueFlag; + } + } + } + else + { + check(Chunk.Materials->Main_DataPtr); + + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + MaterialIndices.GetRaw(Channel) = OutSave.MaterialBuffers.AddUninitialized(VOXELS_PER_DATA_CHUNK); + } + + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + const FVoxelMaterial& Material = Chunk.Materials->Main_DataPtr[Index]; + + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + OutSave.MaterialBuffers[MaterialIndices.GetRaw(Channel) + Index] = Material.GetRaw(Channel); + } + } + } + + NewChunk.MaterialsIndex = OutSave.MaterialsIndices.Add(MaterialIndices); + } + else + { + NewChunk.MaterialsIndex = -1; + } + + OutSave.Chunks.Add(NewChunk); + } + + ensure(OutSave.Chunks.GetSlack() == 0); + + ensure(OutSave.ValueBuffers.GetSlack() == 0); + ensure(OutSave.MaterialBuffers.GetSlack() == 0); + + ensure(OutSave.SingleValues.GetSlack() == 0); + ensure(OutSave.SingleMaterials.GetSlack() == 0); + + ensure(OutSave.MaterialsIndices.GetSlack() == 0); + + ChunksToSave.Empty(); + + FMemoryWriter Writer(OutSave.PlaceableItems); + { + FVoxelObjectArchive Archive = FVoxelObjectArchive::MakeWriter(Writer); + FVoxelPlaceableItemsUtilities::SerializeItems(Archive, {}, AssetItems); + OutObjects = Archive.GetWriterObjects(); + } + OutSave.PlaceableItems.Shrink(); + + OutSave.UpdateAllocatedSize(); +} + +void FVoxelSaveBuilder::AddAssetItem(const FVoxelAssetItem& AssetItem) +{ + AssetItems.Add(AssetItem); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSaveLoader::ExtractChunk( + int32 ChunkIndex, + const IVoxelDataOctreeMemory& Memory, + TVoxelDataOctreeLeafData& OutValues, + TVoxelDataOctreeLeafData& OutMaterials) const +{ + OutValues.ClearData(Memory); + OutMaterials.ClearData(Memory); + + auto& Chunk = Save.Chunks[ChunkIndex]; + if (Chunk.ValuesIndex >= 0) + { + if (Chunk.bSingleValue) + { + OutValues.SetSingleValue(Save.SingleValues[Chunk.ValuesIndex]); + } + else + { + OutValues.CreateData(Memory, [&](FVoxelValue* RESTRICT DataPtr) + { + check(Save.ValueBuffers.Num() >= Chunk.ValuesIndex + VOXELS_PER_DATA_CHUNK); + FMemory::Memcpy(DataPtr, &Save.ValueBuffers[Chunk.ValuesIndex], sizeof(FVoxelValue) * VOXELS_PER_DATA_CHUNK); + }); + } + OutValues.SetIsDirty(true, Memory); + } + if (Chunk.MaterialsIndex >= 0) + { + const auto& MaterialIndices = Save.MaterialsIndices[Chunk.MaterialsIndex]; + + bool bHasAnySingleValue = false; + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + if (MaterialIndices.GetRaw(Channel) & FVoxelUncompressedWorldSaveImpl::MaterialIndexSingleValueFlag) + { + bHasAnySingleValue = true; + break; + } + } + + if (bHasAnySingleValue) + { + OutMaterials.bUseChannels = true; + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + const int32 ChannelIndex = MaterialIndices.GetRaw(Channel); + if (ChannelIndex & FVoxelUncompressedWorldSaveImpl::MaterialIndexSingleValueFlag) + { + OutMaterials.Channels_SingleValue[Channel] = Save.SingleMaterials[ChannelIndex & (~FVoxelUncompressedWorldSaveImpl::MaterialIndexSingleValueFlag)]; + } + else + { + uint8* RESTRICT& DataPtr = OutMaterials.Channels_DataPtr[Channel]; + OutMaterials.Channels_Allocate(DataPtr, Memory); + + check(Save.MaterialBuffers.Num() >= ChannelIndex + VOXELS_PER_DATA_CHUNK); + FMemory::Memcpy(DataPtr, &Save.MaterialBuffers[ChannelIndex], sizeof(uint8) * VOXELS_PER_DATA_CHUNK); + } + } + } + else + { + OutMaterials.CreateData(Memory, [&](FVoxelMaterial* RESTRICT DataPtr) + { + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + DataPtr[Index].GetRaw(Channel) = Save.MaterialBuffers[MaterialIndices.GetRaw(Channel) + Index]; + } + } + }); + } + OutMaterials.SetIsDirty(true, Memory); + } +} + +void FVoxelSaveLoader::GetPlaceableItems(const FVoxelPlaceableItemLoadInfo& LoadInfo, TArray& OutAssetItems) +{ + VOXEL_FUNCTION_COUNTER(); + ensure(IsInGameThread()); + + FMemoryReader Reader(Save.PlaceableItems); + + if (Save.Version < FVoxelSaveVersion::ProperlySerializePlaceableItemsObjects) + { + int32 Num; + Reader << Num; + if (Num > 0) + { + FVoxelMessages::Error(FString::Printf(TEXT("You had %d voxel assets in your scene. These cannot be loaded anymore. Please contact the dev for a workaround."), Num)); + } + } + else + { + FVoxelObjectArchive Archive = FVoxelObjectArchive::MakeReader(Reader, LoadInfo.Objects ? *LoadInfo.Objects : TArray()); + FVoxelPlaceableItemsUtilities::SerializeItems(Archive, LoadInfo, OutAssetItems); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSaveUtilities::CompressVoxelSave(const FVoxelUncompressedWorldSave& UncompressedSave, FVoxelCompressedWorldSave& OutCompressedSave) +{ + OutCompressedSave.Objects = UncompressedSave.Objects; + CompressVoxelSave(UncompressedSave.Const(), OutCompressedSave.NewMutable()); +} + +void UVoxelSaveUtilities::CompressVoxelSave(const FVoxelUncompressedWorldSaveImpl& UncompressedSave, FVoxelCompressedWorldSaveImpl& OutCompressedSave) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + OutCompressedSave.Depth = UncompressedSave.GetDepth(); + OutCompressedSave.Guid = UncompressedSave.GetGuid(); + + FLargeMemoryWriter MemoryWriter(UncompressedSave.GetAllocatedSize()); + const_cast(UncompressedSave).Serialize(MemoryWriter); + + FVoxelSerializationUtilities::CompressData(MemoryWriter, OutCompressedSave.CompressedData); + + OutCompressedSave.UpdateAllocatedSize(); +} + +bool UVoxelSaveUtilities::DecompressVoxelSave(const FVoxelCompressedWorldSave& CompressedSave, FVoxelUncompressedWorldSave& OutUncompressedSave) +{ + OutUncompressedSave.Objects = CompressedSave.Objects; + return DecompressVoxelSave(CompressedSave.Const(), OutUncompressedSave.NewMutable()); +} + +bool UVoxelSaveUtilities::DecompressVoxelSave(const FVoxelCompressedWorldSaveImpl& CompressedSave, FVoxelUncompressedWorldSaveImpl& OutUncompressedSave) +{ + VOXEL_FUNCTION_COUNTER(); + + if (CompressedSave.CompressedData.Num() == 0) + { + return false; + } + else + { + TArray64 UncompressedData; + if (!FVoxelSerializationUtilities::DecompressData(CompressedSave.CompressedData, UncompressedData)) + { + FVoxelMessages::Error("DecompressVoxelSave failed: Corrupted data"); + return false; + } + + FLargeMemoryReader Reader(UncompressedData.GetData(), UncompressedData.Num()); + OutUncompressedSave.Serialize(Reader); + ensure(Reader.AtEnd() && !Reader.IsError()); + + return true; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugManager.cpp new file mode 100644 index 00000000..e0ec30c2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugManager.cpp @@ -0,0 +1,697 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelData/VoxelData.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelComponents/VoxelInvokerComponent.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelMessages.h" +#include "VoxelWorld.h" +#include "IVoxelPool.h" +#include "VoxelThreadPool.h" + +#include "Engine/Engine.h" +#include "EngineUtils.h" +#include "DrawDebugHelpers.h" +#include "Kismet/GameplayStatics.h" + +static TAutoConsoleVariable CVarShowUpdatedChunks( + TEXT("voxel.renderer.ShowUpdatedChunks"), + 0, + TEXT("If true, will show the chunks recently updated"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowRenderChunks( + TEXT("voxel.renderer.ShowRenderChunks"), + 0, + TEXT("If true, will show the render chunks"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowMultiplayerSyncedChunks( + TEXT("voxel.multiplayer.ShowSyncedChunks"), + 0, + TEXT("If true, will show the synced chunks"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowValuesState( + TEXT("voxel.data.ShowValuesState"), + 0, + TEXT("If true, will show the values data chunks and their status (cached/created...)"), + ECVF_Default); +static TAutoConsoleVariable CVarShowMaterialsState( + TEXT("voxel.data.ShowMaterialsState"), + 0, + TEXT("If true, will show the materials data chunks and their status (cached/created...)"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowDirtyValues( + TEXT("voxel.data.ShowDirtyValues"), + 0, + TEXT("If true, will show the data chunks with dirty values"), + ECVF_Default); +static TAutoConsoleVariable CVarShowDirtyMaterials( + TEXT("voxel.data.ShowDirtyMaterials"), + 0, + TEXT("If true, will show the data chunks with dirty materials"), + ECVF_Default); + +static TAutoConsoleVariable CVarFreezeDebug( + TEXT("voxel.FreezeDebug"), + 0, + TEXT("If true, won't clear previous frames boxes"), + ECVF_Default); + +static TAutoConsoleVariable CVarDebugDrawTime( + TEXT("voxel.debug.DrawTime"), + 1, + TEXT("Draw time will be multiplied by this"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowChunksEmptyStates( + TEXT("voxel.renderer.ShowChunksEmptyStates"), + 0, + TEXT("If true, will show updated chunks empty state, only if non-empty. Use ShowAllChunksEmptyStates to show empty too."), + ECVF_Default); + +static TAutoConsoleVariable CVarShowAllChunksEmptyStates( + TEXT("voxel.renderer.ShowAllChunksEmptyStates"), + 0, + TEXT("If true, will show updated chunks empty state, both empty and non-empty. Use ShowChunksEmptyStates to only show non-empty ones"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowWorldBounds( + TEXT("voxel.ShowWorldBounds"), + 0, + TEXT("If true, will show the world bounds"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowInvokers( + TEXT("voxel.ShowInvokers"), + 0, + TEXT("If true, will show the voxel invokers"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowDirtyVoxels( + TEXT("voxel.data.ShowDirtyVoxels"), + 0, + TEXT("If true, will show every dirty voxel in the scene"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowPlaceableItemsChunks( + TEXT("voxel.data.ShowPlaceableItemsChunks"), + 0, + TEXT("If true, will show every chunk that has a placeable item"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +inline FConsoleCommandWithWorldAndArgsDelegate CreateCommandWithVoxelWorldDelegate(T Lambda) +{ + return FConsoleCommandWithWorldAndArgsDelegate::CreateLambda([=](const TArray& Args, UWorld* World) + { + for (TActorIterator It(World); It; ++It) + { + if (It->IsCreated()) + { + Lambda(**It, Args); + } + } + }); +} + +template +inline FConsoleCommandWithWorldAndArgsDelegate CreateCommandWithVoxelWorldDelegateNoArgs(T Lambda) +{ + return CreateCommandWithVoxelWorldDelegate([&](AVoxelWorld& World, const TArray& Args) { Lambda(World); }); +} + +static FAutoConsoleCommandWithWorldAndArgs ClearChunksEmptyStatesCmd( + TEXT("voxel.renderer.ClearChunksEmptyStates"), + TEXT("Clear the empty states debug"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) { World.GetDebugManager().ClearChunksEmptyStates(); })); + +static FAutoConsoleCommandWithWorldAndArgs UpdateAllCmd( + TEXT("voxel.renderer.UpdateAll"), + TEXT("Update all the chunks in all the voxel world in the scene"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) { UVoxelBlueprintLibrary::UpdateBounds(&World, FVoxelIntBox::Infinite); })); + +static FAutoConsoleCommandWithWorldAndArgs RecomputeMeshPositionsCmd( + TEXT("voxel.renderer.RecomputeMeshPositions"), + TEXT("Recompute the positions of all the meshes in all the voxel world in the scene"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) { World.GetRenderer().RecomputeMeshPositions(); })); + +static FAutoConsoleCommandWithWorldAndArgs ForceLODsUpdateCmd( + TEXT("voxel.renderer.ForceLODUpdate"), + TEXT("Update the LODs"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) { World.GetLODManager().ForceLODsUpdate(); })); + +static FAutoConsoleCommandWithWorldAndArgs CacheAllValuesCmd( + TEXT("voxel.data.CacheAllValues"), + TEXT("Cache all values"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::CacheValues(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs CacheAllMaterialsCmd( + TEXT("voxel.data.CacheAllMaterials"), + TEXT("Cache all materials"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::CacheMaterials(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs ClearAllCachedValuesCmd( + TEXT("voxel.data.ClearAllCachedValues"), + TEXT("Clear all cached values"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::ClearCachedValues(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs ClearAllCachedMaterialsCmd( + TEXT("voxel.data.ClearAllCachedMaterials"), + TEXT("Clear all cached materials"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::ClearCachedMaterials(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs CheckForSingleValuesCmd( + TEXT("voxel.data.CheckForSingleValues"), + TEXT("Check if values in a chunk are all the same, and if so only store one"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::CheckForSingleValues(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs CheckForSingleMaterialsCmd( + TEXT("voxel.data.CheckForSingleMaterials"), + TEXT("Check if materials in a chunk are all the same, and if so only store one"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::CheckForSingleMaterials(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs RoundVoxelsCmd( + TEXT("voxel.data.RoundVoxels"), + TEXT("Round all voxels that do not impact the surface nor the normals"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::RoundVoxels(&World, FVoxelIntBox::Infinite); + if (World.GetData().bEnableUndoRedo) UVoxelBlueprintLibrary::SaveFrame(&World); + })); + +static FAutoConsoleCommandWithWorldAndArgs ClearUnusedMaterialsCmd( + TEXT("voxel.data.ClearUnusedMaterials"), + TEXT("Will clear all materials that do not affect the surface to improve compression"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::ClearUnusedMaterials(&World, FVoxelIntBox::Infinite); + if (World.GetData().bEnableUndoRedo) UVoxelBlueprintLibrary::SaveFrame(&World); + })); + +static FAutoConsoleCommandWithWorldAndArgs RegenerateAllSpawnersCmd( + TEXT("voxel.spawners.RegenerateAll"), + TEXT("Regenerate all spawners that can be regenerated"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelBlueprintLibrary::RegenerateSpawners(&World, FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorldAndArgs CompressIntoHeightmapCmd( + TEXT("voxel.data.CompressIntoHeightmap"), + TEXT("Update the heightmap to match the voxel world data"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::CompressIntoHeightmap(&World); + UVoxelBlueprintLibrary::UpdateBounds(&World, FVoxelIntBox::Infinite); + if (World.GetData().bEnableUndoRedo) UVoxelBlueprintLibrary::SaveFrame(&World); + })); + +static FAutoConsoleCommandWithWorldAndArgs RoundToGeneratorCmd( + TEXT("voxel.data.RoundToGenerator"), + TEXT("Set the voxels back to the generator value if all the voxels in a radius of 2 have the same sign as the generator"), + CreateCommandWithVoxelWorldDelegateNoArgs([](AVoxelWorld& World) + { + UVoxelDataTools::RoundToGenerator(&World, FVoxelIntBox::Infinite); + UVoxelBlueprintLibrary::UpdateBounds(&World, FVoxelIntBox::Infinite); + if (World.GetData().bEnableUndoRedo) UVoxelBlueprintLibrary::SaveFrame(&World); + })); + +static bool GShowCollisionAndNavmeshDebug = false; + +static FAutoConsoleCommandWithWorldAndArgs ShowCollisionAndNavmeshDebugCmd( + TEXT("voxel.renderer.ShowCollisionAndNavmeshDebug"), + TEXT("If true, will show chunks used for collisions/navmesh and will color all chunks according to their usage"), + CreateCommandWithVoxelWorldDelegate([](AVoxelWorld& World, const TArray& Args) + { + if (Args.Num() == 0) + { + GShowCollisionAndNavmeshDebug = !GShowCollisionAndNavmeshDebug; + } + else if (Args[0] == "0") + { + GShowCollisionAndNavmeshDebug = false; + } + else + { + GShowCollisionAndNavmeshDebug = true; + } + + World.GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); + })); + +static FAutoConsoleCommandWithWorld RebaseOntoCameraCmd( + TEXT("voxel.RebaseOntoCamera"), + TEXT("Call SetWorldOriginLocation so that the camera is at 0 0 0"), + FConsoleCommandWithWorldDelegate::CreateLambda([](UWorld* World) + { + auto* CameraManager = UGameplayStatics::GetPlayerCameraManager(World, 0); + if (ensure(CameraManager)) + { + const FVector Position = CameraManager->GetCameraLocation(); + UGameplayStatics::SetWorldOriginLocation(World, UGameplayStatics::GetWorldOriginLocation(World) + FIntVector(Position)); + } + })); + +static FAutoConsoleCommand CmdDestroyGlobalThreadPool( + TEXT("voxel.threading.DestroyGlobalPool"), + TEXT("Destroy the global thread pool"), + FConsoleCommandDelegate::CreateStatic(&IVoxelPool::DestroyGlobalPool)); + +static FAutoConsoleCommandWithWorld CmdDestroyWorldThreadPool( + TEXT("voxel.threading.DestroyWorldPool"), + TEXT("Destroy the current world thread pool"), + FConsoleCommandWithWorldDelegate::CreateStatic(&IVoxelPool::DestroyWorldPool)); + +static FAutoConsoleCommand CmdLogThreadPoolStats( + TEXT("voxel.threading.LogStats"), + TEXT(""), + FConsoleCommandDelegate::CreateLambda([](){ FVoxelQueuedThreadPoolStats::Get().LogTimes(); })); + +static FAutoConsoleCommand CmdLogMemoryStats( + TEXT("voxel.LogMemoryStats"), + TEXT(""), + FConsoleCommandDelegate::CreateStatic(&UVoxelBlueprintLibrary::LogMemoryStats)); + +static void LogSecondsPerCycles() +{ + LOG_VOXEL(Log, TEXT("SECONDS PER CYCLES: %e"), FPlatformTime::GetSecondsPerCycle()); +} + +static FAutoConsoleCommand CmdLogSecondsPerCycles( + TEXT("voxel.debug.LogSecondsPerCycles"), + TEXT(""), + FConsoleCommandDelegate::CreateStatic(&LogSecondsPerCycles)); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline float GetBoundsThickness(const FVoxelIntBox& Bounds) +{ + return Bounds.Size().GetMax(); +} + +#define DRAW_BOUNDS(Bounds, Color, bThick) UVoxelDebugUtilities::DrawDebugIntBox(World, Bounds, DebugDT, bThick ? GetBoundsThickness(Bounds) : 0, FLinearColor(Color)); +#define DRAW_BOUNDS_ARRAY(BoundsArray, Color, bThick) for (auto& Bounds : BoundsArray) { DRAW_BOUNDS(Bounds, FColorList::Color, bThick) } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelDebugManagerSettings::FVoxelDebugManagerSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& Data, + bool bDisabled) + : VoxelWorld(const_cast(World)) + , Pool(Pool) + , Data(Data) + , bDisabled(bDisabled) +{ +} + +TVoxelSharedRef FVoxelDebugManager::Create(const FVoxelDebugManagerSettings& Settings) +{ + return MakeShareable(new FVoxelDebugManager(Settings)); +} + +void FVoxelDebugManager::Destroy() +{ + StopTicking(); +} + +FVoxelDebugManager::FVoxelDebugManager(const FVoxelDebugManagerSettings& Settings) + : Settings(Settings) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDebugManager::ReportUpdatedChunks(TFunction()> InUpdatedChunks) +{ + if (CVarShowUpdatedChunks.GetValueOnGameThread()) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + UpdatedChunks = InUpdatedChunks(); + + FString Log = "Updated chunks: "; + for (auto& Bounds : UpdatedChunks) + { + Log += Bounds.ToString() + "; "; + } + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), 1, FColor::Blue, Log); + LOG_VOXEL(Log, TEXT("%s"), *Log); + } +} + +void FVoxelDebugManager::ReportRenderChunks(TFunction()> InRenderChunks) +{ + if (CVarShowRenderChunks.GetValueOnGameThread()) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + RenderChunks = InRenderChunks(); + } +} + +void FVoxelDebugManager::ReportMultiplayerSyncedChunks(TFunction()> InMultiplayerSyncedChunks) +{ + if (CVarShowMultiplayerSyncedChunks.GetValueOnGameThread()) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + MultiplayerSyncedChunks = InMultiplayerSyncedChunks(); + } +} + +void FVoxelDebugManager::ReportMeshTaskCount(int32 InTaskCount) +{ + MeshTaskCount = InTaskCount; +} + +void FVoxelDebugManager::ReportMeshTasksCallbacksQueueNum(int32 Num) +{ + MeshTasksCallbacksQueueNum = Num; +} + +void FVoxelDebugManager::ReportMeshActionQueueNum(int32 Num) +{ + MeshActionQueueNum = Num; +} + +void FVoxelDebugManager::ReportFoliageTaskCount(int32 TaskCount) +{ + FoliageTaskCount.Set(TaskCount); +} + +void FVoxelDebugManager::ReportChunkEmptyState(const FVoxelIntBox& Bounds, bool bIsEmpty) +{ + ChunksEmptyStates.Emplace(FChunkEmptyState{ Bounds, bIsEmpty }); +} + +void FVoxelDebugManager::ClearChunksEmptyStates() +{ + ChunksEmptyStates.Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FVoxelDebugManager::ShowCollisionAndNavmeshDebug() +{ + return GShowCollisionAndNavmeshDebug; +} + +FColor FVoxelDebugManager::GetCollisionAndNavmeshDebugColor(bool bEnableCollisions, bool bEnableNavmesh) +{ + if (bEnableCollisions && bEnableNavmesh) + { + return FColor::Yellow; + } + if (bEnableCollisions) + { + return FColor::Blue; + } + if (bEnableNavmesh) + { + return FColor::Green; + } + return FColor::White; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDebugManager::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Settings.bDisabled) return; + + auto* World = Settings.VoxelWorld.Get(); + if (!World) return; + if (World->bDisableDebugManager) return; + + const float DebugDT = DeltaTime * 1.5f * CVarDebugDrawTime.GetValueOnGameThread(); + + if (CVarShowRenderChunks.GetValueOnGameThread()) + { + DRAW_BOUNDS_ARRAY(RenderChunks, Grey, false); + } + if (CVarShowUpdatedChunks.GetValueOnGameThread()) + { + DRAW_BOUNDS_ARRAY(UpdatedChunks, Blue, true); + } + if (CVarShowMultiplayerSyncedChunks.GetValueOnGameThread()) + { + DRAW_BOUNDS_ARRAY(MultiplayerSyncedChunks, Blue, true); + } + if (CVarShowWorldBounds.GetValueOnGameThread()) + { + DRAW_BOUNDS(Settings.Data->WorldBounds, FColorList::Red, true); + } + if (!World->bDisableOnScreenMessages) + { + const int32 PoolTaskCount = Settings.Pool->GetNumTasks(); + if (PoolTaskCount > 0) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, FString::Printf(TEXT("Total tasks remaining: %d"), PoolTaskCount)); + } + if (MeshTaskCount > 0) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, FString::Printf(TEXT("Mesh tasks remaining: %d"), MeshTaskCount)); + } + if (MeshTasksCallbacksQueueNum > 0) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, FString::Printf(TEXT("Mesh tasks callbacks queued: %d"), MeshTasksCallbacksQueueNum)); + } + if (MeshActionQueueNum > 0) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, FString::Printf(TEXT("Mesh actions queued: %d"), MeshActionQueueNum)); + } + if (FoliageTaskCount.GetValue() > 0) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, FString::Printf(TEXT("Foliage tasks remaining: %d"), FoliageTaskCount.GetValue())); + } + } + if (!CVarFreezeDebug.GetValueOnGameThread()) + { + UpdatedChunks.Reset(); + MultiplayerSyncedChunks.Reset(); + } + + if (CVarShowInvokers.GetValueOnGameThread()) + { + const FColor LocalInvokerColor = FColor::Green; + const FColor RemoteInvokerColor = FColor::Silver; + const FColor LODColor = FColor::Red; + const FColor CollisionsColor = FColor::Blue; + const FColor NavmeshColor = FColor::Green; + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, LocalInvokerColor, TEXT("Local Invokers")); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, RemoteInvokerColor, TEXT("Remote Invokers")); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, LODColor, TEXT("Invokers LOD Bounds")); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, CollisionsColor, TEXT("Invokers Collisions Bounds")); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, NavmeshColor, TEXT("Invokers Navmesh Bounds")); + + for (auto& Invoker : UVoxelInvokerComponentBase::GetInvokers(World->GetWorld())) + { + if (Invoker.IsValid()) + { + DrawDebugPoint( + World->GetWorld(), + World->LocalToGlobal(Invoker->GetInvokerVoxelPosition(World)), + 100, + Invoker->IsLocalInvoker() ? LocalInvokerColor : RemoteInvokerColor, + false, + DebugDT); + + const auto InvokerSettings = Invoker->GetInvokerSettings(World); + + if (InvokerSettings.bUseForLOD) + { + DRAW_BOUNDS(InvokerSettings.LODBounds, LODColor, true); + } + if (InvokerSettings.bUseForCollisions) + { + DRAW_BOUNDS(InvokerSettings.CollisionsBounds, CollisionsColor, true); + } + if (InvokerSettings.bUseForNavmesh) + { + DRAW_BOUNDS(InvokerSettings.NavmeshBounds, NavmeshColor, true); + } + } + } + } + + if (CVarShowChunksEmptyStates.GetValueOnGameThread()) + { + const static FColor NotEmpty = FColorList::Brown; + + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, NotEmpty, TEXT("Not empty chunks (range analysis failed)")); + + for (auto& EmptyState : ChunksEmptyStates) + { + if (!EmptyState.bIsEmpty) + { + DRAW_BOUNDS(EmptyState.Bounds, NotEmpty, false); + } + } + } + if (CVarShowAllChunksEmptyStates.GetValueOnGameThread()) + { + const static FColor Empty = FColorList::Green; + const static FColor NotEmpty = FColorList::Brown; + + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, Empty, TEXT("Empty chunks (range analysis successful)")); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, NotEmpty, TEXT("Not empty chunks (range analysis failed)")); + + for (auto& EmptyState : ChunksEmptyStates) + { + if (EmptyState.bIsEmpty) + { + DRAW_BOUNDS(EmptyState.Bounds, Empty, false); + } + else + { + DRAW_BOUNDS(EmptyState.Bounds, NotEmpty, false); + } + } + } + + const FColor SingleColor = FColorList::Green; + const FColor SingleDirtyColor = FColorList::Blue; + const FColor CachedColor = FColorList::Yellow; + const FColor DirtyColor = FColorList::Red; + + const UVoxelDebugUtilities::FDrawDataOctreeSettings DrawDataOctreeSettings + { + World, + DebugDT, + false, + false, + SingleColor, + SingleDirtyColor, + CachedColor, + DirtyColor + }; + + if (CVarShowValuesState.GetValueOnGameThread()) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, "Values state:"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, DirtyColor, "Dirty"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, CachedColor, "Cached"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, SingleColor, "Single Item Stored"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, SingleDirtyColor, "Single Item Stored - Dirty"); + + auto LocalDrawDataOctreeSettings = DrawDataOctreeSettings; + LocalDrawDataOctreeSettings.bShowSingle = true; + LocalDrawDataOctreeSettings.bShowCached = true; + + FVoxelReadScopeLock Lock(*Settings.Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + UVoxelDebugUtilities::DrawDataOctreeImpl(*Settings.Data, LocalDrawDataOctreeSettings); + } + if (CVarShowMaterialsState.GetValueOnGameThread()) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, FColor::White, "Materials state:"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, DirtyColor, "Dirty"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, CachedColor, "Cached"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, SingleColor, "Single Item Stored"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, SingleDirtyColor, "Single Item Stored - Dirty"); + + auto LocalDrawDataOctreeSettings = DrawDataOctreeSettings; + LocalDrawDataOctreeSettings.bShowSingle = true; + LocalDrawDataOctreeSettings.bShowCached = true; + + FVoxelReadScopeLock Lock(*Settings.Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + UVoxelDebugUtilities::DrawDataOctreeImpl(*Settings.Data, LocalDrawDataOctreeSettings); + } + + if (CVarShowDirtyValues.GetValueOnGameThread()) + { + auto LocalDrawDataOctreeSettings = DrawDataOctreeSettings; + LocalDrawDataOctreeSettings.bShowSingle = false; + LocalDrawDataOctreeSettings.bShowCached = false; + + FVoxelReadScopeLock Lock(*Settings.Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + UVoxelDebugUtilities::DrawDataOctreeImpl(*Settings.Data, LocalDrawDataOctreeSettings); + } + if (CVarShowDirtyMaterials.GetValueOnGameThread()) + { + auto LocalDrawDataOctreeSettings = DrawDataOctreeSettings; + LocalDrawDataOctreeSettings.bShowSingle = false; + LocalDrawDataOctreeSettings.bShowCached = false; + + FVoxelReadScopeLock Lock(*Settings.Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + UVoxelDebugUtilities::DrawDataOctreeImpl(*Settings.Data, LocalDrawDataOctreeSettings); + } + + if (CVarShowPlaceableItemsChunks.GetValueOnGameThread()) + { + FVoxelReadScopeLock Lock(*Settings.Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelOctreeUtilities::IterateEntireTree(Settings.Data->GetOctree(), [&](const FVoxelDataOctreeBase& Octree) + { + if (Octree.IsLeafOrHasNoChildren() && Octree.GetItemHolder().NumItems() > 0) + { + ensureThreadSafe(Octree.IsLockedForRead()); + UVoxelDebugUtilities::DrawDebugIntBox(World, Octree.GetBounds(), DebugDT, 0, FColorList::Red); + } + }); + } + + if (GShowCollisionAndNavmeshDebug) + { + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, GetCollisionAndNavmeshDebugColor(true, false), "Chunks with collisions"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, GetCollisionAndNavmeshDebugColor(false, true), "Chunks with navmesh"); + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), DebugDT, GetCollisionAndNavmeshDebugColor(true, true), "Chunks with navmesh and collision"); + } + if (CVarShowDirtyVoxels.GetValueOnGameThread()) + { + FVoxelDataUtilities::IterateDirtyDataInBounds( + *Settings.Data, + FVoxelIntBox::Infinite, + [&](int32 X, int32 Y, int32 Z, const FVoxelValue& Value) + { + DrawDebugPoint( + World->GetWorld(), + World->LocalToGlobal(FIntVector(X, Y, Z)), + 2, + Value.IsEmpty() ? FColor::Blue : FColor::Red, + false, + DebugDT); + }); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugUtilities.cpp new file mode 100644 index 00000000..2b07a30d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelDebugUtilities.cpp @@ -0,0 +1,254 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelDebug/VoxelLineBatchComponent.h" +#include "VoxelIntBox.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" +#include "VoxelData/VoxelData.h" +#include "VoxelTools/VoxelToolHelpers.h" + +#include "Components/LineBatchComponent.h" +#include "DrawDebugHelpers.h" +#include "Engine/Engine.h" + +void UVoxelDebugUtilities::DrawDebugIntBox( + AVoxelWorld* World, + FVoxelIntBox Bounds, + FTransform Transform, + float Lifetime, + float Thickness, + FLinearColor Color) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + // no debug line drawing on dedicated server + if (GEngine->GetNetMode(World->GetWorld()) == NM_DedicatedServer) return; + + DrawDebugIntBox( + *World, + World->GetLineBatchComponent(), + Transform, + Bounds, + Lifetime, + Thickness, + Color); +} + +void UVoxelDebugUtilities::DrawDebugIntBox( + const AVoxelWorldInterface* World, + FVoxelIntBox Box, + float Lifetime, + float Thickness, + FLinearColor Color) +{ + DrawDebugIntBox(Cast(const_cast(World)), Box, FTransform(), Lifetime, Thickness, Color); +} + +void UVoxelDebugUtilities::DrawDebugIntBox( + const IVoxelWorldInterface& World, + UVoxelLineBatchComponent& LineBatchComponent, + FTransform Transform, + FVoxelIntBox Box, + float Lifetime, + float Thickness, + FLinearColor Color) +{ + VOXEL_FUNCTION_COUNTER(); + + const float LineLifeTime = (Lifetime > 0.f) ? Lifetime : LineBatchComponent.DefaultLifeTime; + + // Put it in local voxel world space + const FVector Min = LineBatchComponent.GetComponentTransform().InverseTransformPosition(World.LocalToGlobal(Box.Min)); + const FVector Max = LineBatchComponent.GetComponentTransform().InverseTransformPosition(World.LocalToGlobal(Box.Max)); + + const float BorderOffset = Thickness / 2; + + const FBox DebugBox(Min + BorderOffset, Max - BorderOffset); + const FVector Extent = DebugBox.GetExtent(); + const FVector Center = DebugBox.GetCenter(); + const uint8 DepthPriority = 0; + + Transform = LineBatchComponent.GetComponentTransform() * Transform; + + { + VOXEL_SCOPE_COUNTER("DrawLines"); + auto& Lines = LineBatchComponent.BatchedLines; + + FVector Start = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, Extent.Z)); + FVector End = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, -Extent.Z)); + End = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, -Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, -Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, -Extent.Z)); + End = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, -Extent.Z)); + new(Lines)FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(Extent.X, Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(Extent.X, -Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, -Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + + Start = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, Extent.Z)); + End = Transform.TransformPosition(Center + FVector(-Extent.X, Extent.Y, -Extent.Z)); + new(Lines) FBatchedLine(Start, End, Color, LineLifeTime, Thickness, DepthPriority); + } + { + VOXEL_SCOPE_COUNTER("MarkRenderStateDirty"); + LineBatchComponent.MarkRenderStateDirty(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDebugUtilities::DebugVoxelsInsideBounds( + AVoxelWorld* World, + FVoxelIntBox Bounds, + FLinearColor Color, + float Lifetime, + float Thickness, + bool bDebugDensities, + FLinearColor TextColor) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + for (int32 X = Bounds.Min.X; X < Bounds.Max.X; X++) + { + for (int32 Y = Bounds.Min.Y; Y < Bounds.Max.Y; Y++) + { + for (int32 Z = Bounds.Min.Z; Z < Bounds.Max.Z; Z++) + { + DrawDebugIntBox(World, FVoxelIntBox(X, Y, Z), Lifetime, Thickness, Color); + + if (bDebugDensities) + { + auto& Data = World->GetData(); + FVoxelReadScopeLock Lock(Data, FVoxelIntBox(X, Y, Z), "DebugVoxelsInsideBox"); + float Value = Data.GetValue(X, Y, Z, 0).ToFloat(); + DrawDebugString(World->GetWorld(), World->LocalToGlobal(FIntVector(X, Y, Z)), LexToString(Value), nullptr, TextColor.ToFColor(false), Lifetime); + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void UVoxelDebugUtilities::DrawDataOctreeImpl(const FVoxelData& Data, const FDrawDataOctreeSettings& Settings) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelOctreeUtilities::IterateAllLeaves(Data.GetOctree(), [&](const FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForRead()); + + auto& LeafData = Leaf.GetData(); + const auto Draw = [&](FColor Color) + { + UVoxelDebugUtilities::DrawDebugIntBox(Settings.World, Leaf.GetBounds(), Settings.Lifetime, 0, FLinearColor(Color));; + }; + if (LeafData.HasData()) + { + if (LeafData.HasAllocation()) + { + if (LeafData.IsDirty()) + { + Draw(Settings.DirtyColor); + } + else + { + if (Settings.bShowCached) + { + Draw(Settings.CachedColor); + } + } + } + else if (Settings.bShowSingle) + { + if (LeafData.IsDirty()) + { + Draw(Settings.SingleDirtyColor); + } + else + { + Draw(Settings.SingleColor); + } + } + } + }); +} + +template VOXEL_API void UVoxelDebugUtilities::DrawDataOctreeImpl(const FVoxelData&, const FDrawDataOctreeSettings&); +template VOXEL_API void UVoxelDebugUtilities::DrawDataOctreeImpl(const FVoxelData&, const FDrawDataOctreeSettings&); + +void UVoxelDebugUtilities::DrawDataOctree( + AVoxelWorld* World, + EVoxelDataType DataType, + float Lifetime, + bool bShowSingle, + bool bShowCached, + FColor SingleColor, + FColor SingleDirtyColor, + FColor CachedColor, + FColor DirtyColor) +{ + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const FDrawDataOctreeSettings Settings + { + World, + Lifetime, + bShowSingle, + bShowCached, + SingleColor, + SingleDirtyColor, + CachedColor, + DirtyColor + }; + + auto& Data = World->GetData(); + + FVoxelReadScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + + if (DataType == EVoxelDataType::Values) DrawDataOctreeImpl(Data, Settings); + if (DataType == EVoxelDataType::Materials) DrawDataOctreeImpl(Data, Settings); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelLineBatchComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelLineBatchComponent.cpp new file mode 100644 index 00000000..6fad8044 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDebug/VoxelLineBatchComponent.cpp @@ -0,0 +1,296 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDebug/VoxelLineBatchComponent.h" +#include "VoxelMinimal.h" + +#include "PrimitiveViewRelevance.h" +#include "PrimitiveSceneProxy.h" +#include "Engine/Engine.h" +#include "MaterialShared.h" +#include "Materials/Material.h" +#include "Engine/CollisionProfile.h" +#include "SceneManagement.h" +#include "DynamicMeshBuilder.h" + +DECLARE_DWORD_COUNTER_STAT(TEXT("Num Debug Lines Drawn"), STAT_NumDebugLinesDrawn, STATGROUP_VoxelCounters); + +UVoxelLineBatchComponent::UVoxelLineBatchComponent() +{ + bAutoActivate = true; + bTickInEditor = true; + PrimaryComponentTick.bCanEverTick = true; + + UPrimitiveComponent::SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); + + bUseEditorCompositing = true; + SetGenerateOverlapEvents(false); + + // Ignore streaming updates since GetUsedMaterials() is not implemented. + bIgnoreStreamingManagerUpdate = true; +} + +void UVoxelLineBatchComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + VOXEL_FUNCTION_COUNTER(); + + bool bDirty = false; + // Update the life time of batched lines, removing the lines which have expired. + for (int32 LineIndex = 0; LineIndex < BatchedLines.Num(); LineIndex++) + { + FBatchedLine& Line = BatchedLines[LineIndex]; + if (Line.RemainingLifeTime > 0.0f) + { + Line.RemainingLifeTime -= DeltaTime; + if (Line.RemainingLifeTime <= 0.0f) + { + // The line has expired, remove it. + BatchedLines.RemoveAtSwap(LineIndex--); + bDirty = true; + } + } + } + + // Update the life time of batched points, removing the points which have expired. + for (int32 PtIndex = 0; PtIndex < BatchedPoints.Num(); PtIndex++) + { + FBatchedPoint& Pt = BatchedPoints[PtIndex]; + if (Pt.RemainingLifeTime > 0.0f) + { + Pt.RemainingLifeTime -= DeltaTime; + if (Pt.RemainingLifeTime <= 0.0f) + { + // The point has expired, remove it. + BatchedPoints.RemoveAtSwap(PtIndex--); + bDirty = true; + } + } + } + + // Update the life time of batched meshes, removing the meshes which have expired. + for (int32 MeshIndex = 0; MeshIndex < BatchedMeshes.Num(); MeshIndex++) + { + FBatchedMesh& Mesh = BatchedMeshes[MeshIndex]; + if (Mesh.RemainingLifeTime > 0.0f) + { + Mesh.RemainingLifeTime -= DeltaTime; + if (Mesh.RemainingLifeTime <= 0.0f) + { + // The mesh has expired, remove it. + BatchedMeshes.RemoveAtSwap(MeshIndex--); + bDirty = true; + } + } + } + + if (bDirty) + { + MarkRenderStateDirty(); + } +} + +void UVoxelLineBatchComponent::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) +{ + Super::ApplyWorldOffset(InOffset, bWorldShift); + + VOXEL_FUNCTION_COUNTER(); + + bool bDirty = false; + for (FBatchedLine& Line : BatchedLines) + { + Line.Start += InOffset; + Line.End += InOffset; + bDirty = true; + } + + for (FBatchedPoint& Point : BatchedPoints) + { + Point.Position += InOffset; + bDirty = true; + } + + for (FBatchedMesh& Mesh : BatchedMeshes) + { + for (FVector& Vert : Mesh.MeshVerts) + { + Vert += InOffset; + bDirty = true; + } + } + + if (bDirty) + { + MarkRenderStateDirty(); + } +} + +FPrimitiveSceneProxy* UVoxelLineBatchComponent::CreateSceneProxy() +{ + if (BatchedLines.Num() == 0 && + BatchedPoints.Num() == 0 && + BatchedMeshes.Num() == 0) + { + return nullptr; + } + + return new FVoxelLineBatcherSceneProxy(this); +} + +FBoxSphereBounds UVoxelLineBatchComponent::CalcBounds(const FTransform& LocalToWorld) const +{ + VOXEL_FUNCTION_COUNTER(); + + if (!bCalculateAccurateBounds) + { + const FVector BoxExtent(HALF_WORLD_MAX); + return FBoxSphereBounds(FVector::ZeroVector, BoxExtent, BoxExtent.Size()); + } + + FBox BBox(ForceInit); + for (const FBatchedLine& Line : BatchedLines) + { + BBox += Line.Start; + BBox += Line.End; + } + + for (const FBatchedPoint& Point : BatchedPoints) + { + BBox += Point.Position; + } + + for (const FBatchedMesh& Mesh : BatchedMeshes) + { + for (const FVector& Vert : Mesh.MeshVerts) + { + BBox += Vert; + } + } + + if (BBox.IsValid) + { + // Points are in world space, so no need to transform. + return FBoxSphereBounds(BBox); + } + else + { + const FVector BoxExtent(1.f); + return FBoxSphereBounds(LocalToWorld.GetLocation(), BoxExtent, 1.f); + } +} + +void UVoxelLineBatchComponent::Flush() +{ + VOXEL_FUNCTION_COUNTER(); + + if (BatchedLines.Num() > 0 || BatchedPoints.Num() > 0 || BatchedMeshes.Num() > 0) + { + BatchedLines.Empty(); + BatchedPoints.Empty(); + BatchedMeshes.Empty(); + MarkRenderStateDirty(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelLineBatcherSceneProxy::FVoxelLineBatcherSceneProxy(const UVoxelLineBatchComponent* InComponent) + : FPrimitiveSceneProxy(InComponent) + , Lines(InComponent->BatchedLines) + , Points(InComponent->BatchedPoints) + , Meshes(InComponent->BatchedMeshes) +{ + bWillEverBeLit = false; +} + +SIZE_T FVoxelLineBatcherSceneProxy::GetTypeHash() const +{ + static size_t UniquePointer; + return reinterpret_cast(&UniquePointer); +} + +void FVoxelLineBatcherSceneProxy::GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (VisibilityMap & (1 << ViewIndex)) + { + const FSceneView* View = Views[ViewIndex]; + FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); + + INC_DWORD_STAT_BY(STAT_NumDebugLinesDrawn, Lines.Num()); + if (ensure(!PDI->View)) // FSimpleElementCollector does not have a view + { + auto* SimpleCollector = static_cast(PDI); + auto& BatchedElements = SimpleCollector->BatchedElements; // No support for depth priority; Would need to use TopBatchedElements + + // Reserve all for thick and not thick - we don't care about losing a bit of memory there + BatchedElements.AddReserveLines(Lines.Num(), false, false); + BatchedElements.AddReserveLines(Lines.Num(), false, true); + for (auto& Line : Lines) + { + BatchedElements.AddLine(Line.Start, Line.End, Line.Color, FHitProxyId(), Line.Thickness); + } + } + else + { + // Slow path + for (auto& Line : Lines) + { + PDI->DrawLine(Line.Start, Line.End, Line.Color, Line.DepthPriority, Line.Thickness); + } + } + + for (auto& Point : Points) + { + PDI->DrawPoint(Point.Position, Point.Color, Point.PointSize, Point.DepthPriority); + } + + for (auto& Mesh : Meshes) + { + static FVector const PosX(1.f, 0, 0); + static FVector const PosY(0, 1.f, 0); + static FVector const PosZ(0, 0, 1.f); + + // this seems far from optimal in terms of perf, but it's for debugging + FDynamicMeshBuilder MeshBuilder(View->GetFeatureLevel()); + + for (int32 VertIdx = 0; VertIdx < Mesh.MeshVerts.Num(); ++VertIdx) + { + MeshBuilder.AddVertex(Mesh.MeshVerts[VertIdx], FVector2D::ZeroVector, PosX, PosY, PosZ, FColor::White); + } + for (int32 Idx = 0; Idx < Mesh.MeshIndices.Num(); Idx += 3) + { + MeshBuilder.AddTriangle(Mesh.MeshIndices[Idx], Mesh.MeshIndices[Idx + 1], Mesh.MeshIndices[Idx + 2]); + } + + FMaterialRenderProxy* const MaterialRenderProxy = new FColoredMaterialRenderProxy(GEngine->DebugMeshMaterial->GetRenderProxy(), Mesh.Color); + Collector.RegisterOneFrameMaterialProxy(MaterialRenderProxy); + MeshBuilder.GetMesh(FMatrix::Identity, MaterialRenderProxy, Mesh.DepthPriority, false, false, ViewIndex, Collector); + } + } + } +} + +FPrimitiveViewRelevance FVoxelLineBatcherSceneProxy::GetViewRelevance(const FSceneView* View) const +{ + FPrimitiveViewRelevance ViewRelevance; + ViewRelevance.bDrawRelevance = IsShown(View); + ViewRelevance.bDynamicRelevance = true; + // ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative + ViewRelevance.UE_25_SWITCH(bSeparateTranslucencyRelevance, bSeparateTranslucency) = true; + ViewRelevance.UE_25_SWITCH(bNormalTranslucencyRelevance, bNormalTranslucency) = true; + return ViewRelevance; +} + +uint32 FVoxelLineBatcherSceneProxy::GetMemoryFootprint(void) const +{ + return sizeof(*this) + GetAllocatedSize(); +} + +uint32 FVoxelLineBatcherSceneProxy::GetAllocatedSize(void) const +{ + return FPrimitiveSceneProxy::GetAllocatedSize() + Lines.GetAllocatedSize() + Points.GetAllocatedSize() + Meshes.GetAllocatedSize(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefaultPool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefaultPool.cpp new file mode 100644 index 00000000..086a38c5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefaultPool.cpp @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDefaultPool.h" +#include "VoxelThreadPool.h" +#include "VoxelQueuedWork.h" +#include "Misc/QueuedThreadPool.h" +#include "VoxelMinimal.h" + +FVoxelDefaultPool::FVoxelDefaultPool( + int32 ThreadCount, + bool bConstantPriorities, + const TMap& InPriorityCategories, + const TMap& InPriorityOffsets) + : Pool(FVoxelQueuedThreadPool::Create(FVoxelQueuedThreadPoolSettings( + FString::Printf(TEXT("Voxel Pool %llu"), UNIQUE_ID()), + ThreadCount, + 1024 * 1024, + EThreadPriority::TPri_Normal, + bConstantPriorities))) +{ + for (int32 Index = 0; Index < 256; Index++) + { + const_cast&>(PriorityCategories)[Index] = InPriorityCategories.FindRef(EVoxelTaskType(Index)); + const_cast&>(PriorityOffsets)[Index] = InPriorityOffsets.FindRef(EVoxelTaskType(Index)); + } +} + +FVoxelDefaultPool::~FVoxelDefaultPool() +{ +} + +TVoxelSharedRef FVoxelDefaultPool::Create( + int32 ThreadCount, + bool bConstantPriorities, + const TMap& PriorityCategories, + const TMap& PriorityOffsets) +{ + LOG_VOXEL(Log, TEXT("Creating pool with %d threads"), ThreadCount); + if (!ensureMsgf(ThreadCount >= 1, TEXT("Invalid MeshThreadCount: %d"), ThreadCount)) + { + ThreadCount = 1; + } + + auto FixedPriorityCategories = PriorityCategories; + auto FixedPriorityOffsets = PriorityOffsets; + FixPriorityCategories(FixedPriorityCategories); + FixPriorityOffsets(FixedPriorityOffsets); + + return MakeShareable(new FVoxelDefaultPool( + ThreadCount, + bConstantPriorities, + FixedPriorityCategories, + FixedPriorityOffsets)); +} + +void FVoxelDefaultPool::QueueTask(EVoxelTaskType Type, IVoxelQueuedWork* Task) +{ + Pool->AddQueuedWork(Task, PriorityCategories[uint8(Type)], PriorityOffsets[uint8(Type)]); +} + +void FVoxelDefaultPool::QueueTasks(EVoxelTaskType Type, const TArray& Tasks) +{ + Pool->AddQueuedWorks(Tasks, PriorityCategories[uint8(Type)], PriorityOffsets[uint8(Type)]); +} + +int32 FVoxelDefaultPool::GetNumTasks() const +{ + return Pool->GetNumPendingWorks(); +} + +void FVoxelDefaultPool::FixPriorityCategories(TMap& PriorityCategories) +{ + for (auto& It : PriorityCategories) + { + It.Value = FMath::Max(0, It.Value); + } +#define FIX(Name) if (!PriorityCategories.Contains(EVoxelTaskType::Name)) PriorityCategories.Add(EVoxelTaskType::Name, EVoxelTaskType_DefaultPriorityCategories::Name); + FIX(ChunksMeshing); + FIX(CollisionsChunksMeshing); + FIX(VisibleChunksMeshing); + FIX(VisibleCollisionsChunksMeshing); + FIX(CollisionCooking); + FIX(FoliageBuild); + FIX(HISMBuild); + FIX(AsyncEditFunctions); + FIX(MeshMerge); + FIX(RenderOctree); +#undef FIX +} + +void FVoxelDefaultPool::FixPriorityOffsets(TMap& PriorityOffsets) +{ +#define FIX(Name) if (!PriorityOffsets.Contains(EVoxelTaskType::Name)) PriorityOffsets.Add(EVoxelTaskType::Name, EVoxelTaskType_DefaultPriorityOffsets::Name); + FIX(ChunksMeshing); + FIX(VisibleChunksMeshing); + FIX(CollisionsChunksMeshing); + FIX(VisibleCollisionsChunksMeshing); + FIX(CollisionCooking); + FIX(FoliageBuild); + FIX(HISMBuild); + FIX(AsyncEditFunctions); + FIX(RenderOctree); + FIX(MeshMerge); +#undef FIX +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefinitions.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefinitions.cpp new file mode 100644 index 00000000..9d15c662 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelDefinitions.cpp @@ -0,0 +1,140 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDefinitions.h" +#include "VoxelLog.h" +#include "VoxelStats.h" +#include "VoxelFeedbackContext.h" +#include "VoxelIntBox.h" +#include "VoxelItemStack.h" +#include "VoxelEditorDelegates.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" + +#include "Logging/LogMacros.h" +#include "Serialization/CustomVersion.h" + +static_assert(FVoxelUtilities::IsPowerOfTwo(RENDER_CHUNK_SIZE), "RENDER_CHUNK_SIZE must be a power of 2"); +static_assert(FVoxelUtilities::IsPowerOfTwo(DATA_CHUNK_SIZE), "DATA_CHUNK_SIZE must be a power of 2"); + +#if VOXEL_MATERIAL_ENABLE_UV1 && !VOXEL_MATERIAL_ENABLE_UV0 +#error "Error" +#endif +#if VOXEL_MATERIAL_ENABLE_UV2 && !VOXEL_MATERIAL_ENABLE_UV1 +#error "Error" +#endif +#if VOXEL_MATERIAL_ENABLE_UV3 && !VOXEL_MATERIAL_ENABLE_UV2 +#error "Error" +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +DEFINE_LOG_CATEGORY(LogVoxel); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if ENABLE_VOXEL_MEMORY_STATS +TMap& GetVoxelMemoryCounters() +{ + static TMap GVoxelMemoryCounters; + return GVoxelMemoryCounters; +} + +FVoxelMemoryCounterStaticRef::FVoxelMemoryCounterStaticRef(const TCHAR* Name, const FVoxelMemoryCounterRef& Ref) +{ + GetVoxelMemoryCounters().Add(Name, Ref); +} +#endif + +DEFINE_VOXEL_MEMORY_STAT(STAT_TotalVoxelMemory); + +static FFeedbackContext* GVoxelFeedbackContext = nullptr; + +void SetVoxelFeedbackContext(class FFeedbackContext& FeedbackContext) +{ + GVoxelFeedbackContext = &FeedbackContext; +} + +FVoxelScopedSlowTask::FVoxelScopedSlowTask(float InAmountOfWork, const FText& InDefaultMessage, bool bInEnabled) + : FScopedSlowTask(InAmountOfWork, InDefaultMessage, bInEnabled, GVoxelFeedbackContext ? *GVoxelFeedbackContext : *GWarn) +{ + +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// +/- 1024: prevents integers overflow +FVoxelIntBox const FVoxelIntBox::Infinite = FVoxelIntBox(FIntVector(MIN_int32 + 1024), FIntVector(MAX_int32 - 1024)); + +const FVoxelPlaceableItemHolder EmptyVoxelPlaceableItemHolder; +FVoxelItemStack FVoxelItemStack::Empty = FVoxelItemStack(EmptyVoxelPlaceableItemHolder); + +const FVoxelVector FVoxelVector::ZeroVector(0.0f, 0.0f, 0.0f); +const FVoxelVector FVoxelVector::OneVector(1.0f, 1.0f, 1.0f); +const FVoxelVector FVoxelVector::UpVector(0.0f, 0.0f, 1.0f); +const FVoxelVector FVoxelVector::DownVector(0.0f, 0.0f, -1.0f); +const FVoxelVector FVoxelVector::ForwardVector(1.0f, 0.0f, 0.0f); +const FVoxelVector FVoxelVector::BackwardVector(-1.0f, 0.0f, 0.0f); +const FVoxelVector FVoxelVector::RightVector(0.0f, 1.0f, 0.0f); +const FVoxelVector FVoxelVector::LeftVector(0.0f, -1.0f, 0.0f); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define VOXEL_DEBUG_DELEGATE(Type) \ + template<> \ + VOXEL_API FVoxelDebug::TDelegate& FVoxelDebug::GetDelegate() \ + { \ + static TDelegate Delegate; \ + return Delegate; \ + } + +VOXEL_DEBUG_DELEGATE(FVoxelValue); +VOXEL_DEBUG_DELEGATE(float); + +#undef VOXEL_DEBUG_DELEGATE + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint32& FVoxelRangeFailStatus::GetTlsSlot() +{ + // Not inline, else it's messed up across modules + static uint32 TlsSlot = 0xFFFFFFFF; + return TlsSlot; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +VOXEL_API FString VoxelStats_RemoveLambdaFromFunctionName(const FString& FunctionName) +{ +#if PLATFORM_WINDOWS + ensure(FunctionName.EndsWith("::operator ()")); + + TArray Array; + FunctionName.ParseIntoArray(Array, TEXT("::")); + + // operator() + if (ensure(Array.Num() > 1)) Array.Pop(false); + // + if (ensure(Array.Num() > 1)) Array.Pop(false); + + return FString::Join(Array, TEXT("::")); +#else + return FunctionName; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelEditorDelegates::FFixVoxelLandscapeMaterial FVoxelEditorDelegates::FixVoxelLandscapeMaterial; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelEvents/VoxelEventManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelEvents/VoxelEventManager.cpp new file mode 100644 index 00000000..b76758fe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelEvents/VoxelEventManager.cpp @@ -0,0 +1,416 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEvents/VoxelEventManager.h" +#include "VoxelComponents/VoxelInvokerComponent.h" +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelWorld.h" +#include "VoxelMinimal.h" + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num Voxel Events"), STAT_NumVoxelEvents, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Event Manager - Num active or generated chunks"), STAT_VoxelEventManager_NumActiveOrGeneratedChunks, STATGROUP_VoxelCounters); + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelEventsMemory); + +static TAutoConsoleVariable CVarShowEventsBounds( + TEXT("voxel.events.ShowBounds"), + 0, + TEXT("If true, will show event updates bounds"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelEventManagerSettings::FVoxelEventManagerSettings(const AVoxelWorld* InWorld, EVoxelPlayType PlayType) + : UpdateRate(FMath::Max(SMALL_NUMBER, InWorld->EventsTickRate)) + , VoxelWorldInterface(InWorld) + , World(InWorld->GetWorld()) + , WorldBounds(InWorld->GetWorldBounds()) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef FVoxelEventManager::Create(const FVoxelEventManagerSettings& Settings) +{ + TVoxelSharedRef Manager = MakeShareable(new FVoxelEventManager(Settings)); + UVoxelInvokerComponentBase::OnForceRefreshInvokers.AddThreadSafeSP(Manager, &FVoxelEventManager::ClearOldInvokerComponents); + return Manager; +} + +void FVoxelEventManager::Destroy() +{ + StopTicking(); +} + +FVoxelEventManager::FVoxelEventManager(const FVoxelEventManagerSettings& Settings) + : Settings(Settings) +{ + +} + +FVoxelEventManager::~FVoxelEventManager() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelEventsMemory, EventsAllocatedSize); + DEC_DWORD_STAT_BY(STAT_NumVoxelEvents, NumEvents); + DEC_DWORD_STAT_BY(STAT_VoxelEventManager_NumActiveOrGeneratedChunks, NumActiveOrGeneratedChunks); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelEventHandle FVoxelEventManager::BindEvent( + bool bFireExistingOnes, + int32 ChunkSize, + int32 DistanceInChunks, + const FChunkDelegate& OnActivate, + const FChunkDelegate& OnDeactivate, + EVoxelEventFlags::Type Flags) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensureAlways(OnActivate.IsBound() || OnDeactivate.IsBound())) + { + return {}; + } + + const FEventKey EventKey{ ChunkSize, DistanceInChunks, Flags }; + + auto& EventInfo = Events.FindOrAdd(EventKey); + if (!EventInfo.IsValid()) + { + EventInfo = MakeUnique(ChunkSize, DistanceInChunks, Flags); + } + + FVoxelEventHandle Handle; + Handle.OnActivateHandle = EventInfo->OnActivate.Add(OnActivate); + Handle.OnDeactivateHandle = EventInfo->OnDeactivate.Add(OnDeactivate); + Handle.ChunkSize = ChunkSize; + Handle.DistanceInChunks = DistanceInChunks; + Handle.Flags = Flags; + + if (bFireExistingOnes) + { + IterateActiveChunks(ChunkSize, DistanceInChunks, Flags, [&](auto Bounds) + { + OnActivate.ExecuteIfBound(Bounds); + }); + } + + // Force update + ClearOldInvokerComponents(); + + return Handle; +} + +FVoxelEventHandle FVoxelEventManager::BindGenerationEvent( + bool bFireExistingOnes, + int32 ChunkSize, + int32 DistanceInChunks, + const FChunkDelegate& OnGenerate, + EVoxelEventFlags::Type Flags) +{ + return BindEvent( + bFireExistingOnes, + ChunkSize, + DistanceInChunks, + OnGenerate, + FChunkDelegate(), + EVoxelEventFlags::Type(Flags | EVoxelEventFlags::GenerationEvent)); +} + +void FVoxelEventManager::UnbindEvent(FVoxelEventHandle Handle) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(Handle.IsValid())) + { + return; + } + + const FEventKey EventKey{ Handle.ChunkSize, Handle.DistanceInChunks, Handle.Flags }; + auto* EventPtr = Events.Find(EventKey); + if (!ensure(EventPtr)) + { + return; + } + auto& Event = **EventPtr; + Event.OnActivate.Remove(Handle.OnActivateHandle); + Event.OnDeactivate.Remove(Handle.OnDeactivateHandle); + + if (!Event.OnActivate.IsBound() && !Event.OnDeactivate.IsBound()) + { + Events.Remove(EventKey); + EventsInvokers.Remove(EventKey); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEventManager::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + const double Time = FPlatformTime::Seconds(); + if (Time - LastUpdateTime > 1. / Settings.UpdateRate) + { + LastUpdateTime = Time; + Update(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEventManager::Update() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Settings.VoxelWorldInterface.IsValid()) return; + + TArray> NewInvokerComponents = UVoxelInvokerComponentBase::GetInvokers(Settings.World.Get()); + NewInvokerComponents.RemoveAllSwap([](auto& Invoker) { return !Invoker->bUseForEvents; }); + NewInvokerComponents.Sort([](auto& A, auto& B) { return A.Get() < B.Get(); }); + + if (NewInvokerComponents.Num() == 0) return; + + const auto GetInvokerPosition = [&](auto& Invoker) { return Invoker->GetInvokerVoxelPosition(Settings.VoxelWorldInterface.Get()); }; + + TArray>> InvokersToUpdate; + if (NewInvokerComponents != OldInvokerComponents) + { + OldInvokerComponents = NewInvokerComponents; + EventsInvokers.Reset(); + for (auto& EventsIt : Events) + { + const FEventKey EventKey = EventsIt.Key; + const auto& EventInfo = *EventsIt.Value; + + if (!EventInfo.IsBound()) continue; + + check(!EventsInvokers.Contains(EventKey)); + auto& EventInvokers = EventsInvokers.Add(EventKey); + EventInvokers.Reserve(NewInvokerComponents.Num()); + + TArray Positions; + for (auto& InvokerComponent : NewInvokerComponents) + { + if ((EventInfo.Flags & EVoxelEventFlags::LocalInvokerOnly) && !InvokerComponent->IsLocalInvoker()) + { + continue; + } + + const FIntVector Position = GetInvokerPosition(InvokerComponent); + EventInvokers.Emplace(EventInfo.ChunkSize, Position, InvokerComponent); + Positions.Add(Position); + } + + InvokersToUpdate.Emplace(EventKey, MoveTemp(Positions)); + } + } + else + { + for (auto& EventInvokerIt : EventsInvokers) + { + const FEventKey EventKey = EventInvokerIt.Key; + auto& EventInvokers = EventInvokerIt.Value; + + if (!Events[EventKey]->IsBound()) continue; + + // First check if some need to update + bool bUpdate = false; + for (auto& EventInvoker : EventInvokers) + { + const FIntVector Position = GetInvokerPosition(EventInvoker.InvokerComponent); + const uint64 DistanceSquared = FVoxelUtilities::SquaredSize(EventInvoker.Position - Position); + if (DistanceSquared > FMath::Square(EventInvoker.ChunkSize / 4.f)) // Heuristic + { + bUpdate = true; + break; + } + } + if (!bUpdate) continue; + + // If true iterate all to have all the positions + + TArray Positions; + for (auto& EventInvoker : EventInvokers) + { + const FIntVector Position = GetInvokerPosition(EventInvoker.InvokerComponent); + EventInvoker.Position = Position; + Positions.Add(Position); + } + + InvokersToUpdate.Emplace(EventKey, MoveTemp(Positions)); + } + } + if (InvokersToUpdate.Num() > 0) + { + UpdateInvokers(InvokersToUpdate); + } +} + +void FVoxelEventManager::UpdateInvokers(const TArray>>& InvokersToUpdate) +{ + VOXEL_FUNCTION_COUNTER(); + + const bool bDebug = CVarShowEventsBounds.GetValueOnGameThread() != 0; + + for (auto& It : InvokersToUpdate) + { + const FEventKey EventKey = It.Key; + const auto& InvokerPositions = It.Value; + auto& EventInfo = *Events[EventKey]; + + const auto GetChunkBounds = [&](const FIntVector& Chunk) + { + return FVoxelIntBox(Chunk * EventInfo.ChunkSize, (Chunk + 1) * EventInfo.ChunkSize); + }; + + TSet NewActiveChunks; + { + VOXEL_SCOPE_COUNTER("Find NewActiveChunks"); + + // NOTE: This part could be multi threaded + + // Iterate every position and add all chunks within activation radius + for (const FIntVector& InvokerPosition : InvokerPositions) + { + const FIntVector MinChunkPosition = FVoxelUtilities::DivideFloor(InvokerPosition - EventInfo.ChunkSize * EventInfo.DistanceInChunks, EventInfo.ChunkSize); + // Max is exclusive, since this is the coordinate of the Bounds.Min of the chunk + const FIntVector MaxChunkPosition = FVoxelUtilities::DivideCeil(InvokerPosition + EventInfo.ChunkSize * EventInfo.DistanceInChunks, EventInfo.ChunkSize); + + if (!Settings.WorldBounds.Intersect(FVoxelIntBox(MinChunkPosition, MaxChunkPosition))) + { + continue; + } + + const uint64 SquaredDistanceInVoxels = FMath::Square(EventInfo.DistanceInChunks * EventInfo.ChunkSize); + + for (int32 X = MinChunkPosition.X; X < MaxChunkPosition.X; X++) + { + for (int32 Y = MinChunkPosition.Y; Y < MaxChunkPosition.Y; Y++) + { + for (int32 Z = MinChunkPosition.Z; Z < MaxChunkPosition.Z; Z++) + { + const FIntVector Chunk = FIntVector(X, Y, Z); + const FVoxelIntBox ChunkBounds = GetChunkBounds(Chunk); + + if (ChunkBounds.ComputeSquaredDistanceFromBoxToPoint(InvokerPosition) <= SquaredDistanceInVoxels && + ChunkBounds.Intersect(Settings.WorldBounds)) + { + NewActiveChunks.Add(Chunk); + } + } + } + } + } + } + + { + VOXEL_SCOPE_COUNTER("Fire Delegates"); + + if (EventInfo.Flags & EVoxelEventFlags::GenerationEvent) + { + // Generation event: trigger all chunks not already triggered + ensure(!EventInfo.OnDeactivate.IsBound()); + if (ensure(EventInfo.OnActivate.IsBound())) + { + for (auto& Chunk : NewActiveChunks) + { + bool bAlreadyInSet; + EventInfo.ActiveOrGeneratedChunks.Add(Chunk, &bAlreadyInSet); + if (!bAlreadyInSet) + { + const FVoxelIntBox Bounds = GetChunkBounds(Chunk); + EventInfo.OnActivate.Broadcast(Bounds); + + if (bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(Settings.VoxelWorldInterface.Get(), Bounds, 1.f, 0, FColor::Yellow); + } + } + } + } + } + else + { + // Normal event: activate new chunks, deactivate old chunks + if (EventInfo.OnActivate.IsBound()) + { + for (auto& Chunk : NewActiveChunks) + { + if (!EventInfo.ActiveOrGeneratedChunks.Contains(Chunk)) + { + const FVoxelIntBox Bounds = GetChunkBounds(Chunk); + EventInfo.OnActivate.Broadcast(Bounds); + + if (bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(Settings.VoxelWorldInterface.Get(), Bounds, 1.f, 0, FColor::Blue); + } + } + } + } + if (EventInfo.OnDeactivate.IsBound()) + { + for (auto& Chunk : EventInfo.ActiveOrGeneratedChunks) + { + if (!NewActiveChunks.Contains(Chunk)) + { + const FVoxelIntBox Bounds = GetChunkBounds(Chunk); + EventInfo.OnDeactivate.Broadcast(Bounds); + + if (bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(Settings.VoxelWorldInterface.Get(), Bounds, 1.f, 0, FColor::Red); + } + } + } + } + EventInfo.ActiveOrGeneratedChunks = NewActiveChunks; + } + } + } + + UpdateEventsAllocatedSize(); +} + +void FVoxelEventManager::ClearOldInvokerComponents() +{ + OldInvokerComponents.Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEventManager::UpdateEventsAllocatedSize() +{ + VOXEL_FUNCTION_COUNTER(); + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelEventsMemory, EventsAllocatedSize); + DEC_DWORD_STAT_BY(STAT_VoxelEventManager_NumActiveOrGeneratedChunks, NumActiveOrGeneratedChunks); + EventsAllocatedSize = 0; + NumActiveOrGeneratedChunks = 0; + + EventsAllocatedSize += Events.GetAllocatedSize(); + for (auto& It : Events) + { + NumActiveOrGeneratedChunks += It.Value->ActiveOrGeneratedChunks.Num(); + EventsAllocatedSize += It.Value->GetAllocatedSize(); + } + + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelEventsMemory, EventsAllocatedSize); + INC_DWORD_STAT_BY(STAT_VoxelEventManager_NumActiveOrGeneratedChunks, NumActiveOrGeneratedChunks); + + DEC_DWORD_STAT_BY(STAT_NumVoxelEvents, NumEvents); + NumEvents = Events.Num(); + INC_DWORD_STAT_BY(STAT_NumVoxelEvents, NumEvents); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp new file mode 100644 index 00000000..b904f45e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp @@ -0,0 +1,145 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGenerators/VoxelGenerator.h" + +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" +#include "VoxelMessages.h" + +#include "UObject/Package.h" +#include "UObject/MetaData.h" +#include "UObject/PropertyPortFlags.h" + +void UVoxelGenerator::ApplyParameters(const TMap& Parameters) +{ + ApplyParametersInternal(Parameters); +} + +void UVoxelGenerator::GetParameters(TArray& OutParameters) const +{ + VOXEL_FUNCTION_COUNTER(); + + TSet AllIds; + + int32 Priority = 0; + for (TFieldIterator It(GetClass()); It; ++It) + { + auto* Property = *It; + if (!Property->HasAnyPropertyFlags(CPF_Edit) || Property->HasAnyPropertyFlags(CPF_EditConst)) + { + continue; + } + + const FName Id = Property->GetFName(); + FString Name; + FString Category; + FString ToolTip; + TMap MetaData; + +#if WITH_EDITOR + Name = Property->GetDisplayNameText().ToString(); + Category = Property->GetMetaDataText(TEXT("Category")).ToString(); + ToolTip = Property->GetToolTipText().ToString(); + +#if ENGINE_MINOR_VERSION < 25 + { + UPackage* Package = Property->GetOutermost(); + check(Package); + + UMetaData* PackageMetaData = Package->GetMetaData(); + check(PackageMetaData); + + MetaData = PackageMetaData->ObjectMetaDataMap.FindRef(Property); + } +#else + if (Property->GetMetaDataMap()) + { + MetaData = *Property->GetMetaDataMap(); + } +#endif +#else + Name = Property->GetName(); +#endif + + const auto Type = FVoxelGeneratorParameterType(*Property); + + FString DefaultValue; + Property->ExportTextItem(DefaultValue, Property->ContainerPtrToValuePtr(this), nullptr, nullptr, PPF_None); + + OutParameters.Add(FVoxelGeneratorParameter(Id, Type, Name, Category, ToolTip, Priority++, MetaData, DefaultValue)); + + bool bIsInSet = false; + AllIds.Add(Id, &bIsInSet); + ensureMsgf(!bIsInSet, TEXT("%s"), *Id.ToString()); + } +} + +TVoxelSharedRef UVoxelGenerator::GetInstance(const TMap& Parameters) +{ + const auto Backup = ApplyParametersInternal(Parameters); + const auto Result = GetInstance(); + ApplyParametersInternal(Backup); + return Result; +} + +TVoxelSharedRef UVoxelGenerator::GetInstance() +{ + unimplemented(); + return TVoxelSharedPtr().ToSharedRef(); +} + +TMap UVoxelGenerator::ApplyParametersInternal(const TMap& Parameters) +{ + TMap ParametersBackup; + + for (auto& It : Parameters) + { + auto* Property = UE_25_SWITCH(FindField, FindFProperty) < FProperty > (GetClass(), It.Key); + if (!Property) + { + continue; + } + + void* PropertyData = Property->ContainerPtrToValuePtr(this); + // Export backup + Property->ExportTextItem(ParametersBackup.Add(It.Key), PropertyData, nullptr, nullptr, PPF_None); + // Import new value + Property->ImportText(*It.Value, PropertyData, PPF_None, this); + } + + return ParametersBackup; +} + +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelTransformableGenerator::GetTransformableInstance(const TMap& Parameters) +{ + const auto Backup = ApplyParametersInternal(Parameters); + const auto Result = GetTransformableInstance(); + ApplyParametersInternal(Backup); + return Result; +} + +TVoxelSharedRef UVoxelTransformableGenerator::GetTransformableInstance() +{ + unimplemented(); + return TVoxelSharedPtr().ToSharedRef(); +} + +TVoxelSharedRef UVoxelTransformableGenerator::GetInstance(const TMap& Parameters) +{ + return GetTransformableInstance(Parameters); +} + +TVoxelSharedRef UVoxelTransformableGenerator::GetInstance() +{ + return GetTransformableInstance(); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox UVoxelTransformableGeneratorWithBounds::GetBounds() const +{ + unimplemented(); + return {}; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorCache.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorCache.cpp new file mode 100644 index 00000000..40eab562 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorCache.cpp @@ -0,0 +1,25 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGenerators/VoxelGeneratorCache.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorTools.h" + +UVoxelGeneratorInstanceWrapper* UVoxelGeneratorCache::MakeGeneratorInstance(FVoxelGeneratorPicker Picker) const +{ + auto*& Instance = Cache.FindOrAdd(Picker); + if (!Instance) + { + Instance = UVoxelGeneratorTools::MakeGeneratorInstance(Picker, GeneratorInit); + } + return Instance; +} + +UVoxelTransformableGeneratorInstanceWrapper* UVoxelGeneratorCache::MakeTransformableGeneratorInstance(FVoxelTransformableGeneratorPicker Picker) const +{ + auto*& Instance = TransformableCache.FindOrAdd(Picker); + if (!Instance) + { + Instance = UVoxelGeneratorTools::MakeTransformableGeneratorInstance(Picker, GeneratorInit); + } + return Instance; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorParameters.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorParameters.cpp new file mode 100644 index 00000000..7a1c8620 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorParameters.cpp @@ -0,0 +1,203 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGenerators/VoxelGeneratorParameters.h" +#include "UObject/Package.h" + +FString FVoxelGeneratorParameterTerminalType::ToString_Terminal() const +{ + switch (PropertyType) + { + default: ensure(false); + case EVoxelGeneratorParameterPropertyType::Float: return TEXT("float"); + case EVoxelGeneratorParameterPropertyType::Int: return TEXT("int"); + case EVoxelGeneratorParameterPropertyType::Bool: return TEXT("bool"); + case EVoxelGeneratorParameterPropertyType::Object: return FString::Printf(TEXT("%s (object)"), *PropertyClass.ToString()); + case EVoxelGeneratorParameterPropertyType::Struct: return FString::Printf(TEXT("%s (struct)"), *PropertyClass.ToString()); + } +} + +bool FVoxelGeneratorParameterTerminalType::CanBeAssignedFrom_Terminal(const FVoxelGeneratorParameterTerminalType& Other) const +{ + switch (PropertyType) + { + default: ensure(false); + case EVoxelGeneratorParameterPropertyType::Float: + { + switch (Other.PropertyType) + { + case EVoxelGeneratorParameterPropertyType::Float: return true; + case EVoxelGeneratorParameterPropertyType::Int: return true; + default: return false; + } + } + case EVoxelGeneratorParameterPropertyType::Int: + { + switch (Other.PropertyType) + { + case EVoxelGeneratorParameterPropertyType::Int: return true; + default: return false; + } + } + case EVoxelGeneratorParameterPropertyType::Bool: + { + switch (Other.PropertyType) + { + case EVoxelGeneratorParameterPropertyType::Bool: return true; + default: return false; + } + } + case EVoxelGeneratorParameterPropertyType::Object: + { + switch (Other.PropertyType) + { + case EVoxelGeneratorParameterPropertyType::Object: + { + auto* ThisClass = FindObject(ANY_PACKAGE, *PropertyClass.ToString()); + auto* OtherClass = FindObject(ANY_PACKAGE, *Other.PropertyClass.ToString()); + + if (!ThisClass || !OtherClass) + { + ensureVoxelSlow(false); + return false; + } + + return OtherClass->IsChildOf(ThisClass); + } + default: return false; + } + } + case EVoxelGeneratorParameterPropertyType::Struct: + { + switch (Other.PropertyType) + { + case EVoxelGeneratorParameterPropertyType::Struct: + { + auto* ThisStruct = FindObject(ANY_PACKAGE, *PropertyClass.ToString()); + auto* OtherStruct = FindObject(ANY_PACKAGE, *Other.PropertyClass.ToString()); + + if (!ThisStruct || !OtherStruct) + { + ensureVoxelSlow(false); + return false; + } + + return OtherStruct->IsChildOf(ThisStruct); + } + default: return false; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelGeneratorParameterType::FVoxelGeneratorParameterType(FProperty& Property) +{ + if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Float; + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Int; + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Bool; + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Object; + + auto* ObjectProperty = UE_25_SWITCH(Cast, CastField)(&Property); + PropertyClass = ObjectProperty->PropertyClass->GetFName(); + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Object; + + auto* ObjectProperty = UE_25_SWITCH(Cast, CastField)(&Property); + PropertyClass = ObjectProperty->PropertyClass->GetFName(); + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Struct; + + auto* ObjectProperty = UE_25_SWITCH(Cast, CastField)(&Property); + PropertyClass = ObjectProperty->Struct->GetFName(); + } + else if (Property.IsA()) + { + auto* ArrayProperty = UE_25_SWITCH(Cast, CastField)(&Property); + + ContainerType = EVoxelGeneratorParameterContainerType::Array; + + const auto InnerType = FVoxelGeneratorParameterType(*ArrayProperty->Inner); + ensure(InnerType.ContainerType == EVoxelGeneratorParameterContainerType::None); + + PropertyType = InnerType.PropertyType; + PropertyClass = InnerType.PropertyClass; + } + else if (Property.IsA()) + { + auto* SetProperty = UE_25_SWITCH(Cast, CastField)(&Property); + + ContainerType = EVoxelGeneratorParameterContainerType::Set; + + const auto InnerType = FVoxelGeneratorParameterType(*SetProperty->ElementProp); + ensure(InnerType.ContainerType == EVoxelGeneratorParameterContainerType::None); + + PropertyType = InnerType.PropertyType; + PropertyClass = InnerType.PropertyClass; + } + else if (Property.IsA()) + { + auto* MapProperty = UE_25_SWITCH(Cast, CastField)(&Property); + + ContainerType = EVoxelGeneratorParameterContainerType::Map; + + const auto KeyType = FVoxelGeneratorParameterType(*MapProperty->KeyProp); + ensure(KeyType.ContainerType == EVoxelGeneratorParameterContainerType::None); + + const auto LocalValueType = FVoxelGeneratorParameterType(*MapProperty->ValueProp); + ensure(LocalValueType.ContainerType == EVoxelGeneratorParameterContainerType::None); + + PropertyType = KeyType.PropertyType; + PropertyClass = KeyType.PropertyClass; + + ValueType = FVoxelGeneratorParameterTerminalType(LocalValueType); + } + else + { + ensureMsgf(false, TEXT("Property: %s"), *Property.GetNameCPP()); + } +} + +FString FVoxelGeneratorParameterType::ToString() const +{ + switch (ContainerType) + { + default: ensure(false); + case EVoxelGeneratorParameterContainerType::None: return ToString_Terminal(); + case EVoxelGeneratorParameterContainerType::Array: return "Array of " + ToString_Terminal(); + case EVoxelGeneratorParameterContainerType::Set: return "Set of " + ToString_Terminal(); + case EVoxelGeneratorParameterContainerType::Map: return "Map of " + ToString_Terminal() + " to " + ValueType.ToString_Terminal(); + } +} + +bool FVoxelGeneratorParameterType::CanBeAssignedFrom(const FVoxelGeneratorParameterType& Other) const +{ + if (ContainerType != Other.ContainerType) + { + return false; + } + + if (ContainerType == EVoxelGeneratorParameterContainerType::Map && !ValueType.CanBeAssignedFrom_Terminal(Other.ValueType)) + { + return false; + } + + return CanBeAssignedFrom_Terminal(Other); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorPicker.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorPicker.cpp new file mode 100644 index 00000000..bd58f04a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorPicker.cpp @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelMessages.h" + +#include "UObject/Package.h" + +TVoxelSharedRef FVoxelGeneratorPicker::GetInstance(bool bSilent) const +{ + VOXEL_FUNCTION_COUNTER(); + + auto* Generator = GetGenerator(); + if (Generator) + { + return Generator->GetInstance(Parameters); + } + else + { + FVoxelMessages::CondError(!bSilent, FUNCTION_ERROR("Invalid generator")); + return MakeVoxelShared(); + } +} + +TVoxelSharedRef FVoxelTransformableGeneratorPicker::GetInstance(bool bSilent) const +{ + VOXEL_FUNCTION_COUNTER(); + + auto* Generator = GetGenerator(); + if (Generator) + { + return Generator->GetTransformableInstance(Parameters); + } + else + { + FVoxelMessages::CondError(!bSilent, FUNCTION_ERROR("Invalid generator")); + return MakeVoxelShared(); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorTools.cpp new file mode 100644 index 00000000..3d8771d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorTools.cpp @@ -0,0 +1,298 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGenerators/VoxelGeneratorTools.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" +#include "VoxelGenerators/VoxelGeneratorInstanceWrapper.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelMessages.h" +#include "VoxelUtilities/VoxelGeneratorUtilities.h" + +#include "UObject/PropertyPortFlags.h" + +UVoxelGeneratorInstanceWrapper* UVoxelGeneratorTools::MakeGeneratorInstance(FVoxelGeneratorPicker GeneratorPicker, FVoxelGeneratorInit GeneratorInit) +{ + if (!GeneratorPicker.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid generator")); + return nullptr; + } + + auto* Instance = NewObject(); + Instance->Instance = GeneratorPicker.GetInstance(true); + Instance->Instance->Init(GeneratorInit); + return Instance; +} + +UVoxelTransformableGeneratorInstanceWrapper* UVoxelGeneratorTools::MakeTransformableGeneratorInstance(FVoxelTransformableGeneratorPicker GeneratorPicker, FVoxelGeneratorInit GeneratorInit) +{ + if (!GeneratorPicker.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid generator")); + return nullptr; + } + + auto* Instance = NewObject(); + Instance->Instance = GeneratorPicker.GetInstance(true); + Instance->Instance->Init(GeneratorInit); + return Instance; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelGeneratorTools::SetGeneratorParameterImpl(TVoxelGeneratorPicker& Picker, FName Name, FProperty& Property, void* Data, const FString& FunctionName) +{ + if (!CheckIsValidParameterName(Picker, Name, Property, FunctionName)) + { + return false; + } + + FString Result; + Property.ExportTextItem(Result, Data, nullptr, nullptr, PPF_None); + Picker.Parameters.Add(Name, Result); + + return true; +} + +bool UVoxelGeneratorTools::CheckIsValidParameterName( + TVoxelGeneratorPicker GeneratorPicker, + FName Name, + FProperty& Property, + const FString& FunctionName) +{ + if (!GeneratorPicker.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR_IMPL(FunctionName, "Invalid generator")); + return false; + } + + TArray Parameters; + GeneratorPicker.GetGenerator()->GetParameters(Parameters); + + const FVoxelGeneratorParameterType Type(Property); + for (auto& It : Parameters) + { + if (It.Id == Name) + { + if (It.Type.CanBeAssignedFrom(Type)) + { + return true; + } + else + { + FVoxelMessages::Error(FUNCTION_ERROR_IMPL(FunctionName, FString::Printf( + TEXT("Incompatible parameter type: cannot cast from %s to %s"), + *Type.ToString(), + *It.Type.ToString()))); + return false; + } + } + } + + FVoxelMessages::Error(FUNCTION_ERROR_IMPL(FunctionName, FString::Printf(TEXT( + "Parameter name not found: %s. Make sure you use the parameter unique name and not its display name. " + "You can find the unique name in the parameter tooltip in the Generator details."), *Name.ToString()))); + return false; +} + +bool UVoxelGeneratorTools::SetGeneratorParameter(const FVoxelGeneratorPicker& Picker, FName UniqueName, int32 Value) +{ + checkf(false, TEXT("SetGeneratorParameter can only be called from blueprints")); + return false; +} + +bool UVoxelGeneratorTools::SetTransformableGeneratorParameter(const FVoxelTransformableGeneratorPicker& Picker, FName UniqueName, int32 Value) +{ + checkf(false, TEXT("SetTransformableGeneratorParameter can only be called from blueprints")); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +TVoxelTexture UVoxelGeneratorTools::CreateTextureFromGeneratorImpl( + const FVoxelGeneratorInstance& Generator, + FName OutputName, + const FIntPoint& Start, + const FIntPoint& Size, + float Scale) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Size.X * Size.Y); + check(Size.X > 0 && Size.Y > 0); + + using TGeneratorType = typename TChooseClass::Value, v_flt, T>::Result; + + const auto FunctionPtr = Generator.GetOutputsPtrMap().FindRef(OutputName); + if (!ensure(FunctionPtr)) return {}; + + const auto Texture = MakeVoxelShared::FTextureData>(); + Texture->SetSize(Size.X, Size.Y); + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + const auto Value = (Generator.*FunctionPtr)(Scale * (Start.X + X), Scale * (Start.Y + Y), 0, 0, FVoxelItemStack::Empty); + Texture->SetValue(X, Y, T(Value)); + } + } + return TVoxelTexture(Texture); +} + +template VOXEL_API TVoxelTexture UVoxelGeneratorTools::CreateTextureFromGeneratorImpl( + const FVoxelGeneratorInstance& Generator, + FName OutputName, + const FIntPoint& Start, + const FIntPoint& Size, + float Scale); + +template VOXEL_API TVoxelTexture UVoxelGeneratorTools::CreateTextureFromGeneratorImpl( + const FVoxelGeneratorInstance& Generator, + FName OutputName, + const FIntPoint& Start, + const FIntPoint& Size, + float Scale); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +TVoxelSharedPtr SetupGenerator( + const FString& FunctionName, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName, + int32 SizeX, + int32 SizeY) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Generator || !Generator->IsValid()) + { + FVoxelMessages::Error( FUNCTION_ERROR_IMPL(FunctionName, "Invalid Generator!")); + return {}; + } + if (SizeX <= 0 || SizeY <= 0) + { + FVoxelMessages::Error( FUNCTION_ERROR_IMPL(FunctionName, "Invalid Size!")); + return {}; + } + + using TGeneratorType = typename TChooseClass::Value, v_flt, T>::Result; + + if (!Generator->Instance->GetOutputsPtrMap().Contains(OutputName)) + { + FVoxelMessages::Error(FUNCTION_ERROR_IMPL(FunctionName, FVoxelUtilities::GetMissingGeneratorOutputErrorString(OutputName, *Generator->Instance))); + return {}; + } + + return Generator->Instance; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelGeneratorTools::CreateFloatTextureFromGenerator( + FVoxelFloatTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName, + int32 SizeX, + int32 SizeY, + float Scale, + int32 StartX, + int32 StartY) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto Instance = SetupGenerator(__FUNCTION__, Generator, OutputName, SizeX, SizeY); + if (!Instance) return; + + OutTexture.Texture = CreateTextureFromGeneratorImpl(*Instance, OutputName, FIntPoint(StartX, StartY), FIntPoint(SizeX, SizeY), Scale); +} + +void UVoxelGeneratorTools::CreateFloatTextureFromGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelFloatTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName, + int32 SizeX, + int32 SizeY, + float Scale, + int32 StartX, + int32 StartY, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto Instance = SetupGenerator(__FUNCTION__, Generator, OutputName, SizeX, SizeY); + if (!Instance) return; + + FVoxelToolHelpers::StartAsyncLatentAction_WithoutWorld_WithValue( + WorldContextObject, + LatentInfo, + __FUNCTION__, + bHideLatentWarnings, + OutTexture, + [=](FVoxelFloatTexture& Texture) + { + Texture.Texture = CreateTextureFromGeneratorImpl(*Instance, OutputName, FIntPoint(StartX, StartY), FIntPoint(SizeX, SizeY), Scale); + }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelGeneratorTools::CreateColorTextureFromGenerator( + FVoxelColorTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName, + int32 SizeX, + int32 SizeY, + float Scale, + int32 StartX, + int32 StartY) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto Instance = SetupGenerator(__FUNCTION__, Generator, OutputName, SizeX, SizeY); + if (!Instance) return; + + OutTexture.Texture = CreateTextureFromGeneratorImpl(*Instance, OutputName, FIntPoint(StartX, StartY), FIntPoint(SizeX, SizeY), Scale); +} + +void UVoxelGeneratorTools::CreateColorTextureFromGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelColorTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName, + int32 SizeX, + int32 SizeY, + float Scale, + int32 StartX, + int32 StartY, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto Instance = SetupGenerator(__FUNCTION__, Generator, OutputName, SizeX, SizeY); + if (!Instance) return; + + FVoxelToolHelpers::StartAsyncLatentAction_WithoutWorld_WithValue( + WorldContextObject, + LatentInfo, + __FUNCTION__, + bHideLatentWarnings, + OutTexture, + [=](FVoxelColorTexture& Texture) + { + Texture.Texture = CreateTextureFromGeneratorImpl(*Instance, OutputName, FIntPoint(StartX, StartY), FIntPoint(SizeX, SizeY), Scale); + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/README.md b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/README.md new file mode 100644 index 00000000..78e88f98 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/README.md @@ -0,0 +1,25 @@ +# SDFGen +A simple commandline utility to generate grid-based signed distance field (level set) generator from triangle meshes, using code from Robert Bridson's website. + + +The MIT License (MIT) + +Copyright (c) 2015, Christopher Batty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.cpp new file mode 100644 index 00000000..ac94713c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.cpp @@ -0,0 +1,481 @@ +// Copyright 2020 Phyronnaz + +#include "makelevelset3.h" +#include "VoxelImporters/VoxelMeshImporter.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" + +// 10% faster to build with FORCEINLINE +FORCEINLINE float PointSegmentDistance(const FVector& Point, const FVector& A, const FVector& B, float& Alpha) +{ + const FVector AB = B - A; + const float Size = AB.SizeSquared(); + // Find parameter value of closest point on segment + Alpha = FVector::DotProduct(B - Point, AB) / Size; + Alpha = FMath::Clamp(Alpha, 0.f, 1.f); + // And find the distance + return FVector::Dist(Point, FMath::Lerp(B, A, Alpha)); +} + +// 10-20% better perf with this FORCEINLINE +FORCEINLINE float PointTriangleDistance( + const FVector& Point, + const FVector& A, + const FVector& B, + const FVector& C, + float& AlphaA, float& AlphaB, float& AlphaC) +{ + // First find barycentric coordinates of closest point on infinite plane + { + const FVector CA = A - C; + const FVector CB = B - C; + const FVector CPoint = Point - C; + const float SizeCA = CA.SizeSquared(); + const float SizeCB = CB.SizeSquared(); + const float d = FVector::DotProduct(CA, CB); + const float InvDet = 1.f / FMath::Max(SizeCA * SizeCB - d * d, SMALL_NUMBER); + const float a = FVector::DotProduct(CA, CPoint); + const float b = FVector::DotProduct(CB, CPoint); + + // The barycentric coordinates themselves + AlphaA = InvDet * (SizeCB * a - d * b); + AlphaB = InvDet * (SizeCA * b - d * a); + AlphaC = 1 - AlphaA - AlphaB; + } + + if (AlphaA >= 0 && AlphaB >= 0 && AlphaC >= 0) + { + // If we're inside the triangle + return FVector::Dist(Point, AlphaA * A + AlphaB * B + AlphaC * C); + } + else + { + // We have to clamp to one of the edges + + if (AlphaA > 0) + { + // This rules out edge BC for us + float AlphaAB; + const float DistanceAB = PointSegmentDistance(Point, A, B, AlphaAB); + float AlphaAC; + const float DistanceAC = PointSegmentDistance(Point, A, C, AlphaAC); + + if (DistanceAB < DistanceAC) + { + AlphaA = AlphaAB; + AlphaB = 1 - AlphaAB; + AlphaC = 0; + return DistanceAB; + } + else + { + AlphaA = AlphaAC; + AlphaB = 0; + AlphaC = 1 - AlphaAC; + return DistanceAC; + } + } + else if (AlphaB > 0) + { + // This rules out edge AC + float AlphaAB; + const float DistanceAB = PointSegmentDistance(Point, A, B, AlphaAB); + float AlphaBC; + const float DistanceBC = PointSegmentDistance(Point, B, C, AlphaBC); + + if (DistanceAB < DistanceBC) + { + AlphaA = AlphaAB; + AlphaB = 1 - AlphaAB; + AlphaC = 0; + return DistanceAB; + } + else + { + AlphaA = 0; + AlphaB = AlphaBC; + AlphaC = 1 - AlphaBC; + return DistanceBC; + } + } + else + { + ensureVoxelSlowNoSideEffects(AlphaC > 0); + // Rules out edge AB + + float AlphaBC; + const float DistanceBC = PointSegmentDistance(Point, B, C, AlphaBC); + float AlphaAC; + const float DistanceAC = PointSegmentDistance(Point, A, C, AlphaAC); + + if (DistanceBC < DistanceAC) + { + AlphaA = 0; + AlphaB = AlphaBC; + AlphaC = 1 - AlphaBC; + return DistanceBC; + } + else + { + AlphaA = AlphaAC; + AlphaB = 0; + AlphaC = 1 - AlphaAC; + return DistanceAC; + } + } + } +} + +struct FVector2D_Double +{ + double X; + double Y; + + FVector2D_Double() = default; + FVector2D_Double(double X, double Y) + : X(X) + , Y(Y) + { + } + + FORCEINLINE FVector2D_Double operator*(const FVector2D_Double& Other) const + { + return { X * Other.X, Y * Other.Y }; + } + FORCEINLINE FVector2D_Double operator+(const FVector2D_Double& Other) const + { + return { X + Other.X, Y + Other.Y }; + } + FORCEINLINE FVector2D_Double operator-(const FVector2D_Double& Other) const + { + return { X - Other.X, Y - Other.Y }; + } + FORCEINLINE FVector2D_Double& operator-=(const FVector2D_Double& Other) + { + return *this = *this - Other; + } +}; + +// Calculate twice signed area of triangle (0,0)-(A.X,A.Y)-(B.X,B.Y) +// Return an SOS-determined sign (-1, +1, or 0 only if it's a truly degenerate triangle) +inline int32 Orientation( + const FVector2D_Double& A, + const FVector2D_Double& B, + double& TwiceSignedArea) +{ + TwiceSignedArea = A.Y * B.X - A.X * B.Y; + if (TwiceSignedArea > 0) return 1; + else if (TwiceSignedArea < 0) return -1; + else if (B.Y > A.Y) return 1; + else if (B.Y < A.Y) return -1; + else if (A.X > B.X) return 1; + else if (A.X < B.X) return -1; + else return 0; // Only true when A.X == B.X and A.Y == B.Y +} + +// Robust test of (x0,y0) in the triangle (x1,y1)-(x2,y2)-(x3,y3) +// If true is returned, the barycentric coordinates are set in a,b,c. +inline bool PointInTriangle2D( + const FVector2D_Double& Point, + FVector2D_Double A, + FVector2D_Double B, + FVector2D_Double C, + double& AlphaA, double& AlphaB, double& AlphaC) +{ + A -= Point; + B -= Point; + C -= Point; + + const int32 SignA = Orientation(B, C, AlphaA); + if (SignA == 0) return false; + + const int32 SignB = Orientation(C, A, AlphaB); + if (SignB != SignA) return false; + + const int32 SignC = Orientation(A, B, AlphaC); + if (SignC != SignA) return false; + + const double Sum = AlphaA + AlphaB + AlphaC; + checkSlow(Sum != 0); // if the SOS signs match and are non-zero, there's no way all of a, b, and c are zero. + AlphaA /= Sum; + AlphaB /= Sum; + AlphaC /= Sum; + + return true; +} + +void MakeLevelSet3( + const FVoxelMeshImporterSettingsBase& Settings, + const FMakeLevelSet3InData& InData, + FMakeLevelSet3OutData& OutData) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(!InData.bExportUVs || InData.UVs.Num() == InData.Vertices.Num()); + check(InData.ColorTextures.Num() == 0 || InData.UVs.Num() == InData.Vertices.Num()); + + const FIntVector Size = InData.Size; + + OutData.Phi.Resize(Size); + OutData.Phi.Assign(MAX_flt); + + if (InData.bExportUVs) + { + OutData.UVs.Resize(Size); + OutData.UVs.Memzero(); + } + + if (InData.bExportPositions) + { + OutData.Positions.Resize(Size); + OutData.Positions.Assign(FVector(MAX_flt)); + } + + OutData.Colors.SetNum(InData.ColorTextures.Num()); + for (int32 Index = 0; Index < InData.ColorTextures.Num(); Index++) + { + OutData.Colors[Index].Resize(Size); + OutData.Colors[Index].Memzero(); + } + + TVoxelArray3 Computed(Size); + Computed.Memzero(); + + TVoxelArray3 ClosestTriangleIndices(Size); + ClosestTriangleIndices.Assign(-1); + + TVoxelArray3 IntersectionCount(Size); // IntersectionCount(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} + IntersectionCount.Assign(0); + + // Find the axis mappings based on the sweep direction + int32 IndexI; + int32 IndexJ; + int32 IndexK; + switch (Settings.SweepDirection) + { + default: ensure(false); + case EVoxelAxis::X: IndexI = 1; IndexJ = 2; IndexK = 0; break; + case EVoxelAxis::Y: IndexI = 2; IndexJ = 0; IndexK = 1; break; + case EVoxelAxis::Z: IndexI = 0; IndexJ = 1; IndexK = 2; break; + } + + // We begin by initializing distances near the mesh, and figuring out intersection counts + { + VOXEL_ASYNC_SCOPE_COUNTER("Intersections"); + for (int32 TriangleIndex = 0; TriangleIndex < InData.Triangles.Num(); TriangleIndex++) + { + VOXEL_SLOW_SCOPE_COUNTER("Process triangle"); + + const FIntVector& Triangle = InData.Triangles[TriangleIndex]; + const int32 IndexA = Triangle.X; + const int32 IndexB = Triangle.Y; + const int32 IndexC = Triangle.Z; + + const FVector& VertexA = InData.Vertices[IndexA]; + const FVector& VertexB = InData.Vertices[IndexB]; + const FVector& VertexC = InData.Vertices[IndexC]; + + const auto ToVoxelSpace = [&](const FVector& Value) + { + return (Value - InData.Origin) / Settings.VoxelSize; + }; + const auto FromVoxelSpace = [&](const FVector& Value) + { + return Value * Settings.VoxelSize + InData.Origin; + }; + + const FVector VoxelVertexA = ToVoxelSpace(VertexA); + const FVector VoxelVertexB = ToVoxelSpace(VertexB); + const FVector VoxelVertexC = ToVoxelSpace(VertexC); + + const FVector MinVoxelVertex = FVoxelUtilities::ComponentMin3(VoxelVertexA, VoxelVertexB, VoxelVertexC); + const FVector MaxVoxelVertex = FVoxelUtilities::ComponentMax3(VoxelVertexA, VoxelVertexB, VoxelVertexC); + + { + VOXEL_SLOW_SCOPE_COUNTER("Compute distance"); + + const FIntVector Start = FVoxelUtilities::Clamp(FVoxelUtilities::FloorToInt(MinVoxelVertex) - Settings.ExactBand, FIntVector(0), Size - 1); + const FIntVector End = FVoxelUtilities::Clamp(FVoxelUtilities::CeilToInt(MaxVoxelVertex) + Settings.ExactBand, FIntVector(0), Size - 1); + + // Do distances nearby + for (int32 Z = Start.Z; Z <= End.Z; Z++) + { + for (int32 Y = Start.Y; Y <= End.Y; Y++) + { + for (int32 X = Start.X; X <= End.X; X++) + { + const FVector Position = FromVoxelSpace(FVector(X, Y, Z)); + float AlphaA, AlphaB, AlphaC; + const float Distance = PointTriangleDistance(Position, VertexA, VertexB, VertexC, AlphaA, AlphaB, AlphaC); + if (Distance < OutData.Phi(X, Y, Z)) + { + Computed(X, Y, Z) = true; + OutData.Phi(X, Y, Z) = Distance; + ClosestTriangleIndices(X, Y, Z) = TriangleIndex; + + if (InData.bExportUVs || InData.ColorTextures.Num() > 0) + { + const FVector2D UV = AlphaA * InData.UVs[IndexA] + AlphaB * InData.UVs[IndexB] + AlphaC * InData.UVs[IndexC]; + if (InData.bExportUVs) + { + OutData.UVs(X, Y, Z) = UV; + } + for (int32 Index = 0; Index < InData.ColorTextures.Num(); Index++) + { + const auto& ColorTexture = InData.ColorTextures[Index]; + OutData.Colors[Index](X, Y, Z) = ColorTexture.Sample( + UV.X * ColorTexture.GetSizeX(), + UV.Y * ColorTexture.GetSizeY(), + EVoxelSamplerMode::Tile).ToFColor(true); + } + } + + if (InData.bExportPositions) + { + OutData.Positions(X, Y, Z) = AlphaA * VoxelVertexA + AlphaB * VoxelVertexB + AlphaC * VoxelVertexC; + } + } + } + } + } + } + + { + VOXEL_SLOW_SCOPE_COUNTER("Compute intersections"); + + const FIntVector Start = FVoxelUtilities::Clamp(FVoxelUtilities::CeilToInt(MinVoxelVertex), FIntVector(0), Size - 1); + const FIntVector End = FVoxelUtilities::Clamp(FVoxelUtilities::FloorToInt(MaxVoxelVertex), FIntVector(0), Size - 1); + + // Do intersection counts. Make sure to follow SweepDirection! + FIntVector Position; + for (int32 I = Start[IndexI]; I <= End[IndexI]; I++) + { + Position[IndexI] = I; + for (int32 J = Start[IndexJ]; J <= End[IndexJ]; J++) + { + Position[IndexJ] = J; + + const auto Get2D = [&](const FVector& V) { return FVector2D_Double(V[IndexI], V[IndexJ]); }; + + double AlphaA, AlphaB, AlphaC; + if (PointInTriangle2D(FVector2D_Double(I, J), Get2D(VoxelVertexA), Get2D(VoxelVertexB), Get2D(VoxelVertexC), AlphaA, AlphaB, AlphaC)) + { + const float K = AlphaA * VoxelVertexA[IndexK] + AlphaB * VoxelVertexB[IndexK] + AlphaC * VoxelVertexC[IndexK]; // Intersection K coordinate +#if 1 + Position[IndexK] = FMath::Clamp(Settings.bReverseSweep ? FMath::FloorToInt(K) : FMath::CeilToInt(K), 0, Size[IndexK] - 1); + IntersectionCount(Position)++; +#else + const int32 IntervalX = FMath::CeilToInt(X); // Intersection is in (IntervalX - 1, IntervalX] + if (IntervalX < 0) + { + IntersectionCount(FMath::Clamp(FMath::CeilToInt(X), 0, Size.X - 1), Y, Z)++; // We enlarge the first interval to include everything to the -X direction + } + else if (IntervalX < Size.X) + { + IntersectionCount(IntervalX, Y, Z)++; // We ignore intersections that are beyond the +X side of the grid + } +#endif + } + } + } + } + } + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Compute Signs"); + + // Then figure out signs (inside/outside) from intersection counts + FIntVector Position; + for (int32 I = 0; I < Size[IndexI]; I++) + { + Position[IndexI] = I; + for (int32 J = 0; J < Size[IndexJ]; J++) + { + Position[IndexJ] = J; + + // Compute the number of intersections, and skip it if it's a leak + if (Settings.bHideLeaks) + { + int32 Count = 0; + for (int32 K = 0; K < Size[IndexK]; K++) + { + Position[IndexK] = K; + Count += IntersectionCount(Position); + } + + if (Settings.bWatertight) + { + if (Count % 2 == 1) + { + // For watertight meshes, we're expecting to come in and out of the mesh + OutData.NumLeaks++; + continue; + } + } + else + { + if (Count == 0) + { + // For other meshes, only skip when there was no hit + OutData.NumLeaks++; + continue; + } + } + } + // If we are not watertight, start inside (unless we're reverse) + int32 Count = (!Settings.bWatertight && !Settings.bReverseSweep) ? 1 : 0; + + FVector2D LastUV = FVector2D(ForceInit); + + TArray> LastColors; + LastColors.SetNum(InData.ColorTextures.Num()); + + for (int32 K = 0; K < Size[IndexK]; K++) + { + Position[IndexK] = Settings.bReverseSweep ? Size[IndexK] - 1 - K : K; + + Count += IntersectionCount(Position); + if (Count % 2 == 1) + { + // If parity of intersections so far is odd, we are inside the mesh + OutData.Phi(Position) *= -1; + } + + // Only propagate values that are actually valid + const bool bComputed = Computed(Position); + if (InData.bExportUVs) + { + if (!bComputed) + { + OutData.UVs(Position) = LastUV; + } + else + { + LastUV = OutData.UVs(Position); + } + } + for (int32 Index = 0; Index < InData.ColorTextures.Num(); Index++) + { + if (!bComputed) + { + OutData.Colors[Index](Position) = LastColors[Index]; + } + else + { + LastColors[Index] = OutData.Colors[Index](Position); + } + } + } + } + } + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Divide distances"); + for (float& Distance : OutData.Phi.Data) + { + Distance /= Settings.VoxelSize; + Distance /= Settings.DistanceDivisor; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.h new file mode 100644 index 00000000..c6fd18bb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/SDFGen/makelevelset3.h @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTexture.h" +#include "VoxelContainers/VoxelArray3.h" +#include "VoxelContainers/VoxelArrayView.h" + +struct FVoxelMeshImporterSettingsBase; + +struct FMakeLevelSet3InData +{ + TVoxelArrayView Vertices; + TVoxelArrayView Triangles; + + TVoxelArrayView UVs; + TArray> ColorTextures; + + FVector Origin { ForceInit}; + FIntVector Size { ForceInit }; + + bool bExportPositions = false; + bool bExportUVs = false; +}; + +struct FMakeLevelSet3OutData +{ + TVoxelArray3 Phi; + // In voxel space + TVoxelArray3 Positions; + TVoxelArray3 UVs; + TArray> Colors; + + int32 NumLeaks = 0; +}; + +VOXEL_API void MakeLevelSet3( + const FVoxelMeshImporterSettingsBase& Settings, + const FMakeLevelSet3InData& InData, + FMakeLevelSet3OutData& OutData); + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelLandscapeImporter.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelLandscapeImporter.cpp new file mode 100644 index 00000000..37b01fd2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelLandscapeImporter.cpp @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelImporters/VoxelLandscapeImporter.h" +#include "Landscape.h" +#include "VoxelMinimal.h" + +#if WITH_EDITOR +void AVoxelLandscapeImporter::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && + PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_STATIC(AVoxelLandscapeImporter, Landscape) && + PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive && + Landscape && + LayerInfos.Num() == 0) + { + for (auto& Layer : Landscape->EditorLayerSettings) + { + FVoxelLandscapeImporterLayerInfo LayerInfo; + LayerInfo.LayerInfo = Layer.LayerInfoObj; + LayerInfo.Layer = EVoxelRGBA(LayerInfos.Num() % 4); + LayerInfo.Index = LayerInfos.Num(); + LayerInfos.Add(LayerInfo); + } + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp new file mode 100644 index 00000000..c0927fce --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp @@ -0,0 +1,633 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelImporters/VoxelMeshImporter.h" + +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelMessages.h" + +#include "SDFGen/makelevelset3.h" + +#include "Components/StaticMeshComponent.h" +#include "Engine/StaticMesh.h" +#include "Engine/TextureRenderTarget2D.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "Kismet/KismetRenderingLibrary.h" + +static void GetMergedSectionFromStaticMesh( + UStaticMesh* InMesh, + int32 LODIndex, + TArray& Vertices, + TArray& Indices, + TArray& UVs) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(InMesh->UE_27_SWITCH(RenderData, GetRenderData())) || !ensure(InMesh->UE_27_SWITCH(RenderData, GetRenderData())->LODResources.IsValidIndex(LODIndex))) return; + + const FStaticMeshLODResources& LODResources = InMesh->UE_27_SWITCH(RenderData, GetRenderData())->LODResources[LODIndex]; + const FRawStaticIndexBuffer& IndexBuffer = LODResources.IndexBuffer; + const FPositionVertexBuffer& PositionVertexBuffer = LODResources.VertexBuffers.PositionVertexBuffer; + const FStaticMeshVertexBuffer& StaticMeshVertexBuffer = LODResources.VertexBuffers.StaticMeshVertexBuffer; + const int32 NumTextureCoordinates = StaticMeshVertexBuffer.GetNumTexCoords(); + + ensure(IndexBuffer.GetNumIndices() % 3 == 0); + + const auto Get = [](auto& Array, auto Index) -> auto& + { +#if VOXEL_DEBUG + return Array[Index]; +#else + return Array.GetData()[Index]; +#endif + }; + + if (!FPlatformProperties::RequiresCookedData() || InMesh->bAllowCPUAccess) + { + { + VOXEL_SCOPE_COUNTER("Copy Vertices from CPU"); + Vertices.SetNumUninitialized(PositionVertexBuffer.GetNumVertices()); + for (uint32 Index = 0; Index < PositionVertexBuffer.GetNumVertices(); Index++) + { + Get(Vertices, Index) = PositionVertexBuffer.VertexPosition(Index); + } + } + { + VOXEL_SCOPE_COUNTER("Copy Triangles from CPU"); + Indices.SetNumUninitialized(IndexBuffer.GetNumIndices()); + for (int32 Index = 0; Index < IndexBuffer.GetNumIndices(); Index++) + { + Get(Indices, Index) = IndexBuffer.GetIndex(Index); + } + } + if (NumTextureCoordinates > 0) + { + VOXEL_SCOPE_COUNTER("Copy UVs from CPU"); + UVs.SetNumUninitialized(StaticMeshVertexBuffer.GetNumVertices()); + for (uint32 Index = 0; Index < StaticMeshVertexBuffer.GetNumVertices(); Index++) + { + Get(UVs, Index) = StaticMeshVertexBuffer.GetVertexUV(Index, 0); + } + } + } + else + { + LOG_VOXEL(Log, TEXT("Extracting mesh data from GPU for %s"), *InMesh->GetName()); + + ENQUEUE_RENDER_COMMAND(VoxelDistanceFieldCompute)([&](FRHICommandListImmediate& RHICmdList) + { + { + VOXEL_SCOPE_COUNTER("Copy Vertices from GPU"); + Vertices.SetNumUninitialized(PositionVertexBuffer.GetNumVertices()); + const int32 NumBytes = PositionVertexBuffer.GetNumVertices() * PositionVertexBuffer.GetStride(); + + void* BufferData = RHICmdList.LockVertexBuffer(PositionVertexBuffer.VertexBufferRHI, 0, NumBytes, EResourceLockMode::RLM_ReadOnly); + FMemory::Memcpy(Vertices.GetData(), BufferData, NumBytes); + RHICmdList.UnlockVertexBuffer(PositionVertexBuffer.VertexBufferRHI); + } + { + VOXEL_SCOPE_COUNTER("Copy Triangles from GPU"); + Indices.SetNumUninitialized(IndexBuffer.GetNumIndices()); + + const bool bIs32Bit = IndexBuffer.Is32Bit(); + const int32 NumBytes = IndexBuffer.GetNumIndices() * (bIs32Bit ? sizeof(uint32) : sizeof(uint16)); + + void* BufferData = RHICmdList.LockIndexBuffer(IndexBuffer.IndexBufferRHI, 0, NumBytes, EResourceLockMode::RLM_ReadOnly); + if (bIs32Bit) + { + FMemory::Memcpy(Indices.GetData(), BufferData, NumBytes); + } + else + { + TArray Indices16; + Indices16.SetNumUninitialized(IndexBuffer.GetNumIndices()); + FMemory::Memcpy(Indices16.GetData(), BufferData, NumBytes); + for (int32 Index = 0; Index < Indices16.Num(); Index++) + { + Get(Indices, Index) = Get(Indices16, Index); + } + } + RHICmdList.UnlockIndexBuffer(IndexBuffer.IndexBufferRHI); + } + if (NumTextureCoordinates > 0) + { + VOXEL_SCOPE_COUNTER("Copy UVs from GPU"); + UVs.SetNumUninitialized(StaticMeshVertexBuffer.GetNumVertices()); + + const bool bFullPrecision = StaticMeshVertexBuffer.GetUseFullPrecisionUVs(); + const int32 NumBytes = StaticMeshVertexBuffer.GetNumVertices() * (bFullPrecision ? sizeof(FVector2D) : sizeof(FVector2DHalf)); + + void* BufferData = RHICmdList.LockVertexBuffer(StaticMeshVertexBuffer.TexCoordVertexBuffer.VertexBufferRHI, 0, NumBytes, EResourceLockMode::RLM_ReadOnly); + if (bFullPrecision) + { + FMemory::Memcpy(UVs.GetData(), BufferData, NumBytes); + } + else + { + TArray UVsHalf; + UVsHalf.SetNumUninitialized(StaticMeshVertexBuffer.GetNumVertices()); + FMemory::Memcpy(UVsHalf.GetData(), BufferData, NumBytes); + for (int32 Index = 0; Index < UVsHalf.Num(); Index++) + { + Get(UVs, Index) = Get(UVsHalf, Index); + } + } + RHICmdList.UnlockVertexBuffer(StaticMeshVertexBuffer.TexCoordVertexBuffer.VertexBufferRHI); + } + }); + + FRenderCommandFence Fence; + Fence.BeginFence(); + Fence.Wait(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMeshImporterSettings::FVoxelMeshImporterSettings() +{ + ColorsMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color")); + UVsMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs")); +} + +FVoxelMeshImporterSettings::FVoxelMeshImporterSettings(const FVoxelMeshImporterSettingsBase& Base) + : FVoxelMeshImporterSettingsBase(Base) +{ + bImportColors = false; + bImportUVs = false; +} + +void UVoxelMeshImporterLibrary::CreateMeshDataFromStaticMesh(UStaticMesh* StaticMesh, FVoxelMeshImporterInputData& Data) +{ + VOXEL_FUNCTION_COUNTER(); + + check(StaticMesh); + Data.Vertices.Reset(); + Data.Triangles.Reset(); + Data.UVs.Reset(); + + const int32 LOD = 0; + TArray Indices; + + GetMergedSectionFromStaticMesh(StaticMesh, LOD, Data.Vertices, Indices, Data.UVs); + + const auto Get = [](auto& Array, auto Index) -> auto& + { +#if VOXEL_DEBUG + return Array[Index]; +#else + return Array.GetData()[Index]; +#endif + }; + + for (uint32 Index : Indices) + { + if (Index >= uint32(Data.Vertices.Num())) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid index buffer")); + Data = {}; + return; + } + } + + ensure(Indices.Num() % 3 == 0); + Data.Triangles.SetNumUninitialized(Indices.Num() / 3); + for (int32 Index = 0; Index < Data.Triangles.Num(); Index++) + { + Get(Data.Triangles, Index) = FIntVector( + Get(Indices, 3 * Index + 0), + Get(Indices, 3 * Index + 1), + Get(Indices, 3 * Index + 2)); + } +} + +bool UVoxelMeshImporterLibrary::ConvertMeshToVoxels( + UObject* WorldContextObject, + const FVoxelMeshImporterInputData& Mesh, + const FTransform& Transform, + const FVoxelMeshImporterSettings& Settings, + FVoxelMeshImporterRenderTargetCache& RenderTargetCache, + FVoxelDataAssetData& OutAsset, + FIntVector& OutOffset, + int32& OutNumLeaks) +{ + VOXEL_FUNCTION_COUNTER(); + + if (RenderTargetCache.LastRenderedRenderTargetSize != Settings.RenderTargetSize) + { + RenderTargetCache.LastRenderedRenderTargetSize = Settings.RenderTargetSize; + RenderTargetCache.LastRenderedColorsMaterial = nullptr; + RenderTargetCache.LastRenderedUVsMaterial = nullptr; + RenderTargetCache.ColorsRenderTarget = nullptr; + RenderTargetCache.UVsRenderTarget = nullptr; + } + + TVoxelTexture ColorTexture; + TVoxelTexture UVTexture; + if (Settings.bImportColors) + { + if (!Settings.ColorsMaterial) + { + FVoxelMessages::Error(FUNCTION_ERROR("PaintColors = true but ColorsMaterial = nullptr")); + return false; + } + if (!RenderTargetCache.ColorsRenderTarget || Settings.ColorsMaterial != RenderTargetCache.LastRenderedColorsMaterial) + { + RenderTargetCache.LastRenderedColorsMaterial = Settings.ColorsMaterial; + FVoxelTextureUtilities::ClearCache(RenderTargetCache.ColorsRenderTarget); + RenderTargetCache.ColorsRenderTarget = CreateTextureFromMaterial( + WorldContextObject, + Settings.ColorsMaterial, + Settings.RenderTargetSize, + Settings.RenderTargetSize); + } + ColorTexture = FVoxelTextureUtilities::CreateFromTexture_Color(RenderTargetCache.ColorsRenderTarget); + } + if (Settings.bImportUVs) + { + if (!Settings.UVsMaterial) + { + FVoxelMessages::Error(FUNCTION_ERROR("PaintUVChannels = true but UVChannelsMaterial = nullptr")); + return false; + } + if (!RenderTargetCache.UVsRenderTarget || Settings.UVsMaterial != RenderTargetCache.LastRenderedUVsMaterial) + { + RenderTargetCache.LastRenderedUVsMaterial = Settings.UVsMaterial; + FVoxelTextureUtilities::ClearCache(RenderTargetCache.UVsRenderTarget); + RenderTargetCache.UVsRenderTarget = CreateTextureFromMaterial( + WorldContextObject, + Settings.UVsMaterial, + Settings.RenderTargetSize, + Settings.RenderTargetSize); + } + UVTexture = FVoxelTextureUtilities::CreateFromTexture_Color(RenderTargetCache.UVsRenderTarget); + } + + OutNumLeaks = 0; + + TArray Vertices; + Vertices.Reserve(Mesh.Vertices.Num()); + FBox Box(ForceInit); + for (auto& Vertex : Mesh.Vertices) + { + const auto NewVertex = Transform.TransformPosition(Vertex); + Vertices.Add(NewVertex); + Box += NewVertex; + } + Box = Box.ExpandBy(Settings.VoxelSize); + + const FVector SizeFloat = Box.GetSize() / Settings.VoxelSize; + const FIntVector Size(FVoxelUtilities::CeilToInt(SizeFloat)); + const FVector Origin = Box.Min; + + if (int64(Size.X) * int64(Size.Y) * int64(Size.Z) >= MAX_int32) + { + FVoxelMessages::Error(FUNCTION_ERROR("Converted assets would have more than 2B voxels! Abording")); + return false; + } + if (Size.X * Size.Y * Size.Z == 0) + { + FVoxelMessages::Error(FUNCTION_ERROR("Size = 0! Abording")); + return false; + } + + FMakeLevelSet3InData InData; + { + InData.Vertices = Vertices; + InData.Triangles = Mesh.Triangles; + InData.UVs = Mesh.UVs; + + InData.Origin = Origin; + InData.Size = Size; + + InData.bExportPositions = false; + InData.bExportUVs = false; + + if (Settings.bImportColors) + { + InData.ColorTextures.Add(ColorTexture); + } + if (Settings.bImportUVs) + { + InData.ColorTextures.Add(UVTexture); + } + } + + FMakeLevelSet3OutData OutData; + MakeLevelSet3(Settings, InData, OutData); + + OutOffset = FVoxelUtilities::RoundToInt(Box.Min / Settings.VoxelSize); + OutNumLeaks = OutData.NumLeaks; + + const bool bHasMaterials = Settings.bImportColors || Settings.bImportUVs; + OutAsset.SetSize(Size, bHasMaterials); + + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + OutAsset.SetValue(X, Y, Z, FVoxelValue(OutData.Phi(X, Y, Z))); + if (bHasMaterials) + { + FVoxelMaterial Material(ForceInit); + if (Settings.bImportColors) + { + const FColor Color = OutData.Colors[0](X, Y, Z); + Material.SetColor(Color); + } + if (Settings.bImportUVs) + { + const FColor Color = OutData.Colors.Last()(X, Y, Z); + Material.SetU(uint8(0), Color.R); + Material.SetV(uint8(0), Color.G); + Material.SetU(uint8(1), Color.B); + Material.SetV(uint8(1), Color.A); + } + OutAsset.SetMaterial(X, Y, Z, Material); + } + } + } + } + + return true; +} + +void UVoxelMeshImporterLibrary::ConvertMeshToDistanceField( + const FVoxelMeshImporterInputData& Mesh, + const FTransform& Transform, + const FVoxelMeshImporterSettingsBase& Settings, + float BoxExtension, + TArray& OutDistanceField, + TArray& OutSurfacePositions, + FIntVector& OutSize, + FIntVector& OutOffset, + int32& OutNumLeaks, + EVoxelComputeDevice Device, + bool bMultiThreaded, + int32 MaxPasses_Debug) +{ + VOXEL_FUNCTION_COUNTER(); + + TArray Vertices; + Vertices.Reserve(Mesh.Vertices.Num()); + FBox Box(ForceInit); + for (auto& Vertex : Mesh.Vertices) + { + const auto NewVertex = Transform.TransformPosition(Vertex); + Vertices.Add(NewVertex); + Box += NewVertex; + } + Box = Box.ExpandBy(Settings.VoxelSize * BoxExtension); + + const FVector SizeFloat = Box.GetSize() / Settings.VoxelSize; + const FIntVector Size(FVoxelUtilities::CeilToInt(SizeFloat)); + const FVector Origin = Box.Min; + + if (int64(Size.X) * int64(Size.Y) * int64(Size.Z) >= MAX_int32) + { + FVoxelMessages::Error(FUNCTION_ERROR("Converted assets would have more than 2B voxels! Abording")); + return; + } + if (Size.X * Size.Y * Size.Z == 0) + { + FVoxelMessages::Error(FUNCTION_ERROR("Size = 0! Abording")); + return; + } + + FMakeLevelSet3InData InData; + { + InData.Vertices = Vertices; + InData.Triangles = Mesh.Triangles; + InData.UVs = Mesh.UVs; + + InData.Origin = Origin; + InData.Size = Size; + + InData.bExportPositions = true; + InData.bExportUVs = false; + } + + auto SettingsCopy = Settings; + SettingsCopy.DistanceDivisor = 1; + SettingsCopy.ExactBand = 0.f; // No need for anything bigger + + FMakeLevelSet3OutData OutData; + MakeLevelSet3(SettingsCopy, InData, OutData); + + OutDistanceField = MoveTemp(OutData.Phi.Data); + OutSurfacePositions = MoveTemp(OutData.Positions.Data); + + OutSize = Size; + OutOffset = FVoxelUtilities::RoundToInt(Box.Min / Settings.VoxelSize); + OutNumLeaks = OutData.NumLeaks; + + // Propagate distances + FVoxelDistanceFieldUtilities::JumpFlood(Size, OutSurfacePositions, Device, bMultiThreaded, MaxPasses_Debug); + FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(Size, OutSurfacePositions, OutDistanceField); +} + +UVoxelMeshImporterInputData* UVoxelMeshImporterLibrary::CreateMeshDataFromStaticMesh(UStaticMesh* StaticMesh) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!StaticMesh) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid StaticMesh")); + return nullptr; + } + auto* Object = NewObject(GetTransientPackage()); + CreateMeshDataFromStaticMesh(StaticMesh, Object->Data); + return Object; +} + +UTextureRenderTarget2D* UVoxelMeshImporterLibrary::CreateTextureFromMaterial( + UObject* WorldContextObject, + UMaterialInterface* Material, + int32 Width, + int32 Height) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!WorldContextObject) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid WorldContextObject")); + return nullptr; + } + if (!Material) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Material")); + return nullptr; + } + if (Width <= 0) + { + FVoxelMessages::Error(FUNCTION_ERROR("Width <= 0")); + return nullptr; + } + if (Height <= 0) + { + FVoxelMessages::Error(FUNCTION_ERROR("Height <= 0")); + return nullptr; + } + + UTextureRenderTarget2D* RenderTarget2D = UKismetRenderingLibrary::CreateRenderTarget2D(WorldContextObject, Width, Height, ETextureRenderTargetFormat::RTF_RGBA8); + UKismetRenderingLibrary::DrawMaterialToRenderTarget(WorldContextObject, RenderTarget2D, Material); + return RenderTarget2D; +} + +void UVoxelMeshImporterLibrary::ConvertMeshToVoxels( + UObject* WorldContextObject, + UVoxelMeshImporterInputData* Mesh, + FTransform Transform, + bool bSubtractive, + FVoxelMeshImporterSettings Settings, + FVoxelMeshImporterRenderTargetCache& RenderTargetCache, + UVoxelDataAsset*& Asset, + int32& NumLeaks) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Mesh) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Mesh")); + return; + } + + if (bSubtractive) + { + Settings.DistanceDivisor *= -1; + } + + FIntVector PositionOffset{ ForceInit }; + + const auto Data = MakeVoxelShared(); + if (!ConvertMeshToVoxels(WorldContextObject, Mesh->Data, Transform, Settings, RenderTargetCache, *Data, PositionOffset, NumLeaks)) + { + return; + } + + Asset = NewObject(GetTransientPackage()); + Asset->bSubtractiveAsset = bSubtractive; + Asset->PositionOffset = PositionOffset; + Asset->SetData(Data); +} + +void UVoxelMeshImporterLibrary::ConvertMeshToVoxels_NoMaterials( + UObject* WorldContextObject, + UVoxelMeshImporterInputData* Mesh, + FTransform Transform, + bool bSubtractive, + FVoxelMeshImporterSettingsBase Settings, + UVoxelDataAsset*& Asset, + int32& NumLeaks) +{ + FVoxelMeshImporterRenderTargetCache RenderTargetCache; + ConvertMeshToVoxels(WorldContextObject, Mesh, Transform, bSubtractive, FVoxelMeshImporterSettings(Settings), RenderTargetCache, Asset, NumLeaks); + + ensure(!RenderTargetCache.ColorsRenderTarget); + ensure(!RenderTargetCache.UVsRenderTarget); + ensure(!RenderTargetCache.LastRenderedColorsMaterial); + ensure(!RenderTargetCache.LastRenderedUVsMaterial); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +AVoxelMeshImporter::AVoxelMeshImporter() +{ +#if WITH_EDITOR + MeshComponent = CreateDefaultSubobject("Mesh"); + + StaticMesh = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_SM_Chair")); + MeshComponent->SetStaticMesh(StaticMesh); + MeshComponent->SetRelativeScale3D(FVector(100.f)); + RootComponent = MeshComponent; + + PrimaryActorTick.bCanEverTick = true; +#endif +} + +void AVoxelMeshImporter::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (GetWorld()->WorldType != EWorldType::Editor) + { + Destroy(); + } + + if (StaticMesh) + { + if (CachedStaticMesh != StaticMesh) + { + CachedStaticMesh = StaticMesh; + + TArray Indices; + TArray UVs; + CachedVertices.Reset(); + GetMergedSectionFromStaticMesh(StaticMesh, 0, CachedVertices, Indices, UVs); + } + + // TODO: Use PostEditMove + const FTransform Transform = GetTransform(); + if (CachedTransform.ToMatrixWithScale() != Transform.ToMatrixWithScale()) + { + CachedTransform = Transform; + + CachedBox = FBox(ForceInit); + for (auto& Vertex : CachedVertices) + { + CachedBox += Transform.TransformPosition(Vertex); + } + CachedBox = CachedBox.ExpandBy(Settings.VoxelSize); + + InitMaterialInstance(); + MaterialInstance->SetVectorParameterValue("Offset", CachedBox.Min); + } + + UpdateSizes(); + } +} + +#if WITH_EDITOR +void AVoxelMeshImporter::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + MeshComponent->SetStaticMesh(StaticMesh); + InitMaterialInstance(); + MaterialInstance->SetScalarParameterValue("VoxelSize", Settings.VoxelSize); + UpdateSizes(); +} +#endif + +void AVoxelMeshImporter::InitMaterialInstance() +{ + if (MaterialInstance) + { + return; + } + auto* Material = LoadObject(nullptr, TEXT("Material'/Voxel/MaterialHelpers/MeshImporterMaterial.MeshImporterMaterial'")); + MaterialInstance = UMaterialInstanceDynamic::Create(Material, GetTransientPackage()); + MeshComponent->SetMaterial(0, MaterialInstance); + MaterialInstance->SetScalarParameterValue("VoxelSize", Settings.VoxelSize); // To have it on start +} + +void AVoxelMeshImporter::UpdateSizes() +{ + const FVector SizeFloat = CachedBox.GetSize() / Settings.VoxelSize; + SizeX = FMath::CeilToInt(SizeFloat.X); + SizeY = FMath::CeilToInt(SizeFloat.Y); + SizeZ = FMath::CeilToInt(SizeFloat.Z); + NumberOfVoxels = SizeX * SizeY * SizeZ; + const bool bHasMaterials = Settings.bImportColors || Settings.bImportUVs; + SizeInMB = double(NumberOfVoxels) * (sizeof(FVoxelValue) + (bHasMaterials ? sizeof(FVoxelMaterial) : 0)) / double(1 << 20); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialBuilder.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialBuilder.cpp new file mode 100644 index 00000000..e80e4583 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialBuilder.cpp @@ -0,0 +1,120 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMaterialBuilder.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +void FVoxelMaterialBuilder::Clear() +{ + MaterialConfig = EVoxelMaterialConfig(-1); + + MaterialOverride.Reset(); + + Color = FColor{ ForceInit }; + SingleIndex = 0; + Wetness = 0; + + Us.Memzero(); + Vs.Memzero(); + + IndicesStrengths.Reset(); +} + +FVoxelMaterial FVoxelMaterialBuilder::Build() const +{ + if (MaterialOverride.IsSet()) + { + return MaterialOverride.GetValue(); + } + + FVoxelMaterial Material{ ForceInit }; + + if (MaterialConfig == EVoxelMaterialConfig::RGB) + { + Material.SetColor(Color); + + Material.SetU0(Us[0]); + Material.SetU1(Us[1]); + Material.SetU2(Us[2]); + Material.SetU3(Us[3]); + + Material.SetV0(Vs[0]); + Material.SetV1(Vs[1]); + Material.SetV2(Vs[2]); + Material.SetV3(Vs[3]); + } + else if (MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + Material.SetColor(Color); + Material.SetSingleIndex(SingleIndex); + + Material.SetU0(Us[0]); + Material.SetU1(Us[1]); + Material.SetU2(Us[2]); + Material.SetU3(Us[3]); + + Material.SetV0(Vs[0]); + Material.SetV1(Vs[1]); + Material.SetV2(Vs[2]); + Material.SetV3(Vs[3]); + } + else + { + check(MaterialConfig == EVoxelMaterialConfig::MultiIndex); + + TVoxelStaticArray MaterialIndices{ ForceInit }; + TVoxelStaticArray Alphas{ ForceInit }; + + if (IndicesStrengths.Num() > 0) + { + TVoxelStaticArray MaterialStrengths{ ForceInit }; + uint32 LockedChannels = 0; + + const auto AddIndexStrength = [&](int32 Index, const FIndexStrength& IndexStrength) + { + MaterialIndices[Index] = IndexStrength.Index; + MaterialStrengths[Index] = IndexStrength.Strength; + if (IndexStrength.bLocked) + { + LockedChannels |= 1 << IndexStrength.Index; + } + }; + + if (IndicesStrengths.Num() > 4) + { + const TVoxelStaticArray, 4> Stack = FVoxelUtilities::FindTopXElements<4>(IndicesStrengths, [&](auto& A, auto& B) { return A.Strength < B.Strength; }); + + AddIndexStrength(0, Stack[0].Get<1>()); + AddIndexStrength(1, Stack[1].Get<1>()); + AddIndexStrength(2, Stack[2].Get<1>()); + AddIndexStrength(3, Stack[3].Get<1>()); + } + else + { + for (int32 Index = 0; Index < IndicesStrengths.Num(); Index++) + { + AddIndexStrength(Index, IndicesStrengths[Index]); + } + } + + Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(MaterialStrengths, LockedChannels); + } + + Material.SetMultiIndex_Wetness(Wetness); + + Material.SetMultiIndex_Blend0_AsFloat(Alphas[0]); + Material.SetMultiIndex_Blend1_AsFloat(Alphas[1]); + Material.SetMultiIndex_Blend2_AsFloat(Alphas[2]); + + Material.SetMultiIndex_Index0(MaterialIndices[0]); + Material.SetMultiIndex_Index1(MaterialIndices[1]); + Material.SetMultiIndex_Index2(MaterialIndices[2]); + Material.SetMultiIndex_Index3(MaterialIndices[3]); + + Material.SetU2(Us[2]); + Material.SetU3(Us[3]); + Material.SetV2(Vs[2]); + Material.SetV3(Vs[3]); + } + + return Material; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialInterface.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialInterface.cpp new file mode 100644 index 00000000..5547b031 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialInterface.cpp @@ -0,0 +1,290 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelMaterialInterface.h" +#include "Materials/Material.h" +#include "Materials/MaterialInterface.h" +#include "Materials/MaterialInstanceDynamic.h" + +FAutoConsoleCommandWithWorldAndArgs ClearInstancePoolCmd( + TEXT("voxel.renderer.ClearMaterialInstancePool"), + TEXT("Clear material instance pool"), + FConsoleCommandWithWorldAndArgsDelegate::CreateLambda([](const auto&, auto*) { FVoxelMaterialInterfaceManager::Get().ClearInstancePool(); })); + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Material Instances Pool"), STAT_VoxelMaterialInstancesPool, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Material Instances Used"), STAT_VoxelMaterialInstancesUsed, STATGROUP_VoxelCounters); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMaterialInterfaceManager* FVoxelMaterialInterfaceManager::Singleton = nullptr; + +FVoxelMaterialInterfaceManager::FVoxelMaterialInterfaceManager() +{ + UMaterial* Material = UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface); + check(Material); + DefaultMaterialPtr = CreateMaterial(Material); +} + +TVoxelSharedRef FVoxelMaterialInterfaceManager::DefaultMaterial() const +{ + check(DefaultMaterialPtr.IsValid()); + return DefaultMaterialPtr.ToSharedRef(); +} + +TVoxelSharedRef FVoxelMaterialInterfaceManager::CreateMaterial(UMaterialInterface* MaterialInterface) +{ + return CreateMaterialImpl(MaterialInterface, false); +} + +// To access SetParentInternal +class FMaterialInstanceResource +{ +public: + static void SetParent(UMaterialInstanceDynamic& Instance, UMaterialInterface* Parent) + { + VOXEL_FUNCTION_COUNTER(); + Instance.SetParentInternal(Parent, false); + } +}; + +TVoxelSharedRef FVoxelMaterialInterfaceManager::CreateMaterialInstance(UMaterialInterface* Parent) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!IsValid(Parent)) + { + return DefaultMaterial(); + } + + UMaterialInstanceDynamic* Instance = GetInstanceFromPool(); + check(Instance); + + FMaterialInstanceResource::SetParent(*Instance, Parent); + + return CreateMaterialImpl(Instance, true); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelMaterialInterfaceManager::AddReferencedObjects(FReferenceCollector& Collector) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + Collector.AddReferencedObjects(InstancePool); + + for (auto& Chunk : Chunks) + { + check(Chunk.IsValid()); + for (auto& MaterialInfo : Chunk->MaterialsInfos_AnyThread) + { + Collector.AddReferencedObject(MaterialInfo.Material); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMaterialInterfaceManager::FMaterialInfo* FVoxelMaterialInterfaceManager::GetMaterialInfo_AnyThread(FMaterialReference Reference) +{ + if (!Chunks.IsValidIndex(Reference.ChunkIndex)) + { + return nullptr; + } + + auto& Chunk = Chunks[Reference.ChunkIndex]; + if (!Chunk.IsValid()) + { + return nullptr; + } + + if (!Chunk->MaterialsInfos_AnyThread.IsValidIndex(Reference.Index)) + { + return nullptr; + } + + return &Chunk->MaterialsInfos_AnyThread[Reference.Index]; +} + +FVoxelMaterialInterfaceManager::FMaterialReference FVoxelMaterialInterfaceManager::AllocateMaterialInfo_GameThread() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + // Free pending references + ProcessReferencesToDecrement_GameThread(); + + for (int32 ChunkIndex = 0; ChunkIndex < Chunks.Num(); ChunkIndex++) + { + auto& Chunk = Chunks[ChunkIndex]; + check(Chunk.IsValid()); + if (Chunk->FreeIndices_GameThread.Num() > 0) + { + const int32 Index = Chunk->FreeIndices_GameThread.Pop(); + return FMaterialReference{ Index, ChunkIndex }; + } + if (Chunk->MaterialsInfos_AnyThread.Num() < ChunkSize) + { + const int32 Index = Chunk->MaterialsInfos_AnyThread.Emplace(); + return FMaterialReference{ Index, ChunkIndex }; + } + } + + if (Chunks.Num() == MaxNumChunks) + { + LOG_VOXEL(Fatal, TEXT("FVoxelMaterialInterfaceManager: ran out of material info slots. Total slots: %d"), ChunkSize * MaxNumChunks); + } + + const int32 ChunkIndex = Chunks.Add(MakeVoxelShared()); + const int32 Index = Chunks[ChunkIndex]->MaterialsInfos_AnyThread.Emplace(); + return FMaterialReference{ Index, ChunkIndex }; +} + +void FVoxelMaterialInterfaceManager::DestroyMaterialInfo_GameThread(FMaterialInfo& MaterialInfo) +{ + if (MaterialInfo.bIsInstance) + { + ensure(!MaterialInfo.Material || MaterialInfo.Material->IsA()); + ReturnInstanceToPool(Cast(MaterialInfo.Material)); + } +} + +void FVoxelMaterialInterfaceManager::ProcessReferencesToDecrement_GameThread() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + FMaterialReference Reference; + while (ReferencesToDecrement.Dequeue(Reference)) + { + FMaterialInfo* MaterialInfo = GetMaterialInfo_AnyThread(Reference); + if (ensure(MaterialInfo)) + { + MaterialInfo->ReferenceCount--; + ensure(MaterialInfo->ReferenceCount >= 0); + if (MaterialInfo->ReferenceCount == 0) + { + Chunks[Reference.ChunkIndex]->FreeIndices_GameThread.Add(Reference.Index); + DestroyMaterialInfo_GameThread(*MaterialInfo); + *MaterialInfo = {}; + } + } + } +} + +UMaterialInterface* FVoxelMaterialInterfaceManager::GetMaterial_AnyThread(FMaterialReference Reference) +{ + auto* MaterialInfo = GetMaterialInfo_AnyThread(Reference); + return ensure(MaterialInfo) ? MaterialInfo->Material : nullptr; +} + +void FVoxelMaterialInterfaceManager::RemoveReference_AnyThread(FMaterialReference Reference) +{ + ReferencesToDecrement.Enqueue(Reference); +} + +TVoxelSharedRef FVoxelMaterialInterfaceManager::CreateMaterialImpl(UMaterialInterface* MaterialInterface, bool bIsInstance) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!IsValid(MaterialInterface)) + { + return DefaultMaterial(); + } + + FMaterialReference& Reference = MaterialsMap.FindOrAdd(MaterialInterface); + + FMaterialInfo* MaterialInfo = GetMaterialInfo_AnyThread(Reference); + if (!MaterialInfo || MaterialInfo->Material != MaterialInterface) + { + Reference = AllocateMaterialInfo_GameThread(); + MaterialInfo = GetMaterialInfo_AnyThread(Reference); + check(MaterialInfo && !MaterialInfo->Material && MaterialInfo->ReferenceCount == 0); + MaterialInfo->Material = MaterialInterface; + MaterialInfo->bIsInstance = bIsInstance; + } + check(MaterialInfo && MaterialInfo->Material == MaterialInterface && MaterialInfo->bIsInstance == bIsInstance); + + MaterialInfo->ReferenceCount++; + + return TVoxelSharedRef(new FVoxelMaterialInterface(Reference)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UMaterialInstanceDynamic* FVoxelMaterialInterfaceManager::GetInstanceFromPool() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + INC_DWORD_STAT(STAT_VoxelMaterialInstancesUsed); + + UMaterialInstanceDynamic* Instance = nullptr; + + while (!Instance && InstancePool.Num() > 0) + { + Instance = InstancePool.Pop(false); + DEC_DWORD_STAT(STAT_VoxelMaterialInstancesPool); + } + + if (!Instance) + { + Instance = NewObject(); + } + + check(Instance); + return Instance; +} + +void FVoxelMaterialInterfaceManager::ReturnInstanceToPool(UMaterialInstanceDynamic* Instance) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + DEC_DWORD_STAT(STAT_VoxelMaterialInstancesUsed); + + if (Instance) + { + Instance->ClearParameterValues(); + + InstancePool.Add(Instance); + INC_DWORD_STAT(STAT_VoxelMaterialInstancesPool); + } +} + +void FVoxelMaterialInterfaceManager::ClearInstancePool() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + LOG_VOXEL(Log, TEXT("Clear Material Instance Pool: %d instances removed"), InstancePool.Num()); + DEC_DWORD_STAT_BY(STAT_VoxelMaterialInstancesPool, InstancePool.Num()); + InstancePool.Reset(); +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMaterialInterface::FVoxelMaterialInterface(FVoxelMaterialInterfaceManager::FMaterialReference Reference) + : Reference(Reference) +{ +} + +FVoxelMaterialInterface::~FVoxelMaterialInterface() +{ + FVoxelMaterialInterfaceManager::Get().RemoveReference_AnyThread(Reference); +} + +UMaterialInterface* FVoxelMaterialInterface::GetMaterial() const +{ + VOXEL_SLOW_FUNCTION_COUNTER(); + return FVoxelMaterialInterfaceManager::Get().GetMaterial_AnyThread(Reference); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialUtilities.cpp new file mode 100644 index 00000000..5688d3d6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMaterialUtilities.cpp @@ -0,0 +1,46 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +#include "Engine/EngineTypes.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceDynamic.h" + +bool FVoxelUtilities::IsMaterialTessellated(UMaterialInterface* Material) +{ + if (!ensure(Material)) + { + return false; + } + + UMaterial* BaseMaterial = Material->GetMaterial(); + if (!ensure(BaseMaterial)) + { + return false; + } + +PRAGMA_DISABLE_DEPRECATION_WARNINGS + return BaseMaterial->D3D11TessellationMode != EMaterialTessellationMode::MTM_NoTessellation; +PRAGMA_ENABLE_DEPRECATION_WARNINGS +} + +UMaterialInterface* FVoxelUtilities::GetDefaultMaterial(int32 NumIndices) +{ + static TWeakObjectPtr Materials[7]; + + check(0 <= NumIndices && NumIndices <= 6); + auto& Material = Materials[NumIndices]; + + if (Material.IsValid()) + { + return Material.Get(); + } + + UMaterial* Parent = UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface); + + Material = UMaterialInstanceDynamic::Create(Parent, nullptr); + check(Material.IsValid()); + + Material->AddToRoot(); + return Material.Get(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMessages.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMessages.cpp new file mode 100644 index 00000000..383b436c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMessages.cpp @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMessages.h" +#include "Logging/MessageLog.h" +#include "Misc/UObjectToken.h" +#include "Misc/MessageDialog.h" + +FVoxelMessages::FLogMessageDelegate FVoxelMessages::LogMessageDelegate; +FVoxelMessages::FShowNotificationDelegate FVoxelMessages::ShowNotificationDelegate; + +void FVoxelMessages::LogMessage(const TSharedRef& Message, EVoxelShowNotification ShouldShow) +{ + if (!ensure(IsInGameThread())) return; + + if (LogMessageDelegate.IsBound()) + { + LogMessageDelegate.Broadcast(Message, ShouldShow); + } + else + { + FMessageLog("PIE").AddMessage(Message); + } +} + +void FVoxelMessages::LogMessage(const FText& Message, EMessageSeverity::Type Severity, EVoxelShowNotification ShouldShow, const UObject* Object) +{ + if (!ensure(IsInGameThread())) return; + + TSharedRef NewMessage = FTokenizedMessage::Create(Severity); + if (Object) + { + NewMessage->AddToken(FUObjectToken::Create(Object)); + NewMessage->AddToken(FTextToken::Create(FText::FromString(": "))); + } + NewMessage->AddToken(FTextToken::Create(Message)); + LogMessage(NewMessage, ShouldShow); +} + +void FVoxelMessages::ShowNotification(const FNotification& Notification) +{ + ensure(Notification.UniqueId != 0); + if (!ensure(IsInGameThread())) return; + + if (ShowNotificationDelegate.IsBound()) + { + ShowNotificationDelegate.Broadcast(Notification); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelModule.cpp new file mode 100644 index 00000000..59a2d8d4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelModule.cpp @@ -0,0 +1,187 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelModule.h" + +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelMessages.h" +#include "IVoxelPool.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" + +#include "Containers/Ticker.h" +#include "Interfaces/IPluginManager.h" +#include "ShaderCore.h" +#include "Misc/Paths.h" +#include "Misc/PackageName.h" +#include "Misc/MessageDialog.h" +#include "Misc/ConfigCacheIni.h" +#include "Modules/ModuleManager.h" + +void FVoxelModule::StartupModule() +{ + LOG_VOXEL(Log, TEXT("VOXEL_DEBUG=%d"), VOXEL_DEBUG); + + { + check((32 << (FVoxelUtilities::GetDepthFromSize<32>(1023) - 1)) < 1023); + check((32 << FVoxelUtilities::GetDepthFromSize<32>(1023)) >= 1023); + check((32 << (FVoxelUtilities::GetDepthFromSize<32>(1025) - 1)) < 1025); + check((32 << FVoxelUtilities::GetDepthFromSize<32>(1025)) >= 1025); + + FVoxelMaterial Material(ForceInit); + const auto CheckUV = [&](int32 Tex) + { + for (int32 Value = 0; Value < 256; Value++) + { +#define checkBytesEqual(A, B) checkf(A == B, TEXT("%d == %d"), A, B); + + Material.SetU(Tex, Value); + checkBytesEqual(Material.GetU(Tex), Value); + + Material.SetV(Tex, 255 - Value); + checkBytesEqual(Material.GetV(Tex), 255 - Value); + + Material.SetV(Tex, Value); + checkBytesEqual(Material.GetV(Tex), Value); + + Material.SetU(Tex, 255 - Value); + checkBytesEqual(Material.GetU(Tex), 255 - Value); + + Material.SetU_AsFloat(Tex, 0.5f); + check(Material.GetU_AsFloat(Tex) == FVoxelUtilities::UINT8ToFloat(FVoxelUtilities::FloatToUINT8(0.5f))); + + Material.SetV_AsFloat(Tex, 0.5f); + check(Material.GetV_AsFloat(Tex) == FVoxelUtilities::UINT8ToFloat(FVoxelUtilities::FloatToUINT8(0.5f))); + +#undef checkBytesEqual + + Material.SetUV_AsFloat(Tex, FVector2D(1.f, 1.f)); + checkf(Material.GetUV_AsFloat(Tex) == FVector2D(1.f, 1.f), TEXT("%s"), *Material.GetUV_AsFloat(Tex).ToString()); + } + }; +#if VOXEL_MATERIAL_ENABLE_UV0 + CheckUV(0); +#endif +#if VOXEL_MATERIAL_ENABLE_UV1 + CheckUV(1); +#endif +#if VOXEL_MATERIAL_ENABLE_UV2 + CheckUV(2); +#endif +#if VOXEL_MATERIAL_ENABLE_UV3 + CheckUV(3); +#endif + } + + FVoxelSerializationUtilities::TestCompression(128, EVoxelCompressionLevel::BestSpeed); + FVoxelSerializationUtilities::TestCompression(128, EVoxelCompressionLevel::BestCompression); + + //FVoxelSerializationUtilities::TestCompression(1llu << 32, EVoxelCompressionLevel::BestSpeed); + + { + const auto Plugin = IPluginManager::Get().FindPlugin(VOXEL_PLUGIN_NAME); + + // This is needed to correctly share content across Pro and Free + FPackageName::UnRegisterMountPoint(TEXT("/") VOXEL_PLUGIN_NAME TEXT("/"), Plugin->GetContentDir()); + FPackageName::RegisterMountPoint("/Voxel/", Plugin->GetContentDir()); + + const FString PluginBaseDir = Plugin.IsValid() ? FPaths::ConvertRelativePathToFull(Plugin->GetBaseDir()) : ""; + + const FString PluginShaderDir = FPaths::Combine(PluginBaseDir, TEXT("Shaders")); + AddShaderSourceDirectoryMapping(TEXT("/Plugin/Voxel"), PluginShaderDir); + +#if USE_EMBREE_VOXEL + const FString EmbreeBase = PluginBaseDir / "Source" / "ThirdParty" / "Embree3/"; + + bool bSuccess = true; + +#if PLATFORM_WINDOWS + { + const FString Folder = EmbreeBase / "Win64" / "lib"; + LOG_VOXEL(Log, TEXT("Loading embree3.dll from %s"), *Folder); + + FPlatformProcess::PushDllDirectory(*Folder); + auto* Handle = FPlatformProcess::GetDllHandle(TEXT("embree3.dll")); + FPlatformProcess::PopDllDirectory(*Folder); + + bSuccess = Handle != nullptr; + } +#elif PLATFORM_MAC + { + const FString Folder = EmbreeBase / "MacOSX" / "lib"; + + const TArray Libs = + { + "libtbb.dylib", + "libtbbmalloc.dylib", + "libembree3.3.dylib" + }; + + for (auto& Lib : Libs) + { + const FString Path = Folder / Lib; + LOG_VOXEL(Log, TEXT("Loading %s"), *Path); + auto* Handle = FPlatformProcess::GetDllHandle(*Path); + bSuccess &= Handle != nullptr; + } + } +#endif + + if (!bSuccess) + { + FMessageDialog::Open(EAppMsgType::Ok, EAppReturnType::Ok, VOXEL_LOCTEXT("Voxel Plugin: embree not found, voxel spawners won't work.")); + } +#endif + } + + FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([=](float) + { + int32 VoxelPluginVersion = 0; + GConfig->GetInt(TEXT("VoxelPlugin"), TEXT("VoxelPluginVersion"), VoxelPluginVersion, GEditorPerProjectIni); + + const auto OpenLink = [=](const FString& Url) + { + FString Error; + FPlatformProcess::LaunchURL(*Url, nullptr, &Error); + if (!Error.IsEmpty()) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to open " + Url + "\n:" + Error)); + } + }; + + constexpr int32 LatestVoxelPluginVersion = 2; + if (VoxelPluginVersion < LatestVoxelPluginVersion) + { + const auto Close = [=]() + { + GConfig->SetInt(TEXT("VoxelPlugin"), TEXT("VoxelPluginVersion"), LatestVoxelPluginVersion, GEditorPerProjectIni); + }; + + FVoxelMessages::FNotification Notification; + Notification.UniqueId = OBJECT_LINE_ID(); + Notification.Message = "Voxel Plugin has been updated to 1.2!"; + Notification.Duration = 1e6f; + Notification.OnClose = FSimpleDelegate::CreateLambda(Close); + + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Show Release Notes"; + Button.Tooltip = "See the latest plugin release notes"; + Button.OnClick = FSimpleDelegate::CreateLambda([=]() + { + OpenLink("https://releases.voxelplugin.com"); + Close(); + }); + + FVoxelMessages::ShowNotification(Notification); + } + + + return false; + })); +} + +void FVoxelModule::ShutdownModule() +{ + IVoxelPool::Shutdown(); +} + +IMPLEMENT_MODULE(FVoxelModule, Voxel) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.cpp new file mode 100644 index 00000000..3611a4a7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.cpp @@ -0,0 +1,134 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h" +#include "VoxelMultiplayer/VoxelMultiplayerUtilities.h" +#include "VoxelMinimal.h" + +#include "Misc/MessageDialog.h" + +bool FVoxelMultiplayerClientWithSocket::ReceiveDiffs(TArray>& OutValueDiffs, TArray>& OutMaterialDiffs) +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsValid()); + check(NextLoadType == EVoxelMultiplayerNextLoadType::Diffs); + + TArray ReceivedData; + if (TryToReceiveData(ExpectedSize, ReceivedData)) + { + FVoxelMultiplayerUtilities::ReadDiffs(ReceivedData, OutValueDiffs, OutMaterialDiffs); + ResetHeaders(); + return true; + } + else + { + return false; + } +} + +bool FVoxelMultiplayerClientWithSocket::ReceiveSave(FVoxelCompressedWorldSaveImpl& OutSave) +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsValid()); + check(NextLoadType == EVoxelMultiplayerNextLoadType::Save); + + TArray ReceivedData; + if (TryToReceiveData(ExpectedSize, ReceivedData)) + { + FVoxelMultiplayerUtilities::ReadSave(ReceivedData, OutSave); + ResetHeaders(); + return true; + } + else + { + return false; + } +} + +EVoxelMultiplayerNextLoadType FVoxelMultiplayerClientWithSocket::GetNextLoadType() +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsValid()); + if (NextLoadType == EVoxelMultiplayerNextLoadType::Unknown) + { + TArray ReceivedData; + if (TryToReceiveData(FVoxelMultiplayerUtilities::HeaderBytes, ReceivedData)) + { + if (!FVoxelMultiplayerUtilities::LoadHeader(ReceivedData, ExpectedSize, NextLoadType)) + { + FMessageDialog::Open(EAppMsgType::Ok, EAppReturnType::Ok, FText::FromString("Voxel Multiplayer MAGIC error! The socket is sending corrupted data")); + Destroy(); + } + } + } + return NextLoadType; +} + +bool FVoxelMultiplayerClientWithSocket::TryToReceiveData(uint32 Size, TArray& OutData) +{ + VOXEL_FUNCTION_COUNTER(); + + FetchPendingData(); + + if (uint32(PendingData.Num()) >= Size) + { + OutData = TArray(PendingData.GetData(), Size); + PendingData.RemoveAt(0, Size, false); + return true; + } + else + { + return false; + } +} + +void FVoxelMultiplayerClientWithSocket::ResetHeaders() +{ + ExpectedSize = 0; + NextLoadType = EVoxelMultiplayerNextLoadType::Unknown; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelMultiplayerServerWithSocket::SendDiffs(const TArray>& ValueDiffs, const TArray>& MaterialDiffs) +{ + VOXEL_FUNCTION_COUNTER(); + + check(ValueDiffs.Num() > 0 || MaterialDiffs.Num() > 0); + + TArray Data; + FVoxelMultiplayerUtilities::WriteDiffs(Data, ValueDiffs, MaterialDiffs); + + TArray Header; + FVoxelMultiplayerUtilities::CreateHeader(Header, Data.Num(), EVoxelMultiplayerNextLoadType::Diffs); + + SendData(Header, ETarget::ExistingSockets); + SendData(Data, ETarget::ExistingSockets); +} + +void FVoxelMultiplayerServerWithSocket::SendSave(FVoxelCompressedWorldSaveImpl& Save, bool bForceLoad) +{ + VOXEL_FUNCTION_COUNTER(); + + TArray Data; + FVoxelMultiplayerUtilities::WriteSave(Data, Save); + + TArray Header; + FVoxelMultiplayerUtilities::CreateHeader(Header, Data.Num(), EVoxelMultiplayerNextLoadType::Save); + + if (bForceLoad) + { + SendData(Header, ETarget::ExistingSockets); + SendData(Data, ETarget::ExistingSockets); + } + + SendData(Header, ETarget::NewSockets); + SendData(Data, ETarget::NewSockets); + + ClearNewSockets(); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerManager.cpp new file mode 100644 index 00000000..d1f0250a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerManager.cpp @@ -0,0 +1,185 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMultiplayer/VoxelMultiplayerManager.h" +#include "VoxelMultiplayer/VoxelMultiplayerInterface.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelData/VoxelSaveUtilities.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" + +// TODO https://github.com/Phyronnaz/VoxelPrivate/blob/82df5f5c96f124139a13cbbf88453841e2bee0fe/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerManager.h#L1 +// TODO https://github.com/Phyronnaz/VoxelPrivate/blob/82df5f5c96f124139a13cbbf88453841e2bee0fe/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerManager.cpp#L1 + +FVoxelMultiplayerSettings::FVoxelMultiplayerSettings( + const AVoxelWorld* InWorld, + const TVoxelSharedRef& InData, + const TVoxelSharedRef& InDebugManager, + const TVoxelSharedRef& InLODManager) + : Data(InData) + , DebugManager(InDebugManager) + , LODManager(InLODManager) + , VoxelWorld(InWorld) + , MultiplayerSyncRate(FMath::Max(SMALL_NUMBER, InWorld->MultiplayerSyncRate)) +{ +} + +TVoxelSharedRef FVoxelMultiplayerManager::Create(const FVoxelMultiplayerSettings& Settings) +{ + TVoxelSharedPtr Server; + TVoxelSharedPtr Client; + + auto* MultiplayerInterfaceInstance = Settings.VoxelWorld->GetMultiplayerInterfaceInstance(); + if (MultiplayerInterfaceInstance) + { + if (MultiplayerInterfaceInstance->IsServer()) + { + Server = MultiplayerInterfaceInstance->CreateServer(); + } + else + { + Client = MultiplayerInterfaceInstance->CreateClient(); + } + } + else + { + FVoxelMessages::Error( + "bEnableMultiplayer = true, but the multiplayer instance is not created! " + "You need to call CreateMultiplayerInterfaceInstance before creating the voxel world.", + Settings.VoxelWorld.Get()); + } + + TVoxelSharedRef MultiplayerManager = MakeShareable(new FVoxelMultiplayerManager(Settings, Server, Client)); + if (Server.IsValid()) + { + Server->OnConnection.BindThreadSafeSP(MultiplayerManager, &FVoxelMultiplayerManager::OnConnection); + } + return MultiplayerManager; +} + +void FVoxelMultiplayerManager::Destroy() +{ + StopTicking(); +} + +FVoxelMultiplayerManager::FVoxelMultiplayerManager( + const FVoxelMultiplayerSettings& Settings, + TVoxelSharedPtr Server, + TVoxelSharedPtr Client) + : Settings(Settings) + , Server(Server) + , Client(Client) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelMultiplayerManager::Tick(float DeltaTime) +{ + const double Time = FPlatformTime::Seconds(); + if (Server.IsValid() && Time - LastSyncTime > 1. / Settings.MultiplayerSyncRate) + { + LastSyncTime = Time; + SendData(); + } + if (Client.IsValid()) + { + ReceiveData(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelMultiplayerManager::ReceiveData() const +{ + VOXEL_FUNCTION_COUNTER(); + + check(Client.IsValid()); + if (!Client->IsValid()) return; + + if (!ensure(Settings.VoxelWorld.IsValid())) return; + + const EVoxelMultiplayerNextLoadType NextLoadType = Client->GetNextLoadType(); + if (!Client->IsValid()) return; + + switch (NextLoadType) + { + case EVoxelMultiplayerNextLoadType::Save: + { + FVoxelCompressedWorldSaveImpl Save; + if (Client->ReceiveSave(Save)) + { + FVoxelUncompressedWorldSaveImpl DecompressedSave; + UVoxelSaveUtilities::DecompressVoxelSave(Save, DecompressedSave); + + UVoxelDataTools::LoadFromSave(Settings.VoxelWorld.Get(), DecompressedSave, {}); + } + break; + } + case EVoxelMultiplayerNextLoadType::Diffs: + { + TArray> ValueDiffs; + TArray> MaterialDiffs; + if (Client->ReceiveDiffs(ValueDiffs, MaterialDiffs)) + { + TArray ModifiedBounds; + // TODO Async? + Settings.Data->LoadFromDiffs(ValueDiffs, MaterialDiffs, ModifiedBounds); + + Settings.LODManager->UpdateBounds(ModifiedBounds); + Settings.DebugManager->ReportMultiplayerSyncedChunks([&]() { return ModifiedBounds; }); + } + break; + } + case EVoxelMultiplayerNextLoadType::Unknown: + break; + default: + check(false); + break; + } +} + +void FVoxelMultiplayerManager::SendData() const +{ + VOXEL_FUNCTION_COUNTER(); + + check(Server.IsValid()); + if (!Server->IsValid()) return; + + TArray> ValueDiffs; + TArray> MaterialDiffs; + Settings.Data->GetDiffs(ValueDiffs, MaterialDiffs); + + if (ValueDiffs.Num() > 0 || MaterialDiffs.Num() > 0) + { + Server->SendDiffs(ValueDiffs, MaterialDiffs); + } +} + +void FVoxelMultiplayerManager::OnConnection() +{ + VOXEL_FUNCTION_COUNTER(); + + check(Server.IsValid()); + if (!Server->IsValid()) return; + + LOG_VOXEL(Log, TEXT("Sending world to clients")); + + FVoxelUncompressedWorldSaveImpl Save; + TArray Objects; + Settings.Data->GetSave(Save, Objects); + ensureMsgf(Objects.Num() == 0, TEXT("Placeable items are not supported in multiplayer")); + + FVoxelCompressedWorldSaveImpl CompressedSave; + UVoxelSaveUtilities::CompressVoxelSave(Save, CompressedSave); + + Server->SendSave(CompressedSave, false); + + OnClientConnection.Broadcast(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp new file mode 100644 index 00000000..586c9844 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp @@ -0,0 +1,262 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMultiplayer/VoxelMultiplayerTcp.h" +#include "VoxelMessages.h" + +#include "Sockets.h" +#include "SocketSubsystem.h" +#include "IPAddress.h" +#include "Common/TcpSocketBuilder.h" +#include "Common/TcpListener.h" +#include "Async/Async.h" + +#define TCP_MAX_PACKET_SIZE 128000 + +bool UVoxelMultiplayerTcpInterface::ConnectToServer(FString& OutError, const FString& Ip, int32 Port) +{ + if (Client.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Client already created!"), this); + OutError = "Client already created"; + return false; + } + if (Server.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Server already created!"), this); + OutError = "Server already created"; + return false; + } + + Client = MakeVoxelShared(); + if (!Client->Connect(Ip, Port, OutError)) + { + Client.Reset(); + FVoxelMessages::Error(FUNCTION_ERROR("Connect Error: " + OutError), this); + return false; + } + return true; +} + +bool UVoxelMultiplayerTcpInterface::StartServer(FString& OutError, const FString& Ip, int32 Port) +{ + if (Client.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Client already created!"), this); + OutError = "Client already created"; + return false; + } + if (Server.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Server already created!"), this); + OutError = "Server already created"; + return false; + } + + Server = MakeVoxelShared(); + if (!Server->Start(Ip, Port, OutError)) + { + Server.Reset(); + FVoxelMessages::Error(FUNCTION_ERROR("Start Error: " + OutError), this); + return false; + } + return true; +} + +bool UVoxelMultiplayerTcpInterface::IsServer() const +{ + if (!Client.IsValid() && !Server.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Neither Client nor Server is created! You need to call ConnectToServer or StartServer before creating the voxel world"), this); + return false; + } + ensure(!Client.IsValid() || !Server.IsValid()); + return Server.IsValid(); +} + +TVoxelSharedPtr UVoxelMultiplayerTcpInterface::CreateClient() const +{ + return Client; +} + +TVoxelSharedPtr UVoxelMultiplayerTcpInterface::CreateServer() const +{ + return Server; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_ERROR(Test, Error) if (!(Test)) { OutError = Error; return false; } + +bool FVoxelMultiplayerTcpClient::Connect(const FString& Ip, int32 Port, FString& OutError) +{ + VOXEL_FUNCTION_COUNTER(); + + check(!IsValid()); + + FIPv4Address Address; + if (!FIPv4Address::Parse(Ip, Address)) + { + OutError = "Invalid IP address"; + return false; + } + const FIPv4Endpoint Endpoint(Address, Port); + + Socket = FTcpSocketBuilder(TEXT("RemoteConnection")).AsBlocking(); + + int32 BufferSize = TCP_MAX_PACKET_SIZE; + int32 NewSize; + Socket->SetReceiveBufferSize(BufferSize, NewSize); + if (!ensureAlwaysMsgf(BufferSize == NewSize, TEXT("Invalid buffer size: %d"), BufferSize)) + { + OutError = "Invalid buffer size"; + Destroy(); + return false; + } + + if (!Socket->Connect(*Endpoint.ToInternetAddr())) + { + OutError = "Couldn't connect"; + Destroy(); + return false; + } + + LOG_VOXEL(Log, TEXT("TCP Client: Connected! %s:%d"), *Ip, Port); + + return true; +} + +bool FVoxelMultiplayerTcpClient::IsValid() const +{ + return Socket != nullptr; +} + +void FVoxelMultiplayerTcpClient::Destroy() +{ + VOXEL_FUNCTION_COUNTER(); + + if (Socket) + { + ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket); + Socket = nullptr; + } +} + +void FVoxelMultiplayerTcpClient::FetchPendingData() +{ + VOXEL_FUNCTION_COUNTER(); + + uint32 PendingDataSize; + if (Socket->HasPendingData(PendingDataSize)) + { + const int32 CurrentPos = PendingData.AddUninitialized(PendingDataSize); + + int32 BytesRead = 0; + ensureAlwaysMsgf(Socket->Recv(PendingData.GetData() + CurrentPos, PendingDataSize, BytesRead), TEXT("Receive data: invalid socket!")); + if (!ensureAlwaysMsgf(BytesRead == PendingDataSize, TEXT("Received %d instead of %d"), BytesRead, PendingDataSize)) + { + PendingData.Reset(); + Destroy(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMultiplayerTcpServer::FVoxelMultiplayerTcpServer() +{ + // For unique ptr forward decl +} + +FVoxelMultiplayerTcpServer::~FVoxelMultiplayerTcpServer() +{ + // For unique ptr forward decl +} + +bool FVoxelMultiplayerTcpServer::Start(const FString& Ip, int32 Port, FString& OutError) +{ + VOXEL_FUNCTION_COUNTER(); + + if (IsValid()) + { + OutError = "Already started"; + return false; + } + + FIPv4Address Address; + if (!FIPv4Address::Parse(Ip, Address)) + { + OutError = "Invalid IP address"; + return false; + } + FIPv4Endpoint Endpoint(Address, Port); + + TcpListener = MakeUnique(Endpoint); + TcpListener->OnConnectionAccepted().BindRaw(this, &FVoxelMultiplayerTcpServer::Accept); + + LOG_VOXEL(Log, TEXT("TCP Server: Started! %s:%d"), *Ip, Port); + + return true; +} + +bool FVoxelMultiplayerTcpServer::IsValid() const +{ + return TcpListener.IsValid(); +} + +void FVoxelMultiplayerTcpServer::Destroy() +{ + VOXEL_FUNCTION_COUNTER(); + + TcpListener.Reset(); +} + +void FVoxelMultiplayerTcpServer::SendData(const TArray& Data, ETarget Target) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Target == ETarget::NewSockets) + { + NewSocketsSection.Lock(); + } + for (FSocket* Socket : (Target == ETarget::NewSockets ? NewSockets : Sockets)) + { + for (int32 Offset = 0; Offset < Data.Num(); Offset += TCP_MAX_PACKET_SIZE) + { + int32 BytesToSend = FMath::Min(TCP_MAX_PACKET_SIZE, Data.Num() - Offset); + int32 BytesSent = 0; + ensureAlwaysMsgf(Socket->Send(Data.GetData() + Offset, BytesToSend, BytesSent), TEXT("SendData: invalid socket!")); + ensureAlwaysMsgf(BytesSent == BytesToSend, TEXT("%d bytes sent instead of %d!"), BytesSent, BytesToSend); + } + } + if (Target == ETarget::NewSockets) + { + NewSocketsSection.Unlock(); + } +} + +void FVoxelMultiplayerTcpServer::ClearNewSockets() +{ + VOXEL_FUNCTION_COUNTER(); + + FScopeLock Lock(&NewSocketsSection); + Sockets.Append(NewSockets); + NewSockets.Reset(); +} + +bool FVoxelMultiplayerTcpServer::Accept(FSocket* NewSocket, const FIPv4Endpoint& Endpoint) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + LOG_VOXEL(Log, TEXT("TCP Server: Client connected! %s"), *Endpoint.ToString()); + + AsyncTask(ENamedThreads::GameThread, MakeVoxelWeakPtrLambda(this, [=]() { OnConnection.ExecuteIfBound(); })); + + FScopeLock Lock(&NewSocketsSection); + NewSockets.Add(NewSocket); + + return true; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerUtilities.cpp new file mode 100644 index 00000000..cf662505 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerUtilities.cpp @@ -0,0 +1,115 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMultiplayer/VoxelMultiplayerUtilities.h" +#include "VoxelData/VoxelSave.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" + +#include "Serialization/MemoryReader.h" +#include "Serialization/MemoryWriter.h" +#include "Serialization/LargeMemoryReader.h" +#include "Serialization/LargeMemoryWriter.h" + +inline void SerializeDataHeader(FArchive& Archive, bool& bValues, uint32& ItemCount) +{ + Archive << bValues << ItemCount; +} + +void FVoxelMultiplayerUtilities::ReadDiffs(const TArray& Data, TArray>& OutValueDiffs, TArray>& OutMaterialDiffs) +{ + VOXEL_FUNCTION_COUNTER(); + + check(Data.Num() > 0); + + TArray64 UncompressedData; + if (!ensure(FVoxelSerializationUtilities::DecompressData(Data, UncompressedData))) + { + return; + } + check(UncompressedData.Num() >= sizeof(bool) + sizeof(uint32)); + + FLargeMemoryReader Reader(UncompressedData.GetData(), UncompressedData.Num()); + + bool bValues; + uint32 ItemCount; + SerializeDataHeader(Reader, bValues, ItemCount); + + if (bValues) + { + for (uint32 Index = 0; Index < ItemCount; Index++) + { + TVoxelChunkDiff Diff; + Reader << Diff; + OutValueDiffs.Add(Diff); + } + } + else + { + for (uint32 Index = 0; Index < ItemCount; Index++) + { + TVoxelChunkDiff Diff; + Reader << Diff; + OutMaterialDiffs.Add(Diff); + } + } + + ensure(Reader.AtEnd() && !Reader.IsError()); +} + +template +void WriteDiffsImpl(TArray& Data, const TArray>& Diffs) +{ + VOXEL_FUNCTION_COUNTER(); + + FLargeMemoryWriter Writer; + + bool bValue = TIsSame::Value; + uint32 SizeToSend = Diffs.Num(); + + check((bValue || TIsSame::Value)); + + SerializeDataHeader(Writer, bValue, SizeToSend); + + for (uint32 Index = 0; Index < SizeToSend; Index++) + { + Writer << const_cast&>(Diffs[Index]); + } + + TArray CompressedData; + // NOTE: Change the compression level here + FVoxelSerializationUtilities::CompressData(Writer, CompressedData, EVoxelCompressionLevel::BestSpeed); + Data.Append(CompressedData); +} + +void FVoxelMultiplayerUtilities::WriteDiffs(TArray& Data, const TArray>& ValueDiffs, const TArray>& MaterialDiffs) +{ + VOXEL_FUNCTION_COUNTER(); + + if (ValueDiffs.Num() > 0) + { + WriteDiffsImpl(Data, ValueDiffs); + } + if (MaterialDiffs.Num() > 0) + { + WriteDiffsImpl(Data, MaterialDiffs); + } +} + +void FVoxelMultiplayerUtilities::ReadSave(const TArray& Data, FVoxelCompressedWorldSaveImpl& OutSave) +{ + VOXEL_FUNCTION_COUNTER(); + + FMemoryReader Reader(Data); + OutSave.Serialize(Reader); +} + +void FVoxelMultiplayerUtilities::WriteSave(TArray& Data, const FVoxelCompressedWorldSaveImpl& Save) +{ + VOXEL_FUNCTION_COUNTER(); + + FMemoryWriter Writer(Data); + const_cast(Save).Serialize(Writer); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelObjectArchive.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelObjectArchive.cpp new file mode 100644 index 00000000..5228d629 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelObjectArchive.cpp @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelObjectArchive.h" +#include "VoxelMinimal.h" + +FVoxelObjectArchive FVoxelObjectArchive::MakeReader(FArchive& Ar, const TArray& Objects) +{ + check(IsInGameThread()); + + FVoxelObjectArchive Result(Ar, false); + Result.ReaderObjects.Reserve(Objects.Num()); + for (auto& Object : Objects) + { + if (Object.Index == 0) + { + ensure(!Object.Object.IsValid()); + continue; + } + + UObject* ObjectPtr = Object.Object.Get(); + if (!ensure(ObjectPtr)) + { + LOG_VOXEL(Error, TEXT("Failed to load %s"), *Object.Object.ToString()); + } + Result.ReaderObjects.Add(Object.Index, ObjectPtr); + } + return Result; +} + +FVoxelObjectArchive FVoxelObjectArchive::MakeWriter(FArchive& Ar) +{ + return FVoxelObjectArchive(Ar, true); +} + +TArray FVoxelObjectArchive::GetWriterObjects() const +{ + check(IsSaving()); + + TArray Objects; + Objects.Reserve(WriterObjects.Num()); + for (auto& It : WriterObjects) + { + const TSoftObjectPtr& Object = It.Key; + const int32 Index = It.Value; + + if (Index == 0) + { + ensure(Object.IsNull()); + Objects.Add(FVoxelObjectArchiveEntry{ {}, 0 }); + } + else + { + ensure(!Object.IsNull()); + Objects.Add(FVoxelObjectArchiveEntry{ Object, Index }); + } + } + + return Objects; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelAssetActor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelAssetActor.cpp new file mode 100644 index 00000000..9664c211 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelAssetActor.cpp @@ -0,0 +1,447 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/Actors/VoxelAssetActor.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelRender/LODManager/VoxelFixedResolutionLODManager.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/Renderers/VoxelDefaultRenderer.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelWorld.h" +#include "VoxelDefaultPool.h" +#include "VoxelMessages.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" + +#include "Components/BoxComponent.h" + +AVoxelAssetActor::AVoxelAssetActor() +{ + Root = CreateDefaultSubobject(TEXT("Root")); + SetRootComponent(Root); + +#if WITH_EDITOR + PrimitiveComponent = CreateDefaultSubobject(TEXT("PrimitiveComponent")); + PrimitiveComponent->SetupAttachment(Root); + + SetActorEnableCollision(true); // To place items on top of it + + Box = CreateDefaultSubobject("Box"); + Box->SetCollisionEnabled(ECollisionEnabled::NoCollision); + Box->SetupAttachment(Root); + + PrimaryActorTick.bCanEverTick = true; +#endif +} + +void AVoxelAssetActor::AddItemToWorld(AVoxelWorld* World) +{ + check(World); + + if (World->GetPlayType() != EVoxelPlayType::Game) + { + return; + } + if (!Generator.IsValid()) + { + FVoxelMessages::Error("Invalid generator", this); + return; + } + + AddItemToData(World, &World->GetData()); +} + +int32 AVoxelAssetActor::GetPriority() const +{ + return Priority; +} + +FVoxelIntBox AVoxelAssetActor::AddItemToData( + AVoxelWorld* VoxelWorld, + FVoxelData* VoxelWorldData) const +{ + VOXEL_FUNCTION_COUNTER(); + + auto Transform = GetTransform() * VoxelWorld->GetTransform().Inverse(); + Transform.ScaleTranslation(1.f / VoxelWorld->VoxelSize); + + FVoxelIntBox WorldBounds; + if (bOverrideAssetBounds) + { + // Might be one-off error there + WorldBounds = AssetBounds.Translate(FVoxelUtilities::FloorToInt(Transform.GetTranslation())); + } + else + { + if (auto* GeneratorWithBounds = Cast(Generator.GetObject())) + { + WorldBounds = GeneratorWithBounds->GetBounds().ApplyTransform(Transform); + } + else + { + FVoxelMessages::Error( + "Voxel Asset Actor: AssetBounds are not overriden, and cannot deduce them from the generator\n" + "You need to tick the checkbox next to Asset Bounds on the asset actor", + this); + WorldBounds = FVoxelIntBox(-25, 25).Translate(FVoxelUtilities::FloorToInt(Transform.GetTranslation())); + } + + // Small hack to update the asset bounds from the world bounds when bOverrideAssetBounds = false + const_cast(this)->AssetBounds = WorldBounds.Translate(-FVoxelUtilities::FloorToInt(Transform.GetTranslation())); + } + + if (!VoxelWorldData || !ensure(WorldBounds.IsValid())) + { + return WorldBounds; + } + + auto AssetInstance = Generator.GetInstance(false); + AssetInstance->Init(VoxelWorld->GetGeneratorInit()); + + if (bImportAsReference) + { + FVoxelWriteScopeLock Lock(*VoxelWorldData, WorldBounds, FUNCTION_FNAME); + VoxelWorldData->AddItem( + AssetInstance, + WorldBounds, + Transform, + Priority); + } + else + { + if (WorldBounds.Count() > 1e8) + { + FVoxelMessages::Error( + "Voxel Asset Actor: importing would affect more than 100 000 000 voxels. Please tick ImportAsReference instead.", + this); + } + else + { + FVoxelWriteScopeLock Lock(*VoxelWorldData, WorldBounds, FUNCTION_FNAME); + UVoxelAssetTools::ImportAssetImpl( + *VoxelWorldData, + WorldBounds, + Transform, + *AssetInstance, + bSubtractiveAsset, + MergeMode, + EVoxelMaterialMask::All); // TODO: expose material mask? + } + } + + return WorldBounds; +} + +#if WITH_EDITOR +void AVoxelAssetActor::UpdatePreview() +{ + if (!PreviewWorld) return; + if (!Generator.IsValid()) return; + + if (IsPreviewCreated()) + { + DestroyPreview(); + CreatePreview(); + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void AVoxelAssetActor::BeginPlay() +{ + Super::BeginPlay(); + + SetActorHiddenInGame(true); + SetActorEnableCollision(false); + PrimaryActorTick.SetTickFunctionEnable(false); +} + +void AVoxelAssetActor::BeginDestroy() +{ + Super::BeginDestroy(); + + if (IsPreviewCreated()) + { + DestroyPreview(); + } +} + +void AVoxelAssetActor::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + if (GetWorld()->WorldType != EWorldType::Editor) + { + // Editor preview still ticks + return; + } + if (PreviewWorld) + { + if (Generator.IsValid() && !IsPreviewCreated()) + { + CreatePreview(); + } + if (!PreviewWorld->OnPropertyChanged.IsBoundToObject(this)) + { + PreviewWorld->OnPropertyChanged.AddUObject(this, &AVoxelAssetActor::UpdatePreview); + } + + UpdateBox(); + } +} + +void AVoxelAssetActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + if (PreviewWorld && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + ClampTransform(); + } + if (PropertyChangedEvent.MemberProperty && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + const auto Name = PropertyChangedEvent.MemberProperty->GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, Generator) || + Name == GET_MEMBER_NAME_STATIC(FVoxelTransformableGeneratorPicker, Type) || + Name == GET_MEMBER_NAME_STATIC(FVoxelTransformableGeneratorPicker, Class) || + Name == GET_MEMBER_NAME_STATIC(FVoxelTransformableGeneratorPicker, Object) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, bOverrideAssetBounds) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, AssetBounds) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, PreviewLOD) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, bSubtractiveAsset) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, bImportAsReference) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, PreviewWorld) || + Name == STATIC_FNAME("RelativeScale3D") || + Name == STATIC_FNAME("RelativeRotation")) + { + UpdatePreview(); + } + } +} + +void AVoxelAssetActor::PostEditMove(const bool bFinished) +{ + Super::PostEditMove(bFinished); + + if (PreviewWorld && Generator.IsValid()) + { + if (UpdateType == EVoxelAssetActorPreviewUpdateType::RealTime) + { + UpdatePreview(); + } + if (bFinished) + { + ClampTransform(); + if (UpdateType == EVoxelAssetActorPreviewUpdateType::EndOfMove) + { + UpdatePreview(); + } + } + } +} + +bool AVoxelAssetActor::CanEditChange(const FProperty* InProperty) const +{ + if (InProperty) + { + const FName Name = InProperty->GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, bRoundAssetPosition) || + Name == GET_MEMBER_NAME_STATIC(AVoxelAssetActor, bRoundAssetRotation)) + { + if (PreviewWorld && PreviewWorld->RenderType == EVoxelRenderType::Cubic) + { + return false; + } + } + } + + return Super::CanEditChange(InProperty); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +bool AVoxelAssetActor::IsPreviewCreated() const +{ + return Data.IsValid(); +} + +void AVoxelAssetActor::CreatePreview() +{ + BindEditorDelegates(this); + + if (!ensure(PreviewWorld)) return; + if (!ensure(Generator.IsValid())) return; + if (!ensure(!IsPreviewCreated())) return; + + // Do that now as sometimes this is called before AVoxelWorld::BeginPlay + PreviewWorld->UpdateDynamicRendererSettings(); + + if (!StaticPool.IsValid()) + { + StaticPool = FVoxelDefaultPool::Create(8, true, {}, {}); + } + + PrimitiveComponent->SetWorldTransform(PreviewWorld->GetTransform()); + const FVoxelIntBox Bounds = AddItemToData(PreviewWorld, nullptr); + + { + bool bIsGeneratorSubtractive = bSubtractiveAsset; + if (bImportAsReference) + { + if (auto* DataAsset = Cast(Generator.GetObject())) + { + bIsGeneratorSubtractive = DataAsset->bSubtractiveAsset; + } + else + { + bIsGeneratorSubtractive = false; + } + } + auto EmptyGenerator = MakeVoxelShared(bIsGeneratorSubtractive ? -1 : 1); + EmptyGenerator->Init(PreviewWorld->GetGeneratorInit()); + Data = FVoxelData::Create( + FVoxelDataSettings( + Bounds, + EmptyGenerator, + false, + false)); + + const auto RealMergeMode = MergeMode; + MergeMode = EVoxelAssetMergeMode::AllValuesAndAllMaterials; + AddItemToData(PreviewWorld, Data.Get()); + MergeMode = RealMergeMode; + } + + DebugManager = FVoxelDebugManager::Create(FVoxelDebugManagerSettings( + PreviewWorld, + EVoxelPlayType::Preview, + StaticPool.ToSharedRef(), + Data.ToSharedRef(), + true)); + + Renderer = FVoxelDefaultRenderer::Create(FVoxelRendererSettings( + PreviewWorld, + EVoxelPlayType::Preview, + PrimitiveComponent, + Data.ToSharedRef(), + StaticPool.ToSharedRef(), + nullptr, + DebugManager.ToSharedRef(), + true)); + + LODManager = FVoxelFixedResolutionLODManager::Create( + FVoxelLODSettings(PreviewWorld, + EVoxelPlayType::Preview, + Renderer.ToSharedRef(), + StaticPool.ToSharedRef(), + Data.Get())); + + while (!LODManager->Initialize(FVoxelUtilities::ClampDepth(PreviewLOD), MaxPreviewChunks) && ensure(PreviewLOD < 24)) + { + PreviewLOD++; + } +} + +void AVoxelAssetActor::DestroyPreview() +{ + if (!ensure(IsPreviewCreated())) return; + + Data.Reset(); + + DebugManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), DebugManager); + + Renderer->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), Renderer); + + LODManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), LODManager); + + auto Components = GetComponents(); // need a copy as we are modifying it when destroying comps + for (auto& Component : Components) + { + if (Component && Component->HasAnyFlags(RF_Transient) && Component->IsA()) + { + Component->DestroyComponent(); + } + } +} + +void AVoxelAssetActor::UpdateBox() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(PreviewWorld)) return; + + const FVoxelIntBox Bounds = AddItemToData(PreviewWorld, nullptr); + + Box->SetWorldTransform(PreviewWorld->GetTransform()); + Box->SetBoxExtent(FVector(Bounds.Size()) / 2 * PreviewWorld->VoxelSize * PreviewWorld->GetActorScale3D()); + Box->SetWorldLocation(PreviewWorld->LocalToGlobalFloat(Bounds.GetCenter())); +} + +void AVoxelAssetActor::ClampTransform() +{ + const bool bForceRound = PreviewWorld->RenderType == EVoxelRenderType::Cubic; + + if (bRoundAssetPosition || bForceRound) + { + const FVector WorldLocation = PreviewWorld->GetActorLocation(); + const float VoxelSize = PreviewWorld->VoxelSize; + + FVector Position = GetActorLocation(); + Position -= WorldLocation; + Position /= VoxelSize; + + Position.X = FMath::RoundToInt(Position.X); + Position.Y = FMath::RoundToInt(Position.Y); + Position.Z = FMath::RoundToInt(Position.Z); + + Position *= VoxelSize; + Position += WorldLocation; + + SetActorLocation(Position); + } + + if (bRoundAssetRotation || bForceRound) + { + const FRotator WorldRotation = PreviewWorld->GetActorRotation(); + FRotator Rotation = GetActorRotation(); + + Rotation = (FRotationMatrix(Rotation) * FRotationMatrix(WorldRotation).Inverse()).Rotator(); + + Rotation.Pitch = FMath::RoundToInt(Rotation.Pitch / 90) * 90; + Rotation.Yaw = FMath::RoundToInt(Rotation.Yaw / 90) * 90; + Rotation.Roll = FMath::RoundToInt(Rotation.Roll / 90) * 90; + + Rotation = (FRotationMatrix(Rotation) * FRotationMatrix(WorldRotation)).Rotator(); + + SetActorRotation(Rotation); + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void AVoxelAssetActor::OnPrepareToCleanseEditorObject(UObject* Object) +{ + DestroyPreview(); +} +#endif + +#if WITH_EDITOR +TVoxelSharedPtr AVoxelAssetActor::StaticPool; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDataItemActor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDataItemActor.cpp new file mode 100644 index 00000000..3ec47bb7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDataItemActor.cpp @@ -0,0 +1,82 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/Actors/VoxelDataItemActor.h" +#include "VoxelMinimal.h" + +#include "Engine/World.h" +#include "TimerManager.h" + +AVoxelDataItemActor::AVoxelDataItemActor() +{ +#if WITH_EDITOR + static bool bAddedDelegate = false; + if (!bAddedDelegate) + { + FCoreUObjectDelegates::OnObjectPropertyChanged.AddLambda([](UObject* Object, FPropertyChangedEvent& PropertyChangedEvent) + { + if (!PropertyChangedEvent.Property || + PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) + { + return; + } + auto* Component = Cast(Object); + if (!Component) + { + return; + } + auto* DataItemActor = Cast(Component->GetOwner()); + if (!DataItemActor) + { + return; + } + + DataItemActor->ScheduleRefresh(); + }); + } +#endif +} + +void AVoxelDataItemActor::ScheduleRefresh() +{ + if (RefreshDelay <= 0) + { + OnRefresh.Broadcast(); + } + else + { + if (auto* World = GetWorld()) + { + World->GetTimerManager().SetTimer(RefreshTimerHandle, MakeWeakObjectPtrDelegate(this, [=]() + { + OnRefresh.Broadcast(); + }), RefreshDelay, false); + } + } +} + +#if WITH_EDITOR +void AVoxelDataItemActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + // Note: PropertyChangedEvent.Property will be null when undoing + if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + ScheduleRefresh(); + } +} + +void AVoxelDataItemActor::PostEditMove(bool bFinished) +{ + Super::PostEditMove(bFinished); + + ScheduleRefresh(); +} +#endif + +void AVoxelDataItemActor::Destroyed() +{ + OnRefresh.Broadcast(); + + Super::Destroyed(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.cpp new file mode 100644 index 00000000..7193181c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.cpp @@ -0,0 +1,132 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h" +#include "VoxelTools/VoxelAssetTools.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" + +#include "Components/BoxComponent.h" + +AVoxelDisableEditsBox::AVoxelDisableEditsBox() +{ + SetActorEnableCollision(false); + + Box = CreateDefaultSubobject("Box"); + Box->SetCollisionEnabled(ECollisionEnabled::NoCollision); + Box->SetBoxExtent(FVector::OneVector * 100); + SetRootComponent(Box); + +#if WITH_EDITOR + PrimaryActorTick.bCanEverTick = true; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelDisableEditsBox::AddItemToWorld(AVoxelWorld* World) +{ + check(World); + + FVoxelDisableEditsBoxItemReference Reference; + UVoxelAssetTools::AddDisableEditsBox( + Reference, + World, + GetBox(World)); +} + +FVoxelIntBox AVoxelDisableEditsBox::GetBox(AVoxelWorld* World) const +{ + check(World); + + const FIntVector Position = World->GlobalToLocal(GetActorLocation() + FVector::OneVector * World->VoxelSize / 2.f); + + const FVector Scale = GetActorScale3D(); + + const float Ratio = World->VoxelSize / 100.f; + const FIntVector S = FVoxelUtilities::RoundToInt(Scale / Ratio); + + return FVoxelIntBox(Position - S, Position + S); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void AVoxelDisableEditsBox::BeginPlay() +{ + Super::BeginPlay(); + PrimaryActorTick.SetTickFunctionEnable(false); +} + +void AVoxelDisableEditsBox::Tick(float DeltaTime) +{ + if (PreviewWorld) + { + if (VoxelSize != PreviewWorld->VoxelSize || WorldLocation != PreviewWorld->GetActorLocation()) + { + ClampTransform(); + } + } +} + +void AVoxelDisableEditsBox::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + if (PreviewWorld) + { + ClampTransform(); + } +} + +void AVoxelDisableEditsBox::PostEditMove(bool bFinished) +{ + Super::PostEditMove(bFinished); + + if (bFinished && PreviewWorld) + { + ClampTransform(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelDisableEditsBox::ClampTransform() +{ + VoxelSize = PreviewWorld->VoxelSize; + WorldLocation = PreviewWorld->GetActorLocation(); + + { + FVector Position = GetActorLocation(); + + const FVector HalfSize = FVector(VoxelSize) / 2; + + Position -= HalfSize; + Position -= WorldLocation; + Position /= VoxelSize; + + Position.X = FMath::RoundToInt(Position.X); + Position.Y = FMath::RoundToInt(Position.Y); + Position.Z = FMath::RoundToInt(Position.Z); + + Position *= VoxelSize; + Position += WorldLocation; + Position += HalfSize; + + SetActorLocation(Position); + } + { + FVector Scale = GetActorScale3D(); + + const float Ratio = VoxelSize / 100.f; + Scale.X = FMath::RoundToInt(FMath::Max(Scale.X, 0.f) / Ratio) * Ratio; + Scale.Y = FMath::RoundToInt(FMath::Max(Scale.Y, 0.f) / Ratio) * Ratio; + Scale.Z = FMath::RoundToInt(FMath::Max(Scale.Z, 0.f) / Ratio) * Ratio; + + SetActorScale3D(Scale); + } + SetActorRotation(FRotator::ZeroRotator); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp new file mode 100644 index 00000000..aa5aa082 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp @@ -0,0 +1,162 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h" +#include "VoxelPlaceableItems/Actors/VoxelDataItemActor.h" +#include "VoxelWorld.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelSpawners/IVoxelSpawnerManager.h" + +#include "EngineUtils.h" + +void UVoxelPlaceableItemActorHelper::Initialize() +{ + for (TActorIterator ActorItr(GetWorld()); ActorItr; ++ActorItr) + { + AddActor(**ActorItr); + } + + GetWorld()->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &UVoxelPlaceableItemActorHelper::OnActorSpawned)); +} + +AVoxelWorld& UVoxelPlaceableItemActorHelper::GetVoxelWorld() const +{ + return *CastChecked(GetOuter()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelPlaceableItemActorHelper::AddActor(AVoxelDataItemActor& Actor) +{ + AVoxelWorld& VoxelWorld = GetVoxelWorld(); + if (!ensure(VoxelWorld.IsCreated())) return; + + if (!ensure(VoxelWorld.PlaceableItemManager)) return; + UVoxelPlaceableItemManager& PlaceableItemManager = *VoxelWorld.PlaceableItemManager; + + FActorData& ActorData = ActorsData.FindOrAdd(&Actor); + + PlaceableItemManager.Clear(); + Actor.CallAddItemToWorld(&VoxelWorld); + + PlaceableItemManager.ApplyToData(VoxelWorld.GetData(), &ActorData.Items); + PlaceableItemManager.DrawDebug(VoxelWorld, VoxelWorld.GetLineBatchComponent()); + + Actor.OnRefresh.AddUObject(this, &UVoxelPlaceableItemActorHelper::OnActorUpdated, MakeWeakObjectPtr(&Actor)); +} + +void UVoxelPlaceableItemActorHelper::OnActorSpawned(AActor* Actor) +{ + if (auto* DataItemActor = Cast(Actor)) + { + AddActor(*DataItemActor); + } +} + +void UVoxelPlaceableItemActorHelper::OnActorUpdated(TWeakObjectPtr Actor) +{ + if (!ensure(Actor.IsValid()) || !ensure(ActorsData.Contains(Actor))) + { + return; + } + + FActorData& ActorData = ActorsData[Actor]; + + AVoxelWorld& VoxelWorld = GetVoxelWorld(); + if (!ensure(VoxelWorld.IsCreated()) || !ensure(IsValid(&VoxelWorld))) return; + + if (!ensure(VoxelWorld.PlaceableItemManager)) return; + UVoxelPlaceableItemManager& PlaceableItemManager = *VoxelWorld.PlaceableItemManager; + + FVoxelData& Data = VoxelWorld.GetData(); + + TArray BoundsToUpdate; + { + // Find which items to add/remove + TSet ItemInfosToAdd; + TSet ItemInfosToRemove; + { + PlaceableItemManager.Clear(); + if (!Actor->IsActorBeingDestroyed()) // If we're being destroyed, just remove existing items + { + Actor->CallAddItemToWorld(&VoxelWorld); + } + + TSet PreviousItemInfos; + TSet NewItemInfos; + + for (auto& It : ActorData.Items) + { + PreviousItemInfos.Add(It.Key); + } + NewItemInfos.Append(PlaceableItemManager.GetDataItemInfos()); + + ItemInfosToAdd = NewItemInfos.Difference(PreviousItemInfos); + ItemInfosToRemove = PreviousItemInfos.Difference(NewItemInfos); + } + + // Remove the items that aren't here anymore + for (const auto& ItemInfoToRemove : ItemInfosToRemove) + { + FItemPtr ItemPtr; + if (!ensure(ActorData.Items.RemoveAndCopyValue(ItemInfoToRemove, ItemPtr))) + { + continue; + } + + BoundsToUpdate.Add(ItemInfoToRemove.Bounds); + + FVoxelWriteScopeLock Lock(Data, ItemInfoToRemove.Bounds, FUNCTION_FNAME); + FString Error; + if (!ensure(Data.RemoveItem(ItemPtr, Error))) + { + LOG_VOXEL(Error, TEXT("Failed to remove data item for %s: %s"), *Actor->GetName(), *Error); + } + } + + // Add the new ones + { + PlaceableItemManager.Clear(); + + for (auto& ItemInfoToAdd : ItemInfosToAdd) + { + PlaceableItemManager.AddDataItem(ItemInfoToAdd); + } + + TMap NewItems; + PlaceableItemManager.ApplyToData(Data, &NewItems); + + for (auto& NewIt : NewItems) + { + const FItemInfo& ItemInfo = NewIt.Key; + const FItemPtr& ItemPtr = NewIt.Value; + ensure(ItemPtr.IsValid()); + + BoundsToUpdate.Add(ItemInfo.Bounds); + ActorData.Items.Add(ItemInfo, ItemPtr); + } + } + } + + if (BoundsToUpdate.Num() > 0) + { + VoxelWorld.GetLODManager().UpdateBounds(BoundsToUpdate); + + if (!Data.IsCurrentFrameEmpty()) + { + // Save the frame for the eventual asset item merge/remove edits + // Dummy frame, doesn't really store anything interesting + Data.SaveFrame(FVoxelIntBox(BoundsToUpdate)); + +#if WITH_EDITOR + IVoxelWorldEditor::GetVoxelWorldEditor()->RegisterTransaction(&VoxelWorld, "Applying voxel data item"); +#endif + } + + VoxelWorld.GetSpawnerManager().Regenerate(FVoxelIntBox(BoundsToUpdate)); + } + + PlaceableItemManager.DrawDebug(VoxelWorld, VoxelWorld.GetLineBatchComponent()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItem.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItem.cpp new file mode 100644 index 00000000..baf46886 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItem.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelMessages.h" +#include "VoxelObjectArchive.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelPlaceableItemsPointers); + +#define Macro(X) DEFINE_STAT(STAT_Num## X ## Pointers); +FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + +void FVoxelPlaceableItemsUtilities::SerializeItems( + FVoxelObjectArchive& Ar, + const FVoxelPlaceableItemLoadInfo& LoadInfo, + TArray& AssetItems) +{ + int32 Version = FVoxelPlaceableItemVersion::LatestVersion; + Ar << Version; + + int32 NumAssetItems = AssetItems.Num(); + Ar << NumAssetItems; + + if (Ar.IsSaving()) + { + // Might not be in game thread! + for (auto& Item : AssetItems) + { + TSoftObjectPtr Generator = Item.Generator->Object; + if (!ensure(!Generator.IsNull())) + { + LOG_VOXEL(Error, TEXT("Invalid Object pointer on a generator instance of class %s"), *Item.Generator->Class->GetName()); + } + + Ar << Generator; + Ar << Item.Bounds; + Ar << Item.LocalToWorld; + Ar << Item.Priority; + } + } + else + { + check(IsInGameThread()); + AssetItems.Empty(NumAssetItems); + for (int32 Index = 0; Index < NumAssetItems; Index++) + { + TSoftObjectPtr Generator; + FVoxelIntBox Bounds; + FTransform LocalToWorld; + int32 Priority = 0; + + Ar << Generator; + Ar << Bounds; + Ar << LocalToWorld; + Ar << Priority; + + auto* LoadedGenerator = Generator.LoadSynchronous(); + + if (!ensure(LoadedGenerator)) + { + LOG_VOXEL(Error, TEXT("Failed to load %s as VoxelTransformableGenerator"), *Generator.ToString()); + continue; + } + + const auto Instance = LoadedGenerator->GetTransformableInstance(); + Instance->Init(ensure(LoadInfo.GeneratorInit) ? *LoadInfo.GeneratorInit : FVoxelGeneratorInit()); + AssetItems.Emplace(FVoxelAssetItem{ Instance, Bounds, LocalToWorld, Priority }); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemManager.cpp new file mode 100644 index 00000000..8e581d87 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemManager.cpp @@ -0,0 +1,150 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" + +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelDebug/VoxelLineBatchComponent.h" +#include "VoxelGenerators/VoxelGeneratorCache.h" +#include "VoxelGenerators/VoxelGeneratorInstanceWrapper.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" + +void UVoxelPlaceableItemManager::AddDataItem(FVoxelDataItemConstructionInfo Info) +{ + if (!Info.Generator) + { + FVoxelMessages::Error(FUNCTION_ERROR("Generator is null!")); + return; + } + if (!Info.Generator->IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Generator is invalid!")); + return; + } + if (!Info.Bounds.IsValid()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Bounds are invalid! ") + Info.Bounds.ToString()); + return; + } + DataItemInfos.Add(Info); +} + +void UVoxelPlaceableItemManager::DrawDebugLine(FVector Start, FVector End, FLinearColor Color) +{ + DebugLines.Add({ Start, End, Color }); +} + +void UVoxelPlaceableItemManager::DrawDebugPoint(FVector Position, FLinearColor Color) +{ + DebugPoints.Add({ Position, Color }); +} + +UVoxelGeneratorCache* UVoxelPlaceableItemManager::GetGeneratorCache() const +{ + if (!GeneratorCache) + { + auto* This = const_cast(this); + This->GeneratorCache = NewObject(This); + } + return GeneratorCache; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelPlaceableItemManager::Generate() +{ + OnGenerate(); +} + +void UVoxelPlaceableItemManager::Clear() +{ + DataItemInfos.Reset(); + DebugLines.Reset(); + DebugPoints.Reset(); + + if (GeneratorCache && GeneratorCache->GetOuter() == this) + { + // Only clear if it's our own cache + GeneratorCache->ClearCache(); + } + + OnClear(); +} + +void UVoxelPlaceableItemManager::ApplyToData( + FVoxelData& Data, + TMap* OutItems) +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto& Info : DataItemInfos) + { + if (!ensure(Info.Generator) || !ensure(Info.Generator->IsValid())) + { + continue; + } + + FVoxelWriteScopeLock Lock(Data, Info.Bounds, FUNCTION_FNAME); // TODO No lock on start + const auto ItemPtr = Data.AddItem(Info.Generator->Instance, Info.Bounds, TArray(Info.Parameters), uint32(Info.Mask)); + ensure(ItemPtr.IsValid()); + + if (OutItems) + { + OutItems->Add(Info, ItemPtr); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelPlaceableItemManager::DrawDebug(const IVoxelWorldInterface& VoxelWorldInterface, UVoxelLineBatchComponent& LineBatchComponent) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!bEnableDebug) + { + return; + } + + const float Lifetime = 100000; + + const auto GetPosition = [&](const FVector& Position) + { + return LineBatchComponent.GetComponentTransform().InverseTransformPosition(VoxelWorldInterface.LocalToGlobalFloat(Position)); + }; + + for (auto& Line : DebugLines) + { + LineBatchComponent.BatchedLines.Add(FBatchedLine( + GetPosition(Line.Start), + GetPosition(Line.End), + Line.Color, + Lifetime, + 0.f, + 0)); + } + + for (auto& Point : DebugPoints) + { + LineBatchComponent.BatchedPoints.Add(FBatchedPoint( + GetPosition(Point.Position), + Point.Color, + 5.f, + Lifetime, + 0)); + } + LineBatchComponent.MarkRenderStateDirty(); + + if (bDebugBounds) + { + for (auto& Info : DataItemInfos) + { + UVoxelDebugUtilities::DrawDebugIntBox(VoxelWorldInterface, LineBatchComponent, FTransform(), Info.Bounds, Lifetime); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.cpp new file mode 100644 index 00000000..58bd31de --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.cpp @@ -0,0 +1,86 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h" + +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelData/VoxelData.inl" +#include "VoxelWorld.h" +#include "FastNoise/VoxelFastNoise.inl" + +#include "Misc/ScopeExit.h" + +void UVoxelPlaceableItemsUtilities::AddWorms(FAddWorm AddWorm, const FVoxelPerlinWormsSettings Settings) +{ + struct FLocalData + { + int32 Seed; + FRandomStream Stream; + int32 NumWorms = 0; + }; + static TUniquePtr LocalData; + + const bool bIsRoot = !LocalData.IsValid(); + ON_SCOPE_EXIT + { + if (bIsRoot) + { + LocalData.Reset(); + } + }; + + if (bIsRoot) + { + LocalData = MakeUnique(); + + LocalData->Seed = Settings.Seed; + LocalData->Stream = FRandomStream(LocalData->Seed++); + } + + LocalData->NumWorms++; + + if (LocalData->NumWorms > Settings.MaxWorms) + { + return; + } + + // Noise modules are per call, as they have different seeds + + FVoxelFastNoise ModuleX; + FVoxelFastNoise ModuleY; + FVoxelFastNoise ModuleZ; + + ModuleX.SetSeed(LocalData->Seed++); + ModuleY.SetSeed(LocalData->Seed++); + ModuleZ.SetSeed(LocalData->Seed++); + + auto& Stream = LocalData->Stream; + + FVector CurrentPosition = Settings.Start; + FVector CurrentDir = Settings.Direction.GetSafeNormal(); + for (int32 SegmentIndex = 0; SegmentIndex < Settings.NumSegments; SegmentIndex++) + { + const FVector NewPosition = CurrentPosition + CurrentDir * Settings.SegmentLength; + AddWorm.ExecuteIfBound(CurrentPosition, NewPosition, Settings.Radius); + CurrentPosition = NewPosition; + + const FVector NoisePosition = Settings.NoiseDirection * Settings.NoiseSegmentLength * SegmentIndex; + CurrentDir = CurrentDir.RotateAngleAxis(Settings.RotationAmplitude.X * ModuleX.GetSimplex_3D(NoisePosition.X, NoisePosition.Y, NoisePosition.Z, 0.02f), FVector(1, 0, 0)); + CurrentDir = CurrentDir.RotateAngleAxis(Settings.RotationAmplitude.Y * ModuleY.GetSimplex_3D(NoisePosition.X, NoisePosition.Y, NoisePosition.Z, 0.02f), FVector(0, 1, 0)); + CurrentDir = CurrentDir.RotateAngleAxis(Settings.RotationAmplitude.Z * ModuleZ.GetSimplex_3D(NoisePosition.X, NoisePosition.Y, NoisePosition.Z, 0.02f), FVector(0, 0, 1)); + + if (Stream.FRand() < Settings.SplitProbability) + { + auto NewSettings = Settings; + NewSettings.Start = CurrentPosition; + NewSettings.Direction = CurrentDir + .RotateAngleAxis((Stream.FRand() * 2 - 1) * Settings.RotationAmplitude.X, FVector(1, 0, 0)) + .RotateAngleAxis((Stream.FRand() * 2 - 1) * Settings.RotationAmplitude.Y, FVector(0, 1, 0)) + .RotateAngleAxis((Stream.FRand() * 2 - 1) * Settings.RotationAmplitude.Z, FVector(0, 0, 1)); + + NewSettings.NumSegments = FMath::Min(Settings.BranchMeanSize + (Stream.FRand() * 2 - 1) * Settings.BranchSizeVariation * Settings.BranchMeanSize, Settings.NumSegments - (SegmentIndex + 1)); + NewSettings.SplitProbability *= Settings.SplitProbabilityGain; + + AddWorms(AddWorm, NewSettings); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelLODManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelLODManager.cpp new file mode 100644 index 00000000..456cc45f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelLODManager.cpp @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelData.h" +#include "VoxelWorld.h" + +FVoxelLODSettings::FVoxelLODSettings( + const AVoxelWorld* InWorld, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Renderer, + const TVoxelSharedRef& Pool, + const FVoxelData* Data) + : Renderer(Renderer) + , Pool(Pool) + , VoxelSize(InWorld->VoxelSize) + , OctreeDepth(FVoxelUtilities::ClampDepth(FMath::Max(1, Data + ? FVoxelUtilities::ConvertDepth(Data->Depth) + : InWorld->RenderOctreeDepth))) + , WorldBounds(Data + ? Data->WorldBounds + : InWorld->GetWorldBounds()) + , bConstantLOD(PlayType == EVoxelPlayType::Game + ? InWorld->bConstantLOD + : false) + , bStaticWorld(PlayType == EVoxelPlayType::Game + ? InWorld->bStaticWorld + : false) + , MinDelayBetweenLODUpdates(InWorld->MinDelayBetweenLODUpdates) + , bEnableTransitions(InWorld->bEnableTransitions) + , bInvertTransitions(InWorld->RenderType == EVoxelRenderType::SurfaceNets) + + , World(InWorld->GetWorld()) +{ +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelRenderer.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelRenderer.cpp new file mode 100644 index 00000000..14ee8644 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/IVoxelRenderer.cpp @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelRender/VoxelMaterialIndices.h" +#include "VoxelData/VoxelData.h" +#include "VoxelMessages.h" +#include "VoxelPriorityHandler.h" +#include "VoxelWorld.h" +#include "VoxelUniqueError.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +#include "Logging/MessageLog.h" + +FVoxelRendererSettingsBase::FVoxelRendererSettingsBase( + const AVoxelWorld* InWorld, + EVoxelPlayType InPlayType, + UPrimitiveComponent* RootComponent, + const FVoxelData* Data) + : VoxelSize(InWorld->VoxelSize) + , WorldOffset(Data + ? MakeVoxelShared(0) + : InWorld->GetWorldOffsetPtr()) + , ProcMeshClass(InPlayType == EVoxelPlayType::Game && InWorld->ProcMeshClass + ? InWorld->ProcMeshClass.Get() + : UVoxelProceduralMeshComponent::StaticClass()) + , bCastFarShadow(InWorld->bCastFarShadow) + + , PlayType(InPlayType) + + , World(InWorld->GetWorld()) + , RootComponent(RootComponent) + + , UVConfig(InWorld->UVConfig) + , UVScale(InWorld->UVScale) + , NormalConfig(InWorld->NormalConfig) + , MaterialConfig(InWorld->MaterialConfig) + , bHardColorTransitions(InWorld->bHardColorTransitions) + + , BoundsExtension(InWorld->BoundsExtension) + + , CollisionTraceFlag(InWorld->CollisionTraceFlag) + , NumConvexHullsPerAxis(InWorld->NumConvexHullsPerAxis) + , bCleanCollisionMeshes(InWorld->bCleanCollisionMeshes) + + , RenderType(InWorld->RenderType) + , RenderSharpness(FMath::Max(0, InWorld->RenderSharpness)) + , bCreateMaterialInstances(InPlayType == EVoxelPlayType::Game + ? InWorld->bCreateMaterialInstances && !InWorld->bMergeChunks + : false /* we don't want to created dynamic material instances in editor */) + , bDitherChunks(InWorld->bDitherChunks && bCreateMaterialInstances) + , ChunksDitheringDuration(InWorld->ChunksDitheringDuration) + + , bOptimizeIndices(InWorld->bOptimizeIndices) + + , MaxDistanceFieldLOD(InWorld->bGenerateDistanceFields ? InWorld->MaxDistanceFieldLOD : -1) + , DistanceFieldBoundsExtension(InWorld->DistanceFieldBoundsExtension) + , DistanceFieldResolutionDivisor(InWorld->DistanceFieldResolutionDivisor) + , DistanceFieldSelfShadowBias(InWorld->DistanceFieldSelfShadowBias) + + , bOneMaterialPerCubeSide(InWorld->MaterialConfig == EVoxelMaterialConfig::SingleIndex && InWorld->bOneMaterialPerCubeSide) + , bHalfPrecisionCoordinates(InWorld->bHalfPrecisionCoordinates) + , bInterpolateColors(InWorld->bInterpolateColors) + , bInterpolateUVs(InWorld->bInterpolateUVs) + , bSRGBColors(InWorld->bSRGBColors) + + , bRenderWorld(InWorld->bRenderWorld) + + , MeshUpdatesBudget(InPlayType == EVoxelPlayType::Game + ? FMath::Max(0.001f, InWorld->MeshUpdatesBudget) + : 1000) + + , HolesMaterials(InWorld->HolesMaterials) + , MaterialsMeshConfigs(InWorld->MaterialsMeshConfigs) + + , bMergeChunks(InWorld->bMergeChunks) + , ChunksClustersSize(FMath::Max(RENDER_CHUNK_SIZE, InWorld->ChunksClustersSize)) + , bDoNotMergeCollisionsAndNavmesh(InWorld->bMergeChunks && InWorld->bDoNotMergeCollisionsAndNavmesh) + + , bStaticWorld(InPlayType == EVoxelPlayType::Game + ? InWorld->bStaticWorld + : false) + + , PriorityDuration(InWorld->PriorityDuration) + , DynamicSettings(InWorld->GetRendererDynamicSettings()) +{ +} + +FVoxelRendererSettings::FVoxelRendererSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + UPrimitiveComponent* RootComponent, + const TVoxelSharedRef& Data, + const TVoxelSharedRef& Pool, + const TVoxelSharedPtr& ToolRenderingManager, + const TVoxelSharedRef& DebugManager, + bool bUseDataSettings) + : FVoxelRendererSettingsBase(World, PlayType, RootComponent, bUseDataSettings ? &Data.Get() : nullptr) + , Data(Data) + , Pool(Pool) + , ToolRenderingManager(ToolRenderingManager) + , DebugManager(DebugManager) +{ + +} + +IVoxelRenderer::IVoxelRenderer(const FVoxelRendererSettings& Settings) + : Settings(Settings) + , InvokersPositionsForPriorities(MakeVoxelShared(32)) +{ +} + +void IVoxelRenderer::SetInvokersPositionsForPriorities(const TArray& NewInvokersPositionsForPriorities) +{ + while (InvokersPositionsForPriorities->GetMax() < NewInvokersPositionsForPriorities.Num()) + { + InvokersPositionsForPriorities = MakeVoxelShared(2 * InvokersPositionsForPriorities->GetMax()); + } + InvokersPositionsForPriorities->Set(NewInvokersPositionsForPriorities); +} + +inline UObject* GetRootOwner(const TWeakObjectPtr& RootComponent) +{ + return RootComponent.IsValid() ? RootComponent->GetOwner() : nullptr; +} + +UMaterialInterface* FVoxelRendererSettingsBase::GetVoxelMaterial(int32 LOD, const FVoxelMaterialIndices& MaterialIndices) const +{ + auto* MaterialCollection = DynamicSettings->LODData[LOD].MaterialCollection.Get(); + if (!MaterialCollection) + { + static TVoxelUniqueError<> UniqueError; + FVoxelMessages::CondError( + UniqueError(UniqueId, {}), + "Invalid Material Collection", + GetRootOwner(RootComponent)); + return FVoxelUtilities::GetDefaultMaterial(MaterialIndices.NumIndices); + } + + return MaterialCollection->GetVoxelMaterial(MaterialIndices, UniqueId); +} + +UMaterialInterface* FVoxelRendererSettingsBase::GetVoxelMaterial(int32 LOD) const +{ + if (auto* Material = DynamicSettings->LODData[LOD].Material.Get()) + { + return Material; + } + else + { + static TVoxelUniqueError<> UniqueError; + FVoxelMessages::CondError( + UniqueError(UniqueId, {}), + "Invalid VoxelMaterial", + GetRootOwner(RootComponent)); + return FVoxelUtilities::GetDefaultMaterial(0); + } +} + +uint64 FVoxelRendererSettingsBase::UniqueIdCounter = 0; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.cpp new file mode 100644 index 00000000..143a91f1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.cpp @@ -0,0 +1,341 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/LODManager/VoxelDefaultLODManager.h" +#include "VoxelRender/LODManager/VoxelRenderOctree.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelIntBox.h" +#include "IVoxelPool.h" +#include "VoxelWorldInterface.h" +#include "VoxelComponents/VoxelInvokerComponent.h" + +DECLARE_DWORD_COUNTER_STAT(TEXT("Voxel Chunk Updates"), STAT_VoxelChunkUpdates, STATGROUP_VoxelCounters); + +static TAutoConsoleVariable CVarFreezeLODs( + TEXT("voxel.lod.FreezeLODs"), + 0, + TEXT("Stops LOD manager tick"), + ECVF_Default); + +TVoxelSharedRef FVoxelDefaultLODManager::Create( + const FVoxelLODSettings& LODSettings, + TWeakObjectPtr VoxelWorldInterface, + const TVoxelSharedRef& DynamicSettings) +{ + TVoxelSharedRef Result = MakeShareable( + new FVoxelDefaultLODManager( + LODSettings, + VoxelWorldInterface, + DynamicSettings)); + + UVoxelInvokerComponentBase::OnForceRefreshInvokers.AddThreadSafeSP(Result, &FVoxelDefaultLODManager::ClearInvokerComponents); + return Result; +} + +FVoxelDefaultLODManager::FVoxelDefaultLODManager( + const FVoxelLODSettings& LODSettings, + TWeakObjectPtr VoxelWorldInterface, + const TVoxelSharedRef& DynamicSettings) + : IVoxelLODManager(LODSettings) + , VoxelWorldInterface(VoxelWorldInterface) + , DynamicSettings(DynamicSettings) + , Task(TUniquePtr>( + new FVoxelRenderOctreeAsyncBuilder(LODSettings.OctreeDepth, LODSettings.WorldBounds))) +{ +} + +FVoxelDefaultLODManager::~FVoxelDefaultLODManager() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); // FTickable + if (!Task->IsDone()) + { + Task->CancelAndAutodelete(); + Task.Release(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +inline FVoxelIntBox GetBoundsToUpdate(const FVoxelIntBox& Bounds) +{ + // For normals + return Bounds.Extend(2); +} + +int32 FVoxelDefaultLODManager::UpdateBounds(const FVoxelIntBox& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!Octree.IsValid()) + { + return 0; + } + + TArray ChunksToUpdate; + Octree->GetChunksToUpdateForBounds(GetBoundsToUpdate(Bounds), ChunksToUpdate, OnChunkUpdate); + return Settings.Renderer->UpdateChunks(Bounds, ChunksToUpdate, FinishDelegate); +} + +int32 FVoxelDefaultLODManager::UpdateBounds(const TArray& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!Octree.IsValid() || Bounds.Num() == 0) + { + return 0; + } + + TArray ChunksToUpdate; + FVoxelIntBox GlobalBounds = Bounds[0]; + for (auto& BoundsToUpdate : Bounds) + { + GlobalBounds = GlobalBounds + BoundsToUpdate; + Octree->GetChunksToUpdateForBounds(GetBoundsToUpdate(BoundsToUpdate), ChunksToUpdate, OnChunkUpdate); + } + return Settings.Renderer->UpdateChunks(GlobalBounds, ChunksToUpdate, FinishDelegate); +} + +void FVoxelDefaultLODManager::ForceLODsUpdate() +{ + bLODUpdateQueued = true; +} + +bool FVoxelDefaultLODManager::AreCollisionsEnabled(const FIntVector& Position, uint8& OutLOD) const +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Octree.IsValid()) + { + return false; + } + + OutLOD = 255; + + auto* Ptr = Octree.Get(); + while (Ptr->HasChildren()) + { + Ptr = &Ptr->GetChild(Position); + if (Ptr->GetSettings().bEnableCollisions) + { + OutLOD = Ptr->Height; + } + } + + return OutLOD != 255; +} + +void FVoxelDefaultLODManager::Destroy() +{ + if (IsTicking()) + { + StopTicking(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultLODManager::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + if (CVarFreezeLODs.GetValueOnGameThread() != 0) + { + return; + } + + const double Time = FPlatformTime::Seconds(); + if (Time - LastInvokersUpdateTime > Settings.MinDelayBetweenLODUpdates) + { + LastInvokersUpdateTime = Time; + UpdateInvokers(); + } + + if (bAsyncTaskWorking && Task->IsDone()) + { + VOXEL_SCOPE_COUNTER("OnTaskFinished"); + + LOG_VOXEL(Verbose, TEXT("LOD Update Finished")); + + Task->ReportBuildTime(); + if (Task->NewOctree.IsValid()) // Make sure the new octree is valid before using it, else the ids will be out of sync + { + // Move Octree to OctreeToDelete so that we delete it async, without a huge cost on the game thread + ensure(!Task->OctreeToDelete.IsValid()); + Task->OctreeToDelete = MoveTemp(Octree); + + Octree = Task->NewOctree; + + INC_DWORD_STAT_BY(STAT_VoxelChunkUpdates, Task->ChunkUpdates.Num()); + Settings.Renderer->UpdateLODs(Octree->UpdateIndex, Task->ChunkUpdates); + + if (Settings.bStaticWorld) + { + // Destroy octree and stop ticking + VOXEL_SCOPE_COUNTER("Destroying octree"); + Octree.Reset(); + Task->NewOctree.Reset(); + Task->OldOctree.Reset(); + StopTicking(); + } + } + bAsyncTaskWorking = false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultLODManager::UpdateInvokers() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!VoxelWorldInterface.IsValid()) + { + return; + } + + ensure(SortedInvokerComponents.Num() == InvokerComponentsInfos.Num()); + + bool bNeedUpdate = false; + + TArray> NewSortedInvokerComponents = UVoxelInvokerComponentBase::GetInvokers(Settings.World.Get()); + NewSortedInvokerComponents.Sort([](auto& A, auto& B) { return A.Get() < B.Get(); }); + + if (SortedInvokerComponents != NewSortedInvokerComponents) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: Invoker Components Array changed")); + bNeedUpdate = true; + } + + TMap, FVoxelInvokerInfo> NewInvokerComponentsInfos; + NewInvokerComponentsInfos.Reserve(NewSortedInvokerComponents.Num()); + + const uint64 SquaredDistanceThreshold = FMath::Square(FMath::Max(DynamicSettings->InvokerDistanceThreshold / Settings.VoxelSize, 0.f)); // Truncate + for (const auto& InvokerComponent : NewSortedInvokerComponents) + { + FVoxelInvokerSettings InvokerSettings = InvokerComponent->GetInvokerSettings(VoxelWorldInterface.Get()); + InvokerSettings.bUseForLOD &= InvokerComponent->IsLocalInvoker(); + + const FIntVector InvokerPosition = InvokerComponent->GetInvokerVoxelPosition(VoxelWorldInterface.Get()); + + FVoxelInvokerInfo Info; + Info.LocalPosition = InvokerPosition; + Info.Settings = InvokerSettings; + + NewInvokerComponentsInfos.Add(InvokerComponent, Info); + + if (!bNeedUpdate) + { + auto* ExistingInfo = InvokerComponentsInfos.Find(InvokerComponent); + if (ensure(ExistingInfo)) // Should be valid if bNeedUpdate is false (would be set to true if invoker components array changed) + { + const auto& OldSettings = ExistingInfo->Settings; + const auto& NewSettings = Info.Settings; + if (OldSettings.bUseForLOD != NewSettings.bUseForLOD) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: bUseForLOD changed")); + bNeedUpdate = true; + } + else if (OldSettings.LODToSet != NewSettings.LODToSet) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: LODToSet changed")); + bNeedUpdate = true; + } + else if (OldSettings.bUseForCollisions != NewSettings.bUseForCollisions) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: bUseForCollisions changed")); + bNeedUpdate = true; + } + else if (OldSettings.bUseForNavmesh != NewSettings.bUseForNavmesh) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: bUseForNavmesh changed")); + bNeedUpdate = true; + } + else if (FVoxelUtilities::SquaredSize(ExistingInfo->LocalPosition - Info.LocalPosition) > SquaredDistanceThreshold) + { + LOG_VOXEL(Verbose, TEXT("Tiggering LOD Update: Invoker Component moved")); + bNeedUpdate = true; + } + } + } + } + + if (bNeedUpdate) + { + if (!Settings.bConstantLOD) + { + bLODUpdateQueued = true; + } + + SortedInvokerComponents = MoveTemp(NewSortedInvokerComponents); + InvokerComponentsInfos = MoveTemp(NewInvokerComponentsInfos); + + TArray InvokersPositionsForPriorities; + for (auto& It : InvokerComponentsInfos) + { + if (It.Key->bUseForPriorities) + { + InvokersPositionsForPriorities.Add(It.Value.LocalPosition); + } + } + Settings.Renderer->SetInvokersPositionsForPriorities(InvokersPositionsForPriorities); + } + + ensure(SortedInvokerComponents.Num() == InvokerComponentsInfos.Num()); + + const double Time = FPlatformTime::Seconds(); + if (bLODUpdateQueued && Task->IsDone() && Time - LastLODUpdateTime > Settings.MinDelayBetweenLODUpdates) + { + bLODUpdateQueued = false; + LastLODUpdateTime = Time; + UpdateLODs(); + } +} + +void FVoxelDefaultLODManager::UpdateLODs() +{ + VOXEL_FUNCTION_COUNTER(); + + LOG_VOXEL(Verbose, TEXT("Starting LOD Update")); + + FVoxelRenderOctreeSettings OctreeSettings; + OctreeSettings.MinLOD = DynamicSettings->MinLOD; + OctreeSettings.MaxLOD = DynamicSettings->MaxLOD; + OctreeSettings.WorldBounds = Settings.WorldBounds; + + OctreeSettings.Invokers.Reserve(InvokerComponentsInfos.Num()); + for (const auto& It : InvokerComponentsInfos) + { + if (It.Value.Settings.bUseForLOD || + It.Value.Settings.bUseForCollisions || + It.Value.Settings.bUseForNavmesh) + { + OctreeSettings.Invokers.Add(It.Value.Settings); + } + } + + OctreeSettings.ChunksCullingLOD = DynamicSettings->ChunksCullingLOD; + + OctreeSettings.bEnableRender = DynamicSettings->bEnableRender; + OctreeSettings.bEnableTransitions = Settings.bEnableTransitions; + OctreeSettings.bInvertTransitions = Settings.bInvertTransitions; + + OctreeSettings.bEnableCollisions = DynamicSettings->bEnableCollisions; + OctreeSettings.bComputeVisibleChunksCollisions = DynamicSettings->bComputeVisibleChunksCollisions; + OctreeSettings.VisibleChunksCollisionsMaxLOD = DynamicSettings->VisibleChunksCollisionsMaxLOD; + + OctreeSettings.bEnableNavmesh = DynamicSettings->bEnableNavmesh; + OctreeSettings.bComputeVisibleChunksNavmesh = DynamicSettings->bComputeVisibleChunksNavmesh; + OctreeSettings.VisibleChunksNavmeshMaxLOD = DynamicSettings->VisibleChunksNavmeshMaxLOD; + + Task->Init(OctreeSettings, Octree); + Settings.Pool->QueueTask(EVoxelTaskType::RenderOctree, Task.Get()); + bAsyncTaskWorking = true; +} + +void FVoxelDefaultLODManager::ClearInvokerComponents() +{ + SortedInvokerComponents.Reset(); + InvokerComponentsInfos.Reset(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.h new file mode 100644 index 00000000..1bd9dd02 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.h @@ -0,0 +1,92 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTickable.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelInvokerSettings.h" +#include "VoxelMinimal.h" +#include "VoxelAsyncWork.h" + +class FVoxelRenderOctreeAsyncBuilder; +class FVoxelRenderOctree; +class UVoxelInvokerComponentBase; +class AVoxelWorldInterface; + +struct FVoxelLODDynamicSettings +{ + int32 MinLOD; + int32 MaxLOD; + + // In world space + float InvokerDistanceThreshold; + + int32 ChunksCullingLOD; + + bool bEnableRender; + + bool bEnableCollisions; + bool bComputeVisibleChunksCollisions; + int32 VisibleChunksCollisionsMaxLOD; + + bool bEnableNavmesh; + bool bComputeVisibleChunksNavmesh; + int32 VisibleChunksNavmeshMaxLOD; +}; + +class FVoxelDefaultLODManager : public IVoxelLODManager, public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + static TVoxelSharedRef Create( + const FVoxelLODSettings& LODSettings, + TWeakObjectPtr VoxelWorldInterface, + const TVoxelSharedRef& DynamicSettings); + ~FVoxelDefaultLODManager(); + + //~ Begin IVoxelLODManager Interface + virtual int32 UpdateBounds(const FVoxelIntBox& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) override final; + virtual int32 UpdateBounds(const TArray& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) override final; + + virtual void ForceLODsUpdate() override final; + virtual bool AreCollisionsEnabled(const FIntVector& Position, uint8& OutLOD) const override final; + + virtual void Destroy() override final; + //~ End IVoxelLODManager Interface + + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +private: + FVoxelDefaultLODManager( + const FVoxelLODSettings& LODSettings, + TWeakObjectPtr VoxelWorldInterface, + const TVoxelSharedRef& DynamicSettings); + + const TWeakObjectPtr VoxelWorldInterface; + const TVoxelSharedRef DynamicSettings; + + TUniquePtr> Task; + + TVoxelSharedPtr Octree; + + struct FVoxelInvokerInfo + { + FIntVector LocalPosition{ForceInit}; + FVoxelInvokerSettings Settings; + }; + TMap, FVoxelInvokerInfo> InvokerComponentsInfos; + TArray> SortedInvokerComponents; + + bool bAsyncTaskWorking = false; + bool bLODUpdateQueued = true; + double LastLODUpdateTime = 0; + double LastInvokersUpdateTime = 0; + + void UpdateInvokers(); + void UpdateLODs(); + + void ClearInvokerComponents(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.cpp new file mode 100644 index 00000000..105fcf58 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.cpp @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/LODManager/VoxelFixedResolutionLODManager.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelChunkToUpdate.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +TVoxelSharedRef FVoxelFixedResolutionLODManager::Create( + const FVoxelLODSettings& LODSettings) +{ + return MakeShareable(new FVoxelFixedResolutionLODManager(LODSettings)); +} + +bool FVoxelFixedResolutionLODManager::Initialize(int32 ChunkLOD, int32 MaxChunks) +{ + TArray ChunkUpdates; + + const int32 ChunkSize = FVoxelUtilities::GetSizeFromDepth(ChunkLOD); + const FVoxelIntBox& WorldBounds = Settings.WorldBounds; + + const FIntVector Min = FVoxelUtilities::FloorToInt(FVector(WorldBounds.Min) / ChunkSize) * ChunkSize; + const FIntVector Max = FVoxelUtilities::CeilToInt(FVector(WorldBounds.Max) / ChunkSize) * ChunkSize; + + const FIntVector NumChunksPerAxis = (Max - Min) / ChunkSize; + const int64 TotalNumChunks = int64(NumChunksPerAxis.X) * int64(NumChunksPerAxis.Y) * int64(NumChunksPerAxis.Z); + + if (TotalNumChunks > MaxChunks) + { + return false; + } + + uint64 Id = 0; + for (int32 X = Min.X; X < Max.X; X += ChunkSize) + { + for (int32 Y = Min.Y; Y < Max.Y; Y += ChunkSize) + { + for (int32 Z = Min.Z; Z < Max.Z; Z += ChunkSize) + { + const FIntVector Position = FIntVector(X, Y, Z); + const FVoxelIntBox ChunkBounds = FVoxelUtilities::GetBoundsFromPositionAndDepth(Position, ChunkLOD); + if (WorldBounds.Intersect(ChunkBounds)) + { + ChunkUpdates.Emplace( + FVoxelChunkUpdate + { + Id++, + ChunkLOD, + ChunkBounds, + {}, + FVoxelChunkSettings::VisibleWithCollisions(), + {} + }); + } + } + } + } + + Settings.Renderer->UpdateLODs(1, ChunkUpdates); + + return true; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.h new file mode 100644 index 00000000..6ea83032 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelMinimal.h" + +class FVoxelFixedResolutionLODManager : public IVoxelLODManager +{ +public: + static TVoxelSharedRef Create(const FVoxelLODSettings& LODSettings); + + bool Initialize(int32 ChunkLOD, int32 MaxChunks); + + virtual int32 UpdateBounds(const FVoxelIntBox& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) override final { return 0; } + virtual int32 UpdateBounds(const TArray& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate) override final { return 0; } + + virtual void ForceLODsUpdate() override final {} + virtual bool AreCollisionsEnabled(const FIntVector& Position, uint8& OutLOD) const override final { return false; } + + virtual void Destroy() override final {} + +private: + using IVoxelLODManager::IVoxelLODManager; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.cpp new file mode 100644 index 00000000..2817e684 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.cpp @@ -0,0 +1,800 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRenderOctree.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelMessages.h" +#include "Async/Async.h" + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Render Octrees Count"), STAT_VoxelRenderOctreesCount, STATGROUP_VoxelCounters); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelRenderOctreesMemory); + +static TAutoConsoleVariable CVarMaxRenderOctreeChunks( + TEXT("voxel.renderer.MaxRenderOctreeChunks"), + 1000000, + TEXT("Max render octree chunks. Allows to stop the creation of the octree before it gets too big & freezes your computer"), + ECVF_Default); + +static TAutoConsoleVariable CVarLogRenderOctreeBuildTime( + TEXT("voxel.renderer.LogRenderOctreeBuildTime"), + 0, + TEXT("If true, will log the render octree build times"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelRenderOctreeAsyncBuilder::FVoxelRenderOctreeAsyncBuilder(uint8 OctreeDepth, const FVoxelIntBox& WorldBounds) + : FVoxelAsyncWork(STATIC_FNAME("Render Octree Build"), 1e9) + , OctreeDepth(OctreeDepth) + , WorldBounds(WorldBounds) +{ + SetIsDone(true); +} + +void FVoxelRenderOctreeAsyncBuilder::Init(const FVoxelRenderOctreeSettings& InOctreeSettings, TVoxelSharedPtr InOctree) +{ + VOXEL_FUNCTION_COUNTER(); + + OctreeSettings = InOctreeSettings; + OldOctree = InOctree; + + SetIsDone(false); + Counter = FPlatformTime::Seconds(); + Log = "Render octree build stats:"; +} + +#define LOG_TIME_IMPL(Name, Counter) Log += "\n\t" Name ": " + FString::SanitizeFloat((FPlatformTime::Seconds() - Counter) * 1000.f) + "ms"; Counter = FPlatformTime::Seconds(); +#define LOG_TIME(Name) LOG_TIME_IMPL("\t" Name, Counter) + +void FVoxelRenderOctreeAsyncBuilder::ReportBuildTime() +{ + VOXEL_FUNCTION_COUNTER(); + + LOG_TIME("Waiting for game thread"); + + if (CVarLogRenderOctreeBuildTime.GetValueOnGameThread()) + { + LOG_VOXEL(Log, TEXT("%s"), *Log); + } + + if (bTooManyChunks) + { + FVoxelMessages::Error(FString::Printf(TEXT( + "Render octree update was stopped!\n" + "Max render octree chunks count reached: voxel.renderer.MaxRenderOctreeChunks < %d.\n" + "This is caused by too demanding LOD settings.\n" + "You can try the following: \n" + "- reduce World Size\n" + "- increase Max LOD\n" + "- reduce invokers distances"), NumberOfChunks)); + } +} + +void FVoxelRenderOctreeAsyncBuilder::DoWork() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + LOG_TIME_IMPL("Waiting in thread pool", Counter); + + double WorkStartTime = FPlatformTime::Seconds(); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Deleting previous octree"); + OctreeToDelete.Reset(); + LOG_TIME("Deleting previous octree"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Resetting arrays"); + ChunkUpdates.Reset(); + NewOctree.Reset(); + LOG_TIME("Resetting arrays"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Cloning octree"); + NewOctree = OldOctree.IsValid() ? MakeVoxelShared(&*OldOctree) : MakeVoxelShared(OctreeDepth); + LOG_TIME("Cloning octree"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("ResetDivisionType"); + NewOctree->ResetDivisionType(); + LOG_TIME("ResetDivisionType"); + } + + bool bChanged; + { + VOXEL_ASYNC_SCOPE_COUNTER("UpdateSubdividedByDistance"); + bChanged = NewOctree->UpdateSubdividedByDistance(OctreeSettings); + LOG_TIME("UpdateSubdividedByDistance"); + Log += "; Need to recompute neighbors: " + FString(bChanged ? "true" : "false"); + } + + if (bChanged) + { + VOXEL_ASYNC_SCOPE_COUNTER("UpdateSubdividedByNeighbors"); + int32 UpdateSubdividedByNeighborsCounter = 0; + while (NewOctree->UpdateSubdividedByNeighbors(OctreeSettings)) { UpdateSubdividedByNeighborsCounter++; } + LOG_TIME("UpdateSubdividedByNeighbors"); + Log += "; Iterations: " + FString::FromInt(UpdateSubdividedByNeighborsCounter); + } + else + { + VOXEL_ASYNC_SCOPE_COUNTER("ReuseOldNeighbors"); + NewOctree->ReuseOldNeighbors(); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("UpdateSubdividedByOthers"); + NewOctree->UpdateSubdividedByOthers(OctreeSettings); + LOG_TIME("UpdateSubdividedByOthers"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("DeleteChunks"); + NewOctree->DeleteChunks(ChunkUpdates); + LOG_TIME("DeleteChunks"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("GetUpdates"); + NewOctree->GetUpdates(NewOctree->UpdateIndex + 1, bChanged, OctreeSettings, ChunkUpdates); + LOG_TIME("GetUpdates"); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Sort By LODs"); + // Make sure that LOD 0 chunks are processed first + ChunkUpdates.Sort([](const auto& A, const auto& B) { return A.LOD < B.LOD; }); + LOG_TIME("Sort By LODs"); + } + + if (OldOctree.IsValid()) + { + VOXEL_ASYNC_SCOPE_COUNTER("Find previous chunks"); + for (auto& ChunkUpdate : ChunkUpdates) + { + if (ChunkUpdate.NewSettings.bVisible && !ChunkUpdate.OldSettings.bVisible) + { + OldOctree->GetVisibleChunksOverlappingBounds(ChunkUpdate.Bounds, ChunkUpdate.PreviousChunks); + } + } + } + LOG_TIME("Find previous chunks"); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Deleting old octree"); + OldOctree.Reset(); + LOG_TIME("Deleting old octree"); + } + + NumberOfChunks = NewOctree->CurrentChunksCount; + bTooManyChunks = NewOctree->IsCanceled(); + + if (bTooManyChunks) + { + NewOctree.Reset(); + } + + LOG_TIME_IMPL("Total time working", WorkStartTime); +} + +uint32 FVoxelRenderOctreeAsyncBuilder::GetPriority() const +{ + return 0; +} + +#undef LOG_TIME + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_MAX_CHUNKS_COUNT_IMPL(ReturnValue) if (IsCanceled()) { return ReturnValue; } +#define CHECK_MAX_CHUNKS_COUNT() CHECK_MAX_CHUNKS_COUNT_IMPL(;) +#define CHECK_MAX_CHUNKS_COUNT_BOOL() CHECK_MAX_CHUNKS_COUNT_IMPL(false) + +FVoxelRenderOctree::FVoxelRenderOctree(uint8 LOD) + : TSimpleVoxelOctree(LOD) + , Root(this) + , ChunkId(GetId()) + , OctreeBounds(GetBounds()) +{ + check(LOD > 0); + check(ChunkId <= Root->RootIdCounter); + Root->CurrentChunksCount++; + + INC_DWORD_STAT_BY(STAT_VoxelRenderOctreesCount, 1); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderOctreesMemory, sizeof(FVoxelRenderOctree)); +} + +FVoxelRenderOctree::FVoxelRenderOctree(const FVoxelRenderOctree* Source) + : TSimpleVoxelOctree(Source->Height) + , RootIdCounter(Source->RootIdCounter) + , Root(this) + , ChunkId(Source->ChunkId) + , OctreeBounds(GetBounds()) + , UpdateIndex(Source->UpdateIndex) +{ + check(ChunkId <= Root->RootIdCounter); + Root->CurrentChunksCount++; + ChunkSettings = Source->ChunkSettings; + if (Source->HasChildren()) + { + CreateChildren(Source->GetChildren()); + } + + INC_DWORD_STAT_BY(STAT_VoxelRenderOctreesCount, 1); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderOctreesMemory, sizeof(FVoxelRenderOctree)); +} + + +FVoxelRenderOctree::FVoxelRenderOctree(const FVoxelRenderOctree& Parent, uint8 ChildIndex) + : TSimpleVoxelOctree(Parent, ChildIndex) + , Root(Parent.Root) + , ChunkId(GetId()) + , OctreeBounds(GetBounds()) + , UpdateIndex(Parent.UpdateIndex) +{ + check(ChunkId <= Root->RootIdCounter); + Root->CurrentChunksCount++; + + INC_DWORD_STAT_BY(STAT_VoxelRenderOctreesCount, 1); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderOctreesMemory, sizeof(FVoxelRenderOctree)); +} + + +FVoxelRenderOctree::FVoxelRenderOctree(const FVoxelRenderOctree& Parent, uint8 ChildIndex, const ChildrenArray& SourceChildren) + : TSimpleVoxelOctree(Parent, ChildIndex) + , Root(Parent.Root) + , ChunkId(SourceChildren[ChildIndex].ChunkId) + , OctreeBounds(GetBounds()) + , UpdateIndex(Parent.UpdateIndex) +{ + Root->CurrentChunksCount++; + + auto& Source = SourceChildren[ChildIndex]; + ChunkSettings = Source.ChunkSettings; + if (Source.HasChildren()) + { + CreateChildren(Source.GetChildren()); + } + + INC_DWORD_STAT_BY(STAT_VoxelRenderOctreesCount, 1); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderOctreesMemory, sizeof(FVoxelRenderOctree)); +} + +FVoxelRenderOctree::~FVoxelRenderOctree() +{ + Root->CurrentChunksCount--; + DEC_DWORD_STAT_BY(STAT_VoxelRenderOctreesCount, 1); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderOctreesMemory, sizeof(FVoxelRenderOctree)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelRenderOctree::ResetDivisionType() +{ + ChunkSettings.OldDivisionType = ChunkSettings.DivisionType; + ChunkSettings.DivisionType = EDivisionType::Uninitialized; + + if (!!HasChildren()) + { + for (auto& Child : GetChildren()) + { + Child.ResetDivisionType(); + } + } +} + +bool FVoxelRenderOctree::UpdateSubdividedByDistance(const FVoxelRenderOctreeSettings& Settings) +{ + CHECK_MAX_CHUNKS_COUNT_BOOL(); + + if (ShouldSubdivideByDistance(Settings)) + { + ChunkSettings.DivisionType = EDivisionType::ByDistance; + + if (!HasChildren()) + { + CreateChildren(); + } + + bool bChanged = ChunkSettings.OldDivisionType != EDivisionType::ByDistance; + for (auto& Child : GetChildren()) + { + bChanged |= Child.UpdateSubdividedByDistance(Settings); + } + + return bChanged; + } + else + { + return ChunkSettings.OldDivisionType == EDivisionType::ByDistance; + } +} + +bool FVoxelRenderOctree::UpdateSubdividedByNeighbors(const FVoxelRenderOctreeSettings& Settings) +{ + CHECK_MAX_CHUNKS_COUNT_BOOL(); + + bool bShouldContinue = false; + + if (ChunkSettings.DivisionType == EDivisionType::Uninitialized && ShouldSubdivideByNeighbors(Settings)) + { + ChunkSettings.DivisionType = EDivisionType::ByNeighbors; + + if (!HasChildren()) + { + CreateChildren(); + } + + bShouldContinue = true; + } + + if (ChunkSettings.DivisionType != EDivisionType::Uninitialized) + { + for (auto& Child : GetChildren()) + { + bShouldContinue |= Child.UpdateSubdividedByNeighbors(Settings); + } + } + + return bShouldContinue; +} + +void FVoxelRenderOctree::ReuseOldNeighbors() +{ + if (ChunkSettings.OldDivisionType == EDivisionType::ByNeighbors) + { + ChunkSettings.DivisionType = EDivisionType::ByNeighbors; + } + + if (!!HasChildren()) + { + for (auto& Child : GetChildren()) + { + Child.ReuseOldNeighbors(); + } + } +} + +void FVoxelRenderOctree::UpdateSubdividedByOthers(const FVoxelRenderOctreeSettings& Settings) +{ + CHECK_MAX_CHUNKS_COUNT(); + + if (ChunkSettings.DivisionType == EDivisionType::Uninitialized && ShouldSubdivideByOthers(Settings)) + { + ChunkSettings.DivisionType = EDivisionType::ByOthers; + + if (!HasChildren()) + { + CreateChildren(); + } + } + + if (ChunkSettings.DivisionType != EDivisionType::Uninitialized) + { + for (auto& Child : GetChildren()) + { + Child.UpdateSubdividedByOthers(Settings); + } + } +} + +void FVoxelRenderOctree::DeleteChunks(TArray& ChunkUpdates) +{ + CHECK_MAX_CHUNKS_COUNT(); + + if (ChunkSettings.DivisionType == EDivisionType::Uninitialized) + { + if (HasChildren()) + { + for (auto& Child : GetChildren()) + { + ensure(Child.ChunkSettings.DivisionType == EDivisionType::Uninitialized); + + Child.DeleteChunks(ChunkUpdates); + + if (Child.ChunkSettings.Settings.HasRenderChunk()) + { + //ensureVoxelSlowNoSideEffects(!ChunkUpdates.FindByPredicate([&](const FVoxelChunkUpdate& ChunkUpdate) { return ChunkUpdate.Id == Child.ChunkId; })); + ChunkUpdates.Emplace( + FVoxelChunkUpdate + { + Child.ChunkId, + Child.Height, + Child.OctreeBounds, + Child.ChunkSettings.Settings, + {}, + {} + }); + } + } + DestroyChildren(); + } + } + else + { + for (auto& Child : GetChildren()) + { + Child.DeleteChunks(ChunkUpdates); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelRenderOctree::GetUpdates( + uint32 InUpdateIndex, + bool bRecomputeTransitionMasks, + const FVoxelRenderOctreeSettings& Settings, + TArray& ChunkUpdates, + bool bInVisible) +{ + CHECK_MAX_CHUNKS_COUNT(); + + UpdateIndex++; + check(UpdateIndex == InUpdateIndex); + + if (!OctreeBounds.Intersect(Settings.WorldBounds)) + { + return; + } + + FVoxelChunkSettings NewSettings{}; + + // NOTE: we DO want bEnableRender = false to disable VisibleChunks settings + NewSettings.bVisible = Settings.bEnableRender && Height <= Settings.ChunksCullingLOD && bInVisible; + + if (!HasChildren()) + { + check(ChunkSettings.DivisionType == EDivisionType::Uninitialized); + } + else + { + check(ChunkSettings.DivisionType != EDivisionType::Uninitialized); + bool bChildrenVisible; + if (ChunkSettings.DivisionType == EDivisionType::ByDistance || ChunkSettings.DivisionType == EDivisionType::ByNeighbors) + { + // There are visible children + NewSettings.bVisible = false; + bChildrenVisible = true; + } + else + { + check(ChunkSettings.DivisionType == EDivisionType::ByOthers); + bChildrenVisible = false; + } + + for (auto& Child : GetChildren()) + { + Child.GetUpdates(UpdateIndex, bRecomputeTransitionMasks, Settings, ChunkUpdates, bChildrenVisible); + } + } + + NewSettings.bEnableCollisions = + Settings.bEnableCollisions && + ((Height == 0 && + IsInvokerInRange(Settings.Invokers, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.bUseForCollisions; }, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.CollisionsBounds; }) + ) + || + (NewSettings.bVisible && Settings.bComputeVisibleChunksCollisions && Height <= Settings.VisibleChunksCollisionsMaxLOD) + ); + + NewSettings.bEnableNavmesh = + Settings.bEnableNavmesh && + ((Height == 0 && + IsInvokerInRange(Settings.Invokers, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.bUseForNavmesh; }, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.NavmeshBounds; }) + ) + || + (NewSettings.bVisible && Settings.bComputeVisibleChunksNavmesh && Height <= Settings.VisibleChunksNavmeshMaxLOD) + ); + + check(NewSettings.TransitionsMask == 0); + if (NewSettings.HasRenderChunk()) + { + if (NewSettings.bVisible && Settings.bEnableTransitions) + { + if (bRecomputeTransitionMasks) + { + for (int32 DirectionIndex = 0; DirectionIndex < 6; DirectionIndex++) + { + const auto Direction = EVoxelDirectionFlag::Type(1 << DirectionIndex); + const FVoxelRenderOctree* AdjacentChunk = GetVisibleAdjacentChunk(Direction, 0); + if (AdjacentChunk && AdjacentChunk->OctreeBounds.Intersect(Settings.WorldBounds)) + { + check( + (AdjacentChunk->Height == Height - 1) || + (AdjacentChunk->Height == Height) || + (AdjacentChunk->Height == Height + 1) + ); + if (Settings.bInvertTransitions ? (AdjacentChunk->Height > Height) : (AdjacentChunk->Height < Height)) + { + NewSettings.TransitionsMask |= Direction; + } + } + } + } + else + { + NewSettings.TransitionsMask = ChunkSettings.Settings.TransitionsMask; + } + } + } + + if (ChunkSettings.Settings != NewSettings && (ChunkSettings.Settings.HasRenderChunk() || NewSettings.HasRenderChunk())) + { + // Too slow ensureVoxelSlowNoSideEffects(!ChunkUpdates.FindByPredicate([&](const FVoxelChunkUpdate& ChunkUpdate) { return ChunkUpdate.Id == ChunkId; })); + ChunkUpdates.Emplace( + FVoxelChunkUpdate + { + ChunkId, + Height, + OctreeBounds, + ChunkSettings.Settings, + NewSettings, + {} + }); + } + + ChunkSettings.Settings = NewSettings; +} + +void FVoxelRenderOctree::GetChunksToUpdateForBounds(const FVoxelIntBox& Bounds, TArray& ChunksToUpdate, const FVoxelOnChunkUpdate& OnChunkUpdate) const +{ + if (!OctreeBounds.Intersect(Bounds)) + { + return; + } + + if (ChunkSettings.Settings.HasRenderChunk()) + { + OnChunkUpdate.Broadcast(OctreeBounds); + ChunksToUpdate.Add(ChunkId); + } + + if (!!HasChildren()) + { + for (auto& Child : GetChildren()) + { + Child.GetChunksToUpdateForBounds(Bounds, ChunksToUpdate, OnChunkUpdate); + } + } +} + + +void FVoxelRenderOctree::GetVisibleChunksOverlappingBounds(const FVoxelIntBox& Bounds, TArray>& VisibleChunks) const +{ + if (!OctreeBounds.Intersect(Bounds)) + { + return; + } + + if (ChunkSettings.Settings.bVisible) + { + VisibleChunks.Add(ChunkId); + } + + if (!!HasChildren()) + { + for (auto& Child : GetChildren()) + { + Child.GetVisibleChunksOverlappingBounds(Bounds, VisibleChunks); + } + } +} + +FORCEINLINE bool FVoxelRenderOctree::IsCanceled() const +{ + return Root->CurrentChunksCount >= CVarMaxRenderOctreeChunks.GetValueOnAnyThread(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FVoxelRenderOctree::ShouldSubdivideByDistance(const FVoxelRenderOctreeSettings& Settings) const +{ + if (!Settings.bEnableRender) + { + return false; + } + if (Height == 0) + { + return false; + } + if (!OctreeBounds.Intersect(Settings.WorldBounds)) + { + return false; + } + if (Height <= Settings.MinLOD) + { + return false; + } + if (Height > Settings.MaxLOD) + { + return true; + } + + for (auto& Invoker : Settings.Invokers) + { + if (Invoker.bUseForLOD && OctreeBounds.Intersect(Invoker.LODBounds) && Height > Invoker.LODToSet) + { + return true; + } + } + + return false; +} + + +bool FVoxelRenderOctree::ShouldSubdivideByNeighbors(const FVoxelRenderOctreeSettings& Settings) const +{ + if (Height == 0) + { + return false; + } + if (!OctreeBounds.Intersect(Settings.WorldBounds)) + { + return false; + } + for (int32 DirectionIndex = 0; DirectionIndex < 6; DirectionIndex++) + { + const auto Direction = EVoxelDirectionFlag::Type(1 << DirectionIndex); + for (int32 Index = 0; Index < 4; Index++) // Iterate the 4 adjacent subdivided chunks + { + const FVoxelRenderOctree* AdjacentChunk = GetVisibleAdjacentChunk(Direction, Index); + if (!AdjacentChunk) + { + continue; + } + + if (AdjacentChunk->Height + 1 < Height) + { + return true; + } + if (AdjacentChunk->Height >= Height) + { + check(Index == 0); + break; // No need to continue, 4 indices are the same chunk + } + } + } + return false; +} + + +bool FVoxelRenderOctree::ShouldSubdivideByOthers(const FVoxelRenderOctreeSettings& Settings) const +{ + if (!Settings.bEnableCollisions && !Settings.bEnableNavmesh) + { + return false; + } + if (Height == 0) + { + return false; + } + if (!OctreeBounds.Intersect(Settings.WorldBounds)) + { + return false; + } + + if (Settings.bEnableCollisions && IsInvokerInRange(Settings.Invokers, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.bUseForCollisions; }, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.CollisionsBounds; })) + { + return true; + } + if (Settings.bEnableNavmesh && IsInvokerInRange(Settings.Invokers, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.bUseForNavmesh; }, + [](const FVoxelInvokerSettings& Invoker) { return Invoker.NavmeshBounds; })) + { + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +inline bool IsVisibleParent(const FVoxelRenderOctree* Chunk) +{ + return Chunk->ChunkSettings.DivisionType == FVoxelRenderOctree::EDivisionType::ByDistance || Chunk->ChunkSettings.DivisionType == FVoxelRenderOctree::EDivisionType::ByNeighbors; +} + +const FVoxelRenderOctree* FVoxelRenderOctree::GetVisibleAdjacentChunk(EVoxelDirectionFlag::Type Direction, int32 Index) const +{ + const int32 HalfSize = Size() / 2; + const int32 HalfHalfSize = Size() / 4; + + int32 S = HalfSize + HalfHalfSize; // Size / 2: on the border; Size / 4: center of child chunk + int32 X, Y; + if (Index & 0x1) + { + X = -HalfHalfSize; + } + else + { + X = HalfHalfSize; + } + if (Index & 0x2) + { + Y = -HalfHalfSize; + } + else + { + Y = HalfHalfSize; + } + + FIntVector P; + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + P = Position + FIntVector(-S, X, Y); + break; + case EVoxelDirectionFlag::XMax: + P = Position + FIntVector(S, X, Y); + break; + case EVoxelDirectionFlag::YMin: + P = Position + FIntVector(X, -S, Y); + break; + case EVoxelDirectionFlag::YMax: + P = Position + FIntVector(X, S, Y); + break; + case EVoxelDirectionFlag::ZMin: + P = Position + FIntVector(X, Y, -S); + break; + case EVoxelDirectionFlag::ZMax: + P = Position + FIntVector(X, Y, S); + break; + default: + check(false); + P = FIntVector::ZeroValue; + } + + if (Root->OctreeBounds.Contains(P)) + { + const FVoxelRenderOctree* Ptr = Root; + + while (IsVisibleParent(Ptr)) + { + Ptr = &Ptr->GetChild(P); + } + + check(Ptr->OctreeBounds.Contains(P)); + + return Ptr; + } + else + { + return nullptr; + } +} + +template +bool FVoxelRenderOctree::IsInvokerInRange(const TArray& Invokers, T1 SelectInvoker, T2 GetInvokerBounds) const +{ + for (auto& Invoker : Invokers) + { + if (SelectInvoker(Invoker)) + { + if (OctreeBounds.Intersect(GetInvokerBounds(Invoker))) + { + return true; + } + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint64 FVoxelRenderOctree::GetId() +{ + return ++Root->RootIdCounter; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.h new file mode 100644 index 00000000..ad880eb8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.h @@ -0,0 +1,156 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelDirection.h" +#include "VoxelSimpleOctree.h" +#include "VoxelAsyncWork.h" +#include "VoxelInvokerSettings.h" +#include "VoxelRender/VoxelChunkToUpdate.h" + +#include "HAL/ThreadSafeBool.h" + +class FVoxelRenderOctree; +struct FVoxelLODSettings; +class FVoxelDebugManager; + +DECLARE_MULTICAST_DELEGATE_OneParam(FVoxelOnChunkUpdate, FVoxelIntBox); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Render Octrees Memory"), STAT_VoxelRenderOctreesMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct FVoxelRenderOctreeSettings +{ + int32 MinLOD; + int32 MaxLOD; + FVoxelIntBox WorldBounds; + + TArray Invokers; + + int32 ChunksCullingLOD; + + bool bEnableRender; + bool bEnableTransitions; + bool bInvertTransitions; + + bool bEnableCollisions; + bool bComputeVisibleChunksCollisions; + int32 VisibleChunksCollisionsMaxLOD; + + bool bEnableNavmesh; + bool bComputeVisibleChunksNavmesh; + int32 VisibleChunksNavmeshMaxLOD; +}; + +class FVoxelRenderOctreeAsyncBuilder : public FVoxelAsyncWork +{ +public: + TArray ChunkUpdates; + + TVoxelSharedPtr NewOctree; + TVoxelSharedPtr OldOctree; + + // We don't want to do the deletion on the game thread + TVoxelSharedPtr OctreeToDelete; + + FVoxelRenderOctreeAsyncBuilder(uint8 OctreeDepth, const FVoxelIntBox& WorldBounds); + +private: + ~FVoxelRenderOctreeAsyncBuilder() = default; + + template + friend struct TVoxelAsyncWorkDelete; + +public: + void Init(const FVoxelRenderOctreeSettings& OctreeSettings, TVoxelSharedPtr Octree); + void ReportBuildTime(); + +private: + //~ Begin FVoxelAsyncWork Interface + virtual void DoWork() override; + virtual uint32 GetPriority() const override; + //~ End FVoxelAsyncWork Interface + +private: + const uint8 OctreeDepth; + const FVoxelIntBox WorldBounds; + + FVoxelRenderOctreeSettings OctreeSettings{}; + + bool bTooManyChunks = false; + double Counter = 0; + FString Log; + int32 NumberOfChunks = 0; +}; + +class FVoxelRenderOctree : public TSimpleVoxelOctree +{ +private: + // Important: must be the first variable to be initialized, else GetId does the wrong thing for the root! + uint64 RootIdCounter = 0; + +public: + FVoxelRenderOctree* const Root; + const uint64 ChunkId; + const FVoxelIntBox OctreeBounds; + + enum class EDivisionType : uint8 + { + Uninitialized = 0, + ByDistance = 1, + ByNeighbors = 2, + ByOthers = 3 + }; + + struct FChunkSettings + { + FVoxelChunkSettings Settings{}; + EDivisionType DivisionType = EDivisionType::Uninitialized; + EDivisionType OldDivisionType = EDivisionType::Uninitialized; + }; + FChunkSettings ChunkSettings; + int32 CurrentChunksCount = 0; + uint64 UpdateIndex = 0; + + inline const FVoxelChunkSettings& GetSettings() const { return ChunkSettings.Settings; } + + FVoxelRenderOctree(uint8 LOD); + FVoxelRenderOctree(const FVoxelRenderOctree* Source); + + FVoxelRenderOctree(const FVoxelRenderOctree& Parent, uint8 ChildIndex); + FVoxelRenderOctree(const FVoxelRenderOctree& Parent, uint8 ChildIndex, const ChildrenArray& SourceChildren); + + ~FVoxelRenderOctree(); + + void ResetDivisionType(); + bool UpdateSubdividedByDistance(const FVoxelRenderOctreeSettings& Settings); + bool UpdateSubdividedByNeighbors(const FVoxelRenderOctreeSettings& Settings); + void ReuseOldNeighbors(); + void UpdateSubdividedByOthers(const FVoxelRenderOctreeSettings& Settings); + void DeleteChunks(TArray& ChunkUpdates); + + void GetUpdates( + uint32 InUpdateIndex, + bool bRecomputeTransitionMasks, + const FVoxelRenderOctreeSettings& Settings, + TArray& ChunkUpdates, + bool bVisible = true); + + void GetChunksToUpdateForBounds(const FVoxelIntBox& Bounds, TArray& ChunksToUpdate, const FVoxelOnChunkUpdate& OnChunkUpdate) const; + void GetVisibleChunksOverlappingBounds(const FVoxelIntBox& Bounds, TArray>& VisibleChunks) const; + + bool IsCanceled() const; + +private: + bool ShouldSubdivideByDistance(const FVoxelRenderOctreeSettings& Settings) const; + bool ShouldSubdivideByNeighbors(const FVoxelRenderOctreeSettings& Settings) const; + bool ShouldSubdivideByOthers(const FVoxelRenderOctreeSettings& Settings) const; + + const FVoxelRenderOctree* GetVisibleAdjacentChunk(EVoxelDirectionFlag::Type Direction, int32 Index) const; + + template + bool IsInvokerInRange(const TArray& Invokers, T1 SelectInvoker, T2 GetInvokerBounds) const; + + uint64 GetId(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.cpp new file mode 100644 index 00000000..fd34796f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.cpp @@ -0,0 +1,91 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h" +#include "VoxelRender/VoxelMaterialIndices.h" +#include "VoxelUniqueError.h" +#include "VoxelMessages.h" + +#include "Materials/Material.h" + +int32 UVoxelBasicMaterialCollection::GetMaxMaterialIndices() const +{ + return 1; +} + +UMaterialInterface* UVoxelBasicMaterialCollection::GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const +{ + ensure(Indices.NumIndices == 1); + const int32 Index = Indices.SortedIndices[0]; + + UMaterialInterface* MaterialInterface = nullptr; + if (auto* Layer = Layers.FindByKey(Index)) + { + MaterialInterface = Layer->LayerMaterial; + } + + if (!MaterialInterface) + { + static TVoxelUniqueError UniqueError; + FVoxelMessages::CondError( + UniqueError(UniqueIdForErrors, Index), + FString::Printf(TEXT("Missing material for index %d"), Index), + this); + } + + return MaterialInterface; +} + +UMaterialInterface* UVoxelBasicMaterialCollection::GetIndexMaterial(uint8 Index) const +{ + for (const auto& Layer : Layers) + { + if (Layer.LayerIndex == Index) + { + return Layer.LayerMaterial; + } + } + return nullptr; +} + +TArray UVoxelBasicMaterialCollection::GetMaterials() const +{ + TArray Result; + for (const auto& Layer : Layers) + { + Result.Add(FMaterialInfo{ Layer.LayerIndex, FName(), Layer.LayerMaterial }); + } + return Result; +} + +int32 UVoxelBasicMaterialCollection::GetMaterialIndex(FName Name) const +{ + for (auto& Layer : Layers) + { + if (Layer.LayerMaterial && Layer.LayerMaterial->GetFName() == Name) + { + return Layer.LayerIndex; + } + } + return -1; +} + +#if WITH_EDITOR +void UVoxelBasicMaterialCollection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + TSet Indices; + for (auto& Layer : Layers) + { + bool bIsAlreadyInSet = true; + while (bIsAlreadyInSet) + { + Indices.Add(Layer.LayerIndex, &bIsAlreadyInSet); + if (bIsAlreadyInSet) Layer.LayerIndex++; + } + } + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.cpp new file mode 100644 index 00000000..6639262b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.cpp @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h" + +UMaterialInterface* UVoxelCachedMaterialCollection::GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const +{ + const FVoxelMaterialIndices Key{ Indices }; + auto*& Material = CachedMaterials.FindOrAdd(Key); + if (!Material) + { + // Note: if this errors out and return nullptr, we want to call it again next time + Material = GetVoxelMaterial_NotCached(Indices, UniqueIdForErrors); + } + return Material; +} + +void UVoxelCachedMaterialCollection::InitializeCollection() +{ + // Make sure to apply changes + CachedMaterials.Empty(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.cpp new file mode 100644 index 00000000..6bc52837 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.cpp @@ -0,0 +1,372 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" +#include "VoxelUniqueError.h" +#include "VoxelMessages.h" + +#include "Materials/MaterialInstanceDynamic.h" +#include "Materials/MaterialInstanceConstant.h" + +#if WITH_EDITOR +void UVoxelInstancedMaterialCollectionTemplates::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty) + { + if (Template) + { + const auto CreateTemplate = [&](int32 Num) + { + auto* MaterialInstance = NewObject(this); + MaterialInstance->SetParentEditorOnly(Template); + + FStaticParameterSet StaticParameters; + MaterialInstance->GetStaticParameterValues(StaticParameters); + + const auto SetStaticParameter = [&](FName Name, bool bValue) + { + bool bFound = false; + for (auto& StaticSwitchParameter : StaticParameters.StaticSwitchParameters) + { + if (StaticSwitchParameter.ParameterInfo.Name == Name) + { + // Note: we allow duplicates + bFound = true; + StaticSwitchParameter.bOverride = true; + StaticSwitchParameter.Value = bValue; + } + } + if (!bFound) + { + FVoxelMessages::Warning("Static parameter " + Name.ToString() + " not found in " + Template->GetName(), this); + } + }; + + SetStaticParameter("Num Blends > 1", Num > 1); + SetStaticParameter("Num Blends > 2", Num > 2); + SetStaticParameter("Num Blends > 3", Num > 3); + SetStaticParameter("Num Blends > 4", Num > 4); + SetStaticParameter("Num Blends > 5", Num > 5); + + MaterialInstance->UpdateStaticPermutation(StaticParameters); + MaterialInstance->PostEditChange(); + + return MaterialInstance; + }; + + Template1x = CreateTemplate(1); + Template2x = CreateTemplate(2); + Template3x = CreateTemplate(3); + Template4x = CreateTemplate(4); + Template5x = CreateTemplate(5); + Template6x = CreateTemplate(6); + } + else + { + Template1x = nullptr; + Template2x = nullptr; + Template3x = nullptr; + Template4x = nullptr; + Template5x = nullptr; + Template6x = nullptr; + } + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelInstancedMaterialCollection::UVoxelInstancedMaterialCollection() +{ + Redirects.Add(" Sides" ); + Redirects.Add(" Bottom" ); +} + +int32 UVoxelInstancedMaterialCollection::GetMaxMaterialIndices() const +{ + if (MaxMaterialsToBlendAtOnce < 1 || MaxMaterialsToBlendAtOnce > 6) + { + FVoxelMessages::Error("MaxMaterialsToBlendAtOnce should be between 1 and 6", this); + return 1; + } + + if (!Templates) + { + FVoxelMessages::Error("Missing Templates!", this); + return 1; + } + + if (!Templates->Template1x) + { + FVoxelMessages::Error("Template 1x is null!", this); + } + if (MaxMaterialsToBlendAtOnce > 1 && !Templates->Template2x) + { + FVoxelMessages::Error("Template 2x is null!", this); + } + if (MaxMaterialsToBlendAtOnce > 2 && !Templates->Template3x) + { + FVoxelMessages::Error("Template 3x is null!", this); + } + if (MaxMaterialsToBlendAtOnce > 3 && !Templates->Template4x) + { + FVoxelMessages::Error("Template 4x is null!", this); + } + if (MaxMaterialsToBlendAtOnce > 4 && !Templates->Template5x) + { + FVoxelMessages::Error("Template 5x is null!", this); + } + if (MaxMaterialsToBlendAtOnce > 5 && !Templates->Template6x) + { + FVoxelMessages::Error("Template 6x is null!", this); + } + + TSet Indices; + for (auto& Layer : Layers) + { + bool bIsAlreadyInSet; + Indices.Add(Layer.LayerIndex, &bIsAlreadyInSet); + + if (bIsAlreadyInSet) + { + FVoxelMessages::Error(FString::Printf(TEXT("Index %d is used multiple times!"), Layer.LayerIndex), this); + } + } + + return MaxMaterialsToBlendAtOnce; +} + +int32 UVoxelInstancedMaterialCollection::GetMaterialIndex(FName Name) const +{ + for (auto& Layer : Layers) + { + if (Layer.LayerMaterialInstance && Layer.LayerMaterialInstance->GetFName() == Name) + { + return Layer.LayerIndex; + } + } + return -1; +} + +TArray UVoxelInstancedMaterialCollection::GetMaterials() const +{ + TArray Result; + for (const auto& Layer : Layers) + { + Result.Add(FMaterialInfo{ Layer.LayerIndex, FName(), Layer.LayerMaterialInstance }); + } + return Result; +} + +UMaterialInterface* UVoxelInstancedMaterialCollection::GetIndexMaterial(uint8 Index) const +{ + for (const auto& Layer : Layers) + { + if (Layer.LayerIndex == Index) + { + return Layer.LayerMaterialInstance; + } + } + return nullptr; +} + +UMaterialInterface* UVoxelInstancedMaterialCollection::GetVoxelMaterial_NotCached(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const +{ + VOXEL_FUNCTION_COUNTER(); + + // Will happen if the material collection is changed at runtime to a one with a different MaxMaterialsToBlendAtOnce + if (Indices.NumIndices > MaxMaterialsToBlendAtOnce || !ensure(MaxMaterialsToBlendAtOnce > 0)) + { + return nullptr; + } + + for (int32 Index = 0; Index < Indices.NumIndices; Index++) + { + const int32 SortedIndex = Indices.SortedIndices[Index]; + auto* Layer = Layers.FindByKey(SortedIndex); + if (!Layer || !Layer->LayerMaterialInstance) + { + static TVoxelUniqueError UniqueError; + FVoxelMessages::CondError( + UniqueError(UniqueIdForErrors, Index), + FString::Printf(TEXT("No layer with index %d, or layer has no material instance!"), SortedIndex), + this); + return nullptr; + } + } + + if (!Templates) + { + return nullptr; + } + + const auto GetTemplate = [&]() + { + switch (Indices.NumIndices) + { + default: ensure(false); + case 1: return Templates->Template1x; + case 2: return Templates->Template2x; + case 3: return Templates->Template3x; + case 4: return Templates->Template4x; + case 5: return Templates->Template5x; + case 6: return Templates->Template6x; + } + }; + + UMaterialInterface* const Template = GetTemplate(); + if (!Template) + { + return nullptr; + } + + auto* DynamicInstance = UMaterialInstanceDynamic::Create(Template, nullptr); + if (!ensure(DynamicInstance)) return nullptr; + + const auto SetParameters = [&](UMaterialInstance* Instance, const FString& Prefix) + { + const auto GetNewName = [&](FName Name, const FString& Suffix) + { + return FName(*(Prefix + Name.ToString() + Suffix)); + }; + + TSet OverridenParameterNames; + for (const auto& Parameter : Instance->ScalarParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, {}); + DynamicInstance->SetScalarParameterValue(Name, Parameter.ParameterValue); + OverridenParameterNames.Add(Name); + } + for (const auto& Parameter : Instance->VectorParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, {}); + DynamicInstance->SetVectorParameterValue(Name, Parameter.ParameterValue); + OverridenParameterNames.Add(Name); + } + for (const auto& Parameter : Instance->TextureParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, {}); + DynamicInstance->SetTextureParameterValue(Name, Parameter.ParameterValue); + OverridenParameterNames.Add(Name); + } + for (const auto& Parameter : Instance->FontParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, {}); + DynamicInstance->SetFontParameterValue(Name, Parameter.FontValue, Parameter.FontPage); + OverridenParameterNames.Add(Name); + } + + for (const FString& Redirect : Redirects) + { + for (const auto& Parameter : Instance->ScalarParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, Redirect); + if (!OverridenParameterNames.Contains(Name)) + { + DynamicInstance->SetScalarParameterValue(Name, Parameter.ParameterValue); + } + } + for (const auto& Parameter : Instance->VectorParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, Redirect); + if (!OverridenParameterNames.Contains(Name)) + { + DynamicInstance->SetVectorParameterValue(Name, Parameter.ParameterValue); + } + } + for (const auto& Parameter : Instance->TextureParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, Redirect); + if (!OverridenParameterNames.Contains(Name)) + { + DynamicInstance->SetTextureParameterValue(Name, Parameter.ParameterValue); + } + } + for (const auto& Parameter : Instance->FontParameterValues) + { + const FName Name = GetNewName(Parameter.ParameterInfo.Name, Redirect); + if (!OverridenParameterNames.Contains(Name)) + { + DynamicInstance->SetFontParameterValue(Name, Parameter.FontValue, Parameter.FontPage); + } + } + } + }; + + for (int32 Index = 0; Index < Indices.NumIndices; Index++) + { + auto* Instance = Layers.FindByKey(Indices.SortedIndices[Index])->LayerMaterialInstance; + SetParameters(Instance, ParametersPrefix + FString::Printf(TEXT("%d:"), Index)); + } + + return DynamicInstance; +} + +#if WITH_EDITOR +void UVoxelInstancedMaterialCollection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + TSet Indices; + for (auto& Layer : Layers) + { + bool bIsAlreadyInSet = true; + while (bIsAlreadyInSet) + { + Indices.Add(Layer.LayerIndex, &bIsAlreadyInSet); + if (bIsAlreadyInSet) Layer.LayerIndex++; + } + } + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelInstancedMaterialCollectionInstance::InitializeCollection() +{ + Super::InitializeCollection(); + + if (LayersSource) + { + Layers = LayersSource->Layers; + } +} + +void UVoxelInstancedMaterialCollectionInstance::PostLoad() +{ + Super::PostLoad(); + + if (LayersSource) + { + Layers = LayersSource->Layers; + } +} + +#if WITH_EDITOR +void UVoxelInstancedMaterialCollectionInstance::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (LayersSource) + { + Layers = LayersSource->Layers; + } +} + +bool UVoxelInstancedMaterialCollectionInstance::CanEditChange(const FProperty* InProperty) const +{ + if (InProperty && InProperty->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelInstancedMaterialCollectionInstance, Layers)) + { + return false; + } + + return Super::CanEditChange(InProperty); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp new file mode 100644 index 00000000..8fd7b9ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp @@ -0,0 +1,340 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h" +#include "VoxelRender/VoxelMaterialIndices.h" +#include "VoxelRender/VoxelMaterialExpressions.h" +#include "VoxelMessages.h" +#include "VoxelEditorDelegates.h" + +#include "Materials/Material.h" +#include "Materials/MaterialInstanceConstant.h" + +UMaterialInterface* UVoxelLandscapeMaterialCollection::GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const +{ + if (!Material) + { + return nullptr; + } + + FVoxelLandscapeMaterialCollectionPermutation Permutation; + for (int32 Index = 0; Index < Indices.NumIndices; Index++) + { + Permutation.Names[Index] = IndicesToLayers.FindRef(Indices.SortedIndices[Index]).Name; + } + + return FindOrAddPermutation(Permutation); +} + +UMaterialInterface* UVoxelLandscapeMaterialCollection::GetIndexMaterial(uint8 Index) const +{ + for (auto& Layer : Layers) + { + if (Layer.Index == Index) + { + FVoxelLandscapeMaterialCollectionPermutation Permutation; + Permutation.Names[0] = Layer.Name; + return FindOrAddPermutation(Permutation); + } + } + return nullptr; +} + +TArray UVoxelLandscapeMaterialCollection::GetMaterials() const +{ + TArray Result; + for (auto& Layer : Layers) + { + FVoxelLandscapeMaterialCollectionPermutation Permutation; + Permutation.Names[0] = Layer.Name; + Result.Add(FMaterialInfo{ Layer.Index,Layer.Name, FindOrAddPermutation(Permutation) }); + } + return Result; +} + +int32 UVoxelLandscapeMaterialCollection::GetMaterialIndex(FName Name) const +{ + for (auto& Layer : Layers) + { + if (Layer.Name == Name) + { + return Layer.Index; + } + } + return -1; +} + +void UVoxelLandscapeMaterialCollection::InitializeCollection() +{ + IndicesToLayers.Reset(); + for (auto& Layer : Layers) + { + IndicesToLayers.Add(Layer.Index, Layer); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelLandscapeMaterialCollection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) + { + return; + } + + if (!Material) + { + Layers.Empty(); + MaterialCache.Empty(); + return; + } + + CleanupCache(); + + if (NeedsToBeConvertedToVoxel()) + { + FVoxelMessages::FNotification Notification; + Notification.UniqueId = OBJECT_LINE_ID(); + Notification.Message = FString::Printf(TEXT("%s is a landscape only material: it needs to be converted to work with both voxel and landscapes"), *Material->GetName()); + + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Fix Now"; + Button.Tooltip = "Fix the material"; + Button.OnClick = FSimpleDelegate::CreateWeakLambda(Material, [Material = Material, This = MakeWeakObjectPtr(this)]() + { + FVoxelEditorDelegates::FixVoxelLandscapeMaterial.Broadcast(Material->GetMaterial()); + + if (This.IsValid()) + { + This->PostEditChange(); + This->InitializeCollection(); + } + }); + + FVoxelMessages::ShowNotification(Notification); + return; + } + + FixupLayers(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UMaterialInstanceConstant* UVoxelLandscapeMaterialCollection::FindOrAddPermutation(const FVoxelLandscapeMaterialCollectionPermutation& Permutation) const +{ + auto* CachedMaterial = MaterialCache.FindRef(Permutation); + if (CachedMaterial && CachedMaterial->Parent == Material) + { + return CachedMaterial; + } + +#if WITH_EDITOR + return CreateInstanceForPermutation(Permutation); +#else + return nullptr; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +UMaterialInstanceConstant* UVoxelLandscapeMaterialCollection::CreateInstanceForPermutation(const FVoxelLandscapeMaterialCollectionPermutation& Permutation) const +{ + if (!GIsEditor) + { + // Standalone + return nullptr; + } + if (!ensure(Material)) + { + return nullptr; + } + if (NeedsToBeConvertedToVoxel()) + { + return nullptr; + } + + // Make sure the static permutations are always correct + const_cast(this)->FixupLayers(); + + FlushRenderingCommands(); + + UMaterialInstanceConstant* Instance = NewObject(GetOuter()); + + LOG_VOXEL(Log, TEXT("Looking for key '%s', making new combination %s"), *Permutation.ToString(), *Instance->GetName()); + MaterialCache.Add(Permutation, Instance); + // Make sure we're saving the new instance + MarkPackageDirty(); + + Instance->SetParentEditorOnly(Material, false); + + FStaticParameterSet StaticParameters; + { + struct FInfoAndGuid + { + FMaterialParameterInfo Info; + FGuid Guid; + }; + TMap NameToInfo; + ForeachMaterialParameter([&](const FMaterialParameterInfo& Info, const FGuid& Guid) + { + ensure(!NameToInfo.Contains(Info.Name)); + NameToInfo.Add(Info.Name, { Info, Guid }); + }); + + TSet AddedLayers; + for (int32 Index = 0; Index < 6; Index++) + { + const auto Name = Permutation.Names[Index]; + if (Name.IsNone() || !ensure(NameToInfo.Contains(Name))) + { + continue; + } + AddedLayers.Add(Name); + + FStaticTerrainLayerWeightParameter Parameter; + Parameter.ParameterInfo = NameToInfo[Name].Info; + Parameter.bOverride = true; + Parameter.ExpressionGUID = NameToInfo[Name].Guid; + // Pass the layer to use to the voxel expression + // Add 1 000 000 to detect voxel vs landscape indices + Parameter.WeightmapIndex = 1000000 + Index; + StaticParameters.TerrainLayerWeightParameters.Add(Parameter); + } + + for (auto& Layer : Layers) + { + if (AddedLayers.Contains(Layer.Name)) + { + continue; + } + if (!ensure(NameToInfo.Contains(Layer.Name))) + { + continue; + } + + FStaticTerrainLayerWeightParameter Parameter; + Parameter.ParameterInfo = NameToInfo[Layer.Name].Info; + Parameter.bOverride = true; + Parameter.ExpressionGUID = NameToInfo[Layer.Name].Guid; + // 1 000 006 is used to set Default + Parameter.WeightmapIndex = 1000006; + StaticParameters.TerrainLayerWeightParameters.Add(Parameter); + } + } + + Instance->UpdateStaticPermutation(StaticParameters); + Instance->PostEditChange(); + + return Instance; +} + +void UVoxelLandscapeMaterialCollection::ForeachMaterialParameter(TFunctionRef Lambda) const +{ + if (!ensure(Material)) + { + return; + } + + UMaterial* ActualMaterial = Material->GetMaterial(); + if (!ensure(ActualMaterial)) + { + return; + } + + if (!ensure(!NeedsToBeConvertedToVoxel())) + { + return; + } + + TArray ParameterInfos; + TArray Guids; + + ActualMaterial->GetAllParameterInfo(ParameterInfos, Guids); + ActualMaterial->GetAllParameterInfo(ParameterInfos, Guids); + ActualMaterial->GetAllParameterInfo(ParameterInfos, Guids); + ActualMaterial->GetAllParameterInfo(ParameterInfos, Guids); + // Note: don't query landscape visibility parameter name, it just returns __LANDSCAPE_VISIBILITY__ + + if (!ensure(ParameterInfos.Num() == Guids.Num())) + { + return; + } + + for (int32 Index = 0; Index < ParameterInfos.Num(); Index++) + { + Lambda(ParameterInfos[Index], Guids[Index]); + } +} + +bool UVoxelLandscapeMaterialCollection::NeedsToBeConvertedToVoxel() const +{ + if (!ensure(Material)) + { + return false; + } + + UMaterial* ActualMaterial = Material->GetMaterial(); + if (!ensure(ActualMaterial)) + { + return false; + } + + return FVoxelMaterialExpressionUtilities::NeedsToBeConvertedToVoxel(ActualMaterial->Expressions); +} + +void UVoxelLandscapeMaterialCollection::FixupLayers() +{ + VOXEL_FUNCTION_COUNTER(); + + TSet UsedIndices; + TMap ExistingIndices; + + const auto GetUniqueIndex = [&](uint8 Index) + { + bool bIsAlreadyInSet = true; + while (bIsAlreadyInSet) + { + UsedIndices.Add(Index, &bIsAlreadyInSet); + if (bIsAlreadyInSet) Index++; + } + return Index; + }; + + for (auto& Layer : Layers) + { + ExistingIndices.Add(Layer.Name, GetUniqueIndex(Layer.Index)); + } + + Layers.Reset(); + ForeachMaterialParameter([&](const FMaterialParameterInfo& Info, const FGuid& Guid) + { + auto* ExistingIndex = ExistingIndices.Find(Info.Name); + const uint8 Index = ExistingIndex ? *ExistingIndex : GetUniqueIndex(0); + + Layers.Add(FVoxelLandscapeMaterialCollectionLayer{ Info.Name, Index }); + }); +} + +void UVoxelLandscapeMaterialCollection::CleanupCache() const +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto It = MaterialCache.CreateIterator(); It; ++It) + { + if (It.Value() && It.Value()->Parent != Material) + { + It.RemoveCurrent(); + } + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/Transvoxel.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/Transvoxel.h new file mode 100644 index 00000000..6debd3a3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/Transvoxel.h @@ -0,0 +1,1039 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +//================================================================================ +// +// The Transvoxel Algorithm look-up tables +// +// Copyright 2009 by Eric Lengyel +// +// The following data originates from Eric Lengyel's Transvoxel Algorithm. +// http://transvoxel.org/ +// +// The data in this file may be freely used in implementations of the Transvoxel +// Algorithm. If you do use this data, or any transformation of it, in your own +// projects, commercial or otherwise, please give credit by indicating in your +// source code that the data is part of the author's implementation of the +// Transvoxel Algorithm and that it came from the web address given above. +// (Simply copying and pasting the two lines of the previous paragraph would be +// perfect.) If you distribute a commercial product with source code included, +// then the credit in the source code is required. +// +// If you distribute any kind of product that uses this data, a credit visible to +// the end-user would be appreciated, but it is not required. However, you may +// not claim that the entire implementation of the Transvoxel Algorithm is your +// own if you use the data in this file or any transformation of it. +// +// The format of the data in this file is described in the dissertation "Voxel- +// Based TerrainObject for Real-Time Virtual Simulations", available at the web page +// given above. References to sections and figures below pertain to that paper. +// +// The contents of this file are protected by copyright and may not be publicly +// reproduced without permission. +// +//================================================================================ + +namespace Transvoxel +{ + + // The RegularCellData structure holds information about the triangulation + // used for a single equivalence class in the modified Marching Cubes algorithm, + // described in Section 3.2. + + struct RegularCellData + { + unsigned char geometryCounts; // High nibble is vertex count, low nibble is triangle count. + unsigned char vertexIndex[15]; // Groups of 3 indexes giving the triangulation. + + long GetVertexCount(void) const + { + return (geometryCounts >> 4); + } + + long GetTriangleCount(void) const + { + return (geometryCounts & 0x0F); + } + }; + + + // The TransitionCellData structure holds information about the triangulation + // used for a single equivalence class in the Transvoxel Algorithm transition cell, + // described in Section 4.3. + + struct TransitionCellData + { + long geometryCounts; // High nibble is vertex count, low nibble is triangle count. + unsigned char vertexIndex[36]; // Groups of 3 indexes giving the triangulation. + + long GetVertexCount(void) const + { + return (geometryCounts >> 4); + } + + long GetTriangleCount(void) const + { + return (geometryCounts & 0x0F); + } + }; + + + // The regularCellClass table maps an 8-bit regular Marching Cubes case index to + // an equivalence class index. Even though there are 18 equivalence classes in our + // modified Marching Cubes algorithm, a couple of them use the same exact triangulations, + // just with different vertex locations. We combined those classes for this table so + // that the class index ranges from 0 to 15. + + const unsigned char regularCellClass[256] = + { + 0x00, 0x01, 0x01, 0x03, 0x01, 0x03, 0x02, 0x04, 0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, + 0x01, 0x03, 0x02, 0x04, 0x02, 0x04, 0x06, 0x0C, 0x02, 0x05, 0x05, 0x0B, 0x05, 0x0A, 0x07, 0x04, + 0x01, 0x02, 0x03, 0x04, 0x02, 0x05, 0x05, 0x0A, 0x02, 0x06, 0x04, 0x0C, 0x05, 0x07, 0x0B, 0x04, + 0x03, 0x04, 0x04, 0x03, 0x05, 0x0B, 0x07, 0x04, 0x05, 0x07, 0x0A, 0x04, 0x08, 0x0E, 0x0E, 0x03, + 0x01, 0x02, 0x02, 0x05, 0x03, 0x04, 0x05, 0x0B, 0x02, 0x06, 0x05, 0x07, 0x04, 0x0C, 0x0A, 0x04, + 0x03, 0x04, 0x05, 0x0A, 0x04, 0x03, 0x07, 0x04, 0x05, 0x07, 0x08, 0x0E, 0x0B, 0x04, 0x0E, 0x03, + 0x02, 0x06, 0x05, 0x07, 0x05, 0x07, 0x08, 0x0E, 0x06, 0x09, 0x07, 0x0F, 0x07, 0x0F, 0x0E, 0x0D, + 0x04, 0x0C, 0x0B, 0x04, 0x0A, 0x04, 0x0E, 0x03, 0x07, 0x0F, 0x0E, 0x0D, 0x0E, 0x0D, 0x02, 0x01, + 0x01, 0x02, 0x02, 0x05, 0x02, 0x05, 0x06, 0x07, 0x03, 0x05, 0x04, 0x0A, 0x04, 0x0B, 0x0C, 0x04, + 0x02, 0x05, 0x06, 0x07, 0x06, 0x07, 0x09, 0x0F, 0x05, 0x08, 0x07, 0x0E, 0x07, 0x0E, 0x0F, 0x0D, + 0x03, 0x05, 0x04, 0x0B, 0x05, 0x08, 0x07, 0x0E, 0x04, 0x07, 0x03, 0x04, 0x0A, 0x0E, 0x04, 0x03, + 0x04, 0x0A, 0x0C, 0x04, 0x07, 0x0E, 0x0F, 0x0D, 0x0B, 0x0E, 0x04, 0x03, 0x0E, 0x02, 0x0D, 0x01, + 0x03, 0x05, 0x05, 0x08, 0x04, 0x0A, 0x07, 0x0E, 0x04, 0x07, 0x0B, 0x0E, 0x03, 0x04, 0x04, 0x03, + 0x04, 0x0B, 0x07, 0x0E, 0x0C, 0x04, 0x0F, 0x0D, 0x0A, 0x0E, 0x0E, 0x02, 0x04, 0x03, 0x0D, 0x01, + 0x04, 0x07, 0x0A, 0x0E, 0x0B, 0x0E, 0x0E, 0x02, 0x0C, 0x0F, 0x04, 0x0D, 0x04, 0x0D, 0x03, 0x01, + 0x03, 0x04, 0x04, 0x03, 0x04, 0x03, 0x0D, 0x01, 0x04, 0x0D, 0x03, 0x01, 0x03, 0x01, 0x01, 0x00 + }; + + + // The regularCellData table holds the triangulation data for all 16 distinct classes to + // which a case can be mapped by the regularCellClass table. + + const RegularCellData regularCellData[16] = + { + {0x00, {}}, + {0x31, {0, 1, 2}}, + {0x62, {0, 1, 2, 3, 4, 5}}, + {0x42, {0, 1, 2, 0, 2, 3}}, + {0x53, {0, 1, 4, 1, 3, 4, 1, 2, 3}}, + {0x73, {0, 1, 2, 0, 2, 3, 4, 5, 6}}, + {0x93, {0, 1, 2, 3, 4, 5, 6, 7, 8}}, + {0x84, {0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 7}}, + {0x84, {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7}}, + {0xC4, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + {0x64, {0, 4, 5, 0, 1, 4, 1, 3, 4, 1, 2, 3}}, + {0x64, {0, 5, 4, 0, 4, 1, 1, 4, 3, 1, 3, 2}}, + {0x64, {0, 4, 5, 0, 3, 4, 0, 1, 3, 1, 2, 3}}, + {0x64, {0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5}}, + {0x75, {0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6}}, + {0x95, {0, 4, 5, 0, 3, 4, 0, 1, 3, 1, 2, 3, 6, 7, 8}} + }; + + + // The regularVertexData table gives the vertex locations for every one of the 256 possible + // cases in the modified Marching Cubes algorithm. Each 16-bit value also provides information + // about whether a vertex can be reused from a neighboring cell. See Section 3.3 for details. + // The low byte contains the indexes for the two endpoints of the edge on which the vertex lies, + // as numbered in Figure 3.7. The high byte contains the vertex reuse data shown in Figure 3.8. + + const unsigned short regularVertexData[256][12] = + { + {}, + {0x6201, 0x5102, 0x3304}, + {0x6201, 0x2315, 0x4113}, + {0x5102, 0x3304, 0x2315, 0x4113}, + {0x5102, 0x4223, 0x1326}, + {0x3304, 0x6201, 0x4223, 0x1326}, + {0x6201, 0x2315, 0x4113, 0x5102, 0x4223, 0x1326}, + {0x4223, 0x1326, 0x3304, 0x2315, 0x4113}, + {0x4113, 0x8337, 0x4223}, + {0x6201, 0x5102, 0x3304, 0x4223, 0x4113, 0x8337}, + {0x6201, 0x2315, 0x8337, 0x4223}, + {0x5102, 0x3304, 0x2315, 0x8337, 0x4223}, + {0x5102, 0x4113, 0x8337, 0x1326}, + {0x4113, 0x8337, 0x1326, 0x3304, 0x6201}, + {0x6201, 0x2315, 0x8337, 0x1326, 0x5102}, + {0x3304, 0x2315, 0x8337, 0x1326}, + {0x3304, 0x1146, 0x2245}, + {0x6201, 0x5102, 0x1146, 0x2245}, + {0x6201, 0x2315, 0x4113, 0x3304, 0x1146, 0x2245}, + {0x2315, 0x4113, 0x5102, 0x1146, 0x2245}, + {0x5102, 0x4223, 0x1326, 0x3304, 0x1146, 0x2245}, + {0x1146, 0x2245, 0x6201, 0x4223, 0x1326}, + {0x3304, 0x1146, 0x2245, 0x6201, 0x2315, 0x4113, 0x5102, 0x4223, 0x1326}, + {0x4223, 0x1326, 0x1146, 0x2245, 0x2315, 0x4113}, + {0x4223, 0x4113, 0x8337, 0x3304, 0x1146, 0x2245}, + {0x6201, 0x5102, 0x1146, 0x2245, 0x4223, 0x4113, 0x8337}, + {0x4223, 0x6201, 0x2315, 0x8337, 0x3304, 0x1146, 0x2245}, + {0x4223, 0x8337, 0x2315, 0x2245, 0x1146, 0x5102}, + {0x5102, 0x4113, 0x8337, 0x1326, 0x3304, 0x1146, 0x2245}, + {0x4113, 0x8337, 0x1326, 0x1146, 0x2245, 0x6201}, + {0x6201, 0x2315, 0x8337, 0x1326, 0x5102, 0x3304, 0x1146, 0x2245}, + {0x2245, 0x2315, 0x8337, 0x1326, 0x1146}, + {0x2315, 0x2245, 0x8157}, + {0x6201, 0x5102, 0x3304, 0x2315, 0x2245, 0x8157}, + {0x4113, 0x6201, 0x2245, 0x8157}, + {0x2245, 0x8157, 0x4113, 0x5102, 0x3304}, + {0x5102, 0x4223, 0x1326, 0x2315, 0x2245, 0x8157}, + {0x6201, 0x4223, 0x1326, 0x3304, 0x2315, 0x2245, 0x8157}, + {0x6201, 0x2245, 0x8157, 0x4113, 0x5102, 0x4223, 0x1326}, + {0x4223, 0x1326, 0x3304, 0x2245, 0x8157, 0x4113}, + {0x4223, 0x4113, 0x8337, 0x2315, 0x2245, 0x8157}, + {0x6201, 0x5102, 0x3304, 0x4223, 0x4113, 0x8337, 0x2315, 0x2245, 0x8157}, + {0x8337, 0x4223, 0x6201, 0x2245, 0x8157}, + {0x5102, 0x3304, 0x2245, 0x8157, 0x8337, 0x4223}, + {0x5102, 0x4113, 0x8337, 0x1326, 0x2315, 0x2245, 0x8157}, + {0x4113, 0x8337, 0x1326, 0x3304, 0x6201, 0x2315, 0x2245, 0x8157}, + {0x5102, 0x1326, 0x8337, 0x8157, 0x2245, 0x6201}, + {0x8157, 0x8337, 0x1326, 0x3304, 0x2245}, + {0x2315, 0x3304, 0x1146, 0x8157}, + {0x6201, 0x5102, 0x1146, 0x8157, 0x2315}, + {0x3304, 0x1146, 0x8157, 0x4113, 0x6201}, + {0x4113, 0x5102, 0x1146, 0x8157}, + {0x2315, 0x3304, 0x1146, 0x8157, 0x5102, 0x4223, 0x1326}, + {0x1326, 0x4223, 0x6201, 0x2315, 0x8157, 0x1146}, + {0x3304, 0x1146, 0x8157, 0x4113, 0x6201, 0x5102, 0x4223, 0x1326}, + {0x1326, 0x1146, 0x8157, 0x4113, 0x4223}, + {0x2315, 0x3304, 0x1146, 0x8157, 0x4223, 0x4113, 0x8337}, + {0x6201, 0x5102, 0x1146, 0x8157, 0x2315, 0x4223, 0x4113, 0x8337}, + {0x3304, 0x1146, 0x8157, 0x8337, 0x4223, 0x6201}, + {0x4223, 0x5102, 0x1146, 0x8157, 0x8337}, + {0x2315, 0x3304, 0x1146, 0x8157, 0x5102, 0x4113, 0x8337, 0x1326}, + {0x6201, 0x4113, 0x8337, 0x1326, 0x1146, 0x8157, 0x2315}, + {0x6201, 0x3304, 0x1146, 0x8157, 0x8337, 0x1326, 0x5102}, + {0x1326, 0x1146, 0x8157, 0x8337}, + {0x1326, 0x8267, 0x1146}, + {0x6201, 0x5102, 0x3304, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x2315, 0x4113, 0x1326, 0x8267, 0x1146}, + {0x5102, 0x3304, 0x2315, 0x4113, 0x1326, 0x8267, 0x1146}, + {0x5102, 0x4223, 0x8267, 0x1146}, + {0x3304, 0x6201, 0x4223, 0x8267, 0x1146}, + {0x5102, 0x4223, 0x8267, 0x1146, 0x6201, 0x2315, 0x4113}, + {0x1146, 0x8267, 0x4223, 0x4113, 0x2315, 0x3304}, + {0x4113, 0x8337, 0x4223, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x5102, 0x3304, 0x4223, 0x4113, 0x8337, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x2315, 0x8337, 0x4223, 0x1326, 0x8267, 0x1146}, + {0x5102, 0x3304, 0x2315, 0x8337, 0x4223, 0x1326, 0x8267, 0x1146}, + {0x8267, 0x1146, 0x5102, 0x4113, 0x8337}, + {0x6201, 0x4113, 0x8337, 0x8267, 0x1146, 0x3304}, + {0x6201, 0x2315, 0x8337, 0x8267, 0x1146, 0x5102}, + {0x1146, 0x3304, 0x2315, 0x8337, 0x8267}, + {0x3304, 0x1326, 0x8267, 0x2245}, + {0x1326, 0x8267, 0x2245, 0x6201, 0x5102}, + {0x3304, 0x1326, 0x8267, 0x2245, 0x6201, 0x2315, 0x4113}, + {0x1326, 0x8267, 0x2245, 0x2315, 0x4113, 0x5102}, + {0x5102, 0x4223, 0x8267, 0x2245, 0x3304}, + {0x6201, 0x4223, 0x8267, 0x2245}, + {0x5102, 0x4223, 0x8267, 0x2245, 0x3304, 0x6201, 0x2315, 0x4113}, + {0x4113, 0x4223, 0x8267, 0x2245, 0x2315}, + {0x3304, 0x1326, 0x8267, 0x2245, 0x4223, 0x4113, 0x8337}, + {0x1326, 0x8267, 0x2245, 0x6201, 0x5102, 0x4223, 0x4113, 0x8337}, + {0x3304, 0x1326, 0x8267, 0x2245, 0x4223, 0x6201, 0x2315, 0x8337}, + {0x5102, 0x1326, 0x8267, 0x2245, 0x2315, 0x8337, 0x4223}, + {0x3304, 0x2245, 0x8267, 0x8337, 0x4113, 0x5102}, + {0x8337, 0x8267, 0x2245, 0x6201, 0x4113}, + {0x5102, 0x6201, 0x2315, 0x8337, 0x8267, 0x2245, 0x3304}, + {0x2315, 0x8337, 0x8267, 0x2245}, + {0x2315, 0x2245, 0x8157, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x5102, 0x3304, 0x2315, 0x2245, 0x8157, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x2245, 0x8157, 0x4113, 0x1326, 0x8267, 0x1146}, + {0x2245, 0x8157, 0x4113, 0x5102, 0x3304, 0x1326, 0x8267, 0x1146}, + {0x4223, 0x8267, 0x1146, 0x5102, 0x2315, 0x2245, 0x8157}, + {0x3304, 0x6201, 0x4223, 0x8267, 0x1146, 0x2315, 0x2245, 0x8157}, + {0x4223, 0x8267, 0x1146, 0x5102, 0x6201, 0x2245, 0x8157, 0x4113}, + {0x3304, 0x2245, 0x8157, 0x4113, 0x4223, 0x8267, 0x1146}, + {0x4223, 0x4113, 0x8337, 0x2315, 0x2245, 0x8157, 0x1326, 0x8267, 0x1146}, + {0x6201, 0x5102, 0x3304, 0x4223, 0x4113, 0x8337, 0x2315, 0x2245, 0x8157, 0x1326, 0x8267, 0x1146}, + {0x8337, 0x4223, 0x6201, 0x2245, 0x8157, 0x1326, 0x8267, 0x1146}, + {0x4223, 0x5102, 0x3304, 0x2245, 0x8157, 0x8337, 0x1326, 0x8267, 0x1146}, + {0x8267, 0x1146, 0x5102, 0x4113, 0x8337, 0x2315, 0x2245, 0x8157}, + {0x6201, 0x4113, 0x8337, 0x8267, 0x1146, 0x3304, 0x2315, 0x2245, 0x8157}, + {0x8337, 0x8267, 0x1146, 0x5102, 0x6201, 0x2245, 0x8157}, + {0x3304, 0x2245, 0x8157, 0x8337, 0x8267, 0x1146}, + {0x8157, 0x2315, 0x3304, 0x1326, 0x8267}, + {0x8267, 0x8157, 0x2315, 0x6201, 0x5102, 0x1326}, + {0x8267, 0x1326, 0x3304, 0x6201, 0x4113, 0x8157}, + {0x8267, 0x8157, 0x4113, 0x5102, 0x1326}, + {0x5102, 0x4223, 0x8267, 0x8157, 0x2315, 0x3304}, + {0x2315, 0x6201, 0x4223, 0x8267, 0x8157}, + {0x3304, 0x5102, 0x4223, 0x8267, 0x8157, 0x4113, 0x6201}, + {0x4113, 0x4223, 0x8267, 0x8157}, + {0x8157, 0x2315, 0x3304, 0x1326, 0x8267, 0x4223, 0x4113, 0x8337}, + {0x8157, 0x2315, 0x6201, 0x5102, 0x1326, 0x8267, 0x4223, 0x4113, 0x8337}, + {0x8157, 0x8337, 0x4223, 0x6201, 0x3304, 0x1326, 0x8267}, + {0x5102, 0x1326, 0x8267, 0x8157, 0x8337, 0x4223}, + {0x8267, 0x8157, 0x2315, 0x3304, 0x5102, 0x4113, 0x8337}, + {0x6201, 0x4113, 0x8337, 0x8267, 0x8157, 0x2315}, + {0x6201, 0x3304, 0x5102, 0x8337, 0x8267, 0x8157}, + {0x8337, 0x8267, 0x8157}, + {0x8337, 0x8157, 0x8267}, + {0x6201, 0x5102, 0x3304, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x2315, 0x4113, 0x8337, 0x8157, 0x8267}, + {0x5102, 0x3304, 0x2315, 0x4113, 0x8337, 0x8157, 0x8267}, + {0x5102, 0x4223, 0x1326, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x4223, 0x1326, 0x3304, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x2315, 0x4113, 0x5102, 0x4223, 0x1326, 0x8337, 0x8157, 0x8267}, + {0x4223, 0x1326, 0x3304, 0x2315, 0x4113, 0x8337, 0x8157, 0x8267}, + {0x4113, 0x8157, 0x8267, 0x4223}, + {0x4223, 0x4113, 0x8157, 0x8267, 0x6201, 0x5102, 0x3304}, + {0x8157, 0x8267, 0x4223, 0x6201, 0x2315}, + {0x3304, 0x2315, 0x8157, 0x8267, 0x4223, 0x5102}, + {0x1326, 0x5102, 0x4113, 0x8157, 0x8267}, + {0x8157, 0x4113, 0x6201, 0x3304, 0x1326, 0x8267}, + {0x1326, 0x5102, 0x6201, 0x2315, 0x8157, 0x8267}, + {0x8267, 0x1326, 0x3304, 0x2315, 0x8157}, + {0x3304, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x5102, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x2315, 0x4113, 0x3304, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x2315, 0x4113, 0x5102, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x5102, 0x4223, 0x1326, 0x3304, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x1146, 0x2245, 0x6201, 0x4223, 0x1326, 0x8337, 0x8157, 0x8267}, + {0x6201, 0x2315, 0x4113, 0x5102, 0x4223, 0x1326, 0x3304, 0x1146, 0x2245, 0x8337, 0x8157, 0x8267}, + {0x4113, 0x4223, 0x1326, 0x1146, 0x2245, 0x2315, 0x8337, 0x8157, 0x8267}, + {0x4223, 0x4113, 0x8157, 0x8267, 0x3304, 0x1146, 0x2245}, + {0x6201, 0x5102, 0x1146, 0x2245, 0x4223, 0x4113, 0x8157, 0x8267}, + {0x8157, 0x8267, 0x4223, 0x6201, 0x2315, 0x3304, 0x1146, 0x2245}, + {0x2315, 0x8157, 0x8267, 0x4223, 0x5102, 0x1146, 0x2245}, + {0x1326, 0x5102, 0x4113, 0x8157, 0x8267, 0x3304, 0x1146, 0x2245}, + {0x1326, 0x1146, 0x2245, 0x6201, 0x4113, 0x8157, 0x8267}, + {0x5102, 0x6201, 0x2315, 0x8157, 0x8267, 0x1326, 0x3304, 0x1146, 0x2245}, + {0x1326, 0x1146, 0x2245, 0x2315, 0x8157, 0x8267}, + {0x2315, 0x2245, 0x8267, 0x8337}, + {0x2315, 0x2245, 0x8267, 0x8337, 0x6201, 0x5102, 0x3304}, + {0x4113, 0x6201, 0x2245, 0x8267, 0x8337}, + {0x5102, 0x4113, 0x8337, 0x8267, 0x2245, 0x3304}, + {0x2315, 0x2245, 0x8267, 0x8337, 0x5102, 0x4223, 0x1326}, + {0x6201, 0x4223, 0x1326, 0x3304, 0x8337, 0x2315, 0x2245, 0x8267}, + {0x4113, 0x6201, 0x2245, 0x8267, 0x8337, 0x5102, 0x4223, 0x1326}, + {0x4113, 0x4223, 0x1326, 0x3304, 0x2245, 0x8267, 0x8337}, + {0x2315, 0x2245, 0x8267, 0x4223, 0x4113}, + {0x2315, 0x2245, 0x8267, 0x4223, 0x4113, 0x6201, 0x5102, 0x3304}, + {0x6201, 0x2245, 0x8267, 0x4223}, + {0x3304, 0x2245, 0x8267, 0x4223, 0x5102}, + {0x5102, 0x4113, 0x2315, 0x2245, 0x8267, 0x1326}, + {0x4113, 0x2315, 0x2245, 0x8267, 0x1326, 0x3304, 0x6201}, + {0x5102, 0x6201, 0x2245, 0x8267, 0x1326}, + {0x3304, 0x2245, 0x8267, 0x1326}, + {0x8267, 0x8337, 0x2315, 0x3304, 0x1146}, + {0x5102, 0x1146, 0x8267, 0x8337, 0x2315, 0x6201}, + {0x3304, 0x1146, 0x8267, 0x8337, 0x4113, 0x6201}, + {0x8337, 0x4113, 0x5102, 0x1146, 0x8267}, + {0x8267, 0x8337, 0x2315, 0x3304, 0x1146, 0x5102, 0x4223, 0x1326}, + {0x1146, 0x8267, 0x8337, 0x2315, 0x6201, 0x4223, 0x1326}, + {0x8267, 0x8337, 0x4113, 0x6201, 0x3304, 0x1146, 0x5102, 0x4223, 0x1326}, + {0x4113, 0x4223, 0x1326, 0x1146, 0x8267, 0x8337}, + {0x3304, 0x2315, 0x4113, 0x4223, 0x8267, 0x1146}, + {0x2315, 0x6201, 0x5102, 0x1146, 0x8267, 0x4223, 0x4113}, + {0x1146, 0x8267, 0x4223, 0x6201, 0x3304}, + {0x5102, 0x1146, 0x8267, 0x4223}, + {0x8267, 0x1326, 0x5102, 0x4113, 0x2315, 0x3304, 0x1146}, + {0x6201, 0x4113, 0x2315, 0x1326, 0x1146, 0x8267}, + {0x6201, 0x3304, 0x1146, 0x8267, 0x1326, 0x5102}, + {0x1326, 0x1146, 0x8267}, + {0x1326, 0x8337, 0x8157, 0x1146}, + {0x8337, 0x8157, 0x1146, 0x1326, 0x6201, 0x5102, 0x3304}, + {0x8337, 0x8157, 0x1146, 0x1326, 0x6201, 0x2315, 0x4113}, + {0x4113, 0x5102, 0x3304, 0x2315, 0x1326, 0x8337, 0x8157, 0x1146}, + {0x8337, 0x8157, 0x1146, 0x5102, 0x4223}, + {0x6201, 0x4223, 0x8337, 0x8157, 0x1146, 0x3304}, + {0x8337, 0x8157, 0x1146, 0x5102, 0x4223, 0x6201, 0x2315, 0x4113}, + {0x4223, 0x8337, 0x8157, 0x1146, 0x3304, 0x2315, 0x4113}, + {0x4223, 0x4113, 0x8157, 0x1146, 0x1326}, + {0x4223, 0x4113, 0x8157, 0x1146, 0x1326, 0x6201, 0x5102, 0x3304}, + {0x1146, 0x8157, 0x2315, 0x6201, 0x4223, 0x1326}, + {0x4223, 0x5102, 0x3304, 0x2315, 0x8157, 0x1146, 0x1326}, + {0x4113, 0x8157, 0x1146, 0x5102}, + {0x6201, 0x4113, 0x8157, 0x1146, 0x3304}, + {0x2315, 0x8157, 0x1146, 0x5102, 0x6201}, + {0x2315, 0x8157, 0x1146, 0x3304}, + {0x2245, 0x3304, 0x1326, 0x8337, 0x8157}, + {0x6201, 0x2245, 0x8157, 0x8337, 0x1326, 0x5102}, + {0x2245, 0x3304, 0x1326, 0x8337, 0x8157, 0x6201, 0x2315, 0x4113}, + {0x2245, 0x2315, 0x4113, 0x5102, 0x1326, 0x8337, 0x8157}, + {0x4223, 0x8337, 0x8157, 0x2245, 0x3304, 0x5102}, + {0x8157, 0x2245, 0x6201, 0x4223, 0x8337}, + {0x2245, 0x3304, 0x5102, 0x4223, 0x8337, 0x8157, 0x4113, 0x6201, 0x2315}, + {0x4223, 0x8337, 0x8157, 0x2245, 0x2315, 0x4113}, + {0x4113, 0x8157, 0x2245, 0x3304, 0x1326, 0x4223}, + {0x1326, 0x4223, 0x4113, 0x8157, 0x2245, 0x6201, 0x5102}, + {0x8157, 0x2245, 0x3304, 0x1326, 0x4223, 0x6201, 0x2315}, + {0x5102, 0x1326, 0x4223, 0x2315, 0x8157, 0x2245}, + {0x3304, 0x5102, 0x4113, 0x8157, 0x2245}, + {0x4113, 0x8157, 0x2245, 0x6201}, + {0x5102, 0x6201, 0x2315, 0x8157, 0x2245, 0x3304}, + {0x2315, 0x8157, 0x2245}, + {0x1146, 0x1326, 0x8337, 0x2315, 0x2245}, + {0x1146, 0x1326, 0x8337, 0x2315, 0x2245, 0x6201, 0x5102, 0x3304}, + {0x6201, 0x2245, 0x1146, 0x1326, 0x8337, 0x4113}, + {0x2245, 0x1146, 0x1326, 0x8337, 0x4113, 0x5102, 0x3304}, + {0x5102, 0x1146, 0x2245, 0x2315, 0x8337, 0x4223}, + {0x1146, 0x3304, 0x6201, 0x4223, 0x8337, 0x2315, 0x2245}, + {0x8337, 0x4113, 0x6201, 0x2245, 0x1146, 0x5102, 0x4223}, + {0x4223, 0x8337, 0x4113, 0x3304, 0x2245, 0x1146}, + {0x4113, 0x2315, 0x2245, 0x1146, 0x1326, 0x4223}, + {0x1146, 0x1326, 0x4223, 0x4113, 0x2315, 0x2245, 0x6201, 0x5102, 0x3304}, + {0x1326, 0x4223, 0x6201, 0x2245, 0x1146}, + {0x4223, 0x5102, 0x3304, 0x2245, 0x1146, 0x1326}, + {0x2245, 0x1146, 0x5102, 0x4113, 0x2315}, + {0x4113, 0x2315, 0x2245, 0x1146, 0x3304, 0x6201}, + {0x6201, 0x2245, 0x1146, 0x5102}, + {0x3304, 0x2245, 0x1146}, + {0x3304, 0x1326, 0x8337, 0x2315}, + {0x5102, 0x1326, 0x8337, 0x2315, 0x6201}, + {0x6201, 0x3304, 0x1326, 0x8337, 0x4113}, + {0x5102, 0x1326, 0x8337, 0x4113}, + {0x4223, 0x8337, 0x2315, 0x3304, 0x5102}, + {0x6201, 0x4223, 0x8337, 0x2315}, + {0x3304, 0x5102, 0x4223, 0x8337, 0x4113, 0x6201}, + {0x4113, 0x4223, 0x8337}, + {0x4113, 0x2315, 0x3304, 0x1326, 0x4223}, + {0x1326, 0x4223, 0x4113, 0x2315, 0x6201, 0x5102}, + {0x3304, 0x1326, 0x4223, 0x6201}, + {0x5102, 0x1326, 0x4223}, + {0x5102, 0x4113, 0x2315, 0x3304}, + {0x6201, 0x4113, 0x2315}, + {0x6201, 0x3304, 0x5102}, + {} + }; + + + // The transitionCellClass table maps a 9-bit transition cell case index to an equivalence + // class index. Even though there are 73 equivalence classes in the Transvoxel Algorithm, + // several of them use the same exact triangulations, just with different vertex locations. + // We combined those classes for this table so that the class index ranges from 0 to 55. + // The high bit is set in the cases for which the inverse state of the voxel data maps to + // the equivalence class, meaning that the winding order of each triangle should be reversed. + + const unsigned char transitionCellClass[512] = + { + 0x00, 0x01, 0x02, 0x84, 0x01, 0x05, 0x04, 0x04, 0x02, 0x87, 0x09, 0x8C, 0x84, 0x0B, 0x05, 0x05, + 0x01, 0x08, 0x07, 0x8D, 0x05, 0x0F, 0x8B, 0x0B, 0x04, 0x0D, 0x0C, 0x1C, 0x04, 0x8B, 0x85, 0x85, + 0x02, 0x07, 0x09, 0x8C, 0x87, 0x10, 0x0C, 0x0C, 0x09, 0x12, 0x15, 0x9A, 0x8C, 0x19, 0x90, 0x10, + 0x84, 0x8D, 0x8C, 0x9C, 0x0B, 0x9D, 0x0F, 0x0F, 0x05, 0x1B, 0x10, 0xAC, 0x05, 0x0F, 0x8B, 0x0B, + 0x01, 0x05, 0x87, 0x0B, 0x08, 0x0F, 0x0D, 0x8B, 0x07, 0x10, 0x12, 0x19, 0x8D, 0x9D, 0x1B, 0x0F, + 0x05, 0x0F, 0x10, 0x9D, 0x0F, 0x1E, 0x1D, 0xA1, 0x8B, 0x1D, 0x99, 0x32, 0x0B, 0xA1, 0x8F, 0x94, + 0x04, 0x8B, 0x0C, 0x0F, 0x0D, 0x1D, 0x1C, 0x8F, 0x0C, 0x99, 0x1A, 0x31, 0x1C, 0x32, 0x2C, 0xA7, + 0x04, 0x0B, 0x0C, 0x0F, 0x8B, 0xA1, 0x8F, 0x96, 0x85, 0x8F, 0x90, 0x27, 0x85, 0x94, 0x8B, 0x8A, + 0x02, 0x04, 0x09, 0x05, 0x07, 0x8B, 0x0C, 0x85, 0x09, 0x0C, 0x15, 0x90, 0x8C, 0x0F, 0x10, 0x8B, + 0x87, 0x0D, 0x12, 0x1B, 0x10, 0x1D, 0x99, 0x8F, 0x0C, 0x1C, 0x1A, 0x2C, 0x0C, 0x8F, 0x90, 0x8B, + 0x09, 0x0C, 0x15, 0x10, 0x12, 0x99, 0x1A, 0x90, 0x15, 0x1A, 0x23, 0x30, 0x9A, 0x31, 0x30, 0x19, + 0x8C, 0x1C, 0x9A, 0xAC, 0x19, 0x32, 0x31, 0x27, 0x90, 0x2C, 0x30, 0x29, 0x10, 0xA7, 0x19, 0x24, + 0x84, 0x04, 0x8C, 0x05, 0x8D, 0x0B, 0x1C, 0x85, 0x8C, 0x0C, 0x9A, 0x10, 0x9C, 0x0F, 0xAC, 0x0B, + 0x0B, 0x8B, 0x19, 0x0F, 0x9D, 0xA1, 0x32, 0x94, 0x0F, 0x8F, 0x31, 0xA7, 0x0F, 0x96, 0x27, 0x8A, + 0x05, 0x85, 0x90, 0x8B, 0x1B, 0x8F, 0x2C, 0x8B, 0x10, 0x90, 0x30, 0x19, 0xAC, 0x27, 0x29, 0x24, + 0x05, 0x85, 0x10, 0x0B, 0x0F, 0x94, 0xA7, 0x8A, 0x8B, 0x8B, 0x19, 0x24, 0x0B, 0x8A, 0x24, 0x83, + 0x03, 0x06, 0x0A, 0x8B, 0x06, 0x0E, 0x0B, 0x0B, 0x0A, 0x91, 0x14, 0x8F, 0x8B, 0x17, 0x05, 0x85, + 0x06, 0x13, 0x11, 0x98, 0x0E, 0x1F, 0x97, 0x2B, 0x0B, 0x18, 0x0F, 0x36, 0x0B, 0xAB, 0x05, 0x85, + 0x0A, 0x11, 0x16, 0x8F, 0x91, 0x20, 0x0F, 0x8F, 0x14, 0x22, 0x21, 0x1D, 0x8F, 0x2D, 0x0B, 0x8B, + 0x8B, 0x98, 0x8F, 0xB7, 0x17, 0xAE, 0x8C, 0x0C, 0x05, 0x2F, 0x8B, 0xB5, 0x85, 0xA6, 0x84, 0x04, + 0x06, 0x0E, 0x91, 0x17, 0x13, 0x1F, 0x18, 0xAB, 0x11, 0x20, 0x22, 0x2D, 0x98, 0xAE, 0x2F, 0xA6, + 0x0E, 0x1F, 0x20, 0xAE, 0x1F, 0x33, 0x2E, 0x2A, 0x97, 0x2E, 0xAD, 0x28, 0x2B, 0x2A, 0x26, 0x25, + 0x0B, 0x97, 0x0F, 0x8C, 0x18, 0x2E, 0x37, 0x8C, 0x0F, 0xAD, 0x9D, 0x90, 0x36, 0x28, 0x35, 0x07, + 0x0B, 0x2B, 0x8F, 0x0C, 0xAB, 0x2A, 0x8C, 0x89, 0x05, 0x26, 0x0B, 0x87, 0x85, 0x25, 0x84, 0x82, + 0x0A, 0x0B, 0x14, 0x05, 0x11, 0x97, 0x0F, 0x05, 0x16, 0x0F, 0x21, 0x0B, 0x8F, 0x8C, 0x8B, 0x84, + 0x91, 0x18, 0x22, 0x2F, 0x20, 0x2E, 0xAD, 0x26, 0x0F, 0x37, 0x9D, 0x35, 0x8F, 0x8C, 0x0B, 0x84, + 0x14, 0x0F, 0x21, 0x8B, 0x22, 0xAD, 0x9D, 0x0B, 0x21, 0x9D, 0x9E, 0x8F, 0x1D, 0x90, 0x8F, 0x85, + 0x8F, 0x36, 0x1D, 0xB5, 0x2D, 0x28, 0x90, 0x87, 0x0B, 0x35, 0x8F, 0x34, 0x8B, 0x07, 0x85, 0x81, + 0x8B, 0x0B, 0x8F, 0x85, 0x98, 0x2B, 0x36, 0x85, 0x8F, 0x8F, 0x1D, 0x8B, 0xB7, 0x0C, 0xB5, 0x04, + 0x17, 0xAB, 0x2D, 0xA6, 0xAE, 0x2A, 0x28, 0x25, 0x8C, 0x8C, 0x90, 0x07, 0x0C, 0x89, 0x87, 0x82, + 0x05, 0x05, 0x0B, 0x84, 0x2F, 0x26, 0x35, 0x84, 0x8B, 0x0B, 0x8F, 0x85, 0xB5, 0x87, 0x34, 0x81, + 0x85, 0x85, 0x8B, 0x04, 0xA6, 0x25, 0x07, 0x82, 0x84, 0x84, 0x85, 0x81, 0x04, 0x82, 0x81, 0x80 + }; + + + // The transitionCellData table holds the triangulation data for all 56 distinct classes to + // which a case can be mapped by the transitionCellClass table. The class index should be ANDed + // with 0x7F before using it to look up triangulation data in this table. + + const TransitionCellData transitionCellData[56] = + { + {0x00, {}}, + {0x42, {0, 1, 3, 1, 2, 3}}, + {0x31, {0, 1, 2}}, + {0x42, {0, 1, 2, 0, 2, 3}}, + {0x53, {0, 1, 4, 1, 3, 4, 1, 2, 3}}, + {0x64, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4}}, + {0x84, {0, 1, 3, 1, 2, 3, 4, 5, 6, 4, 6, 7}}, + {0x73, {0, 1, 3, 1, 2, 3, 4, 5, 6}}, + {0x84, {0, 1, 3, 1, 2, 3, 4, 5, 7, 5, 6, 7}}, + {0x62, {0, 1, 2, 3, 4, 5}}, + {0x53, {0, 1, 3, 0, 3, 4, 1, 2, 3}}, + {0x75, {0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5}}, + {0x84, {0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 7}}, + {0x95, {0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 8, 6, 7, 8}}, + {0xA6, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8, 6, 8, 9}}, + {0x86, {0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6}}, + {0x95, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8}}, + {0x95, {0, 1, 3, 1, 2, 3, 4, 5, 7, 4, 7, 8, 5, 6, 7}}, + {0xA4, {0, 1, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {0xC6, {0, 1, 3, 1, 2, 3, 4, 5, 7, 5, 6, 7, 8, 9, 10, 8, 10, 11}}, + {0x64, {0, 1, 3, 1, 2, 3, 0, 3, 4, 0, 4, 5}}, + {0x93, {0, 1, 2, 3, 4, 5, 6, 7, 8}}, + {0x64, {0, 1, 4, 0, 4, 5, 1, 3, 4, 1, 2, 3}}, + {0x97, {0, 1, 8, 1, 7, 8, 1, 2, 7, 2, 3, 7, 3, 4, 7, 4, 5, 7, 5, 6, 7}}, + {0xB7, {0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 7, 8, 10, 8, 9, 10}}, + {0xA6, {0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 7, 8, 9}}, + {0xB5, {0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 7, 8, 9, 10}}, + {0xA6, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 9, 7, 8, 9}}, + {0xA6, {0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 9, 6, 8, 9, 6, 7, 8}}, + {0x97, {0, 1, 8, 1, 2, 8, 2, 3, 8, 3, 7, 8, 3, 4, 7, 4, 5, 7, 5, 6, 7}}, + {0x86, {0, 1, 7, 1, 6, 7, 1, 2, 6, 2, 5, 6, 2, 4, 5, 2, 3, 4}}, + {0xC8, {0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6, 8, 9, 10, 8, 10, 11}}, + {0xB7, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 9, 10, 6, 7, 9, 7, 8, 9}}, + {0x75, {0, 1, 6, 1, 3, 6, 1, 2, 3, 3, 4, 6, 4, 5, 6}}, + {0xA6, {0, 1, 3, 1, 2, 3, 4, 5, 9, 5, 8, 9, 5, 6, 8, 6, 7, 8}}, + {0xC4, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + {0x86, {1, 2, 4, 2, 3, 4, 0, 1, 7, 1, 4, 7, 4, 6, 7, 4, 5, 6}}, + {0x64, {0, 4, 5, 0, 1, 4, 1, 3, 4, 1, 2, 3}}, + {0x86, {0, 1, 4, 1, 3, 4, 1, 2, 3, 0, 4, 7, 4, 6, 7, 4, 5, 6}}, + {0x97, {1, 2, 3, 1, 3, 4, 1, 4, 5, 0, 1, 8, 1, 5, 8, 5, 7, 8, 5, 6, 7}}, + {0xA6, {0, 1, 3, 1, 2, 3, 4, 5, 9, 5, 8, 9, 5, 6, 8, 6, 7, 8}}, + {0xC8, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 11, 7, 10, 11, 7, 8, 10, 8, 9, 10}}, + {0x97, {0, 1, 8, 1, 2, 8, 2, 7, 8, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6}}, + {0x97, {0, 1, 4, 1, 3, 4, 1, 2, 3, 0, 4, 8, 4, 7, 8, 4, 5, 7, 5, 6, 7}}, + {0xB7, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 10, 7, 9, 10, 7, 8, 9}}, + {0xA8, {0, 1, 9, 1, 2, 9, 2, 8, 9, 2, 3, 8, 3, 7, 8, 3, 4, 7, 4, 6, 7, 4, 5, 6}}, + {0xB9, {0, 1, 7, 1, 6, 7, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 0, 7, 10, 7, 9, 10, 7, 8, 9}}, + {0xA6, {0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 6, 7, 9, 7, 8, 9}}, + {0xC6, {0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8, 9, 10, 11}}, + {0xB7, {0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6, 8, 9, 10}}, + {0xA8, {1, 2, 3, 1, 3, 4, 1, 4, 6, 4, 5, 6, 0, 1, 9, 1, 6, 9, 6, 8, 9, 6, 7, 8}}, + {0xCC, {0, 1, 9, 1, 8, 9, 1, 2, 8, 2, 11, 8, 2, 3, 11, 3, 4, 11, 4, 5, 11, 5, 10, 11, 5, 6, 10, 6, 9, 10, 6, 7, 9, 7, 0, 9}}, + {0x86, {0, 1, 2, 0, 2, 3, 0, 6, 7, 0, 3, 6, 1, 4, 5, 1, 5, 2}}, + {0x97, {0, 1, 4, 1, 3, 4, 1, 2, 3, 2, 5, 6, 2, 6, 3, 0, 7, 8, 0, 4, 7}}, + {0xA8, {0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 3, 6, 7, 3, 7, 4, 0, 8, 9, 0, 5, 8}}, + {0xA8, {0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 2, 6, 3, 3, 6, 7, 0, 8, 9, 0, 5, 8}} + }; + + + // The transitionCornerData table contains the transition cell corner reuse data + // shown in Figure 4.18. + + const unsigned char transitionCornerData[13] = + { + 0x30, 0x21, 0x20, 0x12, 0x40, 0x82, 0x10, 0x81, 0x80, 0x37, 0x27, 0x17, 0x87 + }; + + + // The transitionVertexData table gives the vertex locations for every one of the 512 possible + // cases in the Tranvoxel Algorithm. Each 16-bit value also provides information about whether + // a vertex can be reused from a neighboring cell. See Section 4.5 for details. The low byte + // contains the indexes for the two endpoints of the edge on which the vertex lies, as numbered + // in Figure 4.16. The high byte contains the vertex reuse data shown in Figure 4.17. + + const unsigned short transitionVertexData[512][12] = + { + {}, + {0x2301, 0x1503, 0x199B, 0x289A}, + {0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x8658, 0x4445}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2412, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x8367, 0x4647}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x8478, 0x8367, 0x4647}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445, 0x8367, 0x8478, 0x4647}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x8478, 0x8367, 0x1636}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4445, 0x8478, 0x8367, 0x1636, 0x1503, 0x2301}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8478}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8658, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x1636, 0x1503, 0x2301, 0x2412, 0x8525}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x1636, 0x1503, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B}, + {0x1636, 0x1503, 0x4334, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647, 0x1503, 0x1636, 0x4334}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8367, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334, 0x2412, 0x2301, 0x4514}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1503, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x8525, 0x8658, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x8525}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x2301}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x4514, 0x4445, 0x4647, 0x4334}, + {0x4514, 0x4445, 0x4647, 0x4334}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8525, 0x4514, 0x4334, 0x4647, 0x8658}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B, 0x8658, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x2301, 0x4334, 0x4647, 0x4445, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x2412, 0x8525, 0x199B, 0x289A, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8478, 0x4445, 0x4514, 0x4334, 0x8367}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x8525}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8367, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x8525, 0x8658, 0x8478}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x1503, 0x4334, 0x8367, 0x289A, 0x199B, 0x88BC, 0x89AC}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8525, 0x2412, 0x88BC, 0x89AC, 0x289A, 0x199B}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2301, 0x4334, 0x4647, 0x4445, 0x2412}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8658, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A, 0x8367, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x199B}, + {0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x4514, 0x4334, 0x4647, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x4647, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x1503}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x4514, 0x4334, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x89AC}, + {0x1636, 0x4334, 0x2301, 0x8525, 0x4445, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x4334, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514}, + {0x1636, 0x4334, 0x2301, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x1503, 0x4334, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x289A}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x2412}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x1636, 0x1503, 0x4334}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x1636}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x1636, 0x4647, 0x4445, 0x4514, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x1636, 0x1503, 0x2301, 0x2412, 0x8525}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1636, 0x4647, 0x4445, 0x4514, 0x1503}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2412, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x88BC}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x4445, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x88BC}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8478, 0x4647, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x4647, 0x1636, 0x2412, 0x8525, 0x199B, 0x289A, 0x89AC, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8478}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x289A}, + {0x2412, 0x4445, 0x8478, 0x8367, 0x1636, 0x1503, 0x2301}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1503, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x4514, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x8478, 0x8367, 0x1636}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x2412, 0x1636, 0x8367, 0x289A, 0x199B, 0x88BC, 0x89AC}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x2412, 0x8525, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC, 0x2412, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x1503, 0x2301, 0x88BC, 0x199B, 0x289A, 0x89AC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x8367, 0x4647, 0x8658, 0x199B, 0x88BC, 0x89AC, 0x289A}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x8367, 0x4647, 0x8658, 0x2301, 0x1503, 0x89AC, 0x289A, 0x199B, 0x88BC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x8367}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x8478, 0x8367, 0x4647}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x89AC}, + {0x8525, 0x4445, 0x8478, 0x1503, 0x2301, 0x88BC, 0x199B, 0x289A, 0x89AC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x8478, 0x8658, 0x199B, 0x88BC, 0x89AC, 0x289A}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x2301, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x8658}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x8525, 0x8658, 0x4445}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x2412, 0x289A, 0x89AC}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x199B, 0x289A}, + {} + }; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.cpp new file mode 100644 index 00000000..686b783b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.cpp @@ -0,0 +1,607 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/Meshers/VoxelCubicMesher.h" +#include "VoxelRender/Meshers/VoxelMesherUtilities.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" + +struct FVoxelCubicFullVertex : FVoxelMesherVertex +{ + static constexpr bool bComputeNormal = true; + static constexpr bool bComputeTangent = true; + static constexpr bool bComputeTextureCoordinate = true; + static constexpr bool bComputeMaterial = true; + + FORCEINLINE void SetPosition(const FVector& InPosition) + { + Position = InPosition; + } + FORCEINLINE void SetNormal(const FVector& InNormal) + { + Normal = InNormal; + } + FORCEINLINE void SetTangent(const FVoxelProcMeshTangent& InTangent) + { + Tangent = InTangent; + } + FORCEINLINE void SetTextureCoordinate(const FVector2D& InTextureCoordinate) + { + TextureCoordinate = InTextureCoordinate; + } + FORCEINLINE void SetMaterial(const FVoxelMaterial& InMaterial) + { + Material = InMaterial; + } +}; +static_assert(sizeof(FVoxelCubicFullVertex) == sizeof(FVoxelMesherVertex), ""); + +struct FVoxelCubicGeometryVertex : FVector +{ + static constexpr bool bComputeNormal = false; + static constexpr bool bComputeTangent = false; + static constexpr bool bComputeTextureCoordinate = false; + static constexpr bool bComputeMaterial = false; + + FORCEINLINE void SetPosition(const FVector& InPosition) + { + static_cast(*this) = InPosition; + } + FORCEINLINE void SetNormal(const FVector&) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetTangent(const FVoxelProcMeshTangent&) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetTextureCoordinate(const FVector2D&) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetMaterial(const FVoxelMaterial&) + { + checkVoxelSlow(false); + } +}; +static_assert(sizeof(FVoxelCubicGeometryVertex) == sizeof(FVector), ""); + +template +FORCEINLINE void AddFace( + TMesher& Mesher, int32 Step, FVoxelMaterial Material, + int32 X, int32 Y, int32 Z, + TArray& Indices, TArray& Vertices) +{ + if (TVertex::bComputeMaterial && Mesher.Settings.bOneMaterialPerCubeSide) + { + uint8 Index = Material.GetSingleIndex(); + + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + case EVoxelDirectionFlag::XMax: + case EVoxelDirectionFlag::YMin: + case EVoxelDirectionFlag::YMax: + Index = 3 * Index + 1; + break; + case EVoxelDirectionFlag::ZMin: + Index = 3 * Index + 2; + break; + case EVoxelDirectionFlag::ZMax: + Index = 3 * Index + 0; + break; + } + + Material.SetSingleIndex(Index); + } + + FVector Positions[4]; + FVector Normal; + FVector Tangent; + + /** + * 0 --- 1 + * | \ | + * 3 --- 2 + * + * Triangles: 0 1 2, 0 2 3 + */ + + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + Positions[0] = FVector(0, 0, 1); + Positions[1] = FVector(0, 1, 1); + Positions[2] = FVector(0, 1, 0); + Positions[3] = FVector(0, 0, 0); + Normal = FVector(-1, 0, 0); + Tangent = FVector(0, 1, 0); + break; + case EVoxelDirectionFlag::XMax: + Positions[0] = FVector(1, 1, 1); + Positions[1] = FVector(1, 0, 1); + Positions[2] = FVector(1, 0, 0); + Positions[3] = FVector(1, 1, 0); + Normal = FVector(1, 0, 0); + Tangent = FVector(0, -1, 0); + break; + case EVoxelDirectionFlag::YMin: + Positions[0] = FVector(1, 0, 1); + Positions[1] = FVector(0, 0, 1); + Positions[2] = FVector(0, 0, 0); + Positions[3] = FVector(1, 0, 0); + Normal = FVector(0, -1, 0); + Tangent = FVector(-1, 0, 0); + break; + case EVoxelDirectionFlag::YMax: + Positions[0] = FVector(0, 1, 1); + Positions[1] = FVector(1, 1, 1); + Positions[2] = FVector(1, 1, 0); + Positions[3] = FVector(0, 1, 0); + Normal = FVector(0, 1, 0); + Tangent = FVector(1, 0, 0); + break; + case EVoxelDirectionFlag::ZMin: + Positions[0] = FVector(0, 1, 0); + Positions[1] = FVector(1, 1, 0); + Positions[2] = FVector(1, 0, 0); + Positions[3] = FVector(0, 0, 0); + Normal = FVector(0, 0, -1); + Tangent = FVector(1, 0, 0); + break; + default: + check(Direction == EVoxelDirectionFlag::ZMax); + Positions[0] = FVector(1, 0, 1); + Positions[1] = FVector(1, 1, 1); + Positions[2] = FVector(0, 1, 1); + Positions[3] = FVector(0, 0, 1); + Normal = FVector(0, 0, 1); + Tangent = FVector(1, 0, 0); + break; + } + + int32 PositionsIndices[4]; + for (int32 Index = 0; Index < 4; Index++) + { + const FVector VertexPositionInCube = Positions[Index]; + const FVector VertexPosition = (VertexPositionInCube + FVector(X, Y, Z)) * Step - FVector(0.5f); + + TVertex Vertex; + Vertex.SetPosition(VertexPosition); + if (TVertex::bComputeNormal) Vertex.SetNormal(Normal); + if (TVertex::bComputeTangent) Vertex.SetTangent(FVoxelProcMeshTangent(Tangent, false)); + if (TVertex::bComputeMaterial) Vertex.SetMaterial(Material); + + if (TVertex::bComputeTextureCoordinate) + { + FVector2D TextureCoordinate; + if (Mesher.Settings.UVConfig == EVoxelUVConfig::GlobalUVs) + { + const FVector V = VertexPosition + FVector(Mesher.ChunkPosition); + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + TextureCoordinate = { V.Y, V.Z }; + break; + case EVoxelDirectionFlag::XMax: + TextureCoordinate = { -V.Y, V.Z }; + break; + case EVoxelDirectionFlag::YMin: + TextureCoordinate = { -V.X, V.Z }; + break; + case EVoxelDirectionFlag::YMax: + TextureCoordinate = { V.X, V.Z }; + break; + case EVoxelDirectionFlag::ZMin: + TextureCoordinate = { V.X, V.Y }; + break; + default: + check(Direction == EVoxelDirectionFlag::ZMax); + TextureCoordinate = { V.X, -V.Y }; + break; + } + TextureCoordinate /= Mesher.Settings.UVScale; + TextureCoordinate.Y *= -1; // Y is down + } + else if (Mesher.Settings.UVConfig == EVoxelUVConfig::PackWorldUpInUVs) + { + TextureCoordinate = FVoxelMesherUtilities::GetUVs(Mesher, FVector(X, Y, Z)); + } + else + { + check(Mesher.Settings.UVConfig == EVoxelUVConfig::PerVoxelUVs); + const auto& V = VertexPositionInCube; + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + TextureCoordinate = { V.Y, V.Z }; + break; + case EVoxelDirectionFlag::XMax: + TextureCoordinate = { 1 - V.Y, V.Z }; + break; + case EVoxelDirectionFlag::YMin: + TextureCoordinate = { 1 - V.X, V.Z }; + break; + case EVoxelDirectionFlag::YMax: + TextureCoordinate = { V.X, V.Z }; + break; + case EVoxelDirectionFlag::ZMin: + TextureCoordinate = { V.X, V.Y }; + break; + default: + check(Direction == EVoxelDirectionFlag::ZMax); + TextureCoordinate = { V.X, 1 - V.Y }; + break; + } + TextureCoordinate.Y = 1 - TextureCoordinate.Y; // Y is down + } + Vertex.SetTextureCoordinate(TextureCoordinate); + } + + PositionsIndices[Index] = Vertices.Num(); + Vertices.Emplace(Vertex); + } + + Indices.Add(PositionsIndices[2]); + Indices.Add(PositionsIndices[1]); + Indices.Add(PositionsIndices[0]); + + Indices.Add(PositionsIndices[3]); + Indices.Add(PositionsIndices[2]); + Indices.Add(PositionsIndices[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox FVoxelCubicMesher::GetBoundsToCheckIsEmptyOn() const +{ + return FVoxelIntBox(ChunkPosition - FIntVector(Step), ChunkPosition - FIntVector(Step) + CUBIC_CHUNK_SIZE_WITH_NEIGHBORS * Step); +} + +FVoxelIntBox FVoxelCubicMesher::GetBoundsToLock() const +{ + return GetBoundsToCheckIsEmptyOn(); +} + +TVoxelSharedPtr FVoxelCubicMesher::CreateFullChunkImpl(FVoxelMesherTimes& Times) +{ + TArray Vertices; + TArray Indices; + + CreateGeometryTemplate(Times, Indices, Vertices); + + UnlockData(); + + return MESHER_TIME_RETURN(CreateChunk, FVoxelMesherUtilities::CreateChunkFromVertices( + Settings, + LOD, + MoveTemp(Indices), + MoveTemp(reinterpret_cast&>(Vertices)))); +} + + +void FVoxelCubicMesher::CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + CreateGeometryTemplate(Times, Indices, reinterpret_cast&>(Vertices)); + UnlockData(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelCubicMesher::CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + if (T::bComputeMaterial) + { + Accelerator = MakeUnique(Data, GetBoundsToLock()); + } + + TVoxelQueryZone QueryZone(GetBoundsToCheckIsEmptyOn(), FIntVector(CUBIC_CHUNK_SIZE_WITH_NEIGHBORS), LOD, CachedValues); + MESHER_TIME_VALUES(CUBIC_CHUNK_SIZE_WITH_NEIGHBORS * CUBIC_CHUNK_SIZE_WITH_NEIGHBORS * CUBIC_CHUNK_SIZE_WITH_NEIGHBORS, Data.Get(QueryZone, LOD)); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Iteration"); + for (int32 X = 0; X < RENDER_CHUNK_SIZE; X++) + { + for (int32 Y = 0; Y < RENDER_CHUNK_SIZE; Y++) + { + for (int32 Z = 0; Z < RENDER_CHUNK_SIZE; Z++) + { + const FVoxelValue Value = GetValue(X, Y, Z); + if (Value.IsEmpty()) continue; + + const uint8 Flag = + (GetValue(X - 1, Y, Z).IsEmpty() << 0) | + (GetValue(X + 1, Y, Z).IsEmpty() << 1) | + (GetValue(X, Y - 1, Z).IsEmpty() << 2) | + (GetValue(X, Y + 1, Z).IsEmpty() << 3) | + (GetValue(X, Y, Z - 1).IsEmpty() << 4) | + (GetValue(X, Y, Z + 1).IsEmpty() << 5); + + if (!Flag) continue; + + FVoxelMaterial Material; + if (T::bComputeMaterial) + { + Material = MESHER_TIME_RETURN_MATERIALS(1, Accelerator->GetMaterial( + X + ChunkPosition.X, + Y + ChunkPosition.Y, + Z + ChunkPosition.Z, + LOD)); + } + +#define CHECK_SIDE(Direction) if (Flag & Direction) AddFace(*this, Step, Material, X, Y, Z, Indices, Vertices) + CHECK_SIDE(EVoxelDirectionFlag::XMin); + CHECK_SIDE(EVoxelDirectionFlag::XMax); + CHECK_SIDE(EVoxelDirectionFlag::YMin); + CHECK_SIDE(EVoxelDirectionFlag::YMax); + CHECK_SIDE(EVoxelDirectionFlag::ZMin); + CHECK_SIDE(EVoxelDirectionFlag::ZMax); +#undef CHECK_SIDE + } + } + } + } +} + +FORCEINLINE FVoxelValue FVoxelCubicMesher::GetValue(int32 X, int32 Y, int32 Z) const +{ + checkVoxelSlow( + -1 <= X && + -1 <= Y && + -1 <= Z && + X <= RENDER_CHUNK_SIZE && + Y <= RENDER_CHUNK_SIZE && + Z <= RENDER_CHUNK_SIZE); + const int32 Index = + (X + 1) + + (Y + 1) * CUBIC_CHUNK_SIZE_WITH_NEIGHBORS + + (Z + 1) * CUBIC_CHUNK_SIZE_WITH_NEIGHBORS * CUBIC_CHUNK_SIZE_WITH_NEIGHBORS; + return CachedValues[Index]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox FVoxelCubicTransitionsMesher::GetBoundsToCheckIsEmptyOn() const +{ + return FVoxelIntBox(ChunkPosition - FIntVector(Step), ChunkPosition - FIntVector(Step) + CUBIC_CHUNK_SIZE_WITH_NEIGHBORS * Step); +} + +FVoxelIntBox FVoxelCubicTransitionsMesher::GetBoundsToLock() const +{ + return GetBoundsToCheckIsEmptyOn(); +} + +TVoxelSharedPtr FVoxelCubicTransitionsMesher::CreateFullChunkImpl(FVoxelMesherTimes& Times) +{ + Accelerator = MakeUnique(Data, GetBoundsToLock()); + + TArray Vertices; + TArray Indices; + + CreateTransitionsForDirection(Times, Indices, Vertices); + CreateTransitionsForDirection(Times, Indices, Vertices); + CreateTransitionsForDirection(Times, Indices, Vertices); + CreateTransitionsForDirection(Times, Indices, Vertices); + CreateTransitionsForDirection(Times, Indices, Vertices); + CreateTransitionsForDirection(Times, Indices, Vertices); + + UnlockData(); + + return MESHER_TIME_RETURN(CreateChunk, FVoxelMesherUtilities::CreateChunkFromVertices( + Settings, + LOD, + MoveTemp(Indices), + MoveTemp(reinterpret_cast&>(Vertices)))); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +inline constexpr EVoxelDirectionFlag::Type InverseVoxelDirection() +{ + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + return EVoxelDirectionFlag::XMax; + case EVoxelDirectionFlag::XMax: + return EVoxelDirectionFlag::XMin; + case EVoxelDirectionFlag::YMin: + return EVoxelDirectionFlag::YMax; + case EVoxelDirectionFlag::YMax: + return EVoxelDirectionFlag::YMin; + case EVoxelDirectionFlag::ZMin: + return EVoxelDirectionFlag::ZMax; + case EVoxelDirectionFlag::ZMax: + default: + return EVoxelDirectionFlag::ZMin; + } +} + +template +void FVoxelCubicTransitionsMesher::CreateTransitionsForDirection(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + if (!(TransitionsMask & Direction)) return; + + for (int32 LX = 0; LX < RENDER_CHUNK_SIZE; LX++) + { + for (int32 LY = 0; LY < RENDER_CHUNK_SIZE; LY++) + { + // "Big" denotes the low resolution LOD voxels + // "Small" denotes the high resolution LOD voxels + // "Other Side" is the name of neighbor we are creating transitions for + // "Other Side" neighbor has a higher resolution than us (their LOD = our LOD - 1) + + const FVoxelValue BigValue = MESHER_TIME_RETURN_VALUES(1, GetValue(Step, LX * Step, LY * Step, 0)); + if (!BigValue.IsEmpty()) + { + // First case: the big voxel is full + + const FVoxelValue BigOtherSideValue = MESHER_TIME_RETURN_VALUES(1, GetValue(Step, LX * Step, LY * Step, -Step)); + // Face is already created by low res normal mesher + if (BigOtherSideValue.IsEmpty()) + { + continue; + } + + // Query the high res other side values + + FVoxelValue SmallOtherSideValues[4]; + { + MESHER_TIME_SCOPE_VALUES(4); + SmallOtherSideValues[0] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 0) * HalfStep, -HalfStep); + SmallOtherSideValues[1] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 0) * HalfStep, -HalfStep); + SmallOtherSideValues[2] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 1) * HalfStep, -HalfStep); + SmallOtherSideValues[3] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 1) * HalfStep, -HalfStep); + } + + if (!SmallOtherSideValues[0].IsEmpty() && + !SmallOtherSideValues[1].IsEmpty() && + !SmallOtherSideValues[2].IsEmpty() && + !SmallOtherSideValues[3].IsEmpty()) + { + // All the higher res LOD values are full too, nothing to do + continue; + + } + + // Need to do some stitching + + // The new faces are facing outwards, same direction as the transitions + constexpr EVoxelDirectionFlag::Type FaceDirection = Direction; + + const auto Material = MESHER_TIME_RETURN_MATERIALS(1, GetMaterial(Step, LX * Step, LY * Step, 0)); + Add2DFace(Step, Material, LX, LY, Vertices, Indices); + } + else + { + FVoxelValue SmallOtherSideValues[4]; + { + MESHER_TIME_SCOPE_VALUES(4); + SmallOtherSideValues[0] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 0) * HalfStep, -HalfStep); + SmallOtherSideValues[1] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 0) * HalfStep, -HalfStep); + SmallOtherSideValues[2] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 1) * HalfStep, -HalfStep); + SmallOtherSideValues[3] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 1) * HalfStep, -HalfStep); + } + + const uint8 IsFullOtherSide = + ((!SmallOtherSideValues[0].IsEmpty()) << 0) | + ((!SmallOtherSideValues[1].IsEmpty()) << 1) | + ((!SmallOtherSideValues[2].IsEmpty()) << 2) | + ((!SmallOtherSideValues[3].IsEmpty()) << 3); + + // All the voxels in the high res chunk are empty too, nothing to do + if (!IsFullOtherSide) continue; + + FVoxelValue SmallValues[4]; + { + MESHER_TIME_SCOPE_VALUES(4); + SmallValues[0] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 0) * HalfStep, 0); + SmallValues[1] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 0) * HalfStep, 0); + SmallValues[2] = GetValue(HalfStep, (2 * LX + 0) * HalfStep, (2 * LY + 1) * HalfStep, 0); + SmallValues[3] = GetValue(HalfStep, (2 * LX + 1) * HalfStep, (2 * LY + 1) * HalfStep, 0); + } + + const uint8 IsFull = + ((!SmallValues[0].IsEmpty()) << 0) | + ((!SmallValues[1].IsEmpty()) << 1) | + ((!SmallValues[2].IsEmpty()) << 2) | + ((!SmallValues[3].IsEmpty()) << 3); + + const uint8 AreBothFull = IsFullOtherSide & IsFull; + + if (!AreBothFull) continue; + + // When AreBothFull is true, the high res mesher did not create a face, and we need to add one ourselves + + // The face direction is from the high res to the low res: the opposite of the transition direction, + // which is low res to high res (ie us to other) + constexpr EVoxelDirectionFlag::Type FaceDirection = InverseVoxelDirection(); + + const auto Material = MESHER_TIME_RETURN_MATERIALS(1, GetMaterial(Step, LX * Step, LY * Step, -HalfStep)); + if (AreBothFull & 0x1) + { + Add2DFace(HalfStep, Material, 2 * LX + 0, 2 * LY + 0, Vertices, Indices); + } + if (AreBothFull & 0x2) + { + Add2DFace(HalfStep, Material, 2 * LX + 1, 2 * LY + 0, Vertices, Indices); + } + if (AreBothFull & 0x4) + { + Add2DFace(HalfStep, Material, 2 * LX + 0, 2 * LY + 1, Vertices, Indices); + } + if (AreBothFull & 0x8) + { + Add2DFace(HalfStep, Material, 2 * LX + 1, 2 * LY + 1, Vertices, Indices); + } + } + } + } +} + +template +FORCEINLINE FVoxelValue FVoxelCubicTransitionsMesher::GetValue(int32 InStep, int32 X, int32 Y, int32 Z) const +{ + const FIntVector Position = Local2DToGlobal(RENDER_CHUNK_SIZE * Step - InStep, X, Y, Z); + return Accelerator->GetValue(ChunkPosition + Position, LOD); +} + +template +FORCEINLINE FVoxelMaterial FVoxelCubicTransitionsMesher::GetMaterial(int32 InStep, int32 X, int32 Y, int32 Z) const +{ + const FIntVector Position = Local2DToGlobal(RENDER_CHUNK_SIZE * Step - InStep, X, Y, Z); + return Accelerator->GetMaterial(ChunkPosition + Position, LOD); +} + +template +inline bool IsDirectionMax() +{ + return Direction == EVoxelDirectionFlag::XMax || Direction == EVoxelDirectionFlag::YMax || Direction == EVoxelDirectionFlag::ZMax; +} + +template +void FVoxelCubicTransitionsMesher::Add2DFace( + int32 InStep, + const FVoxelMaterial& Material, + int32 LX, int32 LY, + TArray& Vertices, TArray& Indices) +{ + const int32 LZ = IsDirectionMax() + ? IsDirectionMax() ? 1 : -1 + : 0; + + const FIntVector P = Local2DToGlobal(Step / InStep * RENDER_CHUNK_SIZE, LX, LY, LZ); + AddFace(*this, InStep, Material, P.X, P.Y, P.Z, Indices, Vertices); +} + +template +FORCEINLINE FIntVector FVoxelCubicTransitionsMesher::Local2DToGlobal(int32 InSize, int32 LX, int32 LY, int32 LZ) +{ + const int32& S = InSize; + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + return { LZ, LX, LY }; + case EVoxelDirectionFlag::XMax: + return { S - LZ, LY, LX }; + case EVoxelDirectionFlag::YMin: + return { LY, LZ, LX }; + case EVoxelDirectionFlag::YMax: + return { LX, S - LZ, LY }; + case EVoxelDirectionFlag::ZMin: + return { LX, LY, LZ }; + case EVoxelDirectionFlag::ZMax: + return { LY, LX, S - LZ }; + default: + check(false); + return {}; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.h new file mode 100644 index 00000000..dcdfab38 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.h @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelDirection.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelRender/Meshers/VoxelMesher.h" + +#define CUBIC_CHUNK_SIZE_WITH_NEIGHBORS (RENDER_CHUNK_SIZE + 2) + +class FVoxelCubicMesher : public FVoxelMesher +{ +public: + using FVoxelMesher::FVoxelMesher; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const override final; + virtual FVoxelIntBox GetBoundsToLock() const override final; + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) override final; + virtual void CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) override final; + +private: + TUniquePtr Accelerator; + TVoxelStaticArray CachedValues; + +private: + template + void CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); + +private: + FVoxelValue GetValue(int32 X, int32 Y, int32 Z) const; +}; + +class FVoxelCubicTransitionsMesher : public FVoxelTransitionsMesher +{ +public: + using FVoxelTransitionsMesher::FVoxelTransitionsMesher; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const override final; + virtual FVoxelIntBox GetBoundsToLock() const override final; + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) override final; + +private: + TUniquePtr Accelerator; + +private: + template + void CreateTransitionsForDirection(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); + +private: + template + FVoxelValue GetValue(int32 InStep, int32 X, int32 Y, int32 Z) const; + template + FVoxelMaterial GetMaterial(int32 InStep, int32 X, int32 Y, int32 Z) const; + + // LX * HalfStep = GX + template + void Add2DFace( + int32 InStep, + const FVoxelMaterial& Material, + int32 LX, int32 LY, + TArray& Vertices, TArray& Indices); + + template + static FIntVector Local2DToGlobal(int32 InSize, int32 LX, int32 LY, int32 LZ); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.cpp new file mode 100644 index 00000000..2ca52b14 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.cpp @@ -0,0 +1,1176 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/Meshers/VoxelMarchingCubeMesher.h" +#include "VoxelRender/Meshers/VoxelMesherUtilities.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "Transvoxel.h" +#include "HAL/IConsoleManager.h" + +#define checkError(x) if(!(x)) { return false; } + +static TAutoConsoleVariable CVarEnableUniqueUVs( + TEXT("voxel.mesher.UniqueUVs"), + 0, + TEXT("If true, will duplicate the vertices to assign to each triangle in a chunk a unique part of the UV space"), + ECVF_Default); + +static TAutoConsoleVariable CVarRandomizeTangents( + TEXT("voxel.mesher.RandomizeTangents"), + 0, + TEXT("If true, will randomize voxel tangents to help debug materials that should not be using them"), + ECVF_Default); + +class FMarchingCubeHelpers +{ +public: + template + static TArray CreateMesherVertices(TArray& Vertices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray MesherVertices; + MesherVertices.SetNumUninitialized(Vertices.Num()); + for (int32 Index = 0; Index < Vertices.Num(); Index++) + { + auto& Vertex = Vertices[Index]; + auto& MesherVertex = MesherVertices[Index]; + MesherVertex.Position = Vertex.Position; + } + return MesherVertices; + } + + template + static void ComputeMaterials(TMesher& Mesher, TArray& MesherVertices, TArray& Vertices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const auto GetMaterial = [&](const FIntVector& P) + { + return Mesher.Accelerator->GetMaterial( + P.X + Mesher.ChunkPosition.X, + P.Y + Mesher.ChunkPosition.Y, + P.Z + Mesher.ChunkPosition.Z, + Mesher.LOD); + }; + if (Mesher.Settings.bInterpolateColors || Mesher.Settings.bInterpolateUVs) + { + for (int32 Index = 0; Index < Vertices.Num(); Index++) + { + auto& Vertex = Vertices[Index]; + auto& MesherVertex = MesherVertices[Index]; + + const auto PositionA = FVoxelUtilities::FloorToInt(MesherVertex.Position); + const auto PositionB = FVoxelUtilities::CeilToInt(MesherVertex.Position); + + const auto MaterialA = GetMaterial(PositionA); + const auto MaterialB = GetMaterial(PositionB); + + ensureVoxelSlowNoSideEffects(Vertex.MaterialPosition == PositionA || Vertex.MaterialPosition == PositionB); + MesherVertex.Material = Vertex.MaterialPosition == PositionA ? MaterialA : MaterialB; + + const FVector Difference = MesherVertex.Position - FVector(PositionA); + const float Alpha = Difference.X + Difference.Y + Difference.Z; + ensureVoxelSlowNoSideEffects(0 <= Alpha && Alpha <= 1); + + if (Mesher.Settings.bInterpolateUVs) + { + if (Mesher.Settings.MaterialConfig != EVoxelMaterialConfig::MultiIndex) + { + MesherVertex.Material.SetU0_AsFloat(FMath::Lerp(MaterialA.GetU0_AsFloat(), MaterialB.GetU0_AsFloat(), Alpha)); + MesherVertex.Material.SetV0_AsFloat(FMath::Lerp(MaterialA.GetV0_AsFloat(), MaterialB.GetV0_AsFloat(), Alpha)); + MesherVertex.Material.SetU1_AsFloat(FMath::Lerp(MaterialA.GetU1_AsFloat(), MaterialB.GetU1_AsFloat(), Alpha)); + MesherVertex.Material.SetV1_AsFloat(FMath::Lerp(MaterialA.GetV1_AsFloat(), MaterialB.GetV1_AsFloat(), Alpha)); + } + MesherVertex.Material.SetU2_AsFloat(FMath::Lerp(MaterialA.GetU2_AsFloat(), MaterialB.GetU2_AsFloat(), Alpha)); + MesherVertex.Material.SetV2_AsFloat(FMath::Lerp(MaterialA.GetV2_AsFloat(), MaterialB.GetV2_AsFloat(), Alpha)); + MesherVertex.Material.SetU3_AsFloat(FMath::Lerp(MaterialA.GetU3_AsFloat(), MaterialB.GetU3_AsFloat(), Alpha)); + MesherVertex.Material.SetV3_AsFloat(FMath::Lerp(MaterialA.GetV3_AsFloat(), MaterialB.GetV3_AsFloat(), Alpha)); + } + + if (Mesher.Settings.bInterpolateColors) + { + if (Mesher.Settings.MaterialConfig == EVoxelMaterialConfig::RGB) + { + MesherVertex.Material.SetColor(FMath::Lerp(MaterialA.GetLinearColor(), MaterialB.GetLinearColor(), Alpha)); + } + else if (Mesher.Settings.MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + MesherVertex.Material.SetR_AsFloat(FMath::Lerp(MaterialA.GetR_AsFloat(), MaterialB.GetR_AsFloat(), Alpha)); + MesherVertex.Material.SetG_AsFloat(FMath::Lerp(MaterialA.GetG_AsFloat(), MaterialB.GetG_AsFloat(), Alpha)); + MesherVertex.Material.SetB_AsFloat(FMath::Lerp(MaterialA.GetB_AsFloat(), MaterialB.GetB_AsFloat(), Alpha)); + } + else + { + checkVoxelSlow(Mesher.Settings.MaterialConfig == EVoxelMaterialConfig::MultiIndex); + MesherVertex.Material.SetMultiIndex_Blend0_AsFloat(FMath::Lerp(MaterialA.GetMultiIndex_Blend0_AsFloat(), MaterialB.GetMultiIndex_Blend0_AsFloat(), Alpha)); + MesherVertex.Material.SetMultiIndex_Blend1_AsFloat(FMath::Lerp(MaterialA.GetMultiIndex_Blend1_AsFloat(), MaterialB.GetMultiIndex_Blend1_AsFloat(), Alpha)); + MesherVertex.Material.SetMultiIndex_Blend2_AsFloat(FMath::Lerp(MaterialA.GetMultiIndex_Blend2_AsFloat(), MaterialB.GetMultiIndex_Blend2_AsFloat(), Alpha)); + MesherVertex.Material.SetMultiIndex_Wetness_AsFloat(FMath::Lerp(MaterialA.GetMultiIndex_Wetness_AsFloat(), MaterialB.GetMultiIndex_Wetness_AsFloat(), Alpha)); + } + } + } + } + else + { + for (int32 Index = 0; Index < Vertices.Num(); Index++) + { + auto& Vertex = Vertices[Index]; + auto& MesherVertex = MesherVertices[Index]; + + MesherVertex.Material = GetMaterial(Vertex.MaterialPosition); + } + } + } + + static void FixupTangents(TArray& MesherVertices) + { + if (CVarRandomizeTangents.GetValueOnAnyThread() != 0) + { + for (auto& Vertex : MesherVertices) + { + Vertex.Tangent.TangentX = FVector(FMath::FRandRange(-1, 1), FMath::FRandRange(-1, 1), FMath::FRandRange(-1, 1)).GetSafeNormal(); + } + } + } + + static void ComputeFlatNormals(TArray& Vertices, TArray& Indices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray NewVertices; + for (int32 I = 0; I < Indices.Num(); I += 3) + { + uint32& IndexA = Indices[I + 0]; + uint32& IndexB = Indices[I + 1]; + uint32& IndexC = Indices[I + 2]; + + auto VertexA = Vertices[IndexA]; + auto VertexB = Vertices[IndexB]; + auto VertexC = Vertices[IndexC]; + + const FVector Normal = FVector::CrossProduct(VertexC.Position - VertexA.Position, VertexB.Position - VertexA.Position).GetSafeNormal(); + VertexA.Normal = Normal; + VertexB.Normal = Normal; + VertexC.Normal = Normal; + + IndexA = NewVertices.Add(VertexA); + IndexB = NewVertices.Add(VertexB); + IndexC = NewVertices.Add(VertexC); + } + Vertices = MoveTemp(NewVertices); + } + static void ComputeNormals(FVoxelMarchingCubeMesher& Mesher, TArray& MesherVertices, TArray& Indices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const auto GetGradient = [&](const FVector& Position) + { + if (Mesher.LOD == 0) + { + // For LOD 0, we used the cached data + // One downside: might not look as great as using the float value from the generator + // However, the generator at LOD 0 should have small enough values to generate nice normals from them even if they are FVoxelValues + // Additionally, computing the normals from the generator often requires the interpolation fix at LOD 0, + // which ends up doing the same as what we are doing here but a lot slower (eg 50ms of 54ms total taken to compute normals!) + return FVoxelDataUtilities::GetGradientFromGetValue( + FVoxelDataUtilities::MakeBilinearInterpolatedData(Mesher), + Position.X, + Position.Y, + Position.Z, + 0, + 1); + } + else + { + return FVoxelDataUtilities::GetGradientFromGetFloatValue( + *Mesher.Accelerator, + v_flt(Position.X) + Mesher.ChunkPosition.X, + v_flt(Position.Y) + Mesher.ChunkPosition.Y, + v_flt(Position.Z) + Mesher.ChunkPosition.Z, + Mesher.LOD, + Mesher.Step); + } + }; + + if (Mesher.Settings.NormalConfig == EVoxelNormalConfig::GradientNormal) + { + for (auto& Vertex : MesherVertices) + { + Vertex.Normal = GetGradient(Vertex.Position); + Vertex.Tangent = FVoxelProcMeshTangent(); + } + } + else if (Mesher.Settings.NormalConfig == EVoxelNormalConfig::FlatNormal) + { + ComputeFlatNormals(MesherVertices, Indices); + } + else if (Mesher.Settings.NormalConfig == EVoxelNormalConfig::MeshNormal) + { + for (auto& Vertex : MesherVertices) + { + Vertex.Normal = FVector(0, 0, 0); + Vertex.Tangent = FVoxelProcMeshTangent(); + } + for (int32 Index = 0; Index < Indices.Num(); Index += 3) + { + auto& A = MesherVertices[Indices[Index + 0]]; + auto& B = MesherVertices[Indices[Index + 1]]; + auto& C = MesherVertices[Indices[Index + 2]]; + const FVector Normal = FVector::CrossProduct(C.Position - A.Position, B.Position - A.Position).GetSafeNormal(); + A.Normal += Normal; + B.Normal += Normal; + C.Normal += Normal; + } + for (auto& Vertex : MesherVertices) + { + if (Vertex.Position.X < Mesher.Step || + Vertex.Position.Y < Mesher.Step || + Vertex.Position.Z < Mesher.Step || + Vertex.Position.X > (RENDER_CHUNK_SIZE - 1) * Mesher.Step || + Vertex.Position.Y > (RENDER_CHUNK_SIZE - 1) * Mesher.Step || + Vertex.Position.Z > (RENDER_CHUNK_SIZE - 1) * Mesher.Step) + { + // Can't use mesh normals on edges, as it looks like crap because of the missing neighbor vertices + Vertex.Normal = GetGradient(Vertex.Position); + } + else + { + Vertex.Normal.Normalize(); + } + } + } + else + { + check(Mesher.Settings.NormalConfig == EVoxelNormalConfig::NoNormal); + for (auto& Vertex : MesherVertices) + { + Vertex.Normal = FVector(0, 0, 0); + Vertex.Tangent = FVoxelProcMeshTangent(); + } + } + + FixupTangents(MesherVertices); + } + static void ComputeNormals(FVoxelMarchingCubeTransitionsMesher& Mesher, TArray& MesherVertices, TArray& Indices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (Mesher.Settings.NormalConfig == EVoxelNormalConfig::GradientNormal || Mesher.Settings.NormalConfig == EVoxelNormalConfig::MeshNormal) + { + for (auto& Vertex : MesherVertices) + { + Vertex.Normal = FVoxelDataUtilities::GetGradientFromGetFloatValue( + *Mesher.Accelerator, + v_flt(Vertex.Position.X) + Mesher.ChunkPosition.X, + v_flt(Vertex.Position.Y) + Mesher.ChunkPosition.Y, + v_flt(Vertex.Position.Z) + Mesher.ChunkPosition.Z, + Mesher.LOD, + Mesher.Step);; + Vertex.Tangent = FVoxelProcMeshTangent(); + } + } + else if (Mesher.Settings.NormalConfig == EVoxelNormalConfig::FlatNormal) + { + ComputeFlatNormals(MesherVertices, Indices); + } + else + { + check(Mesher.Settings.NormalConfig == EVoxelNormalConfig::NoNormal); + for (auto& Vertex : MesherVertices) + { + Vertex.Normal = FVector(0, 0, 0); + Vertex.Tangent = FVoxelProcMeshTangent(); + } + } + + FixupTangents(MesherVertices); + } + + template + static void ComputeUVs(TMesher& Mesher, TArray& MesherVertices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (auto& Vertex : MesherVertices) + { + Vertex.TextureCoordinate = FVoxelMesherUtilities::GetUVs(Mesher, Vertex.Position); + } + } +}; + +FVoxelIntBox FVoxelMarchingCubeMesher::GetBoundsToCheckIsEmptyOn() const +{ + return FVoxelIntBox(ChunkPosition, ChunkPosition + CHUNK_SIZE_WITH_END_EDGE * Step); +} + +FVoxelIntBox FVoxelMarchingCubeMesher::GetBoundsToLock() const +{ + // We need to lock for the normals (also work with LOD 0) + return FVoxelIntBox(ChunkPosition - FIntVector(Step), ChunkPosition + FIntVector(Step) + CHUNK_SIZE_WITH_END_EDGE * Step); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedPtr FVoxelMarchingCubeMesher::CreateFullChunkImpl(FVoxelMesherTimes& Times) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + struct FLocalVertex + { + FVector Position; + FIntVector MaterialPosition; + + FLocalVertex() = default; + FORCEINLINE FLocalVertex(const FVector& Position, const FIntVector& MaterialPosition) + : Position(Position) + , MaterialPosition(MaterialPosition) + { + } + }; + + TArray Indices; + TArray Vertices; + CreateGeometryTemplate(Times, Indices, Vertices); + + FVoxelMesherUtilities::SanitizeMesh(Indices, Vertices); + + TArray MesherVertices = FMarchingCubeHelpers::CreateMesherVertices(Vertices); + + MESHER_TIME_MATERIALS(MesherVertices.Num(), FMarchingCubeHelpers::ComputeMaterials(*this, MesherVertices, Vertices)); + MESHER_TIME(Normals, FMarchingCubeHelpers::ComputeNormals(*this, MesherVertices, Indices)); + + UnlockData(); + + MESHER_TIME(UVs, FMarchingCubeHelpers::ComputeUVs(*this, MesherVertices)); + + if (CVarEnableUniqueUVs.GetValueOnAnyThread() != 0) + { + { + TArray NewVertices; + NewVertices.Reserve(Indices.Num()); + TArray NewIndices; + NewIndices.Reserve(Indices.Num()); + + for (uint32 Index : Indices) + { + NewIndices.Add(NewVertices.Num()); + NewVertices.Add(MesherVertices[Index]); + } + + MesherVertices = MoveTemp(NewVertices); + Indices = MoveTemp(NewIndices); + } + + const int32 NumTriangles = Indices.Num() / 3; + const int32 NumSquares = FVoxelUtilities::DivideCeil(NumTriangles, 2); + const int32 NumSquaresPerRow = FMath::CeilToInt(FMath::Sqrt(NumSquares)); + check(NumSquares <= NumSquaresPerRow * NumSquaresPerRow); + + const int32 TextureUVSize = 1; + const float SquareUVSize = TextureUVSize / float(NumSquaresPerRow); + + const auto AddTriangle = [&Indices, &MesherVertices](int32 TriangleIndex, const FVector2D& A, const FVector2D& B, const FVector2D& C) + { + const int32 IndexA = Indices[3 * TriangleIndex + 0]; + const int32 IndexB = Indices[3 * TriangleIndex + 1]; + const int32 IndexC = Indices[3 * TriangleIndex + 2]; + MesherVertices[IndexA].TextureCoordinate = A; + MesherVertices[IndexB].TextureCoordinate = B; + MesherVertices[IndexC].TextureCoordinate = C; + }; + + int32 Row = 0; + int32 Column = 0; + for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; TriangleIndex += 2) + { + /** + * A B + * C D + */ + const FVector2D A = { (Column + 0) * SquareUVSize, (Row + 0) * SquareUVSize }; + const FVector2D B = { (Column + 1) * SquareUVSize, (Row + 0) * SquareUVSize }; + const FVector2D C = { (Column + 0) * SquareUVSize, (Row + 1) * SquareUVSize }; + const FVector2D D = { (Column + 1) * SquareUVSize, (Row + 1) * SquareUVSize }; + + AddTriangle(TriangleIndex, A, B, D); + + if (TriangleIndex + 1 < NumTriangles) + { + AddTriangle(TriangleIndex + 1, A, D, C); + } + + Column++; + if (Column == NumSquaresPerRow) + { + Column = 0; + Row++; + } + } + } + + return MESHER_TIME_RETURN(CreateChunk, FVoxelMesherUtilities::CreateChunkFromVertices( + Settings, + LOD, + MoveTemp(Indices), + MoveTemp(MesherVertices))); +} + +void FVoxelMarchingCubeMesher::CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + struct FVectorVertex : FVector + { + FVectorVertex() = default; + FORCEINLINE FVectorVertex(const FVector& Position, const FIntVector&) + : FVector(Position) + { + } + }; + CreateGeometryTemplate(Times, Indices, reinterpret_cast&>(Vertices)); + UnlockData(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +bool FVoxelMarchingCubeMesher::CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const int32 DataSize = LOD == 0 ? CHUNK_SIZE_WITH_NORMALS : CHUNK_SIZE_WITH_END_EDGE; + + FVoxelIntBox BoundsToQuery(ChunkPosition, ChunkPosition + CHUNK_SIZE_WITH_END_EDGE * Step); + if (LOD == 0) + { + // Account for normals + BoundsToQuery = BoundsToQuery.Extend(1); + } + TVoxelQueryZone QueryZone(BoundsToQuery, FIntVector(DataSize), LOD, CachedValues); + MESHER_TIME_VALUES(DataSize * DataSize * DataSize, Data.Get(QueryZone, LOD)); + + Accelerator = MakeUnique(Data, GetBoundsToLock()); + + uint32 VoxelIndex = 0; + if (LOD == 0) VoxelIndex += DataSize * DataSize; // Additional voxel for normals + for (int32 LZ = 0; LZ < RENDER_CHUNK_SIZE; LZ++) + { + if (LOD == 0) VoxelIndex += DataSize; // Additional voxel for normals + for (int32 LY = 0; LY < RENDER_CHUNK_SIZE; LY++) + { + if (LOD == 0) VoxelIndex += 1; // Additional voxel for normals + for (int32 LX = 0; LX < RENDER_CHUNK_SIZE; LX++) + { + { + CurrentCache[GetCacheIndex(0, LX, LY)] = -1; // Set EdgeIndex 0 to -1 if the cell isn't voxelized, eg all corners = 0 + + uint32 CubeIndices[8]; + CubeIndices[0] = VoxelIndex; + CubeIndices[1] = VoxelIndex + 1; + CubeIndices[2] = VoxelIndex + DataSize; + CubeIndices[3] = VoxelIndex + 1 + DataSize; + CubeIndices[4] = VoxelIndex + DataSize * DataSize; + CubeIndices[5] = VoxelIndex + 1 + DataSize * DataSize; + CubeIndices[6] = VoxelIndex + DataSize + DataSize * DataSize; + CubeIndices[7] = VoxelIndex + 1 + DataSize + DataSize * DataSize; + + checkVoxelSlow(CubeIndices[0] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[1] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[2] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[3] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[4] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[5] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[6] < uint32(DataSize * DataSize * DataSize)); + checkVoxelSlow(CubeIndices[7] < uint32(DataSize * DataSize * DataSize)); + + const uint32 CaseCode = + (CachedValues[CubeIndices[0]].IsEmpty() << 0) | + (CachedValues[CubeIndices[1]].IsEmpty() << 1) | + (CachedValues[CubeIndices[2]].IsEmpty() << 2) | + (CachedValues[CubeIndices[3]].IsEmpty() << 3) | + (CachedValues[CubeIndices[4]].IsEmpty() << 4) | + (CachedValues[CubeIndices[5]].IsEmpty() << 5) | + (CachedValues[CubeIndices[6]].IsEmpty() << 6) | + (CachedValues[CubeIndices[7]].IsEmpty() << 7); + + if (CaseCode != 0 && CaseCode != 255) + { + // Cell has a nontrivial triangulation + + const uint8 ValidityMask = (LX != 0) + 2 * (LY != 0) + 4 * (LZ != 0); + + checkVoxelSlow(0 <= CaseCode && CaseCode < 256); + const uint8 CellClass = Transvoxel::regularCellClass[CaseCode]; + const uint16* RESTRICT VertexData = Transvoxel::regularVertexData[CaseCode]; + checkVoxelSlow(0 <= CellClass && CellClass < 16); + Transvoxel::RegularCellData CellData = Transvoxel::regularCellData[CellClass]; + + // Indices of the vertices used in this cube + TVoxelStaticArray VertexIndices; + for (int32 I = 0; I < CellData.GetVertexCount(); I++) + { + int32 VertexIndex = -2; + const uint16 EdgeCode = VertexData[I]; + + // A: low point / B: high point + const uint8 LocalIndexA = (EdgeCode >> 4) & 0x0F; + const uint8 LocalIndexB = EdgeCode & 0x0F; + + checkVoxelSlow(0 <= LocalIndexA && LocalIndexA < 8); + checkVoxelSlow(0 <= LocalIndexB && LocalIndexB < 8); + + const uint32 IndexA = CubeIndices[LocalIndexA]; + const uint32 IndexB = CubeIndices[LocalIndexB]; + + const FVoxelValue& ValueAtA = CachedValues[IndexA]; + const FVoxelValue& ValueAtB = CachedValues[IndexB]; + + checkVoxelSlow(ValueAtA.IsEmpty() != ValueAtB.IsEmpty()); + + uint8 EdgeIndex = ((EdgeCode >> 8) & 0x0F); + checkVoxelSlow(1 <= EdgeIndex && EdgeIndex < 4); + + // Direction to go to use an already created vertex: + // first bit: x is different + // second bit: y is different + // third bit: z is different + // fourth bit: vertex isn't cached + uint8 CacheDirection = EdgeCode >> 12; + + if (ValueAtA.IsNull()) + { + EdgeIndex = 0; + CacheDirection = LocalIndexA ^ 7; + } + if (ValueAtB.IsNull()) + { + checkVoxelSlow(!ValueAtA.IsNull()); + EdgeIndex = 0; + CacheDirection = LocalIndexB ^ 7; + } + + const bool bIsVertexCached = ((ValidityMask & CacheDirection) == CacheDirection) && CacheDirection; // CacheDirection == 0 => LocalIndexB = 0 (as only B can be = 7) and ValueAtB = 0 + + if (bIsVertexCached) + { + checkVoxelSlow(!(CacheDirection & 0x08)); + + bool XIsDifferent = !!(CacheDirection & 0x01); + bool YIsDifferent = !!(CacheDirection & 0x02); + bool ZIsDifferent = !!(CacheDirection & 0x04); + + VertexIndex = (ZIsDifferent ? OldCache : CurrentCache)[GetCacheIndex(EdgeIndex, LX - XIsDifferent, LY - YIsDifferent)]; + ensureVoxelSlowNoSideEffects(-1 <= VertexIndex && VertexIndex < Vertices.Num()); // Can happen if the generator is returning different values + } + + if (!bIsVertexCached || VertexIndex == -1) + { + // We are on one the lower edges of the chunk. Compute vertex + + const FIntVector PositionA((LX + (LocalIndexA & 0x01)) * Step, (LY + ((LocalIndexA & 0x02) >> 1)) * Step, (LZ + ((LocalIndexA & 0x04) >> 2)) * Step); + const FIntVector PositionB((LX + (LocalIndexB & 0x01)) * Step, (LY + ((LocalIndexB & 0x02) >> 1)) * Step, (LZ + ((LocalIndexB & 0x04) >> 2)) * Step); + + FVector IntersectionPoint; + FIntVector MaterialPosition; + + if (EdgeIndex == 0) + { + if (ValueAtA.IsNull()) + { + IntersectionPoint = FVector(PositionA); + MaterialPosition = PositionA; + } + else + { + checkVoxelSlow(ValueAtB.IsNull()); + IntersectionPoint = FVector(PositionB); + MaterialPosition = PositionB; + } + } + else if (LOD == 0) + { + // Full resolution + + const float Alpha = ValueAtA.ToFloat() / (ValueAtA.ToFloat() - ValueAtB.ToFloat()); + checkError(!FMath::IsNaN(Alpha) && FMath::IsFinite(Alpha)); + + switch (EdgeIndex) + { + case 2: // X + IntersectionPoint = FVector(FMath::Lerp(PositionA.X, PositionB.X, Alpha), PositionA.Y, PositionA.Z); + break; + case 1: // Y + IntersectionPoint = FVector(PositionA.X, FMath::Lerp(PositionA.Y, PositionB.Y, Alpha), PositionA.Z); + break; + case 3: // Z + IntersectionPoint = FVector(PositionA.X, PositionA.Y, FMath::Lerp(PositionA.Z, PositionB.Z, Alpha)); + break; + default: + checkVoxelSlow(false); + } + + // Use the material of the point inside + MaterialPosition = !ValueAtA.IsEmpty() ? PositionA : PositionB; + } + else + { + // Interpolate + + const bool bIsAlongX = (EdgeIndex == 2); + const bool bIsAlongY = (EdgeIndex == 1); + const bool bIsAlongZ = (EdgeIndex == 3); + + checkVoxelSlow(!bIsAlongX || (PositionA.Y == PositionB.Y && PositionA.Z == PositionB.Z)); + checkVoxelSlow(!bIsAlongY || (PositionA.X == PositionB.X && PositionA.Z == PositionB.Z)); + checkVoxelSlow(!bIsAlongZ || (PositionA.X == PositionB.X && PositionA.Y == PositionB.Y)); + + int32 Min = bIsAlongX ? PositionA.X : bIsAlongY ? PositionA.Y : PositionA.Z; + int32 Max = bIsAlongX ? PositionB.X : bIsAlongY ? PositionB.Y : PositionB.Z; + + FVoxelValue ValueAtACopy = ValueAtA; + FVoxelValue ValueAtBCopy = ValueAtB; + + while (Max - Min != 1) + { + checkError((Max + Min) % 2 == 0); + const int32 Middle = (Max + Min) / 2; + + FVoxelValue ValueAtMiddle = MESHER_TIME_RETURN_VALUES(1, Accelerator->Get( + (bIsAlongX ? Middle : PositionA.X) + ChunkPosition.X, + (bIsAlongY ? Middle : PositionA.Y) + ChunkPosition.Y, + (bIsAlongZ ? Middle : PositionA.Z) + ChunkPosition.Z, LOD)); + + if (ValueAtACopy.IsEmpty() == ValueAtMiddle.IsEmpty()) + { + // If min and middle have same sign + Min = Middle; + ValueAtACopy = ValueAtMiddle; + } + else + { + // If max and middle have same sign + Max = Middle; + ValueAtBCopy = ValueAtMiddle; + } + + checkError(Min <= Max); + } + + const float Alpha = ValueAtACopy.ToFloat() / (ValueAtACopy.ToFloat() - ValueAtBCopy.ToFloat()); + checkError(!FMath::IsNaN(Alpha) && FMath::IsFinite(Alpha)); + + const float R = FMath::Lerp(Min, Max, Alpha); + IntersectionPoint = FVector( + bIsAlongX ? R : PositionA.X, + bIsAlongY ? R : PositionA.Y, + bIsAlongZ ? R : PositionA.Z); + + // Get intersection material + if (!ValueAtACopy.IsEmpty()) + { + checkVoxelSlow(ValueAtBCopy.IsEmpty()); + MaterialPosition = FIntVector( + bIsAlongX ? Min : PositionA.X, + bIsAlongY ? Min : PositionA.Y, + bIsAlongZ ? Min : PositionA.Z); + } + else + { + checkVoxelSlow(!ValueAtBCopy.IsEmpty()); + MaterialPosition = FIntVector( + bIsAlongX ? Max : PositionA.X, + bIsAlongY ? Max : PositionA.Y, + bIsAlongZ ? Max : PositionA.Z); + } + } + + VertexIndex = Vertices.Num(); + + if (Settings.RenderSharpness != 0) + { + IntersectionPoint = FVector(FVoxelUtilities::RoundToInt(IntersectionPoint * Settings.RenderSharpness)) / Settings.RenderSharpness; + } + + Vertices.Add(T(IntersectionPoint, MaterialPosition)); + + checkVoxelSlow((ValueAtB.IsNull() && LocalIndexB == 7) == !CacheDirection); + checkVoxelSlow(CacheDirection || EdgeIndex == 0); + + // Save vertex if not on edge + if (CacheDirection & 0x08 || !CacheDirection) // ValueAtB.IsNull() && LocalIndexB == 7 => !CacheDirection + { + CurrentCache[GetCacheIndex(EdgeIndex, LX, LY)] = VertexIndex; + } + } + + VertexIndices[I] = VertexIndex; + checkVoxelSlow(0 <= VertexIndex && VertexIndex < Vertices.Num()); + } + + // Add triangles + // 3 vertex per triangle + for (int32 Index = 0; Index < 3 * CellData.GetTriangleCount(); Index += 3) + { + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 0]]); + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 1]]); + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 2]]); + } + } + } + + VoxelIndex++; + } + VoxelIndex += 1; // End edge voxel + if (LOD == 0) VoxelIndex += 1; // Additional voxel for normals + } + VoxelIndex += DataSize; // End edge voxel + if (LOD == 0) VoxelIndex += DataSize; // Additional voxel for normals + + // Can't use Unreal Swap on restrict ptrs with clang + std::swap(CurrentCache, OldCache); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE int32 FVoxelMarchingCubeMesher::GetCacheIndex(int32 EdgeIndex, int32 LX, int32 LY) +{ + checkVoxelSlow(0 <= LX && LX < RENDER_CHUNK_SIZE); + checkVoxelSlow(0 <= LY && LY < RENDER_CHUNK_SIZE); + checkVoxelSlow(0 <= EdgeIndex && EdgeIndex < EDGE_INDEX_COUNT); + return EdgeIndex + LX * EDGE_INDEX_COUNT + LY * EDGE_INDEX_COUNT * RENDER_CHUNK_SIZE; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox FVoxelMarchingCubeTransitionsMesher::GetBoundsToCheckIsEmptyOn() const +{ + return FVoxelIntBox(ChunkPosition, ChunkPosition + CHUNK_SIZE_WITH_END_EDGE * Step); +} + +FVoxelIntBox FVoxelMarchingCubeTransitionsMesher::GetBoundsToLock() const +{ + return FVoxelIntBox(ChunkPosition - FIntVector(Step), ChunkPosition + FIntVector(Step) + CHUNK_SIZE_WITH_END_EDGE * Step); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +bool FVoxelMarchingCubeTransitionsMesher::CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + Accelerator = MakeUnique(Data, GetBoundsToLock()); + + bool bSuccess = true; + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + bSuccess &= CreateGeometryForDirection(Times, Indices, Vertices); + return bSuccess; +} + +template +bool FVoxelMarchingCubeTransitionsMesher::CreateGeometryForDirection(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + if (!(TransitionsMask & Direction)) return true; + +#if VOXEL_DEBUG + for (auto& Value : Cache2D) + { + Value = -100; + } +#endif + + for (int32 LX = 0; LX < RENDER_CHUNK_SIZE; LX++) + { + for (int32 LY = 0; LY < RENDER_CHUNK_SIZE; LY++) + { + // Set EdgeIndex 0, 1, 2 and 7 to -1 for when the cell aren't polygonized (0 on all corners) + Cache2D[GetCacheIndex(0, LX, LY)] = -1; + Cache2D[GetCacheIndex(1, LX, LY)] = -1; + Cache2D[GetCacheIndex(2, LX, LY)] = -1; + Cache2D[GetCacheIndex(7, LX, LY)] = -1; + + FVoxelValue CornerValues[13]; + + { + MESHER_TIME_SCOPE_VALUES(13); + + CornerValues[0] = GetValue((2 * LX + 0) * HalfStep, (2 * LY + 0) * HalfStep, HalfLOD); + CornerValues[1] = GetValue((2 * LX + 1) * HalfStep, (2 * LY + 0) * HalfStep, HalfLOD); + CornerValues[2] = GetValue((2 * LX + 2) * HalfStep, (2 * LY + 0) * HalfStep, HalfLOD); + CornerValues[3] = GetValue((2 * LX + 0) * HalfStep, (2 * LY + 1) * HalfStep, HalfLOD); + CornerValues[4] = GetValue((2 * LX + 1) * HalfStep, (2 * LY + 1) * HalfStep, HalfLOD); + CornerValues[5] = GetValue((2 * LX + 2) * HalfStep, (2 * LY + 1) * HalfStep, HalfLOD); + CornerValues[6] = GetValue((2 * LX + 0) * HalfStep, (2 * LY + 2) * HalfStep, HalfLOD); + CornerValues[7] = GetValue((2 * LX + 1) * HalfStep, (2 * LY + 2) * HalfStep, HalfLOD); + CornerValues[8] = GetValue((2 * LX + 2) * HalfStep, (2 * LY + 2) * HalfStep, HalfLOD); + + CornerValues[9] = GetValue((LX + 0) * Step, (LY + 0) * Step, LOD); + CornerValues[10] = GetValue((LX + 1) * Step, (LY + 0) * Step, LOD); + CornerValues[11] = GetValue((LX + 0) * Step, (LY + 1) * Step, LOD); + CornerValues[12] = GetValue((LX + 1) * Step, (LY + 1) * Step, LOD); + } + + if (CornerValues[9].IsEmpty() != CornerValues[0].IsEmpty()) + { + CornerValues[9] = CornerValues[0]; + } + if (CornerValues[10].IsEmpty() != CornerValues[2].IsEmpty()) + { + CornerValues[10] = CornerValues[2]; + } + if (CornerValues[11].IsEmpty() != CornerValues[6].IsEmpty()) + { + CornerValues[11] = CornerValues[6]; + } + if (CornerValues[12].IsEmpty() != CornerValues[8].IsEmpty()) + { + CornerValues[12] = CornerValues[8]; + } + + const uint32 CaseCode = + (CornerValues[0].IsEmpty() << 0) + | (CornerValues[1].IsEmpty() << 1) + | (CornerValues[2].IsEmpty() << 2) + | (CornerValues[5].IsEmpty() << 3) + | (CornerValues[8].IsEmpty() << 4) + | (CornerValues[7].IsEmpty() << 5) + | (CornerValues[6].IsEmpty() << 6) + | (CornerValues[3].IsEmpty() << 7) + | (CornerValues[4].IsEmpty() << 8); + + if (!(CaseCode == 0 || CaseCode == 511)) + { + const uint8 ValidityMask = (LX != 0) + 2 * (LY != 0); + + FIntVector Positions[13] = { + FIntVector(2 * LX + 0, 2 * LY + 0, 0) * HalfStep, + FIntVector(2 * LX + 1, 2 * LY + 0, 0) * HalfStep, + FIntVector(2 * LX + 2, 2 * LY + 0, 0) * HalfStep, + FIntVector(2 * LX + 0, 2 * LY + 1, 0) * HalfStep, + FIntVector(2 * LX + 1, 2 * LY + 1, 0) * HalfStep, + FIntVector(2 * LX + 2, 2 * LY + 1, 0) * HalfStep, + FIntVector(2 * LX + 0, 2 * LY + 2, 0) * HalfStep, + FIntVector(2 * LX + 1, 2 * LY + 2, 0) * HalfStep, + FIntVector(2 * LX + 2, 2 * LY + 2, 0) * HalfStep, + + FIntVector(2 * LX + 0, 2 * LY + 0, 1) * HalfStep, + FIntVector(2 * LX + 2, 2 * LY + 0, 1) * HalfStep, + FIntVector(2 * LX + 0, 2 * LY + 2, 1) * HalfStep, + FIntVector(2 * LX + 2, 2 * LY + 2, 1) * HalfStep + }; + + checkVoxelSlow(0 <= CaseCode && CaseCode < 512); + const uint8 CellClass = Transvoxel::transitionCellClass[CaseCode]; + const uint16* VertexData = Transvoxel::transitionVertexData[CaseCode]; + checkVoxelSlow(0 <= (CellClass & 0x7F) && (CellClass & 0x7F) < 56); + const Transvoxel::TransitionCellData CellData = Transvoxel::transitionCellData[CellClass & 0x7F]; + const bool bFlip = ((CellClass >> 7) != 0); + + TArray> VertexIndices; // Not sure how many indices max, let's just say 64 + VertexIndices.SetNumUninitialized(CellData.GetVertexCount()); + + for (int32 i = 0; i < CellData.GetVertexCount(); i++) + { + int32 VertexIndex = -1; + const uint16& EdgeCode = VertexData[i]; + + // A: low point / B: high point + const uint8 IndexVertexA = (EdgeCode >> 4) & 0x0F; + const uint8 IndexVertexB = EdgeCode & 0x0F; + + checkVoxelSlow(0 <= IndexVertexA && IndexVertexA < 13); + checkVoxelSlow(0 <= IndexVertexB && IndexVertexB < 13); + + const FIntVector& PositionA = Positions[IndexVertexA]; + const FIntVector& PositionB = Positions[IndexVertexB]; + + const FVoxelValue& ValueAtA = CornerValues[IndexVertexA]; + const FVoxelValue& ValueAtB = CornerValues[IndexVertexB]; + + uint8 EdgeIndex = (EdgeCode >> 8) & 0x0F; + checkVoxelSlow(EdgeIndex < 10); + // Direction to go to use an already created vertex + // First bit: x is different + // Second bit: y is different + // Third bit: interior edge, never cached + // Fourth bit: own edge, need to create + uint8 CacheDirection = EdgeCode >> 12; + + if (!(CacheDirection & 0x04)) // If not interior edge + { + static uint8 CacheDirectionMap[13] = {3, 2, 2, 1, 4, 8, 1, 8, 8, 3, 2, 1, 8}; + if (ValueAtA.IsNull()) + { + static uint8 EdgeIndexMap[10] = {0, 1, 2, 0, 1, 0, 2, 7, 7, 7}; + EdgeIndex = EdgeIndexMap[EdgeIndex]; + CacheDirection = CacheDirectionMap[IndexVertexA]; + } + if (ValueAtB.IsNull()) + { + checkVoxelSlow(!ValueAtA.IsNull()); + static uint8 EdgeIndexMap[10] = {0, 1, 2, 1, 0, 2, 0, 7, 7, 7}; + EdgeIndex = EdgeIndexMap[EdgeIndex]; + CacheDirection = CacheDirectionMap[IndexVertexB]; + } + } + const bool bIsVertexCached = ((ValidityMask & CacheDirection) == CacheDirection); + + if (bIsVertexCached) + { + checkVoxelSlow(!(CacheDirection & 0x08) && !(CacheDirection & 0x04)); + + const bool XIsDifferent = !!(CacheDirection & 0x01); + const bool YIsDifferent = !!(CacheDirection & 0x02); + + VertexIndex = Cache2D[GetCacheIndex(EdgeIndex, LX - XIsDifferent, LY - YIsDifferent)]; + checkVoxelSlow(-1 <= VertexIndex && VertexIndex < Vertices.Num()); + } + + if (!bIsVertexCached || VertexIndex == -1) + { + FVector IntersectionPoint; + FIntVector MaterialPosition; + + const bool bIsLowResChunk = EdgeIndex == 7 || EdgeIndex == 8 || EdgeIndex == 9; + + if (EdgeIndex == 0 || EdgeIndex == 1 || EdgeIndex == 2 || EdgeIndex == 7) + { + if (ValueAtA.IsNull()) + { + const auto P = Local2DToGlobal(PositionA.X, PositionA.Y, 0); + IntersectionPoint = FVector(P); + MaterialPosition = P; + } + else + { + checkVoxelSlow(ValueAtB.IsNull()); + const auto P = Local2DToGlobal(PositionB.X, PositionB.Y, 0); + IntersectionPoint = FVector(P); + MaterialPosition = P; + } + } + else + { + const bool bIsAlongX = EdgeIndex == 3 || EdgeIndex == 4 || EdgeIndex == 8; + const bool bIsAlongY = EdgeIndex == 5 || EdgeIndex == 6 || EdgeIndex == 9; + + checkVoxelSlow((bIsAlongX && !bIsAlongY) || (!bIsAlongX && bIsAlongY)); + + int32 Min = bIsAlongX ? PositionA.X : PositionA.Y; + int32 Max = bIsAlongX ? PositionB.X : PositionB.Y; + + FVoxelValue ValueAtACopy = ValueAtA; + FVoxelValue ValueAtBCopy = ValueAtB; + + while (Max - Min != 1) + { + checkError((Max + Min) % 2 == 0); + const int32 Middle = (Max + Min) / 2; + + FVoxelValue ValueAtMiddle = MESHER_TIME_RETURN_VALUES(1, GetValue( + bIsAlongX ? Middle : PositionA.X, + bIsAlongY ? Middle : PositionA.Y, + bIsLowResChunk ? LOD : HalfLOD)); + + if (ValueAtACopy.IsEmpty() == ValueAtMiddle.IsEmpty()) + { + // If min and middle have same sign + Min = Middle; + ValueAtACopy = ValueAtMiddle; + } + else + { + // If max and middle have same sign + Max = Middle; + ValueAtBCopy = ValueAtMiddle; + } + + checkError(Min <= Max); + } + + const float Alpha = ValueAtACopy.ToFloat() / (ValueAtACopy.ToFloat() - ValueAtBCopy.ToFloat()); + checkError(!FMath::IsNaN(Alpha) && FMath::IsFinite(Alpha)); + + const FIntVector GlobalMin = Local2DToGlobal( + bIsAlongX ? Min : PositionA.X, + bIsAlongY ? Min : PositionA.Y, + 0); + const FIntVector GlobalMax = Local2DToGlobal( + bIsAlongX ? Max : PositionA.X, + bIsAlongY ? Max : PositionA.Y, + 0); + + IntersectionPoint = FMath::Lerp(FVector(GlobalMin), FVector(GlobalMax), Alpha); + + // Get intersection material + if (!ValueAtACopy.IsEmpty()) + { + checkVoxelSlow(ValueAtBCopy.IsEmpty()); + MaterialPosition = GlobalMin; + } + else + { + checkVoxelSlow(!ValueAtBCopy.IsEmpty()); + MaterialPosition = GlobalMax; + } + } + + VertexIndex = Vertices.Num(); + Vertices.Emplace(T(IntersectionPoint, MaterialPosition, bIsLowResChunk)); + + // If own vertex, save it + if (CacheDirection & 0x08) + { + Cache2D[GetCacheIndex(EdgeIndex, LX, LY)] = VertexIndex; + } + } + + VertexIndices[i] = VertexIndex; + checkVoxelSlow(0 <= VertexIndex && VertexIndex < Vertices.Num()); + } + + // Add triangles + // 3 vertex per triangle + const int32 NumIndices = 3 * CellData.GetTriangleCount(); + if (bFlip) + { + for (int32 Index = 0; Index < NumIndices; Index += 3) + { + Indices.Add(VertexIndices[CellData.vertexIndex[NumIndices - 1 - (Index + 0)]]); + Indices.Add(VertexIndices[CellData.vertexIndex[NumIndices - 1 - (Index + 1)]]); + Indices.Add(VertexIndices[CellData.vertexIndex[NumIndices - 1 - (Index + 2)]]); + } + } + else + { + for (int32 Index = 0; Index < NumIndices; Index += 3) + { + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 0]]); + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 1]]); + Indices.Add(VertexIndices[CellData.vertexIndex[Index + 2]]); + } + } + } + } + } + return true; +} + +TVoxelSharedPtr FVoxelMarchingCubeTransitionsMesher::CreateFullChunkImpl(FVoxelMesherTimes& Times) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + struct FLocalVertex + { + FVector Position; + FIntVector MaterialPosition; + bool bNeedToTranslateVertex; + + FLocalVertex() = default; + FORCEINLINE FLocalVertex(const FVector& Position, const FIntVector& MaterialPosition, bool bNeedToTranslateVertex) + : Position(Position) + , MaterialPosition(MaterialPosition) + , bNeedToTranslateVertex(bNeedToTranslateVertex) + { + } + }; + + TArray Indices; + TArray Vertices; + + if (!CreateGeometryTemplate(Times, Indices, Vertices)) + { + return {}; + } + + TArray MesherVertices = FMarchingCubeHelpers::CreateMesherVertices(Vertices); + + MESHER_TIME_MATERIALS(MesherVertices.Num(), FMarchingCubeHelpers::ComputeMaterials(*this, MesherVertices, Vertices)); + MESHER_TIME(Normals, FMarchingCubeHelpers::ComputeNormals(*this, MesherVertices, Indices)); + + UnlockData(); + + MESHER_TIME(UVs, FMarchingCubeHelpers::ComputeUVs(*this, MesherVertices)); + + // Can't translate if we don't have valid normals + if (Settings.NormalConfig != EVoxelNormalConfig::FlatNormal && + Settings.NormalConfig != EVoxelNormalConfig::NoNormal) + { + VOXEL_ASYNC_SCOPE_COUNTER("Translate Vertices"); + for (int32 Index = 0; Index < Vertices.Num(); Index++) + { + if (Vertices[Index].bNeedToTranslateVertex) + { + auto& Vertex = MesherVertices[Index]; + Vertex.Position = FVoxelMesherUtilities::GetTranslatedTransvoxel(Vertex.Position, Vertex.Normal, TransitionsMask, LOD); + } + } + } + + // Important: sanitize AFTER translating! + FVoxelMesherUtilities::SanitizeMesh(Indices, MesherVertices); + + return MESHER_TIME_RETURN(CreateChunk, FVoxelMesherUtilities::CreateChunkFromVertices(Settings, LOD, MoveTemp(Indices), MoveTemp(MesherVertices))); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE int32 FVoxelMarchingCubeTransitionsMesher::GetCacheIndex(int32 EdgeIndex, int32 LX, int32 LY) +{ + checkVoxelSlow(0 <= LX && LX < RENDER_CHUNK_SIZE); + checkVoxelSlow(0 <= LY && LY < RENDER_CHUNK_SIZE); + checkVoxelSlow(0 <= EdgeIndex && EdgeIndex < TRANSITION_EDGE_INDEX_COUNT); + return EdgeIndex + LX * TRANSITION_EDGE_INDEX_COUNT + LY * TRANSITION_EDGE_INDEX_COUNT * RENDER_CHUNK_SIZE; +} + +template +FORCEINLINE FVoxelValue FVoxelMarchingCubeTransitionsMesher::GetValue(int32 X, int32 Y, int32 InLOD) const +{ + const FIntVector GlobalPosition = Local2DToGlobal(X, Y, 0); + return Accelerator->Get(ChunkPosition + GlobalPosition, InLOD); +} + +template +FORCEINLINE FIntVector FVoxelMarchingCubeTransitionsMesher::Local2DToGlobal(int32 X, int32 Y, int32 Z) const +{ + switch (Direction) + { + case EVoxelDirectionFlag::XMin: + return { Z, X, Y }; + case EVoxelDirectionFlag::XMax: + return { Size - Z, Y, X }; + case EVoxelDirectionFlag::YMin: + return { Y, Z, X }; + case EVoxelDirectionFlag::YMax: + return { X, Size - Z, Y }; + case EVoxelDirectionFlag::ZMin: + return { X, Y, Z }; + case EVoxelDirectionFlag::ZMax: + return { Y, X, Size - Z }; + default: + check(false); + return {}; + } +} + +#undef checkError \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.h new file mode 100644 index 00000000..d07651b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.h @@ -0,0 +1,98 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelRender/Meshers/VoxelMesher.h" + +#define CHUNK_SIZE_WITH_END_EDGE (RENDER_CHUNK_SIZE + 1) +#define CHUNK_SIZE_WITH_NORMALS (RENDER_CHUNK_SIZE + 3) + +#define EDGE_INDEX_COUNT 4 + +class FVoxelMarchingCubeMesher : public FVoxelMesher +{ +public: + using FVoxelMesher::FVoxelMesher; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const override final; + virtual FVoxelIntBox GetBoundsToLock() const override final; + + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) override final; + virtual void CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) override final; + +public: + // For GetGradient template + FORCEINLINE FVoxelValue GetValue(int32 X, int32 Y, int32 Z, int32 InLOD) const + { + checkVoxelSlow(LOD == 0); + checkVoxelSlow(InLOD == 0); + checkVoxelSlow(-1 <= X && X < CHUNK_SIZE_WITH_NORMALS - 1); + checkVoxelSlow(-1 <= Y && Y < CHUNK_SIZE_WITH_NORMALS - 1); + checkVoxelSlow(-1 <= Z && Z < CHUNK_SIZE_WITH_NORMALS - 1); + return CachedValues[(X + 1) + (Y + 1) * CHUNK_SIZE_WITH_NORMALS + (Z + 1) * CHUNK_SIZE_WITH_NORMALS * CHUNK_SIZE_WITH_NORMALS]; + } + +private: + // Use LOD0 size as it's bigger + using FCachedValues = TVoxelStaticArray; + using FCache = TVoxelStaticArray; + + TUniquePtr CachedValuesStorage = MakeUnique(); + TUniquePtr CacheStorageA = MakeUnique(); + TUniquePtr CacheStorageB = MakeUnique(); + + TUniquePtr Accelerator; + + FVoxelValue* RESTRICT const CachedValues = CachedValuesStorage->GetData(); + + // Cache to get index of already created vertices + int32* RESTRICT CurrentCache = CacheStorageA->GetData(); + int32* RESTRICT OldCache = CacheStorageB->GetData(); + +private: + // T: will be created as T(IntersectionPoint, MaterialPosition) + template + bool CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); + +private: + static int32 GetCacheIndex(int32 EdgeIndex, int32 LX, int32 LY); + + friend class FMarchingCubeHelpers; +}; + +#define TRANSITION_EDGE_INDEX_COUNT 10 + +class FVoxelMarchingCubeTransitionsMesher : public FVoxelTransitionsMesher +{ +public: + using FVoxelTransitionsMesher::FVoxelTransitionsMesher; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const override final; + virtual FVoxelIntBox GetBoundsToLock() const override final; + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) override final; + +private: + TUniquePtr Accelerator; + TVoxelStaticArray Cache2D; + +private: + // T: will be created as T(IntersectionPoint, MaterialPosition, bNeedToTranslate) + template + bool CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); + template + bool CreateGeometryForDirection(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); + +private: + static int32 GetCacheIndex(int32 EdgeIndex, int32 LX, int32 LY); + template + FVoxelValue GetValue(int32 X, int32 Y, int32 InLOD) const; + template + FIntVector Local2DToGlobal(int32 X, int32 Y, int32 Z) const; + + friend class FMarchingCubeHelpers; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.cpp new file mode 100644 index 00000000..a09d238d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.cpp @@ -0,0 +1,436 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/Meshers/VoxelMesher.h" +#include "VoxelRender/VoxelMesherAsyncWork.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelUtilities/VoxelStatsUtilities.h" + +#include "Async/Async.h" +#include "Engine/World.h" + +static TAutoConsoleVariable CVarDoNotSkipEmptyChunks( + TEXT("voxel.mesher.DoNotSkipEmptyChunks"), + 0, + TEXT("If true, all chunks will be computed"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelMesherStats +{ + static FVoxelMesherStats Singleton; + + struct FChunkStats + { + int32 LOD = 0; + double Time = 0; + FVoxelMesherTimes Times; + }; + + FCriticalSection Section; + struct FStats + { + TArray NormalStats; + TArray TransitionsStats; + TArray GeometryStats; + }; + TMap, FStats> StatsMap; + + static void Clean() + { + Singleton.StatsMap.Remove(nullptr); + } + static void Report(TWeakObjectPtr World, int32 LOD, double TotalTime, const FVoxelMesherTimes& Times, bool bIsTransition, bool bIsGeometry) + { +#if ENABLE_MESHER_STATS + check(!bIsGeometry || !bIsTransition); + FScopeLock Lock(&Singleton.Section); + auto& LocalStats = Singleton.StatsMap.FindOrAdd(World); + (bIsGeometry ? LocalStats.GeometryStats : bIsTransition ? LocalStats.TransitionsStats : LocalStats.NormalStats).Add(FChunkStats{ LOD, TotalTime, Times }); +#endif + } + static void Clear(UWorld* World) + { + Clean(); + + FScopeLock Lock(&Singleton.Section); + auto& LocalStats = Singleton.StatsMap.FindOrAdd(World); + LocalStats.NormalStats.Empty(); + LocalStats.TransitionsStats.Empty(); + } + static void PrintStats(UWorld* World) + { + Clean(); + + FScopeLock Lock(&Singleton.Section); + + double TotalValuesTime = 0; + double TotalMaterialsTime = 0; + uint64 TotalValuesAccesses = 0; + uint64 TotalMaterialsAccesses = 0; + + double TotalDistanceFieldsTime = 0; + + const auto Print = [&](const TArray& Stats) + { + struct FMean + { + double TotalTime = 0; + int32 Count = 0; + + double ValuesTime = 0; + double MaterialsTime = 0; + + double NormalsTime = 0; + double UVsTime = 0; + double CreateChunkTime = 0; + + double FinishCreatingChunkTime = 0; + double DistanceFieldTime = 0; + + uint64 ValuesAccesses = 0; + uint64 MaterialsAccesses = 0; + }; + TMap LODToMeans; + double GlobalTotalTime = 0; + for (auto& Stat : Stats) + { + auto& Mean = LODToMeans.FindOrAdd(Stat.LOD); + Mean.TotalTime += Stat.Time; + Mean.Count++; + + Mean.ValuesTime += FPlatformTime::ToSeconds64(Stat.Times._Values); + Mean.MaterialsTime += FPlatformTime::ToSeconds64(Stat.Times._Materials); + + Mean.NormalsTime += FPlatformTime::ToSeconds64(Stat.Times.Normals); + Mean.UVsTime += FPlatformTime::ToSeconds64(Stat.Times.UVs); + Mean.CreateChunkTime += FPlatformTime::ToSeconds64(Stat.Times.CreateChunk); + + Mean.FinishCreatingChunkTime += FPlatformTime::ToSeconds64(Stat.Times.FinishCreatingChunk); + Mean.DistanceFieldTime += FPlatformTime::ToSeconds64(Stat.Times.DistanceField); + + Mean.ValuesAccesses += Stat.Times._ValuesAccesses; + Mean.MaterialsAccesses += Stat.Times._MaterialsAccesses; + + GlobalTotalTime += Stat.Time; + } + + LODToMeans.KeySort(TLess()); + + LOG_VOXEL(Log, TEXT("\tLOD; Chunks (%%) ; Total (%%) ; Avg ; Values (%%) , Per Voxel ; Materials (%%) , Per Voxel ; Normals (%%) ; UVs (%%) ; CreateChunk (%%) ; FinishCreatingChunk (%%); DistanceFields (%%)")); + for (auto& It : LODToMeans) + { + auto& V = It.Value; + + TotalValuesTime += V.ValuesTime; + TotalMaterialsTime += V.MaterialsTime; + TotalValuesAccesses += V.ValuesAccesses; + TotalMaterialsAccesses += V.MaterialsAccesses; + + TotalDistanceFieldsTime += V.DistanceFieldTime; + + LOG_VOXEL(Log, TEXT("\t %2d: %6d (%5.2f%%); %8.3fs (%5.2f%%); %8.3fms; %8.3fs (%5.2f%%), %8.1fns; %8.3fs (%5.2f%%), %8.1fns; %8.3fs (%5.2f%%); %8.3fs (%5.2f%%); %8.3fs (%5.2f%%); %8.3fs (%5.2f%%); %8.3fs (%5.2f%%)"), + It.Key, + V.Count, + V.Count / double(Stats.Num()) * 100, + V.TotalTime, + V.TotalTime / GlobalTotalTime * 100, + V.TotalTime / It.Value.Count * 1000, + + V.ValuesTime, + V.ValuesTime / V.TotalTime * 100, + V.ValuesAccesses > 0 ? V.ValuesTime / V.ValuesAccesses * 1e9 : 0, + + V.MaterialsTime, + V.MaterialsTime / V.TotalTime * 100, + V.MaterialsAccesses > 0 ? V.MaterialsTime / V.MaterialsAccesses * 1e9 : 0, + + V.NormalsTime, + V.NormalsTime / V.TotalTime * 100, + + V.UVsTime, + V.UVsTime / V.TotalTime * 100, + + V.CreateChunkTime, + V.CreateChunkTime / V.TotalTime * 100, + + V.FinishCreatingChunkTime, + V.FinishCreatingChunkTime / V.TotalTime * 100, + + V.DistanceFieldTime, + V.DistanceFieldTime / V.TotalTime * 100); + } + + return GlobalTotalTime; + }; + + auto& LocalStats = Singleton.StatsMap.FindOrAdd(World); + LOG_VOXEL(Log, TEXT("###############################################################################")); + LOG_VOXEL(Log, TEXT("################################# Voxel Stats #################################")); + LOG_VOXEL(Log, TEXT("###############################################################################")); + LOG_VOXEL(Log, TEXT("World: %s"), *World->GetName()); + LOG_VOXEL(Log, TEXT("Normal Chunks:")); + const double NormalTime = Print(LocalStats.NormalStats); + LOG_VOXEL(Log, TEXT("Transitions Chunks:")); + const double TransitionsTime = Print(LocalStats.TransitionsStats); + LOG_VOXEL(Log, TEXT("Geometry Chunks (Spawners):")); + const double GeometryTime = Print(LocalStats.GeometryStats); + LOG_VOXEL(Log, TEXT("###############################################################################")); + LOG_VOXEL(Log, TEXT("################################### Summary ###################################")); + LOG_VOXEL(Log, TEXT("###############################################################################")); + LOG_VOXEL(Log, TEXT("Main Time: %fs"), NormalTime); + LOG_VOXEL(Log, TEXT("Transitions Time: %fs"), TransitionsTime); + LOG_VOXEL(Log, TEXT("Geometry Time: %fs"), GeometryTime); + LOG_VOXEL(Log, TEXT("------------------------------")); + const double TotalTime = NormalTime + TransitionsTime + GeometryTime; + LOG_VOXEL(Log, TEXT("Total Time: %fs"), TotalTime); + LOG_VOXEL(Log, TEXT("Values Time: %3.2f%% of total time (%fs)"), 100 * TotalValuesTime / TotalTime, TotalValuesTime); + LOG_VOXEL(Log, TEXT("Distance Fields Time: %3.2f%% of total time (%fs)"), 100 * TotalDistanceFieldsTime / TotalTime, TotalDistanceFieldsTime); + LOG_VOXEL(Log, TEXT("Transitions Time: %3.2f%% of Main + Transitions"), 100 * TransitionsTime / (NormalTime + TransitionsTime)); + LOG_VOXEL(Log, TEXT("------------------------------")); + LOG_VOXEL(Log, TEXT("Values: %llu reads in %fs, avg %.1fns/voxel"), TotalValuesAccesses, TotalValuesTime, TotalValuesTime / TotalValuesAccesses * 1e9); + LOG_VOXEL(Log, TEXT("Materials: %llu reads in %fs, avg %.1fns/voxel"), TotalMaterialsAccesses, TotalMaterialsTime, TotalMaterialsTime / TotalMaterialsAccesses * 1e9); + } +}; + +static FAutoConsoleCommandWithWorld ClearMesherStatsCmd( + TEXT("voxel.mesher.ClearStats"), + TEXT("Clear the mesher stats"), + FConsoleCommandWithWorldDelegate::CreateStatic(&FVoxelMesherStats::Clear)); + +static FAutoConsoleCommandWithWorld PrintMesherStatsCmd( + TEXT("voxel.mesher.PrintStats"), + TEXT("Print the mesher stats"), + FConsoleCommandWithWorldDelegate::CreateStatic(&FVoxelMesherStats::PrintStats)); + +FVoxelMesherStats FVoxelMesherStats::Singleton; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMesherBase::FVoxelMesherBase( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings, + bool bIsTransitions) + : LOD(LOD) + , Step(1 << LOD) + , Size(RENDER_CHUNK_SIZE << LOD) + , ChunkPosition(ChunkPosition) + , Settings(Settings) + , Data(*Settings.Data) + , bIsTransitions(bIsTransitions) +{ +} + +FVoxelMesherBase::~FVoxelMesherBase() +{ +} + +void FVoxelMesherBase::UnlockData() +{ + Data.Unlock(MoveTemp(LockInfo)); +} + +void FVoxelMesherBase::LockData() +{ + LockInfo = Data.Lock(EVoxelLockType::Read, GetBoundsToLock(), "Mesher"); +} + +bool FVoxelMesherBase::IsEmpty() const +{ + const FVoxelIntBox Bounds = GetBoundsToCheckIsEmptyOn(); + const bool bIsEmpty = CVarDoNotSkipEmptyChunks.GetValueOnAnyThread() != 0 ? false : Data.IsEmpty(Bounds, LOD); + + if (!bIsTransitions) + { + VOXEL_ASYNC_SCOPE_COUNTER("DebugIsEmpty"); + const FVoxelIntBox BoundsCopy(Bounds.Min, Bounds.Max - FIntVector(Step)); + AsyncTask(ENamedThreads::GameThread, [WeakDebug = MakeVoxelWeakPtr(Settings.DebugManager), BoundsCopy, bIsEmpty] + { + auto Debug = WeakDebug.Pin(); + if (Debug.IsValid()) + { + Debug->ReportChunkEmptyState(BoundsCopy, bIsEmpty); + } + }); + } + + return bIsEmpty; +} + +TVoxelSharedPtr FVoxelMesherBase::CreateEmptyChunk() const +{ + const auto Chunk = MakeVoxelShared(); + // We need to make sure the chunk has the right configuration, even if it's empty + // This is because else, we might end up with a MainChunk that's single, but with a TransitionChunk that's not + if (Settings.MaterialConfig == EVoxelMaterialConfig::RGB) + { + Chunk->SetIsSingle(true); + Chunk->CreateSingleBuffers(); + } + else + { + Chunk->SetIsSingle(false); + } + return Chunk; +} + +void FVoxelMesherBase::FinishCreatingChunk(FVoxelChunkMesh& Chunk) const +{ + if (Settings.bOptimizeIndices) + { + Chunk.IterateBuffers([](auto& Buffer) { Buffer.OptimizeIndices(); }); + } + Chunk.IterateBuffers([](FVoxelChunkMeshBuffers& Buffer) { Buffer.Shrink(); }); + Chunk.IterateBuffers([](FVoxelChunkMeshBuffers& Buffer) { Buffer.ComputeBounds(); }); + Chunk.IterateBuffers([](FVoxelChunkMeshBuffers& Buffer) { Buffer.Guid = FGuid::NewGuid(); }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMesher::FVoxelMesher( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings) + : FVoxelMesherBase(LOD, ChunkPosition, Settings, false) +{ +} + +TVoxelSharedPtr FVoxelMesher::CreateFullChunk() +{ + VOXEL_SCOPE_COUNTER_FORMAT("Creating Chunk LOD=%d", LOD); + + { + VOXEL_ASYNC_SCOPE_COUNTER("InitArea"); + Data.Generator->InitArea(FVoxelIntBox(ChunkPosition, ChunkPosition + Step * RENDER_CHUNK_SIZE), LOD); + } + + LockData(); + + TVoxelSharedPtr Chunk; + if (IsEmpty()) + { + Chunk = CreateEmptyChunk(); + FinishCreatingChunk(*Chunk); + UnlockData(); + } + else + { + const double StartTime = FPlatformTime::Seconds(); + FVoxelMesherTimes Times; + + Chunk = CreateFullChunkImpl(Times); + check(!LockInfo.IsValid()); + + if (Chunk.IsValid()) + { + { + MESHER_TIME_SCOPE(FinishCreatingChunk) + FinishCreatingChunk(*Chunk); + } + + if (LOD <= Settings.MaxDistanceFieldLOD) + { + MESHER_TIME_SCOPE(DistanceField) + Chunk->BuildDistanceField(LOD, ChunkPosition, Data, Settings); + } + } + + const double EndTime = FPlatformTime::Seconds(); + FVoxelMesherStats::Report(Settings.World, LOD, EndTime - StartTime, Times, false, false); + } + + return Chunk; +} + +void FVoxelMesher::CreateGeometry(TArray& Indices, TArray& Vertices) +{ + VOXEL_SCOPE_COUNTER_FORMAT("Creating Geometry LOD=%d", LOD); + + { + VOXEL_ASYNC_SCOPE_COUNTER("InitArea"); + Data.Generator->InitArea(FVoxelIntBox(ChunkPosition, ChunkPosition + Step * RENDER_CHUNK_SIZE), LOD); + } + + LockData(); + + if (IsEmpty()) + { + UnlockData(); + } + else + { + const double StartTime = FPlatformTime::Seconds(); + FVoxelMesherTimes Times; + CreateGeometryImpl(Times, Indices, Vertices); + check(!LockInfo.IsValid()); + const double EndTime = FPlatformTime::Seconds(); + FVoxelMesherStats::Report(Settings.World, LOD, EndTime - StartTime, Times, false, true); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelTransitionsMesher::FVoxelTransitionsMesher( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings, + uint8 TransitionsMask) + : FVoxelMesherBase(LOD, ChunkPosition, Settings, true) + , TransitionsMask(TransitionsMask) + , HalfLOD(LOD - 1) + , HalfStep(Step / 2) +{ +} + +TVoxelSharedPtr FVoxelTransitionsMesher::CreateFullChunk() +{ + VOXEL_SCOPE_COUNTER_FORMAT("Creating Transitions LOD=%d Num=%u", LOD, FVoxelUtilities::Popc(TransitionsMask)); + + check(TransitionsMask); + + LockData(); + + TVoxelSharedPtr Chunk; + if (IsEmpty()) + { + Chunk = CreateEmptyChunk(); + FinishCreatingChunk(*Chunk); + UnlockData(); + } + else + { + const double StartTime = FPlatformTime::Seconds(); + FVoxelMesherTimes Times; + + Chunk = CreateFullChunkImpl(Times); + check(!LockInfo.IsValid()); + + if (Chunk.IsValid()) + { + MESHER_TIME_SCOPE(FinishCreatingChunk) + FinishCreatingChunk(*Chunk); + } + + const double EndTime = FPlatformTime::Seconds(); + FVoxelMesherStats::Report(Settings.World, LOD, EndTime - StartTime, Times, true, false); + } + + return Chunk; +} + +void FVoxelTransitionsMesher::CreateGeometry(TArray& Indices, TArray& Vertices) +{ + unimplemented(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.h new file mode 100644 index 00000000..89b7771a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.h @@ -0,0 +1,148 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" + +struct FVoxelRendererSettings; +struct FVoxelChunkMesh; +class FVoxelData; +class FVoxelDataLockInfo; + +#if ENABLE_MESHER_STATS +struct FVoxelScopedMesherTime +{ + const uint64 StartCycle = FPlatformTime::Cycles64(); + uint64& Cycles; + + FORCEINLINE explicit FVoxelScopedMesherTime(uint64& Cycles) + : Cycles(Cycles) + { + } + FORCEINLINE ~FVoxelScopedMesherTime() + { + Cycles += FPlatformTime::Cycles64() - StartCycle; + } +}; + +#define MESHER_TIME_SCOPE(Time) FVoxelScopedMesherTime LocalScope(Times.Time); +#define MESHER_TIME(Time, X) { FVoxelScopedMesherTime LocalScope(Times.Time); X; } +#define MESHER_TIME_RETURN(Time, X) [&]() { FVoxelScopedMesherTime LocalScope(Times.Time); return X; }() + +#define MESHER_TIME_SCOPE_VALUES(Count) FVoxelScopedMesherTime LocalScope(Times._Values); Times._ValuesAccesses += Count; +#define MESHER_TIME_VALUES(Count, X) { FVoxelScopedMesherTime LocalScope(Times._Values); Times._ValuesAccesses += Count; X; } +#define MESHER_TIME_RETURN_VALUES(Count, X) [&]() { FVoxelScopedMesherTime LocalScope(Times._Values); Times._ValuesAccesses += Count; return X; }() + +#define MESHER_TIME_SCOPE_MATERIALS(Count) FVoxelScopedMesherTime LocalScope(Times._Materials); Times._MaterialsAccesses += Count; +#define MESHER_TIME_MATERIALS(Count, X) { FVoxelScopedMesherTime LocalScope(Times._Materials); Times._MaterialsAccesses += Count; X; } +#define MESHER_TIME_RETURN_MATERIALS(Count, X) [&]() { FVoxelScopedMesherTime LocalScope(Times._Materials); Times._MaterialsAccesses += Count; return X; }() +#else +#define MESHER_TIME_SCOPE(Time) +#define MESHER_TIME(Time, X) X +#define MESHER_TIME_RETURN(Time, X) X + +#define MESHER_TIME_SCOPE_VALUES(Count) +#define MESHER_TIME_VALUES(Count, X) X +#define MESHER_TIME_RETURN_VALUES(Count, X) X + +#define MESHER_TIME_SCOPE_MATERIALS(Count) +#define MESHER_TIME_MATERIALS(Count, X) X +#define MESHER_TIME_RETURN_MATERIALS(Count, X) X +#endif + +// All times are in cycles +struct FVoxelMesherTimes +{ + uint64 _Values = 0; + uint64 _Materials = 0; + uint64 _ValuesAccesses = 0; + uint64 _MaterialsAccesses = 0; + + uint64 Normals = 0; + uint64 UVs = 0; + uint64 CreateChunk = 0; + + uint64 FinishCreatingChunk = 0; + uint64 DistanceField = 0; +}; + +class FVoxelMesherBase +{ +public: + const int32 LOD; + const int32 Step; + const int32 Size; + const FIntVector ChunkPosition; + const FVoxelRendererSettings& Settings; + const FVoxelData& Data; + const bool bIsTransitions; + + FVoxelMesherBase( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings, + bool bIsTransitions); + virtual ~FVoxelMesherBase(); + + virtual TVoxelSharedPtr CreateFullChunk() = 0; + virtual void CreateGeometry(TArray& Indices, TArray& Vertices) = 0; + + TVoxelSharedPtr CreateEmptyChunk() const; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const = 0; + virtual FVoxelIntBox GetBoundsToLock() const = 0; + + void UnlockData(); + +private: + TUniquePtr LockInfo; + + void LockData(); + bool IsEmpty() const; + void FinishCreatingChunk(FVoxelChunkMesh& Chunk) const; + + friend class FVoxelMesher; + friend class FVoxelTransitionsMesher; +}; + +class FVoxelMesher : public FVoxelMesherBase +{ +public: + FVoxelMesher( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings); + + virtual TVoxelSharedPtr CreateFullChunk() override final; + virtual void CreateGeometry(TArray& Indices, TArray& Vertices) override final; + +protected: + // Need to call UnlockData + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) = 0; + // Need to call UnlockData + virtual void CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) = 0; +}; + +class FVoxelTransitionsMesher : public FVoxelMesherBase +{ +public: + const uint8 TransitionsMask; + const int32 HalfLOD; + const int32 HalfStep; + + FVoxelTransitionsMesher( + int32 LOD, + const FIntVector& ChunkPosition, + const FVoxelRendererSettings& Settings, + uint8 TransitionsMask); + + virtual TVoxelSharedPtr CreateFullChunk() override final; + virtual void CreateGeometry(TArray& Indices, TArray& Vertices) override final; + +protected: + // Need to call UnlockData + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.cpp new file mode 100644 index 00000000..e199238e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.cpp @@ -0,0 +1,382 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/Meshers/VoxelMesherUtilities.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelChunkMesh.h" + +FORCEINLINE int32 AddVertexToBuffer( + const FVoxelMesherVertex& Vertex, + FVoxelChunkMeshBuffers& Buffer, + const FVoxelRendererSettings& Settings, + EVoxelMaterialConfig MaterialConfig, + const FColor* Color = nullptr, + const FVector2D* UV = nullptr) +{ + const int32 Index = Buffer.Positions.Emplace(Vertex.Position); + if (Settings.bRenderWorld) + { + const auto GetColor = [&](FColor InColor) + { + if (Settings.bSRGBColors) + { + // Convert the color from SRGB to linear + return FLinearColor(InColor).ToFColor(false); + } + else + { + // Use as-is + return InColor; + } + }; + + Buffer.Normals.Emplace(Vertex.Normal); + Buffer.Tangents.Emplace(Vertex.Tangent); + Buffer.TextureCoordinates[0].Emplace(Vertex.TextureCoordinate); + + if (MaterialConfig == EVoxelMaterialConfig::MultiIndex) + { + check(Color && UV); + Buffer.Colors.Emplace(GetColor(*Color)); + Buffer.TextureCoordinates[1].Emplace(*UV); + if (VOXEL_MATERIAL_ENABLE_UV2) Buffer.TextureCoordinates[2].Emplace(Vertex.Material.GetUV_AsFloat(2)); + if (VOXEL_MATERIAL_ENABLE_UV3) Buffer.TextureCoordinates[3].Emplace(Vertex.Material.GetUV_AsFloat(3)); + } + else + { + Buffer.Colors.Emplace(GetColor(Vertex.Material.GetColor())); + if (VOXEL_MATERIAL_ENABLE_UV0) Buffer.TextureCoordinates[1].Emplace(Vertex.Material.GetUV_AsFloat(0)); + if (VOXEL_MATERIAL_ENABLE_UV1) Buffer.TextureCoordinates[2].Emplace(Vertex.Material.GetUV_AsFloat(1)); + if (VOXEL_MATERIAL_ENABLE_UV2) Buffer.TextureCoordinates[3].Emplace(Vertex.Material.GetUV_AsFloat(2)); + if (VOXEL_MATERIAL_ENABLE_UV3) Buffer.TextureCoordinates[4].Emplace(Vertex.Material.GetUV_AsFloat(3)); + } + } + return Index; +} + +inline void ReserveBuffer( + FVoxelChunkMeshBuffers& Buffer, + int32 Num, + const FVoxelRendererSettings& Settings, + EVoxelMaterialConfig MaterialConfig) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + Buffer.Positions.Reserve(Num); + if (Settings.bRenderWorld) + { + Buffer.Normals.Reserve(Num); + Buffer.Tangents.Reserve(Num); + Buffer.Colors.Reserve(Num); + + if (MaterialConfig == EVoxelMaterialConfig::MultiIndex) + { + Buffer.TextureCoordinates.SetNum(2 + VOXEL_MATERIAL_ENABLE_UV2 + VOXEL_MATERIAL_ENABLE_UV3); + Buffer.TextureCoordinates[0].Reserve(Num); + // Note: we always create the additional UV channel, else it creates issues when merging chunks + Buffer.TextureCoordinates[1].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV2) Buffer.TextureCoordinates[2].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV3) Buffer.TextureCoordinates[3].Reserve(Num); + } + else + { + Buffer.TextureCoordinates.SetNum(1 + VOXEL_MATERIAL_ENABLE_UV0 + VOXEL_MATERIAL_ENABLE_UV1 + VOXEL_MATERIAL_ENABLE_UV2 + VOXEL_MATERIAL_ENABLE_UV3); + Buffer.TextureCoordinates[0].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV0) Buffer.TextureCoordinates[1].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV1) Buffer.TextureCoordinates[2].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV2) Buffer.TextureCoordinates[3].Reserve(Num); + if (VOXEL_MATERIAL_ENABLE_UV3) Buffer.TextureCoordinates[4].Reserve(Num); + } + } +} + +TVoxelSharedPtr FVoxelMesherUtilities::CreateChunkFromVertices( + const FVoxelRendererSettings& Settings, + int32 LOD, + TArray&& Indices, + TArray&& Vertices) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + auto Chunk = MakeVoxelShared(); + + if (Settings.MaterialConfig == EVoxelMaterialConfig::RGB) + { + if (Settings.bHardColorTransitions) + { + VOXEL_ASYNC_SCOPE_COUNTER("Hard Color Transitions"); + + // Add new vertices as needed + for (int32 I = 0; I < Indices.Num(); I += 3) + { + uint32& IndexA = Indices[I + 0]; + uint32& IndexB = Indices[I + 1]; + uint32& IndexC = Indices[I + 2]; + + const FColor ColorA = Vertices[IndexA].Material.GetColor(); + const FColor ColorB = Vertices[IndexB].Material.GetColor(); + const FColor ColorC = Vertices[IndexC].Material.GetColor(); + + if (ColorA == ColorB && ColorA == ColorC) + { + continue; + } + + FColor Color; + if (ColorA == ColorB) + { + Color = ColorA; + } + else if (ColorA == ColorC) + { + Color = ColorA; + } + else if (ColorB == ColorC) + { + Color = ColorB; + } + else + { + // Deterministic way to choose between 2 colors + const auto PickColor = [](FColor A, FColor B) + { + if (A.R < B.R) return A; + if (B.R < A.R) return B; + if (A.G < B.G) return A; + if (B.G < A.G) return B; + if (A.B < B.B) return A; + if (B.B < A.B) return B; + if (A.A < B.A) return A; + if (B.A < A.A) return B; + ensureVoxelSlow(A == B); + return A; + }; + // Pick the min color between the 3 (arbitrary, but should always be the same to have consistent results) + Color = PickColor(ColorA, PickColor(ColorB, ColorC)); + } + + if (Color != ColorA) + { + auto NewVertex = Vertices[IndexA]; + NewVertex.Material.SetColor(Color); + IndexA = Vertices.Add(NewVertex); + } + if (Color != ColorB) + { + auto NewVertex = Vertices[IndexB]; + NewVertex.Material.SetColor(Color); + IndexB = Vertices.Add(NewVertex); + } + if (Color != ColorC) + { + auto NewVertex = Vertices[IndexC]; + NewVertex.Material.SetColor(Color); + IndexC = Vertices.Add(NewVertex); + } + } + + // Make sure to remove all unused vertices + RemoveUnusedVertices(Indices, Vertices); + } + + Chunk->SetIsSingle(true); + FVoxelChunkMeshBuffers& Buffers = Chunk->CreateSingleBuffers(); + + Buffers.Indices = MoveTemp(Indices); + + ReserveBuffer(Buffers, Vertices.Num(), Settings, EVoxelMaterialConfig::RGB); + for (auto& Vertex : Vertices) + { + AddVertexToBuffer(Vertex, Buffers, Settings, EVoxelMaterialConfig::RGB); + } + } + else if (Settings.MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + Chunk->SetIsSingle(false); + + TVoxelStaticArray, 256> IndicesMaps{ ForceInit }; + for (int32 I = 0; I < Indices.Num(); I += 3) + { + const int32 IndexA = Indices[I + 0]; + const int32 IndexB = Indices[I + 1]; + const int32 IndexC = Indices[I + 2]; + + const FVoxelMesherVertex& VertexA = Vertices[IndexA]; + const FVoxelMesherVertex& VertexB = Vertices[IndexB]; + const FVoxelMesherVertex& VertexC = Vertices[IndexC]; + + const uint8 MaterialIndexA = VertexA.Material.GetSingleIndex(); + const uint8 MaterialIndexB = VertexB.Material.GetSingleIndex(); + const uint8 MaterialIndexC = VertexC.Material.GetSingleIndex(); + + const uint8 MaterialIndexToUse = FMath::Min3(MaterialIndexA, MaterialIndexB, MaterialIndexC); + + FVoxelMaterialIndices MaterialIndices; + MaterialIndices.NumIndices = 1; + MaterialIndices.SortedIndices[0] = MaterialIndexToUse; + + bool bAdded; + FVoxelChunkMeshBuffers& Buffer = Chunk->FindOrAddBuffer(MaterialIndices, bAdded); + if (bAdded) + { + ReserveBuffer(Buffer, Vertices.Num(), Settings, EVoxelMaterialConfig::SingleIndex); + } + + TMap& IndicesMap = IndicesMaps[MaterialIndexToUse]; + + const auto AddVertex = [&](int32 Index, const FVoxelMesherVertex& Vertex) + { + int32 FinalIndex; + if (int32* FinalIndexPtr = IndicesMap.Find(Index)) + { + FinalIndex = *FinalIndexPtr; + } + else + { + FinalIndex = AddVertexToBuffer(Vertex, Buffer, Settings, EVoxelMaterialConfig::SingleIndex); + IndicesMap.Add(Index, FinalIndex); + } + Buffer.Indices.Emplace(FinalIndex); + }; + + AddVertex(IndexA, VertexA); + AddVertex(IndexB, VertexB); + AddVertex(IndexC, VertexC); + } + } + else + { + check(Settings.MaterialConfig == EVoxelMaterialConfig::MultiIndex); + Chunk->SetIsSingle(false); + + const int32 MaxMaterialIndices = FMath::Clamp(Settings.DynamicSettings->LODData[LOD].MaxMaterialIndices.GetValue(), 1, 6); + + constexpr int32 StaticMaxMaterialIndices = 6; + + if (Indices.Num() > 0) + { + TArray> MaterialIndices; + { + TVoxelStaticArray Strengths{ ForceInit }; + for (const auto& Vertex : Vertices) + { + const FVoxelMaterial& Material = Vertex.Material; + + const auto ClampStrength = [](float Strength) + { + // TODO + // Consider that under threshold, material won't be displayed anyways + return Strength < 0.f ? 0.f : Strength; + }; + + const float Blend0 = Material.GetMultiIndex_Blend0_AsFloat(); + const float Blend1 = Material.GetMultiIndex_Blend1_AsFloat(); + const float Blend2 = Material.GetMultiIndex_Blend2_AsFloat(); + + const TVoxelStaticArray MaterialStrengths = FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>({ Blend0, Blend1, Blend2 }); + + Strengths[Vertex.Material.GetMultiIndex_Index0()] += ClampStrength(MaterialStrengths[0]); + Strengths[Vertex.Material.GetMultiIndex_Index1()] += ClampStrength(MaterialStrengths[1]); + Strengths[Vertex.Material.GetMultiIndex_Index2()] += ClampStrength(MaterialStrengths[2]); + Strengths[Vertex.Material.GetMultiIndex_Index3()] += ClampStrength(MaterialStrengths[3]); + } + + struct FIndexStrength + { + uint8 Index = 0; + float Strength = 0; + }; + + TArray> SortedStrengths; + for (int32 Index = 0; Index < 256; Index++) + { + if (Strengths[Index] > 0.f) + { + SortedStrengths.Add({ uint8(Index), Strengths[Index] }); + } + } + SortedStrengths.Sort([](FIndexStrength A, FIndexStrength B) { return A.Strength > B.Strength; }); + + for (auto& IndexStrength : SortedStrengths) + { + if (IndexStrength.Strength == 0.f) + { + break; + } + MaterialIndices.Add(IndexStrength.Index); + if (MaterialIndices.Num() == MaxMaterialIndices) + { + break; + } + } + + if (MaterialIndices.Num() == 0) + { + MaterialIndices.Add(0); + } + MaterialIndices.Sort(); + + check(1 <= MaterialIndices.Num() && MaterialIndices.Num() <= MaxMaterialIndices); + } + + const auto MakeBuffer = [&]() -> auto& + { + FVoxelMaterialIndices VoxelMaterialIndices; + VoxelMaterialIndices.NumIndices = MaterialIndices.Num(); + VoxelMaterialIndices.SortedIndices.CopyFromArray(MaterialIndices); + + bool bAdded; + FVoxelChunkMeshBuffers& Buffer = Chunk->FindOrAddBuffer(VoxelMaterialIndices, bAdded); + check(bAdded); + ReserveBuffer(Buffer, Vertices.Num(), Settings, EVoxelMaterialConfig::MultiIndex); + + return Buffer; + }; + FVoxelChunkMeshBuffers& Buffer = MakeBuffer(); + + Buffer.Indices = MoveTemp(Indices); + + for (const FVoxelMesherVertex& Vertex : Vertices) + { + FColor Color{ ForceInit }; + FVector2D UV{ ForceInit }; + + if (MaterialIndices.Num() > 1) + { + TArray> Strengths; + { + const FVoxelMaterial& Material = Vertex.Material; + const float Blend0 = Material.GetMultiIndex_Blend0_AsFloat(); + const float Blend1 = Material.GetMultiIndex_Blend1_AsFloat(); + const float Blend2 = Material.GetMultiIndex_Blend2_AsFloat(); + + const TVoxelStaticArray MaterialStrengths = FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>({ Blend0, Blend1, Blend2 }); + + for (uint8 MaterialIndex : MaterialIndices) + { + // Consider all channels, as eg we could have 0 0 0 0 as indices but 0 1 1 as blends + Strengths.Add( + (MaterialIndex == Material.GetMultiIndex_Index0() ? MaterialStrengths[0] : 0.f) + + (MaterialIndex == Material.GetMultiIndex_Index1() ? MaterialStrengths[1] : 0.f) + + (MaterialIndex == Material.GetMultiIndex_Index2() ? MaterialStrengths[2] : 0.f) + + (MaterialIndex == Material.GetMultiIndex_Index3() ? MaterialStrengths[3] : 0.f)); + } + } + + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Dynamic>(Strengths); + + if (Alphas.Num() > 0) Color.R = FVoxelUtilities::FloatToUINT8(Alphas[0]); + if (Alphas.Num() > 1) Color.G = FVoxelUtilities::FloatToUINT8(Alphas[1]); + if (Alphas.Num() > 2) Color.B = FVoxelUtilities::FloatToUINT8(Alphas[2]); + if (Alphas.Num() > 3) UV.X = Alphas[3]; + if (Alphas.Num() > 4) UV.Y = Alphas[4]; + } + + Color.A = Vertex.Material.GetMultiIndex_Wetness(); + + AddVertexToBuffer(Vertex, Buffer, Settings, EVoxelMaterialConfig::MultiIndex, &Color, &UV); + } + } + } + + return Chunk; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.h new file mode 100644 index 00000000..b492d9e9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.h @@ -0,0 +1,189 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelMaterial.h" +#include "VoxelDirection.h" +#include "VoxelRender/VoxelProcMeshTangent.h" + +struct FVoxelRendererSettings; +struct FVoxelChunkMesh; + +struct FVoxelMesherVertex +{ + FVector Position; + FVector Normal; + FVoxelProcMeshTangent Tangent; + FVector2D TextureCoordinate; + FVoxelMaterial Material; +}; + +namespace FVoxelMesherUtilities +{ + TVoxelSharedPtr CreateChunkFromVertices( + const FVoxelRendererSettings& Settings, + int32 LOD, + TArray&& Indices, + TArray&& Vertices); + + inline FVector GetTranslatedTransvoxel(const FVector& Vertex, const FVector& Normal, uint8 TransitionsMask, uint8 LOD) + { + const int32 Step = 1 << LOD; + const int32 Size = RENDER_CHUNK_SIZE << LOD; + + const float LowerBound = Step; + const float UpperBound = (RENDER_CHUNK_SIZE - 1) * Step; + + if ((LowerBound <= Vertex.X && Vertex.X <= UpperBound) && + (LowerBound <= Vertex.Y && Vertex.Y <= UpperBound) && + (LowerBound <= Vertex.Z && Vertex.Z <= UpperBound)) + { + // Fast exit + return Vertex; + } + + if ((Vertex.X == 0.f && !(TransitionsMask & EVoxelDirectionFlag::XMin)) || (Vertex.X == Size && !(TransitionsMask & EVoxelDirectionFlag::XMax)) || + (Vertex.Y == 0.f && !(TransitionsMask & EVoxelDirectionFlag::YMin)) || (Vertex.Y == Size && !(TransitionsMask & EVoxelDirectionFlag::YMax)) || + (Vertex.Z == 0.f && !(TransitionsMask & EVoxelDirectionFlag::ZMin)) || (Vertex.Z == Size && !(TransitionsMask & EVoxelDirectionFlag::ZMax))) + { + // Can't translate when on a corner + return Vertex; + } + + FVector Delta(0.f); + + if ((TransitionsMask & EVoxelDirectionFlag::XMin) && Vertex.X < LowerBound) + { + Delta.X = LowerBound - Vertex.X; + } + if ((TransitionsMask & EVoxelDirectionFlag::XMax) && Vertex.X > UpperBound) + { + Delta.X = UpperBound - Vertex.X; + } + if ((TransitionsMask & EVoxelDirectionFlag::YMin) && Vertex.Y < LowerBound) + { + Delta.Y = LowerBound - Vertex.Y; + } + if ((TransitionsMask & EVoxelDirectionFlag::YMax) && Vertex.Y > UpperBound) + { + Delta.Y = UpperBound - Vertex.Y; + } + if ((TransitionsMask & EVoxelDirectionFlag::ZMin) && Vertex.Z < LowerBound) + { + Delta.Z = LowerBound - Vertex.Z; + } + if ((TransitionsMask & EVoxelDirectionFlag::ZMax) && Vertex.Z > UpperBound) + { + Delta.Z = UpperBound - Vertex.Z; + } + + Delta /= 4; + + const FVector Q = FVector( + (1 - Normal.X * Normal.X) * Delta.X - Normal.Y * Normal.X * Delta.Y - Normal.Z * Normal.X * Delta.Z, + - Normal.X * Normal.Y * Delta.X + (1 - Normal.Y * Normal.Y) * Delta.Y - Normal.Z * Normal.Y * Delta.Z, + - Normal.X * Normal.Z * Delta.X - Normal.Y * Normal.Z * Delta.Y + (1 - Normal.Z * Normal.Z) * Delta.Z); + + return Vertex + Q; + } + + template + FORCEINLINE FVector2D GetUVs(const T& Mesher, const FVector& IntersectionPoint) + { + const auto& UVConfig = Mesher.Settings.UVConfig; + const auto& UVScale = Mesher.Settings.UVScale; + const auto& Data = Mesher.Data; + const auto& ChunkPosition = Mesher.ChunkPosition; + + if (UVConfig == EVoxelUVConfig::PackWorldUpInUVs) + { + const FVector WorldUp = Data.Generator->GetUpVector( + v_flt(ChunkPosition.X) + IntersectionPoint.X, + v_flt(ChunkPosition.Y) + IntersectionPoint.Y, + v_flt(ChunkPosition.Z) + IntersectionPoint.Z).GetSafeNormal(); + return FVector2D(WorldUp.X, WorldUp.Y); + } + else if (UVConfig == EVoxelUVConfig::GlobalUVs) + { + return FVector2D(ChunkPosition.X + IntersectionPoint.X, ChunkPosition.Y + IntersectionPoint.Y) / UVScale; + } + else + { + check(UVConfig == EVoxelUVConfig::PerVoxelUVs); + return FVector2D(IntersectionPoint.X, IntersectionPoint.Y); + } + } + + template + inline static void SanitizeMesh(TArray& Indices, TArray& Vertices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray NewIndices; + NewIndices.Reserve(Indices.Num()); + check(Indices.Num() % 3 == 0); + for (int32 Index = 0; Index < Indices.Num(); Index += 3) + { + const uint32 IndexA = FVoxelUtilities::Get(Indices, Index + 0); + const uint32 IndexB = FVoxelUtilities::Get(Indices, Index + 1); + const uint32 IndexC = FVoxelUtilities::Get(Indices, Index + 2); + + const auto& A = FVoxelUtilities::Get(Vertices, IndexA); + const auto& B = FVoxelUtilities::Get(Vertices, IndexB); + const auto& C = FVoxelUtilities::Get(Vertices, IndexC); + + const FVector BA = B.Position - A.Position; + const FVector CA = C.Position - A.Position; + const FVector Cross = FVector::CrossProduct(BA, CA); + if (Cross.Size() > 1e-4) // See Chaos::FConvexBuilder::IsValidTriangle + { + NewIndices.Emplace(IndexA); + NewIndices.Emplace(IndexB); + NewIndices.Emplace(IndexC); + } + } + Indices = MoveTemp(NewIndices); + } + + template + inline static void RemoveUnusedVertices(TArray& Indices, TArray& Vertices) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TBitArray<> UsedVertices(false, Vertices.Num()); + for (uint32 Index : Indices) + { + UsedVertices[Index] = true; + } + + TArray NewIndices; + NewIndices.Empty(Vertices.Num()); + NewIndices.SetNumUninitialized(Vertices.Num()); + + int32 WriteIndex = 0; + for (int32 ReadIndex = 0; ReadIndex < Vertices.Num(); ReadIndex++) + { + if (UsedVertices[ReadIndex]) + { + NewIndices[ReadIndex] = WriteIndex; + Vertices[WriteIndex] = Vertices[ReadIndex]; + WriteIndex++; + } + else + { + NewIndices[ReadIndex] = -1; + } + } + + check(WriteIndex <= Vertices.Num()); + Vertices.SetNum(WriteIndex, false); + + for (uint32& Index : Indices) + { + Index = NewIndices[Index]; + checkVoxelSlow(Index != -1); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.cpp new file mode 100644 index 00000000..bf647569 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.cpp @@ -0,0 +1,563 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/Meshers/VoxelSurfaceNetMesher.h" +#include "VoxelRender/Meshers/VoxelMesherUtilities.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" + +/** + * This code is based on an original implementation kindly provided by Dexyfex + * You can check out his website here: https://dexyfex.com/ + */ + +struct FVoxelSurfaceNetFullVertex : FVoxelMesherVertex +{ + static constexpr bool bComputeParentPosition = true; + static constexpr bool bComputeNormal = true; + static constexpr bool bComputeTextureCoordinate = true; + static constexpr bool bComputeMaterial = true; + + FORCEINLINE void SetPosition(const FVector& InPosition) + { + Position = InPosition; + } + FORCEINLINE void SetParentPosition(const FVector& InParentPosition) + { + Tangent = FVoxelProcMeshTangent(InParentPosition, false); + } + FORCEINLINE void SetNormal(const FVector& InNormal) + { + Normal = InNormal; + } + FORCEINLINE void SetTextureCoordinate(const FVector2D& InTextureCoordinate) + { + TextureCoordinate = InTextureCoordinate; + } + FORCEINLINE void SetMaterial(const FVoxelMaterial& InMaterial) + { + Material = InMaterial; + } +}; +static_assert(sizeof(FVoxelSurfaceNetFullVertex) == sizeof(FVoxelMesherVertex), ""); + +struct FVoxelSurfaceNetGeometryVertex : FVector +{ + static constexpr bool bComputeParentPosition = false; + static constexpr bool bComputeNormal = false; + static constexpr bool bComputeTextureCoordinate = false; + static constexpr bool bComputeMaterial = false; + + FORCEINLINE void SetPosition(const FVector& InPosition) + { + static_cast(*this) = InPosition; + } + FORCEINLINE void SetParentPosition(const FVector& InParentPosition) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetNormal(const FVector& InNormal) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetTextureCoordinate(const FVector2D& InTextureCoordinate) + { + checkVoxelSlow(false); + } + FORCEINLINE void SetMaterial(const FVoxelMaterial& InMaterial) + { + checkVoxelSlow(false); + } +}; +static_assert(sizeof(FVoxelSurfaceNetGeometryVertex) == sizeof(FVector), ""); + +FVoxelIntBox FVoxelSurfaceNetMesher::GetBoundsToCheckIsEmptyOn() const +{ + return FVoxelIntBox(ChunkPosition, ChunkPosition + SN_EXTENDED_CHUNK_SIZE * Step); +} + +FVoxelIntBox FVoxelSurfaceNetMesher::GetBoundsToLock() const +{ + return GetBoundsToCheckIsEmptyOn(); +} + +inline float SampleIsoValue(const float Values[8], const FVector& Offset) +{ + // TODO use FVoxelUtilities::TrilinearInterpolation + const FVector i = FVector(1.0f) - Offset; + const float x1 = Values[0] * i.X + Values[1] * Offset.X; + const float x2 = Values[2] * i.X + Values[3] * Offset.X; + const float x3 = Values[4] * i.X + Values[5] * Offset.X; + const float x4 = Values[6] * i.X + Values[7] * Offset.X; + const float y1 = x1 * i.Y + x2 * Offset.Y; + const float y2 = x3 * i.Y + x4 * Offset.Y; + const float z1 = y1 * i.Z + y2 * Offset.Z; + return z1; +} + +inline FVector GetNormal(const float Values[8], const FVector& Offset) +{ + const float MaxX = SampleIsoValue(Values, Offset + FVector(+1, 0, 0)); + const float MinX = SampleIsoValue(Values, Offset + FVector(-1, 0, 0)); + const float MaxY = SampleIsoValue(Values, Offset + FVector(0, +1, 0)); + const float MinY = SampleIsoValue(Values, Offset + FVector(0, -1, 0)); + const float MaxZ = SampleIsoValue(Values, Offset + FVector(0, 0, +1)); + const float MinZ = SampleIsoValue(Values, Offset + FVector(0, 0, -1)); + return FVector(MaxX - MinX, MaxY - MinY, MaxZ - MinZ).GetSafeNormal(); +} + +template +void FVoxelSurfaceNetMesher::CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TVoxelQueryZone QueryZone(GetBoundsToCheckIsEmptyOn(), FIntVector(SN_EXTENDED_CHUNK_SIZE), LOD, CachedValues); + MESHER_TIME_VALUES(SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE, Data.Get(QueryZone, LOD)); + + Accelerator = MakeUnique(Data, GetBoundsToLock()); + + constexpr uint32 EdgeIndexOffsets[12] = + { + 0, + SN_EXTENDED_CHUNK_SIZE * 3, + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + SN_EXTENDED_CHUNK_SIZE * 3 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 1, + 4, + 1 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 4 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 2, + 5, + 2 + SN_EXTENDED_CHUNK_SIZE * 3, + 5 + SN_EXTENDED_CHUNK_SIZE * 3 + }; + constexpr uint32 ParentEdgeIndexOffsetsMin[12] = + { + 0, + 2 * SN_EXTENDED_CHUNK_SIZE * 3, + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 1, + 7, + 1 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 7 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 2, + 8, + 2 + 2 * SN_EXTENDED_CHUNK_SIZE * 3, + 8 + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + }; + constexpr uint32 ParentEdgeIndexOffsetsMax[12] = + { + 3, + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + 3, + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3 + 3, + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3 + 3, + 1 + SN_EXTENDED_CHUNK_SIZE * 3, + 7 + SN_EXTENDED_CHUNK_SIZE * 3, + 1 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3 + SN_EXTENDED_CHUNK_SIZE * 3, + 7 + 2 * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3 + SN_EXTENDED_CHUNK_SIZE * 3, + 2 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 8 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 2 + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3, + 8 + 2 * SN_EXTENDED_CHUNK_SIZE * 3 + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3 + }; + constexpr uint32 EdgeFirstCornerIndex[12] = { 0, 2, 4, 6, 0, 1, 4, 5, 0, 1, 2, 3 }; + constexpr uint32 EdgeSecondCornerIndex[12] = { 1, 3, 5, 7, 2, 3, 6, 7, 4, 5, 6, 7 }; + // TODO: Replace buffer by simple math + const FVector Corners[8] = + { + {0,0,0}, + {1,0,0}, + {0,1,0}, + {1,1,0}, + {0,0,1}, + {1,0,1}, + {0,1,1}, + {1,1,1} + }; + const FVector ParentCorners[8] = + { + {0,0,0}, + {2,0,0}, + {0,2,0}, + {2,2,0}, + {0,0,2}, + {2,0,2}, + {0,2,2}, + {2,2,2} + }; + constexpr uint32 Offsets[3] = { 1, SN_EXTENDED_CHUNK_SIZE, SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE }; + const FIntVector icorners[3] = { {1,0,0},{0,1,0},{0,0,1} }; + + { + VOXEL_ASYNC_SCOPE_COUNTER("Find Intersections"); + + // find intersections and calculate the edge blend factors for all cells + for (int32 LZ = 0; LZ < SN_EXTENDED_CHUNK_SIZE; LZ++) + { + for (int32 LY = 0; LY < SN_EXTENDED_CHUNK_SIZE; LY++) + { + for (int32 LX = 0; LX < SN_EXTENDED_CHUNK_SIZE; LX++) + { + const uint32 VoxelIndex = LX + LY * SN_EXTENDED_CHUNK_SIZE + LZ * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE; + const FVoxelValue MinCornerValue = CachedValues[VoxelIndex]; + FIntVector MinCornerPosition = FIntVector(LX, LY, LZ) * Step; + FIntVector MaxCornerPositions = FIntVector(LX + 1, LY + 1, LZ + 1); + + for (uint32 Direction = 0; Direction < 3; Direction++) + { + float Factor = -1; // empty value, valid factor should be between 0 and 1 + if (MaxCornerPositions[Direction] < SN_EXTENDED_CHUNK_SIZE) // don't go outside of the cached area + { + const FVoxelValue MaxCornerInDirectionValue = CachedValues[VoxelIndex + Offsets[Direction]]; + if (MinCornerValue.IsEmpty() != MaxCornerInDirectionValue.IsEmpty()) + { + FVoxelValue MinValue = MinCornerValue; + FVoxelValue MaxValue = MaxCornerInDirectionValue; + + if (LOD != 0) + { + // for LOD chunks, search along the edge for the actual intersecting segment + FIntVector MinPosition = MinCornerPosition; + FIntVector MaxPosition = MinCornerPosition + icorners[Direction] * Step; + float c1 = 0; + float c2 = 1; + for (int iStep = Step; iStep > 1; iStep >>= 1) + { + const float cmid = (c1 + c2) * 0.5f; + const FIntVector MidPosition = (MinPosition + MaxPosition) / 2; + const FVoxelValue MidValue = MESHER_TIME_RETURN_VALUES(1, Accelerator->Get(MidPosition + ChunkPosition, LOD)); + if (MinValue.IsEmpty() != MidValue.IsEmpty())//intersection is between c1 and cmid + { + c2 = cmid; + MaxValue = MidValue; + MaxPosition = MidPosition; + } + else //intersection is between cmid and c2 + { + c1 = cmid; + MinValue = MidValue; + MinPosition = MidPosition; + } + } + // TODO is this needed + Factor = c1 + (c2 - c1) * MinValue.ToFloat() / (MinValue.ToFloat() - MaxValue.ToFloat()); + } + else + { + Factor = MinValue.ToFloat() / (MinValue.ToFloat() - MaxValue.ToFloat()); + } + } + } + EdgeFactors[3 * VoxelIndex + Direction] = Factor; + } + + if (TVertex::bComputeMaterial) + { + // We need to find the min value that's a surface value + if (LX < SN_EXTENDED_CHUNK_SIZE - 1 && + LY < SN_EXTENDED_CHUNK_SIZE - 1 && + LZ < SN_EXTENDED_CHUNK_SIZE - 1) + { + FVoxelValue VoxelValues[8]; + VoxelValues[0] = CachedValues[VoxelIndex]; + VoxelValues[1] = CachedValues[VoxelIndex + 1]; + VoxelValues[2] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE]; + VoxelValues[3] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE + 1]; + VoxelValues[4] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE]; + VoxelValues[5] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE + 1]; + VoxelValues[6] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE]; + VoxelValues[7] = CachedValues[VoxelIndex + SN_EXTENDED_CHUNK_SIZE + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE + 1]; + + FVoxelValue MinValue = FVoxelValue::Empty(); + int32 MinValueIndex = 0; + + for (int32 Index = 0; Index < 8; Index++) + { + if (VoxelValues[Index] > MinValue) + { + continue; + } + bool bIsSurfaceValue = false; + for (int32 Neighbor = 0; Neighbor < 3; Neighbor++) + { + const int32 NeighborIndex = Index ^ (1 << Neighbor); + if (VoxelValues[Index].IsEmpty() != VoxelValues[NeighborIndex].IsEmpty()) + { + bIsSurfaceValue = true; + break; + } + } + if (!bIsSurfaceValue) + { + continue; + } + MinValue = VoxelValues[Index]; + MinValueIndex = Index; + } + + MaterialPositions[VoxelIndex] = + { + LX + bool(MinValueIndex & 0x1), + LY + bool(MinValueIndex & 0x2), + LZ + bool(MinValueIndex & 0x4) + }; + } + else + { + MaterialPositions[VoxelIndex] = { LX, LY, LZ }; + } + } + } + } + } + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Generate Vertices"); + + // generate the vertices and store the indices for the next step + for (uint32 LZ = 0; LZ < SN_CHUNK_SIZE; LZ++) + { + for (uint32 LY = 0; LY < SN_CHUNK_SIZE; LY++) + { + for (uint32 LX = 0; LX < SN_CHUNK_SIZE; LX++) + { + const uint32 VoxelIndex = LX + LY * SN_EXTENDED_CHUNK_SIZE + LZ * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE; + const uint32 VertexIndex = LX + LY * SN_CHUNK_SIZE + LZ * SN_CHUNK_SIZE * SN_CHUNK_SIZE; + + uint32 VoxelIndices[8]; + VoxelIndices[0] = VoxelIndex; + VoxelIndices[1] = VoxelIndex + 1; + VoxelIndices[2] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE; + VoxelIndices[3] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE + 1; + VoxelIndices[4] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE; + VoxelIndices[5] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE + 1; + VoxelIndices[6] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE; + VoxelIndices[7] = VoxelIndex + SN_EXTENDED_CHUNK_SIZE + SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE + 1; + + FVoxelValue VoxelValues[8]; + VoxelValues[0] = CachedValues[VoxelIndices[0]]; + VoxelValues[1] = CachedValues[VoxelIndices[1]]; + VoxelValues[2] = CachedValues[VoxelIndices[2]]; + VoxelValues[3] = CachedValues[VoxelIndices[3]]; + VoxelValues[4] = CachedValues[VoxelIndices[4]]; + VoxelValues[5] = CachedValues[VoxelIndices[5]]; + VoxelValues[6] = CachedValues[VoxelIndices[6]]; + VoxelValues[7] = CachedValues[VoxelIndices[7]]; + + float VoxelFloats[8]; + VoxelFloats[0] = VoxelValues[0].ToFloat(); + VoxelFloats[1] = VoxelValues[1].ToFloat(); + VoxelFloats[2] = VoxelValues[2].ToFloat(); + VoxelFloats[3] = VoxelValues[3].ToFloat(); + VoxelFloats[4] = VoxelValues[4].ToFloat(); + VoxelFloats[5] = VoxelValues[5].ToFloat(); + VoxelFloats[6] = VoxelValues[6].ToFloat(); + VoxelFloats[7] = VoxelValues[7].ToFloat(); + + const uint32 MarchingCubesCase = + (VoxelValues[0].IsEmpty() << 0) | + (VoxelValues[1].IsEmpty() << 1) | + (VoxelValues[2].IsEmpty() << 2) | + (VoxelValues[3].IsEmpty() << 3) | + (VoxelValues[4].IsEmpty() << 4) | + (VoxelValues[5].IsEmpty() << 5) | + (VoxelValues[6].IsEmpty() << 6) | + (VoxelValues[7].IsEmpty() << 7); + + const uint8 SurfaceNetsCase = + (VoxelValues[3].IsEmpty() << 0) | + (VoxelValues[5].IsEmpty() << 1) | + (VoxelValues[6].IsEmpty() << 2) | + (VoxelValues[7].IsEmpty() << 3); + + VertexSNCases[VertexIndex] = SurfaceNetsCase; + + if ((MarchingCubesCase == 0) || (MarchingCubesCase == 255)) //cell is empty + { + VertexIndices[VertexIndex] = -1; + continue; + } + + VertexIndices[VertexIndex] = Vertices.Num(); + + + const uint32 VoxelEdgeIndex = VoxelIndex * 3; + FVector CrossingTotal = FVector(0, 0, 0); + uint32 CrossingCount = 0; + + constexpr int32 RemoveFirstBit = 0xFFFE; // TODO: what if more than 64k vertices + const FIntVector ParentPosition((LX & RemoveFirstBit), (LY & RemoveFirstBit), (LZ & RemoveFirstBit)); + const uint32 ParentVoxelIndex = ParentPosition.X + ParentPosition.Y * SN_EXTENDED_CHUNK_SIZE + ParentPosition.Z * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE; + const uint32 ParentVoxelEdgeIndex = ParentVoxelIndex * 3; + FVector ParentCrossingTotal = FVector(0, 0, 0); + uint32 ParentCrossingCount = 0; + + for (uint32 Edge = 0; Edge < 12; Edge++) + { + // if this edge has a crossing, find the point and add it to the avg total, increment count + const float EdgeFactor = EdgeFactors[VoxelEdgeIndex + EdgeIndexOffsets[Edge]]; + if (EdgeFactor >= 0) + { + const FVector MinPosition = Corners[EdgeFirstCornerIndex[Edge]]; + const FVector MaxPosition = Corners[EdgeSecondCornerIndex[Edge]]; + const FVector MidPosition = FMath::Lerp(MinPosition, MaxPosition, EdgeFactor); // blend between corners + CrossingTotal += MidPosition; + CrossingCount++; + } + + // find crossings of parent cell + const float ParentEdgeFactorMin = EdgeFactors[ParentVoxelEdgeIndex + ParentEdgeIndexOffsetsMin[Edge]]; + const float ParentEdgeFactorMax = EdgeFactors[ParentVoxelEdgeIndex + ParentEdgeIndexOffsetsMax[Edge]]; + if ((ParentEdgeFactorMin >= 0) || (ParentEdgeFactorMax >= 0)) + { + const float ParentEdgeFactor = + ((ParentEdgeFactorMin >= 0) ? 0.5f * ParentEdgeFactorMin : 0.5f) + + ((ParentEdgeFactorMax >= 0) ? 0.5f * ParentEdgeFactorMax : 0); + const FVector MinPosition = ParentCorners[EdgeFirstCornerIndex[Edge]]; + const FVector MaxPosition = ParentCorners[EdgeSecondCornerIndex[Edge]]; + const FVector MidPosition = FMath::Lerp(MinPosition, MaxPosition, ParentEdgeFactor); // blend between corners + ParentCrossingTotal += MidPosition; + ParentCrossingCount++; + } + } + + ensureVoxelSlowNoSideEffects(CrossingCount > 0); + const FVector Offset = CrossingTotal / CrossingCount; + + const FVector ParentOffset = ParentCrossingCount == 0 ? FVector::ZeroVector : ParentCrossingTotal / ParentCrossingCount; + + + const FIntVector CellPosition(LX * Step, LY * Step, LZ * Step); + const FVector CornerPosition{ CellPosition }; + const FVector FinalPosition = CornerPosition + Offset * Step; + + const FIntVector ParentCellPosition = ParentPosition * Step; + const FVector ParentCornerPosition{ ParentCellPosition }; + const FVector ParentFinalPosition = ParentCornerPosition + ParentOffset * Step; + + TVertex Vertex; + Vertex.SetPosition(FinalPosition); + if (TVertex::bComputeParentPosition) + { + Vertex.SetParentPosition((ParentFinalPosition - FinalPosition) / Step); // Divide by Step to avoid overflowing the tangent + } + if (TVertex::bComputeNormal) + { + Vertex.SetNormal(MESHER_TIME_RETURN(Normals, GetNormal(VoxelFloats, Offset))); + } + if (TVertex::bComputeMaterial) + { + Vertex.SetMaterial(MESHER_TIME_RETURN_MATERIALS(1, Accelerator->GetMaterial(MaterialPositions[VoxelIndex] * Step + ChunkPosition, LOD))); + } + if (TVertex::bComputeTextureCoordinate) + { + Vertex.SetTextureCoordinate(MESHER_TIME_RETURN(UVs, FVoxelMesherUtilities::GetUVs(*this, FinalPosition))); + } + Vertices.Add(Vertex); + } + } + } + } + + UnlockData(); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Generate Mesh"); + + // generate the mesh indices + for (uint32 LZ = 0; LZ < RENDER_CHUNK_SIZE; LZ++) + { + for (uint32 LY = 0; LY < RENDER_CHUNK_SIZE; LY++) + { + for (uint32 LX = 0; LX < RENDER_CHUNK_SIZE; LX++) + { + const uint32 VoxelIndex = LX + LY * SN_CHUNK_SIZE + LZ * SN_CHUNK_SIZE * SN_CHUNK_SIZE; + const uint8 SurfaceNetCase = VertexSNCases[VoxelIndex]; + + constexpr uint32 QuadsOffsets[8] = + { + 0, + SN_CHUNK_SIZE, + SN_CHUNK_SIZE + 1, + 1, + SN_CHUNK_SIZE * SN_CHUNK_SIZE, + SN_CHUNK_SIZE * SN_CHUNK_SIZE + SN_CHUNK_SIZE, + SN_CHUNK_SIZE * SN_CHUNK_SIZE + SN_CHUNK_SIZE + 1, + SN_CHUNK_SIZE * SN_CHUNK_SIZE + 1 + }; + constexpr uint32 QuadsPositions[6][4] = + { + { 0, 1, 3, 2 }, + { 0, 3, 4, 7 }, + { 0, 4, 1, 5 }, + { 0, 3, 1, 2 }, + { 0, 4, 3, 7 }, + { 0, 1, 4, 5 } + }; + + uint32 QuadsIndices[3] = { 0, 0, 0 }; //indexing into quads array + uint32 QuadCount = 0; + + switch (SurfaceNetCase) //surface nets polygonization + { + case 0: QuadCount = 0; break; + case 1: QuadCount = 1; QuadsIndices[0] = 0; break; + case 2: QuadCount = 1; QuadsIndices[0] = 1; break; + case 3: QuadCount = 2; QuadsIndices[0] = 0; QuadsIndices[1] = 1; break; + case 4: QuadCount = 1; QuadsIndices[0] = 2; break; + case 5: QuadCount = 2; QuadsIndices[0] = 0; QuadsIndices[1] = 2; break; + case 6: QuadCount = 2; QuadsIndices[0] = 1; QuadsIndices[1] = 2; break; + case 7: QuadCount = 3; QuadsIndices[0] = 0; QuadsIndices[1] = 1; QuadsIndices[2] = 2; break; //^^ forward cases + case 8: QuadCount = 3; QuadsIndices[0] = 3; QuadsIndices[1] = 4; QuadsIndices[2] = 5; break; //vv complement cases + case 9: QuadCount = 2; QuadsIndices[0] = 4; QuadsIndices[1] = 5; break; + case 10: QuadCount = 2; QuadsIndices[0] = 3; QuadsIndices[1] = 5; break; + case 11: QuadCount = 1; QuadsIndices[0] = 5; break; + case 12: QuadCount = 2; QuadsIndices[0] = 3; QuadsIndices[1] = 4; break; + case 13: QuadCount = 1; QuadsIndices[0] = 4; break; + case 14: QuadCount = 1; QuadsIndices[0] = 3; break; + case 15: QuadCount = 0; break; + default: checkVoxelSlow(false); + } + + for (uint32 QuadIndex = 0; QuadIndex < QuadCount; QuadIndex++) + { + const uint32* Positions = QuadsPositions[QuadsIndices[QuadIndex]]; + const uint32 Index0 = VertexIndices[VoxelIndex + QuadsOffsets[Positions[0]]]; + const uint32 Index1 = VertexIndices[VoxelIndex + QuadsOffsets[Positions[1]]]; + const uint32 Index2 = VertexIndices[VoxelIndex + QuadsOffsets[Positions[2]]]; + const uint32 Index3 = VertexIndices[VoxelIndex + QuadsOffsets[Positions[3]]]; + + Indices.Add(Index0); + Indices.Add(Index2); + Indices.Add(Index3); + + Indices.Add(Index3); + Indices.Add(Index1); + Indices.Add(Index0); + } + } + } + } + } +} + +TVoxelSharedPtr FVoxelSurfaceNetMesher::CreateFullChunkImpl(FVoxelMesherTimes& Times) +{ + TArray Indices; + TArray Vertices; + CreateGeometryTemplate(Times, Indices, Vertices); + + FVoxelMesherUtilities::SanitizeMesh(Indices, Vertices); + + return MESHER_TIME_RETURN(CreateChunk, FVoxelMesherUtilities::CreateChunkFromVertices( + Settings, + LOD, + MoveTemp(Indices), + MoveTemp(reinterpret_cast&>(Vertices)))); +} + +void FVoxelSurfaceNetMesher::CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) +{ + CreateGeometryTemplate(Times, Indices, reinterpret_cast&>(Vertices)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.h new file mode 100644 index 00000000..6f7fb4ae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.h @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelRender/Meshers/VoxelMesher.h" + +/** + * This code is based on an original implementation kindly provided by Dexyfex + * You can check out his website here: https://dexyfex.com/ + */ + +#define SN_CHUNK_SIZE (RENDER_CHUNK_SIZE + 1) /* +1 since SN vertices are within cells */ +#define SN_EXTENDED_CHUNK_SIZE (RENDER_CHUNK_SIZE + 3) /* +3 to get parent's outer edge */ + +class FVoxelSurfaceNetMesher : public FVoxelMesher +{ +public: + using FVoxelMesher::FVoxelMesher; + +protected: + virtual FVoxelIntBox GetBoundsToCheckIsEmptyOn() const override final; + virtual FVoxelIntBox GetBoundsToLock() const override final; + virtual TVoxelSharedPtr CreateFullChunkImpl(FVoxelMesherTimes& Times) override final; + virtual void CreateGeometryImpl(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices) override final; + +private: + TUniquePtr Accelerator; + + FVoxelValue CachedValues[SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE]; + float EdgeFactors[SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * SN_EXTENDED_CHUNK_SIZE * 3]; // edge blending factors for each cell, X,Y,Z + uint32 VertexIndices[SN_CHUNK_SIZE * SN_CHUNK_SIZE * SN_CHUNK_SIZE]; // final vertex indices, per voxel. 65535 if no vertex + uint8 VertexSNCases[SN_CHUNK_SIZE * SN_CHUNK_SIZE * SN_CHUNK_SIZE]; // surface net voxel cases for each cell + + // The material position is detected in a first step + TVoxelStaticArray MaterialPositions; + + template + void CreateGeometryTemplate(FVoxelMesherTimes& Times, TArray& Indices, TArray& Vertices); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp new file mode 100644 index 00000000..4e88d09d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp @@ -0,0 +1,117 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h" +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.h" +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" +#include "PhysicsEngine/PhysicsSettings.h" + +double GTotalVoxelCollisionCookingTime = 0; + +static TAutoConsoleVariable CVarLogCollisionCookingTimes( + TEXT("voxel.collision.LogCookingTimes"), + 0, + TEXT("If true, will log the time it took to cook the voxel meshes collisions"), + ECVF_Default); + +static FAutoConsoleCommand CmdLogTotalCollisionCookingTime( + TEXT("voxel.collision.LogTotalCookingTime"), + TEXT("Log the accumulated total spent computing collision. Also see voxel.collision.ClearTotalCookingTime"), + FConsoleCommandDelegate::CreateLambda([]() + { + LOG_VOXEL(Log, TEXT("Total collision cooking time: %fs"), GTotalVoxelCollisionCookingTime); + })); + +static FAutoConsoleCommand CmdClearTotalCollisionCookingTime( + TEXT("voxel.collision.ClearTotalCookingTime"), + TEXT("Clear the accumulated total spent computing collision. Also see voxel.collision.LogTotalCookingTime"), + FConsoleCommandDelegate::CreateLambda([]() + { + GTotalVoxelCollisionCookingTime = 0; + LOG_VOXEL(Log, TEXT("Total collision cooking time cleared")); + })); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +IVoxelAsyncPhysicsCooker::IVoxelAsyncPhysicsCooker(UVoxelProceduralMeshComponent* Component) + : FVoxelAsyncWork(STATIC_FNAME("AsyncPhysicsCooker"), Component->PriorityDuration) + , UniqueId(UNIQUE_ID()) + , Component(Component) + , PhysicsCallbackHandler(Component->PhysicsCallbackHandler) + , LOD(Component->LOD) + , CollisionTraceFlag( + Component->CollisionTraceFlag == ECollisionTraceFlag::CTF_UseDefault + ? ECollisionTraceFlag(UPhysicsSettings::Get()->DefaultShapeComplexity) + : Component->CollisionTraceFlag) + , PriorityHandler(Component->PriorityHandler) + , bCleanCollisionMesh(Component->bCleanCollisionMesh) + , NumConvexHullsPerAxis(Component->NumConvexHullsPerAxis) + , Buffers([&]() + { + TArray> TmpBuffers; + TmpBuffers.Reserve(Component->ProcMeshSections.Num()); + for (auto& Section : Component->ProcMeshSections) + { + if (Section.Settings.bEnableCollisions) + { + TmpBuffers.Add(Section.Buffers); + } + } + return TmpBuffers; + }()) + , LocalToRoot(Component->GetRelativeTransform()) +{ + check(IsInGameThread()); + ensure(CollisionTraceFlag != ECollisionTraceFlag::CTF_UseDefault); + ensure(Buffers.Num() > 0); +} + +IVoxelAsyncPhysicsCooker* IVoxelAsyncPhysicsCooker::CreateCooker(UVoxelProceduralMeshComponent* Component) +{ + check(Component); +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + return new FVoxelAsyncPhysicsCooker_PhysX(Component); +#elif WITH_CHAOS + return new FVoxelAsyncPhysicsCooker_Chaos(Component); +#else + return nullptr; +#endif +} + +void IVoxelAsyncPhysicsCooker::DoWork() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const double CookStartTime = FPlatformTime::Seconds(); + + CookMesh(); + + const double CookEndTime = FPlatformTime::Seconds(); + + if (CVarLogCollisionCookingTimes.GetValueOnAnyThread() != 0) + { + LOG_VOXEL(Log, TEXT("Collisions cooking took %fms"), (CookEndTime - CookStartTime) * 1000); + } + + GTotalVoxelCollisionCookingTime += CookEndTime - CookStartTime; +} + +void IVoxelAsyncPhysicsCooker::PostDoWork() +{ + auto Pinned = PhysicsCallbackHandler.Pin(); + if (Pinned.IsValid()) + { + Pinned->CookerCallback(UniqueId, Component); + FVoxelUtilities::DeleteOnGameThread_AnyThread(Pinned); + } +} + +uint32 IVoxelAsyncPhysicsCooker::GetPriority() const +{ + return PriorityHandler.GetPriority(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h new file mode 100644 index 00000000..2460f79d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h @@ -0,0 +1,50 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelAsyncWork.h" +#include "VoxelPriorityHandler.h" +#include "PhysicsEngine/BodySetup.h" +#include "UObject/WeakObjectPtrTemplates.h" + +struct FVoxelProcMeshBuffers; +struct FVoxelProceduralMeshComponentMemoryUsage; +class UBodySetup; +class UVoxelProceduralMeshComponent; +class IVoxelProceduralMeshComponent_PhysicsCallbackHandler; + +class IVoxelAsyncPhysicsCooker : public FVoxelAsyncWork +{ +public: + const uint64 UniqueId; + const TWeakObjectPtr Component; + const TVoxelWeakPtr PhysicsCallbackHandler; + + const int32 LOD; + const ECollisionTraceFlag CollisionTraceFlag; + const FVoxelPriorityHandler PriorityHandler; + const bool bCleanCollisionMesh; + const int32 NumConvexHullsPerAxis; + const TArray> Buffers; + const FTransform LocalToRoot; + + explicit IVoxelAsyncPhysicsCooker(UVoxelProceduralMeshComponent* Component); + + static IVoxelAsyncPhysicsCooker* CreateCooker(UVoxelProceduralMeshComponent* Component); + +public: + //~ Begin IVoxelAsyncPhysicsCooker Interface + virtual bool Finalize(UBodySetup& BodySetup, FVoxelProceduralMeshComponentMemoryUsage& OutMemoryUsage) = 0; +protected: + virtual void CookMesh() = 0; + //~ End IVoxelAsyncPhysicsCooker Interface + +protected: + //~ Begin FVoxelAsyncWork Interface + virtual void DoWork() override; + virtual void PostDoWork() override; + virtual uint32 GetPriority() const override; + //~ End FVoxelAsyncWork Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp new file mode 100644 index 00000000..0cd1c199 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +#include "PhysicsEngine/BodySetup.h" + +#if WITH_CHAOS +#include "Chaos/ImplicitObject.h" +#include "Chaos/CollisionConvexMesh.h" +#include "Chaos/TriangleMeshImplicitObject.h" + +FVoxelAsyncPhysicsCooker_Chaos::FVoxelAsyncPhysicsCooker_Chaos(UVoxelProceduralMeshComponent* Component) + : IVoxelAsyncPhysicsCooker(Component) +{ +} + +bool FVoxelAsyncPhysicsCooker_Chaos::Finalize(UBodySetup& BodySetup, FVoxelProceduralMeshComponentMemoryUsage& OutMemoryUsage) +{ +#if TRACK_CHAOS_GEOMETRY + for (auto& TriMesh : TriMeshes) + { + TriMesh->Track(Chaos::MakeSerializable(TriMesh), "Voxel Mesh"); + } +#endif + + // Force trimesh collisions off + for (auto& TriMesh : TriMeshes) + { + TriMesh->SetDoCollide(false); + } + + BodySetup.ChaosTriMeshes = MoveTemp(TriMeshes); + BodySetup.bCreatedPhysicsMeshes = true; + + return true; +} + +void FVoxelAsyncPhysicsCooker_Chaos::CookMesh() +{ + if (CollisionTraceFlag != ECollisionTraceFlag::CTF_UseComplexAsSimple) + { + ensure(false); + } + if (CollisionTraceFlag != ECollisionTraceFlag::CTF_UseSimpleAsComplex) + { + CreateTriMesh(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelAsyncPhysicsCooker_Chaos::CreateTriMesh() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + int32 NumIndices = 0; + int32 NumVertices = 0; + for (auto& Buffer : Buffers) + { + NumIndices += Buffer->GetNumIndices(); + NumVertices += Buffer->GetNumVertices(); + } + + const auto Process = [&](auto& Triangles) + { + Chaos::TParticles Particles; + + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy data from buffers"); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Allocate"); + ensure(NumIndices % 3 == 0); + Triangles.SetNumUninitialized(NumIndices / 3); + Particles.AddParticles(NumVertices); + } + + int32 IndexIndex = 0; + int32 VertexIndex = 0; + for (int32 SectionIndex = 0; SectionIndex < Buffers.Num(); SectionIndex++) + { + auto& Buffer = *Buffers[SectionIndex]; + + const int32 VertexOffset = VertexIndex; + + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy vertices"); + + auto& PositionBuffer = Buffer.VertexBuffers.PositionVertexBuffer; + for (uint32 Index = 0; Index < PositionBuffer.GetNumVertices(); Index++) + { + Particles.X(VertexIndex++) = PositionBuffer.VertexPosition(Index); + } + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy triangles"); + + auto& IndexBuffer = Buffer.IndexBuffer; + + ensure(IndexBuffer.GetNumIndices() % 3 == 0); + const int32 NumTriangles = IndexBuffer.GetNumIndices() / 3; + + const auto Lambda = [&](const auto* RESTRICT Data) + { + for (int32 Index = 0; Index < NumTriangles; Index++) + { + checkVoxelSlow(3 * Index + 2 < IndexBuffer.GetNumIndices()); + + const Chaos::TVector Triangle{ + int32(Data[3 * Index + 2]) + VertexOffset, + int32(Data[3 * Index + 1]) + VertexOffset, + int32(Data[3 * Index + 0]) + VertexOffset + }; + + FVoxelUtilities::Get(Triangles, IndexIndex++) = Triangle; + + #if VOXEL_DEBUG + const auto A = Particles.X(Triangle.X); + const auto B = Particles.X(Triangle.Y); + const auto C = Particles.X(Triangle.Z); + ensure(Chaos::FConvexBuilder::IsValidTriangle(A, B, C)); + #endif + } + }; + if (IndexBuffer.Is32Bit()) + { + Lambda(IndexBuffer.GetData_32()); + } + else + { + Lambda(IndexBuffer.GetData_16()); + } + } + } + check(IndexIndex == Triangles.Num()); + check(VertexIndex == Particles.Size()); + } + + TArray MaterialIndices; + + VOXEL_ASYNC_SCOPE_COUNTER("Build Tri Mesh"); + TriMeshes.Emplace(new Chaos::FTriangleMeshImplicitObject(MoveTemp(Particles), MoveTemp(Triangles), MoveTemp(MaterialIndices))); + }; + + if (NumVertices < TNumericLimits::Max()) + { + TArray> TrianglesSmallIdx; + Process(TrianglesSmallIdx); + } + else + { + TArray> TrianglesLargeIdx; + Process(TrianglesLargeIdx); + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h new file mode 100644 index 00000000..6e92a111 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAsyncPhysicsCooker.h" + +#if WITH_CHAOS +namespace Chaos +{ + class FTriangleMeshImplicitObject; +} + +class IPhysXCooking; + +class FVoxelAsyncPhysicsCooker_Chaos : public IVoxelAsyncPhysicsCooker +{ +public: + explicit FVoxelAsyncPhysicsCooker_Chaos(UVoxelProceduralMeshComponent* Component); + +private: + ~FVoxelAsyncPhysicsCooker_Chaos() = default; + + template + friend struct TVoxelAsyncWorkDelete; + +protected: + //~ Begin IVoxelAsyncPhysicsCooker Interface + virtual bool Finalize(UBodySetup& BodySetup, FVoxelProceduralMeshComponentMemoryUsage& OutMemoryUsage) override; + virtual void CookMesh() override; + //~ End IVoxelAsyncPhysicsCooker Interface + +private: + void CreateTriMesh(); + + TArray> TriMeshes; +}; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.cpp new file mode 100644 index 00000000..5fb255ed --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.cpp @@ -0,0 +1,375 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelPhysXHelpers.h" + +#include "IPhysXCookingModule.h" + +#include "PhysicsPublic.h" +#include "PhysicsEngine/BodySetup.h" +#include "PhysicsEngine/PhysicsSettings.h" + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +inline IPhysXCooking* GetPhysXCooking() +{ + static IPhysXCookingModule* PhysXCookingModule = nullptr; + if (!PhysXCookingModule) + { + PhysXCookingModule = GetPhysXCookingModule(); + } + return PhysXCookingModule->GetPhysXCooking(); +} + +static const FName PhysXFormat = FPlatformProperties::GetPhysicsFormat(); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelAsyncPhysicsCooker_PhysX::FVoxelAsyncPhysicsCooker_PhysX(UVoxelProceduralMeshComponent* Component) + : IVoxelAsyncPhysicsCooker(Component) + , PhysXCooking(GetPhysXCooking()) +{ +} + +#if ENGINE_MINOR_VERSION >= 24 +class UMRMeshComponent +{ +public: + static void FinishCreatingPhysicsMeshes(UBodySetup& Body, const TArray& ConvexMeshes, const TArray& ConvexMeshesNegX, const TArray& TriMeshes) + { + Body.FinishCreatingPhysicsMeshes_PhysX(ConvexMeshes, ConvexMeshesNegX, TriMeshes); + } +}; +#endif + +bool FVoxelAsyncPhysicsCooker_PhysX::Finalize(UBodySetup& BodySetup, FVoxelProceduralMeshComponentMemoryUsage& OutMemoryUsage) +{ + VOXEL_FUNCTION_COUNTER(); + + if (ErrorCounter.GetValue() > 0) + { + return false; + } + + { + VOXEL_SCOPE_COUNTER("FinishCreatingPhysicsMeshes"); +#if ENGINE_MINOR_VERSION < 24 + BodySetup.FinishCreatingPhysicsMeshes({}, {}, CookResult.TriangleMeshes); +#else + UMRMeshComponent::FinishCreatingPhysicsMeshes(BodySetup, {}, {}, CookResult.TriangleMeshes); +#endif + } + + // TODO a bit hacky? + Component->UpdateConvexMeshes(CookResult.ConvexBounds, MoveTemp(CookResult.ConvexElems), MoveTemp(CookResult.ConvexMeshes)); + + OutMemoryUsage.TriangleMeshes = CookResult.TriangleMeshesMemoryUsage; + + return true; +} + +void FVoxelAsyncPhysicsCooker_PhysX::CookMesh() +{ + if (CollisionTraceFlag != ECollisionTraceFlag::CTF_UseComplexAsSimple) + { + DecomposeMeshToHulls(); + CreateConvexMesh(); + } + if (CollisionTraceFlag != ECollisionTraceFlag::CTF_UseSimpleAsComplex) + { + CreateTriMesh(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelAsyncPhysicsCooker_PhysX::CreateTriMesh() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray Vertices; + TArray Indices; + TArray MaterialIndices; + + // Copy data from buffers + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy data from buffers"); + + { + int32 NumIndices = 0; + int32 NumVertices = 0; + for (auto& Buffer : Buffers) + { + NumIndices += Buffer->GetNumIndices(); + NumVertices += Buffer->GetNumVertices(); + } + VOXEL_ASYNC_SCOPE_COUNTER("Reserve"); + Vertices.Reserve(NumVertices); + Indices.Reserve(NumIndices); + MaterialIndices.Reserve(NumIndices); + } + + int32 VertexOffset = 0; + for (int32 SectionIndex = 0; SectionIndex < Buffers.Num(); SectionIndex++) + { + auto& Buffer = *Buffers[SectionIndex]; + const auto Get = [](auto& Array, int32 Index) -> auto& + { +#if VOXEL_DEBUG + return Array[Index]; +#else + return Array.GetData()[Index]; +#endif + }; + + // Copy vertices + { + auto& PositionBuffer = Buffer.VertexBuffers.PositionVertexBuffer; + + const int32 Offset = Vertices.Num(); + check(PositionBuffer.GetNumVertices() <= uint32(Vertices.GetSlack())); + Vertices.AddUninitialized(PositionBuffer.GetNumVertices()); + + VOXEL_ASYNC_SCOPE_COUNTER("Copy vertices"); + for (uint32 Index = 0; Index < PositionBuffer.GetNumVertices(); Index++) + { + Get(Vertices, Offset + Index) = PositionBuffer.VertexPosition(Index); + } + } + + // Copy triangle data + { + auto& IndexBuffer = Buffer.IndexBuffer; + + ensure(Indices.Num() == MaterialIndices.Num()); + const int32 Offset = Indices.Num(); + ensure(IndexBuffer.GetNumIndices() % 3 == 0); + const int32 NumTriangles = IndexBuffer.GetNumIndices() / 3; + + check(NumTriangles <= Indices.GetSlack()); + check(NumTriangles <= MaterialIndices.GetSlack()); + Indices.AddUninitialized(NumTriangles); + MaterialIndices.AddUninitialized(NumTriangles); + + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy triangles"); + const auto Lambda = [&](const auto* RESTRICT Data) + { + for (int32 Index = 0; Index < NumTriangles; Index++) + { + // Need to add base offset for indices + FTriIndices TriIndices; + TriIndices.v0 = Data[3 * Index + 0] + VertexOffset; + TriIndices.v1 = Data[3 * Index + 1] + VertexOffset; + TriIndices.v2 = Data[3 * Index + 2] + VertexOffset; + checkVoxelSlow(3 * Index + 2 < IndexBuffer.GetNumIndices()); + Get(Indices, Offset + Index) = TriIndices; + } + }; + if (IndexBuffer.Is32Bit()) + { + Lambda(IndexBuffer.GetData_32()); + } + else + { + Lambda(IndexBuffer.GetData_16()); + } + } + // Also store material info + { + VOXEL_ASYNC_SCOPE_COUNTER("Copy material info"); + for (int32 Index = 0; Index < NumTriangles; Index++) + { + Get(MaterialIndices, Offset + Index) = SectionIndex; + } + } + } + + VertexOffset = Vertices.Num(); + } + } + + if (Indices.Num() < 3) + { + // If less than 3 triangles the cooking is likely to fail + return; + } + + physx::PxTriangleMesh* TriangleMesh = nullptr; + + constexpr bool bFlipNormals = true; // Always true due to the order of the vertices (clock wise vs not) + const bool bSuccess = PhysXCooking->CreateTriMesh( + PhysXFormat, + GetCookFlags(), + Vertices, + Indices, + MaterialIndices, + bFlipNormals, + TriangleMesh); + + CookResult.TriangleMeshes.Add(TriangleMesh); + + if (TriangleMesh) + { + CookResult.TriangleMeshesMemoryUsage += FVoxelPhysXHelpers::GetAllocatedSize(*TriangleMesh); + } + + if (!bSuccess) + { + // Happens sometimes + LOG_VOXEL(Warning, TEXT("Failed to cook TriMesh. Num vertices: %d; Num triangles: %d"), Vertices.Num(), Indices.Num()); + ErrorCounter.Increment(); + } +} + +void FVoxelAsyncPhysicsCooker_PhysX::CreateConvexMesh() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (auto& Element : CookResult.ConvexElems) + { + CookResult.ConvexMeshes.AddZeroed(); + const EPhysXCookingResult Result = PhysXCooking->CreateConvex(PhysXFormat, GetCookFlags(), Element.VertexData, CookResult.ConvexMeshes.Last()); + switch (Result) + { + case EPhysXCookingResult::Failed: + { + LOG_VOXEL(Warning, TEXT("Failed to cook convex")); + ErrorCounter.Increment(); + break; + } + case EPhysXCookingResult::SucceededWithInflation: + { + LOG_VOXEL(Warning, TEXT("Cook convex failed but succeeded with inflation")); + break; + } + case EPhysXCookingResult::Succeeded: break; + default: ensure(false); + } + } +} + +void FVoxelAsyncPhysicsCooker_PhysX::DecomposeMeshToHulls() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (Buffers.Num() == 1 && Buffers[0]->GetNumVertices() < 4) return; + + auto& ConvexElems = CookResult.ConvexElems; + + FBox Box(ForceInit); + for(auto& Buffer : Buffers) + { + auto& PositionBuffer = Buffer->VertexBuffers.PositionVertexBuffer; + for (uint32 Index = 0; Index < PositionBuffer.GetNumVertices(); Index++) + { + Box += PositionBuffer.VertexPosition(Index); + } + } + + const int32 ChunkSize = RENDER_CHUNK_SIZE << LOD; + const FIntVector Size = + FVoxelUtilities::ComponentMax( + FIntVector(1), + FVoxelUtilities::CeilToInt(Box.GetSize() / ChunkSize * NumConvexHullsPerAxis)); + + if (!ensure(Size.GetMax() <= 64)) return; + + ConvexElems.SetNum(Size.X * Size.Y * Size.Z); + + for (auto& Buffer : Buffers) + { + auto& PositionBuffer = Buffer->VertexBuffers.PositionVertexBuffer; + for (uint32 Index = 0; Index < PositionBuffer.GetNumVertices(); Index++) + { + const FVector Vertex = PositionBuffer.VertexPosition(Index); + + FIntVector MainPosition; + const auto Lambda = [&](int32 OffsetX, int32 OffsetY, int32 OffsetZ) + { + const FVector Offset = FVector(OffsetX, OffsetY, OffsetZ) * (1 << LOD); // 1 << LOD: should be max distance between the vertices + FIntVector Position = FVoxelUtilities::FloorToInt((Vertex + Offset - Box.Min) / ChunkSize * NumConvexHullsPerAxis); + Position = FVoxelUtilities::Clamp(Position, FIntVector(0), Size - 1); + + // Avoid adding too many duplicates by checking we're not in the center + if (OffsetX == 0 && OffsetY == 0 && OffsetZ == 0) + { + MainPosition = Position; + } + else + { + if (Position == MainPosition) + { + return; + } + } + ConvexElems[Position.X + Size.X * Position.Y + Size.X * Size.Y * Position.Z].VertexData.Add(Vertex); + }; + Lambda(0, 0, 0); + // Iterate possible neighbors to avoid holes between hulls + Lambda(+1, 0, 0); + Lambda(-1, 0, 0); + Lambda(0, +1, 0); + Lambda(0, -1, 0); + Lambda(0, 0, +1); + Lambda(0, 0, -1); + } + } + + constexpr int32 Threshold = 8; + + // Merge the hulls until they are big enough + // This moves the vertices to the end + for (int32 Index = 0; Index < ConvexElems.Num() - 1; Index++) + { + auto& Element = ConvexElems[Index]; + if (Element.VertexData.Num() < Threshold) + { + ConvexElems[Index + 1].VertexData.Append(Element.VertexData); + Element.VertexData.Reset(); + } + } + + // Remove all empty hulls + ConvexElems.RemoveAll([](auto& Element) { return Element.VertexData.Num() == 0; }); + if(!ensure(ConvexElems.Num() > 0)) return; + + // Then merge backwards while the last hull isn't big enough + while (ConvexElems.Last().VertexData.Num() < Threshold && ConvexElems.Num() > 1) + { + ConvexElems[ConvexElems.Num() - 2].VertexData.Append(ConvexElems.Last().VertexData); + ConvexElems.Pop(); + } + + CookResult.ConvexBounds = FBox(ForceInit); + for (auto& Element : ConvexElems) + { + ensure(Element.VertexData.Num() >= 4); + for (auto& Vertex : Element.VertexData) + { + // Transform from component space to root component space, as the root is going to hold the convex meshes + Vertex = LocalToRoot.TransformPosition(Vertex); + } + Element.UpdateElemBox(); + CookResult.ConvexBounds += Element.ElemBox; + } +} + +EPhysXMeshCookFlags FVoxelAsyncPhysicsCooker_PhysX::GetCookFlags() const +{ + EPhysXMeshCookFlags CookFlags = EPhysXMeshCookFlags::Default; + if (!bCleanCollisionMesh) + { + CookFlags |= EPhysXMeshCookFlags::DeformableMesh; + } + // TODO try and bench CookFlags |= EPhysXMeshCookFlags::DisableActiveEdgePrecompute; + // TODO: option/check perf + CookFlags |= EPhysXMeshCookFlags::FastCook; + return CookFlags; +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.h new file mode 100644 index 00000000..4989ff25 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_PhysX.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPhysXCooking.h" +#include "VoxelAsyncPhysicsCooker.h" + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +struct FKConvexElem; +class IPhysXCooking; + +class FVoxelAsyncPhysicsCooker_PhysX : public IVoxelAsyncPhysicsCooker +{ +public: + explicit FVoxelAsyncPhysicsCooker_PhysX(UVoxelProceduralMeshComponent* Component); + +private: + ~FVoxelAsyncPhysicsCooker_PhysX() = default; + + template + friend struct TVoxelAsyncWorkDelete; + + //~ Begin IVoxelAsyncPhysicsCooker Interface + virtual bool Finalize(UBodySetup& BodySetup, FVoxelProceduralMeshComponentMemoryUsage& OutMemoryUsage) override; + virtual void CookMesh() override; + //~ End IVoxelAsyncPhysicsCooker Interface + +private: + void CreateTriMesh(); + void CreateConvexMesh(); + void DecomposeMeshToHulls(); + EPhysXMeshCookFlags GetCookFlags() const; + + IPhysXCooking* const PhysXCooking; + FThreadSafeCounter ErrorCounter; + + struct FCookResult + { + FBox ConvexBounds; + TArray ConvexElems; + TArray ConvexMeshes; + TArray TriangleMeshes; + + uint64 TriangleMeshesMemoryUsage = 0; + }; + FCookResult CookResult; +}; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelPhysXHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelPhysXHelpers.h new file mode 100644 index 00000000..7802db17 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelPhysXHelpers.h @@ -0,0 +1,192 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +THIRD_PARTY_INCLUDES_START +#include "Px.h" +#include "PxVec4.h" +#include "PxTriangleMesh.h" +THIRD_PARTY_INCLUDES_END + +// The code below is using PhysX class layouts. PhysX is licensed under the following terms: + +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +namespace FVoxelPhysXHelpers +{ + using namespace physx; + + namespace Cm + { + class RefCountable + { + public: + virtual ~RefCountable() {} + + private: + volatile PxI32 mRefCount; + }; + + } + + namespace Ps + { + class UserAllocated + { + }; + } + + class CenterExtents : public Ps::UserAllocated + { + public: + PxVec3 mCenter; + PxVec3 mExtents; + }; + + class TriangleMesh : public PxTriangleMesh, public Ps::UserAllocated, public Cm::RefCountable + { + public: + virtual ~TriangleMesh() {} + + PxU32 mNbVertices; + PxU32 mNbTriangles; + PxVec3* mVertices; + void* mTriangles; //!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3) + // 16 bytes block + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU8* mExtraTrigData; // one per trig + PxReal mGeomEpsilon; //!< see comments in cooking code referencing this variable + // 16 bytes block + /* + low 3 bits (mask: 7) are the edge flags: + b001 = 1 = ignore edge 0 = edge v0-->v1 + b010 = 2 = ignore edge 1 = edge v0-->v2 + b100 = 4 = ignore edge 2 = edge v1-->v2 + */ + PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide + //!< Flag whether triangle adajacencies are build + PxU16* mMaterialIndices; //!< the size of the array is numTriangles. + PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new] + PxU32* mAdjacencies; //!< Adjacency information for each face - 3 adjacent faces + + class GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization + + // GRB data ------------------------- + void* mGRB_triIndices; //!< GRB: GPU-friendly tri indices [uint4] + + void* mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) + PxU32* mGRB_vertValency; //!< GRB: number of adjacent vertices to a vertex + PxU32* mGRB_adjVertStart; //!< GRB: offset for each vertex in the adjacency list + PxU32* mGRB_adjVertices; //!< GRB: list of adjacent vertices + + PxU32 mGRB_meshAdjVerticiesTotal; //!< GRB: total number of indices in the 'mGRB_adjVertices' + PxU32* mGRB_faceRemap; //!< GRB : gpu to cpu triangle indice remap + void* mGRB_BV32Tree; //!< GRB: BV32 tree + // End of GRB data ------------------ + + }; + + PX_ALIGN_PREFIX(16) + struct RTreePage + { + typedef PxF32 RTreeValue; + +#define RTREE_N 4 + RTreeValue minx[RTREE_N]; + RTreeValue miny[RTREE_N]; + RTreeValue minz[RTREE_N]; + RTreeValue maxx[RTREE_N]; + RTreeValue maxy[RTREE_N]; + RTreeValue maxz[RTREE_N]; + PxU32 ptrs[RTREE_N]; +#undef RTREE_N + } PX_ALIGN_SUFFIX(16); + + PX_ALIGN_PREFIX(16) + struct RTree + { + PxVec4 mBoundsMin, mBoundsMax, mInvDiagonal, mDiagonalScaler; // 16 + PxU32 mPageSize; + PxU32 mNumRootPages; + PxU32 mNumLevels; + PxU32 mTotalNodes; // 16 + PxU32 mTotalPages; + PxU32 mFlags; + RTreePage* mPages; + } PX_ALIGN_SUFFIX(16); + + class RTreeTriangleMesh : public TriangleMesh + { + public: + virtual ~RTreeTriangleMesh() {} + + RTree mRTree; + }; + + inline uint32 GetAllocatedSize(PxTriangleMesh& InTriangleMesh) + { + auto& TriangleMesh = static_cast(InTriangleMesh); + + uint64 Size = 0; + Size += sizeof(RTreeTriangleMesh); + + if (TriangleMesh.mVertices) + { + Size += TriangleMesh.mNbVertices * sizeof(PxVec3); + } + if (TriangleMesh.mTriangles) + { + const PxU32 triangleSize = TriangleMesh.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32); + Size += TriangleMesh.mNbTriangles * 3 * triangleSize; + } + if (TriangleMesh.mExtraTrigData) + { + Size += TriangleMesh.mNbTriangles * sizeof(PxU8); + } + if (TriangleMesh.mMaterialIndices) + { + Size += TriangleMesh.mNbTriangles * sizeof(PxU16); + } + if (TriangleMesh.mFaceRemap) + { + Size += TriangleMesh.mNbTriangles * sizeof(PxU32); + } + if (TriangleMesh.mAdjacencies) + { + Size += TriangleMesh.mNbTriangles * sizeof(PxU32) * 3; + } + ensure(Size < MAX_int32); + return Size; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.cpp new file mode 100644 index 00000000..271b3995 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.cpp @@ -0,0 +1,1356 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDefaultRenderer.h" +#include "VoxelMessages.h" +#include "IVoxelPool.h" +#include "VoxelRender/VoxelMesherAsyncWork.h" +#include "VoxelRender/Renderers/VoxelRendererMeshHandler.h" +#include "VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h" +#include "VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h" +#include "VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/VoxelRenderUtilities.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelData/VoxelData.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelRenderer); + +static TAutoConsoleVariable CVarFreezeRenderer( + TEXT("voxel.renderer.FreezeRenderer"), + 0, + TEXT("Stops renderer tick"), + ECVF_Default); + +FVoxelDefaultRenderer::FVoxelDefaultRenderer(const FVoxelRendererSettings& Settings) + : IVoxelRenderer(Settings) + , MeshHandler(Settings.bMergeChunks ? Settings.bDoNotMergeCollisionsAndNavmesh + ? StaticCastVoxelSharedRef(MakeVoxelShared(*this)) + : StaticCastVoxelSharedRef(MakeVoxelShared(*this)) + : StaticCastVoxelSharedRef(MakeVoxelShared(*this))) +{ + MeshHandler->Init(); +} + +TVoxelSharedRef FVoxelDefaultRenderer::Create(const FVoxelRendererSettings& Settings) +{ + TVoxelSharedRef Renderer = MakeShareable(new FVoxelDefaultRenderer(Settings)); + Renderer->OnMaterialInstanceCreated.AddThreadSafeSP(Settings.Data->Generator, &FVoxelGeneratorInstance::SetupMaterialInstance); + return Renderer; +} + +FVoxelDefaultRenderer::~FVoxelDefaultRenderer() +{ + check(IsInGameThread()); + VOXEL_FUNCTION_COUNTER(); + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderer, AllocatedSize); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultRenderer::Destroy() +{ + // This function is needed because the async tasks can keep the renderer alive while the voxel world is destroyed + + StopTicking(); + + // Destroy mesh handler & meshes + MeshHandler->StartDestroying(); + + for (auto& It : ChunksMap) + { + CancelTasks(It.Value); + if (It.Value.MeshId.IsValid()) + { + // Not really needed, but useful for error checks + MeshHandler->RemoveChunk(It.Value.MeshId); + } + } + + ChunksMap.Reset(); + MeshHandler.Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 FVoxelDefaultRenderer::UpdateChunks( + const FVoxelIntBox& Bounds, + const TArray& ChunksToUpdate, + const FVoxelOnChunkUpdateFinished& FinishDelegate) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Settings.bStaticWorld) + { + FVoxelMessages::Error("Can't update chunks with bStaticWorld = true!"); + return 0; + } + + if (ChunksToUpdate.Num() == 0) + { + return 0; + } + + const double Time = FPlatformTime::Seconds(); + for (auto& ChunkId : ChunksToUpdate) + { + auto& Chunk = ChunksMap.FindChecked(ChunkId); + Chunk.PendingUpdates.Add({ Time, FinishDelegate }); + // Trigger tasks if not already triggered: if they are, they will trigger new ones when their callback will be processed in Tick + StartTask(Chunk); + StartTask(Chunk); + } + + FlushQueuedTasks(); + + if (Settings.bDitherChunks) + { + VOXEL_SCOPE_COUNTER("Cancel Dithering"); + + FVoxelIntBoxWithValidity ChunksToRemoveBounds; + // First remove all chunks that are dithering out + for (auto& ChunkToRemove : ChunksToRemove) + { + FChunk& Chunk = ChunksMap.FindChecked(ChunkToRemove.Id); + if (Chunk.Bounds.Intersect(Bounds)) + { + ChunkToRemove.Time = 0; + ChunksToRemoveBounds += Chunk.Bounds; + } + } + + // Next force show all chunks dithering in overlapping the chunks dithering out we removed + // Else they will be holes + // This also covers chunks dithering in overlapping Bounds: + // if they are dithering in, some other chunk overlapping these same bounds must be dithering out + // This chunk will have been picked up in the iteration above + if (ChunksToRemoveBounds.IsValid()) + { + for (auto& ChunkToShow : ChunksToShow) + { + FChunk& Chunk = ChunksMap.FindChecked(ChunkToShow.Id); + if (Chunk.Bounds.Intersect(ChunksToRemoveBounds.GetBox())) + { + ChunkToShow.Time = 0; + } + } + } + + // Force update as we don't want to have any outdated chunks that could be used if UpdateLODs is called before Tick + if (ChunksToRemoveBounds.IsValid()) + { + // If invalid, no chunk was removed nor shown + ProcessChunksToRemoveOrShow(); + } + } + + Settings.DebugManager->ReportUpdatedChunks([&]() + { + TArray UpdatedChunks; + UpdatedChunks.Reserve(ChunksToUpdate.Num()); + for (auto& ChunkId : ChunksToUpdate) + { + auto& Chunk = ChunksMap.FindChecked(ChunkId); + UpdatedChunks.Add(Chunk.Bounds); + } + return UpdatedChunks; + }); + + return ChunksToUpdate.Num(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultRenderer::UpdateLODs(const uint64 InUpdateIndex, const TArray& ChunkUpdates) +{ + VOXEL_FUNCTION_COUNTER(); + + check(InUpdateIndex > 0); + if (Settings.bStaticWorld && InUpdateIndex != 1) + { + FVoxelMessages::Error("Can't update LODs with bStaticWorld = true!");\ + return; + } + +#if VOXEL_DEBUG + { + TSet Ids; + for (auto& ChunkUpdate : ChunkUpdates) + { + ensure(!Ids.Contains(ChunkUpdate.Id)); + Ids.Add(ChunkUpdate.Id); + } + } + for (auto& ChunkUpdate : ChunkUpdates) + { + if (auto* DebugChunk = DebugChunks.Find(ChunkUpdate.Id)) + { + ensure(*DebugChunk == ChunkUpdate.OldSettings); + if (ChunkUpdate.NewSettings.HasRenderChunk()) + { + *DebugChunk = ChunkUpdate.NewSettings; + } + else + { + DebugChunks.Remove(ChunkUpdate.Id); + } + } + else + { + ensure(!ChunkUpdate.OldSettings.HasRenderChunk()); + DebugChunks.Add(ChunkUpdate.Id, ChunkUpdate.NewSettings); + } + } +#endif + + UpdateIndex++; + if (!ensure(UpdateIndex == InUpdateIndex)) return; + + // Map used to know which chunks to wait for before dithering out + TMap>> OldChunksToNewChunks; + // Need to do it after the main pass, else OldChunksToNewChunks wouldn't be filled + TArray ChunksToDitherOutOrRemoveOnceNewChunksAreUpdated; + // Can't call ClearPreviousChunks in the Update loop as old chunks are not processed yet + TArray ChunksPendingClearPreviousChunks; + + for (auto& ChunkUpdate : ChunkUpdates) + { + const auto NewSettings = ChunkUpdate.NewSettings; + const auto OldSettings = ChunkUpdate.OldSettings; + + const auto GetChunk = [&]() -> FChunk& + { + FChunk* Chunk = ChunksMap.Find(ChunkUpdate.Id); + if (Chunk) + { + ensure(Chunk->LOD == ChunkUpdate.LOD && Chunk->Bounds == ChunkUpdate.Bounds); + } + else + { + ensure(!OldSettings.HasRenderChunk()); + Chunk = &ChunksMap.Add(ChunkUpdate.Id, FChunk(ChunkUpdate.Id, ChunkUpdate.LOD, ChunkUpdate.Bounds)); + } + check(Chunk); + return *Chunk; + }; + + FChunk& Chunk = GetChunk(); + // Can only have pending settings if dithering out (force visible = true) or waiting for new chunks (force visible = true, force collisions/navmesh) + ensure( + Chunk.Settings == Chunk.PendingSettings || + Chunk.GetState() == EChunkState::DitheringOut || + Chunk.GetState() == EChunkState::WaitingForNewChunks); + ensure(Chunk.PendingSettings == OldSettings); + + // Take a backup of the actual chunk settings + // These will be different than OldSettings if DitheringOut or WaitingForNewChunks + const auto OldChunksSettings = Chunk.Settings; + + // We set the chunk settings here so that we have the right priority when starting tasks below + Chunk.Settings = NewSettings; + + // By default, apply the visibility & transitions change + bool bApplyNewVisibilityAndMask = true; + + // Check if visibility changed (most common case) + if (NewSettings.bVisible != OldSettings.bVisible) + { + const auto CancelDithering = [&]() + { + VOXEL_SCOPE_COUNTER("CancelDithering"); + + ensure(Chunk.GetState() == EChunkState::DitheringIn || Chunk.GetState() == EChunkState::DitheringOut); + ensure(Settings.bDitherChunks); + ensure(Chunk.NumNewChunksLeft == 0); + if (Chunk.MeshId.IsValid()) + { + MeshHandler->ResetDithering(Chunk.MeshId); + } + if (Chunk.GetState() == EChunkState::DitheringOut) + { + ensure(Chunk.PreviousChunks.Num() == 0); + ChunksToRemove.RemoveAllSwap([&](auto& X) { return X.Id == Chunk.Id; }, false); + } + if (Chunk.GetState() == EChunkState::DitheringIn) + { + ensure(Settings.bDitherChunks); + // Note: will probably have previous chunks + ChunksToShow.RemoveAllSwap([&](auto& X) { return X.Id == Chunk.Id; }, false); + } + }; + + if (NewSettings.bVisible) + { + switch (Chunk.GetState()) + { + case EChunkState::Showed: + default: + { + ensure(false); + break; + } + case EChunkState::WaitingForNewChunks: + { + ensure(Chunk.NumNewChunksLeft != 0); + Chunk.NumNewChunksLeft = 0; + ensure(Chunk.Settings.HasRenderChunk() || (!Chunk.Tasks.MainTask.IsValid() && !Chunk.Tasks.TransitionsTask.IsValid())); + if (Chunk.BuiltData.MainChunk.IsValid()) + { + // Do not clear previous chunks now as it's not safe to do so while in Update (as old chunks have not been processed now) + // This chunk is already updated, no need to wait for it + ChunksPendingClearPreviousChunks.Add(Chunk.Id); + } + else + { + // We got put in the WaitingForNewChunks state without a finished task + // Start it now + StartTask(Chunk); + } + break; + } + case EChunkState::DitheringOut: + { + ensure(Chunk.MeshId.IsValid()); // Chunks that are dithering out always have a mesh + ensure(Chunk.NumNewChunksLeft == 0); + ensure(Chunk.PreviousChunks.Num() == 0); + CancelDithering(); + + // Tricky detail: when dithering out, we're not in the render octree anymore + // This means we're not updated when an edit happens + // However, we're still safe because editing cancels dithering! + // So if there was an edit, the chunk would already be deleted + + // This chunk is already updated, no need to wait for it + ChunksPendingClearPreviousChunks.Add(Chunk.Id); + break; + } + case EChunkState::DitheringIn: + { + ensure(false); // Cannot happen as it would mean we were dithering in with bVisible = false + ensure(Chunk.NumNewChunksLeft == 0); + CancelDithering(); + if (!Chunk.Tasks.MainTask.IsValid() && !Chunk.Tasks.TransitionsTask.IsValid()) + { + // If we have not tasks but still dithering in, then the mesh must be already updated + // No need to wait + ChunksPendingClearPreviousChunks.Add(Chunk.Id); + } + break; + } + case EChunkState::Hidden: + { + ensure(Chunk.NumNewChunksLeft == 0); + ensure(Chunk.PreviousChunks.Num() == 0); + if (Chunk.MeshId.IsValid()) + { + // Note: will be shown by ApplyPendingSettings below + if (Settings.bDitherChunks) + { + // Dither as we were hidden + // Dither out of the other chunks will happen when processing ChunksPendingClearPreviousChunks + // So both surface nets cases are correctly handled + DitherInChunk(Chunk, ChunkUpdate.PreviousChunks); + } + } + // This chunk is already updated, no need to wait for it + ChunksPendingClearPreviousChunks.Add(Chunk.Id); + break; + } + case EChunkState::NewChunk: + { + StartTask(Chunk); + break; + } + } + ensure(Chunk.NumNewChunksLeft == 0); + + // Set UpdateIndex as we are being showed + Chunk.UpdateIndex = UpdateIndex; + + Chunk.SetState(Settings.bDitherChunks ? EChunkState::DitheringIn : EChunkState::Showed, STATIC_FNAME("Turned visible")); + if (!Chunk.MeshId.IsValid()) + { + // If we don't have a mesh: + // - either we started a task that will update it later on + // - or a task has already finished & the resulting chunk was empty + ensure( + Chunk.Tasks.MainTask.IsValid() || + (Chunk.BuiltData.MainChunk.IsValid() && Chunk.BuiltData.MainChunk->IsEmpty())); + } + + // When updating if we don't have a mesh yet we always need previous chunks, even with no dithering because we need to wait for task to be done + // If we did have a mesh then we added ourselves to ChunksPendingClearPreviousChunks in the switch above + // In both cases we can safely add the previous chunks + for (auto& PreviousChunkId : ChunkUpdate.PreviousChunks) + { + OldChunksToNewChunks.FindOrAdd(PreviousChunkId).Add(ChunkUpdate.Id); + } + } + else // !NewSettings.bVisible + { + switch (Chunk.GetState()) + { + case EChunkState::NewChunk: + case EChunkState::Hidden: + default: + { + ensure(false); + break; + } + case EChunkState::WaitingForNewChunks: + { + ensure(Chunk.NumNewChunksLeft != 0); + ensure(Chunk.MeshId.IsValid() || Chunk.PreviousChunks.Num() != 0); // Either we had a mesh or chunks to wait for + // Keep previous chunks along, will be cleared when this will be cleared + break; + } + case EChunkState::DitheringOut: + { + ensure(false); // Cannot happen as it would mean we were dithering out with bVisible = true + ensure(Chunk.NumNewChunksLeft == 0); + ensure(Chunk.PreviousChunks.Num() == 0); + CancelDithering(); + break; + } + case EChunkState::DitheringIn: + { + ensure(Chunk.NumNewChunksLeft == 0); + // Note: might have previous chunks, keep them + CancelDithering(); + break; + } + case EChunkState::Showed: + { + // Note: can have previous chunks if main chunk finished dithering but transitions are still being computed + break; + } + } + + // We can't be in any of these states if we get here + ensureVoxelSlowNoSideEffects(!ChunksToRemove.FindByPredicate([&](const FChunkToRemove& ChunkToRemove) { return ChunkToRemove.Id == Chunk.Id; })); + ensureVoxelSlowNoSideEffects(!ChunksToShow.FindByPredicate([&](const FChunkToShow& ChunkToShow) { return ChunkToShow.Id == Chunk.Id; })); + + if (!NewSettings.HasRenderChunk()) + { + // If this chunk is being removed, no need to finish computing the tasks + CancelTasks(Chunk); + } + + if (!Chunk.MeshId.IsValid() && Chunk.PreviousChunks.Num() == 0) + { + ensure(Chunk.NumNewChunksLeft == 0); + if (!NewSettings.HasRenderChunk()) + { + // No mesh nor previous chunks: remove it immediately + DestroyChunk(Chunk); + continue; + } + else + { + // No mesh nor chunks to wait for: can just set the state to hidden + Chunk.SetState(EChunkState::Hidden, STATIC_FNAME("")); + } + } + else + { + Chunk.SetState(EChunkState::WaitingForNewChunks, STATIC_FNAME("Hiding chunk")); + ChunksToDitherOutOrRemoveOnceNewChunksAreUpdated.Add(Chunk.Id); + } + } + + // Can never happen after an update + // Might lead to some abrupt changes, but w/e it's pretty bad already if we get there + ensure(Chunk.GetState() != EChunkState::DitheringOut); + } + else + { + // Other case: visibility did not change. This is an update to the state of collisions/navmesh + // Much less frequent + switch (Chunk.GetState()) + { + case EChunkState::NewChunk: + { + // Hidden new chunks + ensure(!NewSettings.bVisible); + ensure(NewSettings.HasRenderChunk() && !OldSettings.HasRenderChunk()); + StartTask(Chunk); + Chunk.SetState(EChunkState::Hidden, STATIC_FNAME("Hidden New Chunk")); + break; + } + case EChunkState::Hidden: + { + // If we were hidden and we still are, something else changed. + // Check that we are still rendered + if (!NewSettings.HasRenderChunk()) + { + // If not remove the mesh if valid, and destroy + // Make sure to cancel any pending tasks first + CancelTasks(Chunk); + if (Chunk.MeshId.IsValid()) + { + MeshHandler->RemoveChunk(Chunk.MeshId); + Chunk.MeshId.Reset(); + } + DestroyChunk(Chunk); + continue; + } + // Else just stay hidden, ApplyPendingSettings below will do the job + break; + } + case EChunkState::DitheringIn: + { + // ApplyPendingSettings will do the job + ensure(NewSettings.bVisible); + break; + } + case EChunkState::Showed: + { + // ApplyPendingSettings will do the job + ensure(NewSettings.bVisible); + break; + } + case EChunkState::WaitingForNewChunks: + { + // ApplyPendingSettings will do the job + // Make sure we don't update visibility/transitions though + bApplyNewVisibilityAndMask = false; + // Should be invisible for the LOD tree, but visible here + ensure(!NewSettings.bVisible); + ensure(OldChunksSettings.bVisible); + break; + } + case EChunkState::DitheringOut: + { + // ApplyPendingSettings will do the job + // Make sure we don't update visibility/transitions though + bApplyNewVisibilityAndMask = false; + // Should be invisible for the LOD tree, but visible here + ensure(!NewSettings.bVisible); + ensure(OldChunksSettings.bVisible); + break; + } + default: ensure(false); + } + } + + // Settings were changed for priority, set them back + // Make sure to use OldChunksSettings and not OldSettings + Chunk.Settings = OldChunksSettings; + // Set new settings + Chunk.PendingSettings = NewSettings; + + if (Chunk.GetState() == EChunkState::WaitingForNewChunks) + { + // We don't want to hide it just yet if it's not visible anymore, as we need to dither it out/wait for new chunks to be updated + // Same for collisions/navmesh, we don't want things to fall through while new chunks are still loading + // So skip ApplyPendingSettings + continue; + } + + // Finally, apply all the new settings + // Only apply new visibility if it actually changed + // This is to keep a chunk that's dithering out or waiting for new chunks visible, even if for the LOD tree it's not + // They will correctly call ApplyPendingSettings once updated + ApplyPendingSettings(Chunk, bApplyNewVisibilityAndMask); + + // Else stack is cleared when debugging + ensureVoxelSlowNoSideEffects(&Chunk); + } + + FlushQueuedTasks(); + + { + VOXEL_SCOPE_COUNTER("Old Chunks"); + // Now that OldChunksToNewChunks is fully built, add previous chunks + for (uint64 OldChunkId : ChunksToDitherOutOrRemoveOnceNewChunksAreUpdated) + { + auto* NewChunks = OldChunksToNewChunks.Find(OldChunkId); + + // NewChunks is invalid when we don't have new chunks to replace us + // This seems to only happen when bEnableRender is set to false at runtime + + FChunk& OldChunk = ChunksMap.FindChecked(OldChunkId); + ensure(OldChunk.MeshId.IsValid() || OldChunk.PreviousChunks.Num() > 0); // We need to have a mesh or be waiting for previous ones + ensure(OldChunk.NumNewChunksLeft == 0); + ensure(OldChunk.GetState() == EChunkState::WaitingForNewChunks); + + if (NewChunks) + { + for (uint64 NewChunkId : *NewChunks) + { + auto& NewChunk = ChunksMap.FindChecked(NewChunkId); + // AddUnique: in some cases NewChunks is already referencing us + NewChunk.PreviousChunks.AddUnique(OldChunkId); + OldChunk.NumNewChunksLeft++; + } + } + + // Might need to remove it/dither it out now if no new chunks + if (OldChunk.NumNewChunksLeft == 0) + { + ClearPreviousChunks(OldChunk); + RemoveOrHideChunk(OldChunk); + } + } + } + + { + VOXEL_SCOPE_COUNTER("ClearPreviousChunks"); + // Process all meshes already displayed + // Need to do it because needs to be done after old chunks + for (uint64 Id : ChunksPendingClearPreviousChunks) + { + ClearPreviousChunks(ChunksMap.FindChecked(Id)); + } + } + + Settings.DebugManager->ReportRenderChunks([&]() + { + TArray Result; + Result.Reserve(ChunksMap.Num()); + for (auto& It : ChunksMap) + { + Result.Add(It.Value.Bounds); + } + return Result; + }); + + FlushQueuedTasks(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 FVoxelDefaultRenderer::GetTaskCount() const +{ + return UpdateIndex > 0 ? TaskCount.GetValue() : -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultRenderer::RecomputeMeshPositions() +{ + VOXEL_FUNCTION_COUNTER(); + + MeshHandler->RecomputeMeshPositions(); +} + +void FVoxelDefaultRenderer::ApplyNewMaterials() +{ + VOXEL_FUNCTION_COUNTER(); + + if (Settings.bStaticWorld) + { + FVoxelMessages::Error("Can't ApplyNewMaterials with bStaticWorld = true!"); + return; + } + + Settings.OnMaterialsChanged(); + + MeshHandler->ClearChunkMaterials(); + + for (auto& It : ChunksMap) + { + const auto& Chunk = It.Value; + if (Chunk.MeshId.IsValid() && ensure(Chunk.BuiltData.MainChunk.IsValid())) + { + MeshHandler->UpdateChunk( + Chunk.MeshId, + Chunk.Settings, + *Chunk.BuiltData.MainChunk, + Chunk.BuiltData.TransitionsChunk.Get(), + Chunk.BuiltData.TransitionsMask); + } + } +} + +void FVoxelDefaultRenderer::ApplyToAllMeshes(TFunctionRef Lambda) +{ + VOXEL_FUNCTION_COUNTER(); + + MeshHandler->ApplyToAllMeshes(Lambda); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultRenderer::CreateGeometry_AnyThread( + int32 LOD, + const FIntVector& ChunkPosition, + TArray& OutIndices, + TArray& OutVertices) const +{ + FVoxelMesherAsyncWork::CreateGeometry_AnyThread(*this, LOD, ChunkPosition, OutIndices, OutVertices); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDefaultRenderer::Tick(float) +{ + VOXEL_FUNCTION_COUNTER(); + + if (CVarFreezeRenderer.GetValueOnGameThread() != 0) + { + return; + } + + const double Time = FPlatformTime::Seconds(); + const double MaxTime = Time + Settings.MeshUpdatesBudget * 0.001f; + + { + VOXEL_SCOPE_COUNTER("MeshHandler Tick"); + MeshHandler->Tick(MaxTime); + } + + ProcessChunksToRemoveOrShow(); + ProcessMeshUpdates(MaxTime); + FlushQueuedTasks(); + + if (!OnWorldLoadedFired && UpdateIndex > 0 && TaskCount.GetValue() == 0 && TasksCallbacksQueue.IsEmpty()) + { + OnWorldLoaded.Broadcast(); + OnWorldLoadedFired = true; + } + + UpdateAllocatedSize(); + + Settings.DebugManager->ReportMeshTaskCount(TaskCount.GetValue()); + Settings.DebugManager->ReportMeshTasksCallbacksQueueNum(TasksCallbacksQueue.Num()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelDefaultRenderer::StartTask(FChunk& Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + auto& Task = MainOrTransitions == EMainOrTransitions::Main ? Chunk.Tasks.MainTask : Chunk.Tasks.TransitionsTask; + if (Task.IsValid()) + { + if (IfTaskExists == EIfTaskExists::DoNothing) + { + return; + } + else + { + ensure(IfTaskExists == EIfTaskExists::Assert); + ensure(false); + return; + } + } + + if (MainOrTransitions == EMainOrTransitions::Transitions) + { + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + if (Chunk.MeshId.IsValid()) + { + MeshHandler->SetTransitionsMaskForSurfaceNets(Chunk.MeshId, Chunk.Settings.TransitionsMask); + } + Chunk.BuiltData.TransitionsChunkCreationTime = FPlatformTime::Seconds(); // Make sure to always update the time + return; + } + if (Chunk.Settings.TransitionsMask == 0) + { + if (Chunk.BuiltData.TransitionsChunk.IsValid() && !Chunk.BuiltData.TransitionsChunk->IsEmpty()) + { + // Remove transitions + Chunk.BuiltData.TransitionsChunk.Reset(); + if (Chunk.MeshId.IsValid()) + { + // If we have a main chunk, use it. Else remove the mesh entirely. + if (Chunk.BuiltData.MainChunk.IsValid() && !Chunk.BuiltData.MainChunk->IsEmpty()) + { + MeshHandler->UpdateChunk(Chunk.MeshId, Chunk.Settings, *Chunk.BuiltData.MainChunk, nullptr, 0); + } + else + { + MeshHandler->RemoveChunk(Chunk.MeshId); + Chunk.MeshId.Reset(); + } + } + } + Chunk.BuiltData.TransitionsMask = 0; + Chunk.BuiltData.TransitionsChunkCreationTime = FPlatformTime::Seconds(); // Make sure to always update the time + return; + } + } + + Task = TUniquePtr>(new FVoxelMesherAsyncWork( + *this, + Chunk.Id, + Chunk.LOD, + Chunk.Bounds, + MainOrTransitions == EMainOrTransitions::Transitions, + MainOrTransitions == EMainOrTransitions::Transitions ? Chunk.Settings.TransitionsMask : 0)); + QueuedTasks[Chunk.Settings.bVisible][Chunk.Settings.bEnableCollisions].Emplace(Task.Get()); +} + +void FVoxelDefaultRenderer::CancelTasks(FChunk& Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Chunk.Tasks.MainTask.IsValid()) + { + CancelTask(Chunk.Tasks.MainTask); + } + if (Chunk.Tasks.TransitionsTask.IsValid()) + { + CancelTask(Chunk.Tasks.TransitionsTask); + } +} + +void FVoxelDefaultRenderer::ClearPreviousChunks(FChunk& Chunk) +{ + if (Chunk.PreviousChunks.Num() == 0) return; + + VOXEL_FUNCTION_COUNTER(); + + static TSet StackIds; + if (!ensure(!StackIds.Contains(Chunk.Id))) return; + StackIds.Add(Chunk.Id); + + for (uint64 PreviousChunkId : Chunk.PreviousChunks) + { + FChunk* PreviousChunkPtr = ChunksMap.Find(PreviousChunkId); + if (!ensure(PreviousChunkPtr)) continue; // This crashed on Prod + + FChunk& PreviousChunk = *PreviousChunkPtr; + if (PreviousChunk.UpdateIndex > Chunk.UpdateIndex) continue; // This previous chunk has been updated since it was added to our PreviousChunk list + + ensure(PreviousChunk.GetState() == EChunkState::WaitingForNewChunks); // Should be true if the UpdateIndex is correct + + PreviousChunk.NumNewChunksLeft--; + if (!ensure(PreviousChunk.NumNewChunksLeft >= 0)) continue; + + if (PreviousChunk.NumNewChunksLeft == 0) + { + // If 0 and have no previous chunks shouldn't be a previous chunk and should already be deleted + if (!ensure(PreviousChunk.MeshId.IsValid() || PreviousChunk.PreviousChunks.Num() > 0)) continue; + NewChunksFinished(PreviousChunk, Chunk); + } + } + + Chunk.PreviousChunks.Reset(); + + ensure(StackIds.Remove(Chunk.Id) == 1); +} + +void FVoxelDefaultRenderer::NewChunksFinished(FChunk& Chunk, const FChunk& NewChunk) +{ + VOXEL_FUNCTION_COUNTER(); + + // Only visible chunks need to wait + ensure(Chunk.Settings.bVisible); + + ensure(Chunk.GetState() == EChunkState::WaitingForNewChunks); + ensure(Chunk.NumNewChunksLeft == 0); + ensureVoxelSlowNoSideEffects(!ChunksToRemove.FindByPredicate([&](const FChunkToRemove& ChunkToRemove) { return ChunkToRemove.Id == Chunk.Id; })); + ensureVoxelSlowNoSideEffects(!ChunksToShow.FindByPredicate([&](const FChunkToShow& ChunkToShow) { return ChunkToShow.Id == Chunk.Id; })); + + ClearPreviousChunks(Chunk); // Recursively delete previous chunks + + if (Settings.bDitherChunks && Chunk.MeshId.IsValid()) // Could be 0 if we were waiting for previous chunks + { + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + // For surface nets, the only chunk that can transition is the high res one + // So check if we're the high res one, and if not just delete self + if (NewChunk.LOD > Chunk.LOD) + { + ApplyPendingSettings(Chunk, false); + ensure(Chunk.MeshId.IsValid()); + ensure(Chunk.Settings.bVisible); + + Chunk.SetState(EChunkState::DitheringOut, STATIC_FNAME("NewChunksFinished")); + MeshHandler->DitherChunk(Chunk.MeshId, EDitheringType::SurfaceNets_HighResToLowRes); + ChunksToRemove.Add(FChunkToRemove{ Chunk.Id, FPlatformTime::Seconds() + Settings.ChunksDitheringDuration }); + } + else + { + RemoveOrHideChunk(Chunk); + } + } + else + { + ApplyPendingSettings(Chunk, false); + ensure(Chunk.MeshId.IsValid()); + ensure(Chunk.Settings.bVisible); + + Chunk.SetState(EChunkState::DitheringOut, STATIC_FNAME("NewChunksFinished")); + MeshHandler->DitherChunk(Chunk.MeshId, EDitheringType::Classic_DitherOut); + // 2x: First dithering in new chunk, then dither out old chunk + ChunksToRemove.Add(FChunkToRemove{ Chunk.Id, FPlatformTime::Seconds() + 2 * Settings.ChunksDitheringDuration }); + } + } + else + { + RemoveOrHideChunk(Chunk); + } +} + +void FVoxelDefaultRenderer::RemoveOrHideChunk(FChunk& Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + ensure(Chunk.PreviousChunks.Num() == 0); + ensure(Chunk.NumNewChunksLeft == 0); + + // DitheringOut if dithering enabled, else it's removed once WaitingForNewChunks is over + ensure(Chunk.GetState() == EChunkState::DitheringOut || Chunk.GetState() == EChunkState::WaitingForNewChunks); + + ensureVoxelSlowNoSideEffects(!ChunksToRemove.FindByPredicate([&](const FChunkToRemove& ChunkToRemove) { return ChunkToRemove.Id == Chunk.Id; })); + ensureVoxelSlowNoSideEffects(!ChunksToShow.FindByPredicate([&](const FChunkToShow& ChunkToShow) { return ChunkToShow.Id == Chunk.Id; })); + + // Note: MeshId might be 0 if we were waiting for other chunks + + if (Chunk.GetState() == EChunkState::DitheringOut) + { + ensure(Chunk.MeshId.IsValid()); // If we were dithering out, we must have a mesh + // Reset the dithering to be safe when showing this mesh again + MeshHandler->ResetDithering(Chunk.MeshId); + } + + // Make sure to check PendingSettings and not Settings + if (Chunk.PendingSettings.HasRenderChunk()) + { + ApplyPendingSettings(Chunk, true); // Can apply the real settings now + ensure(Chunk.Settings == Chunk.PendingSettings); + Chunk.SetState(EChunkState::Hidden, STATIC_FNAME("RemoveOrHideChunk")); + } + else + { + CancelTasks(Chunk); + if (Chunk.MeshId.IsValid()) + { + MeshHandler->RemoveChunk(Chunk.MeshId); + Chunk.MeshId.Reset(); + } + DestroyChunk(Chunk); + // Chunk is removed, no need to apply pending settings + } +} + +void FVoxelDefaultRenderer::DitherInChunk(FChunk& Chunk, const TArray>& PreviousChunks) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(Chunk.MeshId.IsValid())) return; + + ensureVoxelSlowNoSideEffects(!ChunksToRemove.FindByPredicate([&](const FChunkToRemove& ChunkToRemove) { return ChunkToRemove.Id == Chunk.Id; })); + ensureVoxelSlowNoSideEffects(!ChunksToShow.FindByPredicate([&](const FChunkToShow& ChunkToShow) { return ChunkToShow.Id == Chunk.Id; })); + + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + // For surface nets, the only chunk that can transition is the high res one + // So check if we're the high res one, and if not just hide self until previous one finished dithering + + // If no previous chunk nothing to transition from, just show + if (PreviousChunks.Num() > 0) + { + const FChunk& PreviousChunk = ChunksMap[PreviousChunks[0]]; + // PreviousChunk is always valid, as the LOD of a specific Id is always the same + // No need to check UpdateIndex etc + + if (PreviousChunk.LOD > Chunk.LOD) + { + // We are the high res one + MeshHandler->DitherChunk(Chunk.MeshId, EDitheringType::SurfaceNets_LowResToHighRes); + ChunksToShow.Add(FChunkToShow{ Chunk.Id, FPlatformTime::Seconds() + Settings.ChunksDitheringDuration }); + } + else + { + // We are the low res: the high res will do the work + // Note: bTransitionsChunkIsBuilt is always true for surface nets, so dithering will happen at the same time for both + MeshHandler->HideChunk(Chunk.MeshId); + ChunksToShow.Add(FChunkToShow{ Chunk.Id, FPlatformTime::Seconds() + Settings.ChunksDitheringDuration }); + } + } + } + else + { + MeshHandler->DitherChunk(Chunk.MeshId, EDitheringType::Classic_DitherIn); + ChunksToShow.Add(FChunkToShow{ Chunk.Id, FPlatformTime::Seconds() + Settings.ChunksDitheringDuration }); + } +} + +void FVoxelDefaultRenderer::ApplyPendingSettings(FChunk& Chunk, bool bApplyVisibility) +{ + VOXEL_FUNCTION_COUNTER(); + + const bool bInitialHasMesh_Debug = Chunk.MeshId.IsValid(); + + const auto OldSettings = Chunk.Settings; + auto NewSettings = Chunk.PendingSettings; + + if (!bApplyVisibility) + { + NewSettings.bVisible = OldSettings.bVisible; + // Make sure the TransitionsMask stays the same as we do not want to update transitions + NewSettings.TransitionsMask = OldSettings.TransitionsMask; + } + + // Only these two states are allowed to have different settings + ensure( + Chunk.GetState() == EChunkState::DitheringOut || + Chunk.GetState() == EChunkState::WaitingForNewChunks || + NewSettings == Chunk.PendingSettings); + + // ApplyPendingSettings does not handle removing a chunk + ensure(NewSettings.HasRenderChunk()); + + Chunk.Settings = NewSettings; + + if (NewSettings.bVisible != OldSettings.bVisible || + NewSettings.bEnableCollisions != OldSettings.bEnableCollisions || + NewSettings.bEnableNavmesh != OldSettings.bEnableNavmesh) + { + if (Chunk.MeshId.IsValid() && ensure(Chunk.BuiltData.MainChunk.IsValid())) // If we have a mesh we must have a built chunk + { + MeshHandler->UpdateChunk( + Chunk.MeshId, + Chunk.Settings, + *Chunk.BuiltData.MainChunk, + Chunk.BuiltData.TransitionsChunk.Get(), + Chunk.BuiltData.TransitionsMask); + } + } + + // Important: do not update transitions if bApplyVisibility = false + // Note: this might not be needed since we copying OldSettings.TransitionsMask to NewSettings.TransitionsMask, but do it anyways + // as it would be a waste of CPU time to compute new transitions for a chunk dithering out + if (bApplyVisibility) + { + // Check transitions now + const uint8 WantedMask = NewSettings.TransitionsMask; + if (Chunk.Tasks.TransitionsTask.IsValid()) + { + if (Chunk.Tasks.TransitionsTask->TransitionsMask != WantedMask) + { + // Task already started and has different mask, cancel it + CancelTask(Chunk.Tasks.TransitionsTask); + if (Chunk.BuiltData.TransitionsMask != WantedMask) + { + // Start new task only if mask changed + StartTask(Chunk); + } + else + { + // Could be that this task was triggered by an update + // If so we might need to start a new task even if the mask is the same + CheckPendingUpdates(Chunk); + } + } + } + else + { + if (Chunk.BuiltData.TransitionsMask != WantedMask) + { + // Mask changed, start new task + StartTask(Chunk); + } + } + } + + // if bApplyVisibility = false, we must not change the MeshId + ensure(bApplyVisibility || bInitialHasMesh_Debug == Chunk.MeshId.IsValid()); +} + +void FVoxelDefaultRenderer::CheckPendingUpdates(FChunk& Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + for (int32 Index = 0; Index < Chunk.PendingUpdates.Num(); Index++) + { + const auto& PendingUpdate = Chunk.PendingUpdates[Index]; + if (PendingUpdate.WantedUpdateTime > Chunk.BuiltData.MainChunkCreationTime) + { + StartTask(Chunk); + } + if (PendingUpdate.WantedUpdateTime > Chunk.BuiltData.TransitionsChunkCreationTime) + { + StartTask(Chunk); + } + if (PendingUpdate.WantedUpdateTime < FMath::Min(Chunk.BuiltData.MainChunkCreationTime, Chunk.BuiltData.TransitionsChunkCreationTime)) + { + PendingUpdate.OnUpdateFinished.Broadcast(Chunk.Bounds); + Chunk.PendingUpdates.RemoveAtSwap(Index); + Index--; + } + } +} + +void FVoxelDefaultRenderer::ProcessChunksToRemoveOrShow() +{ + VOXEL_FUNCTION_COUNTER(); + + const double Time = FPlatformTime::Seconds(); + + // Process ChunksToShow before ChunksToRemove for action queue ordering + + { + VOXEL_SCOPE_COUNTER("Processing ChunksToShow"); + ensure(Settings.bDitherChunks || ChunksToShow.Num() == 0); + for (int32 Index = 0; Index < ChunksToShow.Num(); Index++) + { + const auto ChunkToShow = ChunksToShow[Index]; + if (ChunkToShow.Time < Time) + { + FChunk& Chunk = ChunksMap.FindChecked(ChunkToShow.Id); + if (Chunk.GetState() != EChunkState::DitheringIn) continue; // Chunk is not dithering in anymore + + // ensure(Chunk.PreviousChunks.Num() == 0); Not always true: main chunk can have finished dithering but transitions still being computed + Chunk.SetState(EChunkState::Showed, STATIC_FNAME("ChunkToShow")); + + if (Chunk.MeshId.IsValid()) + { + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + // If we were the low res chunk we were hidden + MeshHandler->ShowChunk(Chunk.MeshId); + } + else + { + // Needed if it was canceled in UpdateChunks + MeshHandler->ResetDithering(Chunk.MeshId); + } + } + + ChunksToShow.RemoveAtSwap(Index, 1, false); + Index--; // Go back to process the element we swapped + } + } + } + + { + VOXEL_SCOPE_COUNTER("Processing ChunksToRemove"); + ensure(Settings.bDitherChunks || ChunksToRemove.Num() == 0); + for (int32 Index = 0; Index < ChunksToRemove.Num(); Index++) + { + const auto ChunkToRemove = ChunksToRemove[Index]; + if (ChunkToRemove.Time < Time) + { + FChunk& Chunk = ChunksMap.FindChecked(ChunkToRemove.Id); + if (Chunk.GetState() != EChunkState::DitheringOut) continue; // Chunk is not dithering out anymore + + ChunksToRemove.RemoveAtSwap(Index, 1, false); + Index--; // Go back to process the element we swapped + + // Do it after so that it's not in ChunksToRemove anymore + // for the checks to pass + RemoveOrHideChunk(Chunk); + } + } + } +} + +void FVoxelDefaultRenderer::ProcessMeshUpdates(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelTaskCallback Callback; + while ( // First check the time, else dequeued elements aren't processed! + FPlatformTime::Seconds() < MaxTime && + TasksCallbacksQueue.Dequeue(Callback)) + { + FChunk* Chunk = ChunksMap.Find(Callback.ChunkId); + if (!Chunk) continue; + + auto& Tasks = Chunk->Tasks; + auto& Task = Callback.bIsTransitionTask ? Tasks.TransitionsTask : Tasks.MainTask; + if (!Task.IsValid() || Task->TaskId != Callback.TaskId) continue; // If task was canceled + if (!ensure(Task->IsDone())) continue; // Must be done if we're in the callback + + // Move built data + auto& BuiltData = Chunk->BuiltData; + const auto PreviousBuiltData = BuiltData; + if (Callback.bIsTransitionTask) + { + ensure(Task->TransitionsMask == Chunk->Settings.TransitionsMask); // Should have been canceled + BuiltData.TransitionsMask = Task->TransitionsMask; + BuiltData.TransitionsChunk = Task->Chunk; + BuiltData.TransitionsChunkCreationTime = Task->CreationTime; + } + else + { + BuiltData.MainChunk = Task->Chunk; + BuiltData.MainChunkCreationTime = Task->CreationTime; + } + + // Finally, delete the task + Task.Reset(); + + // Do nothing while the main chunk isn't valid - we don't want to have unneeded updates for transitions then main + if (BuiltData.MainChunk.IsValid()) + { + auto& MeshId = Chunk->MeshId; + const auto Update = [&]() + { + if (!MeshId.IsValid()) + { + MeshId = MeshHandler->AddChunk(Chunk->LOD, Chunk->Bounds.Min); + } + MeshHandler->UpdateChunk(MeshId, Chunk->Settings, *BuiltData.MainChunk, BuiltData.TransitionsChunk.Get(), BuiltData.TransitionsMask); + + if (Settings.bStaticWorld) + { + // Free up memory ASAP + BuiltData.MainChunk.Reset(); + BuiltData.TransitionsChunk.Reset(); + } + }; + + const bool bTransitionsChunkIsBuilt = + BuiltData.TransitionsChunk.IsValid() || + Chunk->Settings.TransitionsMask == 0 || + Settings.RenderType == EVoxelRenderType::SurfaceNets; + + if (BuiltData.MainChunk->IsEmpty() && (!BuiltData.TransitionsChunk.IsValid() || BuiltData.TransitionsChunk->IsEmpty())) + { + // Both empty, remove mesh if existing + if (MeshId.IsValid()) + { + MeshHandler->RemoveChunk(MeshId); + MeshId = {}; + } + } + else + { + Update(); + + ensure(MeshId.IsValid()); + + // Dither in if first update + // If first load and LOD 0, don't dither as it doesn't look nice to have the world dithering under the player + if (Settings.bDitherChunks && + !PreviousBuiltData.MainChunk.IsValid() && + !(UpdateIndex == 1 && Chunk->LOD == 0)) + { + // Can be a first update if: + // - we are a showed new chunks that's dithering in + // - we are a hidden chunk that's updated for the first time. If so don't dither in + ensure(Chunk->GetState() == EChunkState::Hidden || Chunk->GetState() == EChunkState::DitheringIn); + if (Chunk->GetState() == EChunkState::DitheringIn) + { + DitherInChunk(*Chunk, Chunk->PreviousChunks); + } + } + } + + // Dither out/remove previous chunks only once transitions are built too + // Note: bTransitionsChunkIsBuilt is always true for surface nets + if (bTransitionsChunkIsBuilt) + { + ClearPreviousChunks(*Chunk); + } + } + else + { + ensure(!Chunk->MeshId.IsValid()); + } + + // Start new tasks as needed + CheckPendingUpdates(*Chunk); + } +} + +void FVoxelDefaultRenderer::FlushQueuedTasks() +{ + VOXEL_FUNCTION_COUNTER(); + + const auto Flush = [&](bool bVisible, bool bHasCollisions) + { + auto& Tasks = QueuedTasks[bVisible][bHasCollisions]; + if (Tasks.Num() > 0) + { + TaskCount.Add(Tasks.Num()); + const auto TaskType = + bVisible + ? bHasCollisions + ? EVoxelTaskType::VisibleCollisionsChunksMeshing + : EVoxelTaskType::VisibleChunksMeshing + : bHasCollisions + ? EVoxelTaskType::CollisionsChunksMeshing + : EVoxelTaskType::ChunksMeshing; + Settings.Pool->QueueTasks(TaskType, Tasks); + Tasks.Reset(); + } + }; + Flush(false, false); + Flush(false, true); + Flush(true, false); + Flush(true, true); +} + +void FVoxelDefaultRenderer::DestroyChunk(FChunk& Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + ensure(!Chunk.MeshId.IsValid()); + ensure(Chunk.PreviousChunks.Num() == 0); + ensureVoxelSlowNoSideEffects(!ChunksToRemove.FindByPredicate([&](const FChunkToRemove& ChunkToRemove) { return ChunkToRemove.Id == Chunk.Id; })); + ensureVoxelSlowNoSideEffects(!ChunksToShow.FindByPredicate([&](const FChunkToShow& ChunkToShow) { return ChunkToShow.Id == Chunk.Id; })); + + if (!ensure(!Chunk.Tasks.MainTask.IsValid()) || + !ensure(!Chunk.Tasks.TransitionsTask.IsValid())) + { + CancelTasks(Chunk); + } + + for (auto& PendingUpdate : Chunk.PendingUpdates) + { + // We must always fire all delegates + PendingUpdate.OnUpdateFinished.Broadcast(FVoxelIntBox()); + } + ensure(ChunksMap.Remove(Chunk.Id) == 1); +} + +void FVoxelDefaultRenderer::UpdateAllocatedSize() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderer, AllocatedSize); + + AllocatedSize = 0; + AllocatedSize += ChunksMap.GetAllocatedSize(); + AllocatedSize += ChunksToRemove.GetAllocatedSize(); + AllocatedSize += ChunksToShow.GetAllocatedSize(); + + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelRenderer, AllocatedSize); +} + +void FVoxelDefaultRenderer::CancelTask(TUniquePtr>& Task) +{ + VOXEL_FUNCTION_COUNTER(); + + check(Task.IsValid()); + + const bool bIsDone = Task->CancelAndAutodelete(); + Task.Release(); + if (!bIsDone) + { + // If IsDone, QueueChunkCallback_AnyThread was called + ensure(TaskCount.Decrement() >= 0); + } +} + +void FVoxelDefaultRenderer::QueueChunkCallback_AnyThread(uint64 TaskId, uint64 ChunkId, bool bIsTransitionTask) +{ + ensure(TaskCount.Decrement() >= 0); + TasksCallbacksQueue.Enqueue({TaskId, ChunkId, bIsTransitionTask}); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.h new file mode 100644 index 00000000..c4d3d4c7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.h @@ -0,0 +1,220 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelMesherAsyncWork.h" +#include "VoxelRender/VoxelChunkToUpdate.h" +#include "VoxelRendererMeshHandler.h" +#include "VoxelTickable.h" +#include "VoxelQueueWithNum.h" + +struct FVoxelChunkMesh; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Renderer"), STAT_VoxelRenderer, STATGROUP_VoxelMemory, VOXEL_API); + +class FVoxelDefaultRenderer : public IVoxelRenderer, public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + static TVoxelSharedRef Create(const FVoxelRendererSettings& Settings); + virtual ~FVoxelDefaultRenderer() override; + +private: + explicit FVoxelDefaultRenderer(const FVoxelRendererSettings& Settings); + +public: + //~ Begin IVoxelRender Interface + virtual void Destroy() override; + + virtual int32 UpdateChunks(const FVoxelIntBox& Bounds, const TArray& ChunksToUpdate, const FVoxelOnChunkUpdateFinished& FinishDelegate) override; + virtual void UpdateLODs(uint64 InUpdateIndex, const TArray& ChunkUpdates) override; + + virtual int32 GetTaskCount() const override; + + virtual void RecomputeMeshPositions() override; + virtual void ApplyNewMaterials() override; + virtual void ApplyToAllMeshes(TFunctionRef Lambda) override; + + virtual void CreateGeometry_AnyThread( + int32 LOD, + const FIntVector& ChunkPosition, + TArray& OutIndices, + TArray& OutVertices) const override; + //~ End IVoxelRender Interface + + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +private: + enum class EChunkState : uint8 + { + NewChunk, + Hidden, + DitheringIn, + Showed, + WaitingForNewChunks, + DitheringOut + }; + enum class EMainOrTransitions : uint8 + { + Main, + Transitions + }; + struct FChunk + { + const uint64 Id; + const uint8 LOD; + const FVoxelIntBox Bounds; + + FChunk(uint64 Id, uint8 LOD, const FVoxelIntBox& Bounds) + : Id(Id) + , LOD(LOD) + , Bounds(Bounds) + { + } + + private: + EChunkState State = EChunkState::NewChunk; + +#if VOXEL_DEBUG + struct FChunkStateDebug + { + EChunkState OldChunkState; + EChunkState NewChunkState; + FName DebugName; + }; + TArray StateHistory; +#endif + + public: + FORCEINLINE EChunkState GetState() const + { + return State; + } + FORCEINLINE void SetState(EChunkState NewState, FName DebugName) + { +#if VOXEL_DEBUG + StateHistory.Add(FChunkStateDebug{ State, NewState, DebugName }); +#endif + State = NewState; + } + + public: + struct FChunkTasks + { + TUniquePtr> MainTask; + TUniquePtr> TransitionsTask; + }; + FChunkTasks Tasks; + + struct FChunkBuiltData + { + uint8 TransitionsMask = 0; + double MainChunkCreationTime = 0; + double TransitionsChunkCreationTime = 0; + TVoxelSharedPtr MainChunk; + TVoxelSharedPtr TransitionsChunk; + }; + FChunkBuiltData BuiltData; + + IVoxelRendererMeshHandler::FChunkId MeshId; + + // Settings to be applied once eg new chunks are spawned + FVoxelChunkSettings PendingSettings{}; + // Current settings + FVoxelChunkSettings Settings{}; + + struct FPendingUpdate + { + // We want the mesh to be from a task that was built >= at this time + double WantedUpdateTime = 0; + // Callback once we have a mesh recent enough + FVoxelOnChunkUpdateFinished OnUpdateFinished; + }; + TArray> PendingUpdates; + + // Chunks that were shown at this position before this one was shown, and that need to be dithered out + // once this chunk is updated + TArray> PreviousChunks; + // Number of new chunks left to update + int32 NumNewChunksLeft = 0; + + // Set when the chunk is being shown + // This is needed to track if chunks in PreviousChunks are still valid and haven't been switched back to Show + // Else we'd be decreasing a wrong NumNewChunksLeft + uint64 UpdateIndex = 0; + }; + TMap ChunksMap; + + struct FChunkToRemove + { + uint64 Id = 0; + double Time = 0; // Time at which to remove the chunk + }; + TArray ChunksToRemove; + + struct FChunkToShow + { + uint64 Id = 0; + double Time = 0; // Time at which to stop dithering the chunk + }; + TArray ChunksToShow; + + TArray QueuedTasks[2][2]; // [bVisible][bHasCollisions] + + enum class EIfTaskExists : uint8 + { + DoNothing, + Assert + }; + + template + void StartTask(FChunk& Chunk); + void CancelTasks(FChunk& Chunk); + void ClearPreviousChunks(FChunk& Chunk); + void NewChunksFinished(FChunk& Chunk, const FChunk& NewChunk); + void RemoveOrHideChunk(FChunk& Chunk); + void DitherInChunk(FChunk& Chunk, const TArray>& PreviousChunks); + void ApplyPendingSettings(FChunk& Chunk, bool bApplyVisibility); + void CheckPendingUpdates(FChunk& Chunk); + + void ProcessChunksToRemoveOrShow(); + void ProcessMeshUpdates(double MaxTime); + void FlushQueuedTasks(); + + void DestroyChunk(FChunk& Chunk); + +private: + // Need shared ptr for async callbacks + TVoxelSharedPtr MeshHandler; + + FThreadSafeCounter TaskCount; + uint64 UpdateIndex = 0; + bool OnWorldLoadedFired = false; + +#if VOXEL_DEBUG + TMap DebugChunks; +#endif + +private: + uint32 AllocatedSize = 0; + + void UpdateAllocatedSize(); + +public: + void QueueChunkCallback_AnyThread(uint64 TaskId, uint64 ChunkId, bool bIsTransitionTask); + +private: + struct FVoxelTaskCallback + { + uint64 TaskId; + uint64 ChunkId; + bool bIsTransitionTask; + }; + TVoxelQueueWithNum TasksCallbacksQueue; + + void CancelTask(TUniquePtr>& Task); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelMesherAsyncWork.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelMesherAsyncWork.cpp new file mode 100644 index 00000000..bd2a5299 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelMesherAsyncWork.cpp @@ -0,0 +1,182 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelMesherAsyncWork.h" +#include "VoxelRender/Renderers/VoxelDefaultRenderer.h" +#include "VoxelRender/Meshers/VoxelMarchingCubeMesher.h" +#include "VoxelRender/Meshers/VoxelCubicMesher.h" +#include "VoxelRender/Meshers/VoxelSurfaceNetMesher.h" +#include "VoxelRender/VoxelChunkMesh.h" + +#include "Async/Async.h" +#include "Misc/MessageDialog.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" + +FVoxelMesherAsyncWork::FVoxelMesherAsyncWork( + FVoxelDefaultRenderer& Renderer, + const uint64 ChunkId, + const int32 LOD, + const FVoxelIntBox& Bounds, + const bool bIsTransitionTask, + const uint8 TransitionsMask) + : FVoxelAsyncWork(STATIC_FNAME("FVoxelMesherAsyncWork"), Renderer.Settings.PriorityDuration) + , ChunkId(ChunkId) + , LOD(LOD) + , ChunkPosition(Bounds.Min) + , bIsTransitionTask(bIsTransitionTask) + , TransitionsMask(TransitionsMask) + , Renderer(Renderer.AsShared()) + , PriorityHandler(Bounds, Renderer.GetInvokersPositionsForPriorities()) +{ + check(IsInGameThread()); + ensure(!bIsTransitionTask || TransitionsMask != 0); +} + +FVoxelMesherAsyncWork::~FVoxelMesherAsyncWork() +{ +} + +static void ShowGeneratorError(TVoxelWeakPtr Data) +{ + static TSet> IgnoredDatas; + if (!IgnoredDatas.Contains(Data)) + { + const auto Result = FMessageDialog::Open( + EAppMsgType::YesNo, + VOXEL_LOCTEXT( + "Error: The generator is returning different values for the same position/LOD.\n" + "Please check your code.\n" + "If you're using a voxel graph, this is an internal error, please report it to the developer.\n" + "Hide future errors?")); + + if (Result == EAppReturnType::Yes) + { + IgnoredDatas.Add(Data); + } + } +} + +void FVoxelMesherAsyncWork::DoWork() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + auto PinnedRenderer = Renderer.Pin(); + if (IsCanceled()) return; + if (!ensure(PinnedRenderer.IsValid())) return; // Either we're canceled, or the renderer is valid + + const auto Mesher = GetMesher( + PinnedRenderer->Settings, + LOD, + ChunkPosition, + bIsTransitionTask, + TransitionsMask); + + CreationTime = FPlatformTime::Seconds(); + + if (PinnedRenderer->Settings.bRenderWorld) + { + const auto MesherChunk = Mesher->CreateFullChunk(); + if (MesherChunk.IsValid()) + { + Chunk = MesherChunk.ToSharedRef(); + } + else + { + AsyncTask(ENamedThreads::GameThread, [Data = MakeVoxelWeakPtr(PinnedRenderer->Settings.Data)]() { ShowGeneratorError(Data); }); + Chunk = Mesher->CreateEmptyChunk(); + } + } + else + { + // If we're not rendering the world, we only need the geometry for collisions/navmesh + + TArray Indices; + TArray Vertices; + Mesher->CreateGeometry(Indices, Vertices); + + Chunk = MakeVoxelShared(); + Chunk->SetIsSingle(true); + FVoxelChunkMeshBuffers& Buffers = Chunk->CreateSingleBuffers(); + + Buffers.Indices = MoveTemp(Indices); + Buffers.Positions = MoveTemp(Vertices); + } + + FVoxelUtilities::DeleteOnGameThread_AnyThread(PinnedRenderer); +} + +void FVoxelMesherAsyncWork::PostDoWork() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + auto RendererPtr = Renderer.Pin(); + if (ensure(RendererPtr.IsValid())) + { + RendererPtr->QueueChunkCallback_AnyThread(TaskId, ChunkId, bIsTransitionTask); + FVoxelUtilities::DeleteOnGameThread_AnyThread(RendererPtr); + } +} + +uint32 FVoxelMesherAsyncWork::GetPriority() const +{ + return PriorityHandler.GetPriority(); +} + +TUniquePtr FVoxelMesherAsyncWork::GetMesher( + const FVoxelRendererSettings& Settings, + int32 LOD, + const FIntVector& ChunkPosition, + bool bIsTransitionTask, + uint8 TransitionsMask) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + switch (Settings.RenderType) + { + default: + case EVoxelRenderType::MarchingCubes: + { + if (bIsTransitionTask) + { + return MakeUnique(LOD, ChunkPosition, Settings, TransitionsMask); + } + else + { + return MakeUnique(LOD, ChunkPosition, Settings); + } + } + case EVoxelRenderType::Cubic: + { + if (bIsTransitionTask) + { + return MakeUnique(LOD, ChunkPosition, Settings, TransitionsMask); + } + else + { + return MakeUnique(LOD, ChunkPosition, Settings); + } + } + case EVoxelRenderType::SurfaceNets: + { + if (bIsTransitionTask) + { + check(false); + return nullptr; + } + else + { + return MakeUnique(LOD, ChunkPosition, Settings); + } + } + } +} + +void FVoxelMesherAsyncWork::CreateGeometry_AnyThread( + const FVoxelDefaultRenderer& Renderer, + int32 LOD, + const FIntVector& ChunkPosition, + TArray& OutIndices, + TArray& OutVertices) +{ + const auto Mesher = GetMesher(Renderer.Settings, LOD, ChunkPosition, false, 0); + Mesher->CreateGeometry(OutIndices, OutVertices); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.cpp new file mode 100644 index 00000000..fd1900d7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.cpp @@ -0,0 +1,395 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRendererBasicMeshHandler.h" +#include "VoxelRender/VoxelRenderUtilities.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelChunkMaterials.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" +#include "IVoxelPool.h" +#include "VoxelAsyncWork.h" + +#include "Async/Async.h" + +class FVoxelBasicMeshMergeWork : public FVoxelAsyncWork +{ +public: + static FVoxelBasicMeshMergeWork* Create( + FVoxelRendererBasicMeshHandler& Handler, + FVoxelRendererBasicMeshHandler::FChunkInfoRef ChunkInfoRef, + FVoxelChunkMeshesToBuild&& MeshesToBuild) + { + auto* ChunkInfo = Handler.GetChunkInfo(ChunkInfoRef); + check(ChunkInfo); + return new FVoxelBasicMeshMergeWork( + ChunkInfoRef, + ChunkInfo->Position, + Handler, + ChunkInfo->UpdateIndex.ToSharedRef(), + MoveTemp(MeshesToBuild)); + } + +private: + const FVoxelRendererBasicMeshHandler::FChunkInfoRef ChunkInfoRef; + const FIntVector Position; + const FVoxelRendererSettingsBase RendererSettings; + const TVoxelWeakPtr Handler; + + const FVoxelChunkMeshesToBuild MeshesToBuild; + const TVoxelSharedRef UpdateIndexPtr; + const int32 UpdateIndex; + + FVoxelBasicMeshMergeWork( + FVoxelRendererBasicMeshHandler::FChunkInfoRef Ref, + const FIntVector& Position, + FVoxelRendererBasicMeshHandler& Handler, + const TVoxelSharedRef& UpdateIndexPtr, + FVoxelChunkMeshesToBuild&& MeshesToBuild) + : FVoxelAsyncWork(STATIC_FNAME("FVoxelBasicMeshMergeWork"), 1e9, true) + , ChunkInfoRef(Ref) + , Position(Position) + , RendererSettings(static_cast(Handler.Renderer.Settings)) + , Handler(StaticCastVoxelSharedRef(Handler.AsShared())) + , MeshesToBuild(MoveTemp(MeshesToBuild)) + , UpdateIndexPtr(UpdateIndexPtr) + , UpdateIndex(UpdateIndexPtr->GetValue()) + { + } + ~FVoxelBasicMeshMergeWork() = default; + + virtual uint32 GetPriority() const override + { + return 0; + } + virtual void DoWork() override + { + if (UpdateIndexPtr->GetValue() > UpdateIndex) + { + // Canceled + return; + } + auto BuiltMeshes = FVoxelRenderUtilities::BuildMeshes_AnyThread(MeshesToBuild, RendererSettings, Position, *UpdateIndexPtr, UpdateIndex); + if (!BuiltMeshes.IsValid()) + { + // Canceled + return; + } + auto HandlerPinned = Handler.Pin(); + if (HandlerPinned.IsValid()) + { + // Queue callback + HandlerPinned->MeshMergeCallback(ChunkInfoRef, UpdateIndex, MoveTemp(BuiltMeshes)); + FVoxelUtilities::DeleteOnGameThread_AnyThread(HandlerPinned); + } + } +}; + +FVoxelRendererBasicMeshHandler::~FVoxelRendererBasicMeshHandler() +{ + FlushBuiltDataQueue(); + FlushActionQueue(MAX_dbl); + + ensure(ChunkInfos.Num() == 0); +} + +IVoxelRendererMeshHandler::FChunkId FVoxelRendererBasicMeshHandler::AddChunkImpl(int32 LOD, const FIntVector& Position) +{ + return ChunkInfos.Add(FChunkInfo::Create(LOD, Position)); +} + +void FVoxelRendererBasicMeshHandler::ApplyAction(const FAction& Action) +{ + VOXEL_FUNCTION_COUNTER(); + + switch (Action.Action) + { + case EAction::UpdateChunk: + { + check(Action.UpdateChunk().InitialCall.MainChunk); + const auto& MainChunk = *Action.UpdateChunk().InitialCall.MainChunk; + const auto* TransitionChunk = Action.UpdateChunk().InitialCall.TransitionChunk; + + // This should never happen, as the chunk should be removed instead + ensure(!MainChunk.IsEmpty() || (TransitionChunk && !TransitionChunk->IsEmpty())); + + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + if (!ChunkInfo.Materials.IsValid()) + { + ChunkInfo.Materials = MakeShared(); + } + if (!ChunkInfo.UpdateIndex.IsValid()) + { + ChunkInfo.UpdateIndex = MakeVoxelShared(); + } + + // Cancel any previous build task + // Note: we do not clear the built data, as it could still be used + // The added cost of applying the update is probably worth it compared to stalling the entire queue waiting for an update + ChunkInfo.UpdateIndex->Increment(); + + // Find the meshes to build (= copying mesh buffers to proc mesh buffers) + FVoxelChunkMeshesToBuild MeshesToBuild = FVoxelRenderUtilities::GetMeshesToBuild( + ChunkInfo.LOD, + ChunkInfo.Position, + Renderer.Settings, + Action.UpdateChunk().InitialCall.ChunkSettings, + *ChunkInfo.Materials, + MainChunk, + TransitionChunk, + Renderer.OnMaterialInstanceCreated, + ChunkInfo.DitheringInfo); + + // Start a task to asynchronously build them + auto* Task = FVoxelBasicMeshMergeWork::Create(*this, { Action.ChunkId, ChunkInfo.UniqueId }, MoveTemp(MeshesToBuild)); + Renderer.Settings.Pool->QueueTask(EVoxelTaskType::MeshMerge, Task); + + FAction NewAction; + NewAction.Action = EAction::UpdateChunk; + NewAction.ChunkId = Action.ChunkId; + NewAction.UpdateChunk().AfterCall.UpdateIndex = ChunkInfo.UpdateIndex->GetValue(); + NewAction.UpdateChunk().AfterCall.DistanceFieldVolumeData = Action.UpdateChunk().InitialCall.MainChunk->GetDistanceFieldVolumeData(); + ActionQueue.Enqueue(NewAction); + + if (Renderer.Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + SetTransitionsMaskForSurfaceNets(Action.ChunkId, Action.UpdateChunk().InitialCall.ChunkSettings.TransitionsMask); + } + break; + } + case EAction::RemoveChunk: + case EAction::DitherChunk: + case EAction::ResetDithering: + case EAction::SetTransitionsMaskForSurfaceNets: + case EAction::HideChunk: + case EAction::ShowChunk: + { + ActionQueue.Enqueue(Action); + break; + } + default: ensure(false); + } +} + +void FVoxelRendererBasicMeshHandler::ClearChunkMaterials() +{ + for (auto& ChunkInfo : ChunkInfos) + { + if (ChunkInfo.Materials.IsValid()) + { + ChunkInfo.Materials->Reset(); + } + } +} + +void FVoxelRendererBasicMeshHandler::Tick(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::Tick(MaxTime); + + FlushBuiltDataQueue(); + FlushActionQueue(MaxTime); + + Renderer.Settings.DebugManager->ReportMeshActionQueueNum(ActionQueue.Num()); +} + +void FVoxelRendererBasicMeshHandler::FlushBuiltDataQueue() +{ + VOXEL_FUNCTION_COUNTER(); + + // Copy built data from async task callbacks to the chunk infos + // Should be fast enough to not require checking the time + FBuildCallback Callback; + while (CallbackQueue.Dequeue(Callback)) + { + auto& BuiltData = Callback.BuiltData; + + if (!ensure(BuiltData.BuiltMeshes.IsValid())) continue; + + auto* ChunkInfo = GetChunkInfo(Callback.ChunkInfoRef); + if (ChunkInfo && BuiltData.UpdateIndex >= ChunkInfo->UpdateIndex->GetValue()) + { + // Not outdated + ensure(BuiltData.UpdateIndex == ChunkInfo->UpdateIndex->GetValue()); + ChunkInfo->BuiltData = MoveTemp(BuiltData); + } + } +} + +void FVoxelRendererBasicMeshHandler::FlushActionQueue(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + FAction Action; + // Peek: if UpdateChunk isn't ready yet we don't want to pop the action + // Always process dithering in immediately, as else the chunk will be showed until the next tick and then hidden (one frame glitch) + while (ActionQueue.Peek(Action) && + (FPlatformTime::Seconds() < MaxTime || + (Action.Action == EAction::DitherChunk && Action.DitherChunk().DitheringType == EDitheringType::Classic_DitherIn))) + { + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + CleanUp(ChunkInfo.Meshes); + + if (IsDestroying() && Action.Action != EAction::RemoveChunk) + { + ActionQueue.Pop(); + continue; + } + + switch (Action.Action) + { + case EAction::UpdateChunk: + { + const int32 WantedUpdateIndex = Action.UpdateChunk().AfterCall.UpdateIndex; + if (ChunkInfo.MeshUpdateIndex >= WantedUpdateIndex) + { + // Already updated + // This happens when a previous UpdateChunk used the built data we triggered + break; + } + if (WantedUpdateIndex > ChunkInfo.BuiltData.UpdateIndex) + { + // Not built yet, wait + + if (ChunkInfo.BuiltData.UpdateIndex != -1) + { + // Stored built data is outdated, clear it to save memory + ensure(ChunkInfo.BuiltData.BuiltMeshes.IsValid()); + ChunkInfo.BuiltData.BuiltMeshes.Reset(); + ChunkInfo.BuiltData.UpdateIndex = -1; + } + + return; + } + + // Move to clear the built data value + const auto BuiltMeshes = MoveTemp(ChunkInfo.BuiltData.BuiltMeshes); + ChunkInfo.MeshUpdateIndex = ChunkInfo.BuiltData.UpdateIndex; + ChunkInfo.BuiltData.UpdateIndex = -1; + + if (!ensure(BuiltMeshes.IsValid())) continue; + + int32 MeshIndex = 0; + // Apply built meshes + for (auto& BuiltMesh : *BuiltMeshes) + { + const FVoxelMeshConfig& MeshConfig = BuiltMesh.Key; + if (ChunkInfo.Meshes.Num() <= MeshIndex) + { + // Not enough meshes to render the built mesh, allocate new ones + auto* NewMesh = GetNewMesh(Action.ChunkId, ChunkInfo.Position, ChunkInfo.LOD); + if (!ensureVoxelSlow(NewMesh)) return; + ChunkInfo.Meshes.Add(NewMesh); + } + + auto& Mesh = *ChunkInfo.Meshes[MeshIndex]; + MeshConfig.ApplyTo(Mesh); + + Mesh.SetDistanceFieldData(nullptr); + Mesh.ClearSections(EVoxelProcMeshSectionUpdate::DelayUpdate); + for (auto& Section : BuiltMesh.Value) + { + if (!ensure(Section.Value.IsValid())) continue; + Mesh.AddProcMeshSection(Section.Key, MoveTemp(Section.Value), EVoxelProcMeshSectionUpdate::DelayUpdate); + } + Mesh.FinishSectionsUpdates(); + + MeshIndex++; + } + + // Clear unused meshes + for (; MeshIndex < ChunkInfo.Meshes.Num(); MeshIndex++) + { + auto& Mesh = *ChunkInfo.Meshes[MeshIndex]; + Mesh.SetDistanceFieldData(nullptr); + Mesh.ClearSections(EVoxelProcMeshSectionUpdate::UpdateNow); + } + + // Handle distance fields + const auto& DistanceFieldVolumeData = Action.UpdateChunk().AfterCall.DistanceFieldVolumeData; + if (DistanceFieldVolumeData.IsValid()) + { + // Use the first mesh to hold the distance field data + // We should always have at least one mesh, else the chunk should have been removed instead of updated + if (ensure(ChunkInfo.Meshes.Num() > 0)) + { + ChunkInfo.Meshes[0]->SetDistanceFieldData(DistanceFieldVolumeData); + } + } + break; + } + case EAction::RemoveChunk: + { + for (auto& Mesh : ChunkInfo.Meshes) + { + RemoveMesh(*Mesh); + } + ChunkInfos.RemoveAt(Action.ChunkId); + break; + } + case EAction::DitherChunk: + { + ChunkInfo.DitheringInfo.bIsValid = true; + ChunkInfo.DitheringInfo.DitheringType = Action.DitherChunk().DitheringType; + ChunkInfo.DitheringInfo.Time = FVoxelRenderUtilities::GetWorldCurrentTime(Renderer.Settings.World.Get()); + for (auto& Mesh : ChunkInfo.Meshes) + { + FVoxelRenderUtilities::StartMeshDithering( + *Mesh, + Renderer.Settings, + ChunkInfo.DitheringInfo); + } + break; + } + case EAction::ResetDithering: + { + ChunkInfo.DitheringInfo.bIsValid = false; + for (auto& Mesh : ChunkInfo.Meshes) + { + FVoxelRenderUtilities::ResetDithering(*Mesh, Renderer.Settings); + } + break; + } + case EAction::SetTransitionsMaskForSurfaceNets: + { + for (auto& Mesh : ChunkInfo.Meshes) + { + FVoxelRenderUtilities::SetMeshTransitionsMask(*Mesh, Action.SetTransitionsMaskForSurfaceNets().TransitionsMask); + } + break; + } + case EAction::HideChunk: + { + for (auto& Mesh : ChunkInfo.Meshes) + { + FVoxelRenderUtilities::HideMesh(*Mesh); + } + break; + } + case EAction::ShowChunk: + { + for (auto& Mesh : ChunkInfo.Meshes) + { + FVoxelRenderUtilities::ShowMesh(*Mesh); + } + break; + } + default: ensure(false); + } + + if (CVarLogActionQueue.GetValueOnGameThread() != 0) + { + LOG_VOXEL(Log, TEXT("ActionQueue: LOD: %d; %s; Position: %s"), ChunkInfo.LOD, *Action.ToString(), *ChunkInfo.Position.ToString()); + } + + ActionQueue.Pop(); + } +} + +void FVoxelRendererBasicMeshHandler::MeshMergeCallback(FChunkInfoRef ChunkInfoRef, int32 UpdateIndex, TUniquePtr BuiltMeshes) +{ + CallbackQueue.Enqueue({ ChunkInfoRef, FChunkBuiltData{ UpdateIndex, MoveTemp(BuiltMeshes) } }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h new file mode 100644 index 00000000..43d75b35 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h @@ -0,0 +1,106 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelQueueWithNum.h" +#include "VoxelMinimal.h" +#include "VoxelRender/VoxelRenderUtilities.h" +#include "VoxelRendererMeshHandler.h" + +class FVoxelRendererBasicMeshHandler : public IVoxelRendererMeshHandler +{ +public: + using IVoxelRendererMeshHandler::IVoxelRendererMeshHandler; + virtual ~FVoxelRendererBasicMeshHandler() override; + + //~ Begin IVoxelRendererMeshHandler Interface + virtual FChunkId AddChunkImpl(int32 LOD, const FIntVector& Position) final override; + virtual void ApplyAction(const FAction& Action) final override; + virtual void ClearChunkMaterials() final override; + virtual void Tick(double MaxTime) final override; + //~ End IVoxelRendererMeshHandler Interface + +private: + struct FChunkBuiltData + { + int32 UpdateIndex = -1; + TUniquePtr BuiltMeshes; + }; + struct FChunkInfo + { + const uint64 UniqueId; // Unique ID used for tasks callbacks. Can't use ChunkId as it might get reused + const int32 LOD; + const FIntVector Position; + + TSharedPtr Materials; + TArray> Meshes; + + // Used to see if an async build task is still valid + // Shared ptr: so that the async task can check as well + TVoxelSharedPtr UpdateIndex; + // Update index used when last updating the meshes + int32 MeshUpdateIndex = -1; + // Processed data waiting to be displayed + FChunkBuiltData BuiltData; + + // Record last dithering state to be applied on new materials + // Needed as we can't reliably read that state from the materials + FVoxelRenderUtilities::FDitheringInfo DitheringInfo; + + static FChunkInfo Create( + int32 LOD, + const FIntVector& Position) + { + return FChunkInfo(UNIQUE_ID(), LOD, Position); + } + + private: + FChunkInfo( + uint64 UniqueId, + int32 LOD, + const FIntVector& Position) + : UniqueId(UniqueId) + , LOD(LOD) + , Position(Position) + { + } + }; + TVoxelTypedSparseArray ChunkInfos; + + struct FChunkInfoRef + { + FChunkId ChunkId; + uint64 UniqueId = 0; + }; + inline FChunkInfo* GetChunkInfo(FChunkInfoRef Ref) + { + if (!ChunkInfos.IsValidIndex(Ref.ChunkId)) + { + return nullptr; + } + auto& ChunkInfo = ChunkInfos[Ref.ChunkId]; + if (ChunkInfo.UniqueId != Ref.UniqueId) + { + return nullptr; + } + return &ChunkInfo; + } + + struct FBuildCallback + { + FChunkInfoRef ChunkInfoRef; + FChunkBuiltData BuiltData; + }; + TQueue CallbackQueue; + + // Queue all actions + // This ensures they are processed sequentially even when using async tasks + TVoxelQueueWithNum ActionQueue; + + void FlushBuiltDataQueue(); + void FlushActionQueue(double MaxTime); + void MeshMergeCallback(FChunkInfoRef ChunkInfoRef, int32 UpdateIndex, TUniquePtr BuiltMeshes); + + friend class FVoxelBasicMeshMergeWork; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.cpp new file mode 100644 index 00000000..860f14f9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.cpp @@ -0,0 +1,435 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRendererClusteredMeshHandler.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelChunkMaterials.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" +#include "IVoxelPool.h" +#include "VoxelAsyncWork.h" + +#include "Async/Async.h" + +class FVoxelClusteredMeshMergeWork : public FVoxelAsyncWork +{ +public: + static FVoxelClusteredMeshMergeWork* Create( + FVoxelRendererClusteredMeshHandler& Handler, + FVoxelRendererClusteredMeshHandler::FClusterRef ClusterRef) + { + auto* Cluster = Handler.GetCluster(ClusterRef); + check(Cluster); + return new FVoxelClusteredMeshMergeWork( + ClusterRef, + Cluster->Position, + Handler, + Cluster->UpdateIndex.ToSharedRef(), + Cluster->ChunkMeshesToBuild); + } + +private: + const FVoxelRendererClusteredMeshHandler::FClusterRef ClusterRef; + const FIntVector Position; + const FVoxelRendererSettingsBase RendererSettings; + const TVoxelWeakPtr Handler; + + const TMap> MeshesToBuild; + const TVoxelSharedRef UpdateIndexPtr; + const int32 UpdateIndex; + + FVoxelClusteredMeshMergeWork( + FVoxelRendererClusteredMeshHandler::FClusterRef ClusterRef, + const FIntVector& Position, + FVoxelRendererClusteredMeshHandler& Handler, + const TVoxelSharedRef& UpdateIndexPtr, + const TMap>& MeshesToBuild) + : FVoxelAsyncWork(STATIC_FNAME("FVoxelClusteredMeshMergeWork"), 1e9, true) + , ClusterRef(ClusterRef) + , Position(Position) + , RendererSettings(static_cast(Handler.Renderer.Settings)) + , Handler(StaticCastVoxelSharedRef(Handler.AsShared())) + , MeshesToBuild(MeshesToBuild) + , UpdateIndexPtr(UpdateIndexPtr) + , UpdateIndex(UpdateIndexPtr->GetValue()) + { + } + ~FVoxelClusteredMeshMergeWork() = default; + + virtual uint32 GetPriority() const override + { + return 0; + } + virtual void DoWork() override + { + if (UpdateIndexPtr->GetValue() > UpdateIndex) + { + // Canceled + return; + } + FVoxelChunkMeshesToBuild RealMap; + for (auto& ChunkIt : MeshesToBuild) + { + // Key is ChunkId = useless here + for (auto& MeshIt : *ChunkIt.Value) + { + auto& MeshMap = RealMap.FindOrAdd(MeshIt.Key); + for (auto& SectionIt : MeshIt.Value) + { + MeshMap.FindOrAdd(SectionIt.Key).Append(SectionIt.Value); + } + } + } + auto BuiltMeshes = FVoxelRenderUtilities::BuildMeshes_AnyThread(RealMap, RendererSettings, Position, *UpdateIndexPtr, UpdateIndex); + if (!BuiltMeshes.IsValid()) + { + // Canceled + return; + } + auto HandlerPinned = Handler.Pin(); + if (HandlerPinned.IsValid()) + { + // Queue callback + HandlerPinned->MeshMergeCallback(ClusterRef, UpdateIndex, MoveTemp(BuiltMeshes)); + FVoxelUtilities::DeleteOnGameThread_AnyThread(HandlerPinned); + } + } +}; + +FVoxelRendererClusteredMeshHandler::~FVoxelRendererClusteredMeshHandler() +{ + FlushBuiltDataQueue(); + FlushActionQueue(MAX_dbl); + + ensure(ChunkInfos.Num() == 0); + ensure(Clusters.Num() == 0); +} + +IVoxelRendererMeshHandler::FChunkId FVoxelRendererClusteredMeshHandler::AddChunkImpl(int32 LOD, const FIntVector& Position) +{ + return ChunkInfos.Add(FChunkInfo::Create(LOD, Position)); +} + +void FVoxelRendererClusteredMeshHandler::ApplyAction(const FAction& Action) +{ + VOXEL_FUNCTION_COUNTER(); + + const FChunkId ChunkId{ Action.ChunkId }; + + switch (Action.Action) + { + case EAction::UpdateChunk: + { + check(Action.UpdateChunk().InitialCall.MainChunk); + const auto& MainChunk = *Action.UpdateChunk().InitialCall.MainChunk; + const auto* TransitionChunk = Action.UpdateChunk().InitialCall.TransitionChunk; + + // This should never happen, as the chunk should be removed instead + ensure(!MainChunk.IsEmpty() || (TransitionChunk && !TransitionChunk->IsEmpty())); + + auto& ChunkInfo = ChunkInfos[ChunkId]; + + if (!ChunkInfo.ClusterId.IsValid()) + { + const FClusterRef ClusterRef = FindOrCreateCluster(ChunkInfo.LOD, ChunkInfo.Position); + ChunkInfo.ClusterId = ClusterRef.ClusterId; + Clusters[ChunkInfo.ClusterId].NumChunks++; + } + + auto& Cluster = Clusters[ChunkInfo.ClusterId]; + if (!Cluster.Materials.IsValid()) + { + Cluster.Materials = MakeShared(); + } + if (!Cluster.UpdateIndex.IsValid()) + { + Cluster.UpdateIndex = MakeVoxelShared(); + } + + // Cancel any previous build task + // Note: we do not clear the built data, as it could still be used + // The added cost of applying the update is probably worth it compared to stalling the entire queue waiting for an update + Cluster.UpdateIndex->Increment(); + + // Find the meshes to build (= copying & merging mesh buffers to proc mesh buffers) + FVoxelChunkMeshesToBuild MeshesToBuild = FVoxelRenderUtilities::GetMeshesToBuild( + ChunkInfo.LOD, + ChunkInfo.Position, + Renderer.Settings, + Action.UpdateChunk().InitialCall.ChunkSettings, + *Cluster.Materials, + MainChunk, + TransitionChunk, + Renderer.OnMaterialInstanceCreated, + {}); + + Cluster.ChunkMeshesToBuild.FindOrAdd(ChunkInfo.UniqueId) = MakeVoxelShared(MoveTemp(MeshesToBuild)); + + // Start a task to asynchronously build them + auto* Task = FVoxelClusteredMeshMergeWork::Create(*this, { ChunkInfo.ClusterId, Cluster.UniqueId }); + Renderer.Settings.Pool->QueueTask(EVoxelTaskType::MeshMerge, Task); + + FAction NewAction; + NewAction.Action = EAction::UpdateChunk; + NewAction.ChunkId = Action.ChunkId; + NewAction.UpdateChunk().AfterCall.UpdateIndex = Cluster.UpdateIndex->GetValue(); + NewAction.UpdateChunk().AfterCall.DistanceFieldVolumeData = Action.UpdateChunk().InitialCall.MainChunk->GetDistanceFieldVolumeData(); + ActionQueue.Enqueue(NewAction); + break; + } + case EAction::RemoveChunk: + { + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + + if (!ChunkInfo.ClusterId.IsValid()) + { + // No cluster = no mesh, just remove it + ChunkInfos.RemoveAt(Action.ChunkId); + return; + } + + // For remove, we trigger an update + // and then queue a remove action + + auto& Cluster = Clusters[ChunkInfo.ClusterId]; + + // Cancel any previous build task + // Note: we do not clear the built data, as it could still be used + // The added cost of applying the update is probably worth it compared to stalling the entire queue waiting for an update + Cluster.UpdateIndex->Increment(); + + ensure(Cluster.ChunkMeshesToBuild.Remove(ChunkInfo.UniqueId) == 1); // We must have some mesh + + // Start a task to asynchronously build them + auto* Task = FVoxelClusteredMeshMergeWork::Create(*this, { ChunkInfo.ClusterId, Cluster.UniqueId }); + Renderer.Settings.Pool->QueueTask(EVoxelTaskType::MeshMerge, Task); + + FAction NewAction; + NewAction.Action = EAction::UpdateChunk; + NewAction.ChunkId = Action.ChunkId; + NewAction.UpdateChunk().AfterCall.UpdateIndex = Cluster.UpdateIndex->GetValue(); + NewAction.UpdateChunk().AfterCall.DistanceFieldVolumeData = nullptr; + ActionQueue.Enqueue(NewAction); + // Enqueue remove as well + ActionQueue.Enqueue(Action); + } + case EAction::SetTransitionsMaskForSurfaceNets: + { + break; + } + case EAction::DitherChunk: + case EAction::ResetDithering: + // These 2 should be done by an UpdateChunk + case EAction::HideChunk: + case EAction::ShowChunk: + default: ensure(false); + } +} + +void FVoxelRendererClusteredMeshHandler::ClearChunkMaterials() +{ + for (auto& Cluster : Clusters) + { + if (Cluster.Materials.IsValid()) + { + Cluster.Materials->Reset(); + } + } +} + +void FVoxelRendererClusteredMeshHandler::Tick(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::Tick(MaxTime); + + FlushBuiltDataQueue(); + FlushActionQueue(MaxTime); + + Renderer.Settings.DebugManager->ReportMeshActionQueueNum(ActionQueue.Num()); +} + +FVoxelRendererClusteredMeshHandler::FClusterRef FVoxelRendererClusteredMeshHandler::FindOrCreateCluster( + int32 LOD, + const FIntVector& Position) +{ + const int32 Divisor = FMath::Clamp(uint64(Renderer.Settings.ChunksClustersSize) << LOD, 1, MAX_int32); + const FIntVector Key = FVoxelUtilities::DivideFloor(Position, Divisor); + auto& ClusterRef = ClustersMap[LOD].FindOrAdd(Key); + if (!GetCluster(ClusterRef)) + { + ClusterRef.ClusterId = Clusters.Add(FCluster::Create(LOD, Key * Divisor)); + ClusterRef.UniqueId = Clusters[ClusterRef.ClusterId].UniqueId; + } + check(GetCluster(ClusterRef)); + return ClusterRef; +} + +void FVoxelRendererClusteredMeshHandler::FlushBuiltDataQueue() +{ + VOXEL_FUNCTION_COUNTER(); + + // Copy built data from async task callbacks to the chunk infos + // Should be fast enough to not require checking the time + FBuildCallback Callback; + while (CallbackQueue.Dequeue(Callback)) + { + auto& BuiltData = Callback.BuiltData; + + if (!ensure(BuiltData.BuiltMeshes.IsValid())) continue; + + auto* Cluster = GetCluster(Callback.ClusterRef); + if (Cluster && BuiltData.UpdateIndex >= Cluster->UpdateIndex->GetValue()) + { + // Not outdated + ensure(BuiltData.UpdateIndex == Cluster->UpdateIndex->GetValue()); + Cluster->BuiltData = MoveTemp(BuiltData); + } + } +} + +void FVoxelRendererClusteredMeshHandler::FlushActionQueue(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + FAction Action; + // Peek: if UpdateChunk isn't ready yet we don't want to pop the action + while (ActionQueue.Peek(Action) && FPlatformTime::Seconds() < MaxTime) + { + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + + if (IsDestroying() && Action.Action != EAction::RemoveChunk) + { + ActionQueue.Pop(); + continue; + } + + switch (Action.Action) + { + case EAction::UpdateChunk: + { + if (!ensure(ChunkInfo.ClusterId.IsValid())) continue; // Not possible if UpdateChunk was called + auto& Cluster = Clusters[ChunkInfo.ClusterId]; + const int32 WantedUpdateIndex = Action.UpdateChunk().AfterCall.UpdateIndex; + if (Cluster.MeshUpdateIndex >= WantedUpdateIndex) + { + // Already updated + // This happens when a previous UpdateChunk used the built data we triggered + break; + } + if (WantedUpdateIndex > Cluster.BuiltData.UpdateIndex) + { + // Not built yet, wait + + if (Cluster.BuiltData.UpdateIndex != -1) + { + // Stored built data is outdated, clear it to save memory + ensure(Cluster.BuiltData.BuiltMeshes.IsValid()); + Cluster.BuiltData.BuiltMeshes.Reset(); + Cluster.BuiltData.UpdateIndex = -1; + } + + return; + } + + // Move to clear the built data value + const auto BuiltMeshes = MoveTemp(Cluster.BuiltData.BuiltMeshes); + Cluster.MeshUpdateIndex = Cluster.BuiltData.UpdateIndex; + Cluster.BuiltData.UpdateIndex = -1; + + if (!ensure(BuiltMeshes.IsValid())) continue; + + int32 MeshIndex = 0; + CleanUp(Cluster.Meshes); + // Apply built meshes + for (auto& BuiltMesh : *BuiltMeshes) + { + const FVoxelMeshConfig& MeshConfig = BuiltMesh.Key; + if (Cluster.Meshes.Num() <= MeshIndex) + { + // Not enough meshes to render the built mesh, allocate new ones + auto* NewMesh = GetNewMesh(Action.ChunkId, Cluster.Position, Cluster.LOD); + if (!ensure(NewMesh)) return; + Cluster.Meshes.Add(NewMesh); + } + + auto& Mesh = *Cluster.Meshes[MeshIndex]; + MeshConfig.ApplyTo(Mesh); + + Mesh.SetDistanceFieldData(nullptr); + Mesh.ClearSections(EVoxelProcMeshSectionUpdate::DelayUpdate); + for (auto& Section : BuiltMesh.Value) + { + Mesh.AddProcMeshSection(Section.Key, MoveTemp(Section.Value), EVoxelProcMeshSectionUpdate::DelayUpdate); + } + Mesh.FinishSectionsUpdates(); + + MeshIndex++; + } + + // Clear unused meshes + for (; MeshIndex < Cluster.Meshes.Num(); MeshIndex++) + { + auto& Mesh = *Cluster.Meshes[MeshIndex]; + Mesh.SetDistanceFieldData(nullptr); + Mesh.ClearSections(EVoxelProcMeshSectionUpdate::UpdateNow); + } + + // Handle distance fields + const auto& DistanceFieldVolumeData = Action.UpdateChunk().AfterCall.DistanceFieldVolumeData; + if (DistanceFieldVolumeData.IsValid()) + { +#if 0 + // Use the first mesh to hold the distance field data + // We should always have at least one mesh, else the chunk should have been removed instead of updated + if (ensure(Cluster.Meshes.Num() > 0)) + { + Cluster.Meshes[0]->SetDistanceFieldData(DistanceFieldVolumeData); + } +#endif + // TODO: No distance fields with merging = on + } + break; + } + case EAction::RemoveChunk: + { + if (ChunkInfo.ClusterId.IsValid()) + { + auto& Cluster = Clusters[ChunkInfo.ClusterId]; + Cluster.NumChunks--; + ensure(Cluster.NumChunks >= 0); + if (Cluster.NumChunks == 0) + { + for (auto& Mesh : CleanUp(Cluster.Meshes)) + { + RemoveMesh(*Mesh); + } + Clusters.RemoveAt(ChunkInfo.ClusterId); + } + } + ChunkInfos.RemoveAt(Action.ChunkId); + break; + } + case EAction::ShowChunk: + case EAction::HideChunk: + case EAction::SetTransitionsMaskForSurfaceNets: + case EAction::DitherChunk: + case EAction::ResetDithering: + default: ensure(false); + } + + if (CVarLogActionQueue.GetValueOnGameThread() != 0) + { + LOG_VOXEL(Log, TEXT("ActionQueue: LOD: %d; %s; Position: %s"), ChunkInfo.LOD, *Action.ToString(), *ChunkInfo.Position.ToString()); + } + + ActionQueue.Pop(); + } +} + +void FVoxelRendererClusteredMeshHandler::MeshMergeCallback(FClusterRef ClusterRef, int32 UpdateIndex, TUniquePtr BuiltMeshes) +{ + CallbackQueue.Enqueue({ ClusterRef, FClusterBuiltData{ UpdateIndex, MoveTemp(BuiltMeshes) } }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h new file mode 100644 index 00000000..b5a50aaa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h @@ -0,0 +1,145 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelQueueWithNum.h" +#include "Containers/StaticArray.h" +#include "VoxelRender/VoxelRenderUtilities.h" +#include "VoxelRendererMeshHandler.h" + +class FVoxelRendererClusteredMeshHandler : public IVoxelRendererMeshHandler +{ +public: + using IVoxelRendererMeshHandler::IVoxelRendererMeshHandler; + virtual ~FVoxelRendererClusteredMeshHandler() override; + + //~ Begin IVoxelRendererMeshHandler Interface + virtual FChunkId AddChunkImpl(int32 LOD, const FIntVector& Position) final override; + virtual void ApplyAction(const FAction& Action) final override; + virtual void ClearChunkMaterials() final override; + virtual void Tick(double MaxTime) final override; + //~ End IVoxelRendererMeshHandler Interface + +private: + DEFINE_TYPED_VOXEL_SPARSE_ARRAY_ID(FClusterId); + + struct FClusterBuiltData + { + int32 UpdateIndex = -1; + TUniquePtr BuiltMeshes; + }; + struct FCluster + { + const uint64 UniqueId; // Unique ID used for tasks callbacks + const int32 LOD; + const FIntVector Position; + + int32 NumChunks = 0; + + TSharedPtr Materials; + TArray> Meshes; + + // Used to see if an async build task is still valid + // Shared ptr: so that the async task can check as well + TVoxelSharedPtr UpdateIndex; + // Update index used when last updating the meshes + int32 MeshUpdateIndex = -1; + + // Processed data waiting to be displayed + FClusterBuiltData BuiltData; + + // Chunk unique id -> its meshes + // Shared ptr: used by build task + TMap> ChunkMeshesToBuild; + + static FCluster Create( + int32 LOD, + const FIntVector& Position) + { + return FCluster(UNIQUE_ID(), LOD, Position); + } + + private: + explicit FCluster( + uint64 UniqueId, + int32 LOD, + const FIntVector& Position) + : UniqueId(UniqueId) + , LOD(LOD) + , Position(Position) + { + } + }; + TVoxelTypedSparseArray Clusters; + + struct FClusterRef + { + FClusterId ClusterId; + uint64 UniqueId = 0; + }; + inline FCluster* GetCluster(FClusterRef Ref) + { + if (!Clusters.IsValidIndex(Ref.ClusterId)) + { + return nullptr; + } + auto& Cluster = Clusters[Ref.ClusterId]; + if (Cluster.UniqueId != Ref.UniqueId) + { + return nullptr; + } + return &Cluster; + } + + TStaticArray, 32> ClustersMap; // Max 32 LODs + + FClusterRef FindOrCreateCluster(int32 LOD, const FIntVector& Position); + + struct FChunkInfo + { + const uint64 UniqueId; // Unique ID used in cluster map + + const int32 LOD; + const FIntVector Position; + + FClusterId ClusterId; + + static FChunkInfo Create( + int32 LOD, + const FIntVector& Position) + { + return FChunkInfo(UNIQUE_ID(), LOD, Position); + } + + private: + FChunkInfo( + uint64 UniqueId, + int32 LOD, + const FIntVector& Position) + : UniqueId(UniqueId) + , LOD(LOD) + , Position(Position) + { + } + }; + TVoxelTypedSparseArray ChunkInfos; + + struct FBuildCallback + { + FClusterRef ClusterRef; + FClusterBuiltData BuiltData; + }; + TQueue CallbackQueue; + + // Queue all actions + // This ensures they are processed sequentially even when using async tasks + TVoxelQueueWithNum ActionQueue; + + void FlushBuiltDataQueue(); + void FlushActionQueue(double MaxTime); + void MeshMergeCallback(FClusterRef ClusterRef, int32 UpdateIndex, TUniquePtr BuiltMeshes); + + friend class FVoxelClusteredMeshMergeWork; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp new file mode 100644 index 00000000..c736f6a1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp @@ -0,0 +1,469 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRendererMeshHandler.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelRenderUtilities.h" + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Proc Mesh Pool"), STAT_VoxelProcMeshPool, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Proc Mesh Frozen Pool"), STAT_VoxelProcMeshFrozenPool, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Proc Mesh Used"), STAT_VoxelProcMeshUsed, STATGROUP_VoxelCounters); + +TAutoConsoleVariable CVarLogActionQueue( + TEXT("voxel.renderer.LogMeshActionQueue"), + 0, + TEXT("If true, will log every queued action when processed"), + ECVF_Default); + +static TAutoConsoleVariable CVarLogMeshPositionsPrecisionsErrors( + TEXT("voxel.renderer.LogMeshPositionsPrecisionsErrors"), + 0, + TEXT("If true, will log mesh positions precisions errors"), + ECVF_Default); + +IVoxelRendererMeshHandler::IVoxelRendererMeshHandler(IVoxelRenderer& Renderer) + : Renderer(Renderer) +{ +} + +IVoxelRendererMeshHandler::~IVoxelRendererMeshHandler() +{ + VOXEL_FUNCTION_COUNTER(); + check(bIsInit); + ensure(bIsDestroying); + + // Avoid crashes + if (GExitPurge) return; + +#if CHECK_CHUNK_IDS + ensure(ValidIndices.Num() == 0); +#endif + + DEC_DWORD_STAT_BY(STAT_VoxelProcMeshUsed, ActiveMeshes.Num()); + DEC_DWORD_STAT_BY(STAT_VoxelProcMeshPool, MeshPool.Num()); + DEC_DWORD_STAT_BY(STAT_VoxelProcMeshFrozenPool, FrozenMeshPool.Num()); + +#if VOXEL_DEBUG + { + int32 NumInvalid = 0; + + for (auto It = ActiveMeshes.CreateIterator(); It; ++It) + { + if (!It.Key().IsValid()) + { + It.RemoveCurrent(); + NumInvalid++; + } + } + + // Meshes are invalid when closing the editor + // Is raised when recompiling a voxel world BP, so ensureVoxelSlowNoSideEffects and not ensure + // ensureVoxelSlowNoSideEffects(NumInvalid == 0 || GExitPurge); + ensureVoxelSlowNoSideEffects(ActiveMeshes.Num() == 0); + } +#endif + + VOXEL_SCOPE_COUNTER("Destroy proc mesh components"); + // Destroy all mesh components we are owning + for (auto& Mesh : MeshPool) + { + if (Mesh.IsValid()) + { + Mesh->DestroyComponent(); + } + } + for (auto& Mesh : FrozenMeshPool) + { + if (Mesh.IsValid()) + { + Mesh->DestroyComponent(); + } + } +} + +IVoxelRendererMeshHandler::FChunkId IVoxelRendererMeshHandler::AddChunk(int32 LOD, const FIntVector& Position) +{ + VOXEL_FUNCTION_COUNTER(); + + const FChunkId Id = AddChunkImpl(LOD, Position); +#if CHECK_CHUNK_IDS + ValidIndices.Add(Id); +#endif + return Id; +} + +#if CHECK_CHUNK_IDS +#define CHECK_CHUNK_ID_IMPL(R) if (!ensure(ChunkId.IsValid()) || !ensure(ValidIndices.Contains(ChunkId))) return R; +#define CHECK_CHUNK_ID() CHECK_CHUNK_ID_IMPL(;) +#define CHECK_CHUNK_ID_RETURN() CHECK_CHUNK_ID_IMPL({}) +#else +#define CHECK_CHUNK_ID() +#define CHECK_CHUNK_ID_RETURN() +#endif + +void IVoxelRendererMeshHandler::UpdateChunk( + FChunkId ChunkId, + const FVoxelChunkSettings& ChunkSettings, + const FVoxelChunkMesh& MainChunk, + const FVoxelChunkMesh* TransitionChunk, + uint8 TransitionsMask) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + ensure(ChunkSettings.bVisible || ChunkSettings.bEnableCollisions || ChunkSettings.bEnableNavmesh); + + FAction Action; + Action.Action = EAction::UpdateChunk; + Action.ChunkId = ChunkId; + Action.UpdateChunk().InitialCall.ChunkSettings = ChunkSettings; + Action.UpdateChunk().InitialCall.ChunkSettings.TransitionsMask = TransitionsMask; // Make subclass believe this is the transitions mask + Action.UpdateChunk().InitialCall.MainChunk = &MainChunk; + Action.UpdateChunk().InitialCall.TransitionChunk = TransitionChunk; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::RemoveChunk(FChunkId ChunkId) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + +#if CHECK_CHUNK_IDS + ValidIndices.Remove(ChunkId); +#endif + + FAction Action; + Action.Action = EAction::RemoveChunk; + Action.ChunkId = ChunkId; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::DitherChunk(FChunkId ChunkId, EDitheringType DitheringType) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + FAction Action; + Action.Action = EAction::DitherChunk; + Action.ChunkId = ChunkId; + Action.DitherChunk().DitheringType = DitheringType; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::ResetDithering(FChunkId ChunkId) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + FAction Action; + Action.Action = EAction::ResetDithering; + Action.ChunkId = ChunkId; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::SetTransitionsMaskForSurfaceNets(FChunkId ChunkId, uint8 TransitionsMask) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + FAction Action; + Action.Action = EAction::SetTransitionsMaskForSurfaceNets; + Action.ChunkId = ChunkId; + Action.SetTransitionsMaskForSurfaceNets().TransitionsMask = TransitionsMask; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::HideChunk(FChunkId ChunkId) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + // Clustered renderer does not support those + ensure(Renderer.Settings.bDitherChunks); + + FAction Action; + Action.Action = EAction::HideChunk; + Action.ChunkId = ChunkId; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::ShowChunk(FChunkId ChunkId) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_CHUNK_ID(); + + // Clustered renderer does not support those + ensure(Renderer.Settings.bDitherChunks); + + FAction Action; + Action.Action = EAction::ShowChunk; + Action.ChunkId = ChunkId; + ApplyAction(Action); +} + +void IVoxelRendererMeshHandler::Tick(double MaxTime) +{ + TickHandler(); +} + +void IVoxelRendererMeshHandler::RecomputeMeshPositions() +{ + VOXEL_FUNCTION_COUNTER(); + ensure(!bIsDestroying); + + for (auto& It : ActiveMeshes) + { + if (ensure(It.Key.IsValid())) + { + SetMeshPosition(*It.Key, It.Value); + } + } +} + +void IVoxelRendererMeshHandler::ApplyToAllMeshes(TFunctionRef Lambda) +{ + VOXEL_FUNCTION_COUNTER(); + ensure(!bIsDestroying); + + for (auto& It : ActiveMeshes) + { + if (ensureVoxelSlow(It.Key.IsValid())) + { + Lambda(*It.Key); + } + } +} + +void IVoxelRendererMeshHandler::StartDestroying() +{ + ensure(!bIsDestroying); + bIsDestroying = true; +} + +UVoxelProceduralMeshComponent* IVoxelRendererMeshHandler::GetNewMesh(FChunkId ChunkId, const FIntVector& Position, uint8 LOD) +{ + VOXEL_FUNCTION_COUNTER(); + ensure(!bIsDestroying); + + auto& Settings = Renderer.Settings; + + auto* const RootComponent = Settings.RootComponent.Get(); + if (!ensureVoxelSlow(RootComponent)) + { + return nullptr; + } + + UVoxelProceduralMeshComponent* NewMesh = nullptr; + { + while (MeshPool.Num() > 0 && !NewMesh) + { + NewMesh = MeshPool.Pop(false).Get(); + ensure(NewMesh != nullptr); + DEC_DWORD_STAT(STAT_VoxelProcMeshPool); + } + + if (!NewMesh) + { + NewMesh = NewObject(RootComponent, Settings.ProcMeshClass, NAME_None, RF_Transient); + NewMesh->bCastFarShadow = Settings.bCastFarShadow; + NewMesh->SetupAttachment(RootComponent, NAME_None); + auto* Root = Cast(RootComponent); + if (ensure(Root)) + { + NewMesh->BodyInstance.CopyRuntimeBodyInstancePropertiesFrom(&Root->BodyInstance); + NewMesh->BodyInstance.SetObjectType(Root->BodyInstance.GetObjectType()); + NewMesh->SetGenerateOverlapEvents(Root->GetGenerateOverlapEvents()); + } + NewMesh->RegisterComponent(); + NewMesh->SetRelativeScale3D(FVector::OneVector * Settings.VoxelSize); + } + } + check(NewMesh); + + INC_DWORD_STAT(STAT_VoxelProcMeshUsed); + + ensure(!ActiveMeshes.Contains(NewMesh)); + ActiveMeshes.Add(NewMesh, Position); + SetMeshPosition(*NewMesh, Position); + + const FVoxelIntBox Bounds = FVoxelUtilities::GetBoundsFromPositionAndDepth(Position, LOD); + const FVoxelPriorityHandler PriorityHandler(Bounds, Renderer.GetInvokersPositionsForPriorities()); + + // Set mesh variables + NewMesh->Init( + LOD, + ChunkId.GetDebugValue(), + PriorityHandler, + AsShared(), + Settings); + + if (Settings.PlayType == EVoxelPlayType::Game) + { + // Call BP event if user want to do custom stuff + NewMesh->InitChunk(LOD, Bounds); + } + + return NewMesh; +} + +void IVoxelRendererMeshHandler::RemoveMesh(UVoxelProceduralMeshComponent& Mesh) +{ + VOXEL_FUNCTION_COUNTER(); + + // Avoid crashes + if (GExitPurge) return; + + ensure(ActiveMeshes.Remove(&Mesh) == 1); + + // Skip an expensive clear when we're destroying + if (!bIsDestroying) + { + Mesh.SetDistanceFieldData(nullptr); + Mesh.ClearSections(EVoxelProcMeshSectionUpdate::UpdateNow); + Mesh.ClearInit(); + + // Set world location to 0 to avoid precision issues, as SetRelativeLocation calls MoveComponent :( +#if ENGINE_MINOR_VERSION < 24 + check(!Mesh.bAbsoluteLocation); + Mesh.bAbsoluteLocation = true; + Mesh.SetWorldLocationAndRotationNoPhysics(FVector::ZeroVector, FRotator::ZeroRotator); + Mesh.bAbsoluteLocation = false; +#else + Mesh.SetUsingAbsoluteLocation(true); + Mesh.SetWorldLocationAndRotationNoPhysics(FVector::ZeroVector, FRotator::ZeroRotator); + Mesh.SetUsingAbsoluteLocation(false); +#endif + } + + if (UVoxelProceduralMeshComponent::AreVoxelCollisionsFrozen()) + { + FrozenMeshPool.Add(&Mesh); + INC_DWORD_STAT(STAT_VoxelProcMeshFrozenPool); + } + else + { + MeshPool.Add(&Mesh); + INC_DWORD_STAT(STAT_VoxelProcMeshPool); + } + + DEC_DWORD_STAT(STAT_VoxelProcMeshUsed); +} + +TArray>& IVoxelRendererMeshHandler::CleanUp(TArray>& Meshes) const +{ + const int32 NumInvalid = Meshes.RemoveAll([](auto& Mesh) { return !Mesh.IsValid(); }); + // Meshes are invalid when closing the editor + // ensureVoxelSlowNoSideEffects(NumInvalid == 0 || GExitPurge); + return Meshes; +} + +FString IVoxelRendererMeshHandler::FAction::ToString() const +{ + FString String = FString::Printf(TEXT("ChunkId: %u; Type: "), ChunkId.GetDebugValue()); + switch (Action) + { + case EAction::UpdateChunk: + { + String += "UpdateChunk"; + break; + } + case EAction::RemoveChunk: + { + String += "RemoveChunk"; + break; + } + case EAction::DitherChunk: + { + String += "DitherChunk; DitheringType: "; + switch (DitherChunk().DitheringType) + { + case EDitheringType::SurfaceNets_LowResToHighRes: String += "SurfaceNets_LowResToHighRes"; break; + case EDitheringType::SurfaceNets_HighResToLowRes: String += "SurfaceNets_HighResToLowRes"; break; + case EDitheringType::Classic_DitherIn: String += "Classic_DitherIn"; break; + case EDitheringType::Classic_DitherOut: String += "Classic_DitherOut"; break; + default: ensure(false); + } + break; + } + case EAction::ResetDithering: + { + String += "ResetDithering"; + break; + } + case EAction::SetTransitionsMaskForSurfaceNets: + { + String += FString::Printf(TEXT("SetTransitionsMaskForSurfaceNets; TransitionsMask: %d"), SetTransitionsMaskForSurfaceNets().TransitionsMask); + break; + } + case EAction::HideChunk: + { + String += "HideChunk"; + break; + } + case EAction::ShowChunk: + { + String += "ShowChunk"; + break; + } + default: ensure(false); + } + return String; +} + +void IVoxelRendererMeshHandler::Init() +{ + check(!bIsInit); + bIsInit = true; + + UVoxelProceduralMeshComponent::AddOnFreezeVoxelCollisionChanged(FOnFreezeVoxelCollisionChanged::FDelegate::CreateThreadSafeSP( + this, + &IVoxelRendererMeshHandler::OnFreezeVoxelCollisionChanged)); +} + +void IVoxelRendererMeshHandler::SetMeshPosition(UVoxelProceduralMeshComponent& Mesh, const FIntVector& Position) const +{ + VOXEL_FUNCTION_COUNTER(); + ensure(!bIsDestroying); + + // TODO errors might add up when we rebase? + Mesh.SetRelativeLocationAndRotation( + Renderer.Settings.GetChunkRelativePosition(Position), + FRotator::ZeroRotator, + false, + nullptr, + ETeleportType::TeleportPhysics); + + // If we don't do that the component does not update if Position = 0 0 0 :( + // Probably UE bug? + if (Position == FIntVector(0, 0, 0)) + { + Mesh.UpdateComponentToWorld(EUpdateTransformFlags::None, ETeleportType::TeleportPhysics); + } + + if (CVarLogMeshPositionsPrecisionsErrors.GetValueOnGameThread() != 0) + { + const auto A = Mesh.GetRelativeTransform().GetTranslation(); + const auto B = Renderer.Settings.GetChunkRelativePosition(Position); + const float Error = FVector::Distance(A, B); + if (Error > 0) + { + LOG_VOXEL(Log, TEXT("Distance between theorical and actual mesh position: %6.6f voxels"), Error); + ensure(Error < 1); + } + } +} + +void IVoxelRendererMeshHandler::OnFreezeVoxelCollisionChanged(bool bNewFreezeCollisions) +{ + if (!bNewFreezeCollisions) + { + // We can reuse all the frozen meshes + // No need to do anything on them: their collisions will be unfrozen automatically by the proc mesh comp + + DEC_DWORD_STAT_BY(STAT_VoxelProcMeshFrozenPool, FrozenMeshPool.Num()); + INC_DWORD_STAT_BY(STAT_VoxelProcMeshPool, FrozenMeshPool.Num()); + + MeshPool.Append(MoveTemp(FrozenMeshPool)); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.h new file mode 100644 index 00000000..d8ab98e3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.h @@ -0,0 +1,153 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelContainers/VoxelSparseArray.h" +#include "VoxelRender/VoxelChunkToUpdate.h" +#include "VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h" + +enum class EDitheringType : uint8; +struct FVoxelChunkSettings; +struct FVoxelChunkMesh; +struct FVoxelIntBox; +class IVoxelRenderer; +class FDistanceFieldVolumeData; +class UVoxelProceduralMeshComponent; +template +class TAutoConsoleVariable; + +#define CHECK_CHUNK_IDS DO_CHECK + +extern TAutoConsoleVariable CVarLogActionQueue; + +class IVoxelRendererMeshHandler : public IVoxelProceduralMeshComponent_PhysicsCallbackHandler +{ +public: + DEFINE_TYPED_VOXEL_SPARSE_ARRAY_ID(FChunkId); + + IVoxelRenderer& Renderer; + + explicit IVoxelRendererMeshHandler(IVoxelRenderer& Renderer); + virtual ~IVoxelRendererMeshHandler(); + + void Init(); + + FChunkId AddChunk(int32 LOD, const FIntVector& Position); + void UpdateChunk( + FChunkId ChunkId, + const FVoxelChunkSettings& ChunkSettings, + const FVoxelChunkMesh& MainChunk, + const FVoxelChunkMesh* TransitionChunk, + uint8 TransitionsMask); // TransitionsMask isn't always the ChunkSettings one (eg transitions task not done yet), so force specifying it + void RemoveChunk(FChunkId ChunkId); + + void DitherChunk(FChunkId ChunkId, EDitheringType DitheringType); + void ResetDithering(FChunkId ChunkId); + + void SetTransitionsMaskForSurfaceNets(FChunkId ChunkId, uint8 TransitionsMask); + + // We don't want to call Update for that as we can keep the collision data + // Note: doesn't work with clustered mesh handler! Can only be used for dithering + void HideChunk(FChunkId ChunkId); + void ShowChunk(FChunkId ChunkId); + + // Used for ApplyNewMaterials + virtual void ClearChunkMaterials() = 0; + + virtual void Tick(double MaxTime); + +public: + virtual void RecomputeMeshPositions(); + virtual void ApplyToAllMeshes(TFunctionRef Lambda); + virtual void StartDestroying(); + + inline bool IsDestroying() const + { + return bIsDestroying; + } + +protected: + UVoxelProceduralMeshComponent* GetNewMesh(FChunkId ChunkId, const FIntVector& Position, uint8 LOD); + void RemoveMesh(UVoxelProceduralMeshComponent& Mesh); + TArray>& CleanUp(TArray>& Meshes) const; + +protected: + enum class EAction : uint8 + { + UpdateChunk, + RemoveChunk, + DitherChunk, + ResetDithering, + SetTransitionsMaskForSurfaceNets, + HideChunk, + ShowChunk + }; + struct FAction + { + EAction Action = EAction(-1); + FChunkId ChunkId; + + FString ToString() const; + + struct FUpdateChunk + { + struct + { + FVoxelChunkSettings ChunkSettings; + const FVoxelChunkMesh* MainChunk; // Always valid + const FVoxelChunkMesh* TransitionChunk; // Might be null + } InitialCall; + struct + { + // This is used to check if the mesh has been updated + int32 UpdateIndex; + TVoxelSharedPtr DistanceFieldVolumeData; + } AfterCall; + }; + struct FDitherChunk + { + EDitheringType DitheringType; + }; + struct FSetTransitionsMaskForSurfaceNets + { + uint8 TransitionsMask; + }; + +#define ACCESSOR(Name) \ + inline F##Name& Name() { check(Action == EAction::Name); return _##Name; } \ + inline const F##Name& Name() const { check(Action == EAction::Name); return _##Name; } + ACCESSOR(UpdateChunk); + ACCESSOR(DitherChunk); + ACCESSOR(SetTransitionsMaskForSurfaceNets); +#undef ACCESSOR + + private: + FUpdateChunk _UpdateChunk{}; + FDitherChunk _DitherChunk{}; + FSetTransitionsMaskForSurfaceNets _SetTransitionsMaskForSurfaceNets{}; + }; + + virtual FChunkId AddChunkImpl(int32 LOD, const FIntVector& Position) = 0; + virtual void ApplyAction(const FAction& Action) = 0; + +private: + TArray> MeshPool; + // Mesh pool containing frozen meshes that can't be used until collisions aren't frozen anymore + TArray> FrozenMeshPool; + // Meshes are only moved once on creation (reusing from pool = creation too) + // We keep their voxel position here to recompute their position when rebasing + TMap, FIntVector> ActiveMeshes; + // Sanity check + bool bIsInit = false; + // Used to skip clearing mesh sections when the renderer is destroying + bool bIsDestroying = false; + +#if CHECK_CHUNK_IDS + TSet ValidIndices; +#endif + + void SetMeshPosition(UVoxelProceduralMeshComponent& Mesh, const FIntVector& Position) const; + void OnFreezeVoxelCollisionChanged(bool bNewFreezeCollisions); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.cpp new file mode 100644 index 00000000..86f96148 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.cpp @@ -0,0 +1,163 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRendererMixedMeshHandler.h" +#include "VoxelRendererBasicMeshHandler.h" +#include "VoxelRendererClusteredMeshHandler.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" + +FVoxelRendererMixedMeshHandler::FVoxelRendererMixedMeshHandler(IVoxelRenderer& Renderer) + : IVoxelRendererMeshHandler(Renderer) + , BasicMeshHandler(MakeVoxelShared(Renderer)) + , ClusteredMeshHandler(MakeVoxelShared(Renderer)) +{ + BasicMeshHandler->Init(); + ClusteredMeshHandler->Init(); +} + +FVoxelRendererMixedMeshHandler::~FVoxelRendererMixedMeshHandler() +{ + ensure(ChunkInfos.Num() == 0); +} + +IVoxelRendererMeshHandler::FChunkId FVoxelRendererMixedMeshHandler::AddChunkImpl(int32 LOD, const FIntVector& Position) +{ + return ChunkInfos.Add(FChunkInfo(LOD, Position)); +} + +void FVoxelRendererMixedMeshHandler::ApplyAction(const FAction& Action) +{ + VOXEL_FUNCTION_COUNTER(); + + switch (Action.Action) + { + case EAction::UpdateChunk: + { + const auto& UpdateChunk = Action.UpdateChunk().InitialCall; + const auto ChunkSettings = UpdateChunk.ChunkSettings; + const bool bNeedBasicChunk = ChunkSettings.bEnableCollisions || ChunkSettings.bEnableNavmesh; + const bool bNeedClusteredChunk = ChunkSettings.bVisible; + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + + if (bNeedBasicChunk) + { + if (!ChunkInfo.BasicChunkId.IsValid()) + { + ChunkInfo.BasicChunkId = BasicMeshHandler->AddChunk(ChunkInfo.LOD, ChunkInfo.Position); + } + auto BasicChunkSettings = ChunkSettings; + BasicChunkSettings.bVisible = false; + BasicMeshHandler->UpdateChunk( + ChunkInfo.BasicChunkId, + BasicChunkSettings, + *UpdateChunk.MainChunk, + nullptr, + 0); + } + else + { + if (ChunkInfo.BasicChunkId.IsValid()) + { + BasicMeshHandler->RemoveChunk(ChunkInfo.BasicChunkId); + ChunkInfo.BasicChunkId = {}; + } + } + + if (bNeedClusteredChunk) + { + if (!ChunkInfo.ClusteredChunkId.IsValid()) + { + ChunkInfo.ClusteredChunkId = ClusteredMeshHandler->AddChunk(ChunkInfo.LOD, ChunkInfo.Position); + } + auto ClusteredChunkSettings = ChunkSettings; + ClusteredChunkSettings.bEnableCollisions = false; + ClusteredChunkSettings.bEnableNavmesh = false; + ClusteredMeshHandler->UpdateChunk( + ChunkInfo.ClusteredChunkId, + ClusteredChunkSettings, + *UpdateChunk.MainChunk, + UpdateChunk.TransitionChunk, + ChunkSettings.TransitionsMask); + } + else + { + if (ChunkInfo.ClusteredChunkId.IsValid()) + { + ClusteredMeshHandler->RemoveChunk(ChunkInfo.ClusteredChunkId); + ChunkInfo.ClusteredChunkId = {}; + } + } + + break; + } + case EAction::RemoveChunk: + { + auto& ChunkInfo = ChunkInfos[Action.ChunkId]; + if (ChunkInfo.BasicChunkId.IsValid()) + { + BasicMeshHandler->RemoveChunk(ChunkInfo.BasicChunkId); + } + if (ChunkInfo.ClusteredChunkId.IsValid()) + { + ClusteredMeshHandler->RemoveChunk(ChunkInfo.ClusteredChunkId); + } + ChunkInfos.RemoveAt(Action.ChunkId); + break; + } + case EAction::SetTransitionsMaskForSurfaceNets: + { + break; + } + case EAction::DitherChunk: + case EAction::ResetDithering: + // These 2 should be done by an UpdateChunk + case EAction::HideChunk: + case EAction::ShowChunk: + default: ensure(false); + } +} + +void FVoxelRendererMixedMeshHandler::ClearChunkMaterials() +{ + BasicMeshHandler->ClearChunkMaterials(); + ClusteredMeshHandler->ClearChunkMaterials(); +} + +void FVoxelRendererMixedMeshHandler::Tick(double MaxTime) +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::Tick(MaxTime); + + BasicMeshHandler->Tick(MaxTime); + ClusteredMeshHandler->Tick(MaxTime); +} + +void FVoxelRendererMixedMeshHandler::RecomputeMeshPositions() +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::RecomputeMeshPositions(); + + BasicMeshHandler->RecomputeMeshPositions(); + ClusteredMeshHandler->RecomputeMeshPositions(); +} + +void FVoxelRendererMixedMeshHandler::ApplyToAllMeshes(TFunctionRef Lambda) +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::ApplyToAllMeshes(Lambda); + + BasicMeshHandler->ApplyToAllMeshes(Lambda); + ClusteredMeshHandler->ApplyToAllMeshes(Lambda); +} + +void FVoxelRendererMixedMeshHandler::StartDestroying() +{ + VOXEL_FUNCTION_COUNTER(); + + IVoxelRendererMeshHandler::StartDestroying(); + + BasicMeshHandler->StartDestroying(); + ClusteredMeshHandler->StartDestroying(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h new file mode 100644 index 00000000..3bb713f9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRendererMeshHandler.h" + +class FVoxelRendererClusteredMeshHandler; +class FVoxelRendererBasicMeshHandler; + +class FVoxelRendererMixedMeshHandler : public IVoxelRendererMeshHandler +{ +public: + explicit FVoxelRendererMixedMeshHandler(IVoxelRenderer& Renderer); + virtual ~FVoxelRendererMixedMeshHandler() override; + + //~ Begin IVoxelRendererMeshHandler Interface + virtual FChunkId AddChunkImpl(int32 LOD, const FIntVector& Position) override; + virtual void ApplyAction(const FAction& Action) override; + virtual void ClearChunkMaterials() override; + virtual void Tick(double MaxTime) override; + + virtual void RecomputeMeshPositions() override; + virtual void ApplyToAllMeshes(TFunctionRef Lambda) override; + virtual void StartDestroying() override; + //~ End IVoxelRendererMeshHandler Interface + +private: + // Need to be shared ref, else their AsShared fail + TVoxelSharedRef BasicMeshHandler; + TVoxelSharedRef ClusteredMeshHandler; + + struct FChunkInfo + { + const int32 LOD; + const FIntVector Position; + + FChunkId BasicChunkId; + FChunkId ClusteredChunkId; + + FChunkInfo(int32 LOD, const FIntVector& Position) + : LOD(LOD) + , Position(Position) + { + } + }; + TVoxelTypedSparseArray ChunkInfos; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp new file mode 100644 index 00000000..e016fc00 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp @@ -0,0 +1,281 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" + +#include "Materials/MaterialInstanceDynamic.h" +#include "DistanceFieldAtlas.h" + +#if ENABLE_TESSELLATION +#include "ThirdParty/nvtesslib/inc/nvtess.h" +#endif + +#if ENABLE_OPTIMIZE_INDICES +#include "ThirdParty/ForsythTriOO/Src/forsythtriangleorderoptimizer.h" +#endif + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelChunkMeshMemory); + +#if ENABLE_TESSELLATION +/** +* Provides static mesh render data to the NVIDIA tessellation library. +*/ +class FVoxelStaticMeshNvRenderBuffer : public nv::RenderBuffer +{ +public: + + /** Construct from static mesh render buffers. */ + FVoxelStaticMeshNvRenderBuffer( + const TArray& InPositionVertexBuffer, + const TArray& Indices) + : PositionVertexBuffer(InPositionVertexBuffer) + { + mIb = new nv::IndexBuffer((void*)Indices.GetData(), nv::IBT_U32, Indices.Num(), false); + } + + /** Retrieve the position and first texture coordinate of the specified index. */ + virtual nv::Vertex getVertex(const unsigned int Index) const override + { + nv::Vertex Vertex; + + const FVector& Position = PositionVertexBuffer[Index]; + Vertex.pos.x = Position.X; + Vertex.pos.y = Position.Y; + Vertex.pos.z = Position.Z; + + Vertex.uv.x = 0.0f; + Vertex.uv.y = 0.0f; + + return Vertex; + } + +private: + /** The position vertex buffer for the static mesh. */ + const TArray& PositionVertexBuffer; + + /** Copying is forbidden. */ + FVoxelStaticMeshNvRenderBuffer(const FVoxelStaticMeshNvRenderBuffer&) = delete; + FVoxelStaticMeshNvRenderBuffer& operator=(const FVoxelStaticMeshNvRenderBuffer&) = delete; +}; +#endif + +void FVoxelChunkMeshBuffers::BuildAdjacency(TArray& OutAdjacencyIndices) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + +#if ENABLE_TESSELLATION + if (Indices.Num()) + { + FVoxelStaticMeshNvRenderBuffer StaticMeshRenderBuffer(Positions, Indices); + nv::IndexBuffer* PnAENIndexBuffer = nv::tess::buildTessellationBuffer(&StaticMeshRenderBuffer, nv::DBM_PnAenDominantCorner, true); + check(PnAENIndexBuffer); + const int32 IndexCount = int32(PnAENIndexBuffer->getLength()); + OutAdjacencyIndices.Empty(IndexCount); + OutAdjacencyIndices.AddUninitialized(IndexCount); + for (int32 Index = 0; Index < IndexCount; ++Index) + { + OutAdjacencyIndices[Index] = (*PnAENIndexBuffer)[Index]; + } + delete PnAENIndexBuffer; + } + else + { + OutAdjacencyIndices.Empty(); + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelChunkMeshBuffers::OptimizeIndices() +{ +#if ENABLE_OPTIMIZE_INDICES + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray OptimizedIndices; + OptimizedIndices.AddUninitialized(Indices.Num()); + const uint16 CacheSize = 32; + Forsyth::OptimizeFaces(Indices.GetData(), Indices.Num(), GetNumVertices(), OptimizedIndices.GetData(), CacheSize); + Indices = MoveTemp(OptimizedIndices); +#endif +} + +void FVoxelChunkMeshBuffers::Shrink() +{ + Positions.Shrink(); + Normals.Shrink(); + Tangents.Shrink(); + Colors.Shrink(); + for (auto& T : TextureCoordinates) T.Shrink(); + + UpdateStats(); +} + +void FVoxelChunkMeshBuffers::ComputeBounds() +{ + Bounds = FBox(ForceInit); + for (auto& Vertex : Positions) + { + Bounds += Vertex; + } +} + +void FVoxelChunkMeshBuffers::UpdateStats() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelChunkMeshMemory, LastAllocatedSize); + LastAllocatedSize = Indices.GetAllocatedSize(); + LastAllocatedSize += Positions.GetAllocatedSize(); + LastAllocatedSize += Normals.GetAllocatedSize(); + LastAllocatedSize += Tangents.GetAllocatedSize(); + LastAllocatedSize += Colors.GetAllocatedSize(); + for (auto& T : TextureCoordinates) LastAllocatedSize += T.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelChunkMeshMemory, LastAllocatedSize); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelChunkMesh::BuildDistanceField(int32 LOD, const FIntVector& Position, const FVoxelData& Data, const FVoxelRendererSettingsBase& Settings) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (IsEmpty()) + { + return; + } + + // Need to overlap distance fields to avoid glitches + const int32 Extension = Settings.DistanceFieldBoundsExtension; + const int32 HighResSize = RENDER_CHUNK_SIZE + 1 + 2 * Extension; + const int32 Step = 1 << LOD; + + const int32 Divisor = FMath::Clamp(Settings.DistanceFieldResolutionDivisor, 1, HighResSize); + const int32 Size = FVoxelUtilities::DivideCeil(HighResSize, Divisor); + + const auto GetDistanceField = [&]() + { + const int32 ValuesSize = HighResSize + 2; + const int32 NumValues = ValuesSize * ValuesSize * ValuesSize; + + TArray Values; + Values.Empty(NumValues); + Values.SetNumUninitialized(NumValues); + { + const FIntVector Start = Position - Extension * Step; + const FVoxelIntBox Bounds = FVoxelIntBox(Start, Start + HighResSize * Step).Extend(Step); // Extend: See GetSurfacePositionsFromDensities + + FVoxelReadScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + TVoxelQueryZone QueryZone(Bounds, FIntVector(ValuesSize), LOD, Values); + Data.Get(QueryZone, LOD); + } + + TArray Distances; + TArray SurfacePositions; + FIntVector SizeVector(HighResSize); + + FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(SizeVector, Values, Distances, SurfacePositions); + FVoxelDistanceFieldUtilities::DownSample(SizeVector, Distances, SurfacePositions, Divisor, false); + FVoxelDistanceFieldUtilities::JumpFlood(SizeVector, SurfacePositions, EVoxelComputeDevice::CPU); + FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(SizeVector, SurfacePositions, Distances); + + ensure(SizeVector.X == Size); + + return MoveTemp(Distances); + }; + + TArray Distances = GetDistanceField(); + + float MinVolumeDistance = Distances[0]; + float MaxVolumeDistance = Distances[0]; + + for (float& Distance : Distances) + { + // TRICKY: Divide by Size: distance fields are expected to be relative to volume + Distance /= Size; + MinVolumeDistance = FMath::Min(MinVolumeDistance, Distance); + MaxVolumeDistance = FMath::Max(MaxVolumeDistance, Distance); + } + + const float InvDistanceRange = 1.0f / (MaxVolumeDistance - MinVolumeDistance); + + static const auto CVarEightBit = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DistanceFieldBuild.EightBit")); + + const bool bEightBitFixedPoint = CVarEightBit->GetValueOnAnyThread() != 0; + const int32 FormatSize = bEightBitFixedPoint ? sizeof(uint8) : sizeof(FFloat16); + + TArray QuantizedDistanceFieldVolume; + QuantizedDistanceFieldVolume.Empty(FormatSize * Size * Size * Size); + QuantizedDistanceFieldVolume.SetNumUninitialized(FormatSize * Size * Size * Size); + + for (int32 X = 0; X < Size; X++) + { + for (int32 Y = 0; Y < Size; Y++) + { + for (int32 Z = 0; Z < Size; Z++) + { + const uint32 Index = X + Size * Y + Size * Size * Z; + + const float Distance = Distances[Index]; + + if (bEightBitFixedPoint) + { + check(FormatSize == sizeof(uint8)); + // [MinVolumeDistance, MaxVolumeDistance] -> [0, 1] + const float RescaledDistance = (Distance - MinVolumeDistance) * InvDistanceRange; + // Encoding based on D3D format conversion rules for float -> UNORM + const int32 QuantizedDistance = FMath::FloorToInt(RescaledDistance * 255.0f + .5f); + QuantizedDistanceFieldVolume[Index * FormatSize] = FMath::Clamp(QuantizedDistance, 0, 255); + } + else + { + check(FormatSize == sizeof(FFloat16)); + reinterpret_cast(QuantizedDistanceFieldVolume[Index * FormatSize]) = Distance; + } + } + } + } + + check(!DistanceFieldVolumeData.IsValid()); + DistanceFieldVolumeData = MakeVoxelShared(); + DistanceFieldVolumeData->bMeshWasClosed = true; // Not used + DistanceFieldVolumeData->bBuiltAsIfTwoSided = false; + DistanceFieldVolumeData->bMeshWasPlane = false; // Maybe check this? + DistanceFieldVolumeData->Size = FIntVector(Size); + DistanceFieldVolumeData->LocalBoundingBox = FBox(FVector(-Extension - 0.5f) * Step, FVector(-Extension - 0.5f + HighResSize) * Step); + DistanceFieldVolumeData->DistanceMinMax = FVector2D(MinVolumeDistance, MaxVolumeDistance); + + auto& CompressedDistanceFieldVolume = DistanceFieldVolumeData->CompressedDistanceFieldVolume; + static const auto CVarCompress = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DistanceFieldBuild.Compress")); + const bool bCompress = CVarCompress->GetValueOnAnyThread() != 0; + + if (bCompress) + { + VOXEL_ASYNC_SCOPE_COUNTER("Compress"); + + const int32 UncompressedSize = QuantizedDistanceFieldVolume.Num() * QuantizedDistanceFieldVolume.GetTypeSize(); + + // Compressed can be slightly larger than uncompressed + CompressedDistanceFieldVolume.Empty(UncompressedSize * 4 / 3); + CompressedDistanceFieldVolume.AddUninitialized(UncompressedSize * 4 / 3); + int32 CompressedSize = CompressedDistanceFieldVolume.Num() * CompressedDistanceFieldVolume.GetTypeSize(); + + verify(FCompression::CompressMemory( + NAME_Zlib, + CompressedDistanceFieldVolume.GetData(), + CompressedSize, + QuantizedDistanceFieldVolume.GetData(), + UncompressedSize)); + + CompressedDistanceFieldVolume.SetNum(CompressedSize); + CompressedDistanceFieldVolume.Shrink(); + } + else + { + CompressedDistanceFieldVolume = QuantizedDistanceFieldVolume; + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp new file mode 100644 index 00000000..510253cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp @@ -0,0 +1,233 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelMaterialExpressions.h" +#include "VoxelContainers/VoxelStaticArray.h" + +#include "MaterialCompiler.h" +#include "Materials/HLSLMaterialTranslator.h" + +#if WITH_EDITOR +class FHLSLCompilerChild : public FHLSLMaterialTranslator +{ +public: + auto& GetStaticParameters() const + { + return StaticParameters; + } +}; + +class FVoxelMaterialCompiler : public FProxyMaterialCompiler +{ +public: + static TSet& GetVoxelCompilers() + { + static TSet Set; + return Set; + } + + FVoxelMaterialCompiler(FMaterialCompiler* InCompiler) + : FProxyMaterialCompiler(InCompiler) + { + GetVoxelCompilers().Add(this); + } + ~FVoxelMaterialCompiler() + { + ensure(GetVoxelCompilers().Contains(this)); + GetVoxelCompilers().Remove(this); + } + + FMaterialCompiler* GetBaseCompiler() const + { + if (GetVoxelCompilers().Contains(Compiler)) + { + // Can happen in recursive calls + return static_cast(Compiler)->GetBaseCompiler(); + } + else + { + return Compiler; + } + } + + virtual int32 StaticTerrainLayerWeight(FName ParameterName, int32 Default) override + { + FHLSLCompilerChild* HLSLCompiler = static_cast(GetBaseCompiler()); + auto& TerrainLayerWeightParameters = HLSLCompiler->GetStaticParameters().TerrainLayerWeightParameters; + + TArray VoxelIndices; + int32 ParameterVoxelIndex = -1; + for (auto& Parameter : TerrainLayerWeightParameters) + { + const int32 WeightmapIndex = Parameter.WeightmapIndex - 1000000; + if (WeightmapIndex >= 0) + { + VoxelIndices.Add(WeightmapIndex); + + if (Parameter.ParameterInfo.Name == ParameterName) + { + ParameterVoxelIndex = WeightmapIndex; + } + } + } + + if (VoxelIndices.Num() == 0) + { + // Default to regular behavior in case we're compiling a real landscape material + return Compiler->StaticTerrainLayerWeight(ParameterName, Default); + } + + if (ParameterName == UMaterialExpressionLandscapeVisibilityMask::ParameterName) + { + // No support for visibility mask + return INDEX_NONE; + } + + if (!ensure(ParameterVoxelIndex != -1)) + { + // We should have all the parameters overriden + return INDEX_NONE; + } + if (ParameterVoxelIndex == 6) + { + // Valid index: means it's not used + // Make sure to return 0 to avoid inconsistencies + return Constant(0.f); + } + if (!ensure(ParameterVoxelIndex >= 0 && ParameterVoxelIndex < 6)) + { + // Invalid index + return INDEX_NONE; + } + + // Remove all unused layers + VoxelIndices.Remove(6); + + const int32 NumBlends = VoxelIndices.Num(); + ensure(NumBlends > 0 && NumBlends <= 6); + + const int32 R = ComponentMask(VertexColor(), true, false, false, false); + const int32 G = ComponentMask(VertexColor(), false, true, false, false); + const int32 B = ComponentMask(VertexColor(), false, false, true, false); + const int32 U = ComponentMask(TextureCoordinate(1, false, false), true, false, false, false); + const int32 V = ComponentMask(TextureCoordinate(1, false, false), false, true, false, false); + + TVoxelStaticArray Inputs; + for (int32& Input : Inputs) + { + Input = Constant(0.f); + } + Inputs[ParameterVoxelIndex] = Constant(1.f); + + int32 Output = Inputs[0]; + + if (NumBlends > 1) Output = Lerp(Output, Inputs[1], R); + if (NumBlends > 2) Output = Lerp(Output, Inputs[2], G); + if (NumBlends > 3) Output = Lerp(Output, Inputs[3], B); + if (NumBlends > 4) Output = Lerp(Output, Inputs[4], U); + if (NumBlends > 5) Output = Lerp(Output, Inputs[5], V); + + return Output; + } + +public: + virtual int32 ReflectionAboutCustomWorldNormal(int32 CustomWorldNormal, int32 bNormalizeCustomWorldNormal) override + { + return Compiler->ReflectionAboutCustomWorldNormal(CustomWorldNormal, bNormalizeCustomWorldNormal); + } + virtual int32 ParticleRelativeTime() override + { + return Compiler->ParticleRelativeTime(); + } + virtual int32 ParticleMotionBlurFade() override + { + return Compiler->ParticleMotionBlurFade(); + } + virtual int32 ParticleRandom() override + { + return Compiler->ParticleRandom(); + } + virtual int32 ParticleDirection() override + { + return Compiler->ParticleDirection(); + } + virtual int32 ParticleSpeed() override + { + return Compiler->ParticleSpeed(); + } + virtual int32 ParticleSize() override + { + return Compiler->ParticleSize(); + } + virtual int32 VertexInterpolator(uint32 InterpolatorIndex) override + { + return Compiler->VertexInterpolator(InterpolatorIndex); + } + virtual int32 MaterialBakingWorldPosition() override + { + return Compiler->MaterialBakingWorldPosition(); + } + +#if ENGINE_MINOR_VERSION >= 26 + virtual int32 PreSkinVertexOffset() override + { + return Compiler->PreSkinVertexOffset(); + } + virtual int32 PostSkinVertexOffset() override + { + return Compiler->PostSkinVertexOffset(); + } +#endif +}; + +#define FORWARD_CLASS(Name) \ + void Name::GetCaption(TArray& OutCaptions) const \ + { \ + Super::GetCaption(OutCaptions); \ + OutCaptions[0] += " (voxel)"; \ + } \ + \ + int32 Name::Compile(FMaterialCompiler* Compiler, int32 OutputIndex) \ + { \ + FVoxelMaterialCompiler VoxelCompiler(Compiler); \ + return Super::Compile(&VoxelCompiler, OutputIndex); \ + } + +FORWARD_CLASS(UMaterialExpressionVoxelLandscapeLayerBlend) +FORWARD_CLASS(UMaterialExpressionVoxelLandscapeLayerSwitch) +FORWARD_CLASS(UMaterialExpressionVoxelLandscapeLayerWeight) +FORWARD_CLASS(UMaterialExpressionVoxelLandscapeLayerSample) +FORWARD_CLASS(UMaterialExpressionVoxelLandscapeVisibilityMask) + +#undef FORWARD_CLASS + +bool NeedsToBeConvertedToVoxelImp(const TArray& Expressions, TSet& VisitedFunctions) +{ + for (auto* Expression : Expressions) + { + if (FVoxelMaterialExpressionUtilities::GetVoxelExpression(Expression->GetClass())) + { + return true; + } + if (auto* FunctionCall = Cast(Expression)) + { + auto* Function = Cast(FunctionCall->MaterialFunction); + if (Function && !VisitedFunctions.Contains(Function)) + { + VisitedFunctions.Add(Function); + if (NeedsToBeConvertedToVoxelImp(Function->FunctionExpressions, VisitedFunctions)) + { + return true; + } + } + } + } + + return false; +} + +bool FVoxelMaterialExpressionUtilities::NeedsToBeConvertedToVoxel(const TArray& Expressions) +{ + TSet VisitedFunctions; + return NeedsToBeConvertedToVoxelImp(Expressions, VisitedFunctions); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProcMeshBuffers.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProcMeshBuffers.cpp new file mode 100644 index 00000000..91b15c5b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProcMeshBuffers.cpp @@ -0,0 +1,71 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelProcMeshBuffers.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory_Indices); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory_Positions); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory_Colors); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory_Adjacency); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelProcMeshMemory_UVs_Tangents); + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num Voxel Proc Mesh Buffers"), STAT_NumVoxelProcMeshBuffers, STATGROUP_VoxelCounters); + +FVoxelProcMeshBuffers::FVoxelProcMeshBuffers() +{ + INC_DWORD_STAT(STAT_NumVoxelProcMeshBuffers); +} + +FVoxelProcMeshBuffers::~FVoxelProcMeshBuffers() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory, LastAllocatedSize); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Indices, LastAllocatedSize_Indices); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Positions, LastAllocatedSize_Positions); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Colors, LastAllocatedSize_Colors); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Adjacency, LastAllocatedSize_Adjacency); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_UVs_Tangents, LastAllocatedSize_UVs_Tangents); + + DEC_DWORD_STAT(STAT_NumVoxelProcMeshBuffers); +} + +uint32 FVoxelProcMeshBuffers::GetAllocatedSize() const +{ + return + VertexBuffers.StaticMeshVertexBuffer.GetResourceSize() + + VertexBuffers.PositionVertexBuffer.GetNumVertices() * VertexBuffers.PositionVertexBuffer.GetStride() + + VertexBuffers.ColorVertexBuffer.GetNumVertices() * VertexBuffers.ColorVertexBuffer.GetStride() + + IndexBuffer.GetAllocatedSize() + + AdjacencyIndexBuffer.GetAllocatedSize(); +} + +void FVoxelProcMeshBuffers::UpdateStats() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory, LastAllocatedSize); + LastAllocatedSize = GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory, LastAllocatedSize); + + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Indices, LastAllocatedSize_Indices); + LastAllocatedSize_Indices = IndexBuffer.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Indices, LastAllocatedSize_Indices); + + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Positions, LastAllocatedSize_Positions); + LastAllocatedSize_Positions = VertexBuffers.PositionVertexBuffer.GetNumVertices() * VertexBuffers.PositionVertexBuffer.GetStride(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Positions, LastAllocatedSize_Positions); + + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Colors, LastAllocatedSize_Colors); + LastAllocatedSize_Colors = VertexBuffers.ColorVertexBuffer.GetNumVertices() * VertexBuffers.ColorVertexBuffer.GetStride(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Colors, LastAllocatedSize_Colors); + + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Adjacency, LastAllocatedSize_Adjacency); + LastAllocatedSize_Adjacency = AdjacencyIndexBuffer.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_Adjacency, LastAllocatedSize_Adjacency); + + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_UVs_Tangents, LastAllocatedSize_UVs_Tangents); + LastAllocatedSize_UVs_Tangents = VertexBuffers.StaticMeshVertexBuffer.GetResourceSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelProcMeshMemory_UVs_Tangents, LastAllocatedSize_UVs_Tangents); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp new file mode 100644 index 00000000..a495a528 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp @@ -0,0 +1,773 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelProceduralMeshSceneProxy.h" +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelRender/VoxelToolRendering.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelWorldRootComponent.h" +#include "VoxelMessages.h" +#include "VoxelMinimal.h" +#include "IVoxelPool.h" + +#include "PhysicsEngine/PhysicsSettings.h" +#include "PhysicsEngine/BodySetup.h" +#include "AI/NavigationSystemHelpers.h" +#include "AI/NavigationSystemBase.h" +#include "Async/Async.h" +#include "DrawDebugHelpers.h" +#include "Materials/Material.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelPhysicsTriangleMeshesMemory); + +static TAutoConsoleVariable CVarShowCollisionsUpdates( + TEXT("voxel.renderer.ShowCollisionsUpdates"), + 0, + TEXT("If true, will show the chunks that finished updating collisions"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void IVoxelProceduralMeshComponent_PhysicsCallbackHandler::TickHandler() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + FCallback Callback; + while (Queue.Dequeue(Callback)) + { + if (Callback.Component.IsValid()) + { + Callback.Component->PhysicsCookerCallback(Callback.CookerId); + } + } +} + +void IVoxelProceduralMeshComponent_PhysicsCallbackHandler::CookerCallback(uint64 CookerId, TWeakObjectPtr Component) +{ + Queue.Enqueue({ CookerId, Component }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelProceduralMeshComponent::Init( + int32 InDebugLOD, + uint32 InDebugChunkId, + const FVoxelPriorityHandler& InPriorityHandler, + const TVoxelWeakPtr& InPhysicsCallbackHandler, + const FVoxelRendererSettings& RendererSettings) +{ + ensure(InPhysicsCallbackHandler.IsValid()); + + if (UniqueId != 0) + { + // Make sure we don't have any convex collision left +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + UpdateConvexMeshes({}, {}, {}); +#endif + } + + bInit = true; + UniqueId = UNIQUE_ID(); + LOD = InDebugLOD; + DebugChunkId = InDebugChunkId; + PriorityHandler = InPriorityHandler; + PhysicsCallbackHandler = InPhysicsCallbackHandler; + Pool = RendererSettings.Pool; + ToolRenderingManager = RendererSettings.ToolRenderingManager; + PriorityDuration = RendererSettings.PriorityDuration; + CollisionTraceFlag = RendererSettings.CollisionTraceFlag; + NumConvexHullsPerAxis = RendererSettings.NumConvexHullsPerAxis; + bCleanCollisionMesh = RendererSettings.bCleanCollisionMeshes; + bClearProcMeshBuffersOnFinishUpdate = RendererSettings.bStaticWorld && !RendererSettings.bRenderWorld; // We still need the buffers if we are rendering! + DistanceFieldSelfShadowBias = RendererSettings.DistanceFieldSelfShadowBias; +} + +void UVoxelProceduralMeshComponent::ClearInit() +{ + ensure(ProcMeshSections.Num() == 0); + bInit = false; +} + +#if WITH_EDITOR && VOXEL_ENABLE_FOLIAGE_PAINT_HACK +class FStaticLightingSystem +{ +public: + static void SetModel(UModelComponent* Component) + { + static UModel* DummyModel = []() + { + auto* Memory = FMemory::Malloc(sizeof(UModel), alignof(UModel)); + FMemory::Memzero(Memory, sizeof(UModel)); + return reinterpret_cast(Memory); + }(); + Component->Model = DummyModel; + } +}; +#endif + +UVoxelProceduralMeshComponent::UVoxelProceduralMeshComponent() +{ +#if WITH_EDITOR && VOXEL_ENABLE_FOLIAGE_PAINT_HACK + // Create a dummy model for foliage painting to work + FStaticLightingSystem::SetModel(this); +#endif + + Mobility = EComponentMobility::Movable; + + CastShadow = true; + bUseAsOccluder = true; + bCanEverAffectNavigation = true; + + bAllowReregistration = false; // Slows down the editor when editing properties + bCastShadowAsTwoSided = true; + bHasCustomNavigableGeometry = EHasCustomNavigableGeometry::EvenIfNotCollidable; + + // Fix for details crash + BodyInstance.SetMassOverride(100, true); +} + +UVoxelProceduralMeshComponent::~UVoxelProceduralMeshComponent() +{ + if (AsyncCooker) + { + AsyncCooker->CancelAndAutodelete(); + AsyncCooker = nullptr; + } + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPhysicsTriangleMeshesMemory, MemoryUsage.TriangleMeshes); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelProceduralMeshComponent::AreVoxelCollisionsFrozen() +{ + return bAreCollisionsFrozen; +} + +void UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(bool bFrozen) +{ + VOXEL_FUNCTION_COUNTER(); + if (bFrozen != bAreCollisionsFrozen) + { + if (bFrozen) + { + bAreCollisionsFrozen = true; + + OnFreezeVoxelCollisionChanged.Broadcast(true); + } + else + { + bAreCollisionsFrozen = false; + + for (auto& Component : PendingCollisions) + { + if (Component.IsValid()) + { + Component->UpdateCollision(); + } + } + PendingCollisions.Reset(); + + OnFreezeVoxelCollisionChanged.Broadcast(false); + } + } +} + +void UVoxelProceduralMeshComponent::AddOnFreezeVoxelCollisionChanged(const FOnFreezeVoxelCollisionChanged::FDelegate& NewDelegate) +{ + OnFreezeVoxelCollisionChanged.Add(NewDelegate); +} + +bool UVoxelProceduralMeshComponent::bAreCollisionsFrozen = false; +TSet> UVoxelProceduralMeshComponent::PendingCollisions; +FOnFreezeVoxelCollisionChanged UVoxelProceduralMeshComponent::OnFreezeVoxelCollisionChanged; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelProceduralMeshComponent::SetDistanceFieldData(const TVoxelSharedPtr& InDistanceFieldData) +{ + if (DistanceFieldData == InDistanceFieldData) + { + return; + } + + DistanceFieldData = InDistanceFieldData; + + GetScene()->UpdatePrimitiveDistanceFieldSceneData_GameThread(this); + MarkRenderStateDirty(); +} + +void UVoxelProceduralMeshComponent::SetProcMeshSection(int32 Index, FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update) +{ + VOXEL_FUNCTION_COUNTER(); + if (!ensure(ProcMeshSections.IsValidIndex(Index))) + { + return; + } + + Buffers->UpdateStats(); + + ProcMeshSections[Index].Settings = Settings; + + // Due to InitResources etc, we must make sure we are the only component using this buffers, hence the TUniquePtr + // However the buffer is shared between the component and the proxy + ProcMeshSections[Index].Buffers = MakeShareable(Buffers.Release()); + + if (Update == EVoxelProcMeshSectionUpdate::UpdateNow) + { + FinishSectionsUpdates(); + } +} + +int32 UVoxelProceduralMeshComponent::AddProcMeshSection(FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update) +{ + VOXEL_FUNCTION_COUNTER(); + check(Buffers.IsValid()); + + ensure(Settings.bSectionVisible || Settings.bEnableCollisions || Settings.bEnableNavmesh); + + if (Buffers->GetNumIndices() == 0) + { + return -1; + } + + const int32 Index = ProcMeshSections.Emplace(); + SetProcMeshSection(Index, Settings, MoveTemp(Buffers), Update); + + return Index; +} + +void UVoxelProceduralMeshComponent::ReplaceProcMeshSection(FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update) +{ + VOXEL_FUNCTION_COUNTER(); + check(Buffers.IsValid()); + + ensure(Settings.bSectionVisible || Settings.bEnableCollisions || Settings.bEnableNavmesh); + + int32 SectionIndex = -1; + for (int32 Index = 0; Index < ProcMeshSections.Num(); Index++) + { + if (ProcMeshSections[Index].Settings == Settings) + { + ensure(SectionIndex == -1); + SectionIndex = Index; + } + } + if (SectionIndex == -1) + { + AddProcMeshSection(Settings, MoveTemp(Buffers), Update); + } + else + { + SetProcMeshSection(SectionIndex, Settings, MoveTemp(Buffers), Update); + } +} + +void UVoxelProceduralMeshComponent::ClearSections(EVoxelProcMeshSectionUpdate Update) +{ + VOXEL_FUNCTION_COUNTER(); + ProcMeshSections.Empty(); + + if (Update == EVoxelProcMeshSectionUpdate::UpdateNow) + { + FinishSectionsUpdates(); + } +} + +void UVoxelProceduralMeshComponent::FinishSectionsUpdates() +{ + VOXEL_FUNCTION_COUNTER(); + + bool bNeedToComputeCollisions = false; + bool bNeedToComputeNavigation = false; + { + TArray NewGuids; + TMap NewGuidToSettings; + { + int32 NumGuids = 0; + for (auto& Section : ProcMeshSections) + { + NumGuids += Section.Buffers->Guids.Num(); + } + NewGuids.Reserve(NumGuids); + NewGuidToSettings.Reserve(NumGuids); + } + for (auto& Section : ProcMeshSections) + { + for (auto& Guid : Section.Buffers->Guids) + { + NewGuids.Add(Guid); + ensure(!NewGuidToSettings.Contains(Guid)); + NewGuidToSettings.Add(Guid, Section.Settings); + } + } + NewGuids.Sort(); + + if (ProcMeshSectionsSortedGuids != NewGuids) + { + bNeedToComputeCollisions = true; + bNeedToComputeNavigation = true; + } + else + { + for (auto& Guid : NewGuids) + { + const auto& Old = ProcMeshSectionsGuidToSettings[Guid]; + const auto& New = NewGuidToSettings[Guid]; + bNeedToComputeCollisions |= Old.bEnableCollisions != New.bEnableCollisions; + bNeedToComputeNavigation |= Old.bEnableNavmesh != New.bEnableNavmesh; + } + } + + ProcMeshSectionsSortedGuids = MoveTemp(NewGuids); + ProcMeshSectionsGuidToSettings = MoveTemp(NewGuidToSettings); + } + + UpdatePhysicalMaterials(); + UpdateLocalBounds(); + MarkRenderStateDirty(); + + if (bNeedToComputeCollisions) + { + UpdateCollision(); + } + if (bNeedToComputeNavigation) + { + UpdateNavigation(); + } + + if (bClearProcMeshBuffersOnFinishUpdate) + { + ProcMeshSections.Reset(); + } + + LastFinishSectionsUpdatesTime = FPlatformTime::Seconds(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FPrimitiveSceneProxy* UVoxelProceduralMeshComponent::CreateSceneProxy() +{ + // Sometimes called outside of the render thread at EndPlay + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (auto& Section : ProcMeshSections) + { + if (Section.Settings.bSectionVisible || FVoxelDebugManager::ShowCollisionAndNavmeshDebug()) + { + return new FVoxelProceduralMeshSceneProxy(this); + } + } + + if (DistanceFieldData.IsValid()) + { + return new FVoxelProceduralMeshSceneProxy(this); + } + + return nullptr; +} + +UBodySetup* UVoxelProceduralMeshComponent::GetBodySetup() +{ + return BodySetup; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UMaterialInterface* UVoxelProceduralMeshComponent::GetMaterialFromCollisionFaceIndex(int32 FaceIndex, int32& OutSectionIndex) const +{ + // Look for element that corresponds to the supplied face + int32 TotalFaceCount = 0; + for (int32 SectionIndex = 0; SectionIndex < ProcMeshSections.Num(); SectionIndex++) + { + const FVoxelProcMeshSection& Section = ProcMeshSections[SectionIndex]; + const int32 NumFaces = Section.Buffers->GetNumIndices() / 3; + TotalFaceCount += NumFaces; + + if (FaceIndex < TotalFaceCount) + { + OutSectionIndex = SectionIndex; + return GetMaterial(SectionIndex); + } + } + OutSectionIndex = 0; + return nullptr; +} + +int32 UVoxelProceduralMeshComponent::GetNumMaterials() const +{ + int32 Num = ProcMeshSections.Num(); + const auto ToolRenderingManagerPinned = ToolRenderingManager.Pin(); + if (ToolRenderingManagerPinned.IsValid()) + { + Num += ToolRenderingManagerPinned->GetToolsMaterials().Num(); + } + return Num; +} + +UMaterialInterface* UVoxelProceduralMeshComponent::GetMaterial(int32 Index) const +{ + if (!ensure(Index >= 0)) return nullptr; + + if (Index < ProcMeshSections.Num()) + { + auto& MaterialPtr = ProcMeshSections[Index].Settings.Material; + if (MaterialPtr.IsValid()) + { + return MaterialPtr->GetMaterial(); + } + else + { + return UPrimitiveComponent::GetMaterial(Index); + } + } + else + { + Index -= ProcMeshSections.Num(); + const auto ToolRenderingManagerPinned = ToolRenderingManager.Pin(); + if (ToolRenderingManagerPinned.IsValid()) + { + const auto& Materials = ToolRenderingManagerPinned->GetToolsMaterials(); + if (Materials.IsValidIndex(Index) && ensure(Materials[Index].IsValid())) + { + return Materials[Index]->GetMaterial(); + } + } + return nullptr; + } +} + +void UVoxelProceduralMeshComponent::GetUsedMaterials(TArray& OutMaterials, bool bGetDebugMaterials) const +{ + for (auto& Section : ProcMeshSections) + { + if (Section.Settings.Material.IsValid()) + { + OutMaterials.Add(Section.Settings.Material->GetMaterial()); + } + } + + const auto ToolRenderingManagerPinned = ToolRenderingManager.Pin(); + if (ToolRenderingManagerPinned.IsValid()) + { + const auto& Materials = ToolRenderingManagerPinned->GetToolsMaterials(); + for (auto& Material : Materials) + { + if (ensure(Material.IsValid())) + { + OutMaterials.Add(Material->GetMaterial()); + } + } + } +} + +FMaterialRelevance UVoxelProceduralMeshComponent::GetMaterialRelevance(ERHIFeatureLevel::Type InFeatureLevel) const +{ + FMaterialRelevance Result; + const auto Apply = [&](auto* MaterialInterface) + { + if (MaterialInterface) + { + // MaterialInterface will be null in force delete + Result |= MaterialInterface->GetRelevance_Concurrent(InFeatureLevel); + } + }; + for (auto& Section : ProcMeshSections) + { + if (Section.Settings.Material.IsValid()) + { + Apply(Section.Settings.Material->GetMaterial()); + } + else + { + Apply(UMaterial::GetDefaultMaterial(MD_Surface)); + } + } + + const auto ToolRenderingManagerPinned = ToolRenderingManager.Pin(); + if (ToolRenderingManagerPinned.IsValid()) + { + const auto& Materials = ToolRenderingManagerPinned->GetToolsMaterials(); + for (auto& Material : Materials) + { + if (ensure(Material.IsValid())) + { + Apply(Material->GetMaterial()); + } + } + } + + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelProceduralMeshComponent::DoCustomNavigableGeometryExport(FNavigableGeometryExport& GeomExport) const +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto& Section : ProcMeshSections) + { + if (Section.Settings.bEnableNavmesh) + { + TArray Vertices; + // TODO is that copy needed + { + auto& PositionBuffer = Section.Buffers->VertexBuffers.PositionVertexBuffer; + Vertices.SetNumUninitialized(PositionBuffer.GetNumVertices()); + for (int32 Index = 0; Index < Vertices.Num(); Index++) + { + Vertices[Index] = PositionBuffer.VertexPosition(Index); + } + } + TArray Indices; + // Copy needed because int32 vs uint32 + { + auto& IndexBuffer = Section.Buffers->IndexBuffer; + Indices.SetNumUninitialized(IndexBuffer.GetNumIndices()); + for (int32 Index = 0; Index < Indices.Num(); Index++) + { + Indices[Index] = IndexBuffer.GetIndex(Index); + } + } + GeomExport.ExportCustomMesh(Vertices.GetData(), Vertices.Num(), Indices.GetData(), Indices.Num(), GetComponentTransform()); + } + } + return false; +} + +FBoxSphereBounds UVoxelProceduralMeshComponent::CalcBounds(const FTransform& LocalToWorld) const +{ + return FBoxSphereBounds(LocalBounds.TransformBy(LocalToWorld)); +} + +void UVoxelProceduralMeshComponent::OnComponentDestroyed(bool bDestroyingHierarchy) +{ + UPrimitiveComponent::OnComponentDestroyed(bDestroyingHierarchy); + + if (bInit) + { + // Clear convex collisions +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + UpdateConvexMeshes({}, {}, {}, true); +#endif + } + + // Destroy async cooker + if (AsyncCooker) + { + AsyncCooker->CancelAndAutodelete(); + AsyncCooker = nullptr; + } + + // Clear memory + ProcMeshSections.Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelProceduralMeshComponent::UpdatePhysicalMaterials() +{ + VOXEL_FUNCTION_COUNTER(); + + FBodyInstance* BodyInst = GetBodyInstance(); + if (BodyInst && BodyInst->IsValidBodyInstance()) + { + BodyInst->UpdatePhysicalMaterials(); + } +} + +void UVoxelProceduralMeshComponent::UpdateLocalBounds() +{ + VOXEL_FUNCTION_COUNTER(); + + FBox LocalBox(ForceInit); + + for (auto& Section : ProcMeshSections) + { + LocalBox += Section.Buffers->LocalBounds; + } + + LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) : FBoxSphereBounds(ForceInit); // fallback to reset box sphere bounds + + // Update global bounds + UpdateBounds(); + // Need to send to render thread + MarkRenderTransformDirty(); +} + +void UVoxelProceduralMeshComponent::UpdateNavigation() +{ + VOXEL_FUNCTION_COUNTER(); + + if (CanEverAffectNavigation() && IsRegistered() && GetWorld() && GetWorld()->GetNavigationSystem() && FNavigationSystem::WantsComponentChangeNotifies()) + { + bNavigationRelevant = IsNavigationRelevant(); + FNavigationSystem::UpdateComponentData(*this); + } +} + +void UVoxelProceduralMeshComponent::UpdateCollision() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(GetWorld())) + { + return; + } + + if (bAreCollisionsFrozen) + { + PendingCollisions.Add(this); + return; + } + + // Cancel existing task + if (AsyncCooker) + { + AsyncCooker->CancelAndAutodelete(); + AsyncCooker = nullptr; + ensure(BodySetupBeingCooked); + } + + if (!BodySetupBeingCooked) + { + BodySetupBeingCooked = NewObject(this); + } + VOXEL_INLINE_COUNTER("ClearPhysicsMeshes", BodySetupBeingCooked->ClearPhysicsMeshes()); + BodySetupBeingCooked->bGenerateMirroredCollision = false; + BodySetupBeingCooked->CollisionTraceFlag = CollisionTraceFlag; + + if (ProcMeshSections.FindByPredicate([](auto& Section) { return Section.Settings.bEnableCollisions; })) + { + auto PoolPtr = Pool.Pin(); + if (ensure(PoolPtr.IsValid())) + { + AsyncCooker = IVoxelAsyncPhysicsCooker::CreateCooker(this); + if (ensure(AsyncCooker)) + { + PoolPtr->QueueTask(EVoxelTaskType::CollisionCooking, AsyncCooker); + } + } + } + else + { +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + UpdateConvexMeshes({}, {}, {}); +#endif + FinishCollisionUpdate(); + } +} + +void UVoxelProceduralMeshComponent::FinishCollisionUpdate() +{ + VOXEL_FUNCTION_COUNTER(); + + ensure(BodySetupBeingCooked); + + Swap(BodySetup, BodySetupBeingCooked); + RecreatePhysicsState(); + + if (BodySetupBeingCooked) + { + BodySetupBeingCooked->ClearPhysicsMeshes(); + } + + if (CVarShowCollisionsUpdates.GetValueOnGameThread() && + ProcMeshSections.FindByPredicate([](auto& Section) { return Section.Settings.bEnableCollisions; })) + { + const auto Box = Bounds.GetBox(); + DrawDebugBox(GetWorld(), Box.GetCenter(), Box.GetExtent(), FColor::Red, false, 0.1); + } +} + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +void UVoxelProceduralMeshComponent::UpdateConvexMeshes( + const FBox& ConvexBounds, + TArray&& ConvexElements, + TArray&& ConvexMeshes, + bool bCanFail) +{ + VOXEL_FUNCTION_COUNTER(); + + ensure(UniqueId != 0); + ensure(ConvexElements.Num() == ConvexMeshes.Num()); + + if (CollisionTraceFlag == ECollisionTraceFlag::CTF_UseComplexAsSimple) + { + ensure(ConvexElements.Num() == 0); + return; + } + + auto* Owner = GetOwner(); + ensure(Owner || bCanFail); + if (!Owner) return; + + auto* Root = Cast(Owner->GetRootComponent()); + ensure(Root || bCanFail); + if (!Root) return; + + ensure(Root->CollisionTraceFlag == CollisionTraceFlag); + + Root->UpdateConvexCollision(UniqueId, ConvexBounds, MoveTemp(ConvexElements), MoveTemp(ConvexMeshes)); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelProceduralMeshComponent::PhysicsCookerCallback(uint64 CookerId) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!AsyncCooker || CookerId != AsyncCooker->UniqueId) + { + LOG_VOXEL(VeryVerbose, TEXT("Late async cooker callback, ignoring it")); + return; + } + if (!ensure(AsyncCooker->IsDone()) || !ensure(BodySetupBeingCooked)) + { + return; + } + + // Might not be needed? + VOXEL_INLINE_COUNTER("ClearPhysicsMeshes", BodySetupBeingCooked->ClearPhysicsMeshes()); + + FVoxelProceduralMeshComponentMemoryUsage NewMemoryUsage; + if (!AsyncCooker->Finalize(*BodySetupBeingCooked, NewMemoryUsage)) + { + return; + } + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPhysicsTriangleMeshesMemory, MemoryUsage.TriangleMeshes); + MemoryUsage.TriangleMeshes = NewMemoryUsage.TriangleMeshes; + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPhysicsTriangleMeshesMemory, MemoryUsage.TriangleMeshes); + + AsyncCooker->CancelAndAutodelete(); + AsyncCooker = nullptr; + + FinishCollisionUpdate(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp new file mode 100644 index 00000000..41c29785 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp @@ -0,0 +1,731 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelProceduralMeshSceneProxy.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelRender/VoxelToolRendering.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelMinimal.h" + +#include "Engine/Engine.h" +#include "Materials/Material.h" +#include "TessellationRendering.h" +#include "PhysicsEngine/BodySetup.h" +#include "DistanceFieldAtlas.h" +#include "PrimitiveSceneInfo.h" + +// Needed to cancel motion blur when reusing proxies +#include "Renderer/Private/ScenePrivate.h" + +#define NOT_SHIPPING_NOR_TEST !(UE_BUILD_SHIPPING || UE_BUILD_TEST) + +static TAutoConsoleVariable CVarLogProcMeshDelays( + TEXT("voxel.renderer.LogProcMeshDelays"), + 0, + TEXT("If true, will log the time elapsed between the game thread update and the first render thread display"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowToolRendering( + TEXT("voxel.renderer.ShowToolRendering"), + 0, + TEXT("If true, will show the duplicated meshes for tool rendering in red"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowMeshSections( + TEXT("voxel.renderer.ShowMeshSections"), + 0, + TEXT("If true, will assign a unique color to each mesh section"), + ECVF_Default); + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelMeshDistanceFieldMemory); + +DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Draw Calls"), STAT_NumVoxelDrawCalls, STATGROUP_VoxelCounters); +DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Draw Calls For Tools"), STAT_NumVoxelDrawCallsForTools, STATGROUP_VoxelCounters); + +DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Triangles Drawn "), STAT_NumVoxelTrianglesDrawn, STATGROUP_VoxelCounters); +DECLARE_DWORD_COUNTER_STAT(TEXT("Num Voxel Triangles Drawn For Tools"), STAT_NumVoxelTrianglesDrawnForTools, STATGROUP_VoxelCounters); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelProcMeshBuffersRenderData::FVoxelProcMeshBuffersRenderData( + const TVoxelSharedRef& Buffers, + ERHIFeatureLevel::Type FeatureLevel) + : Buffers(Buffers) + , VertexFactory(FeatureLevel, "FVoxelProcMeshBuffersRenderData") +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + { + auto& InitBuffers = const_cast(*Buffers); + BeginInitResource(&InitBuffers.VertexBuffers.PositionVertexBuffer); + BeginInitResource(&InitBuffers.VertexBuffers.StaticMeshVertexBuffer); + BeginInitResource(&InitBuffers.VertexBuffers.ColorVertexBuffer); + BeginInitResource(&InitBuffers.IndexBuffer); + BeginInitResource(&InitBuffers.AdjacencyIndexBuffer); + } + + auto& VertexBuffers = Buffers->VertexBuffers; + auto& IndexBuffer = Buffers->IndexBuffer; + + FLocalVertexFactory::FDataType Data; + VertexBuffers.PositionVertexBuffer.BindPositionVertexBuffer(&VertexFactory, Data); + VertexBuffers.StaticMeshVertexBuffer.BindTangentVertexBuffer(&VertexFactory, Data); + VertexBuffers.StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&VertexFactory, Data); + VertexBuffers.ColorVertexBuffer.BindColorVertexBuffer(&VertexFactory, Data); + VertexFactory.SetData(Data); + VertexFactory.InitResource(); + +#if RHI_RAYTRACING + if (IsRayTracingEnabled()) + { + FRayTracingGeometryInitializer Initializer; + Initializer.IndexBuffer = IndexBuffer.IndexBufferRHI; + Initializer.TotalPrimitiveCount = IndexBuffer.GetNumIndices() / 3; + Initializer.GeometryType = RTGT_Triangles; + Initializer.bFastBuild = true; + Initializer.bAllowUpdate = false; + +#if ENGINE_MINOR_VERSION < 24 + Initializer.PositionVertexBuffer = VertexBuffers.PositionVertexBuffer.VertexBufferRHI; + Initializer.BaseVertexIndex = 0; + Initializer.VertexBufferStride = VertexBuffers.PositionVertexBuffer.GetStride(); + Initializer.VertexBufferByteOffset = 0; + Initializer.VertexBufferElementType = VET_Float3; +#else + FRayTracingGeometrySegment Segment; + Segment.VertexBuffer = VertexBuffers.PositionVertexBuffer.VertexBufferRHI; + Segment.NumPrimitives = Initializer.TotalPrimitiveCount; + Initializer.Segments.Add(Segment); +#endif + + RayTracingGeometry.SetInitializer(Initializer); + RayTracingGeometry.InitResource(); + } +#endif +} + +TVoxelSharedRef FVoxelProcMeshBuffersRenderData::GetRenderData( + const TVoxelSharedRef& Buffers, + ERHIFeatureLevel::Type FeatureLevel) +{ + check(IsInRenderingThread()); + if (!Buffers->RenderData.IsValid()) + { + auto Result = TVoxelSharedRef(new FVoxelProcMeshBuffersRenderData(Buffers, FeatureLevel)); + Buffers->RenderData = Result; + return Result; + } + else + { + return Buffers->RenderData.Pin().ToSharedRef(); + } +} +FVoxelProcMeshBuffersRenderData::~FVoxelProcMeshBuffersRenderData() +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + auto& InitBuffers = const_cast(*Buffers); + InitBuffers.VertexBuffers.PositionVertexBuffer.ReleaseResource(); + InitBuffers.VertexBuffers.StaticMeshVertexBuffer.ReleaseResource(); + InitBuffers.VertexBuffers.ColorVertexBuffer.ReleaseResource(); + InitBuffers.IndexBuffer.ReleaseResource(); + InitBuffers.AdjacencyIndexBuffer.ReleaseResource(); + VertexFactory.ReleaseResource(); + +#if RHI_RAYTRACING + if (IsRayTracingEnabled()) + { + RayTracingGeometry.ReleaseResource(); + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline bool IsCollisionView(const FEngineShowFlags& EngineShowFlags) +{ + return EngineShowFlags.CollisionVisibility || EngineShowFlags.CollisionPawn; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelProceduralMeshSceneProxy::FVoxelProceduralMeshSceneProxy(UVoxelProceduralMeshComponent* Component) + : FPrimitiveSceneProxy(Component) + , Component(Component) + , MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel())) + , LOD(Component->LOD) + , DebugChunkId(Component->DebugChunkId) + , WeakToolRenderingManager(Component->ToolRenderingManager) + , CollisionResponse(Component->GetCollisionResponseToChannels()) + , CollisionTraceFlag(Component->CollisionTraceFlag) +{ + VOXEL_FUNCTION_COUNTER(); + + // Proxy settings + bSupportsDistanceFieldRepresentation = true; + DistanceFieldSelfShadowBias = Component->DistanceFieldSelfShadowBias; + bVerifyUsedMaterials = false; // Fails with tool rendering + + FinishSectionsUpdatesTime = Component->LastFinishSectionsUpdatesTime; + CreateSceneProxyTime = FPlatformTime::Seconds(); + + // Copy distance field data + DistanceFieldData = Component->DistanceFieldData; + + // Copy each section + const int32 NumSections = Component->ProcMeshSections.Num(); + Sections.SetNum(NumSections); + for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) + { + const auto& SrcSection = Component->ProcMeshSections[SectionIndex]; + FVoxelProcMeshProxySection& NewSection = Sections[SectionIndex]; + + ensure(SrcSection.Settings.bSectionVisible || SrcSection.Settings.bEnableCollisions || SrcSection.Settings.bEnableNavmesh); + + if (SrcSection.Buffers->GetNumVertices() == 0) + { + NewSection.bSectionVisible = false; + continue; + } + + // Visibility + NewSection.bSectionVisible = SrcSection.Settings.bSectionVisible; + + // Copy debug info + NewSection.bEnableCollisions_Debug = SrcSection.Settings.bEnableCollisions; + NewSection.bEnableNavmesh_Debug = SrcSection.Settings.bEnableNavmesh; + + // Grab material + NewSection.Material = SrcSection.Settings.Material; + if (!NewSection.Material.IsValid()) + { + NewSection.Material = FVoxelMaterialInterfaceManager::Get().DefaultMaterial(); + } + check(NewSection.Material.IsValid()); + + // Copy buffer ptr + NewSection.Buffers = SrcSection.Buffers; + + // Tessellation + auto& IndexBuffer = NewSection.Buffers->IndexBuffer; + auto& AdjacencyIndexBuffer = NewSection.Buffers->AdjacencyIndexBuffer; + + check( + AdjacencyIndexBuffer.GetNumIndices() == 0 || + AdjacencyIndexBuffer.GetNumIndices() == 4 * IndexBuffer.GetNumIndices()); + + const bool bTessellatedMaterial = + RequiresAdjacencyInformation( + NewSection.Material->GetMaterial(), + &FLocalVertexFactory::StaticType, + GetScene().GetFeatureLevel()); + const bool bHasAdjacency = AdjacencyIndexBuffer.GetNumIndices() > 0; + + ensure(SrcSection.Settings.bEnableTessellation == bHasAdjacency); + ensureMsgf(bTessellatedMaterial == bHasAdjacency, TEXT("Invalid tessellated material or non tessellated material is tessellated")); + NewSection.bRequiresAdjacencyInformation = bTessellatedMaterial && bHasAdjacency; + } + + { + // Hack to cancel motion blur when mesh components are reused in the same frame + const FMatrix PreviousLocalToWorld = Component->GetRenderMatrix(); + ENQUEUE_RENDER_COMMAND(UpdateTransformCommand)( + [this, PreviousLocalToWorld](FRHICommandListImmediate& RHICmdList) + { + FScene& Scene = static_cast(GetScene()); + Scene.VelocityData.OverridePreviousTransform(GetPrimitiveComponentId(), PreviousLocalToWorld); + }); + } +} + +FVoxelProceduralMeshSceneProxy::~FVoxelProceduralMeshSceneProxy() +{ +#if ENGINE_MINOR_VERSION <= 23 + DestroyRenderThreadResources(); +#endif + + for (auto& Section : Sections) + { + check(!Section.RenderData.IsValid()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelProceduralMeshSceneProxy::CreateRenderThreadResources() +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + for (auto& Section : Sections) + { + check(!Section.RenderData.IsValid()); + check(Section.Buffers.IsValid()); + if (Section.bSectionVisible || NOT_SHIPPING_NOR_TEST) // Need to init for debug + { + Section.RenderData = FVoxelProcMeshBuffersRenderData::GetRenderData(Section.Buffers.ToSharedRef(), GetScene().GetFeatureLevel()); + } + } + + if (DistanceFieldData.IsValid()) + { + const_cast(DistanceFieldData->VolumeTexture).Initialize(reinterpret_cast(Component)); // Horrible hack, but w/e if it works :) + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelMeshDistanceFieldMemory, DistanceFieldData->GetResourceSizeBytes()); + } +} + +void FVoxelProceduralMeshSceneProxy::DestroyRenderThreadResources() +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + for (auto& Section : Sections) + { + Section.RenderData.Reset(); + } + + if (DistanceFieldData.IsValid()) + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelMeshDistanceFieldMemory, DistanceFieldData->GetResourceSizeBytes()); + const_cast(DistanceFieldData->VolumeTexture).Release(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelProceduralMeshSceneProxy::GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + + const auto& EngineShowFlags = ViewFamily.EngineShowFlags; + +#if NOT_SHIPPING_NOR_TEST + // Hack to see the delay between update call and actual mesh render update + if (!bLoggedTime && CVarLogProcMeshDelays.GetValueOnRenderThread() != 0 && FinishSectionsUpdatesTime > 0) + { + const double Time = FPlatformTime::Seconds(); + LOG_VOXEL(Log, TEXT("Proc Mesh Delays: ChunkId: %u; CreateSceneProxy: %fms; GetDynamicMeshElements: %fms"), + DebugChunkId, + (CreateSceneProxyTime - FinishSectionsUpdatesTime) * 1000, + (Time - FinishSectionsUpdatesTime) * 1000); + bLoggedTime = true; + } + + // Render bounds + { + VOXEL_SLOW_SCOPE_COUNTER("Render Bounds"); + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (VisibilityMap & (1 << ViewIndex)) + { + RenderBounds(Collector.GetPDI(ViewIndex), EngineShowFlags, GetBounds(), IsSelected()); + } + } + } + + if (IsCollisionView(EngineShowFlags)) + { + if (ShouldDrawComplexCollisions(EngineShowFlags)) + { + VOXEL_SLOW_SCOPE_COUNTER("Complex Collisons"); + for (auto& Section : Sections) + { + if (!Section.bEnableCollisions_Debug) continue; + + const FColor ComplexCollisionColor = FColor(0, 255, 255, 255); + auto* MaterialProxy = new FColoredMaterialRenderProxy(GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(), ComplexCollisionColor); + Collector.RegisterOneFrameMaterialProxy(MaterialProxy); + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + + FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, false, EngineShowFlags.Wireframe); + Collector.AddMesh(ViewIndex, Mesh); + } + } + } + } + else if (FVoxelDebugManager::ShowCollisionAndNavmeshDebug()) + { + VOXEL_SLOW_SCOPE_COUNTER("Collision and Navmesh Debug"); + for (auto& Section : Sections) + { + if (!Section.RenderData.IsValid()) continue; + + const auto* ParentMaterial = + EngineShowFlags.Wireframe + ? GEngine->WireframeMaterial + : GEngine->LevelColorationLitMaterial; + + const auto Color = FVoxelDebugManager::GetCollisionAndNavmeshDebugColor(Section.bEnableCollisions_Debug, Section.bEnableNavmesh_Debug); + + if (!ensure(ParentMaterial)) return; // Happens in packaged games + + auto* MaterialProxy = new FColoredMaterialRenderProxy(ParentMaterial->GetRenderProxy(), Color); + Collector.RegisterOneFrameMaterialProxy(MaterialProxy); + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + + FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, false, EngineShowFlags.Wireframe); + Mesh.bCanApplyViewModeOverrides = false; + Collector.AddMesh(ViewIndex, Mesh); + } + } + } + else +#endif + { + const bool bForceDisableTessellation = !(EngineShowFlags.Materials || EngineShowFlags.Wireframe); // else crash in eg lighting + + for (const auto& Section : Sections) + { + VOXEL_SLOW_SCOPE_COUNTER("Render Section"); + + if (!Section.bSectionVisible) + { + continue; + } + + auto* Material = Section.Material->GetMaterial(); + if (!Material) + { + // Will happen in force delete + continue; + } + + auto* MaterialProxy = Material->GetRenderProxy(); + + if (CVarShowMeshSections.GetValueOnRenderThread() != 0) + { + uint32 Hash = 0; + for (auto& Guid : Section.Buffers->Guids) + { + Hash = FVoxelUtilities::MurmurHash64((uint64(Hash) << 32) ^ Guid.A ^ Guid.B ^ Guid.C ^ Guid.D); + } + MaterialProxy = new FColoredMaterialRenderProxy(GEngine->LevelColorationLitMaterial->GetRenderProxy(), reinterpret_cast(Hash)); + Collector.RegisterOneFrameMaterialProxy(MaterialProxy); + } + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + + FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, !bForceDisableTessellation, EngineShowFlags.Wireframe); + + { + VOXEL_SLOW_SCOPE_COUNTER("Collector.AddMesh"); + Collector.AddMesh(ViewIndex, Mesh); + } + + INC_DWORD_STAT(STAT_NumVoxelDrawCalls); + INC_DWORD_STAT_BY(STAT_NumVoxelTrianglesDrawn, Section.Buffers->GetNumIndices() / 3); + } + } + + const auto ToolRenderingManager = WeakToolRenderingManager.Pin(); + if (ToolRenderingManager.IsValid()) + { + VOXEL_RENDER_SCOPE_COUNTER("Render Tools"); + + TArray> Tools; + + const FBox WorldBounds = GetBounds().GetBox(); + ToolRenderingManager->IterateTools( + [&](const FVoxelToolRendering& Tool) + { + if (Tool.bEnabled && Tool.WorldBounds.Intersect(WorldBounds)) + { + Tools.Add(Tool); + } + }); + + for (auto& Tool : Tools) + { + UMaterialInterface* Material = nullptr; + if (Tool.Material.IsValid()) + { + Material = Tool.Material->GetMaterial(); + } + if (!Material) + { + Material = FVoxelMaterialInterfaceManager::Get().DefaultMaterial()->GetMaterial(); + } + if (!ensure(Material)) + { + continue; + } + auto* MaterialProxy = Material->GetRenderProxy(); + + // Hack to fix translucent rendering when the tool material was changed but the mesh wasn't updated + const_cast(MaterialRelevance) |= Material->GetMaterial()->GetRelevance_Concurrent(GetScene().GetFeatureLevel()); + + if (CVarShowToolRendering.GetValueOnRenderThread() != 0) + { + MaterialProxy = new FColoredMaterialRenderProxy(GEngine->LevelColorationUnlitMaterial->GetRenderProxy(), FColor::Red); + Collector.RegisterOneFrameMaterialProxy(MaterialProxy); + } + + for (const auto& Section : Sections) + { + if (!Section.bSectionVisible) continue; + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + + FMeshBatch& Mesh = DrawSection(Collector, Section, MaterialProxy, !bForceDisableTessellation, EngineShowFlags.Wireframe); + Collector.AddMesh(ViewIndex, Mesh); + + INC_DWORD_STAT(STAT_NumVoxelDrawCallsForTools); + INC_DWORD_STAT_BY(STAT_NumVoxelTrianglesDrawnForTools, Section.Buffers->GetNumIndices() / 3) + } + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if RHI_RAYTRACING +void FVoxelProceduralMeshSceneProxy::GetDynamicRayTracingInstances(FRayTracingMaterialGatheringContext& Context, TArray& OutRayTracingInstances) +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + + for (const auto& Section : Sections) + { + if (Section.bSectionVisible && ensure(Section.Material->GetMaterial()->IsValidLowLevel())) + { + auto& RenderData = *Section.RenderData; + if (RenderData.RayTracingGeometry.RayTracingGeometryRHI.IsValid()) + { + check(RenderData.RayTracingGeometry.Initializer.IndexBuffer.IsValid()); + + FRayTracingInstance RayTracingInstance; + RayTracingInstance.Geometry = &RenderData.RayTracingGeometry; + RayTracingInstance.InstanceTransforms.Add(GetLocalToWorld()); + + FMeshBatch MeshBatch; + + MeshBatch.VertexFactory = &RenderData.VertexFactory; + MeshBatch.SegmentIndex = 0; + MeshBatch.MaterialRenderProxy = Section.Material->GetMaterial()->GetRenderProxy(); + MeshBatch.ReverseCulling = IsLocalToWorldDeterminantNegative(); + MeshBatch.Type = PT_TriangleList; + MeshBatch.DepthPriorityGroup = SDPG_World; + MeshBatch.bCanApplyViewModeOverrides = false; + + FMeshBatchElement& BatchElement = MeshBatch.Elements[0]; + BatchElement.IndexBuffer = &Section.Buffers->IndexBuffer; + + BatchElement.FirstIndex = 0; + BatchElement.NumPrimitives = Section.Buffers->IndexBuffer.GetNumIndices() / 3; + BatchElement.MinVertexIndex = 0; + BatchElement.MaxVertexIndex = Section.Buffers->VertexBuffers.PositionVertexBuffer.GetNumVertices() - 1; + + RayTracingInstance.Materials.Add(MeshBatch); + + RayTracingInstance.BuildInstanceMaskAndFlags(); + OutRayTracingInstances.Add(RayTracingInstance); + } + } + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FPrimitiveViewRelevance FVoxelProceduralMeshSceneProxy::GetViewRelevance(const FSceneView* View) const +{ + FPrimitiveViewRelevance Result; + Result.bDrawRelevance = IsShown(View); + Result.bShadowRelevance = IsShadowCast(View); + Result.bDynamicRelevance = true; + Result.bRenderInMainPass = ShouldRenderInMainPass(); + Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask(); + Result.bRenderCustomDepth = ShouldRenderCustomDepth(); + Result.bTranslucentSelfShadow = bCastVolumetricTranslucentShadow; + MaterialRelevance.SetPrimitiveViewRelevance(Result); + Result.bVelocityRelevance = IsMovable() && Result.UE_25_SWITCH(bOpaqueRelevance, bOpaque) && Result.bRenderInMainPass; + return Result; +} + +bool FVoxelProceduralMeshSceneProxy::CanBeOccluded() const +{ + return !MaterialRelevance.bDisableDepthTest; +} + +uint32 FVoxelProceduralMeshSceneProxy::GetMemoryFootprint() const +{ + return sizeof(*this) + GetAllocatedSize(); +} + +SIZE_T FVoxelProceduralMeshSceneProxy::GetTypeHash() const +{ + static size_t UniquePointer; + return reinterpret_cast(&UniquePointer); +} + +uint32 FVoxelProceduralMeshSceneProxy::GetAllocatedSize() const +{ + return Sections.GetAllocatedSize() + FPrimitiveSceneProxy::GetAllocatedSize(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelProceduralMeshSceneProxy::GetDistancefieldAtlasData( + FBox& LocalVolumeBounds, + FVector2D& OutDistanceMinMax, + FIntVector& OutBlockMin, + FIntVector& OutBlockSize, + bool& bOutBuiltAsIfTwoSided, + bool& bMeshWasPlane, + float& SelfShadowBias, + TArray& ObjectLocalToWorldTransforms, + bool& bOutThrottled) const +{ + if (DistanceFieldData.IsValid()) + { + LocalVolumeBounds = DistanceFieldData->LocalBoundingBox; + OutDistanceMinMax = DistanceFieldData->DistanceMinMax; + OutBlockMin = DistanceFieldData->VolumeTexture.GetAllocationMin(); + OutBlockSize = DistanceFieldData->VolumeTexture.GetAllocationSize(); + bOutBuiltAsIfTwoSided = DistanceFieldData->bBuiltAsIfTwoSided; + bMeshWasPlane = DistanceFieldData->bMeshWasPlane; + ObjectLocalToWorldTransforms.Add(GetLocalToWorld()); + SelfShadowBias = DistanceFieldSelfShadowBias; + bOutThrottled = DistanceFieldData->VolumeTexture.Throttled(); + } + else + { + LocalVolumeBounds = FBox(ForceInit); + OutDistanceMinMax = FVector2D(0, 0); + OutBlockMin = FIntVector(-1, -1, -1); + OutBlockSize = FIntVector(0, 0, 0); + bOutBuiltAsIfTwoSided = false; + bMeshWasPlane = false; + SelfShadowBias = 0; + bOutThrottled = false; + } +} + +void FVoxelProceduralMeshSceneProxy::GetDistanceFieldInstanceInfo(int32& NumInstances, float& BoundsSurfaceArea) const +{ + NumInstances = DistanceFieldData.IsValid() ? 1 : 0; + const FVector AxisScales = GetLocalToWorld().GetScaleVector(); + const FVector BoxDimensions = GetBounds().BoxExtent * AxisScales * 2; + + BoundsSurfaceArea = + 2 * BoxDimensions.X * BoxDimensions.Y + + 2 * BoxDimensions.Z * BoxDimensions.Y + + 2 * BoxDimensions.X * BoxDimensions.Z; +} + +bool FVoxelProceduralMeshSceneProxy::HasDistanceFieldRepresentation() const +{ + return CastsDynamicShadow() && AffectsDistanceFieldLighting() && DistanceFieldData.IsValid() && DistanceFieldData->VolumeTexture.IsValidDistanceFieldVolume(); +} + +bool FVoxelProceduralMeshSceneProxy::HasDynamicIndirectShadowCasterRepresentation() const +{ + return bCastsDynamicIndirectShadow && FVoxelProceduralMeshSceneProxy::HasDistanceFieldRepresentation(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FMeshBatch& FVoxelProceduralMeshSceneProxy::DrawSection( + FMeshElementCollector& Collector, + const FVoxelProcMeshProxySection& Section, + const FMaterialRenderProxy* MaterialRenderProxy, + bool bEnableTessellation, + bool bWireframe) const +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + + check(MaterialRenderProxy); + check(Section.RenderData.IsValid()); + + FMeshBatch& Mesh = Collector.AllocateMesh(); + + Mesh.VertexFactory = &Section.RenderData->VertexFactory; + Mesh.MaterialRenderProxy = MaterialRenderProxy; + Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative(); + Mesh.Type = PT_TriangleList; + Mesh.DepthPriorityGroup = SDPG_World; + Mesh.bUseWireframeSelectionColoring = IsSelected() && bWireframe; // Else mesh LODs view is messed up when actor is selected + Mesh.bCanApplyViewModeOverrides = true; +#if NOT_SHIPPING_NOR_TEST + Mesh.VisualizeLODIndex = LOD % GEngine->LODColorationColors.Num(); +#endif + + FMeshBatchElement& BatchElement = Mesh.Elements[0]; + BatchElement.IndexBuffer = &Section.Buffers->IndexBuffer; + BatchElement.FirstIndex = 0; + BatchElement.NumPrimitives = Section.Buffers->IndexBuffer.GetNumIndices() / 3; + BatchElement.MinVertexIndex = 0; + BatchElement.MaxVertexIndex = Section.Buffers->VertexBuffers.PositionVertexBuffer.GetNumVertices() - 1; + +#if ENABLE_TESSELLATION + if (bEnableTessellation) + { + // Could be different from bRequiresAdjacencyInformation during shader compilation + const bool bCurrentRequiresAdjacencyInformation = RequiresAdjacencyInformation( + MaterialRenderProxy->GetMaterialInterface(), + &FLocalVertexFactory::StaticType, + GetScene().GetFeatureLevel()); + + if (ensure(Section.Buffers->IndexBuffer.GetNumIndices() != 0) && + Section.bRequiresAdjacencyInformation && + bCurrentRequiresAdjacencyInformation) + { + Mesh.Type = PT_12_ControlPointPatchList; + BatchElement.IndexBuffer = &Section.Buffers->AdjacencyIndexBuffer; + BatchElement.FirstIndex *= 4; + } + } +#endif + + return Mesh; +} + +bool FVoxelProceduralMeshSceneProxy::ShouldDrawComplexCollisions(const FEngineShowFlags& EngineShowFlags) const +{ + if (IsCollisionEnabled()) + { + // See if we have a response to the interested channel + bool bHasResponse = EngineShowFlags.CollisionPawn && CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore; + bHasResponse |= EngineShowFlags.CollisionVisibility && CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore; + + if (bHasResponse) + { + // Visibility uses complex and pawn uses simple. However, if UseSimpleAsComplex or UseComplexAsSimple is used we need to adjust accordingly + return + (EngineShowFlags.CollisionVisibility && CollisionTraceFlag != ECollisionTraceFlag::CTF_UseSimpleAsComplex) || + (EngineShowFlags.CollisionPawn && CollisionTraceFlag == ECollisionTraceFlag::CTF_UseComplexAsSimple); + } + } + return false; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h new file mode 100644 index 00000000..58b0e561 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h @@ -0,0 +1,118 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "StaticMeshResources.h" +#include "PrimitiveSceneProxy.h" +#include "VoxelMinimal.h" +#if RHI_RAYTRACING +#include "RayTracingDefinitions.h" +#include "RayTracingInstance.h" +#endif + +class FVoxelToolRenderingManager; +class UVoxelProceduralMeshComponent; +class FVoxelMaterialInterface; +struct FVoxelProcMeshBuffers; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Mesh Distance Field Memory"), STAT_VoxelMeshDistanceFieldMemory, STATGROUP_VoxelMemory, VOXEL_API); + +class FVoxelProcMeshBuffersRenderData : public TVoxelSharedFromThis +{ +public: + const TVoxelSharedRef Buffers; + + FLocalVertexFactory VertexFactory; +#if RHI_RAYTRACING + FRayTracingGeometry RayTracingGeometry; +#endif + + static TVoxelSharedRef GetRenderData( + const TVoxelSharedRef& Buffers, + ERHIFeatureLevel::Type FeatureLevel); + ~FVoxelProcMeshBuffersRenderData(); + +private: + explicit FVoxelProcMeshBuffersRenderData( + const TVoxelSharedRef& Buffers, + ERHIFeatureLevel::Type FeatureLevel); +}; + +struct FVoxelProcMeshProxySection +{ + TVoxelSharedPtr Material; + TVoxelSharedPtr Buffers; + TVoxelSharedPtr RenderData; + + bool bSectionVisible = true; + bool bRequiresAdjacencyInformation = false; + + bool bEnableCollisions_Debug = false; + bool bEnableNavmesh_Debug = false; +}; + +class FVoxelProceduralMeshSceneProxy : public FPrimitiveSceneProxy +{ +public: + explicit FVoxelProceduralMeshSceneProxy(UVoxelProceduralMeshComponent* Component); + ~FVoxelProceduralMeshSceneProxy(); + + //~ Begin FPrimitiveSceneProxy Interface + virtual void CreateRenderThreadResources() override; + ONLY_UE_24_AND_HIGHER(virtual) void DestroyRenderThreadResources() ONLY_UE_24_AND_HIGHER(override); + + virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override; + +#if RHI_RAYTRACING + virtual bool IsRayTracingRelevant() const override { return true; } + virtual void GetDynamicRayTracingInstances(FRayTracingMaterialGatheringContext& Context, TArray& OutRayTracingInstances) override; +#endif + + virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override; + virtual bool CanBeOccluded() const override; + virtual uint32 GetMemoryFootprint() const override; + virtual SIZE_T GetTypeHash() const override; + uint32 GetAllocatedSize() const; + + virtual void GetDistancefieldAtlasData( + FBox& LocalVolumeBounds, + FVector2D& OutDistanceMinMax, + FIntVector& OutBlockMin, + FIntVector& OutBlockSize, + bool& bOutBuiltAsIfTwoSided, + bool& bMeshWasPlane, + float& SelfShadowBias, + TArray& ObjectLocalToWorldTransforms, + bool& bOutThrottled) const override; + virtual void GetDistanceFieldInstanceInfo(int32& NumInstances, float& BoundsSurfaceArea) const override; + virtual bool HasDistanceFieldRepresentation() const override; + virtual bool HasDynamicIndirectShadowCasterRepresentation() const override; + //~ End FPrimitiveSceneProxy Interface + +private: + UVoxelProceduralMeshComponent* const Component; + const FMaterialRelevance MaterialRelevance; + const int32 LOD; + const uint32 DebugChunkId; + const TVoxelWeakPtr WeakToolRenderingManager; + + const FCollisionResponseContainer CollisionResponse; + const ECollisionTraceFlag CollisionTraceFlag; + + TArray Sections; + TVoxelSharedPtr DistanceFieldData; + + double FinishSectionsUpdatesTime = 0; + double CreateSceneProxyTime = 0; + mutable bool bLoggedTime = false; + + FMeshBatch& DrawSection( + FMeshElementCollector& Collector, + const FVoxelProcMeshProxySection& Section, + const FMaterialRenderProxy* MaterialRenderProxy, + bool bEnableTessellation, + bool bWireframe) const; + + bool ShouldDrawComplexCollisions(const FEngineShowFlags& EngineShowFlags) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRawStaticIndexBuffer.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRawStaticIndexBuffer.cpp new file mode 100644 index 00000000..5866de68 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRawStaticIndexBuffer.cpp @@ -0,0 +1,205 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelRawStaticIndexBuffer.h" + +FVoxelRawStaticIndexBuffer::FVoxelRawStaticIndexBuffer(bool InNeedsCPUAccess) + : IndexStorage(InNeedsCPUAccess) + , b32Bit(false) +{ +} + +void FVoxelRawStaticIndexBuffer::SetIndices(const TArray& InIndices, EIndexBufferStride::Type DesiredStride) +{ + const int32 NumInIndices = InIndices.Num(); + bool bShouldUse32Bit = false; + + // Figure out if we should store the indices as 16 or 32 bit. + if (DesiredStride == EIndexBufferStride::Force32Bit) + { + bShouldUse32Bit = true; + } + else if (DesiredStride == EIndexBufferStride::AutoDetect) + { + int32 i = 0; + while (!bShouldUse32Bit && i < NumInIndices) + { + bShouldUse32Bit = InIndices[i] > MAX_uint16; + i++; + } + } + + // Allocate storage for the indices. + const int32 IndexStride = bShouldUse32Bit ? sizeof(uint32) : sizeof(uint16); + IndexStorage.Empty(IndexStride * NumInIndices); + IndexStorage.AddUninitialized(IndexStride * NumInIndices); + + // Store them! + if (bShouldUse32Bit) + { + // If the indices are 32 bit we can just do a memcpy. + check(IndexStorage.Num() == InIndices.Num() * InIndices.GetTypeSize()); + FMemory::Memcpy(IndexStorage.GetData(),InIndices.GetData(),IndexStorage.Num()); + b32Bit = true; + } + else + { + // Copy element by element demoting 32-bit integers to 16-bit. + check(IndexStorage.Num() == InIndices.Num() * sizeof(uint16)); + uint16* DestIndices16Bit = reinterpret_cast(IndexStorage.GetData()); + for (int32 i = 0; i < NumInIndices; ++i) + { + DestIndices16Bit[i] = InIndices[i]; + } + b32Bit = false; + } + + UpdateCachedNumIndices(); +} + +void FVoxelRawStaticIndexBuffer::AllocateData(int32 NumInIndices) +{ + const bool bShouldUse32Bit = NumInIndices > MAX_uint16; + + // Allocate storage for the indices. + const int32 IndexStride = bShouldUse32Bit ? sizeof(uint32) : sizeof(uint16); + IndexStorage.Empty(IndexStride * NumInIndices); + IndexStorage.AddUninitialized(IndexStride * NumInIndices); + + b32Bit = bShouldUse32Bit; + + UpdateCachedNumIndices(); +} + +void FVoxelRawStaticIndexBuffer::InsertIndices( const uint32 At, const uint32* IndicesToAppend, const uint32 NumIndicesToAppend ) +{ + if( NumIndicesToAppend > 0 ) + { + const uint32 IndexStride = b32Bit ? sizeof( uint32 ) : sizeof( uint16 ); + + IndexStorage.InsertUninitialized( At * IndexStride, NumIndicesToAppend * IndexStride ); + uint8* const DestIndices = &IndexStorage[ At * IndexStride ]; + + if( IndicesToAppend ) + { + if( b32Bit ) + { + // If the indices are 32 bit we can just do a memcpy. + FMemory::Memcpy( DestIndices, IndicesToAppend, NumIndicesToAppend * IndexStride ); + } + else + { + // Copy element by element demoting 32-bit integers to 16-bit. + uint16* DestIndices16Bit = reinterpret_cast(DestIndices); + for( uint32 Index = 0; Index < NumIndicesToAppend; ++Index ) + { + DestIndices16Bit[ Index ] = IndicesToAppend[ Index ]; + } + } + } + else + { + // If no indices to insert were supplied, just clear the buffer + FMemory::Memset( DestIndices, 0, NumIndicesToAppend * IndexStride ); + } + } + + UpdateCachedNumIndices(); +} + +void FVoxelRawStaticIndexBuffer::AppendIndices( const uint32* IndicesToAppend, const uint32 NumIndicesToAppend ) +{ + InsertIndices( b32Bit ? IndexStorage.Num() / 4 : IndexStorage.Num() / 2, IndicesToAppend, NumIndicesToAppend ); +} + +void FVoxelRawStaticIndexBuffer::RemoveIndicesAt( const uint32 At, const uint32 NumIndicesToRemove ) +{ + if( NumIndicesToRemove > 0 ) + { + const int32 IndexStride = b32Bit ? sizeof( uint32 ) : sizeof( uint16 ); + IndexStorage.RemoveAt( At * IndexStride, NumIndicesToRemove * IndexStride ); + } + + UpdateCachedNumIndices(); +} + +void FVoxelRawStaticIndexBuffer::GetCopy(TArray& OutIndices) const +{ + OutIndices.Empty(NumIndices); + OutIndices.AddUninitialized(NumIndices); + + if (b32Bit) + { + // If the indices are 32 bit we can just do a memcpy. + check(IndexStorage.Num() == OutIndices.Num() * OutIndices.GetTypeSize()); + FMemory::Memcpy(OutIndices.GetData(),IndexStorage.GetData(),IndexStorage.Num()); + } + else + { + // Copy element by element promoting 16-bit integers to 32-bit. + check(IndexStorage.Num() == OutIndices.Num() * sizeof(uint16)); + const uint16* SrcIndices16Bit = reinterpret_cast(IndexStorage.GetData()); + for (uint32 i = 0; i < NumIndices; ++i) + { + OutIndices[i] = SrcIndices16Bit[i]; + } + } +} + +const uint16* FVoxelRawStaticIndexBuffer::AccessStream16() const +{ + if (!b32Bit) + { + return reinterpret_cast(IndexStorage.GetData()); + } + return nullptr; +} + +FIndexArrayView FVoxelRawStaticIndexBuffer::GetArrayView() const +{ + return FIndexArrayView(IndexStorage.GetData(), NumIndices, b32Bit); +} + +void FVoxelRawStaticIndexBuffer::InitRHI() +{ + const uint32 IndexStride = b32Bit ? sizeof(uint32) : sizeof(uint16); + const uint32 SizeInBytes = IndexStorage.Num(); + check(NumIndices == (b32Bit ? (IndexStorage.Num() / 4) : (IndexStorage.Num() / 2))); + + if (SizeInBytes > 0) + { + // Create the index buffer. + FRHIResourceCreateInfo CreateInfo(&IndexStorage); + IndexBufferRHI = RHICreateIndexBuffer(IndexStride,SizeInBytes,BUF_Static,CreateInfo); + } +} + +void FVoxelRawStaticIndexBuffer::Serialize(FArchive& Ar, bool bNeedsCPUAccess) +{ + IndexStorage.SetAllowCPUAccess(bNeedsCPUAccess); + + if (Ar.UE4Ver() < VER_UE4_SUPPORT_32BIT_STATIC_MESH_INDICES) + { + TResourceArray LegacyIndices; + + b32Bit = false; + LegacyIndices.BulkSerialize(Ar); + const int32 NumLegacyIndices = LegacyIndices.Num(); + const int32 IndexStride = sizeof(uint16); + IndexStorage.Empty(NumLegacyIndices * IndexStride); + IndexStorage.AddUninitialized(NumLegacyIndices * IndexStride); + FMemory::Memcpy(IndexStorage.GetData(),LegacyIndices.GetData(),IndexStorage.Num()); + } + else + { + Ar << b32Bit; + IndexStorage.BulkSerialize(Ar); + } +} + +void FVoxelRawStaticIndexBuffer::Discard() +{ + IndexStorage.SetAllowCPUAccess(false); + IndexStorage.Discard(); + + UpdateCachedNumIndices(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.cpp new file mode 100644 index 00000000..1a2960a5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.cpp @@ -0,0 +1,656 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/VoxelRenderUtilities.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelRender/VoxelChunkMaterials.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/VoxelChunkToUpdate.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/Meshers/VoxelMesherUtilities.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" +#include "VoxelMessages.h" + +#include "Materials/MaterialInstanceDynamic.h" + +static TAutoConsoleVariable CVarMaxSectionsPerChunk( + TEXT("voxel.renderer.MaxSectionsPerChunk"), + 128, + TEXT("If a voxel chunk has more sections that this (eg due to single/double index), it won't be drawn. 1 section = 1 draw call"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowTransitions( + TEXT("voxel.renderer.ShowTransitions"), + 0, + TEXT("If true, will only show the transition meshes"), + ECVF_Default); + +float FVoxelRenderUtilities::GetWorldCurrentTime(UWorld* World) +{ + if (!ensure(World)) return 0; + if (World->WorldType == EWorldType::Editor) + { + return FApp::GetCurrentTime() - GStartTime; + } + else + { + return World->GetTimeSeconds(); + } +} + +void FVoxelRenderUtilities::InitializeMaterialInstance( + UMaterialInstanceDynamic* MaterialInstance, + int32 LOD, + const FIntVector& Position, + const FVoxelRendererSettingsBase& Settings) +{ + VOXEL_FUNCTION_COUNTER(); + + MaterialInstance->SetScalarParameterValue(STATIC_FNAME("LOD"), LOD); + MaterialInstance->SetVectorParameterValue(STATIC_FNAME("ChunkPosition"), FVector(Position)); + MaterialInstance->SetScalarParameterValue(STATIC_FNAME("VoxelSize"), Settings.VoxelSize); + MaterialInstance->SetScalarParameterValue(STATIC_FNAME("ChunkSize"), RENDER_CHUNK_SIZE); + MaterialInstance->SetScalarParameterValue(STATIC_FNAME("FadeDuration"), Settings.ChunksDitheringDuration); +} + +template +inline void IterateDynamicMaterials(UVoxelProceduralMeshComponent& Mesh, T Lambda) +{ + Mesh.IterateSectionsSettings([&](FVoxelProcMeshSectionSettings& SectionSettings) + { + if (!ensure(SectionSettings.Material.IsValid())) return; + UMaterialInstanceDynamic* Material = Cast(SectionSettings.Material->GetMaterial()); + if (Material) + { + Lambda(*Material); + } + }); +} + +inline void SetMaterialDithering( + UMaterialInstanceDynamic& Material, + const FVoxelRendererSettingsBase& Settings, + const FVoxelRenderUtilities::FDitheringInfo& DitheringInfo) +{ + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + check(DitheringInfo.DitheringType == EDitheringType::SurfaceNets_LowResToHighRes || DitheringInfo.DitheringType == EDitheringType::SurfaceNets_HighResToLowRes); + Material.SetScalarParameterValue(STATIC_FNAME("StartTime"), DitheringInfo.Time); + Material.SetScalarParameterValue(STATIC_FNAME("InvertedFade"), DitheringInfo.DitheringType == EDitheringType::SurfaceNets_HighResToLowRes ? 1 : 0); + } + else + { + check(DitheringInfo.DitheringType == EDitheringType::Classic_DitherIn || DitheringInfo.DitheringType == EDitheringType::Classic_DitherOut); + + // StartTime and EndTime are a bit tricky: what's actually done in the shader is + // min(EndTime - Time, Time - StartTime) / FadeDuration + if (DitheringInfo.DitheringType == EDitheringType::Classic_DitherIn) + { + Material.SetScalarParameterValue(STATIC_FNAME("StartTime"), DitheringInfo.Time); + Material.SetScalarParameterValue(STATIC_FNAME("EndTime"), 1e8); + } + else + { + // First dither in new chunk, then dither out old chunk + Material.SetScalarParameterValue(STATIC_FNAME("StartTime"), 0); + Material.SetScalarParameterValue(STATIC_FNAME("EndTime"), DitheringInfo.Time + 2 * Settings.ChunksDitheringDuration); + } + } +} + +void FVoxelRenderUtilities::StartMeshDithering( + UVoxelProceduralMeshComponent& Mesh, + const FVoxelRendererSettingsBase& Settings, + const FDitheringInfo& DitheringInfo) +{ + VOXEL_FUNCTION_COUNTER(); + IterateDynamicMaterials(Mesh, [&](UMaterialInstanceDynamic& Material) + { + SetMaterialDithering(Material, Settings, DitheringInfo); + }); +} + +void FVoxelRenderUtilities::ResetDithering(UVoxelProceduralMeshComponent& Mesh, const FVoxelRendererSettingsBase& Settings) +{ + VOXEL_FUNCTION_COUNTER(); + IterateDynamicMaterials(Mesh, [&](UMaterialInstanceDynamic& Material) + { + if (Settings.RenderType == EVoxelRenderType::SurfaceNets) + { + Material.SetScalarParameterValue(STATIC_FNAME("StartTime"), 0); + Material.SetScalarParameterValue(STATIC_FNAME("InversedFade"), 0); + } + else + { + Material.SetScalarParameterValue(STATIC_FNAME("StartTime"), 0); + Material.SetScalarParameterValue(STATIC_FNAME("EndTime"), 1e8); + } + }); +} + +void FVoxelRenderUtilities::SetMeshTransitionsMask(UVoxelProceduralMeshComponent& Mesh, uint8 TransitionMask) +{ + VOXEL_FUNCTION_COUNTER(); + IterateDynamicMaterials(Mesh, [&](UMaterialInstanceDynamic& Material) + { + float OldValue = 0; + Material.GetScalarParameterValue(FMaterialParameterInfo(STATIC_FNAME("TransitionMask")), OldValue); + if (OldValue != TransitionMask) + { + Material.SetScalarParameterValue(STATIC_FNAME("OldTransitionMask"), OldValue); + Material.SetScalarParameterValue(STATIC_FNAME("TransitionMask"), TransitionMask); + Material.SetScalarParameterValue(STATIC_FNAME("TransitionsStartTime"), GetWorldCurrentTime(Mesh.GetWorld())); + } + }); +} + +void FVoxelRenderUtilities::HideMesh(UVoxelProceduralMeshComponent& Mesh) +{ + VOXEL_FUNCTION_COUNTER(); + Mesh.IterateSectionsSettings([&](FVoxelProcMeshSectionSettings& SectionSettings) + { + SectionSettings.bSectionVisible = false; + }); + Mesh.MarkRenderStateDirty(); +} + +void FVoxelRenderUtilities::ShowMesh(UVoxelProceduralMeshComponent& Mesh) +{ + VOXEL_FUNCTION_COUNTER(); + Mesh.IterateSectionsSettings([&](FVoxelProcMeshSectionSettings& SectionSettings) + { + SectionSettings.bSectionVisible = true; + }); + Mesh.MarkRenderStateDirty(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_CANCEL() if (CancelCounter.GetValue() > CancelThreshold) return {}; + +TUniquePtr FVoxelRenderUtilities::MergeSections_AnyThread( + const FVoxelRendererSettingsBase& RendererSettings, + const TArray& Sections, + const FIntVector& CenterPosition, + const FThreadSafeCounter& CancelCounter, + int32 CancelThreshold) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const bool bShowMainChunks = CVarShowTransitions.GetValueOnAnyThread() == 0; + + auto ProcMeshBuffersPtr = MakeUnique(); + auto& ProcMeshBuffers = *ProcMeshBuffersPtr; + + int32 NumVertices = 0; + int32 NumIndices = 0; + int32 NumAdjacencyIndices = 0; + int32 NumTextureCoordinates = -1; + for (auto& Section : Sections) + { + CHECK_CANCEL(); + + const auto BufferIterator = [&](const FVoxelChunkMeshBuffers& ChunkBuffers) + { + ProcMeshBuffers.Guids.Add(ChunkBuffers.Guid); + + // Else wrong NumTextureCoordinates gets assigned + // Only part of the buffers can be empty; having all of them empty is invalid + if (ChunkBuffers.GetNumVertices() == 0) return; + + NumVertices += ChunkBuffers.GetNumVertices(); + NumIndices += ChunkBuffers.Indices.Num(); + if (Section.bEnableTessellation) + { + // 4x as much adjacency indices + NumAdjacencyIndices += 4 * ChunkBuffers.Indices.Num(); + } + + if (NumTextureCoordinates == -1) + { + NumTextureCoordinates = ChunkBuffers.TextureCoordinates.Num(); + } + else if (!ensure(NumTextureCoordinates == ChunkBuffers.TextureCoordinates.Num())) + { + NumTextureCoordinates = -2; + } + }; + + if (Section.MainChunk.IsValid() && bShowMainChunks) + { + BufferIterator(*Section.MainChunk); + } + if (Section.TransitionChunk.IsValid()) + { + BufferIterator(*Section.TransitionChunk); + } + } + ensure(NumAdjacencyIndices == 4 * NumIndices || NumAdjacencyIndices == 0); // If false, then some chunks have tessellation enabled and some others don't + if (!ensure(NumVertices > 0)) return {}; + if (!ensure(NumTextureCoordinates >= 0)) return {}; + + auto& PositionBuffer = ProcMeshBuffers.VertexBuffers.PositionVertexBuffer; + auto& StaticMeshBuffer = ProcMeshBuffers.VertexBuffers.StaticMeshVertexBuffer; + auto& ColorBuffer = ProcMeshBuffers.VertexBuffers.ColorVertexBuffer; + auto& IndexBuffer = ProcMeshBuffers.IndexBuffer; + auto& AdjacencyIndexBuffer = ProcMeshBuffers.AdjacencyIndexBuffer; + + CHECK_CANCEL(); + PositionBuffer.Init(NumVertices, FVoxelProcMeshBuffers::bNeedsCPUAccess); + CHECK_CANCEL(); + if (RendererSettings.bRenderWorld) + { + StaticMeshBuffer.SetUseFullPrecisionUVs(!RendererSettings.bHalfPrecisionCoordinates); + StaticMeshBuffer.Init(NumVertices, NumTextureCoordinates, FVoxelProcMeshBuffers::bNeedsCPUAccess); + CHECK_CANCEL(); + ColorBuffer.Init(NumVertices, FVoxelProcMeshBuffers::bNeedsCPUAccess); + } + CHECK_CANCEL(); + IndexBuffer.AllocateData(NumIndices); + CHECK_CANCEL(); + AdjacencyIndexBuffer.AllocateData(NumAdjacencyIndices); + CHECK_CANCEL(); + + int32 VerticesOffset = 0; + int32 IndicesOffset = 0; + int32 AdjacencyIndicesOffset = 0; + + const auto Get = [](auto& Array, int32 Index) -> const auto& + { +#if VOXEL_DEBUG + return Array[Index]; +#else + return Array.GetData()[Index]; +#endif + }; + + const auto CopyPositions = [&](const FVoxelChunkMeshBuffers& Chunk, const FVector& Offset) + { + VOXEL_ASYNC_SCOPE_COUNTER("CopyPositions"); + const int32 ChunkNumVertices = Chunk.GetNumVertices(); + for (int32 Index = 0; Index < ChunkNumVertices; Index++) + { + PositionBuffer.VertexPosition(VerticesOffset + Index) = Get(Chunk.Positions, Index) + Offset; + } + }; + const auto CopyColors = [&](const FVoxelChunkMeshBuffers& Chunk) + { + if (!RendererSettings.bRenderWorld) + { + ensure(Chunk.Colors.Num() == 0); + return; + } + + VOXEL_ASYNC_SCOPE_COUNTER("CopyColors"); + const int32 ChunkNumVertices = Chunk.GetNumVertices(); + for (int32 Index = 0; Index < ChunkNumVertices; Index++) + { + ColorBuffer.VertexColor(VerticesOffset + Index) = Get(Chunk.Colors, Index); + } + }; + const auto CopyStaticMesh = [&](const FVoxelChunkMeshBuffers& Chunk) + { + if (!RendererSettings.bRenderWorld) + { + ensure(Chunk.Tangents.Num() == 0); + ensure(Chunk.Normals.Num() == 0); + for (auto& T : Chunk.TextureCoordinates) ensure(T.Num() == 0); + return; + } + + VOXEL_ASYNC_SCOPE_COUNTER("CopyStaticMesh"); + const int32 ChunkNumVertices = Chunk.GetNumVertices(); + for (int32 Index = 0; Index < ChunkNumVertices; Index++) + { + { + auto& Tangent = Get(Chunk.Tangents, Index); + auto& Normal = Get(Chunk.Normals, Index); + StaticMeshBuffer.SetVertexTangents(VerticesOffset + Index, Tangent.TangentX, Tangent.GetY(Normal), Normal); + } + check(Chunk.TextureCoordinates.Num() == NumTextureCoordinates); + for (int32 Tex = 0; Tex < NumTextureCoordinates; Tex++) + { + auto& TextureCoordinate = Get(Chunk.TextureCoordinates[Tex], Index); + StaticMeshBuffer.SetVertexUV(VerticesOffset + Index, Tex, TextureCoordinate); + } + } + }; + const auto CopyIndices = [&](const FVoxelChunkMeshBuffers& Chunk) + { + VOXEL_ASYNC_SCOPE_COUNTER("CopyIndices"); + for (int32 Index = 0; Index < Chunk.Indices.Num(); Index++) + { + IndexBuffer.SetIndex(IndicesOffset + Index, VerticesOffset + Get(Chunk.Indices, Index)); + } + }; + const auto CopyAdjacencyIndices = [&](const FVoxelChunkMeshBuffers& Chunk) + { + TArray AdjacencyIndices; + Chunk.BuildAdjacency(AdjacencyIndices); + ensure(AdjacencyIndices.Num() == 4 * Chunk.Indices.Num()); + + VOXEL_ASYNC_SCOPE_COUNTER("CopyAdjacencyIndices"); + for (int32 Index = 0; Index < AdjacencyIndices.Num(); Index++) + { + AdjacencyIndexBuffer.SetIndex(AdjacencyIndicesOffset + Index, VerticesOffset + Get(AdjacencyIndices, Index)); + } + return AdjacencyIndices.Num(); + }; + + for (const FVoxelChunkMeshSection& Chunk : Sections) + { + CHECK_CANCEL(); + + const FVector PositionOffset(Chunk.ChunkPosition - CenterPosition); + + // Copy main chunk + if (Chunk.MainChunk.IsValid() && bShowMainChunks) + { + auto& MainChunk = *Chunk.MainChunk; + + // Copy bounds + ProcMeshBuffers.LocalBounds += MainChunk.Bounds.ShiftBy(PositionOffset); + + if (Chunk.bTranslateVertices && Chunk.TransitionsMask) + { + VOXEL_ASYNC_SCOPE_COUNTER("TranslateVertices"); + for (int32 Index = 0; Index < MainChunk.GetNumVertices(); Index++) + { + PositionBuffer.VertexPosition(VerticesOffset + Index) = FVoxelMesherUtilities::GetTranslatedTransvoxel( + Get(MainChunk.Positions, Index), + Get(MainChunk.Normals, Index), + Chunk.TransitionsMask, + Chunk.LOD) + PositionOffset; + } + } + else + { + CopyPositions(MainChunk, PositionOffset); + } + CHECK_CANCEL(); + CopyColors(MainChunk); + CHECK_CANCEL(); + CopyStaticMesh(MainChunk); + CHECK_CANCEL(); + CopyIndices(MainChunk); + CHECK_CANCEL(); + if (Chunk.bEnableTessellation) + { + AdjacencyIndicesOffset += CopyAdjacencyIndices(MainChunk); + } + CHECK_CANCEL(); + + VerticesOffset += MainChunk.GetNumVertices(); + IndicesOffset += MainChunk.Indices.Num(); + } + + // Copy transition chunk + if (Chunk.TransitionChunk.IsValid()) + { + auto& TransitionChunk = *Chunk.TransitionChunk; + + // Copy bounds + ProcMeshBuffers.LocalBounds += TransitionChunk.Bounds.ShiftBy(PositionOffset); + + CHECK_CANCEL(); + CopyPositions(TransitionChunk, PositionOffset); + CHECK_CANCEL(); + CopyColors(TransitionChunk); + CHECK_CANCEL(); + CopyStaticMesh(TransitionChunk); + CHECK_CANCEL(); + CopyIndices(TransitionChunk); + CHECK_CANCEL(); + + if (Chunk.bEnableTessellation) + { + AdjacencyIndicesOffset += CopyAdjacencyIndices(TransitionChunk); + } + CHECK_CANCEL(); + + VerticesOffset += TransitionChunk.GetNumVertices(); + IndicesOffset += TransitionChunk.Indices.Num(); + } + } + + check(VerticesOffset == NumVertices); + check(IndicesOffset == NumIndices); + check(AdjacencyIndicesOffset == NumAdjacencyIndices); + + CHECK_CANCEL(); + + // Bounds extension is in world space, and we're in local (voxel) space + ProcMeshBuffers.LocalBounds = ProcMeshBuffers.LocalBounds.ExpandBy(RendererSettings.BoundsExtension / RendererSettings.VoxelSize); + +#if VOXEL_DEBUG + { + VOXEL_ASYNC_SCOPE_COUNTER("Check"); + for (int32 Index = 0; Index < IndexBuffer.GetNumIndices(); Index++) + { + checkf(IndexBuffer.GetIndex(Index) < uint32(NumVertices), TEXT("Invalid index: %u < %u"), IndexBuffer.GetIndex(Index), uint32(NumVertices)); + } + for (int32 Index = 0; Index < AdjacencyIndexBuffer.GetNumIndices(); Index++) + { + checkf(AdjacencyIndexBuffer.GetIndex(Index) < uint32(NumVertices), TEXT("Invalid index: %u < %u"), AdjacencyIndexBuffer.GetIndex(Index), uint32(NumVertices)); + } + } +#endif + + ProcMeshBuffers.UpdateStats(); + + CHECK_CANCEL(); + + return ProcMeshBuffersPtr; +} + +TUniquePtr FVoxelRenderUtilities::BuildMeshes_AnyThread( + const FVoxelChunkMeshesToBuild& ChunkMeshesToBuild, + const FVoxelRendererSettingsBase& RendererSettings, + const FIntVector& Position, + const FThreadSafeCounter& CancelCounter, + int32 CancelThreshold) +{ + auto BuiltMeshesPtr = MakeUnique(); + auto& BuiltMeshes = *BuiltMeshesPtr; + for (auto& MeshToBuild : ChunkMeshesToBuild) + { + const auto& MeshConfig = MeshToBuild.Key; + TArray>> BuiltSections; + CHECK_CANCEL(); + for (auto& Section : MeshToBuild.Value) + { + const FVoxelProcMeshSectionSettings& SectionSettings = Section.Key; + ensure(SectionSettings.bSectionVisible || SectionSettings.bEnableCollisions || SectionSettings.bEnableNavmesh); + auto BuiltSection = MergeSections_AnyThread(RendererSettings, Section.Value, Position, CancelCounter, CancelThreshold); + CHECK_CANCEL(); + BuiltSections.Emplace(SectionSettings, MoveTemp(BuiltSection)); + } + BuiltMeshes.Emplace(MeshConfig, MoveTemp(BuiltSections)); + CHECK_CANCEL(); + } + return BuiltMeshesPtr; +} + +#undef CHECK_CANCEL + +FVoxelChunkMeshesToBuild FVoxelRenderUtilities::GetMeshesToBuild( + int32 LOD, + const FIntVector& Position, + const FVoxelRendererSettingsBase& RendererSettings, + const FVoxelChunkSettings& ChunkSettings, + FVoxelChunkMaterials& ChunkMaterials, + const FVoxelChunkMesh& MainChunk, + const FVoxelChunkMesh* TransitionChunk, + const FVoxelOnMaterialInstanceCreated& OnMaterialInstanceCreated, + const FDitheringInfo& DitheringInfo) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelChunkMeshesToBuild Meshes; + + const auto DefaultSection = + FVoxelChunkMeshSection( + LOD, + Position, + false, // Set below + RendererSettings.RenderType == EVoxelRenderType::MarchingCubes && + // Don't translate if the transition chunk isn't built + TransitionChunk && + // No valid normals for these, so can't translate + RendererSettings.NormalConfig != EVoxelNormalConfig::FlatNormal && + RendererSettings.NormalConfig != EVoxelNormalConfig::NoNormal, + ChunkSettings.TransitionsMask); + const auto DefaultMeshConfig = FVoxelMeshConfig().CopyFrom(*RendererSettings.ProcMeshClass->GetDefaultObject()); + + const auto CreateMaterialInstance = [&](UMaterialInterface* Interface) -> TVoxelSharedRef + { + if (!RendererSettings.bCreateMaterialInstances) + { + return FVoxelMaterialInterfaceManager::Get().CreateMaterial(Interface); + } + + auto* ParentInstance = Cast(Interface); + const auto MaterialInstance = FVoxelMaterialInterfaceManager::Get().CreateMaterialInstance(ParentInstance ? ParentInstance->Parent : Interface); + auto* MaterialInstanceObject = Cast(MaterialInstance->GetMaterial()); + if (ensure(MaterialInstanceObject)) + { + if (ParentInstance) + { + MaterialInstanceObject->CopyParameterOverrides(ParentInstance); + } + InitializeMaterialInstance( + MaterialInstanceObject, + LOD, + Position, + RendererSettings); + OnMaterialInstanceCreated.Broadcast(LOD, FVoxelUtilities::GetBoundsFromPositionAndDepth(Position, LOD), MaterialInstanceObject); + if (DitheringInfo.bIsValid) + { + SetMaterialDithering(*MaterialInstanceObject, RendererSettings, DitheringInfo); + } + } + + return MaterialInstance; + }; + + if (MainChunk.IsSingle()) + { + const auto CreateMaterial = [&]() + { + return CreateMaterialInstance(RendererSettings.GetVoxelMaterial(LOD)); + }; + const auto MaterialInstance = ChunkMaterials.FindOrAddSingle(CreateMaterial); + + auto& SectionMap = Meshes.FindOrAdd(DefaultMeshConfig); + + const bool bEnableTessellation = FVoxelUtilities::IsMaterialTessellated(MaterialInstance->GetMaterial()); + + const FVoxelProcMeshSectionSettings SectionSettings( + MaterialInstance, + ChunkSettings.bEnableCollisions, + ChunkSettings.bEnableNavmesh, + bEnableTessellation, + ChunkSettings.bVisible); + auto& Sections = SectionMap.FindOrAdd(SectionSettings); + + auto& NewSection = Sections.Emplace_GetRef(DefaultSection); + + NewSection.bEnableTessellation = bEnableTessellation; + NewSection.MainChunk = MainChunk.GetSingleBuffers(); + if (TransitionChunk) + { + NewSection.TransitionChunk = TransitionChunk->GetSingleBuffers(); + } + } + else + { + TSet MaterialsSet; + { + MainChunk.IterateMaterials([&](auto& Material) { MaterialsSet.Add(Material); }); + if (TransitionChunk) + { + TransitionChunk->IterateMaterials([&](auto& Material) { MaterialsSet.Add(Material); }); + } + + if (MaterialsSet.Num() > CVarMaxSectionsPerChunk.GetValueOnGameThread()) + { + FVoxelMessages::Error( + "Voxel chunk with more than voxel.renderer.MaxSectionsPerChunk mesh sections.\n" + "Not rendering it to avoid performance drop (1 draw call per section).\n" + "This is because you are changing your single index or double index too frequently\n" + "You most likely painted RGB data with a Single or Double Index material config, or painted too many double index materials on a single chunk\n" + "Decreasing your material collection Max Materials To Blend At Once might help\n" + "You can use voxel.renderer.ShowMeshSections 1 to debug"); + return {}; + } + } + + const auto ShouldSkip = [&](const FVoxelMaterialIndices& Indices) + { + for (uint8 HoleMaterial : RendererSettings.HolesMaterials) + { + for (int32 Index = 0; Index < Indices.NumIndices; Index++) + { + if (Indices.SortedIndices[Index] == HoleMaterial) + { + return true; + } + } + } + return false; + }; + + for (auto& Material : MaterialsSet) + { + if (ShouldSkip(Material)) + { + continue; + } + + const auto CreateMaterial = [&]() + { + return CreateMaterialInstance(RendererSettings.GetVoxelMaterial(LOD, Material)); + }; + const auto MaterialInstance = ChunkMaterials.FindOrAddMultiple(Material, CreateMaterial); + + // Note: we only use the first index to determine the mesh settings to use + // This might lead to unwanted behavior in blendings + auto* MaterialMeshConfig = RendererSettings.MaterialsMeshConfigs.Find(Material.SortedIndices[0]); + auto& MeshConfig = MaterialMeshConfig ? *MaterialMeshConfig : DefaultMeshConfig; + auto& SectionMap = Meshes.FindOrAdd(MeshConfig); + + const bool bEnableTessellation = FVoxelUtilities::IsMaterialTessellated(MaterialInstance->GetMaterial()); + + const FVoxelProcMeshSectionSettings SectionSettings( + MaterialInstance, + ChunkSettings.bEnableCollisions, + ChunkSettings.bEnableNavmesh, + bEnableTessellation, + ChunkSettings.bVisible); + auto& Sections = SectionMap.FindOrAdd(SectionSettings); + + auto& NewSection = Sections[Sections.Emplace(DefaultSection)]; + NewSection.bEnableTessellation = bEnableTessellation; + + const auto MainBuffers = MainChunk.FindBuffer(Material); + if (MainBuffers.IsValid()) + { + NewSection.MainChunk = MainBuffers; + } + if (TransitionChunk) + { + const auto TransitionBuffers = TransitionChunk->FindBuffer(Material); + if (TransitionBuffers.IsValid()) + { + NewSection.TransitionChunk = TransitionBuffers; + } + } + ensure(NewSection.MainChunk.IsValid() || NewSection.TransitionChunk.IsValid()); + } + } + + return Meshes; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.h new file mode 100644 index 00000000..d616f9ed --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.h @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelRender/VoxelMeshConfig.h" +#include "VoxelRender/VoxelProcMeshSectionSettings.h" + +struct FVoxelIntBox; +struct FVoxelChunkMesh; +struct FVoxelChunkMeshBuffers; +struct FVoxelChunkMaterials; +struct FVoxelChunkSettings; +struct FVoxelProcMeshBuffers; +struct FVoxelRendererSettingsBase; +class UMaterialInstanceDynamic; +class UVoxelProceduralMeshComponent; + +DECLARE_MULTICAST_DELEGATE_ThreeParams(FVoxelOnMaterialInstanceCreated, int32 /*ChunkLOD*/, const FVoxelIntBox& /*ChunkBounds*/, UMaterialInstanceDynamic* /*Instance*/); + +struct FVoxelChunkMeshSection +{ + int32 LOD = -1; + FIntVector ChunkPosition = FIntVector(ForceInit); + bool bEnableTessellation = false; + bool bTranslateVertices = false; + uint8 TransitionsMask = 0; + + TVoxelSharedPtr MainChunk; + TVoxelSharedPtr TransitionChunk; + + FVoxelChunkMeshSection() = default; + FVoxelChunkMeshSection( + int32 LOD, + const FIntVector& ChunkPosition, + bool bEnableTessellation, + bool bTranslateVertices, + uint8 TransitionsMask) + : LOD(LOD) + , ChunkPosition(ChunkPosition) + , bEnableTessellation(bEnableTessellation) + , bTranslateVertices(bTranslateVertices) + , TransitionsMask(TransitionsMask) + { + } +}; + +// Map from mesh config -> section config -> array of meshes to merge into that section +using FVoxelChunkMeshesToBuild = TMap>>; +// Map from mesh config -> section config -> built section +using FVoxelBuiltChunkMeshes = TArray>>>>; + +enum class EDitheringType : uint8 +{ + // if we need to fade from the parent to the child + SurfaceNets_LowResToHighRes, + // if we need to fade from the child to the parent + SurfaceNets_HighResToLowRes, + Classic_DitherIn, + Classic_DitherOut +}; + +namespace FVoxelRenderUtilities +{ + struct FDitheringInfo + { + bool bIsValid = false; + EDitheringType DitheringType = EDitheringType::Classic_DitherIn; + float Time = 0; + }; + + float GetWorldCurrentTime(UWorld* World); + + void InitializeMaterialInstance( + UMaterialInstanceDynamic* MaterialInstance, + int32 LOD, + const FIntVector& Position, + const FVoxelRendererSettingsBase& Settings); + + void StartMeshDithering(UVoxelProceduralMeshComponent& Mesh, const FVoxelRendererSettingsBase& Settings, const FDitheringInfo& DitheringInfo); + void ResetDithering(UVoxelProceduralMeshComponent& Mesh, const FVoxelRendererSettingsBase& Settings); + + // For surface nets + void SetMeshTransitionsMask(UVoxelProceduralMeshComponent& Mesh, uint8 TransitionMask); + + void HideMesh(UVoxelProceduralMeshComponent& Mesh); + void ShowMesh(UVoxelProceduralMeshComponent& Mesh); + + TUniquePtr MergeSections_AnyThread( + const FVoxelRendererSettingsBase& RendererSettings, + const TArray& Sections, + const FIntVector& CenterPosition, + const FThreadSafeCounter& CancelCounter = FThreadSafeCounter(), + int32 CancelThreshold = 0); + TUniquePtr BuildMeshes_AnyThread( + const FVoxelChunkMeshesToBuild& ChunkMeshesToBuild, + const FVoxelRendererSettingsBase& RendererSettings, + const FIntVector& Position, + const FThreadSafeCounter& CancelCounter = FThreadSafeCounter(), + int32 CancelThreshold = 0); + + FVoxelChunkMeshesToBuild GetMeshesToBuild( + int32 LOD, + const FIntVector& Position, + const FVoxelRendererSettingsBase& RendererSettings, + const FVoxelChunkSettings& ChunkSettings, + FVoxelChunkMaterials& ChunkMaterials, + const FVoxelChunkMesh& MainChunk, + const FVoxelChunkMesh* TransitionChunk, + const FVoxelOnMaterialInstanceCreated& OnMaterialInstanceCreated, + const FDitheringInfo& DitheringInfo); // DitheringInfo to apply to newly spawned materials +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSerializationUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSerializationUtilities.cpp new file mode 100644 index 00000000..e6bf734a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSerializationUtilities.cpp @@ -0,0 +1,520 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelMaterial.h" +#include "VoxelMinimal.h" +#include "VoxelSettings.h" + +#include "Serialization/LargeMemoryWriter.h" + +THIRD_PARTY_INCLUDES_START +#include "ThirdParty/zlib/zlib-1.2.5/Inc/zlib.h" +THIRD_PARTY_INCLUDES_END + +template +FORCEINLINE FArchive& operator<<(FArchive& Ar, TVoxelValueImpl& Value) +{ + Ar << Value.GetStorage(); + return Ar; +} + +void FVoxelSerializationUtilities::SerializeValues(FArchive& Archive, TNoGrowArray& Values, uint32 ValueConfigFlag, FVoxelSerializationVersion::Type VoxelCustomVersion) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (Archive.IsLoading()) + { + if (VoxelCustomVersion == FVoxelSerializationVersion::BeforeCustomVersionWasAdded) + { + TArray CompatValues; + Archive << CompatValues; + Values = FVoxelValueConverter::ConvertValues(MoveTemp(CompatValues)); + } + else if (VoxelCustomVersion < FVoxelSerializationVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass) + { + TArray CompatValues; + CompatValues.BulkSerialize(Archive); + for (auto& Value : CompatValues) + { + Value.GetStorage() = FVoxelValue16::ClampToStorage(2 * Value.GetStorage()); + } + Values = FVoxelValueConverter::ConvertValues(MoveTemp(CompatValues)); + } + else if (VoxelCustomVersion < FVoxelSerializationVersion::ValueConfigFlagAndSaveGUIDs) + { + TArray CompatValues; + CompatValues.BulkSerialize(Archive); + Values = FVoxelValueConverter::ConvertValues(MoveTemp(CompatValues)); + } + else + { + int32 ValuesSize; + Archive << ValuesSize; + + check(ValueConfigFlag); + if (ValueConfigFlag & EVoxelValueConfigFlag::EightBitsValue) + { + check(!(ValueConfigFlag & EVoxelValueConfigFlag::SixteenBitsValue)); + TArray CompatValues; + CompatValues.Empty(ValuesSize); + CompatValues.SetNumUninitialized(ValuesSize); + Archive.Serialize(CompatValues.GetData(), ValuesSize * sizeof(FVoxelValue8)); + Values = FVoxelValueConverter::ConvertValues(MoveTemp(CompatValues)); + } + else + { + check(ValueConfigFlag & EVoxelValueConfigFlag::SixteenBitsValue); + TArray CompatValues; + CompatValues.Empty(ValuesSize); + CompatValues.SetNumUninitialized(ValuesSize); + Archive.Serialize(CompatValues.GetData(), ValuesSize * sizeof(FVoxelValue16)); + Values = FVoxelValueConverter::ConvertValues(MoveTemp(CompatValues)); + } + } + } + else if (Archive.IsSaving()) + { + int32 ValuesSize = Values.Num(); + Archive << ValuesSize; + Archive.Serialize(Values.GetData(), ValuesSize * sizeof(FVoxelValue)); + } +} + +void FVoxelSerializationUtilities::SerializeMaterials(FArchive& Archive, TNoGrowArray& Materials, uint32 MaterialConfigFlag, FVoxelSerializationVersion::Type VoxelCustomVersion) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + static_assert(sizeof(FVoxelMaterial) == FVoxelMaterial::NumChannels, "Serialization below will be broken"); + + if (Archive.IsLoading()) + { + enum ELegacyVoxelMaterialConfigFlag : uint32 + { + LegacyEnableVoxelColors = 0x01, + LegacyEnableVoxelSpawnedActors = 0x02, + LegacyEnableVoxelGrass = 0x04, + LegacyDisableIndex = 0x10 + }; + + const auto LegacySerializeCompat = [](FArchive& Ar, uint32 ConfigFlags) + { + check(Ar.IsLoading()); + + uint8 Index = 0; + uint8 R = 0; + uint8 G = 0; + uint8 B = 0; + uint8 VoxelActor = 0; + uint8 VoxelGrass = 0; + + if (!(ConfigFlags & LegacyDisableIndex)) + { + Ar << Index; + } + if (ConfigFlags & LegacyEnableVoxelColors) + { + Ar << R; + Ar << G; + Ar << B; + } + if (ConfigFlags & LegacyEnableVoxelSpawnedActors) + { + Ar << VoxelActor; + } + if (ConfigFlags & LegacyEnableVoxelGrass) + { + Ar << VoxelGrass; + } + + FVoxelMaterial Material(ForceInit); + Material.SetA(Index); + Material.SetR(R); + Material.SetG(G); + Material.SetB(B); + + return Material; + }; + + if (VoxelCustomVersion == FVoxelSerializationVersion::BeforeCustomVersionWasAdded) + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + for (int32 I = 0; I < MaterialsSize; I++) + { + Materials[I] = LegacySerializeCompat(Archive, MaterialConfigFlag); + } + } + else if (VoxelCustomVersion < FVoxelSerializationVersion::RemoveEnableVoxelSpawnedActorsEnableVoxelGrass) + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + for (int32 I = 0; I < MaterialsSize; I++) + { + Materials[I] = LegacySerializeCompat(Archive, MaterialConfigFlag); + } + } + else + { + if (MaterialConfigFlag == GVoxelMaterialConfigFlag) + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + Archive.Serialize(Materials.GetData(), MaterialsSize * sizeof(FVoxelMaterial)); + } + else + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + for (int32 I = 0; I < MaterialsSize; I++) + { + Materials[I] = FVoxelMaterial::SerializeWithCustomConfig(Archive, MaterialConfigFlag); + } + } + } + } + else if (Archive.IsSaving()) + { + int32 MaterialsSize = Materials.Num(); + Archive << MaterialsSize; + Archive.Serialize(Materials.GetData(), MaterialsSize * sizeof(FVoxelMaterial)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +namespace FVoxelSerializationUtilities +{ + constexpr int64 MaxChunkSize = MAX_int32; // Could be uint32, but let's not take any risk of overflow + constexpr int64 MaxNumChunks = 16; // That's 32GB + + struct FHeader + { + // Need to store a special flag to tell DecompressData this is a 64 bit archive following the new format + const int32 LegacyFlag = -1; + // Sanity check + const uint32 Magic = 0xDEADBEEF; + + // Sanity check + int64 CompressedSize = 0; + // To pre-allocate buffer + int64 UncompressedSize = 0; + + uint32 Flags = 0; + uint32 NumChunks = 0; + + TVoxelStaticArray ChunksCompressedSize{ ForceInit }; + }; + static_assert(sizeof(FHeader) == 4 + 4 + 8 + 8 + 4 + 4 + MaxNumChunks * 4, ""); +} + +void FVoxelSerializationUtilities::CompressData( + const uint8* const UncompressedData, + const int64 UncompressedDataNum, + TArray& OutCompressedData, + EVoxelCompressionLevel::Type InCompressionLevel) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const double TotalStartTime = FPlatformTime::Seconds(); + + if (UncompressedDataNum == 0 || !ensure(UncompressedData)) + { + OutCompressedData.Empty(); + return; + } + + const auto GetCompressionLevel = [&]() + { + int32 CompressionLevel = InCompressionLevel; + if (CompressionLevel == EVoxelCompressionLevel::VoxelDefault) + { + CompressionLevel = GetDefault()->DefaultCompressionLevel; + } + CompressionLevel = FMath::Clamp(CompressionLevel, -1, 9); + static_assert(Z_NO_COMPRESSION == 0, ""); + static_assert(Z_BEST_COMPRESSION == 9, ""); + return CompressionLevel; + }; + const int32 CompressionLevel = GetCompressionLevel(); + + const int32 NumChunks = FVoxelUtilities::DivideCeil64(UncompressedDataNum, MaxChunkSize); + check(0 < NumChunks && NumChunks < MaxNumChunks); + + struct FChunk + { + int64 Start = 0; + int64 Size = 0; + uLong CompressedSize = 0; + }; + TArray> Chunks; + + // Fill chunks + for (int32 ChunkIndex = 0; ChunkIndex < NumChunks; ChunkIndex++) + { + auto& NewChunk = Chunks.Emplace_GetRef(); + NewChunk.Start = ChunkIndex * MaxChunkSize; + NewChunk.Size = FMath::Min(MaxChunkSize, UncompressedDataNum - ChunkIndex * MaxChunkSize); + } + check(Chunks.Last().Start + Chunks.Last().Size == UncompressedDataNum); + + // Compute estimated compressed size + int64 TotalCompressedSizeBound = 0; + for (auto& Chunk : Chunks) + { + Chunk.CompressedSize = compressBound(Chunk.Size); + TotalCompressedSizeBound += Chunk.CompressedSize; + } + + // Allocate memory + TArray64 CompressedData; + CompressedData.SetNumUninitialized(TotalCompressedSizeBound); + + FHeader Header; + + int64 TotalCompressedSize = 0; + double CompressionTime = 0; + + // Compress chunks + for (int32 ChunkIndex = 0; ChunkIndex < NumChunks; ChunkIndex++) + { + FChunk& Chunk = Chunks[ChunkIndex]; + + const double StartTime = FPlatformTime::Seconds(); + const auto Result = compress2(CompressedData.GetData() + TotalCompressedSize, &Chunk.CompressedSize, UncompressedData + Chunk.Start, Chunk.Size, CompressionLevel); + const double EndTime = FPlatformTime::Seconds(); + + if (!ensureMsgf(Result == Z_OK, TEXT("Compression failed: %d"), Result)) + { + CompressedData.Reset(); + return; + } + + CompressionTime += EndTime - StartTime; + TotalCompressedSize += Chunk.CompressedSize; + + Header.ChunksCompressedSize[ChunkIndex] = Chunk.CompressedSize; + } + check(TotalCompressedSize <= TotalCompressedSizeBound); + checkf(TotalCompressedSize < MAX_int32 - sizeof(FHeader), TEXT("Compressed data overflow: %lld"), TotalCompressedSize); + + // Fill header + Header.CompressedSize = TotalCompressedSize; + Header.UncompressedSize = UncompressedDataNum; + Header.NumChunks = NumChunks; + + // Write final data + OutCompressedData.SetNumUninitialized(sizeof(FHeader) + TotalCompressedSize); + FMemory::Memcpy(OutCompressedData.GetData(), &Header, sizeof(FHeader)); + FMemory::Memcpy(OutCompressedData.GetData() + sizeof(FHeader), CompressedData.GetData(), TotalCompressedSize); + + // Log time + + const double TotalEndTime = FPlatformTime::Seconds(); + + const double UncompressedSizeMB = double(UncompressedDataNum) / double(1 << 20); + const double CompressedSizeMB = double(TotalCompressedSize) / double(1 << 20); + + const double TotalTime = TotalEndTime - TotalStartTime; + + LOG_VOXEL(Log, TEXT("Compressed %f MB in %fs (%f MB/s). Compressed Size: %f MB (%f%%). Compression: %fs (%f%%). Num Chunks: %d."), + UncompressedSizeMB, + TotalTime, + UncompressedSizeMB / TotalTime, + CompressedSizeMB, + 100 * CompressedSizeMB / UncompressedSizeMB, + CompressionTime, + 100 * CompressionTime / TotalTime, + NumChunks); +} + +void FVoxelSerializationUtilities::CompressData(FLargeMemoryWriter& UncompressedData, TArray& CompressedData, EVoxelCompressionLevel::Type CompressionLevel) +{ + // Tell and not TotalSize: TotalSize returns the total memory allocated by the writer, which might be bigger if AllocatedMemory is too big + CompressData(UncompressedData.GetData(), UncompressedData.Tell(), CompressedData, CompressionLevel); +} + +bool FVoxelSerializationUtilities::DecompressData(const TArray& CompressedData, TArray64& UncompressedData) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const double TotalStartTime = FPlatformTime::Seconds(); + + if (CompressedData.Num() == 0) + { + UncompressedData.Empty(); + return false; + } + + int32 Flag; + FMemory::Memcpy(&Flag, CompressedData.GetData(), sizeof(Flag)); + + if (Flag == -1) + { + // New 64 bit archive + + if (!ensure(CompressedData.Num() >= sizeof(FHeader))) + { + UncompressedData.Empty(); + return false; + } + + FHeader Header; + FMemory::Memcpy(&Header, CompressedData.GetData(), sizeof(FHeader)); + + check(Header.LegacyFlag == -1); + if (!ensureMsgf(Header.Magic == FHeader().Magic, TEXT("Magic was %x"), Header.Magic)) + { + UncompressedData.Empty(); + return false; + } + + if (!ensureMsgf(Header.CompressedSize == CompressedData.Num() - sizeof(FHeader), TEXT("Archive is saying its size is %lld, but it's %lld"), Header.CompressedSize, CompressedData.Num() - sizeof(FHeader))) + { + UncompressedData.Empty(); + return false; + } + + if (!ensureMsgf(Header.NumChunks <= MaxNumChunks, TEXT("Header.NumChunks was %u"), Header.NumChunks)) + { + UncompressedData.Empty(); + return false; + } + + // Allocate memory + UncompressedData.SetNumUninitialized(Header.UncompressedSize); + + int64 TotalCompressedSize = 0; + int64 TotalUncompressedSize = 0; + + double DecompressionTime = 0; + + // Decompress all chunks + for (uint32 ChunkIndex = 0; ChunkIndex < Header.NumChunks; ChunkIndex++) + { + const uint32 ChunkCompressedSize = Header.ChunksCompressedSize[ChunkIndex]; + if (!ensureMsgf(TotalCompressedSize + ChunkCompressedSize <= Header.CompressedSize, TEXT("Decompression overflow: Compressed size = %lld, Already processed = %lld, Chunk = %u"), + Header.CompressedSize, TotalCompressedSize, ChunkCompressedSize)) + { + UncompressedData.Empty(); + return false; + } + + uLong UncompressedSize = FMath::Min(MaxChunkSize, Header.UncompressedSize - TotalUncompressedSize); + + const double StartTime = FPlatformTime::Seconds(); + const auto Result = uncompress( + UncompressedData.GetData() + TotalUncompressedSize, &UncompressedSize, + CompressedData.GetData() + sizeof(FHeader) + TotalCompressedSize, ChunkCompressedSize); + const double EndTime = FPlatformTime::Seconds(); + + if (!ensureMsgf(Result == Z_OK, TEXT("Decompression failed: %d"), Result)) + { + UncompressedData.Empty(); + return false; + } + + TotalCompressedSize += ChunkCompressedSize; + TotalUncompressedSize += UncompressedSize; + + DecompressionTime += EndTime - StartTime; + } + + if (!ensureMsgf(TotalCompressedSize == Header.CompressedSize, TEXT("Compressed size mismatch: read %lld, but %lld in header"), TotalCompressedSize, Header.CompressedSize)) + { + UncompressedData.Empty(); + return false; + } + if (!ensureMsgf(TotalUncompressedSize == Header.UncompressedSize, TEXT("Uncompressed size mismatch: read %lld, but %lld in header"), TotalUncompressedSize, Header.UncompressedSize)) + { + UncompressedData.Empty(); + return false; + } + + // Log + + const double TotalEndTime = FPlatformTime::Seconds(); + + const double UncompressedSizeMB = double(TotalUncompressedSize) / double(1 << 20); + const double CompressedSizeMB = double(TotalCompressedSize) / double(1 << 20); + + const double TotalTime = TotalEndTime - TotalStartTime; + + LOG_VOXEL(Log, TEXT("Decompressed %f MB in %fs (%f MB/s). Compressed Size: %f MB (%f%%). Decompression: %fs (%f%%). Num Chunks: %d."), + UncompressedSizeMB, + TotalTime, + UncompressedSizeMB / TotalTime, + CompressedSizeMB, + 100 * CompressedSizeMB / UncompressedSizeMB, + DecompressionTime, + 100 * DecompressionTime / TotalTime, + Header.NumChunks); + + return true; + } + else + { + const ECompressionFlags CompressionFlags = ECompressionFlags(CompressedData.Last()); + + int32 UncompressedSize; + FMemory::Memcpy(&UncompressedSize, CompressedData.GetData(), sizeof(UncompressedSize)); + UncompressedData.SetNum(UncompressedSize); + const uint8* CompressionStart = CompressedData.GetData() + sizeof(UncompressedSize); + const int32 CompressionSize = CompressedData.Num() - 1 - sizeof(UncompressedSize); + + bool bSuccess = false; + ECompressionFlags NewCompressionFlags = (ECompressionFlags)(CompressionFlags & COMPRESS_OptionsFlagsMask); + switch (CompressionFlags & COMPRESS_DeprecatedFormatFlagsMask) + { + case COMPRESS_ZLIB: + bSuccess = FCompression::UncompressMemory(NAME_Zlib, UncompressedData.GetData(), UncompressedSize, CompressionStart, CompressionSize, NewCompressionFlags); + break; + case COMPRESS_GZIP: + bSuccess = FCompression::UncompressMemory(NAME_Gzip, UncompressedData.GetData(), UncompressedSize, CompressionStart, CompressionSize, NewCompressionFlags); + break; + case COMPRESS_Custom: + bSuccess = FCompression::UncompressMemory(TEXT("Oodle"), UncompressedData.GetData(), UncompressedSize, CompressionStart, CompressionSize, NewCompressionFlags); + break; + default: + ensure(false); + } + + return bSuccess; + } +} + +void FVoxelSerializationUtilities::TestCompression(int64 Size, EVoxelCompressionLevel::Type CompressionLevel) +{ + LOG_VOXEL(Log, TEXT("Testing compression on %fMB"), double(Size) / double(1 << 20)); + + TArray64 Data; + Data.SetNumUninitialized(Size); + + const FRandomStream Random(0); + for (int64 Index = 0; Index < Data.Num(); Index++) + { + Data[Index] = Random.GetUnsignedInt(); + } + + TArray CompressedData; + CompressData(Data.GetData(), Data.Num(), CompressedData, CompressionLevel); + + TArray64 UncompressedData; + DecompressData(CompressedData, UncompressedData); + + check(Data.Num() == UncompressedData.Num()); + + for (int64 Index = 0; Index < Data.Num(); Index++) + { + check(Data[Index] == UncompressedData[Index]); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSettings.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSettings.cpp new file mode 100644 index 00000000..480ac38e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSettings.cpp @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSettings.h" + +UVoxelSettings::UVoxelSettings() +{ + CategoryName = "Plugins"; + SectionName = "Voxel Plugin"; +} + +FName UVoxelSettings::GetContainerName() const +{ + return "Project"; +} + +void UVoxelSettings::PostInitProperties() +{ + Super::PostInitProperties(); + +#if WITH_EDITOR + if (IsTemplate()) + { + ImportConsoleVariableValues(); + } +#endif +} + +#if WITH_EDITOR +void UVoxelSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property) + { + ExportValuesToConsoleVariables(PropertyChangedEvent.Property); + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp new file mode 100644 index 00000000..47d24873 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp @@ -0,0 +1,183 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelShaders/VoxelDistanceFieldShader.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" + +#include "ShaderParameterUtils.h" + +IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FVoxelDistanceFieldParameters, "VoxelDistanceFieldParameters"); + +FVoxelDistanceFieldBaseCS::FVoxelDistanceFieldBaseCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) +{ + Src.Bind(Initializer.ParameterMap, TEXT("Src")); + Dst.Bind(Initializer.ParameterMap, TEXT("Dst")); +} + +void FVoxelDistanceFieldBaseCS::ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) +{ + FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); + OutEnvironment.SetDefine(TEXT("NUM_THREADS_CS"), VOXEL_DISTANCE_FIELD_NUM_THREADS_CS); +} + +#if ENGINE_MINOR_VERSION < 25 +bool FVoxelDistanceFieldBaseCS::Serialize(FArchive& Ar) +{ + const bool bShaderHasOutdatedParams = FGlobalShader::Serialize(Ar); + Ar << Src; + Ar << Dst; + return bShaderHasOutdatedParams; +} +#endif + +void FVoxelDistanceFieldBaseCS::SetBuffers( + FRHICommandList& RHICmdList, + const FRWBuffer& SrcBuffer, + const FRWBuffer& DstBuffer) const +{ + Src.SetBuffer(RHICmdList, UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), SrcBuffer); + Dst.SetBuffer(RHICmdList, UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), DstBuffer); +} + +void FVoxelDistanceFieldBaseCS::SetUniformBuffers(FRHICommandList& RHICmdList, const FVoxelDistanceFieldParameters& Parameters) const +{ + const FVoxelDistanceFieldParametersRef ParametersBuffer = FVoxelDistanceFieldParametersRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_MultiFrame); + SetUniformBufferParameter(RHICmdList, UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), GetUniformBufferParameter(), ParametersBuffer); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_TYPE_LAYOUT(FVoxelDistanceFieldBaseCS) +IMPLEMENT_SHADER_TYPE(, FVoxelJumpFloodCS, TEXT("/Plugin/Voxel/Private/DistanceField.usf"), TEXT("ExpandDistanceField"), SF_Compute); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDistanceFieldShaderHelper::WaitForCompletion() const +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + Fence.Wait(); +} + +void FVoxelDistanceFieldShaderHelper::StartCompute(const FIntVector& Size, const TVoxelSharedRef>& InOutData, int32 MaxPasses_Debug) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + check(InOutData->Num() == Size.X * Size.Y * Size.Z); + check(Size.X > 0 && Size.Y > 0 && Size.Z > 0); + + ensure(Fence.IsFenceComplete()); + + ENQUEUE_RENDER_COMMAND(VoxelDistanceFieldCompute)( + MakeVoxelWeakPtrLambda(this, [=](FRHICommandListImmediate& RHICmdList) + { + Compute_RenderThread(RHICmdList, Size, GetData(*InOutData), GetNum(*InOutData), MaxPasses_Debug); + })); + + Fence.BeginFence(); +} + +void FVoxelDistanceFieldShaderHelper::Compute_RenderThread( + FRHICommandListImmediate& RHICmdList, + const FIntVector& Size, + FVector* RESTRICT const Data, + const int32 Num, + int32 MaxPasses_Debug) +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + check(Size.X > 0 && Size.Y > 0 && Size.Z > 0); + check(Num == Size.X * Size.Y * Size.Z); + + if (AllocatedSize != Size) + { + VOXEL_RENDER_SCOPE_COUNTER("Create Buffers"); + + AllocatedSize = Size; + + SrcBuffer.Initialize(sizeof(float), 3 * Num, PF_R32_FLOAT); + DstBuffer.Initialize(sizeof(float), 3 * Num, PF_R32_FLOAT); + } + + { + VOXEL_RENDER_SCOPE_COUNTER("Copy Data To Buffers"); + void* BufferData = RHICmdList.LockVertexBuffer(SrcBuffer.Buffer, 0, SrcBuffer.NumBytes, EResourceLockMode::RLM_WriteOnly); + FMemory::Memcpy(BufferData, Data, SrcBuffer.NumBytes); + RHICmdList.UnlockVertexBuffer(SrcBuffer.Buffer); + } + + const int32 PowerOfTwo = FMath::CeilLogTwo(Size.GetMax()); + for (int32 Pass = 0; Pass < PowerOfTwo; Pass++) + { + if (MaxPasses_Debug == Pass) + { + break; + } + + // -1: we want to start with half the size + const int32 Step = 1 << (PowerOfTwo - 1 - Pass); + ApplyComputeShader(RHICmdList, Size, Step); + } + + // To copy data +#if ENGINE_MINOR_VERSION < 26 + RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, DstBuffer.UAV); +#else + RHICmdList.Transition(FRHITransitionInfo(DstBuffer.UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute)); // TODO not unknown? +#endif + + { + VOXEL_RENDER_SCOPE_COUNTER("Copy Data From Buffers"); + void* BufferData = RHICmdList.LockVertexBuffer(SrcBuffer.Buffer, 0, SrcBuffer.NumBytes, EResourceLockMode::RLM_ReadOnly); + FMemory::Memcpy(Data, BufferData, SrcBuffer.NumBytes); + RHICmdList.UnlockVertexBuffer(SrcBuffer.Buffer); + } + + // Make sure to release the buffers, else will crash on DX12! + SrcBuffer.Release(); + DstBuffer.Release(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelDistanceFieldShaderHelper::ApplyComputeShader( + FRHICommandListImmediate& RHICmdList, + const FIntVector& Size, + int32 Step) +{ + check(IsInRenderingThread()); + + const TShaderMapRef ComputeShader(GetGlobalShaderMap(ERHIFeatureLevel::SM5)); + RHICmdList.SetComputeShader(UE_25_SWITCH(ComputeShader->GetComputeShader(), ComputeShader.GetComputeShader())); + + FVoxelDistanceFieldParameters Parameters; + Parameters.SizeX = Size.X; + Parameters.SizeY = Size.Y; + Parameters.SizeZ = Size.Z; + Parameters.Step = Step; + ComputeShader->SetUniformBuffers(RHICmdList, Parameters); + + const FIntVector NumThreads = FVoxelUtilities::DivideCeil(Size, VOXEL_DISTANCE_FIELD_NUM_THREADS_CS); + check(NumThreads.X > 0 && NumThreads.Y > 0 && NumThreads.Z > 0); + +#if ENGINE_MINOR_VERSION < 26 + RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, SrcBuffer.UAV); + RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, DstBuffer.UAV); +#else + RHICmdList.Transition(FRHITransitionInfo(SrcBuffer.UAV, ERHIAccess::UAVCompute, ERHIAccess::UAVCompute)); + RHICmdList.Transition(FRHITransitionInfo(DstBuffer.UAV, ERHIAccess::UAVCompute, ERHIAccess::UAVCompute)); +#endif + + ComputeShader->SetBuffers(RHICmdList, SrcBuffer, DstBuffer); + RHICmdList.DispatchComputeShader(NumThreads.X, NumThreads.Y, NumThreads.Z); + Swap(SrcBuffer, DstBuffer); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp new file mode 100644 index 00000000..ec28d41b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp @@ -0,0 +1,269 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelShaders/VoxelErosion.h" +#include "VoxelShaders/VoxelErosionShader.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelMessages.h" + +#include "Engine/Texture2D.h" +#include "Logging/MessageLog.h" +#include "Logging/TokenizedMessage.h" + +void UVoxelErosion::Initialize() +{ + if (bIsInit) + { + FVoxelMessages::Error("Erosion is already initialized!"); + return; + } + + RealSize = FMath::Max(32, FMath::CeilToInt(Size / 32.f) * 32);; + + ENQUEUE_RENDER_COMMAND(Step)( + [ThisPtr = this](FRHICommandList& RHICmdList) + { + ThisPtr->Init_RenderThread(); + }); + + FlushRenderingCommands(); + + if (RainMapInit.Texture.GetSizeX() == RealSize && + RainMapInit.Texture.GetSizeY() == RealSize) + { + CopyTextureToRHI(RainMapInit.Texture, RainMap); + } + else + { + FVoxelMessages::Error( + FString::Printf( + TEXT("Voxel Erosion Init: RainMapInit has size (%d, %d), but should have size (%d, %d)"), + RainMapInit.Texture.GetSizeX(), + RainMapInit.Texture.GetSizeY(), + RealSize, + RealSize), + this); + } + + if (HeightmapInit.Texture.GetSizeX() == RealSize && + HeightmapInit.Texture.GetSizeY() == RealSize) + { + CopyTextureToRHI(HeightmapInit.Texture, TerrainHeight); + } + else + { + FVoxelMessages::Error( + FString::Printf( + TEXT("Voxel Erosion Init: HeightmapInit has size (%d, %d), but should have size (%d, %d)"), + HeightmapInit.Texture.GetSizeX(), + HeightmapInit.Texture.GetSizeY(), + RealSize, + RealSize), + this); + } + + bIsInit = true; +} + +bool UVoxelErosion::IsInitialized() const +{ + return bIsInit; +} + +void UVoxelErosion::Step(int32 Count) +{ + if (!bIsInit) + { + FVoxelMessages::Error("Erosion is not initialized!"); + return; + } + + FVoxelErosionParameters Parameters; + Parameters.size = RealSize; + Parameters.dt = DeltaTime; + + Parameters.l = Scale; + Parameters.g = Gravity; + + Parameters.Kc = SedimentCapacity; + Parameters.Ks = SedimentDissolving; + Parameters.Kd = SedimentDeposition; + + Parameters.Kr = RainStrength; + Parameters.Ke = Evaporation; + + ENQUEUE_RENDER_COMMAND(Step)( + [Parameters, Count, ThisPtr = this](FRHICommandList& RHICmdList) + { + ThisPtr->Step_RenderThread(Parameters, Count); + }); +} + +FVoxelFloatTexture UVoxelErosion::GetTerrainHeightTexture() +{ + if (!bIsInit) + { + FVoxelMessages::Error("Erosion is not initialized!"); + return {}; + } + + auto Texture = MakeVoxelShared::FTextureData>(); + CopyRHIToTexture(TerrainHeight, Texture); + return { TVoxelTexture(Texture) }; +} + + +FVoxelFloatTexture UVoxelErosion::GetWaterHeightTexture() +{ + if (!bIsInit) + { + FVoxelMessages::Error("Erosion is not initialized!"); + return {}; + } + + auto Texture = MakeVoxelShared::FTextureData>(); + CopyRHIToTexture(WaterHeight, Texture); + return { TVoxelTexture(Texture) }; +} + + +FVoxelFloatTexture UVoxelErosion::GetSedimentTexture() +{ + if (!bIsInit) + { + FVoxelMessages::Error("Erosion is not initialized!"); + return {}; + } + + auto Texture = MakeVoxelShared::FTextureData>(); + CopyRHIToTexture(Sediment, Texture); + return { TVoxelTexture(Texture) }; +} + +template +void UVoxelErosion::RunShader(const FVoxelErosionParameters& Parameters) +{ + FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); + + TShaderMapRef ComputeShader(GetGlobalShaderMap(ERHIFeatureLevel::SM5)); + RHICmdList.SetComputeShader(UE_25_SWITCH(ComputeShader->GetComputeShader(), ComputeShader.GetComputeShader())); + + ComputeShader->SetSurfaces( + RHICmdList, + RainMapUAV, + TerrainHeightUAV, + TerrainHeight1UAV, + WaterHeightUAV, + WaterHeight1UAV, + WaterHeight2UAV, + SedimentUAV, + Sediment1UAV, + OutflowUAV, + VelocityUAV); + ComputeShader->SetUniformBuffers(RHICmdList, Parameters); + + RHICmdList.DispatchComputeShader(RealSize / VOXEL_EROSION_NUM_THREADS_CS, RealSize / VOXEL_EROSION_NUM_THREADS_CS, 1); + + ComputeShader->UnbindBuffers(RHICmdList); +} + +void UVoxelErosion::CopyTextureToRHI(const TVoxelTexture& Texture, const FTexture2DRHIRef& RHITexture) +{ + ENQUEUE_RENDER_COMMAND(CopyTextureToRHI)([Texture, RHITexture, ThisPtr = this](FRHICommandList& RHICmdList) + { + ThisPtr->CopyTextureToRHI_RenderThread(Texture, RHITexture); + }); + + FlushRenderingCommands(false); +} + +void UVoxelErosion::CopyRHIToTexture(const FTexture2DRHIRef& RHITexture, TVoxelSharedRef::FTextureData>& Texture) +{ + ENQUEUE_RENDER_COMMAND(CopyRHIToTexture)( + [RHITexture, Texture, ThisPtr = this](FRHICommandList& RHICmdList) + { + ThisPtr->CopyRHIToTexture_RenderThread(RHITexture, *Texture); + }); + + FlushRenderingCommands(false); +} + +void UVoxelErosion::CopyTextureToRHI_RenderThread(const TVoxelTexture& Texture, const FTexture2DRHIRef& RHITexture) +{ + check(IsInRenderingThread()); + + const int32 Size = RHITexture->GetSizeX(); + if (!ensureAlways(RHITexture->GetSizeY() == Size)) return; + + if (!ensureAlways(Texture.GetSizeX() == Size)) return; + if (!ensureAlways(Texture.GetSizeY() == Size)) return; + + uint32 MappedStride = 0; + float* const RHIData = static_cast(RHILockTexture2D(RHITexture, 0, RLM_WriteOnly, MappedStride, false)); + if (!ensureAlways(RHIData)) return; + + check(Texture.GetTextureData().Num() == Size * Size); + FMemory::Memcpy(RHIData, Texture.GetTextureData().GetData(), Size * Size * sizeof(float)); + + RHIUnlockTexture2D(RHITexture, 0, false); +} + + +void UVoxelErosion::CopyRHIToTexture_RenderThread(const FTexture2DRHIRef& RHITexture, TVoxelTexture::FTextureData& Texture) +{ + check(IsInRenderingThread()); + + const int32 Size = RHITexture->GetSizeX(); + if (!ensureAlways(RHITexture->GetSizeY() == Size)) return; + + uint32 MappedStride = 0; + const float* RESTRICT const RHIData = static_cast(RHILockTexture2D(RHITexture, 0, RLM_ReadOnly, MappedStride, false)); + if (!ensureAlways(RHIData)) return; + + Texture.SetSize(Size, Size); + + for (int32 Index = 0; Index < Size * Size; Index++) + { + Texture.SetValue(Index, RHIData[Index]); + } + + RHIUnlockTexture2D(RHITexture, 0, false); +} + +void UVoxelErosion::Init_RenderThread() +{ + check(IsInRenderingThread()); + + FRHIResourceCreateInfo CreateInfo; + const UE_26_SWITCH(uint32, ETextureCreateFlags) Flags = TexCreate_ShaderResource | TexCreate_UAV; + +#define CREATE_TEXTURE(Name, SizeX) \ + Name = RHICreateTexture2D(SizeX * RealSize, RealSize, PF_R32_FLOAT, 1, 1, Flags, CreateInfo); \ + Name##UAV = RHICreateUnorderedAccessView(Name); \ + + CREATE_TEXTURE(RainMap, 1); + CREATE_TEXTURE(TerrainHeight, 1); + CREATE_TEXTURE(TerrainHeight1, 1); + CREATE_TEXTURE(WaterHeight, 1); + CREATE_TEXTURE(WaterHeight1, 1); + CREATE_TEXTURE(WaterHeight2, 1); + CREATE_TEXTURE(Sediment, 1); + CREATE_TEXTURE(Sediment1, 1); + CREATE_TEXTURE(Outflow, 4); + CREATE_TEXTURE(Velocity, 2); + +#undef CREATE_TEXTURE +} + + +void UVoxelErosion::Step_RenderThread(const FVoxelErosionParameters& Parameters, int32 Count) +{ + check(IsInRenderingThread()); + for (int32 Index = 0; Index < Count; Index++) + { + RunShader(Parameters); + RunShader(Parameters); + RunShader(Parameters); + RunShader(Parameters); + RunShader(Parameters); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp new file mode 100644 index 00000000..08149fb7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelShaders/VoxelErosionShader.h" +#include "ShaderParameterUtils.h" + +IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FVoxelErosionParameters, "VoxelErosionParameters"); + +FVoxelErosionCS::FVoxelErosionCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) +{ +#define PROCESS_SURFACE(Name) Name.Bind(Initializer.ParameterMap, TEXT(#Name), EShaderParameterFlags::SPF_Optional); + PROCESS_SURFACE(RainMap); + PROCESS_SURFACE(TerrainHeight); + PROCESS_SURFACE(TerrainHeight1); + PROCESS_SURFACE(WaterHeight); + PROCESS_SURFACE(WaterHeight1); + PROCESS_SURFACE(WaterHeight2); + PROCESS_SURFACE(Sediment); + PROCESS_SURFACE(Sediment1); + PROCESS_SURFACE(Outflow); + PROCESS_SURFACE(Velocity); +#undef PROCESS_SURFACE +} + +void FVoxelErosionCS::ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) +{ + FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); + OutEnvironment.SetDefine(TEXT("NUM_THREADS_CS"), VOXEL_EROSION_NUM_THREADS_CS); + //OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization); +} + +#if ENGINE_MINOR_VERSION < 25 +bool FVoxelErosionCS::Serialize(FArchive& Ar) +{ + const bool bShaderHasOutdatedParams = FGlobalShader::Serialize(Ar); + Ar << RainMap; + Ar << TerrainHeight; + Ar << TerrainHeight1; + Ar << WaterHeight; + Ar << WaterHeight1; + Ar << WaterHeight2; + Ar << Sediment; + Ar << Sediment1; + Ar << Outflow; + Ar << Velocity; + return bShaderHasOutdatedParams; +} +#endif + +void FVoxelErosionCS::SetSurfaces( + FRHICommandList& RHICmdList, + FUnorderedAccessViewRHIRef RainMapUAV, + FUnorderedAccessViewRHIRef TerrainHeightUAV, + FUnorderedAccessViewRHIRef TerrainHeight1UAV, + FUnorderedAccessViewRHIRef WaterHeightUAV, + FUnorderedAccessViewRHIRef WaterHeight1UAV, + FUnorderedAccessViewRHIRef WaterHeight2UAV, + FUnorderedAccessViewRHIRef SedimentUAV, + FUnorderedAccessViewRHIRef Sediment1UAV, + FUnorderedAccessViewRHIRef OutflowUAV, + FUnorderedAccessViewRHIRef VelocityUAV) +{ +#define PROCESS_SURFACE(Name) SetUAVParameter(RHICmdList, UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), Name, Name##UAV); + PROCESS_SURFACE(RainMap); + PROCESS_SURFACE(TerrainHeight); + PROCESS_SURFACE(TerrainHeight1); + PROCESS_SURFACE(WaterHeight); + PROCESS_SURFACE(WaterHeight1); + PROCESS_SURFACE(WaterHeight2); + PROCESS_SURFACE(Sediment); + PROCESS_SURFACE(Sediment1); + PROCESS_SURFACE(Outflow); + PROCESS_SURFACE(Velocity); +#undef PROCESS_SURFACE +} + +void FVoxelErosionCS::SetUniformBuffers(FRHICommandList& RHICmdList, const FVoxelErosionParameters& Parameters) +{ + const FVoxelErosionParametersRef ParametersBuffer = FVoxelErosionParametersRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_MultiFrame); + SetUniformBufferParameter(RHICmdList, UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), GetUniformBufferParameter(), ParametersBuffer); +} + +/* Unbinds buffers that will be used elsewhere */ +void FVoxelErosionCS::UnbindBuffers(FRHICommandList& RHICmdList) +{ +#define PROCESS_SURFACE(Name) RHICmdList.SetUAVParameter(UE_25_SWITCH(GetComputeShader(), RHICmdList.GetBoundComputeShader()), Name.GetBaseIndex(), FUnorderedAccessViewRHIRef()); + PROCESS_SURFACE(RainMap); + PROCESS_SURFACE(TerrainHeight); + PROCESS_SURFACE(TerrainHeight1); + PROCESS_SURFACE(WaterHeight); + PROCESS_SURFACE(WaterHeight1); + PROCESS_SURFACE(WaterHeight2); + PROCESS_SURFACE(Sediment); + PROCESS_SURFACE(Sediment1); + PROCESS_SURFACE(Outflow); + PROCESS_SURFACE(Velocity); +#undef PROCESS_SURFACE +} + +IMPLEMENT_TYPE_LAYOUT(FVoxelErosionCS); +IMPLEMENT_SHADER_TYPE(, FVoxelErosionWaterIncrementCS , TEXT("/Plugin/Voxel/Private/Erosion.usf"), TEXT("WaterIncrement") , SF_Compute); +IMPLEMENT_SHADER_TYPE(, FVoxelErosionFlowSimulationCS , TEXT("/Plugin/Voxel/Private/Erosion.usf"), TEXT("FlowSimulation") , SF_Compute); +IMPLEMENT_SHADER_TYPE(, FVoxelErosionErosionDepositionCS , TEXT("/Plugin/Voxel/Private/Erosion.usf"), TEXT("ErosionDeposition") , SF_Compute); +IMPLEMENT_SHADER_TYPE(, FVoxelErosionSedimentTransportationCS, TEXT("/Plugin/Voxel/Private/Erosion.usf"), TEXT("SedimentTransportation"), SF_Compute); +IMPLEMENT_SHADER_TYPE(, FVoxelErosionEvaporationCS , TEXT("/Plugin/Voxel/Private/Erosion.usf"), TEXT("Evaporation") , SF_Compute); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h new file mode 100644 index 00000000..b9905aa1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h @@ -0,0 +1,122 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "GlobalShader.h" +#include "UniformBuffer.h" +#include "VoxelMinimal.h" +#include "ShaderParameterMacros.h" + +#define VOXEL_EROSION_NUM_THREADS_CS 32 + +BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FVoxelErosionParameters, ) + +// Texture size +SHADER_PARAMETER(uint32, size) +SHADER_PARAMETER(float, dt) + +// Size of a pipe +SHADER_PARAMETER(float, l) +// Gravity +SHADER_PARAMETER(float, g) + +// Sediment capacity +SHADER_PARAMETER(float, Kc) +// Sediment dissolving +SHADER_PARAMETER(float, Ks) +// Sediment deposition +SHADER_PARAMETER(float, Kd) + +// Rain strength +SHADER_PARAMETER(float, Kr) +// Evaporation +SHADER_PARAMETER(float, Ke) + +END_GLOBAL_SHADER_PARAMETER_STRUCT() + +typedef TUniformBufferRef FVoxelErosionParametersRef; + +class FVoxelErosionCS : public FGlobalShader +{ + DECLARE_TYPE_LAYOUT(FVoxelErosionCS, NonVirtual); +public: + FVoxelErosionCS() = default; + FVoxelErosionCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer); + + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); + } + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment); + +#if ENGINE_MINOR_VERSION < 25 + virtual bool Serialize(FArchive& Ar) override; +#endif + + void SetSurfaces( + FRHICommandList& RHICmdList, + FUnorderedAccessViewRHIRef RainMapUAV, + FUnorderedAccessViewRHIRef TerrainHeightUAV, + FUnorderedAccessViewRHIRef TerrainHeight1UAV, + FUnorderedAccessViewRHIRef WaterHeightUAV, + FUnorderedAccessViewRHIRef WaterHeight1UAV, + FUnorderedAccessViewRHIRef WaterHeight2UAV, + FUnorderedAccessViewRHIRef SedimentUAV, + FUnorderedAccessViewRHIRef Sediment1UAV, + FUnorderedAccessViewRHIRef OutflowUAV, + FUnorderedAccessViewRHIRef VelocityUAV); + + void SetUniformBuffers(FRHICommandList& RHICmdList, const FVoxelErosionParameters& Parameters); + void UnbindBuffers(FRHICommandList& RHICmdList); + +private: + LAYOUT_FIELD(FShaderResourceParameter, RainMap); + LAYOUT_FIELD(FShaderResourceParameter, TerrainHeight); + LAYOUT_FIELD(FShaderResourceParameter, TerrainHeight1); + LAYOUT_FIELD(FShaderResourceParameter, WaterHeight); + LAYOUT_FIELD(FShaderResourceParameter, WaterHeight1); + LAYOUT_FIELD(FShaderResourceParameter, WaterHeight2); + LAYOUT_FIELD(FShaderResourceParameter, Sediment); + LAYOUT_FIELD(FShaderResourceParameter, Sediment1); + LAYOUT_FIELD(FShaderResourceParameter, Outflow); + LAYOUT_FIELD(FShaderResourceParameter, Velocity); +}; + +class FVoxelErosionWaterIncrementCS : public FVoxelErosionCS +{ + DECLARE_SHADER_TYPE(FVoxelErosionWaterIncrementCS, Global); + + using FVoxelErosionCS::FVoxelErosionCS; +}; + +class FVoxelErosionFlowSimulationCS : public FVoxelErosionCS +{ + DECLARE_SHADER_TYPE(FVoxelErosionFlowSimulationCS, Global); + + using FVoxelErosionCS::FVoxelErosionCS; +}; + +class FVoxelErosionErosionDepositionCS : public FVoxelErosionCS +{ + DECLARE_SHADER_TYPE(FVoxelErosionErosionDepositionCS, Global); + + using FVoxelErosionCS::FVoxelErosionCS; +}; + +class FVoxelErosionSedimentTransportationCS : public FVoxelErosionCS +{ + DECLARE_SHADER_TYPE(FVoxelErosionSedimentTransportationCS, Global); + + using FVoxelErosionCS::FVoxelErosionCS; +}; + +class FVoxelErosionEvaporationCS : public FVoxelErosionCS +{ + DECLARE_SHADER_TYPE(FVoxelErosionEvaporationCS, Global); + + using FVoxelErosionCS::FVoxelErosionCS; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.cpp new file mode 100644 index 00000000..f1478b6e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.cpp @@ -0,0 +1,250 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelSpawnerUtilities.h" +#include "VoxelSpawners/VoxelSpawnerRandomGenerator.h" + +#include "VoxelCancelCounter.h" +#include "VoxelData/VoxelDataIncludes.h" + +#include "Misc/ScopeExit.h" + +void FVoxelHeightHitGenerator::GenerateSpawner( + TArray& OutHits, + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner, + const FRandomStream& RandomStream, + const FVoxelSpawnerRandomGenerator& RandomGenerator, + int32 NumRays) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(Spawner.SpawnerType == EVoxelSpawnerType::Height); + ensure(Spawner.Density.Type == EVoxelSpawnerDensityType::Constant || Spawner.Density.Type == EVoxelSpawnerDensityType::GeneratorOutput); + check(Parameters.Group.SpawnerType == EVoxelSpawnerType::Height); + + // Note: assets are ignored when querying the height and the density, as it gets way too messy + // For flat worlds, the height and the density is queried at Z = 0 if density is computed first, or Z = Height if height first + // For sphere worlds at a normalized (X, Y, Z) if density is computed first, or normalized (X, Y, Z) * Height if height first + // In theory the density could be computed at the exact position if bComputeDensityFirst is false, but this makes the behavior unpredictable + + const FVoxelIntBox Bounds = Parameters.Bounds; + auto& Accelerator = Parameters.Accelerator; + + const int32 ChunkSize = RENDER_CHUNK_SIZE << Parameters.Group.LOD; + + ensure(Bounds.Size().X == ChunkSize); + ensure(Bounds.Size().Y == ChunkSize); + ensure(Bounds.Size().Z == ChunkSize); + + const FVoxelIntBox BoundsLimit = Bounds.Overlap(Accelerator.Data.WorldBounds); + + const FIntVector& ChunkPosition = Bounds.Min; + const bool bIsSphere = Parameters.Config.WorldType == EVoxelSpawnerConfigRayWorldType::Sphere; + FVoxelGeneratorInstance& Generator = *Accelerator.Data.Generator; + + // This is the value to use if the generator doesn't have the custom output. + constexpr v_flt DefaultHeight = 0; + + const auto GetHeight = [&](const FVoxelVector& Position) + { + return Generator.GetCustomOutput(DefaultHeight, Spawner.HeightGraphOutputName_HeightOnly, Position, 0, FVoxelItemStack::Empty); + }; + + const auto GetDensity = [&](const FVoxelVector& Position) + { + return + Spawner.Density.Type == EVoxelSpawnerDensityType::Constant + ? Spawner.Density.Constant + : Generator.GetCustomOutput(0.f, Spawner.Density.GeneratorOutputName, Position, 0, FVoxelItemStack::Empty); + }; + + const auto IsFloatingOrCovered = [&](const FVoxelVector& InsideSurface, const FVoxelVector& OutsideSurface) + { + if (Spawner.bCheckIfFloating_HeightOnly) + { + const v_flt InsideValue = Accelerator.GetFloatValue(InsideSurface, 0); + if (InsideValue > 0) + { + return true; + } + } + + if (Spawner.bCheckIfCovered_HeightOnly) + { + const v_flt OutsideValue = Accelerator.GetFloatValue(OutsideSurface, 0); + if (OutsideValue < 0) + { + return true; + } + } + + return false; + }; + + { + const auto Range = Generator.GetCustomOutputRange( + DefaultHeight, + Spawner.HeightGraphOutputName_HeightOnly, + Bounds, + 0, + FVoxelItemStack::Empty); + + if (bIsSphere) + { + auto Corners = Bounds.GetCorners(0); + if (!Range.Intersects(TVoxelRange::FromList( + FVoxelVector(Corners[0]).Size(), + FVoxelVector(Corners[1]).Size(), + FVoxelVector(Corners[2]).Size(), + FVoxelVector(Corners[3]).Size(), + FVoxelVector(Corners[4]).Size(), + FVoxelVector(Corners[5]).Size(), + FVoxelVector(Corners[6]).Size(), + FVoxelVector(Corners[7]).Size()))) + { + return; + } + } + else + { + if (!Range.Intersects(TVoxelRange(Bounds.Min.Z, Bounds.Max.Z))) + { + return; + } + } + } + + for (int32 Index = 0; Index < NumRays; Index++) + { + if ((Index & 0xFF) == 0 && Parameters.CancelCounter.IsCanceled()) + { + return; + } + ON_SCOPE_EXIT + { + RandomGenerator.Next(); + }; + + if (bIsSphere) + { + FVoxelVector BasisX, BasisY, BasisZ; + FVoxelSpawnerUtilities::GetSphereBasisFromBounds(Bounds, BasisX, BasisY, BasisZ); + + const FVector2D RandomValue = 2 * RandomGenerator.GetValue() - 1; // Map from 0,1 to -1,1 + const auto SamplePosition = [&](float X, float Y) + { + // 1.5f: hack to avoid holes between chunks + return (Bounds.GetCenter() + 1.5f * ChunkSize / 2.f * (BasisX * X + BasisY * Y)).GetSafeNormal(); + }; + const FVoxelVector Start = SamplePosition(RandomValue.X, RandomValue.Y); + + v_flt Height; + FVoxelVector Position; + if (Spawner.bComputeDensityFirst_HeightOnly) + { + const v_flt Density = GetDensity(Start); + if (RandomStream.GetFraction() > Density) + { + continue; + } + + Height = GetHeight(Start); + Position = Start * Height; + if (!BoundsLimit.ContainsFloat(Position)) + { + continue; + } + } + else + { + Height = GetHeight(Start); + Position = Start * Height; + if (!BoundsLimit.ContainsFloat(Position)) + { + continue; + } + + const v_flt Density = GetDensity(Position); + if (RandomStream.GetFraction() > Density) + { + continue; + } + } + ensure(BoundsLimit.ContainsFloat(Position)); + + if (IsFloatingOrCovered(Start * (Height - 1), Start * (Height + 1))) + { + continue; + } + + // Will cancel out in SamplePosition + const v_flt Delta = 1. / ChunkSize; + + const FVoxelVector Left = SamplePosition(RandomValue.X - Delta, RandomValue.Y); + const FVoxelVector Right = SamplePosition(RandomValue.X + Delta, RandomValue.Y); + const FVoxelVector Bottom = SamplePosition(RandomValue.X, RandomValue.Y - Delta); + const FVoxelVector Top = SamplePosition(RandomValue.X, RandomValue.Y + Delta); + + const FVoxelVector Gradient = + BasisX * (GetHeight(Left) - GetHeight(Right)) + + BasisY * (GetHeight(Bottom) - GetHeight(Top)) + + BasisZ * -2.f; + + OutHits.Add(FVoxelSpawnerHit((Position - ChunkPosition).ToFloat(), Gradient.GetSafeNormal().ToFloat())); + } + else + { + const FVector2D LocalPosition = RandomGenerator.GetValue() * ChunkSize; + const v_flt X = v_flt(LocalPosition.X) + Bounds.Min.X; + const v_flt Y = v_flt(LocalPosition.Y) + Bounds.Min.Y; + + v_flt Z; + if (Spawner.bComputeDensityFirst_HeightOnly) + { + const v_flt Density = GetDensity({ X, Y, 0 }); + if (RandomStream.GetFraction() > Density) + { + continue; + } + + Z = GetHeight({ X, Y, 0 }); + if (BoundsLimit.Min.Z > Z || Z > BoundsLimit.Max.Z) + { + continue; + } + } + else + { + Z = GetHeight({ X, Y, 0 }); + if (BoundsLimit.Min.Z > Z || Z > BoundsLimit.Max.Z) + { + continue; + } + const v_flt Density = GetDensity({ X, Y, Z }); + if (RandomStream.GetFraction() > Density) + { + continue; + } + } + ensure(BoundsLimit.Min.Z <= Z && Z <= BoundsLimit.Max.Z); + + if (IsFloatingOrCovered({ X, Y, Z - 1 }, { X, Y, Z + 1 })) + { + continue; + } + + FVoxelVector Gradient; + Gradient.X = + GetHeight({ X - 1, Y, 0 }) - + GetHeight({ X + 1, Y, 0 }); + Gradient.Y = + GetHeight({ X, Y - 1, 0 }) - + GetHeight({ X, Y + 1, 0 }); + Gradient.Z = 2; + + OutHits.Add(FVoxelSpawnerHit((FVoxelVector(X, Y, Z) - ChunkPosition).ToFloat(), Gradient.GetSafeNormal().ToFloat())); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.h new file mode 100644 index 00000000..bd92cc51 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelHitGenerator.h" + +class FVoxelHeightHitGenerator : public FVoxelHitGenerator +{ +public: + using FVoxelHitGenerator::FVoxelHitGenerator; + +protected: + virtual void GenerateSpawner( + TArray& OutHits, + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner, + const FRandomStream& RandomStream, + const FVoxelSpawnerRandomGenerator& RandomGenerator, + int32 NumRays) override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.cpp new file mode 100644 index 00000000..756f028b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.cpp @@ -0,0 +1,54 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/HitGenerators/VoxelHitGenerator.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelSpawnerUtilities.h" +#include "VoxelSpawners/VoxelSpawnerRandomGenerator.h" + +#include "VoxelCancelCounter.h" + +TMap> FVoxelHitGenerator::Generate() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const FVoxelIntBox Bounds = Parameters.Bounds; + + ensure(Bounds.Size().X == Bounds.Size().Y && Bounds.Size().Y == Bounds.Size().Z); + const int32 BoundsSize = Bounds.Size().X; + + const FIntVector& ChunkPosition = Bounds.Min; + const uint32 Seed = Bounds.GetMurmurHash(); + + TMap> OutHits; + for (int32 ElementIndex : Parameters.SpawnersToSpawn) + { + if (Parameters.CancelCounter.IsCanceled()) return {}; + + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner = Parameters.Group.Spawners[ElementIndex]; + + const uint32 ElementSeed = + Parameters.Group.SpawnerType == EVoxelSpawnerType::Ray + ? FVoxelUtilities::MurmurHash32(Seed, Parameters.GroupSeed, ElementIndex, Spawner.Seed, /* avoid collisions with height spawners */23) + : FVoxelUtilities::MurmurHash32(Seed, Parameters.GroupSeed, ElementIndex, Spawner.Seed); + + const FRandomStream RandomStream(ElementSeed); + + const TUniquePtr RandomGenerator = FVoxelSpawnerUtilities::GetRandomGenerator(Spawner); + RandomGenerator->Init( + ElementSeed ^ FVoxelUtilities::MurmurHash32(ChunkPosition.X), + ElementSeed ^ FVoxelUtilities::MurmurHash32(ChunkPosition.Y)); + + const int32 NumRays = FMath::FloorToInt(FMath::Square(double(BoundsSize) / double(Spawner.DistanceBetweenInstancesInVoxel))); + + TArray Hits; + GenerateSpawner(Hits, Spawner, RandomStream, *RandomGenerator, NumRays); + + if (Hits.Num() > 0) + { + OutHits.Add(ElementIndex, MoveTemp(Hits)); + } + } + + return OutHits; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.h new file mode 100644 index 00000000..f079467e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelHitGenerator.h @@ -0,0 +1,51 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" + +struct FVoxelSpawnerHit; +struct FVoxelSpawnerConfigGroup; +struct FVoxelSpawnerThreadSafeConfig; +struct FVoxelSpawnerConfigSpawnerWithRuntimeData; + +class FVoxelCancelCounter; +class FVoxelConstDataAccelerator; +class FVoxelSpawnerRandomGenerator; + +class FVoxelHitGenerator +{ +public: + struct FParameters + { + // Used to have different results even if the groups are identical + int32 GroupSeed; + FVoxelIntBox Bounds; + + const TArray& SpawnersToSpawn; + const FVoxelSpawnerConfigGroup& Group; + const FVoxelSpawnerThreadSafeConfig& Config; + + const FVoxelCancelCounter& CancelCounter; + const FVoxelConstDataAccelerator& Accelerator; + }; + const FParameters Parameters; + + explicit FVoxelHitGenerator(const FParameters& Parameters) + : Parameters(Parameters) + { + } + virtual ~FVoxelHitGenerator() = default; + +public: + TMap> Generate(); + +protected: + virtual void GenerateSpawner( + TArray& OutHits, + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner, + const FRandomStream& RandomStream, + const FVoxelSpawnerRandomGenerator& RandomGenerator, + int32 NumRays) = 0; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.cpp new file mode 100644 index 00000000..2278809a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.cpp @@ -0,0 +1,207 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/HitGenerators/VoxelRayHitGenerator.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelSpawnerUtilities.h" +#include "VoxelSpawners/VoxelSpawnerRayHandler.h" +#include "VoxelSpawners/VoxelSpawnerRandomGenerator.h" + +#include "VoxelCancelCounter.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +void FVoxelRayHitGenerator::GenerateSpawner( + TArray& OutHits, + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner, + const FRandomStream& RandomStream, + const FVoxelSpawnerRandomGenerator& RandomGenerator, + int32 NumRays) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(Parameters.Group.SpawnerType == EVoxelSpawnerType::Ray); + + const FVoxelIntBox Bounds = Parameters.Bounds; + auto& Accelerator = Parameters.Accelerator; + + check(Bounds.Size().X == Bounds.Size().Y && Bounds.Size().Y == Bounds.Size().Z); + const int32 BoundsSize = Bounds.Size().X; + const FIntVector& ChunkPosition = Bounds.Min; + + FVoxelVector BasisX, BasisY; + FVoxelSpawnerUtilities::GetBasisFromBounds(Parameters.Config, Bounds, BasisX, BasisY); + + struct FVoxelRay + { + FVector Position; + FVector Direction; + }; + TArray Hits; + TArray QueuedRays; + for (int32 Index = 0; Index < NumRays; Index++) + { + if ((Index & 0xFF) == 0 && Parameters.CancelCounter.IsCanceled()) + { + return; + } + + const FVector2D RandomValue = 2 * RandomGenerator.GetValue() - 1; // Map from 0,1 to -1,1 + const FVector Start = FVoxelVector::ToFloat(BoundsSize / 2 * (BasisX * RandomValue.X + BasisY * RandomValue.Y + 1)); // +1: we want to be in the center + const FVector Direction = FVoxelVector::ToFloat(FVoxelSpawnerUtilities::GetRayDirection(Parameters.Config, Start, ChunkPosition)); + FVoxelSpawnerHit Hit; + if (RayHandler.TraceRay( + Start - Direction * 4 * BoundsSize /* Ray offset */, + Direction, + Hit.Normal, + Hit.LocalPosition)) + { + Hits.Add(Hit); + QueuedRays.Add({ Hit.LocalPosition, Direction }); + } + RandomGenerator.Next(); + } + + // Process consecutive hits + while (QueuedRays.Num() > 0) + { + const auto Ray = QueuedRays.Pop(false); + const float Offset = 1; + FVoxelSpawnerHit Hit; + if (RayHandler.TraceRay( + Ray.Position + Ray.Direction * Offset, + Ray.Direction, + Hit.Normal, + Hit.LocalPosition)) + { + Hits.Add(Hit); + QueuedRays.Add({ Hit.LocalPosition, Ray.Direction }); + } + } + + for (auto& Hit : Hits) + { + const FVector& LocalPosition = Hit.LocalPosition; + const FVector& Normal = Hit.Normal; + const FVoxelVector GlobalPosition = FVoxelVector(ChunkPosition) + LocalPosition; + + if (!Accelerator.Data.IsInWorld(GlobalPosition)) + { + continue; + } + + const FIntVector VoxelPosition = FVoxelSpawnerUtilities::GetClosestNotEmptyPoint(Accelerator, GlobalPosition); + + TOptional CachedGeneratorDensity; + const auto GetGeneratorDensity = [&]() + { + if (!CachedGeneratorDensity.IsSet()) + { + // Need to get the right ItemHolder + CachedGeneratorDensity = Accelerator.GetCustomOutput(0.f, Spawner.Density.GeneratorOutputName, VoxelPosition.X, VoxelPosition.Y, VoxelPosition.Z, 0); + } + return CachedGeneratorDensity.GetValue(); + }; + + TOptional CachedMaterial; + const auto GetMaterial = [&]() + { + if (!CachedMaterial.IsSet()) + { + CachedMaterial = Accelerator.Get(VoxelPosition, 0); + } + return CachedMaterial.GetValue(); + }; + + const auto GetDensity = [&GetGeneratorDensity, &GetMaterial, this](const FVoxelSpawnerDensity& SpawnerDensity) + { + const auto GetDensityImpl = [&]() + { + switch (SpawnerDensity.Type) + { + default: ensure(false); + case EVoxelSpawnerDensityType::Constant: + { + return SpawnerDensity.Constant; + } + case EVoxelSpawnerDensityType::GeneratorOutput: + { + return GetGeneratorDensity(); + } + case EVoxelSpawnerDensityType::MaterialRGBA: + { + const FVoxelMaterial Material = GetMaterial(); + switch (SpawnerDensity.RGBAChannel) + { + default: ensure(false); + case EVoxelRGBA::R: return Material.GetR_AsFloat(); + case EVoxelRGBA::G: return Material.GetG_AsFloat(); + case EVoxelRGBA::B: return Material.GetB_AsFloat(); + case EVoxelRGBA::A: return Material.GetA_AsFloat(); + } + } + case EVoxelSpawnerDensityType::MaterialUVs: + { + const FVoxelMaterial Material = GetMaterial(); + return SpawnerDensity.UVAxis == EVoxelSpawnerUVAxis::U + ? Material.GetU_AsFloat(SpawnerDensity.UVChannel) + : Material.GetV_AsFloat(SpawnerDensity.UVChannel); + } + case EVoxelSpawnerDensityType::MaterialFiveWayBlend: + { + const FVoxelMaterial Material = GetMaterial(); + + if (Parameters.Config.FiveWayBlendSetup.bFourWayBlend) + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetFourWayBlendStrengths(Material); + return Strengths[FMath::Clamp(SpawnerDensity.FiveWayBlendChannel, 0, 3)]; + } + else + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetFiveWayBlendStrengths(Material); + return Strengths[FMath::Clamp(SpawnerDensity.FiveWayBlendChannel, 0, 4)]; + } + } + case EVoxelSpawnerDensityType::SingleIndex: + { + const FVoxelMaterial Material = GetMaterial(); + return SpawnerDensity.SingleIndexChannels.Contains(Material.GetSingleIndex()) ? 1.f : 0.f; + } + case EVoxelSpawnerDensityType::MultiIndex: + { + const FVoxelMaterial Material = GetMaterial(); + const TVoxelStaticArray Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + + float Strength = 0.f; + for (int32 Channel : SpawnerDensity.MultiIndexChannels) + { + const int32 Index = FVoxelUtilities::GetMultiIndexIndex(Material, Channel); + if (Index != -1) + { + Strength += Strengths[Index]; + } + } + + return Strength; + } + } + }; + + const float Density = GetDensityImpl(); + switch (SpawnerDensity.Transform) + { + default: ensure(false); + case EVoxelSpawnerDensityTransform::Identity: return Density; + case EVoxelSpawnerDensityTransform::OneMinus: return 1.f - Density; + } + }; + + const float Density = GetDensity(Spawner.Density); + const float DensityMultiplier = GetDensity(Spawner.DensityMultiplier_RayOnly); + + if (RandomStream.GetFraction() <= Density * DensityMultiplier) + { + OutHits.Add(FVoxelSpawnerHit(LocalPosition, Normal)); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.h new file mode 100644 index 00000000..4ec4b44d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/HitGenerators/VoxelRayHitGenerator.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelHitGenerator.h" + +class FVoxelSpawnerRayHandler; + +class FVoxelRayHitGenerator : public FVoxelHitGenerator +{ +public: + FVoxelSpawnerRayHandler& RayHandler; + + explicit FVoxelRayHitGenerator(const FParameters& Parameters, FVoxelSpawnerRayHandler& RayHandler) + : FVoxelHitGenerator(Parameters) + , RayHandler(RayHandler) + { + } + +protected: + virtual void GenerateSpawner( + TArray& OutHits, + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner, + const FRandomStream& RandomStream, + const FVoxelSpawnerRandomGenerator& RandomGenerator, + int32 NumRays) override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelAssetSpawner.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelAssetSpawner.cpp new file mode 100644 index 00000000..396b982d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelAssetSpawner.cpp @@ -0,0 +1,215 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelMessages.h" + +#include "Async/Async.h" + +inline FVoxelIntBox GetGeneratorsBounds(const FVoxelAssetSpawnerProxy& Proxy, const TArray& Matrices) +{ + if (Matrices.Num() > 0) + { + FVoxelIntBoxWithValidity BoundsWithValidity; + for (const auto& Matrix : Matrices) + { + BoundsWithValidity += Proxy.GeneratorLocalBounds.ApplyTransform(FTransform(Matrix)); + } + return BoundsWithValidity.GetBox(); + } + else + { + return FVoxelIntBox(); + } +} + +FVoxelAssetSpawnerProxyResult::FVoxelAssetSpawnerProxyResult(const FVoxelAssetSpawnerProxy& Proxy) + : FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType::AssetSpawner, Proxy) +{ + check(Matrices.Num() == GeneratorsIndices.Num()); +} + +void FVoxelAssetSpawnerProxyResult::Init(TArray&& InMatrices, TArray&& InGeneratorsIndices) +{ + check(InMatrices.Num() == InGeneratorsIndices.Num()); + + Bounds = GetGeneratorsBounds(static_cast(Proxy), InMatrices); + Matrices = MoveTemp(InMatrices); + GeneratorsIndices = MoveTemp(InGeneratorsIndices); +} + +void FVoxelAssetSpawnerProxyResult::CreateImpl() +{ + VOXEL_FUNCTION_COUNTER(); + + if (Matrices.Num() == 0) return; + + const auto& AssetProxy = static_cast(Proxy); + auto& Data = *AssetProxy.Manager.Settings.Data; + + { + FVoxelWriteScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + Items.Reserve(Matrices.Num()); + for (int32 Index = 0; Index < Matrices.Num(); Index++) + { + const auto Generator = AssetProxy.Generators[GeneratorsIndices[Index]].ToSharedRef(); + const FTransform Transform(Matrices[Index]); + + Items.Emplace(Data.AddItem( + Generator, + AssetProxy.GeneratorLocalBounds.ApplyTransform(Transform), + Transform, + AssetProxy.Priority)); + } + } + + Proxy.Manager.Settings.LODManager->UpdateBounds(Bounds); +} + +void FVoxelAssetSpawnerProxyResult::DestroyImpl() +{ + VOXEL_FUNCTION_COUNTER(); + + if (Matrices.Num() == 0) return; + + auto& Data = *Proxy.Manager.Settings.Data; + { + FVoxelWriteScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + for (auto& Item : Items) + { + FString Error; + Data.RemoveItem(Item, Error); + } + } + + Items.Empty(); +} + +void FVoxelAssetSpawnerProxyResult::SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) +{ + Ar << Bounds; + Ar << Matrices; + Ar << GeneratorsIndices; +} + +uint32 FVoxelAssetSpawnerProxyResult::GetAllocatedSize() +{ + return sizeof(*this) + Matrices.GetAllocatedSize() + GeneratorsIndices.GetAllocatedSize() + Items.GetAllocatedSize(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TArray> CreateGenerators(UVoxelAssetSpawner& Spawner, FVoxelSpawnerManager& Manager) +{ + TArray> Result; + if (!Spawner.Generator.IsValid()) + { + Result.Add(MakeShareable(new FVoxelTransformableEmptyGeneratorInstance())); + return Result; + } + + const FRandomStream Stream(FCrc::StrCrc32(*Spawner.GetPathName())); + for (int32 Index = 0; Index < FMath::Max(1, Spawner.NumberOfDifferentSeedsToUse); Index++) + { + auto NewGenerator = Spawner.Generator.GetInstance(false); + + FVoxelGeneratorInit Init; + Init.VoxelSize = Manager.Settings.VoxelSize; + + NewGenerator->Init(Init); + + Result.Add(NewGenerator); + } + + return Result; +} + +FVoxelAssetSpawnerProxy::FVoxelAssetSpawnerProxy(UVoxelAssetSpawner* Spawner, FVoxelSpawnerManager& Manager) + : FVoxelBasicSpawnerProxy(Spawner, Manager, EVoxelSpawnerProxyType::AssetSpawner, 0) + , Generators(CreateGenerators(*Spawner, Manager)) + , GeneratorLocalBounds(Spawner->GeneratorLocalBounds) + , Priority(Spawner->Priority) + , bRoundAssetPosition(Spawner->bRoundAssetPosition) +{ +} + +FVoxelAssetSpawnerProxy::~FVoxelAssetSpawnerProxy() +{ +} + +TUniquePtr FVoxelAssetSpawnerProxy::ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const +{ + const uint32 Seed = Bounds.GetMurmurHash() ^ SpawnerSeed; + const auto& Settings = Manager.Settings; + auto& Data = *Settings.Data; + const auto& Generator = *Data.Generator; + + const FRandomStream Stream(Seed); + + TArray Transforms; + Transforms.Reserve(Hits.Num()); + + for (auto& Hit : Hits) + { + const FVector& LocalPosition = Hit.LocalPosition; + const FVector& Normal = Hit.Normal; + // NOTE: will glitch if far from origin + const FVector Position = LocalPosition + FVector(Bounds.Min); + const FVector WorldUp = Generator.GetUpVector(Position); + + if (!CanSpawn(Stream, Position, Normal, WorldUp)) + { + continue; + } + + const FVector PositionToUse = bRoundAssetPosition ? FVector(FVoxelUtilities::RoundToInt(Position)) : Position; + const FMatrix Transform = GetTransform(Stream, Normal, WorldUp, PositionToUse); + Transforms.Emplace(Transform); + } + + if (Transforms.Num() == 0) + { + return nullptr; + } + else + { + Transforms.Shrink(); + + TArray GeneratorsIndices; + GeneratorsIndices.Reserve(Transforms.Num()); + for (auto& Transform : Transforms) + { + GeneratorsIndices.Add(Stream.RandHelper(Generators.Num())); + } + + auto Result = MakeUnique(*this); + Result->Init(MoveTemp(Transforms), MoveTemp(GeneratorsIndices)); + return Result; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelAssetSpawner::GetSpawnerProxy(FVoxelSpawnerManager& Manager) +{ + if (!Generator.IsValid()) + { + FVoxelMessages::Error("Invalid generator!", this); + } + return MakeVoxelShared(this, Manager); +} + +FString UVoxelAssetSpawner::GetDebugInfo() const +{ + return Generator.GetObject() ? Generator.GetObject()->GetName() : "NULL"; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelBasicSpawner.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelBasicSpawner.cpp new file mode 100644 index 00000000..084b1382 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelBasicSpawner.cpp @@ -0,0 +1,89 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelBasicSpawner.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +FVoxelBasicSpawnerProxy::FVoxelBasicSpawnerProxy(UVoxelBasicSpawner* Spawner, FVoxelSpawnerManager& Manager, EVoxelSpawnerProxyType Type, uint32 Seed) + : FVoxelSpawnerProxy(Spawner, Manager, Type, Seed) + , GroundSlopeAngle(Spawner->GroundSlopeAngle) + , bEnableHeightRestriction(Spawner->bEnableHeightRestriction) + , HeightRestriction(Spawner->HeightRestriction) + , HeightRestrictionFalloff(Spawner->HeightRestrictionFalloff) + , Scaling(Spawner->Scaling) + , RotationAlignment(Spawner->RotationAlignment) + , bRandomYaw(Spawner->bRandomYaw) + , RandomPitchAngle(Spawner->RandomPitchAngle) + , LocalPositionOffset(Spawner->LocalPositionOffset) + , LocalRotationOffset(Spawner->LocalRotationOffset) + , GlobalPositionOffset(Spawner->GlobalPositionOffset) +{ +} + +bool FVoxelBasicSpawnerProxy::CanSpawn( + const FRandomStream& RandomStream, + const FVoxelVector& Position, + const FVector& Normal, + const FVector& WorldUp) const +{ + if (bEnableHeightRestriction) + { + if (!HeightRestriction.Contains(Position.Z)) + { + return false; + } + + const float Center = (HeightRestriction.Min + HeightRestriction.Max) / 2.f; + const float Radius = HeightRestriction.Size() / 2.f; + const float Distance = FMath::Abs(Center - Position.Z); + const float Falloff = FMath::Min(HeightRestrictionFalloff, Radius); + + const float Alpha = FVoxelUtilities::SmoothFalloff(Distance, Radius - Falloff, Falloff); + + if (RandomStream.GetFraction() >= Alpha) + { + return false; + } + } + + const float Angle = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Normal, WorldUp))); + if (!GroundSlopeAngle.Contains(Angle)) + { + return false; + } + + return true; +} + +FMatrix FVoxelBasicSpawnerProxy::GetTransform( + const FRandomStream& Stream, + const FVector& Normal, + const FVector& WorldUp, + const FVector& Position) const +{ + FMatrix Matrix = FRotationTranslationMatrix(LocalRotationOffset, LocalPositionOffset); + + const FVector Scale = Scaling.GetScale(Stream); + + const float Yaw = bRandomYaw ? Stream.FRandRange(0, 360.0f) : 0.0f; + const float Pitch = Stream.FRandRange(0, RandomPitchAngle);; + Matrix *= FScaleRotationTranslationMatrix(Scale, FRotator(Pitch, Yaw, 0.0f), FVector::ZeroVector); + + switch (RotationAlignment) + { + case EVoxelBasicSpawnerRotation::AlignToSurface: + Matrix *= FRotationMatrix::MakeFromZ(Normal); + break; + case EVoxelBasicSpawnerRotation::AlignToWorldUp: + Matrix *= FRotationMatrix::MakeFromZ(WorldUp); + break; + case EVoxelBasicSpawnerRotation::RandomAlign: + Matrix *= FRotationMatrix::MakeFromZ(Stream.GetUnitVector()); + break; + default: + check(false); + } + + Matrix *= FTranslationMatrix(Position + GlobalPositionOffset); + + return Matrix; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.cpp new file mode 100644 index 00000000..34b2be04 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.cpp @@ -0,0 +1,178 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelHISMBuildTask.h" +#include "VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h" +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" + +#include "Engine/StaticMesh.h" + +static TAutoConsoleVariable CVarLogHISMBuildTimes( + TEXT("voxel.spawners.LogCullingTreeBuildTimes"), + 0, + TEXT("If true, will log all the HISM build times"), + ECVF_Default); + +TAutoConsoleVariable CVarRandomColorPerHISM( + TEXT("voxel.spawners.RandomColorPerHISM"), + 0, + TEXT("If true, will set the instance random to be a hash of the HISM pointer"), + ECVF_Default); + +// NOTE: start of HISM build tree refactor: +// https://github.com/Phyronnaz/VoxelPrivate/blob/fb7cd3edc0ca01dd6b7e7ab9b6908c7c31c1f290/Source/Voxel/Private/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.cpp#L41 + + +FVoxelHISMBuildTask::FVoxelHISMBuildTask( + UVoxelHierarchicalInstancedStaticMeshComponent* Component, + const TArray& Matrices) + : FVoxelAsyncWork(STATIC_FNAME("HISM Build Task"), 1e9, true) + , UniqueId(GetUniqueId()) + , CancelCounter(MakeVoxelShared()) + , MeshBox(Component->GetStaticMesh()->GetBounds().GetBox()) + , DesiredInstancesPerLeaf(Component->DesiredInstancesPerLeaf()) + , InstancedMeshManager(Component->Voxel_InstancedMeshManager) + , Component(Component) + , BuiltData(MakeVoxelShared()) +{ + check(Matrices.Num() > 0 && Matrices.Num() < 1e8); // If you have 100M instances something went terribly wrong + BuiltData->UniqueId = UniqueId; + { + VOXEL_SCOPE_COUNTER("Copy Matrices"); + BuiltData->BuiltInstancesMatrices = Matrices; + } +} + +void FVoxelHISMBuildTask::DoWork() +{ + const double StartTime = FPlatformTime::Seconds(); + const int32 NumInstances = BuiltData->BuiltInstancesMatrices.Num(); + check(NumInstances > 0); + + BuiltData->InstanceBuffer = MakeUnique(false); + { + VOXEL_ASYNC_SCOPE_COUNTER("AllocateInstances"); + BuiltData->InstanceBuffer->AllocateInstances( + NumInstances, + // TODO custom floats? + ONLY_UE_25_AND_HIGHER(0,) + EResizeBufferFlags::AllowSlackOnGrow | EResizeBufferFlags::AllowSlackOnReduce, + true); + } + + TArray CleanTransforms; + CleanTransforms.SetNumUninitialized(NumInstances); + + { + const bool bRandomColorPerHISM = CVarRandomColorPerHISM.GetValueOnAnyThread() != 0; + float RandomColor = 0.f; + if (bRandomColorPerHISM) + { + const uint32 RandomInt = FVoxelUtilities::MurmurHash64(uint64(Component.Get())); + RandomColor = *reinterpret_cast(&RandomInt); + } + + VOXEL_ASYNC_SCOPE_COUNTER("SetInstances"); + for (int32 InstanceIndex = 0; InstanceIndex < NumInstances; InstanceIndex++) + { + const FVoxelSpawnerMatrix& Matrix = BuiltData->BuiltInstancesMatrices[InstanceIndex]; + const FMatrix CleanMatrix = Matrix.GetCleanMatrix(); + + const float RandomId = bRandomColorPerHISM ? RandomColor : Matrix.GetRandomInstanceId(); + + CleanTransforms[InstanceIndex] = CleanMatrix; + BuiltData->InstanceBuffer->SetInstance(InstanceIndex, CleanMatrix, RandomId); + } + } + + // Only check if we're canceled before the BuildTree, as it's the true expensive operation here + // and if we've finished it, we might as well use the result + if (CancelCounter->GetValue() > 0) + { + return; + } + + TArray SortedInstances; + TArray InstanceReorderTable; + { + VOXEL_ASYNC_SCOPE_COUNTER("BuildTreeAnyThread"); + static_assert(sizeof(FVoxelSpawnerMatrix) == sizeof(FMatrix), ""); + TArray CustomDataFloats; + UHierarchicalInstancedStaticMeshComponent::BuildTreeAnyThread( + CleanTransforms, + // TODO investigate what this fancy stuff does + ONLY_UE_25_AND_HIGHER(CustomDataFloats,) + ONLY_UE_25_AND_HIGHER(0,) + MeshBox, + BuiltData->ClusterTree, + SortedInstances, + InstanceReorderTable, + BuiltData->OcclusionLayerNum, + DesiredInstancesPerLeaf, + false + ); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Build Reorder Table"); + BuiltData->InstancesToBuiltInstances = InstanceReorderTable; + BuiltData->BuiltInstancesToInstances.Init(-1, InstanceReorderTable.Num()); + for (int32 Index = 0; Index < InstanceReorderTable.Num(); Index++) + { + BuiltData->BuiltInstancesToInstances[InstanceReorderTable[Index]] = Index; + } + for (int32 Index : BuiltData->BuiltInstancesToInstances) check(Index != -1); + } + + // in-place sort the instances + { + VOXEL_ASYNC_SCOPE_COUNTER("Sort Instances"); + for (int32 FirstUnfixedIndex = 0; FirstUnfixedIndex < NumInstances; FirstUnfixedIndex++) + { + const int32 LoadFrom = SortedInstances[FirstUnfixedIndex]; + if (LoadFrom != FirstUnfixedIndex) + { + check(LoadFrom > FirstUnfixedIndex); + BuiltData->InstanceBuffer->SwapInstance(FirstUnfixedIndex, LoadFrom); + // Also keep up to date the transforms array + BuiltData->BuiltInstancesMatrices.Swap(FirstUnfixedIndex, LoadFrom); + const int32 SwapGoesTo = InstanceReorderTable[FirstUnfixedIndex]; + check(SwapGoesTo > FirstUnfixedIndex); + check(SortedInstances[SwapGoesTo] == FirstUnfixedIndex); + SortedInstances[SwapGoesTo] = LoadFrom; + InstanceReorderTable[LoadFrom] = SwapGoesTo; + InstanceReorderTable[FirstUnfixedIndex] = FirstUnfixedIndex; + SortedInstances[FirstUnfixedIndex] = FirstUnfixedIndex; + } + } + } + + const double EndTime = FPlatformTime::Seconds(); + if (CVarLogHISMBuildTimes.GetValueOnAnyThread() != 0) + { + LOG_VOXEL( + Log, + TEXT("Building the HISM culling tree took %2.2fms; Instances: %d"), + (EndTime - StartTime) * 1000, + NumInstances); + } + + auto PinnedInstancedMeshManager = InstancedMeshManager.Pin(); + if (PinnedInstancedMeshManager.IsValid()) + { + PinnedInstancedMeshManager->HISMBuildTaskCallback(Component, BuiltData); + FVoxelUtilities::DeleteOnGameThread_AnyThread(PinnedInstancedMeshManager); + } + +#undef CHECK_CANCEL +} + +uint32 FVoxelHISMBuildTask::GetPriority() const +{ + return 0; +} + +uint64 FVoxelHISMBuildTask::GetUniqueId() +{ + return UNIQUE_ID(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.h new file mode 100644 index 00000000..a97a7fac --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHISMBuildTask.h @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelAsyncWork.h" +#include "VoxelSpawners/VoxelSpawnerMatrix.h" +#include "Components/HierarchicalInstancedStaticMeshComponent.h" + +class FVoxelInstancedMeshManager; +class UVoxelHierarchicalInstancedStaticMeshComponent; + +extern TAutoConsoleVariable CVarRandomColorPerHISM; + +struct FVoxelHISMBuiltData +{ + uint64 UniqueId; + + TArray BuiltInstancesMatrices; + + TArray InstancesToBuiltInstances; + TArray BuiltInstancesToInstances; + + TUniquePtr InstanceBuffer; + TArray ClusterTree; + int32 OcclusionLayerNum = 0; +}; + +// Will auto delete +class FVoxelHISMBuildTask : public FVoxelAsyncWork +{ +public: + const uint64 UniqueId; + const TVoxelSharedRef CancelCounter; + + const FBox MeshBox; + const int32 DesiredInstancesPerLeaf; + const TVoxelWeakPtr InstancedMeshManager; + const TWeakObjectPtr Component; + + // Output + const TVoxelSharedRef BuiltData; + + FVoxelHISMBuildTask( + UVoxelHierarchicalInstancedStaticMeshComponent* Component, + const TArray& Matrices); + + //~ Begin FVoxelAsyncWork Interface + virtual void DoWork() override; + virtual uint32 GetPriority() const override; + //~ End FVoxelAsyncWork Interface + + static uint64 GetUniqueId(); + +private: + ~FVoxelHISMBuildTask() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.cpp new file mode 100644 index 00000000..f3035b15 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.cpp @@ -0,0 +1,914 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h" +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelSpawners/VoxelHISMBuildTask.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelMinimal.h" +#include "IVoxelPool.h" +#include "VoxelVector.h" + +#include "Async/Async.h" +#include "DrawDebugHelpers.h" +#include "TimerManager.h" + +#if ENGINE_MINOR_VERSION < 26 +#include "Engine/Private/InstancedStaticMesh.h" +#else +#include "Engine/InstancedStaticMesh.h" +#endif + +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel HISM Num Instances"), STAT_VoxelHISMComponent_NumInstances, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel HISM Num Physics Bodies"), STAT_VoxelHISMComponent_NumPhysicsBodies, STATGROUP_VoxelCounters); +DECLARE_DWORD_COUNTER_STAT(TEXT("Voxel HISM Num Floating Mesh Checked"), STAT_VoxelHISMComponent_NumFloatingMeshChecked, STATGROUP_VoxelCounters); +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelHISMMemory); + +static TAutoConsoleVariable CVarShowHISMCollisions( + TEXT("voxel.spawners.ShowCollisions"), + 0, + TEXT("If true, will show a debug point on HISM instances with collisions"), + ECVF_Default); + +static TAutoConsoleVariable CVarBVHDebugDepth( + TEXT("voxel.spawners.BVHDebugLevel"), + 0, + TEXT("If above or equal to 1, debug BVH nodes at that depth, 1 being the root"), + ECVF_Default); + +static const FMatrix EmptyMatrix = FTransform(FQuat::Identity, FVector::ZeroVector, FVector::ZeroVector).ToMatrixWithScale(); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelHierarchicalInstancedStaticMeshComponent::UVoxelHierarchicalInstancedStaticMeshComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if !UE_BUILD_SHIPPING + PrimaryComponentTick.bCanEverTick = true; +#endif + + Voxel_UpdateAllocatedMemory(); +} + +UVoxelHierarchicalInstancedStaticMeshComponent::~UVoxelHierarchicalInstancedStaticMeshComponent() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHISMMemory, Voxel_AllocatedMemory); + DEC_DWORD_STAT_BY(STAT_VoxelHISMComponent_NumInstances, Voxel_UnbuiltMatrices.Num()); + ensure(Voxel_InstanceBodies.Num() == 0); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_Init( + TVoxelWeakPtr Pool, + TVoxelWeakPtr InstancedMeshManager, + float VoxelSize, + const FIntVector& VoxelPosition, + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings) +{ + Voxel_Pool = Pool; + Voxel_InstancedMeshManager = InstancedMeshManager; + Voxel_VoxelSize = VoxelSize; + Voxel_VoxelPosition = VoxelPosition; + Voxel_MeshAndActorSettings = MeshAndActorSettings; + + auto& MeshSettings = MeshAndActorSettings.MeshSettings; + + Voxel_BuildDelay = MeshSettings.BuildDelay; + bDisableCollision = true; + +#define EQ(X) X = MeshSettings.X; + EQ(bAffectDynamicIndirectLighting) + EQ(bAffectDistanceFieldLighting) + EQ(bCastShadowAsTwoSided) + EQ(bReceivesDecals) + EQ(bUseAsOccluder) + EQ(BodyInstance) + EQ(LightingChannels) + EQ(bRenderCustomDepth) + EQ(CustomDepthStencilValue) +#undef EQ + InstanceStartCullDistance = MeshSettings.CullDistance.Min; + InstanceEndCullDistance = MeshSettings.CullDistance.Max; + CastShadow = MeshSettings.bCastShadow; + bCastDynamicShadow = MeshSettings.bCastShadow; + BodyInstance = MeshSettings.BodyInstance; + SetCustomNavigableGeometry(MeshSettings.CustomNavigableGeometry); + + SetStaticMesh(MeshAndActorSettings.Mesh.Get()); + + for (auto& It : MeshAndActorSettings.GetSectionsMaterials()) + { + SetMaterial(It.Key, It.Value); + } + + Voxel_SetRelativeLocation(); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_SetRelativeLocation() +{ + VOXEL_FUNCTION_COUNTER(); + + const auto InstancedMeshManager = Voxel_InstancedMeshManager.Pin(); + if (ensure(InstancedMeshManager.IsValid())) + { + const FVoxelInstancedMeshManagerSettings& Settings = InstancedMeshManager->Settings; + SetRelativeLocation(FVector(Voxel_VoxelPosition + *Settings.WorldOffset) * Settings.VoxelSize, false, nullptr, ETeleportType::TeleportPhysics); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelWeakPtr UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_AppendTransforms(const TArray& InTransforms, const FVoxelIntBox& InBounds) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(GetStaticMesh())) return nullptr; + if (!ensure(InTransforms.Num() > 0)) return nullptr; + + INC_DWORD_STAT_BY(STAT_VoxelHISMComponent_NumInstances, InTransforms.Num()); + + const int32 FirstInstanceIndex = Voxel_UnbuiltMatrices.Num(); + + const auto NewSection = MakeVoxelShared(); + NewSection->StartIndex = FirstInstanceIndex; + NewSection->Num = InTransforms.Num(); + Voxel_Sections.Emplace(NewSection); + + { + VOXEL_SCOPE_COUNTER("Append"); + Voxel_UnbuiltMatrices.Append(InTransforms); + } + + Voxel_PendingNewInstancesBounds.Add(InBounds); + + Voxel_ScheduleBuildTree(); + Voxel_UpdateAllocatedMemory(); + + return NewSection; +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_RemoveSection(const TVoxelWeakPtr& Section) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto PinnedSection = Section.Pin(); + if (!ensure(PinnedSection.IsValid())) return; + + const int32 StartIndex = PinnedSection->StartIndex; + const int32 Num = PinnedSection->Num; + + if (!ensure(Voxel_UnbuiltMatrices.IsValidIndex(StartIndex)) || + !ensure(StartIndex + Num <= Voxel_UnbuiltMatrices.Num())) return; + + if (!ensure(Voxel_Sections.Remove(PinnedSection) == 1)) return; + + ensure(PinnedSection.IsUnique()); + + const auto Fix = [&](int32& Index) + { + ensure(Voxel_UnbuiltMatrices.IsValidIndex(Index)); + if (Index >= StartIndex) + { + if (Index < StartIndex + Num) + { + Index = -1; + } + else + { + Index -= Num; + } + } + }; + + { + VOXEL_SCOPE_COUNTER("Fix Existing Sections"); + for (auto& ExistingSection : Voxel_Sections) + { + Fix(ExistingSection->StartIndex); + } + } + + { + VOXEL_SCOPE_COUNTER("Fix UnbuiltInstancesToClear"); + for (auto& Index : Voxel_UnbuiltInstancesToClear) + { + Fix(Index); + } + Voxel_UnbuiltInstancesToClear.RemoveSwap(-1); + } + + { + VOXEL_SCOPE_COUNTER("Fix bodies"); + for (auto& It : Voxel_InstanceBodies) + { + for (auto& Body : It.Value) + { + Fix(Body->InstanceBodyIndex); + if (Body->InstanceBodyIndex == -1) + { + DEC_DWORD_STAT(STAT_VoxelHISMComponent_NumPhysicsBodies); + Body->TermBody(); + delete Body; + Body = nullptr; + } + } + It.Value.RemoveSwap(nullptr); + } + } + + { + VOXEL_SCOPE_COUNTER("RemoveAt"); + Voxel_UnbuiltMatrices.RemoveAt(StartIndex, Num); + } + DEC_DWORD_STAT_BY(STAT_VoxelHISMComponent_NumInstances, Num); + + if (Voxel_UnbuiltMatrices.Num() == 0) + { + ensure(Voxel_UnbuiltInstancesToClear.Num() == 0); + // Having 0 instances creates a whole bunch of issues, so add one + Voxel_UnbuiltMatrices.Add(FVoxelSpawnerMatrix(EmptyMatrix)); + } + + // Generate a dummy ID to order deletions with tasks + const uint64 UniqueIdForOrder = FVoxelHISMBuildTask::GetUniqueId(); + Voxel_Mappings.DeletionsStack.Add({ UniqueIdForOrder, StartIndex, Num }); + +#if VOXEL_DEBUG && 0 + { + VOXEL_SCOPE_COUNTER("Debug"); + for (int32 UnbuiltIndex = 0; UnbuiltIndex < Voxel_UnbuiltMatrices.Num(); UnbuiltIndex++) + { + const int32 BuiltIndex = Voxel_Mappings.GetBuiltIndex(UnbuiltIndex); + if (BuiltIndex == -1) continue; + ensure(Voxel_UnbuiltMatrices[UnbuiltIndex] == Voxel_BuiltMatrices[BuiltIndex]); + } + for (int32 BuildIndex = 0; BuildIndex < Voxel_BuiltMatrices.Num(); BuildIndex++) + { + const int32 UnbuiltIndex = Voxel_Mappings.GetUnbuiltIndex(BuildIndex); + if (UnbuiltIndex == -1) continue; + ensure(Voxel_UnbuiltMatrices[UnbuiltIndex] == Voxel_BuiltMatrices[BuildIndex]); + } + } +#endif + + Voxel_ScheduleBuildTree(); + Voxel_UpdateAllocatedMemory(); +} + +bool UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_IsEmpty() const +{ + return Voxel_UnbuiltMatrices.Num() == 0 || (Voxel_UnbuiltMatrices.Num() == 1 && Voxel_UnbuiltMatrices[0] == FVoxelSpawnerMatrix(EmptyMatrix)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_StartBuildTree() +{ + VOXEL_FUNCTION_COUNTER(); + + ensure((Voxel_TaskUniqueId != 0) == Voxel_TaskCancelCounterPtr.IsValid()); + + if (!ensure(GetStaticMesh())) return; + if (!ensure(Voxel_UnbuiltMatrices.Num() > 0)) return; + + auto Pool = Voxel_Pool.Pin(); + if (!ensure(Pool.IsValid())) return; + + if (Voxel_TaskCancelCounterPtr.IsValid()) + { + Voxel_TaskCancelCounterPtr->Increment(); + Voxel_TaskCancelCounterPtr.Reset(); + } + + auto* Task = new FVoxelHISMBuildTask(this, Voxel_UnbuiltMatrices); + Voxel_TaskUniqueId = Task->UniqueId; + Voxel_TaskCancelCounterPtr = Task->CancelCounter; + Pool->QueueTask(EVoxelTaskType::HISMBuild, Task); + + ensure(!Voxel_TaskIdToNewInstancesBounds.Contains(Voxel_TaskUniqueId)); + // Not true when removing instances ensure(Voxel_PendingNewInstancesBounds.Num() > 0); + Voxel_TaskIdToNewInstancesBounds.Add(Voxel_TaskUniqueId, MoveTemp(Voxel_PendingNewInstancesBounds)); + + Voxel_UpdateAllocatedMemory(); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_FinishBuilding(FVoxelHISMBuiltData& BuiltData) +{ + VOXEL_FUNCTION_COUNTER(); + + ensure((Voxel_TaskUniqueId != 0) == Voxel_TaskCancelCounterPtr.IsValid()); + + if (Voxel_TaskUniqueId == 0) + { + // Task is too late, newer one already completed + ensure(!Voxel_TaskIdToNewInstancesBounds.Contains(BuiltData.UniqueId)); + ensure(Voxel_UnbuiltInstancesToClear.Num() == 0); + return; + } + + ensure(Voxel_TaskUniqueId >= BuiltData.UniqueId); // Else the task time traveled + const bool bIsLatestTask = BuiltData.UniqueId == Voxel_TaskUniqueId; + + for (int32 UnbuiltIndex : Voxel_UnbuiltInstancesToClear) + { + if (!BuiltData.InstancesToBuiltInstances.IsValidIndex(UnbuiltIndex)) + { + ensure(!bIsLatestTask); + continue; + } + const int32 BuiltIndex = BuiltData.InstancesToBuiltInstances[UnbuiltIndex]; + BuiltData.InstanceBuffer->SetInstance(BuiltIndex, EmptyMatrix, 0); + BuiltData.BuiltInstancesMatrices[BuiltIndex] = FVoxelSpawnerMatrix(EmptyMatrix); + } + + if (bIsLatestTask) + { + Voxel_TaskUniqueId = 0; + Voxel_TaskCancelCounterPtr.Reset(); + Voxel_UnbuiltInstancesToClear.Reset(); + } + else + { + // Do not reset the task id nor cancel it as this is not the right one yet + } + + const int32 NumInstances = BuiltData.InstanceBuffer->GetNumInstances(); + check(NumInstances > 0); + check(NumInstances == BuiltData.BuiltInstancesMatrices.Num()); + check(NumInstances == BuiltData.InstancesToBuiltInstances.Num()); + check(NumInstances == BuiltData.BuiltInstancesToInstances.Num()); + // Not true with timer delays ensure(!bIsLatestTask || NumInstances == Voxel_UnbuiltMatrices.Num()); + + Voxel_BuiltMatrices = MoveTemp(BuiltData.BuiltInstancesMatrices); + + Voxel_Mappings.InstancesToBuiltInstances = MoveTemp(BuiltData.InstancesToBuiltInstances); + Voxel_Mappings.BuiltInstancesToInstances = MoveTemp(BuiltData.BuiltInstancesToInstances); + + // Remove any old deletions + // < safe as we are generating a dummy id when deleting + Voxel_Mappings.DeletionsStack.RemoveAll([&](auto& Deletion) { return Deletion.TaskUniqueId < BuiltData.UniqueId; }); + + constexpr bool bRequireCPUAccess = true; + if (!PerInstanceRenderData.IsValid() || PerInstanceRenderData->InstanceBuffer.RequireCPUAccess != bRequireCPUAccess) + { + if (ensure(PerInstanceRenderData.IsValid())) + { + VOXEL_SCOPE_COUNTER("ReleasePerInstanceRenderData"); + ReleasePerInstanceRenderData(); + } + + VOXEL_SCOPE_COUNTER("InitPerInstanceRenderData"); + InitPerInstanceRenderData(true, BuiltData.InstanceBuffer.Get(), bRequireCPUAccess); + } + else + { + VOXEL_SCOPE_COUNTER("UpdateFromPreallocatedData"); + PerInstanceRenderData->UpdateFromPreallocatedData(*BuiltData.InstanceBuffer); + } + + { + VOXEL_SCOPE_COUNTER("AcceptPrebuiltTree"); + AcceptPrebuiltTree(BuiltData.ClusterTree, BuiltData.OcclusionLayerNum, NumInstances); + } + + for (auto It = Voxel_TaskIdToNewInstancesBounds.CreateIterator(); It; ++It) + { + if (It.Key() <= BuiltData.UniqueId) + { + for (auto& Chunk : It.Value()) + { + Voxel_RefreshPhysics(Chunk); + } + It.RemoveCurrent(); + } + } + + Voxel_UpdateAllocatedMemory(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_EnablePhysics(const FVoxelIntBox Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(!Voxel_InstanceBodies.Contains(Chunk))) return; + auto& Bodies = Voxel_InstanceBodies.Add(Chunk); + + Voxel_EnablePhysicsImpl(Chunk, Bodies); + + Voxel_UpdateAllocatedMemory(); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_DisablePhysics(const FVoxelIntBox Chunk) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(Voxel_InstanceBodies.Contains(Chunk))) return; + auto Bodies = Voxel_InstanceBodies.FindAndRemoveChecked(Chunk); + + Voxel_DisablePhysicsImpl(Bodies); + + Voxel_UpdateAllocatedMemory(); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_RefreshPhysics(const FVoxelIntBox& BoundsToUpdate) +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto& It : Voxel_InstanceBodies) + { + const auto& Chunk = It.Key; + auto& Bodies = It.Value; + if (Chunk.Intersect(BoundsToUpdate)) + { + Voxel_DisablePhysicsImpl(Bodies); + ensure(Bodies.Num() == 0); + Voxel_EnablePhysicsImpl(Chunk, Bodies); + } + } + + Voxel_UpdateAllocatedMemory(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSpawnerTransforms UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_RemoveInstancesInArea( + const FVoxelIntBox& VoxelBounds, + const FVoxelConstDataAccelerator* Accelerator, + EVoxelSpawnerActorSpawnType SpawnType) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelSpawnerTransforms OutTransforms; + OutTransforms.TransformsOffset = Voxel_VoxelPosition; + + check(SpawnType == EVoxelSpawnerActorSpawnType::All || Accelerator); + + TArray BuiltIndicesToClear; + FVoxelIntBoxWithValidity BoundsToUpdate; + + const auto Lambda = [&](const int32 BuiltIndex) + { + if (Voxel_Mappings.GetUnbuiltIndex(BuiltIndex) == -1) + { + // Deleted + return; + } + + INC_DWORD_STAT_BY(STAT_VoxelHISMComponent_NumFloatingMeshChecked, 1); + + const FVoxelSpawnerMatrix Matrix = Voxel_BuiltMatrices[BuiltIndex]; + const FTransform LocalInstanceTransform = FTransform(Matrix.GetCleanMatrix()); + + if (LocalInstanceTransform.GetScale3D().IsNearlyZero()) + { + return; + } + + const FVoxelVector GlobalVoxelPosition = Voxel_GetGlobalVoxelPosition(Matrix); + if (!VoxelBounds.Contains(FVoxelIntBox(GlobalVoxelPosition))) + { + return; + } + + if (SpawnType == EVoxelSpawnerActorSpawnType::All || Accelerator->GetFloatValue(GlobalVoxelPosition.X, GlobalVoxelPosition.Y, GlobalVoxelPosition.Z, 0) > 0) + { + OutTransforms.Matrices.Add(Matrix); + BuiltIndicesToClear.Add(BuiltIndex); + BoundsToUpdate += FVoxelIntBox(GlobalVoxelPosition); + } + }; + if (!ensure(ClusterTreePtr.IsValid())) return {}; + Voxel_IterateInstancesInBounds(*ClusterTreePtr, Voxel_VoxelBoundsToLocal(VoxelBounds), Lambda); + + if (BuiltIndicesToClear.Num() > 0) + { + Voxel_SetInstancesScaleToZero(BuiltIndicesToClear); + Voxel_RemoveInstancesFromSections(BuiltIndicesToClear); + Voxel_RefreshPhysics(BoundsToUpdate.GetBox()); + } + + return OutTransforms; +} + +bool UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_RemoveInstanceByIndex(int32 InstanceIndex, FVoxelSpawnerTransform& OutTransform) +{ + VOXEL_FUNCTION_COUNTER(); + + const int32 BuiltIndex = Voxel_Mappings.GetBuiltIndex(InstanceIndex); + if (BuiltIndex == -1) + { + return false; + } + + const auto Matrix = Voxel_BuiltMatrices[BuiltIndex]; + + OutTransform.TransformOffset = Voxel_VoxelPosition; + OutTransform.Matrix = Matrix; + + Voxel_SetInstancesScaleToZero({ BuiltIndex }); + Voxel_RemoveInstancesFromSections({ BuiltIndex }); + + const FVoxelIntBox MatrixBounds = FVoxelIntBox(Voxel_GetGlobalVoxelPosition(Matrix)); + + for (auto& It : Voxel_InstanceBodies) + { + const auto& Chunk = It.Key; + auto& Bodies = It.Value; + if (Chunk.Intersect(MatrixBounds)) + { + for (int32 BodyIndex = 0; BodyIndex < Bodies.Num(); BodyIndex++) + { + auto* Body = Bodies[BodyIndex]; + if (Body->InstanceBodyIndex == InstanceIndex) + { + DEC_DWORD_STAT(STAT_VoxelHISMComponent_NumPhysicsBodies); + Body->TermBody(); + delete Body; + Bodies.RemoveAtSwap(BodyIndex); + return true; + } + } + } + } + +#if VOXEL_DEBUG + for (auto& It : Voxel_InstanceBodies) + { + for (auto* Body : It.Value) + { + ensure(Body->InstanceBodyIndex != InstanceIndex); + } + } +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::OnComponentDestroyed(bool bDestroyingHierarchy) +{ + VOXEL_FUNCTION_COUNTER(); + + ensure((Voxel_TaskUniqueId != 0) == Voxel_TaskCancelCounterPtr.IsValid()); + + Voxel_TaskUniqueId = 0; + if (Voxel_TaskCancelCounterPtr.IsValid()) + { + Voxel_TaskCancelCounterPtr->Increment(); + Voxel_TaskCancelCounterPtr.Reset(); + } + + DEC_DWORD_STAT_BY(STAT_VoxelHISMComponent_NumInstances, Voxel_UnbuiltMatrices.Num()); + + // Free up memory + Voxel_UnbuiltMatrices.Empty(); + Voxel_BuiltMatrices.Empty(); + Voxel_Mappings.InstancesToBuiltInstances.Empty(); + Voxel_Mappings.BuiltInstancesToInstances.Empty(); + Voxel_Mappings.DeletionsStack.Empty(); + Voxel_UnbuiltInstancesToClear.Empty(); + Voxel_TaskIdToNewInstancesBounds.Empty(); + Voxel_PendingNewInstancesBounds.Empty(); + + Super::OnComponentDestroyed(bDestroyingHierarchy); +} + +bool UVoxelHierarchicalInstancedStaticMeshComponent::ShouldCreatePhysicsState() const +{ + return UInstancedStaticMeshComponent::ShouldCreatePhysicsState(); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::OnDestroyPhysicsState() +{ + VOXEL_FUNCTION_COUNTER(); + + Super::OnDestroyPhysicsState(); + + for (auto& It : Voxel_InstanceBodies) + { + Voxel_DisablePhysicsImpl(It.Value); + } + Voxel_InstanceBodies.Empty(); +} + +inline void DrawBVH(UWorld* World, const FColor& Color, const TArray& ClusterTree, int32 Depth, int32 Index = 0) +{ + if (ClusterTree.Num() == 0) return; + + const auto& Node = ClusterTree[Index]; + if (Depth <= 0 || Node.FirstChild == -1) + { + const FBox Box(Node.BoundMin, Node.BoundMax); + DrawDebugBox(World, Box.GetCenter(), Box.GetExtent(), Color, false, World->GetDeltaSeconds() * 1.5f); + return; + } + + for (int32 ChildIndex = Node.FirstChild; ChildIndex <= Node.LastChild; ChildIndex++) + { + DrawBVH(World, Color, ClusterTree, Depth - 1, ChildIndex); + } +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + VOXEL_FUNCTION_COUNTER(); + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + if (CVarShowHISMCollisions.GetValueOnGameThread()) + { + if (!GetStaticMesh()) return; + + const FBox MeshBox = GetStaticMesh()->GetBounds().GetBox(); + for (auto& It : Voxel_InstanceBodies) + { + for (auto* Body : It.Value) + { + const FBox BodyBox = MeshBox.TransformBy(Body->GetUnrealWorldTransform().GetScaled(Body->Scale3D)); + DrawDebugBox( + GetWorld(), + BodyBox.GetCenter(), + BodyBox.GetExtent(), + FColor::Red, + false, + DeltaTime * 2); + } + } + } + + if (CVarBVHDebugDepth.GetValueOnGameThread() && ClusterTreePtr.IsValid()) + { + const int32 Depth = CVarBVHDebugDepth.GetValueOnGameThread() - 1; + DrawBVH(GetWorld(), FColor(FVoxelUtilities::MurmurHash64(uint64(this))), *ClusterTreePtr, Depth); + } + + if (CVarRandomColorPerHISM.GetValueOnGameThread() && !Voxel_DebugMaterial) + { + Voxel_DebugMaterial = LoadObject(nullptr, TEXT("/Voxel/MaterialHelpers/Debug_UsePerInstanceRandomAsColor")); + for (int32 Index = 0; Index < GetNumMaterials(); Index++) + { + SetMaterial(Index, Voxel_DebugMaterial); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelHierarchicalInstancedStaticMeshComponent::FVoxelMappings::GetBuiltIndex(int32 UnbuiltIndex) const +{ + // Reverse back the changes + for (int32 StackIndex = DeletionsStack.Num() - 1; StackIndex >= 0; StackIndex--) + { + auto& Deletion = DeletionsStack[StackIndex]; + if (Deletion.StartIndex <= UnbuiltIndex) // If we were affected by the deletion + { + UnbuiltIndex += Deletion.Num; // Translate back + } + } + if (InstancesToBuiltInstances.IsValidIndex(UnbuiltIndex)) + { + return InstancesToBuiltInstances[UnbuiltIndex]; + } + else + { + return -1; + } +} + +int32 UVoxelHierarchicalInstancedStaticMeshComponent::FVoxelMappings::GetUnbuiltIndex(const int32 BuiltIndex) const +{ + if (!BuiltInstancesToInstances.IsValidIndex(BuiltIndex)) + { + return -1; + } + + int32 UnbuiltIndex = BuiltInstancesToInstances[BuiltIndex]; + + // The UnbuiltIndex is the one of the last tree build: need to apply deletions to it + for (int32 StackIndex = 0; StackIndex < DeletionsStack.Num(); StackIndex++) + { + auto& Deletion = DeletionsStack[StackIndex]; + if (Deletion.StartIndex <= UnbuiltIndex) + { + if (UnbuiltIndex < Deletion.StartIndex + Deletion.Num) + { + // Got deleted + return -1; + } + + // Apply the deletion + UnbuiltIndex -= Deletion.Num; + } + } + + // ensureVoxelSlowNoSideEffects(GetBuiltIndex(UnbuiltIndex) == BuiltIndex); + return UnbuiltIndex; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_UpdateAllocatedMemory() +{ + VOXEL_FUNCTION_COUNTER(); + + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHISMMemory, Voxel_AllocatedMemory); + + Voxel_AllocatedMemory = 0; + Voxel_AllocatedMemory += Voxel_UnbuiltMatrices.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_BuiltMatrices.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_Mappings.InstancesToBuiltInstances.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_Mappings.BuiltInstancesToInstances.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_Mappings.DeletionsStack.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_UnbuiltInstancesToClear.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_TaskIdToNewInstancesBounds.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_PendingNewInstancesBounds.GetAllocatedSize(); + Voxel_AllocatedMemory += Voxel_InstanceBodies.GetAllocatedSize(); + + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHISMMemory, Voxel_AllocatedMemory); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_ScheduleBuildTree() +{ + VOXEL_FUNCTION_COUNTER(); + + if (Voxel_BuildDelay <= 0) + { + Voxel_StartBuildTree(); + } + else + { + auto& TimerManager = GetWorld()->GetTimerManager(); + TimerManager.SetTimer( + Voxel_TimerHandle, + this, + &UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_StartBuildTree, + Voxel_BuildDelay, + false); + } +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_RemoveInstancesFromSections(const TArray& BuiltIndices) +{ + VOXEL_FUNCTION_COUNTER(); + +#if VOXEL_DEBUG && 0 + for (int32 Index = 0; Index < Voxel_Sections.Num() - 1; Index++) + { + ensure(Voxel_Sections[Index]->StartIndex < Voxel_Sections[Index + 1]->StartIndex); + ensure(Voxel_Sections[Index]->StartIndex + Voxel_Sections[Index]->Num == Voxel_Sections[Index + 1]->StartIndex); + } + ensure(Voxel_UnbuiltMatrices.Num() == 0 || Voxel_Sections.Last()->StartIndex + Voxel_Sections.Last()->Num == Voxel_UnbuiltMatrices.Num()); +#endif + + for (int32 BuiltIndex : BuiltIndices) + { + const int32 UnbuiltIndex = Voxel_Mappings.GetUnbuiltIndex(BuiltIndex); + if (!ensure(UnbuiltIndex != -1)) continue; + + const int32 SectionIndex = Algo::UpperBoundBy(Voxel_Sections, UnbuiltIndex, [](auto& Section) { return Section->StartIndex; }); + + auto& Section = *Voxel_Sections[SectionIndex - 1]; + + if (!ensure(Section.StartIndex <= UnbuiltIndex && UnbuiltIndex < Section.StartIndex + Section.Num)) continue; + + Section.RemovedIndices.Add(UnbuiltIndex - Section.StartIndex); + } +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_SetInstancesScaleToZero(const TArray& BuiltIndices) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto InstanceBuffer = PerInstanceRenderData.IsValid() ? PerInstanceRenderData->InstanceBuffer_GameThread : nullptr; + ensure(InstanceBuffer.IsValid() || Voxel_TaskUniqueId > 0); + for (int32 BuiltIndex : BuiltIndices) + { + const int32 UnbuiltIndex = Voxel_Mappings.GetUnbuiltIndex(BuiltIndex); + check(UnbuiltIndex >= 0); + + if (InstanceBuffer.IsValid()) + { + InstanceBuffer->SetInstance(BuiltIndex, EmptyMatrix, 0); + } + ensure(Voxel_BuiltMatrices[BuiltIndex] == Voxel_UnbuiltMatrices[UnbuiltIndex]); + Voxel_BuiltMatrices[BuiltIndex] = FVoxelSpawnerMatrix(EmptyMatrix); + Voxel_UnbuiltMatrices[UnbuiltIndex] = FVoxelSpawnerMatrix(EmptyMatrix); + + if (Voxel_TaskUniqueId > 0) + { + Voxel_UnbuiltInstancesToClear.Add(UnbuiltIndex); + } + } + + if (InstanceBuffer.IsValid()) + { + ENQUEUE_RENDER_COMMAND(UVoxelHierarchicalInstancedStaticMeshComponent_UpdateBuffer)( + [PerInstanceRenderData = PerInstanceRenderData](FRHICommandListImmediate& RHICmdList) + { + PerInstanceRenderData->InstanceBuffer.InstanceData = PerInstanceRenderData->InstanceBuffer_GameThread; + PerInstanceRenderData->InstanceBuffer.UpdateRHI(); + } + ); + MarkRenderStateDirty(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_EnablePhysicsImpl(const FVoxelIntBox& Chunk, TArray& OutBodies) const +{ + VOXEL_FUNCTION_COUNTER(); + ensure(bPhysicsStateCreated); + + // We want this function to be const + auto* const Component = const_cast(static_cast(this)); + + UBodySetup* const BodySetup = Component->GetBodySetup(); + if (!ensure(BodySetup)) return; + + const FVoxelIntBox LocalBounds = Voxel_VoxelBoundsToLocal(Chunk); + + TArray Transforms; + const auto Lambda = [&](const int32 BuiltIndex) + { + const FTransform LocalInstanceTransform = FTransform(Voxel_BuiltMatrices[BuiltIndex].GetCleanMatrix()); + const FTransform GlobalInstanceTransform = LocalInstanceTransform * GetComponentTransform(); + + if (GlobalInstanceTransform.GetScale3D().IsNearlyZero() || + !LocalBounds.ContainsFloat(LocalInstanceTransform.GetTranslation())) + { + return; + } + + FBodyInstance* Instance = new FBodyInstance(); + INC_DWORD_STAT(STAT_VoxelHISMComponent_NumPhysicsBodies); + + Instance->CopyBodyInstancePropertiesFrom(&BodyInstance); + Instance->bAutoWeld = false; + + // Make sure we never enable bSimulatePhysics for ISMComps + Instance->bSimulatePhysics = false; + + // Set the body index to the UNBUILT index + const int32 UnbuiltIndex = Voxel_Mappings.GetUnbuiltIndex(BuiltIndex); + if (UnbuiltIndex == -1) + { + // Deleted + return; + } + Instance->InstanceBodyIndex = UnbuiltIndex; + ensure(Voxel_UnbuiltMatrices[Instance->InstanceBodyIndex] == Voxel_BuiltMatrices[BuiltIndex]); + + OutBodies.Add(Instance); + Transforms.Add(GlobalInstanceTransform); + }; + + if (!ensure(ClusterTreePtr.IsValid())) return; + Voxel_IterateInstancesInBounds(*ClusterTreePtr, LocalBounds, Lambda); + + if (OutBodies.Num() == 0) return; + + VOXEL_SCOPE_COUNTER("InitStaticBodies"); + FBodyInstance::InitStaticBodies(OutBodies, Transforms, BodySetup, Component, GetWorld()->GetPhysicsScene()); +} + +void UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_DisablePhysicsImpl(TArray& Bodies) const +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto* Body : Bodies) + { + DEC_DWORD_STAT(STAT_VoxelHISMComponent_NumPhysicsBodies); + Body->TermBody(); + delete Body; + } + Bodies.Reset(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshManager.cpp new file mode 100644 index 00000000..814999c3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshManager.cpp @@ -0,0 +1,392 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h" +#include "VoxelSpawners/VoxelSpawnerActor.h" +#include "VoxelEvents/VoxelEventManager.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelMessages.h" +#include "VoxelWorld.h" + +#include "GameFramework/Actor.h" +#include "Engine/StaticMesh.h" + +DECLARE_DWORD_COUNTER_STAT(TEXT("InstancedMeshManager Num Spawned Actors"), STAT_FVoxelInstancedMeshManager_NumSpawnedActors, STATGROUP_VoxelCounters); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num HISM"), STAT_FVoxelInstancedMeshManager_NumHISM, STATGROUP_VoxelCounters); + +FVoxelInstancedMeshManagerSettings::FVoxelInstancedMeshManagerSettings( + const AVoxelWorld* World, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& EventManager) + : ComponentsOwner(const_cast(World)) + , WorldOffset(World->GetWorldOffsetPtr()) + , Pool(Pool) + , EventManager(EventManager) + , HISMChunkSize(FMath::Max(32, World->HISMChunkSize)) + , CollisionDistanceInChunks(FMath::Max(0, World->SpawnersCollisionDistanceInVoxel / int32(CollisionChunkSize))) + , VoxelSize(World->VoxelSize) + , MaxNumberOfInstances(World->MaxNumberOfFoliageInstances) + , OnMaxInstanceReached(FSimpleDelegate::CreateWeakLambda(const_cast(World), [=]() + { + if (World->OnMaxFoliageInstancesReached.IsBound()) + { + World->OnMaxFoliageInstancesReached.Broadcast(); + } + else + { + FVoxelMessages::Error(FString::Printf(TEXT("Max number of foliage instances reached: %lli"), MaxNumberOfInstances), World); + } + })) +{ +} + +FVoxelInstancedMeshManager::FVoxelInstancedMeshManager(const FVoxelInstancedMeshManagerSettings& Settings) + : Settings(Settings) +{ +} + +TVoxelSharedRef FVoxelInstancedMeshManager::Create(const FVoxelInstancedMeshManagerSettings& Settings) +{ + return MakeShareable(new FVoxelInstancedMeshManager(Settings)); +} + +void FVoxelInstancedMeshManager::Destroy() +{ + StopTicking(); + + // Needed for RegenerateSpawners + for (const auto& HISM : HISMs) + { + if (HISM.IsValid()) + { + HISM->DestroyComponent(); + } + } + DEC_DWORD_STAT_BY(STAT_FVoxelInstancedMeshManager_NumHISM, HISMs.Num()); + HISMs.Empty(); + + MeshSettingsToChunks.Empty(); +} + +FVoxelInstancedMeshManager::~FVoxelInstancedMeshManager() +{ + ensure(IsInGameThread()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelInstancedMeshInstancesRef FVoxelInstancedMeshManager::AddInstances( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransforms& Transforms, + const FVoxelIntBox& Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!ensure(Transforms.Matrices.Num() > 0)) return {}; + + if (NumInstances > Settings.MaxNumberOfInstances) + { + if (!bMaxNumInstancesReachedFired) + { + bMaxNumInstancesReachedFired = true; + Settings.OnMaxInstanceReached.ExecuteIfBound(); + } + return {}; + } + + NumInstances += Transforms.Matrices.Num(); + + const FIntVector Position = ComputeTransformsOffset(Bounds); + ensure(Transforms.TransformsOffset == Position); + + FHISMChunks& Chunks = MeshSettingsToChunks.FindOrAdd(MeshAndActorSettings); + const FIntVector ChunkKey = FVoxelUtilities::DivideFloor(Position, Settings.HISMChunkSize); + + FHISMChunk& Chunk = Chunks.Chunks.FindOrAdd(ChunkKey); + Chunk.Bounds += Bounds; + + auto& HISM = Chunk.HISM; + if (!HISM.IsValid()) + { + HISM = CreateHISM(MeshAndActorSettings, Position); + HISMs.Add(HISM); + INC_DWORD_STAT(STAT_FVoxelInstancedMeshManager_NumHISM); + } + + if (!ensure(HISM.IsValid())) + { + // Happens sometimes + return {}; + } + + FVoxelInstancedMeshInstancesRef Ref; + Ref.HISM = HISM; + Ref.Section = HISM->Voxel_AppendTransforms(Transforms.Matrices, Bounds); + if (ensure(Ref.IsValid())) + { + Ref.NumInstances = Transforms.Matrices.Num(); + } + + return Ref; +} + +void FVoxelInstancedMeshManager::RemoveInstances(FVoxelInstancedMeshInstancesRef Ref) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + NumInstances -= Ref.NumInstances; + ensure(NumInstances >= 0); + + if (!ensure(Ref.IsValid())) return; + if (!/*ensure*/(Ref.HISM.IsValid())) return; // Can be raised due to DeleteTickable being delayed + + Ref.HISM->Voxel_RemoveSection(Ref.Section); + + if (Ref.HISM->Voxel_IsEmpty()) + { + ensure(HISMs.Remove(Ref.HISM) == 1); + DEC_DWORD_STAT(STAT_FVoxelInstancedMeshManager_NumHISM); + + LOG_VOXEL(Verbose, TEXT("Instanced Mesh Manager: Removing HISM for mesh %s"), Ref.HISM->GetStaticMesh() ? *Ref.HISM->GetStaticMesh()->GetPathName() : TEXT("NULL")); + + // TODO pooling? + Ref.HISM->DestroyComponent(); + } +} + +const TArray& FVoxelInstancedMeshManager::GetRemovedIndices(FVoxelInstancedMeshInstancesRef Ref) +{ + const auto Pinned = Ref.Section.Pin(); + if (ensure(Pinned.IsValid())) + { + // Fine to return a ref, as Section is only used on the game thread so it won't be deleted + return Pinned->RemovedIndices; + } + else + { + static const TArray EmptyArray; + return EmptyArray; + } +} + +AVoxelSpawnerActor* FVoxelInstancedMeshManager::SpawnActor( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransform& Transform) const +{ + VOXEL_FUNCTION_COUNTER(); + INC_DWORD_STAT(STAT_FVoxelInstancedMeshManager_NumSpawnedActors); + + if (!MeshAndActorSettings.ActorSettings.ActorClass) return nullptr; + + auto* ComponentsOwner = Settings.ComponentsOwner.Get(); + if (!ComponentsOwner) return nullptr; + + auto* World = ComponentsOwner->GetWorld(); + if (!World) return nullptr; + + if (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview) return nullptr; + + const FTransform LocalTransform(Transform.Matrix.GetCleanMatrix().ConcatTranslation(FVector(Transform.TransformOffset + *Settings.WorldOffset) * Settings.VoxelSize)); + const FTransform GlobalTransform = LocalTransform * ComponentsOwner->GetTransform(); + + FActorSpawnParameters SpawnParameters; + SpawnParameters.bDeferConstruction = true; + + auto* Actor = World->SpawnActor(MeshAndActorSettings.ActorSettings.ActorClass, SpawnParameters); + if (!Actor) return nullptr; + + Actor->InitialLifeSpan = MeshAndActorSettings.ActorSettings.Lifespan; + Actor->FinishSpawning(GlobalTransform); + + Actor->SetStaticMesh(MeshAndActorSettings.Mesh.Get(), MeshAndActorSettings.GetSectionsMaterials(), MeshAndActorSettings.ActorSettings.BodyInstance); + Actor->SetInstanceRandom(Transform.Matrix.GetRandomInstanceId()); + + return Actor; +} + +void FVoxelInstancedMeshManager::SpawnActors( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransforms& Transforms, + TArray& OutActors) const +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto& Matrix : Transforms.Matrices) + { + auto* Actor = SpawnActor(MeshAndActorSettings, { Transforms.TransformsOffset, Matrix }); + if (!Actor) return; + OutActors.Emplace(Actor); + } +} + +void FVoxelInstancedMeshManager::SpawnActorsInArea( + const FVoxelIntBox& Bounds, + const FVoxelData& Data, + EVoxelSpawnerActorSpawnType SpawnType, + TArray& OutActors) const +{ + VOXEL_FUNCTION_COUNTER(); + + const auto TransformsMap = RemoveInstancesInArea(Bounds, Data, SpawnType); + + for (auto& It : TransformsMap) + { + for (const auto& Transforms : It.Value) + { + SpawnActors(It.Key, Transforms, OutActors); + } + } +} + +TMap> FVoxelInstancedMeshManager::RemoveInstancesInArea( + const FVoxelIntBox& Bounds, + const FVoxelData& Data, + EVoxelSpawnerActorSpawnType SpawnType) const +{ + VOXEL_FUNCTION_COUNTER(); + + const auto ExtendedBounds = Bounds.Extend(1); // As we are accessing floats, they can be between Max - 1 and Max + + TMap> TransformsMap; + + FVoxelReadScopeLock Lock(Data, ExtendedBounds, "SpawnActorsInArea"); + + const TUniquePtr Accelerator = + SpawnType == EVoxelSpawnerActorSpawnType::All + ? nullptr + : MakeUnique(Data, ExtendedBounds); + + for (auto& MeshSettingsIt : MeshSettingsToChunks) + { + TArray& TransformsArray = TransformsMap.FindOrAdd(MeshSettingsIt.Key); + for (auto& ChunkIt : MeshSettingsIt.Value.Chunks) + { + const FHISMChunk& Chunk = ChunkIt.Value; + if (Chunk.HISM.IsValid() && Chunk.Bounds.IsValid() && Chunk.Bounds.GetBox().Intersect(Bounds)) + { + auto Transforms = Chunk.HISM->Voxel_RemoveInstancesInArea(Bounds, Accelerator.Get(), SpawnType); + TransformsArray.Emplace(MoveTemp(Transforms)); + } + } + } + + return TransformsMap; +} + +AVoxelSpawnerActor* FVoxelInstancedMeshManager::SpawnActorByIndex(UVoxelHierarchicalInstancedStaticMeshComponent* Component, int32 InstanceIndex) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(Component)) + { + return nullptr; + } + + // Not one of ours + if (!ensure(HISMs.Contains(Component))) + { + return nullptr; + } + + FVoxelSpawnerTransform Transform; + if (!Component->Voxel_RemoveInstanceByIndex(InstanceIndex, Transform)) + { + return nullptr; + } + + ensure(Component->Voxel_GetMeshAndActorSettings().Mesh == Component->GetStaticMesh()); + + return SpawnActor(Component->Voxel_GetMeshAndActorSettings(), Transform); +} + +void FVoxelInstancedMeshManager::RecomputeMeshPositions() +{ + VOXEL_FUNCTION_COUNTER(); + + for (auto& HISM : HISMs) + { + if (ensure(HISM.IsValid())) + { + HISM->Voxel_SetRelativeLocation(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelInstancedMeshManager::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + FQueuedBuildCallback Callback; + while (HISMBuiltDataQueue.Dequeue(Callback)) + { + auto* HISM = Callback.Component.Get(); + if (!HISM) continue; + + HISM->Voxel_FinishBuilding(*Callback.Data); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelInstancedMeshManager::HISMBuildTaskCallback( + TWeakObjectPtr Component, + const TVoxelSharedRef& BuiltData) +{ + HISMBuiltDataQueue.Enqueue({ Component, BuiltData }); +} + +UVoxelHierarchicalInstancedStaticMeshComponent* FVoxelInstancedMeshManager::CreateHISM(const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, const FIntVector& Position) const +{ + VOXEL_FUNCTION_COUNTER(); + + LOG_VOXEL(Verbose, TEXT("Instanced Mesh Manager: Creating a new HISM for mesh %s"), *MeshAndActorSettings.Mesh->GetPathName()); + + auto* ComponentsOwner = Settings.ComponentsOwner.Get(); + if (!ComponentsOwner) + { + return nullptr; + } + + auto& MeshSettings = MeshAndActorSettings.MeshSettings; + + UVoxelHierarchicalInstancedStaticMeshComponent* HISM; + if (MeshSettings.HISMTemplate) + { + HISM = NewObject(ComponentsOwner, MeshSettings.HISMTemplate.Get(), NAME_None, RF_Transient); + } + else + { + HISM = NewObject(ComponentsOwner, NAME_None, RF_Transient); + } + + HISM->Voxel_Init( + Settings.Pool, + const_cast(*this).AsShared(), + Settings.VoxelSize, + Position, + MeshAndActorSettings); + HISM->SetupAttachment(ComponentsOwner->GetRootComponent(), NAME_None); + HISM->RegisterComponent(); + + if (MeshSettings.BodyInstance.GetCollisionEnabled() != ECollisionEnabled::NoCollision) + { + Settings.EventManager->BindEvent( + true, + Settings.CollisionChunkSize, + Settings.CollisionDistanceInChunks, + FChunkDelegate::CreateUObject(HISM, &UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_EnablePhysics), + FChunkDelegate::CreateUObject(HISM, &UVoxelHierarchicalInstancedStaticMeshComponent::Voxel_DisablePhysics)); + } + + return HISM; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshSettings.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshSettings.cpp new file mode 100644 index 00000000..26371d8e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshSettings.cpp @@ -0,0 +1,204 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelInstancedMeshSettings.h" +#include "VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h" +#include "VoxelSpawners/VoxelSpawnerActor.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +FVoxelInstancedMeshSettings::FVoxelInstancedMeshSettings() +{ + BodyInstance.SetCollisionProfileName("BlockAll"); +} + +FVoxelSpawnerActorSettings::FVoxelSpawnerActorSettings() +{ + ActorClass = AVoxelMeshSpawnerActor::StaticClass(); + BodyInstance.SetCollisionProfileName("BlockAll"); +} + +FVoxelInstancedMeshAndActorSettings::FVoxelInstancedMeshAndActorSettings( + TWeakObjectPtr Mesh, + const TMap& SectionMaterials, + FVoxelInstancedMeshSettings MeshSettings, + FVoxelSpawnerActorSettings ActorSettings) + : Mesh(Mesh) + , MeshSettings(MeshSettings) + , ActorSettings(ActorSettings) +{ + SetSectionsMaterials(SectionMaterials); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TMap FVoxelInstancedMeshAndActorSettings::GetSectionsMaterials() const +{ + TMap SectionsMaterials; + for (int32 Index = 0; Index < MaterialsOverrides.Num(); Index++) + { + const TWeakObjectPtr Material = MaterialsOverrides[Index]; + if (Material.IsValid()) + { + SectionsMaterials.Add(Index, Material.Get()); + } + } + return SectionsMaterials; +} + +void FVoxelInstancedMeshAndActorSettings::SetSectionsMaterials(const TMap& SectionMaterials) +{ + MaterialsOverrides.Reset(); + for (auto& It : SectionMaterials) + { + if (It.Key >= MaterialsOverrides.Num()) + { + MaterialsOverrides.SetNum(It.Key + 1); + } + MaterialsOverrides[It.Key] = It.Value; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline bool operator==(FVoxelInt32Interval A, FVoxelInt32Interval B) +{ + return A.Min == B.Min && A.Max == B.Max; +} +inline uint32 GetTypeHash(FVoxelInt32Interval A) +{ + return HashCombine(GetTypeHash(A.Min), GetTypeHash(A.Max)); +} + +inline bool operator==(FLightingChannels A, FLightingChannels B) +{ + return + A.bChannel0 == B.bChannel0 && + A.bChannel1 == B.bChannel1 && + A.bChannel2 == B.bChannel2; +} +inline uint32 GetTypeHash(FLightingChannels A) +{ + return A.bChannel0 + 2 * A.bChannel1 + 4 * A.bChannel2; +} + +class FFoliageTypeCustomizationHelpers +{ +public: + inline static bool Equal(const FBodyInstance& A, const FBodyInstance& B) + { + return A.ObjectType == B.ObjectType && A.CollisionResponses == B.CollisionResponses; + } + inline static uint32 GetTypeHashBody(const FBodyInstance& A) + { + // Ignore collision responses + return uint32(A.ObjectType); + } +}; + +inline bool operator==(const FBodyInstance& A, const FBodyInstance& B) +{ + return FFoliageTypeCustomizationHelpers::Equal(A, B); +} +inline uint32 GetTypeHash(const FBodyInstance& A) +{ + return FFoliageTypeCustomizationHelpers::GetTypeHashBody(A); +} + +inline uint32 GetTypeHash(TArray> Materials) +{ + uint32 Hash = Materials.Num(); + for (auto& Material : Materials) + { + Hash = HashCombine(Hash, GetTypeHash(Material)); + } + return Hash; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline bool operator==(const FVoxelInstancedMeshSettings& A, const FVoxelInstancedMeshSettings& B) +{ + return + A.CullDistance == B.CullDistance && + A.bCastShadow == B.bCastShadow && + A.bAffectDynamicIndirectLighting == B.bAffectDynamicIndirectLighting && + A.bAffectDistanceFieldLighting == B.bAffectDistanceFieldLighting && + A.bCastShadowAsTwoSided == B.bCastShadowAsTwoSided && + A.bReceivesDecals == B.bReceivesDecals && + A.bUseAsOccluder == B.bUseAsOccluder && + A.BodyInstance == B.BodyInstance && + A.CustomNavigableGeometry == B.CustomNavigableGeometry && + A.LightingChannels == B.LightingChannels && + A.bRenderCustomDepth == B.bRenderCustomDepth && + A.CustomDepthStencilValue == B.CustomDepthStencilValue && + A.BuildDelay == B.BuildDelay && + A.HISMTemplate == B.HISMTemplate; +} + +inline bool operator==(const FVoxelSpawnerActorSettings& A, const FVoxelSpawnerActorSettings& B) +{ + return + A.ActorClass == B.ActorClass && + A.BodyInstance == B.BodyInstance && + A.Lifespan == B.Lifespan; +} + +bool operator==(const FVoxelInstancedMeshAndActorSettings& A, const FVoxelInstancedMeshAndActorSettings& B) +{ + return + A.Mesh == B.Mesh && + A.MaterialsOverrides == B.MaterialsOverrides && + A.MeshSettings == B.MeshSettings && + A.ActorSettings == B.ActorSettings; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define HASH(X) (GetTypeHash(Settings.X) * (FVoxelUtilities::MurmurHash32(__LINE__) & 0xFFFF)) ^ + +inline uint32 GetTypeHash(const FVoxelInstancedMeshSettings& Settings) +{ + return + HASH(CullDistance) + HASH(bCastShadow) + HASH(bAffectDynamicIndirectLighting) + HASH(bAffectDistanceFieldLighting) + HASH(bCastShadowAsTwoSided) + HASH(bReceivesDecals) + HASH(bUseAsOccluder) + HASH(BodyInstance) + HASH(CustomNavigableGeometry) + HASH(LightingChannels) + HASH(bRenderCustomDepth) + HASH(CustomDepthStencilValue) + HASH(BuildDelay) + HASH(HISMTemplate) + 0; +} + +inline uint32 GetTypeHash(const FVoxelSpawnerActorSettings& Settings) +{ + return + HASH(ActorClass) + HASH(BodyInstance) + HASH(Lifespan) + 0; +} + +uint32 GetTypeHash(const FVoxelInstancedMeshAndActorSettings& Settings) +{ + return + HASH(Mesh) + HASH(MaterialsOverrides) + HASH(MeshSettings) + HASH(ActorSettings) + 0; +} +#undef HASH \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp new file mode 100644 index 00000000..59fb0750 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp @@ -0,0 +1,391 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelVector.h" +#include "VoxelMessages.h" +#include "VoxelWorldInterface.h" +#include "VoxelUtilities/VoxelGeneratorUtilities.h" + +#include "Async/Async.h" +#include "TimerManager.h" +#include "Engine/StaticMesh.h" + +#if WITH_EDITOR +bool UVoxelMeshSpawner::NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) +{ + return Object == Mesh; +} + +bool UVoxelMeshSpawnerGroup::NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) +{ + return Meshes.Contains(Object); +} +#endif + +FVoxelMeshSpawnerProxyResult::FVoxelMeshSpawnerProxyResult(const FVoxelMeshSpawnerProxy& Proxy) + : FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType::MeshSpawner, Proxy) +{ +} + +FVoxelMeshSpawnerProxyResult::~FVoxelMeshSpawnerProxyResult() +{ +} + +void FVoxelMeshSpawnerProxyResult::Init(const FVoxelIntBox& InBounds, FVoxelSpawnerTransforms&& InTransforms) +{ + Bounds = InBounds; + Transforms = MoveTemp(InTransforms); +} + +void FVoxelMeshSpawnerProxyResult::CreateImpl() +{ + check(IsInGameThread()); + + const auto& MeshProxy = static_cast(Proxy); + + auto& MeshManager = *MeshProxy.Manager.Settings.MeshManager; + if (MeshProxy.bAlwaysSpawnActor) + { + // Only spawn actors the first time + if (!IsDirty()) + { + TArray Actors; + MeshManager.SpawnActors(MeshProxy.InstanceSettings, Transforms, Actors); + MarkDirty(); + } + } + else + { + if (Transforms.Matrices.Num() > 0) + { + // Matrices can be empty if all instances were removed + ensure(!InstancesRef.IsValid()); + const auto Ref = MeshManager.AddInstances(MeshProxy.InstanceSettings, Transforms, Bounds); + if (Ref.IsValid()) + { + // Ref might be invalid if we reached instances limit + InstancesRef = MakeUnique(Ref); + } + } + } +} + +void FVoxelMeshSpawnerProxyResult::DestroyImpl() +{ + check(IsInGameThread()); + ApplyRemovedIndices(); + + const auto& MeshProxy = static_cast(Proxy); + if (MeshProxy.bAlwaysSpawnActor) + { + // Do nothing + ensure(IsDirty()); + } + else + { + auto& MeshManager = *MeshProxy.Manager.Settings.MeshManager; + if (InstancesRef.IsValid()) + { + MeshManager.RemoveInstances(*InstancesRef); + InstancesRef.Reset(); + } + } +} + +void FVoxelMeshSpawnerProxyResult::SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) +{ + check(IsInGameThread()); + ApplyRemovedIndices(); + + Ar << Bounds; + + if (Version < FVoxelSpawnersSaveVersion::StoreSpawnerMatricesRelativeToComponent) + { + Transforms.TransformsOffset = FIntVector::ZeroValue; + Ar << Transforms.Matrices; + } + else + { + Ar << Transforms; + } +} + +uint32 FVoxelMeshSpawnerProxyResult::GetAllocatedSize() +{ + return sizeof(*this) + Transforms.Matrices.GetAllocatedSize(); +} + +void FVoxelMeshSpawnerProxyResult::ApplyRemovedIndices() +{ + const auto& MeshProxy = static_cast(Proxy); + auto& MeshManager = *MeshProxy.Manager.Settings.MeshManager; + + if (MeshProxy.bAlwaysSpawnActor) + { + // Do nothing + } + else + { + if (InstancesRef.IsValid()) + { + auto RemovedIndices = MeshManager.GetRemovedIndices(*InstancesRef); + RemovedIndices.Sort([](int32 A, int32 B) { return A > B; }); // Need to sort in decreasing order for the RemoveAtSwap + for (int32 RemovedIndex : RemovedIndices) + { + Transforms.Matrices.RemoveAtSwap(RemovedIndex, 1, false); + } + Transforms.Matrices.Shrink(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMeshSpawnerProxy::FVoxelMeshSpawnerProxy( + UVoxelMeshSpawnerBase* Spawner, + TWeakObjectPtr Mesh, + const TMap& SectionsMaterials, + FVoxelSpawnerManager& Manager, + uint32 Seed) + : FVoxelBasicSpawnerProxy(Spawner, Manager, EVoxelSpawnerProxyType::MeshSpawner, Seed) + , InstanceSettings(Mesh, SectionsMaterials, Spawner->InstancedMeshSettings, Spawner->ActorSettings) + , InstanceRandom(Spawner->InstanceRandom) + , ColorOutputName(Spawner->ColorOutputName) + , bAlwaysSpawnActor(Spawner->bAlwaysSpawnActor) + , FloatingDetectionOffset(Spawner->FloatingDetectionOffset) +{ + auto& Generator = *Manager.Settings.Data->Generator; + + if (InstanceRandom == EVoxelMeshSpawnerInstanceRandom::ColorOutput && + !Generator.GetOutputsPtrMap().Contains(ColorOutputName)) + { + FVoxelMessages::Error( + "Mesh Spawner: Color output for instance random not found: " + + FVoxelUtilities::GetMissingGeneratorOutputErrorString(ColorOutputName, Generator), + Spawner); + } +} + +TUniquePtr FVoxelMeshSpawnerProxy::ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const +{ + check(Hits.Num() > 0); + + const uint32 Seed = Bounds.GetMurmurHash() ^ SpawnerSeed; + auto& Generator = *Manager.Settings.Data->Generator; + const float VoxelSize = Manager.Settings.VoxelSize; + + const FRandomStream Stream(Seed); + + FVoxelSpawnerTransforms Transforms; + Transforms.TransformsOffset = Manager.Settings.MeshManager->ComputeTransformsOffset(Bounds); + Transforms.Matrices.Reserve(Hits.Num()); + + const auto ColorOutputPtr = Generator.GetOutputsPtrMap().FindRef(ColorOutputName); + + for (auto& Hit : Hits) + { + const FVector& LocalPosition = Hit.LocalPosition; + const FVector& Normal = Hit.Normal; + const FVoxelVector VoxelPosition = FVoxelVector(Bounds.Min) + FVoxelVector(LocalPosition); + const FVector WorldUp = Generator.GetUpVector(VoxelPosition); + + if (!CanSpawn(Stream, VoxelPosition, Normal, WorldUp)) + { + continue; + } + + const FVector RelativeGlobalPosition = VoxelSize * (LocalPosition + FVector(Bounds.Min - Transforms.TransformsOffset)); + const FMatrix Transform = GetTransform(Stream, Normal, WorldUp, RelativeGlobalPosition); + + FVoxelSpawnerMatrix SpawnerMatrix(Transform); + + if (InstanceRandom == EVoxelMeshSpawnerInstanceRandom::Random) + { + SpawnerMatrix.SetRandomInstanceId(Stream.GetFraction()); + } + else if (InstanceRandom == EVoxelMeshSpawnerInstanceRandom::VoxelMaterial) + { + // Note: instead of RoundToInt, should maybe use the nearest voxel that's not empty? + const auto Material = Accelerator.GetMaterial(Bounds.Min + FVoxelUtilities::RoundToInt(LocalPosition), 0); + SpawnerMatrix.SetRandomInstanceId(Material.GetPackedColor()); + } + else + { + check(InstanceRandom == EVoxelMeshSpawnerInstanceRandom::ColorOutput); + const auto Color = + ColorOutputPtr + ? (Generator.*ColorOutputPtr)(VoxelPosition.X, VoxelPosition.Y, VoxelPosition.Z, 0, FVoxelItemStack::Empty) + : FColor::Black; + SpawnerMatrix.SetRandomInstanceId(FVoxelMaterial::CreateFromColor(Color).GetPackedColor()); + } + + SpawnerMatrix.SetPositionOffset(RelativeGlobalPosition - Transform.GetOrigin() + Transform.TransformVector(FloatingDetectionOffset)); + + Transforms.Matrices.Add(SpawnerMatrix); + } + + if (Transforms.Matrices.Num() == 0) + { + return nullptr; + } + else + { + Transforms.Matrices.Shrink(); + + auto Result = MakeUnique(*this); + Result->Init(Bounds, MoveTemp(Transforms)); + return Result; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline TArray> CreateMeshProxies(UVoxelMeshSpawnerGroup* Spawner, FVoxelSpawnerManager& Manager) +{ + TArray> Result; + uint32 Seed = 1; + for (auto* Mesh : Spawner->Meshes) + { + const TMap SectionsMaterials; // TODO + Result.Add(MakeVoxelShared(Spawner, Mesh, SectionsMaterials, Manager, Seed++)); + } + return Result; +} + +FVoxelMeshSpawnerGroupProxy::FVoxelMeshSpawnerGroupProxy(UVoxelMeshSpawnerGroup* Spawner, FVoxelSpawnerManager& Manager) + : FVoxelSpawnerProxy(Spawner, Manager, EVoxelSpawnerProxyType::SpawnerGroup, 0) + , Proxies(CreateMeshProxies(Spawner, Manager)) +{ +} + +TUniquePtr FVoxelMeshSpawnerGroupProxy::ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const +{ + TArray> Results; + + const int32 NumHitsPerProxy = FVoxelUtilities::DivideCeil(Hits.Num(), Proxies.Num()); + int32 HitsStartIndex = 0; + for (auto& Proxy : Proxies) + { + const int32 HitsEndIndex = FMath::Min(HitsStartIndex + NumHitsPerProxy, Hits.Num()); + if (HitsStartIndex < HitsEndIndex) + { + TArray ProxyHits(Hits.GetData() + HitsStartIndex, HitsEndIndex - HitsStartIndex); + auto Result = Proxy->ProcessHits(Bounds, ProxyHits, Accelerator); + if (Result.IsValid()) + { + Results.Emplace(MoveTemp(Result)); + } + } + HitsStartIndex = HitsEndIndex; + } + + if (Results.Num() == 0) + { + return nullptr; + } + else + { + auto Result = MakeUnique(*this); + Result->Init(MoveTemp(Results)); + return Result; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelMeshSpawner::GetSpawnerProxy(FVoxelSpawnerManager& Manager) +{ + if (!Mesh && !bAlwaysSpawnActor) + { + FVoxelMessages::Error("Invalid mesh!", this); + } + TMap SectionsMaterials; + if (Mesh) + { + const int32 NumSections = Mesh->UE_27_SWITCH(StaticMaterials, GetStaticMaterials()).Num(); + for (auto& It : MaterialsOverrides) + { + const int32 Index = It.Key; + if (Index < 0 || Index >= NumSections) + { + FVoxelMessages::Error(FString::Printf(TEXT("Invalid Material Override section index: %d"), Index), this); + continue; + } + SectionsMaterials.Add(Index, It.Value); + } + } + return MakeVoxelShared(this, Mesh, SectionsMaterials, Manager, 0); +} + +FString UVoxelMeshSpawner::GetDebugInfo() const +{ + return Mesh ? Mesh->GetName() : "NULL"; +} + +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelMeshSpawnerGroup::GetSpawnerProxy(FVoxelSpawnerManager& Manager) +{ + if (!bAlwaysSpawnActor) + { + for (auto* Mesh : Meshes) + { + if (!Mesh) + { + FVoxelMessages::Error("Invalid mesh!", this); + } + } + } + return MakeVoxelShared(this, Manager); +} + +float UVoxelMeshSpawnerGroup::GetDistanceBetweenInstancesInVoxel() const +{ + // Scale it to account for instances split between meshes + return DistanceBetweenInstancesInVoxel / FMath::Max(1, Meshes.Num()); +} + +FString UVoxelMeshSpawnerGroup::GetDebugInfo() const +{ + FString Result; + for (auto& Mesh : Meshes) + { + if (!Result.IsEmpty()) + { + Result += ", "; + } + Result += Mesh ? Mesh->GetName() : "NULL"; + } + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelMeshSpawnerBase::Serialize(FArchive& Ar) +{ + Super::Serialize(Ar); + + if (!IsTemplate()) + { + ActorSettings.BodyInstance.FixupData(this); + InstancedMeshSettings.BodyInstance.FixupData(this); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp new file mode 100644 index 00000000..104ce8c9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp @@ -0,0 +1,115 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelEmptySpawner.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelSpawnerResults); +DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Voxel Spawners Num Results"), STAT_VoxelSpawners_NumResults, STATGROUP_VoxelCounters); + +FVoxelSpawnerProxyResult::FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType Type, const FVoxelSpawnerProxy& Proxy) + : Type(Type) + , Proxy(Proxy) +{ + ensure(Type == Proxy.Type || Type == EVoxelSpawnerProxyType::EmptySpawner); + INC_DWORD_STAT(STAT_VoxelSpawners_NumResults); +} + +FVoxelSpawnerProxyResult::~FVoxelSpawnerProxyResult() +{ + DEC_DWORD_STAT(STAT_VoxelSpawners_NumResults); + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerResults, AllocatedSize); +} + +void FVoxelSpawnerProxyResult::Create() +{ + ensure(!bCreated); + bCreated = true; + CreateImpl(); + UpdateStats(); +} + +void FVoxelSpawnerProxyResult::Destroy() +{ + ensure(bCreated); + bCreated = false; + DestroyImpl(); + UpdateStats(); +} + +void FVoxelSpawnerProxyResult::SerializeProxy(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) +{ + SerializeImpl(Ar, Version); + UpdateStats(); +} + +TVoxelSharedRef FVoxelSpawnerProxyResult::CreateFromType(EVoxelSpawnerProxyType Type, FVoxelSpawnerProxy& Proxy) +{ + check(Type == Proxy.Type || Type == EVoxelSpawnerProxyType::EmptySpawner); + + switch (Type) + { + case EVoxelSpawnerProxyType::EmptySpawner: + return MakeVoxelShared(Proxy); + case EVoxelSpawnerProxyType::AssetSpawner: + return MakeVoxelShared(static_cast(Proxy)); + case EVoxelSpawnerProxyType::MeshSpawner: + return MakeVoxelShared(static_cast(Proxy)); + case EVoxelSpawnerProxyType::SpawnerGroup: + return MakeVoxelShared(static_cast(Proxy)); + case EVoxelSpawnerProxyType::Invalid: + default: + check(false); + return TVoxelSharedPtr().ToSharedRef(); + } +} + +void FVoxelSpawnerProxyResult::UpdateStats() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerResults, AllocatedSize); + AllocatedSize = GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerResults, AllocatedSize); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSpawnerProxy::FVoxelSpawnerProxy(UVoxelSpawner* Spawner, FVoxelSpawnerManager& Manager, EVoxelSpawnerProxyType Type, uint32 Seed) + : Manager(Manager) + , Type(Type) + , SpawnerSeed(Spawner->SeedOverride == 0 ? FVoxelUtilities::MurmurHash32(FCrc::StrCrc32(*Spawner->GetPathName())) + Seed : Spawner->SeedOverride) +{ + +} + +TVoxelSharedRef UVoxelSpawner::GetSpawnerProxy(FVoxelSpawnerManager& Manager) +{ + unimplemented(); + return TVoxelSharedPtr().ToSharedRef(); +} + +bool UVoxelSpawner::GetSpawners(TSet& OutSpawners) +{ + check(this); + OutSpawners.Add(this); + return true; +} + +bool FVoxelSpawnersSaveImpl::Serialize(FArchive& Ar) +{ + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + if (Ar.IsSaving()) + { + Version = FVoxelSpawnersSaveVersion::LatestVersion; + } + + Ar << Version; + Ar << Guid; + Ar << CompressedData; + } + + return true; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp new file mode 100644 index 00000000..d8fb497f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp @@ -0,0 +1,73 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerActor.h" +#include "VoxelComponents/VoxelPhysicsRelevancyComponent.h" +#include "Components/StaticMeshComponent.h" +#include "Engine/StaticMesh.h" +#include "RenderingThread.h" +#include "Materials/MaterialInstance.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "Engine/Private/Materials/MaterialInstanceSupport.h" + +AVoxelMeshSpawnerActor::AVoxelMeshSpawnerActor() +{ + RootComponent = StaticMeshComponent = CreateDefaultSubobject("Static Mesh Component"); +} + +void AVoxelMeshSpawnerActor::SetStaticMesh_Implementation(UStaticMesh* Mesh, const TMap& SectionsMaterials, const FBodyInstance& CollisionPresets) +{ + StaticMeshComponent->BodyInstance = CollisionPresets; + StaticMeshComponent->BodyInstance.bSimulatePhysics = true; + StaticMeshComponent->SetStaticMesh(Mesh); + + for (auto& It : SectionsMaterials) + { + StaticMeshComponent->SetMaterial(It.Key, It.Value); + } +} + +template +void GameThread_UpdateMIParameter(const UMaterialInstance* Instance, const ParameterType& Parameter) +{ + FMaterialInstanceResource* Resource = Instance->Resource; + const FMaterialParameterInfo& ParameterInfo = Parameter.ParameterInfo; + typename ParameterType::ValueType Value = ParameterType::GetValue(Parameter); + ENQUEUE_RENDER_COMMAND(SetMIParameterValue)( + [Resource, ParameterInfo, Value](FRHICommandListImmediate& RHICmdList) + { + Resource->RenderThread_UpdateParameter(ParameterInfo, Value); +#if ENGINE_MINOR_VERSION >= 26 + Resource->CacheUniformExpressions(false); +#endif + }); +} + +void AVoxelMeshSpawnerActor::SetInstanceRandom_Implementation(float Value) +{ + for (int32 Index = 0; Index < StaticMeshComponent->GetNumMaterials(); Index++) + { + if (auto* Material = StaticMeshComponent->GetMaterial(Index)) + { + UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(Material, StaticMeshComponent); + if (DynamicMaterial) + { + // Do it manually, as for some values it's not updated + DynamicMaterial->SetScalarParameterValue("PerInstanceRandom", PI); + FScalarParameterValue* ParameterValue = GameThread_FindParameterByName(DynamicMaterial->ScalarParameterValues, FMaterialParameterInfo("PerInstanceRandom")); + if (ensure(ParameterValue)) + { + ParameterValue->ParameterValue = Value; + // Update the material instance data in the rendering thread. + GameThread_UpdateMIParameter(DynamicMaterial, *ParameterValue); + DynamicMaterial->RecacheUniformExpressions(false); + } + StaticMeshComponent->SetMaterial(Index, DynamicMaterial); + } + } + } +} + +AVoxelMeshWithPhysicsRelevancySpawnerActor::AVoxelMeshWithPhysicsRelevancySpawnerActor() +{ + PhysicsRelevancyComponent = CreateDefaultSubobject("Voxel Physics Relevancy Component"); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerConfig.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerConfig.cpp new file mode 100644 index 00000000..f7ec059f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerConfig.cpp @@ -0,0 +1,185 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerConfig.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerOutputsConfig.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +#if WITH_EDITOR +bool UVoxelSpawnerConfig::NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) +{ + if (Object == GeneratorOutputs) + { + return true; + } + + for (auto& Spawner : Spawners) + { + if (Spawner.Spawner == Object) + { + return true; + } + if (Spawner.Spawner && Spawner.Spawner->NeedsToRebuild(Object, PropertyChangedEvent)) + { + return true; + } + } + return false; +} + +void UVoxelSpawnerConfig::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + SetReadOnlyPropertiesFromEditorOnly(); + SetEditorOnlyPropertiesFromReadOnly(); + FixGuids(); + FixSpawnerDensityTypes(); +} +#endif + +void UVoxelSpawnerConfig::PostLoad() +{ + Super::PostLoad(); + + { + const auto UpgradeDensityGraphOutputNameToDensityStruct = [&](auto& Groups) + { + for (auto& Group : Groups) + { + for (auto& Spawner : Group.Spawners) + { + if (!Spawner.DensityGraphOutputName_DEPRECATED.IsNone()) + { + if (FName(Spawner.DensityGraphOutputName_DEPRECATED) == STATIC_FNAME("Constant 0")) + { + Spawner.Density.Type = EVoxelSpawnerDensityType::Constant; + Spawner.Density.Constant = 0.f; + } + else if (FName(Spawner.DensityGraphOutputName_DEPRECATED) == STATIC_FNAME("Constant 1")) + { + Spawner.Density.Type = EVoxelSpawnerDensityType::Constant; + Spawner.Density.Constant = 1.f; + } + else + { + Spawner.Density.Type = EVoxelSpawnerDensityType::GeneratorOutput; + Spawner.Density.GeneratorOutputName = Spawner.DensityGraphOutputName_DEPRECATED; + } + } + } + } + }; + + UpgradeDensityGraphOutputNameToDensityStruct(HeightSpawners_DEPRECATED); + UpgradeDensityGraphOutputNameToDensityStruct(RaySpawners_DEPRECATED); + } + + for (const FVoxelSpawnerConfigRayGroup& Group : RaySpawners_DEPRECATED) + { + for (const FVoxelSpawnerConfigElement_Ray& Spawner : Group.Spawners) + { + FVoxelSpawnerConfigSpawner NewSpawner; + + NewSpawner.Spawner = Spawner.Spawner; + NewSpawner.SpawnerType = EVoxelSpawnerType::Ray; + NewSpawner.Density = Spawner.Density; + NewSpawner.DensityMultiplier_RayOnly = Spawner.DensityMultiplier; + NewSpawner.HeightGraphOutputName_HeightOnly = ""; + NewSpawner.LOD = Group.LOD; + NewSpawner.GenerationDistanceInChunks = Group.GenerationDistanceInChunks; + + NewSpawner.bSave = Spawner.Advanced.bSave; + NewSpawner.bDoNotDespawn = Spawner.Advanced.bDoNotDespawn; + NewSpawner.Seed = Spawner.Advanced.DefaultSeed; + NewSpawner.RandomGenerator = Spawner.Advanced.RandomGenerator; + NewSpawner.Guid = Spawner.Advanced.Guid; + NewSpawner.bComputeDensityFirst_HeightOnly = false; + + Spawners.Add(NewSpawner); + } + } + RaySpawners_DEPRECATED.Reset(); + + for (const FVoxelSpawnerConfigHeightGroup& Group : HeightSpawners_DEPRECATED) + { + for (const FVoxelSpawnerConfigElement_Height& Spawner : Group.Spawners) + { + FVoxelSpawnerConfigSpawner NewSpawner; + + NewSpawner.Spawner = Spawner.Spawner; + NewSpawner.SpawnerType = EVoxelSpawnerType::Height; + NewSpawner.Density = Spawner.Density; + NewSpawner.DensityMultiplier_RayOnly = {}; + NewSpawner.HeightGraphOutputName_HeightOnly = Group.HeightGraphOutputName; + NewSpawner.LOD = FVoxelUtilities::GetDepthFromSize(Group.ChunkSize); + NewSpawner.GenerationDistanceInChunks = Group.GenerationDistanceInChunks; + + NewSpawner.bSave = Spawner.Advanced.bSave; + NewSpawner.bDoNotDespawn = Spawner.Advanced.bDoNotDespawn; + NewSpawner.Seed = Spawner.Advanced.DefaultSeed; + NewSpawner.RandomGenerator = Spawner.Advanced.RandomGenerator; + NewSpawner.Guid = Spawner.Advanced.Guid; + NewSpawner.bComputeDensityFirst_HeightOnly = Spawner.Advanced.bComputeDensityFirst; + + Spawners.Add(NewSpawner); + } + } + HeightSpawners_DEPRECATED.Reset(); + + SetEditorOnlyPropertiesFromReadOnly(); + FixGuids(); + FixSpawnerDensityTypes(); +} + +void UVoxelSpawnerConfig::SetReadOnlyPropertiesFromEditorOnly() +{ + for (auto& Spawner : Spawners) + { + Spawner.LOD = FVoxelUtilities::GetDepthFromSize(Spawner.ChunkSize_EditorOnly); + Spawner.GenerationDistanceInChunks = Spawner.GenerationDistanceInVoxels_EditorOnly / (RENDER_CHUNK_SIZE << Spawner.LOD); + Spawner.GenerationDistanceInChunks = FMath::Max(1, Spawner.GenerationDistanceInChunks); + } +} + +void UVoxelSpawnerConfig::SetEditorOnlyPropertiesFromReadOnly() +{ + for (auto& Spawner : Spawners) + { + Spawner.ChunkSize_EditorOnly = RENDER_CHUNK_SIZE << Spawner.LOD; + Spawner.GenerationDistanceInVoxels_EditorOnly = Spawner.GenerationDistanceInChunks * Spawner.ChunkSize_EditorOnly; + } +} +void UVoxelSpawnerConfig::FixGuids() +{ + TSet Guids; + + for (auto& Spawner : Spawners) + { + if (!Spawner.Guid.IsValid()) + { + Spawner.Guid = FGuid::NewGuid(); + } + + while (true) + { + bool bAlreadyInSet; + Guids.Add(Spawner.Guid, &bAlreadyInSet); + if (!bAlreadyInSet) break; + Spawner.Guid = FGuid::NewGuid(); + } + } +} + +void UVoxelSpawnerConfig::FixSpawnerDensityTypes() +{ + for (auto& Spawner : Spawners) + { + if (Spawner.SpawnerType == EVoxelSpawnerType::Height && + Spawner.Density.Type != EVoxelSpawnerDensityType::Constant && + Spawner.Density.Type != EVoxelSpawnerDensityType::GeneratorOutput) + { + Spawner.Density.Type = EVoxelSpawnerDensityType::Constant; + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.cpp new file mode 100644 index 00000000..629aa8f8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.cpp @@ -0,0 +1,116 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerEmbreeRayHandler.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" + +#if USE_EMBREE_VOXEL + +#define CHECK_EMBREE_ERRORS() \ +if (EmbreeDevice) \ +{ \ + RTCError ReturnError = rtcGetDeviceError(EmbreeDevice); \ + if (!ensureAlwaysMsgf(ReturnError == RTC_ERROR_NONE, TEXT("Embree error!"))) \ + { \ + rtcReleaseScene(EmbreeScene); \ + rtcReleaseDevice(EmbreeDevice); \ + EmbreeScene = nullptr; \ + EmbreeDevice = nullptr; \ + return; \ + } \ +} + +FVoxelSpawnerEmbreeRayHandler::FVoxelSpawnerEmbreeRayHandler( + bool bStoreDebugRays, + TArray&& InIndices, + TArray&& InVertices) + : FVoxelSpawnerRayHandler(bStoreDebugRays) + , Indices(MoveTemp(InIndices)) + , Vertices(MoveTemp(InVertices)) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + // Embree needs 16 bytes of padding, just to be safe we allocate 32 + Indices.Reserve(Indices.Num() + FVoxelUtilities::DivideCeil(32, sizeof(uint32))); + Vertices.Reserve(Vertices.Num() + FVoxelUtilities::DivideCeil(32, sizeof(FVector))); + + EmbreeDevice = rtcNewDevice(nullptr); + CHECK_EMBREE_ERRORS(); + + EmbreeScene = rtcNewScene(EmbreeDevice); + CHECK_EMBREE_ERRORS(); + rtcSetSceneBuildQuality(EmbreeScene, RTC_BUILD_QUALITY_HIGH); + CHECK_EMBREE_ERRORS(); + + Geometry = rtcNewGeometry(EmbreeDevice, RTC_GEOMETRY_TYPE_TRIANGLE); + CHECK_EMBREE_ERRORS(); + rtcSetGeometryBuildQuality(Geometry, RTC_BUILD_QUALITY_HIGH); + CHECK_EMBREE_ERRORS(); + rtcSetSharedGeometryBuffer(Geometry, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, Vertices.GetData(), 0, sizeof(FVector), Vertices.Num()); + CHECK_EMBREE_ERRORS(); + rtcSetSharedGeometryBuffer(Geometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, Indices.GetData(), 0, 3 * sizeof(uint32), Indices.Num() / 3); + CHECK_EMBREE_ERRORS(); + rtcCommitGeometry(Geometry); + CHECK_EMBREE_ERRORS(); + rtcAttachGeometryByID(EmbreeScene, Geometry, 0); + CHECK_EMBREE_ERRORS(); + + rtcCommitScene(EmbreeScene); + CHECK_EMBREE_ERRORS(); +} + +FVoxelSpawnerEmbreeRayHandler::~FVoxelSpawnerEmbreeRayHandler() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + rtcReleaseGeometry(Geometry); + Geometry = nullptr; + CHECK_EMBREE_ERRORS(); + rtcReleaseScene(EmbreeScene); + EmbreeScene = nullptr; + CHECK_EMBREE_ERRORS(); + rtcReleaseDevice(EmbreeDevice); + EmbreeDevice = nullptr; + CHECK_EMBREE_ERRORS(); +} + +bool FVoxelSpawnerEmbreeRayHandler::TraceRayInternal(const FVector& Start, const FVector& Direction, FVector& HitNormal, FVector& HitPosition) const +{ + RTCRayHit RayHit; + RTCRay& Ray = RayHit.ray; + Ray.org_x = Start.X; + Ray.org_y = Start.Y; + Ray.org_z = Start.Z; + Ray.dir_x = Direction.X; + Ray.dir_y = Direction.Y; + Ray.dir_z = Direction.Z; + Ray.tnear = 0; + Ray.tfar = 1e9; + Ray.flags = 0; + Ray.mask = -1; + + RTCHit& Hit = RayHit.hit; + Hit.geomID = RTC_INVALID_GEOMETRY_ID; + Hit.instID[0] = RTC_INVALID_GEOMETRY_ID; + + RTCIntersectContext Context; + rtcInitIntersectContext(&Context); + + rtcIntersect1(EmbreeScene, &Context, &RayHit); + +#if 0 + RTCError ReturnError = rtcGetDeviceError(EmbreeDevice); + ensureAlwaysMsgf(ReturnError == RTC_ERROR_NONE, TEXT("Embree error!")); +#endif + + if (Hit.geomID != RTC_INVALID_GEOMETRY_ID) + { + HitNormal = -FVector(Hit.Ng_x, Hit.Ng_y, Hit.Ng_z).GetSafeNormal(); + HitPosition = Start + Direction * Ray.tfar; + return true; + } + + return false; +} + +#endif // USE_EMBREE_VOXEL diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.h new file mode 100644 index 00000000..ae72151b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerEmbreeRayHandler.h @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#if USE_EMBREE_VOXEL + +#include "CoreMinimal.h" +#include "VoxelSpawners/VoxelSpawnerRayHandler.h" + +#include "embree3/rtcore.h" +#include "embree3/rtcore_ray.h" + +struct FVoxelChunkMesh; + +class FVoxelSpawnerEmbreeRayHandler : public FVoxelSpawnerRayHandler +{ +public: + FVoxelSpawnerEmbreeRayHandler( + bool bStoreDebugRays, + TArray&& Indices, + TArray&& Vertices); + ~FVoxelSpawnerEmbreeRayHandler(); + + virtual bool HasError() const override { return !EmbreeScene || !EmbreeDevice; } + +protected: + virtual bool TraceRayInternal(const FVector& Start, const FVector& Direction, FVector& HitNormal, FVector& HitPosition) const override; + +private: + RTCDevice EmbreeDevice = nullptr; + RTCScene EmbreeScene = nullptr; + RTCGeometry Geometry = nullptr; + + TArray Indices; + TArray Vertices; +}; + +#endif // USE_EMBREE_VOXEL diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp new file mode 100644 index 00000000..61f327d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp @@ -0,0 +1,296 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerGroup.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelMessages.h" + +#include "Logging/MessageLog.h" +#include "Misc/UObjectToken.h" + +FVoxelSpawnerGroupProxyResult::FVoxelSpawnerGroupProxyResult(const FVoxelSpawnerProxy& Proxy) + : FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType::SpawnerGroup, Proxy) +{ + +} + +void FVoxelSpawnerGroupProxyResult::Init(TArray>&& InResults) +{ + Results = MoveTemp(InResults); +} + +void FVoxelSpawnerGroupProxyResult::CreateImpl() +{ + for (auto& Result : Results) + { + check(Result.IsValid()); + Result->Create(); + } +} + +void FVoxelSpawnerGroupProxyResult::DestroyImpl() +{ + for (auto& Result : Results) + { + check(Result.IsValid()); + Result->Destroy(); + } +} + +void FVoxelSpawnerGroupProxyResult::SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) +{ + // TODO +#if 0 + if (Ar.IsSaving()) + { + int32 NumResults = Results.Num(); + Ar << NumResults; + + for (auto& Result : Results) + { + EVoxelSpawnerProxyType ProxyType = Result->Type; + Ar << ProxyType; + + Result->SerializeProxy(Ar, VoxelCustomVersion); + } + } + else + { + check(Ar.IsLoading()); + int32 NumResults = -1; + Ar << NumResults; + check(NumResults >= 0); + + Results.SetNum(NumResults); + for (int32 Index = 0; Index < NumResults; ++Index) + { + EVoxelSpawnerProxyType ProxyType = EVoxelSpawnerProxyType::Invalid; + Ar << ProxyType; + check(ProxyType != EVoxelSpawnerProxyType::Invalid); + + auto Result = CreateFromType(ProxyType, ) + } + } +#endif +} + +uint32 FVoxelSpawnerGroupProxyResult::GetAllocatedSize() +{ + return sizeof(*this) + Results.GetAllocatedSize(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSpawnerGroupProxy::FVoxelSpawnerGroupProxy(UVoxelSpawnerGroup* Spawner, FVoxelSpawnerManager& Manager) + : FVoxelSpawnerProxy(Spawner, Manager, EVoxelSpawnerProxyType::SpawnerGroup, 0) + , SpawnerGroup(Spawner) +{ + +} + +TUniquePtr FVoxelSpawnerGroupProxy::ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const +{ + TArray> Results; + if (Children.Num() > 0) + { + TArray> ChildrenHits; + ChildrenHits.SetNum(Children.Num()); + for (auto& ChildHits : ChildrenHits) + { + ChildHits.Reserve(Hits.Num()); + } + + const uint32 Seed = Bounds.GetMurmurHash() ^ SpawnerSeed; + const FRandomStream Stream(Seed); + + for (auto& Hit : Hits) + { + ChildrenHits[GetChild(Stream.GetFraction())].Add(Hit); + } + + for (int32 Index = 0; Index < Children.Num(); Index++) + { + if (ChildrenHits[Index].Num() == 0) continue; + auto Result = Children[Index].Spawner->ProcessHits(Bounds, ChildrenHits[Index], Accelerator); + if (Result.IsValid()) + { + Results.Emplace(MoveTemp(Result)); + } + } + } + if (Results.Num() == 0) + { + return nullptr; + } + else + { + auto Result = MakeUnique(*this); + Result->Init(MoveTemp(Results)); + return Result; + } +} + +void FVoxelSpawnerGroupProxy::PostSpawn() +{ + check(IsInGameThread()); + + double ChildrenSum = 0; + for (auto& Child : SpawnerGroup->Children) + { + ChildrenSum += Child.Probability; + } + if (ChildrenSum == 0) + { + ChildrenSum = 1; + } + + double ProbabilitySum = 0; + for (auto& Child : SpawnerGroup->Children) + { + const auto ChildSpawner = Manager.GetSpawner(Child.Spawner); + if (!ChildSpawner.IsValid()) + { + Children.Reset(); + return; + } + ProbabilitySum += Child.Probability / ChildrenSum; + Children.Emplace(FChild{ ChildSpawner, float(ProbabilitySum) }); + } + + ensure(ChildrenSum == 1 || FMath::IsNearlyEqual(ProbabilitySum, 1)); +} + +int32 FVoxelSpawnerGroupProxy::GetChild(float RandomNumber) const +{ + for (int32 Index = 0; Index < Children.Num(); Index++) + { + if (RandomNumber < Children[Index].ProbabilitySum) + { + return Index; + } + } + return Children.Num() - 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelSpawnerGroup::GetSpawnerProxy(FVoxelSpawnerManager& Manager) +{ + return MakeVoxelShared(this, Manager); +} + +bool UVoxelSpawnerGroup::GetSpawners(TSet& OutSpawners) +{ + static TArray Stack; + + struct FScopeStack + { + FScopeStack(UVoxelSpawnerGroup* This) : This(This) + { + Stack.Add(This); + } + ~FScopeStack() + { + ensure(Stack.Pop() == This); + } + UVoxelSpawnerGroup* const This; + }; + + if (Stack.Contains(this)) + { + TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Error); + Message->AddToken(FTextToken::Create(VOXEL_LOCTEXT("Recursive spawner group! Spawners in stack: "))); + for (auto* Spawner : Stack) + { + Message->AddToken(FUObjectToken::Create(Spawner)); + } + FMessageLog("PIE").AddMessage(Message); + return false; + } + + FScopeStack ScopeStack(this); + + OutSpawners.Add(this); + for (auto& Child : Children) + { + if (!Child.Spawner) + { + FVoxelMessages::Error("Invalid Child Spawner!", this); + return false; + } + if (!Child.Spawner->GetSpawners(OutSpawners)) + { + return false; + } + } + + return true; +} + +FString UVoxelSpawnerGroup::GetDebugInfo() const +{ + FString Result; + for (auto& Child : Children) + { + + if (!Result.IsEmpty()) + { + Result += ", "; + } + Result += "{ " + (Child.Spawner ? Child.Spawner->GetDebugInfo() : "NULL") + " }"; + } + return Result; +} + +#if WITH_EDITOR +void UVoxelSpawnerGroup::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) +{ + Super::PostEditChangeChainProperty(PropertyChangedEvent); + + if (bNormalizeProbabilitiesOnEdit && + PropertyChangedEvent.Property && + (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive || + PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet)) + { + const int32 EditedIndex = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UVoxelSpawnerGroup, Children)); + if (Children.IsValidIndex(EditedIndex)) + { + double Sum = 0; + for (int32 Index = 0; Index < Children.Num(); Index++) + { + if (Index != EditedIndex) + { + Sum += Children[Index].Probability; + } + } + if (Sum == 0) + { + for (int32 Index = 0; Index < Children.Num(); Index++) + { + if (Index != EditedIndex) + { + ensure(Children[Index].Probability == 0); + Children[Index].Probability = (1 - Children[EditedIndex].Probability) / (Children.Num() - 1); + } + } + } + else + { + for (int32 Index = 0; Index < Children.Num(); Index++) + { + if (Index != EditedIndex) + { + Children[Index].Probability *= (1 - Children[EditedIndex].Probability) / Sum; + } + } + } + } + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerManager.cpp new file mode 100644 index 00000000..c8b878ae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerManager.cpp @@ -0,0 +1,1202 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelSpawnerEmbreeRayHandler.h" +#include "VoxelSpawners/VoxelSpawnerRayHandler.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelEmptySpawner.h" +#include "VoxelSpawners/HitGenerators/VoxelRayHitGenerator.h" +#include "VoxelSpawners/HitGenerators/VoxelHeightHitGenerator.h" + +#include "VoxelEvents/VoxelEventManager.h" + +#include "VoxelData/VoxelDataIncludes.h" + +#include "VoxelRender/IVoxelRenderer.h" + +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelDebug/VoxelDebugUtilities.h" + +#include "IVoxelPool.h" +#include "VoxelAsyncWork.h" +#include "VoxelCancelCounter.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" +#include "VoxelMessages.h" +#include "VoxelPriorityHandler.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" +#include "VoxelWorld.h" +#include "VoxelUtilities/VoxelGeneratorUtilities.h" + +#include "Async/Async.h" +#include "Engine/Engine.h" +#include "DrawDebugHelpers.h" +#include "Serialization/BufferArchive.h" +#include "Serialization/LargeMemoryReader.h" +#include "Serialization/LargeMemoryWriter.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelSpawnerManagerMemory); + +static TAutoConsoleVariable CVarShowVoxelSpawnerRays( + TEXT("voxel.spawners.ShowRays"), + 0, + TEXT("If true, will show the voxel spawner rays"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowVoxelSpawnerHits( + TEXT("voxel.spawners.ShowHits"), + 0, + TEXT("If true, will show the voxel spawner rays hits"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowVoxelSpawnerPositions( + TEXT("voxel.spawners.ShowPositions"), + 0, + TEXT("If true, will show the positions sent to spawners"), + ECVF_Default); + +static TAutoConsoleVariable CVarShowChunks( + TEXT("voxel.spawners.ShowChunks"), + 0, + TEXT("If true, show the spawners chunks"), + ECVF_Default); + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelSpawnerTask : public FVoxelAsyncWork +{ +public: + const TVoxelWeakPtr SpawnerManagerPtr; + const FVoxelIntBox Bounds; + const int32 GroupIndex; + const TArray SpawnersToSpawn; + const FVoxelCancelCounter CancelCounter; + const FVoxelPriorityHandler PriorityHandler; + + FVoxelSpawnerTask( + FVoxelSpawnerManager& SpawnerManager, + const FVoxelIntBox& Bounds, + int32 GroupIndex, + const TArray& SpawnersToSpawn, + const FVoxelCancelCounter& CancelCounter) + : FVoxelAsyncWork(STATIC_FNAME("Spawner Task"), SpawnerManager.Settings.PriorityDuration, true) + , SpawnerManagerPtr(SpawnerManager.AsShared()) + , Bounds(Bounds) + , GroupIndex(GroupIndex) + , SpawnersToSpawn(SpawnersToSpawn) + , CancelCounter(CancelCounter) + , PriorityHandler(Bounds, SpawnerManager.Settings.Renderer->GetInvokersPositionsForPriorities()) + { + SpawnerManager.TaskCounter.Increment(); + SpawnerManager.UpdateTaskCount(); + } + + virtual void DoWork() override + { + auto SpawnerManager = SpawnerManagerPtr.Pin(); + if (!SpawnerManager.IsValid()) return; + + SpawnerManager->SpawnGroup_AnyThread(*this); + + SpawnerManager->TaskCounter.Decrement(); + SpawnerManager->UpdateTaskCount(); + + FVoxelUtilities::DeleteOnGameThread_AnyThread(SpawnerManager); + } + virtual uint32 GetPriority() const override + { + return PriorityHandler.GetPriority(); + } + +private: + ~FVoxelSpawnerTask() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSpawnerSettings::FVoxelSpawnerSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& DebugManager, + const TVoxelSharedRef& Data, + const TVoxelSharedRef& LODManager, + const TVoxelSharedRef& Renderer, + const TVoxelSharedRef& MeshManager, + const TVoxelSharedRef& EventManager) + : VoxelWorldInterface(World) + , Pool(Pool) + , DebugManager(DebugManager) + , Data(Data) + , MeshManager(MeshManager) + , EventManager(EventManager) + , LODManager(LODManager) + , Renderer(Renderer) + , Config(PlayType == EVoxelPlayType::Game || World->bEnableFoliageInEditor ? World->SpawnerConfig : nullptr) + , VoxelSize(World->VoxelSize) + , PriorityDuration(World->PriorityDuration) +{ +} + +inline bool operator==(const FVoxelSpawnerConfigGroupBase& Lhs, const FVoxelSpawnerConfigGroupBase& Rhs) +{ + return + Lhs.LOD == Rhs.LOD && + Lhs.GenerationDistanceInChunks == Rhs.GenerationDistanceInChunks && + Lhs.bInfiniteGenerationDistance == Rhs.bInfiniteGenerationDistance && + Lhs.SpawnerType == Rhs.SpawnerType; +} + +inline uint32 GetTypeHash(const FVoxelSpawnerConfigGroupBase& Key) +{ + return HashCombine(HashCombine(HashCombine( + GetTypeHash(Key.LOD), + GetTypeHash(Key.GenerationDistanceInChunks)), + GetTypeHash(Key.bInfiniteGenerationDistance)), + GetTypeHash(Key.SpawnerType)); +} +inline bool operator<(const FVoxelSpawnerConfigGroupBase& Lhs, const FVoxelSpawnerConfigGroupBase& Rhs) +{ + // Just to be deterministic + return + Lhs.LOD < Rhs.LOD && + Lhs.GenerationDistanceInChunks < Rhs.GenerationDistanceInChunks && + int32(Lhs.bInfiniteGenerationDistance) < int32(Rhs.bInfiniteGenerationDistance) && + int32(Lhs.SpawnerType) < int32(Rhs.SpawnerType); +} + +TVoxelSharedRef FVoxelSpawnerManager::Create(const FVoxelSpawnerSettings& Settings) +{ + VOXEL_FUNCTION_COUNTER(); + + struct FInitStruct + { + FVoxelSpawnerThreadSafeConfig ThreadSafeConfig; + TSet Spawners; + }; + + const auto GetInitStruct = [=]() -> FInitStruct + { + if (!Settings.Config.IsValid()) + { + return {}; + } + + UVoxelSpawnerConfig& Config = *Settings.Config; + + FInitStruct InitStruct; + InitStruct.ThreadSafeConfig.WorldType = Config.WorldType; + InitStruct.ThreadSafeConfig.FiveWayBlendSetup = Config.FiveWayBlendSetup; + + TMap> Groups; + + const auto& Generator = *Settings.Data->Generator; + + // Fill Groups and Spawners + for (const FVoxelSpawnerConfigSpawner& Spawner : Config.Spawners) + { + if (!Spawner.Spawner) + { + FVoxelMessages::Error("Spawner is null!", &Config); + return {}; + } + + { + const auto CheckFloatOutputExists = [&](FName Name) + { + if (!Generator.GetOutputsPtrMap().Contains(Name)) + { + FVoxelMessages::Warning( + FVoxelUtilities::GetMissingGeneratorOutputErrorString(Name, Generator), + &Config); + } + }; + + if (Spawner.SpawnerType == EVoxelSpawnerType::Height) + { + CheckFloatOutputExists(Spawner.HeightGraphOutputName_HeightOnly); + } + + if (Spawner.Density.Type == EVoxelSpawnerDensityType::GeneratorOutput) + { + CheckFloatOutputExists(Spawner.Density.GeneratorOutputName); + } + } + + InitStruct.Spawners.Add(Spawner.Spawner); + + FVoxelSpawnerConfigSpawnerWithRuntimeData SpawnerWithRuntimeData{ Spawner }; + + SpawnerWithRuntimeData.DistanceBetweenInstancesInVoxel = Spawner.Spawner->GetDistanceBetweenInstancesInVoxel(); + SpawnerWithRuntimeData.DebugName = Spawner.Spawner->GetName() + ": " + Spawner.Spawner->GetDebugInfo(); + + Groups.FindOrAdd(FVoxelSpawnerConfigGroupBase{ Spawner.LOD, Spawner.GenerationDistanceInChunks, Spawner.bInfiniteGenerationDistance , Spawner.SpawnerType }).Add(SpawnerWithRuntimeData); + } + + // Make the group creation deterministic + Groups.KeySort([](auto& A, auto& B) { return A < B; }); + + // Build groups + for (const auto& It : Groups) + { + FVoxelSpawnerConfigGroup Group(It.Key); + Group.Spawners = It.Value; + InitStruct.ThreadSafeConfig.Groups.Add(Group); + } + + return InitStruct; + }; + + const FInitStruct InitStruct = GetInitStruct(); + + TVoxelSharedRef Manager = MakeShareable(new FVoxelSpawnerManager(Settings, InitStruct.ThreadSafeConfig)); + + // Spawners config + { + TSet Spawners; + + // Gather all the spawners and the ones they reference + { + TArray QueuedSpawners = InitStruct.Spawners.Array(); + + while (QueuedSpawners.Num() > 0) + { + UVoxelSpawner* Spawner = QueuedSpawners.Pop(); + + bool bAlreadyInSet = false; + Spawners.Add(Spawner, &bAlreadyInSet); + if (bAlreadyInSet) + { + continue; + } + + TSet NewSpawners; + if (!Spawner->GetSpawners(NewSpawners)) + { + continue; + } + + QueuedSpawners.Append(NewSpawners.Array()); + } + } + + // Create the proxies + for (auto* Spawner : Spawners) + { + check(Spawner); + Manager->SpawnersMap.Add(Spawner, Spawner->GetSpawnerProxy(*Manager)); + } + + // Call post spawn + for (auto& It : Manager->SpawnersMap) + { + It.Value->PostSpawn(); + } + } + + // Create GroupsData first + for (const auto& Group : Manager->ThreadSafeConfig.Groups) + { + const int32 ChunkSize = RENDER_CHUNK_SIZE << Group.LOD; + + TArray Proxies; + for (auto& Spawner : Group.Spawners) + { + Proxies.Add(Manager->SpawnersMap[Spawner.Spawner].Get()); + } + Manager->GroupsData.Emplace(ChunkSize, Proxies); + } + + // Then bind delegates. This separation is needed as BindEvent might fire the delegate now. + for (int32 GroupIndex = 0; GroupIndex < Manager->ThreadSafeConfig.Groups.Num(); GroupIndex++) + { + const auto& Group = Manager->ThreadSafeConfig.Groups[GroupIndex]; + + const int32 ChunkSize = RENDER_CHUNK_SIZE << Group.LOD; + + if (Group.bInfiniteGenerationDistance) + { + TArray Children; + const int32 MaxChildren = 1 << 20; + if (!Settings.Data->WorldBounds.Subdivide(ChunkSize, Children, MaxChildren)) + { + // If we have more than a million chunks, something went terribly wrong + FVoxelMessages::Error(FString::Printf(TEXT("bInfiniteGenerationDistance = true: trying to spawn more than %d chunks. Abording"), MaxChildren), Settings.Config.Get()); + continue; + } + for (auto& Child : Children) + { + Manager->SpawnGroup_GameThread(Child, GroupIndex); + } + } + else + { + // Bind generation delegates + Settings.EventManager->BindEvent( + true, + ChunkSize, + Group.GenerationDistanceInChunks, + FChunkDelegate::CreateThreadSafeSP(Manager, &FVoxelSpawnerManager::SpawnGroup_GameThread, GroupIndex), + FChunkDelegate::CreateThreadSafeSP(Manager, &FVoxelSpawnerManager::DestroyGroup_GameThread, GroupIndex)); + } + } + + return Manager; +} + +void FVoxelSpawnerManager::Destroy() +{ + for (auto& Group : GroupsData) + { + FScopeLock Lock(&Group.Section); + + for (auto& It : Group.ChunksData) + { + It.Value.CancelTasks(); + } + } + + StopTicking(); +} + +FVoxelSpawnerManager::~FVoxelSpawnerManager() +{ + ensure(IsInGameThread()); +} + +FVoxelSpawnerManager::FVoxelSpawnerManager(const FVoxelSpawnerSettings& Settings, const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig) + : Settings(Settings) + , ThreadSafeConfig(ThreadSafeConfig) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedPtr FVoxelSpawnerManager::GetSpawner(UVoxelSpawner* Spawner) const +{ + return SpawnersMap.FindRef(Spawner); +} + +void FVoxelSpawnerManager::Regenerate(const FVoxelIntBox& Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + + IterateResultsInBounds( + Bounds, + [&](TVoxelSharedPtr& Result) + { + if (Result.IsValid() && !Result->NeedsToBeSaved()) + { + if (Result->IsCreated()) + { + Result->Destroy(); + } + else + { + FScopeLock Lock(&CreateQueueSection); + ensure(CreateQueue.RemoveSwap(Result) == 1); + } + Result.Reset(); + return true; + } + else + { + return false; + } + }); +} + +void FVoxelSpawnerManager::MarkDirty(const FVoxelIntBox& Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + + IterateResultsInBounds( + Bounds, + [](TVoxelSharedPtr& Result) + { + if (Result.IsValid()) + { + Result->MarkDirty(); + } + return false; + }); +} + +void FVoxelSpawnerManager::Serialize(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Ar.IsSaving()) + { + check(Version == FVoxelSpawnersSaveVersion::LatestVersion); + + struct FGuidInfo + { + FVoxelSpawnerProxy* Proxy = nullptr; + TArray Positions; + TArray> ProxyResults; + }; + TMap GuidInfos; + + // Expand groups to get SpawnerGuid -> Results + // Storing GUIDs is more future-proof than the group/spawner index + for (int32 GroupIndex = 0; GroupIndex < GroupsData.Num(); GroupIndex++) + { + const FSpawnerGroupData& GroupData = GroupsData[GroupIndex]; + const FVoxelSpawnerConfigGroup& Group = ThreadSafeConfig.Groups[GroupIndex]; + + FScopeLock Lock(&GroupData.Section); + for (auto& It : GroupData.ChunksData) + { + const FIntVector& Key = It.Key; + const FSpawnerGroupChunkData& ChunkData = It.Value; + + check(ChunkData.SpawnerProxiesResults.Num() == 0 || ChunkData.SpawnerProxiesResults.Num() == Group.Spawners.Num()); + for (int32 SpawnerIndex = 0; SpawnerIndex < ChunkData.SpawnerProxiesResults.Num(); SpawnerIndex++) + { + const TVoxelSharedPtr& ProxyResult = ChunkData.SpawnerProxiesResults[SpawnerIndex]; + if (ProxyResult.IsValid() && ProxyResult->NeedsToBeSaved()) + { + FGuidInfo& GuidInfo = GuidInfos.FindOrAdd(Group.Spawners[SpawnerIndex].Guid); + + ensure(!GuidInfo.Proxy || GuidInfo.Proxy == GroupData.SpawnerProxies[SpawnerIndex]); + GuidInfo.Proxy = GroupData.SpawnerProxies[SpawnerIndex]; + check(GuidInfo.Proxy); + + ensure(ProxyResult->Type == GuidInfo.Proxy->Type || ProxyResult->Type == EVoxelSpawnerProxyType::EmptySpawner); + + GuidInfo.Positions.Add(Key); + GuidInfo.ProxyResults.Add(ProxyResult); + } + } + } + } + + int32 NumGuid = GuidInfos.Num(); + Ar << NumGuid; + + // Now serialize each GUID + for (auto& It : GuidInfos) + { + FGuid Guid = It.Key; + FGuidInfo& GuidInfo = It.Value; + + // Create one archive per GUID to be safer + FBufferArchive GuidArchive; + { + int32 NumProxyResults = GuidInfo.Positions.Num(); + check(GuidInfo.ProxyResults.Num() == NumProxyResults); + check(NumProxyResults > 0); + + GuidArchive << NumProxyResults; + GuidArchive.Serialize(GuidInfo.Positions.GetData(), NumProxyResults * sizeof(FIntVector)); + + for (auto& Result : GuidInfo.ProxyResults) + { + check(Result->Type == GuidInfo.Proxy->Type || Result->Type == EVoxelSpawnerProxyType::EmptySpawner); + GuidArchive << Result->Type; + Result->SerializeProxy(GuidArchive, Version); + } + } + + Ar << Guid; + + int32 GuidArchiveNum = GuidArchive.Num(); + Ar << GuidArchiveNum; + + Ar.Serialize(GuidArchive.GetData(), GuidArchiveNum * sizeof(uint8)); + } + } + else + { + check(Ar.IsLoading()); + + int32 NumGuid = -1; + Ar << NumGuid; + check(NumGuid >= 0); + + // Deserialize each GUID + for (int32 GuidIndex = 0; GuidIndex < NumGuid; GuidIndex++) + { + FGuid Guid; + TArray GuidArchiveData; + { + Ar << Guid; + + int32 GuidArchiveNum = -1; + Ar << GuidArchiveNum; + check(GuidArchiveNum >= 0); + + GuidArchiveData.SetNumUninitialized(GuidArchiveNum); + Ar.Serialize(GuidArchiveData.GetData(), GuidArchiveNum * sizeof(uint8)); + } + + int32 GroupIndex = -1; + int32 SpawnerIndex = -1; + + // Find the Group Index & Spawner Index from the GUID by iterating all of them + for (int32 LocalGroupIndex = 0; LocalGroupIndex < ThreadSafeConfig.Groups.Num(); LocalGroupIndex++) + { + const FVoxelSpawnerConfigGroup& Group = ThreadSafeConfig.Groups[LocalGroupIndex]; + for (int32 LocalSpawnerIndex = 0; LocalSpawnerIndex < Group.Spawners.Num(); LocalSpawnerIndex++) + { + if (Group.Spawners[LocalSpawnerIndex].Guid == Guid) + { + ensureMsgf(GroupIndex == -1, TEXT("Spawners with identical GUIDs")); + GroupIndex = LocalGroupIndex; + SpawnerIndex = LocalSpawnerIndex; + } + } + } + + if (GroupIndex == -1) + { + FVoxelMessages::Error(FString::Printf(TEXT("Voxel Spawners Serialization: Loading: Spawner with GUID %s not found, skipping it"), *Guid.ToString()), Settings.VoxelWorldInterface.Get()); + ensure(false); + continue; + } + + check(GroupIndex >= 0); + check(SpawnerIndex >= 0); + + const FVoxelSpawnerConfigGroup& Group = ThreadSafeConfig.Groups[GroupIndex]; + const FVoxelSpawnerConfigSpawnerWithRuntimeData& Spawner = Group.Spawners[SpawnerIndex]; + + FSpawnerGroupData& GroupData = GroupsData[GroupIndex]; + FVoxelSpawnerProxy& Proxy = *GroupData.SpawnerProxies[SpawnerIndex]; + + FScopeLock Lock(&GroupData.Section); + + TArray Positions; + TArray> ProxyResults; + + // Load GUID archive + { + FMemoryReader GuidArchive(GuidArchiveData); + + int32 NumProxyResults = -1; + GuidArchive << NumProxyResults; + check(NumProxyResults > 0); + + Positions.SetNumUninitialized(NumProxyResults); + GuidArchive.Serialize(Positions.GetData(), NumProxyResults * sizeof(FIntVector)); + + ProxyResults.Reserve(NumProxyResults); + for (int32 ResultIndex = 0; ResultIndex < NumProxyResults; ResultIndex++) + { + EVoxelSpawnerProxyType ProxyType = EVoxelSpawnerProxyType::Invalid; + GuidArchive << ProxyType; + check(ProxyType != EVoxelSpawnerProxyType::Invalid); + + if (ProxyType != EVoxelSpawnerProxyType::EmptySpawner && ProxyType != Proxy.Type) + { + FVoxelMessages::Error(FString::Printf( + TEXT("Voxel Spawner Serialization: Spawner changed type, skipping it (was %s, is %s now). Spawner: %s; GUID: %s"), + ToString(ProxyType), + ToString(Proxy.Type), + *Spawner.DebugName, + *Guid.ToString()), Settings.VoxelWorldInterface.Get()); + ensure(false); + break; + } + + // Create result + const TVoxelSharedRef Result = FVoxelSpawnerProxyResult::CreateFromType(ProxyType, Proxy); + Result->MarkDirty(); + Result->SerializeProxy(GuidArchive, Version); + ProxyResults.Add(Result); + } + + if (ProxyResults.Num() != NumProxyResults) + { + // Can only be caused if we broke because of the error + continue; + } + + ensure(GuidArchive.AtEnd()); + } + check(Positions.Num() == ProxyResults.Num()); + + // Load the results into the group data + for (int32 ResultIndex = 0; ResultIndex < ProxyResults.Num(); ResultIndex++) + { + TArray>& ExistingProxiesResults = GroupData.ChunksData.FindOrAdd(Positions[ResultIndex]).SpawnerProxiesResults; + ExistingProxiesResults.SetNum(GroupData.SpawnerProxies.Num()); + + TVoxelSharedPtr& ExistingProxyResult = ExistingProxiesResults[SpawnerIndex]; + + bool bIsCreated = false; + if (ExistingProxyResult.IsValid()) + { + bIsCreated = ExistingProxyResult->IsCreated(); + if (bIsCreated) + { + // Destroy existing result + ExistingProxyResult->Destroy(); + } + ExistingProxyResult.Reset(); + } + + ExistingProxyResult = ProxyResults[ResultIndex]; + + if (bIsCreated) + { + // If existing result was created, create this one too + ExistingProxyResult->Create(); + } + } + + GroupData.UpdateStats(); + } + } +} + +void FVoxelSpawnerManager::SaveTo(FVoxelSpawnersSaveImpl& Save) +{ + VOXEL_FUNCTION_COUNTER(); + + Save.Guid = FGuid::NewGuid(); + + FLargeMemoryWriter Archive; + + int32 VoxelCustomVersion = FVoxelSpawnersSaveVersion::LatestVersion; + Archive << VoxelCustomVersion; + + Serialize(Archive, FVoxelSpawnersSaveVersion::LatestVersion); + + FVoxelSerializationUtilities::CompressData(Archive, Save.CompressedData); +} + +void FVoxelSpawnerManager::LoadFrom(const FVoxelSpawnersSaveImpl& Save) +{ + VOXEL_FUNCTION_COUNTER(); + + TArray64 UncompressedData; + FVoxelSerializationUtilities::DecompressData(Save.CompressedData, UncompressedData); + + FLargeMemoryReader Archive(UncompressedData.GetData(), UncompressedData.Num()); + + int32 Version = -1; + Archive << Version; + check(Version >= 0); + + Serialize(Archive, FVoxelSpawnersSaveVersion::Type(Version)); + + ensure(Archive.AtEnd() && !Archive.IsError()); +} + +bool FVoxelSpawnerManager::GetMeshSpawnerTransforms(const FGuid& SpawnerGuid, TArray& OutTransforms) const +{ + int32 GroupIndex; + int32 SpawnerIndex; + if (!FindSpawnerByGuid(SpawnerGuid, GroupIndex, SpawnerIndex)) + { + return false; + } + + auto& GroupData = GroupsData[GroupIndex]; + + FScopeLock Lock(&GroupData.Section); + for (auto& It : GroupData.ChunksData) + { + auto& Results = It.Value.SpawnerProxiesResults; + if (!ensure(Results.IsValidIndex(SpawnerIndex))) + { + continue; + } + + auto& Result = Results[SpawnerIndex]; + if (Result->Type == EVoxelSpawnerProxyType::EmptySpawner) + { + continue; + } + if (!ensure(Result->Type == EVoxelSpawnerProxyType::MeshSpawner)) + { + return false; + } + + auto& MeshResult = static_cast(*Result); + OutTransforms.Add(MeshResult.GetTransforms()); + } + + return true; +} + +int32 FVoxelSpawnerManager::GetTaskCount() const +{ + return TaskCounter.GetValue(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + // TODO: time limit? + FlushCreateQueue_GameThread(); + + if (CVarShowChunks.GetValueOnGameThread()) + { + int32 ColorCounter = 0; + uint64 MessageCounter = OBJECT_LINE_ID(); + + for (int32 GroupIndex = 0; GroupIndex < ThreadSafeConfig.Groups.Num(); GroupIndex++) + { + const auto& Group = ThreadSafeConfig.Groups[GroupIndex]; + const auto& GroupData = GroupsData[GroupIndex]; + + FColor Color = FLinearColor::MakeFromHSV8(255.f * ColorCounter / ThreadSafeConfig.Groups.Num(), 255, 255).ToFColor(true); + Color.A = 255; + + ColorCounter++; + + GEngine->AddOnScreenDebugMessage(MessageCounter++, DeltaTime * 1.5f, Color, + FString::Printf(TEXT("LOD: %d (chunk size %d); Generation Distance: %d chunks (%d voxels); Infinite: %s"), + Group.LOD, + RENDER_CHUNK_SIZE << Group.LOD, + Group.GenerationDistanceInChunks, + Group.GenerationDistanceInChunks * (RENDER_CHUNK_SIZE << Group.LOD), + *LexToString(Group.bInfiniteGenerationDistance))); + + for (const auto& Spawner : Group.Spawners) + { + GEngine->AddOnScreenDebugMessage(MessageCounter++, DeltaTime * 1.5f, Color, TEXT(" ") + Spawner.DebugName); + } + + for (auto& It : GroupData.ChunksData) + { + const FVoxelIntBox Bounds(It.Key, It.Key + GroupData.ChunkSize); + UVoxelDebugUtilities::DrawDebugIntBox(Settings.VoxelWorldInterface.Get(), Bounds, DeltaTime * 1.5f, 0, Color); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::SpawnGroup_GameThread(FVoxelIntBox Bounds, int32 GroupIndex) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + SpawnedSpawnersBounds += Bounds; + + TArray SpawnersToSpawnAsync; + TArray> SpawnerProxiesResultsToCreateNow; + + FSpawnerGroupData& GroupData = GroupsData[GroupIndex]; + + GroupData.Section.Lock(); + ////////////////////////////////////////////////////////////////////////////// + FSpawnerGroupChunkData& ChunkData = GroupData.ChunksData.FindOrAdd(GroupData.GetChunkKey(Bounds)); + const FVoxelCancelCounter CancelCounter(ChunkData.UpdateIndex); + ChunkData.SpawnerProxiesResults.SetNum(GroupData.SpawnerProxies.Num()); + for (int32 SpawnerIndex = 0; SpawnerIndex < GroupData.SpawnerProxies.Num(); SpawnerIndex++) + { + const auto& SpawnerProxyResult = ChunkData.SpawnerProxiesResults[SpawnerIndex]; + ensure(!SpawnerProxyResult.IsValid() || &SpawnerProxyResult->Proxy == GroupData.SpawnerProxies[SpawnerIndex]); + if (!SpawnerProxyResult.IsValid()) + { + SpawnersToSpawnAsync.Add(SpawnerIndex); + } + else + { + ensure(!SpawnerProxyResult->IsCreated() || !SpawnerProxyResult->CanBeDespawned()); + if (SpawnerProxyResult->CanBeDespawned()) + { + // If we cannot be despawned we weren't destroyed, and there is nothing to do + SpawnerProxiesResultsToCreateNow.Add(SpawnerProxyResult); + } + } + } + GroupData.UpdateStats(); + ////////////////////////////////////////////////////////////////////////////// + GroupData.Section.Unlock(); + + if (SpawnersToSpawnAsync.Num() > 0) + { + Settings.Pool->QueueTask( + EVoxelTaskType::FoliageBuild, + new FVoxelSpawnerTask(*this, Bounds, GroupIndex, SpawnersToSpawnAsync, CancelCounter)); + } + + for (auto& Result : SpawnerProxiesResultsToCreateNow) + { + Result->Create(); + } +} + +void FVoxelSpawnerManager::DestroyGroup_GameThread(FVoxelIntBox Bounds, int32 GroupIndex) +{ + VOXEL_FUNCTION_COUNTER(); + + FSpawnerGroupData& GroupData = GroupsData[GroupIndex]; + + FScopeLock Lock(&GroupData.Section); + const FIntVector ChunkKey = GroupData.GetChunkKey(Bounds); + auto* ChunkData = GroupData.ChunksData.Find(ChunkKey); + if (!ensure(ChunkData)) return; + + // Cancel existing tasks + ChunkData->CancelTasks(); + + // Destroy results that can be + bool bCanFreeMemory = true; + for (auto& Result : ChunkData->SpawnerProxiesResults) + { + // Can be invalid if the task was started but not completed + if (!Result.IsValid()) continue; + + if (!Result->CanBeDespawned()) + { + // Can't do anything + bCanFreeMemory = false; + continue; + } + + if (Result->IsCreated()) + { + Result->Destroy(); + } + else + { + FScopeLock CreateQueueLock(&CreateQueueSection); + ensure(CreateQueue.RemoveSwap(Result) == 1); + } + + if (!Result->NeedsToBeSaved()) + { + Result.Reset(); + } + else + { + bCanFreeMemory = false; + } + } + + // Free up memory + if (bCanFreeMemory) + { + GroupData.ChunksData.Remove(ChunkKey); + } + + GroupData.UpdateStats(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::SpawnGroup_AnyThread(const FVoxelSpawnerTask& Task) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (Task.CancelCounter.IsCanceled()) return; + + const FVoxelSpawnerConfigGroup& Group = ThreadSafeConfig.Groups[Task.GroupIndex]; + check(Task.Bounds.Size() == FIntVector(RENDER_CHUNK_SIZE << Group.LOD)); + + const bool bShowDebugRays = CVarShowVoxelSpawnerRays.GetValueOnAnyThread() != 0; + const bool bShowDebugHits = CVarShowVoxelSpawnerHits.GetValueOnAnyThread() != 0; + + TUniquePtr RayHandler; + if (Group.SpawnerType == EVoxelSpawnerType::Ray) + { + // Needs to be done before the Lock as it will lock the data too + TArray Indices; + TArray Vertices; + Settings.Renderer->CreateGeometry_AnyThread(Group.LOD, Task.Bounds.Min, Indices, Vertices); + +#if USE_EMBREE_VOXEL + RayHandler = MakeUnique(bShowDebugRays || bShowDebugHits, MoveTemp(Indices), MoveTemp(Vertices)); +#else + LOG_VOXEL(Error, TEXT("Embree is required for ray spawners!")); + return; +#endif + + if (!ensure(!RayHandler->HasError())) + { + return; + } + } + + if (Task.CancelCounter.IsCanceled()) return; + + const FVoxelIntBox LockedBounds = Task.Bounds.Extend(2); // For neighbors: +1; For max included vs excluded: +1 + FVoxelReadScopeLock Lock(*Settings.Data, LockedBounds, FUNCTION_FNAME); + const FVoxelConstDataAccelerator Accelerator(*Settings.Data, LockedBounds); + + if (Task.CancelCounter.IsCanceled()) return; + + const FVoxelHitGenerator::FParameters Parameters{ + Task.GroupIndex, + Task.Bounds, + Task.SpawnersToSpawn, + Group, + ThreadSafeConfig, + Task.CancelCounter, + Accelerator + }; + + TUniquePtr HitGenerator; + if (Group.SpawnerType == EVoxelSpawnerType::Height) + { + HitGenerator = MakeUnique(Parameters); + } + else + { + HitGenerator = MakeUnique(Parameters, *RayHandler); + } + + const auto HitsMap = HitGenerator->Generate(); + + if (Task.CancelCounter.IsCanceled()) return; + + if (bShowDebugRays || bShowDebugHits) + { + RayHandler->ShowDebug(Settings.VoxelWorldInterface, Task.Bounds.Min, bShowDebugRays, bShowDebugHits); + } + + if (Task.CancelCounter.IsCanceled()) return; + + ProcessHits(Task, Accelerator, Lock, HitsMap); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::ProcessHits( + const FVoxelSpawnerTask& Task, + const FVoxelConstDataAccelerator& Accelerator, + FVoxelReadScopeLock& Lock, + const TMap>& HitsMap) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (Task.CancelCounter.IsCanceled()) return; + + if (CVarShowVoxelSpawnerPositions.GetValueOnAnyThread() != 0) + { + VOXEL_ASYNC_SCOPE_COUNTER("Debug Hits"); + AsyncTask(ENamedThreads::GameThread, [Hits = HitsMap, VoxelWorld = Settings.VoxelWorldInterface, BoundsMin = Task.Bounds.Min]() + { + if (VoxelWorld.IsValid()) + { + if (UWorld* World = VoxelWorld->GetWorld()) + { + for (auto& It : Hits) + { + auto& Color = GColorList.GetFColorByIndex(FMath::RandRange(0, GColorList.GetColorsNum() - 1)); + for (auto& Hit : It.Value) + { + auto Position = VoxelWorld->LocalToGlobalFloat(BoundsMin + FVoxelVector(Hit.LocalPosition)); + DrawDebugPoint(World, Position, 5, Color, true, 1000.f); + DrawDebugLine(World, Position, Position + 50 * Hit.Normal, Color, true, 1000.f); + } + } + } + } + }); + } + + if (Task.CancelCounter.IsCanceled()) return; + + const FVoxelSpawnerConfigGroup& Group = ThreadSafeConfig.Groups[Task.GroupIndex]; + FSpawnerGroupData& GroupData = GroupsData[Task.GroupIndex]; + + TMap> SpawnerProxiesResults; + for (int32 SpawnerIndex : Task.SpawnersToSpawn) + { + if (Task.CancelCounter.IsCanceled()) return; + + const FVoxelSpawnerConfigSpawner& Spawner = Group.Spawners[SpawnerIndex]; + FVoxelSpawnerProxy& SpawnerProxy = *GroupData.SpawnerProxies[SpawnerIndex]; + + const TArray EmptyHits; // Won't be in HitsMap if no hits + auto& Hits = HitsMap.Contains(SpawnerIndex) ? HitsMap[SpawnerIndex] : EmptyHits; + + TVoxelSharedPtr Result; + + if (Hits.Num() > 0) + { + TUniquePtr UniqueResult = SpawnerProxy.ProcessHits(Task.Bounds, Hits, Accelerator); + Result = TVoxelSharedPtr(UniqueResult.Release()); + check(!Result.IsValid() || Result->Type == SpawnerProxy.Type); + } + + if (!Result.IsValid()) + { + // Need to create empty results that can be saved if the voxel data is modified + Result = MakeVoxelShared(SpawnerProxy); + } + + Result->SetCanBeSaved(Spawner.bSave); + Result->SetCanBeDespawned(!Spawner.bDoNotDespawn); + + SpawnerProxiesResults.Add(SpawnerIndex, Result); + } + + // Important: Unlock before locking GroupLock to avoid deadlocks + Lock.Unlock(); + + FScopeLock GroupLock(&GroupData.Section); + if (Task.CancelCounter.IsCanceled()) return; + + auto& ChunkData = GroupData.ChunksData.FindChecked(GroupData.GetChunkKey(Task.Bounds)); + if (!ensure(ChunkData.SpawnerProxiesResults.Num() == GroupData.SpawnerProxies.Num())) return; + + // Atomically add to the queues and save to chunk data + for (auto& It : SpawnerProxiesResults) + { + ensure(!ChunkData.SpawnerProxiesResults[It.Key].IsValid()); + ChunkData.SpawnerProxiesResults[It.Key] = It.Value; + } + { + FScopeLock ScopeLock(&CreateQueueSection); + for (auto& It : SpawnerProxiesResults) + { + CreateQueue.Add(It.Value); + } + } + + for (const auto& SpawnerProxyResult : ChunkData.SpawnerProxiesResults) ensure(SpawnerProxyResult.IsValid()); + ensure(!Task.CancelCounter.IsCanceled()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::UpdateTaskCount() const +{ + Settings.DebugManager->ReportFoliageTaskCount(TaskCounter.GetValue()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSpawnerManager::FlushCreateQueue_GameThread() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + CreateQueueSection.Lock(); + const auto QueueData = MoveTemp(CreateQueue); + CreateQueueSection.Unlock(); + + for (auto& Result : QueueData) + { + check(Result.IsValid()); + Result->Create(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSpawnerManager::IterateResultsInBounds(const FVoxelIntBox& InBounds, T ApplyToResult_ReturnsShouldRebuild) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + if (!SpawnedSpawnersBounds.IsValid()) return; + + // As we are iterating Bounds, we need it as small as possible + const FVoxelIntBox Bounds = InBounds.Overlap(SpawnedSpawnersBounds.GetBox()); + + for (int32 GroupIndex = 0; GroupIndex < GroupsData.Num(); GroupIndex++) + { + FSpawnerGroupData& GroupData = GroupsData[GroupIndex]; + TArray ChunksToRebuild; + { + FScopeLock Lock(&GroupData.Section); + + const FVoxelIntBox KeyBounds = Bounds.MakeMultipleOfBigger(GroupData.ChunkSize); + + const auto IterateChunkData = [&](const FVoxelIntBox& ChunkBounds, FSpawnerGroupChunkData& ChunkData) + { + bool bNeedRebuild = false; + for (TVoxelSharedPtr& SpawnerProxyResult : ChunkData.SpawnerProxiesResults) + { + if (SpawnerProxyResult.IsValid()) + { + bNeedRebuild |= ApplyToResult_ReturnsShouldRebuild(SpawnerProxyResult); + } + } + + if (bNeedRebuild) + { + ChunksToRebuild.Emplace(ChunkBounds); + // If we are rebuilding, we need to cancel existing tasks + ChunkData.CancelTasks(); + } + }; + + // Check if it's faster to iterate ChunksData directly + if (FVoxelIntBox(KeyBounds.Min / GroupData.ChunkSize, KeyBounds.Max / GroupData.ChunkSize).Count() < GroupData.ChunksData.Num()) + { + KeyBounds.Iterate(GroupData.ChunkSize, [&](int32 X, int32 Y, int32 Z) + { + const FVoxelIntBox ChunkBounds = FVoxelIntBox(FIntVector(X, Y, Z), FIntVector(X, Y, Z) + GroupData.ChunkSize); + if (FSpawnerGroupChunkData* ChunkData = GroupData.ChunksData.Find(GroupData.GetChunkKey(ChunkBounds))) + { + IterateChunkData(ChunkBounds, *ChunkData); + } + }); + } + else + { + for (auto& It : GroupData.ChunksData) + { + const FVoxelIntBox ChunkBounds(It.Key, It.Key + GroupData.ChunkSize); + if (ChunkBounds.Intersect(Bounds)) + { + IterateChunkData(ChunkBounds, It.Value); + } + } + } + } + + for (auto& Chunk : ChunksToRebuild) + { + SpawnGroup_GameThread(Chunk, GroupIndex); + } + } +} + +bool FVoxelSpawnerManager::FindSpawnerByGuid(const FGuid& Guid, int32& GroupIndex, int32& SpawnerIndex) const +{ + for (GroupIndex = 0; GroupIndex < ThreadSafeConfig.Groups.Num(); GroupIndex++) + { + auto& Group = ThreadSafeConfig.Groups[GroupIndex]; + for (SpawnerIndex = 0; SpawnerIndex < Group.Spawners.Num(); SpawnerIndex++) + { + if (Group.Spawners[SpawnerIndex].Guid == Guid) + { + return true; + } + } + } + return false; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.cpp new file mode 100644 index 00000000..b6a695cf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.cpp @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerRandomGenerator.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" +#include "Math/Sobol.h" + +FVoxelSpawnerSobolRandomGenerator::FVoxelSpawnerSobolRandomGenerator(int32 CellBits) + : CellBits(CellBits) +{ +} + +void FVoxelSpawnerSobolRandomGenerator::Init(int32 SeedX, int32 SeedY) +{ + Value = FSobol::Evaluate(0, CellBits, FIntPoint::ZeroValue, FIntPoint(SeedX, SeedY)); +} + +void FVoxelSpawnerSobolRandomGenerator::Next() const +{ + Value = FSobol::Next(Index++, CellBits, Value); +} + +void FVoxelSpawnerHaltonRandomGenerator::Init(int32 SeedX, int32 SeedY) +{ + // No symmetry! + Index = FVoxelUtilities::MurmurHash32(SeedX) + FVoxelUtilities::MurmurHash32(SeedY * 23); + Next(); +} + +void FVoxelSpawnerHaltonRandomGenerator::Next() const +{ + Value.X = FVoxelUtilities::Halton<2>(Index); + Value.Y = FVoxelUtilities::Halton<3>(Index); + Index++; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.h new file mode 100644 index 00000000..c2a1a756 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRandomGenerator.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class FVoxelSpawnerRandomGenerator +{ +public: + virtual ~FVoxelSpawnerRandomGenerator() = default; + + inline FVector2D GetValue() const + { + return Value; + } + + virtual void Init(int32 SeedX, int32 SeedY) = 0; + virtual void Next() const = 0; + +protected: + mutable FVector2D Value; +}; + +class FVoxelSpawnerSobolRandomGenerator : public FVoxelSpawnerRandomGenerator +{ +public: + FVoxelSpawnerSobolRandomGenerator(int32 CellBits = 1); + + virtual void Init(int32 SeedX, int32 SeedY) override; + virtual void Next() const override; + +private: + const int32 CellBits; + mutable int32 Index = 0; +}; + +class FVoxelSpawnerHaltonRandomGenerator : public FVoxelSpawnerRandomGenerator +{ +public: + FVoxelSpawnerHaltonRandomGenerator() = default; + + virtual void Init(int32 SeedX, int32 SeedY) override; + virtual void Next() const override; + +private: + mutable uint32 Index = 0; +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.cpp new file mode 100644 index 00000000..d6dbcb0f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.cpp @@ -0,0 +1,65 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerRayHandler.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelWorldInterface.h" +#include "VoxelMinimal.h" + +#include "Async/Async.h" +#include "DrawDebugHelpers.h" + +FVoxelSpawnerRayHandler::FVoxelSpawnerRayHandler(bool bStoreDebugRays) + : bStoreDebugRays(bStoreDebugRays) +{ + +} + +bool FVoxelSpawnerRayHandler::TraceRay(const FVector& Start, const FVector& Direction, FVector& HitNormal, FVector& HitPosition) const +{ + ensure(Direction.IsNormalized()); + + const bool bHit = TraceRayInternal(Start, Direction, HitNormal, HitPosition); + if (bStoreDebugRays) + { + FDebugRay Ray; + Ray.Start = Start; + Ray.Direction = Direction; + Ray.bHit = bHit; + Ray.HitNormal = HitNormal; + Ray.HitPosition = HitPosition; + DebugRays.Add(Ray); + } + return bHit; +} + +void FVoxelSpawnerRayHandler::ShowDebug( + TWeakObjectPtr VoxelWorld, + const FIntVector& ChunkPosition, + bool bShowDebugRays, + bool bShowDebugHits) const +{ + AsyncTask(ENamedThreads::GameThread, [DebugRays = DebugRays, VoxelWorld, ChunkPosition, bShowDebugRays, bShowDebugHits]() + { + if (VoxelWorld.IsValid()) + { + if (UWorld* World = VoxelWorld->GetWorld()) + { + for (auto& Ray : DebugRays) + { + if (bShowDebugRays) + { + // Rays start are 4 * RENDER_CHUNK_SIZE off + auto Start = VoxelWorld->LocalToGlobalFloat(Ray.Start + FVector(ChunkPosition) + Ray.Direction * 3.5f * RENDER_CHUNK_SIZE); + auto End = VoxelWorld->LocalToGlobalFloat(Ray.Start + FVector(ChunkPosition) + Ray.Direction * 5.5f * RENDER_CHUNK_SIZE); + DrawDebugDirectionalArrow(World, Start, End, 200, FColor::Red, true, 1000.f); + } + if (bShowDebugHits && Ray.bHit) + { + auto HitPosition = VoxelWorld->LocalToGlobalFloat(Ray.HitPosition + FVector(ChunkPosition)); + DrawDebugPoint(World, HitPosition, 5, FColor::Blue, true, 1000.f); + } + } + } + } + }); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.h new file mode 100644 index 00000000..50da955d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerRayHandler.h @@ -0,0 +1,40 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class AVoxelWorldInterface; + +class FVoxelSpawnerRayHandler +{ +public: + FVoxelSpawnerRayHandler(bool bStoreDebugRays); + virtual ~FVoxelSpawnerRayHandler() = default; + + virtual bool HasError() const = 0; + bool TraceRay(const FVector& Start, const FVector& Direction, FVector& HitNormal, FVector& HitPosition) const; + +protected: + virtual bool TraceRayInternal(const FVector& Start, const FVector& Direction, FVector& HitNormal, FVector& HitPosition) const = 0; + +public: + void ShowDebug( + TWeakObjectPtr VoxelWorld, + const FIntVector& ChunkPosition, + bool bShowDebugRays, + bool bShowDebugHits) const; + +private: + struct FDebugRay + { + FVector Start; + FVector Direction; + + bool bHit; + FVector HitNormal; + FVector HitPosition; + }; + const bool bStoreDebugRays; + mutable TArray DebugRays; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.cpp new file mode 100644 index 00000000..4c423e83 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.cpp @@ -0,0 +1,111 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawnerUtilities.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelSpawnerRandomGenerator.h" + +TUniquePtr FVoxelSpawnerUtilities::GetRandomGenerator(const FVoxelSpawnerConfigSpawner& Spawner) +{ + if (Spawner.RandomGenerator == EVoxelSpawnerConfigElementRandomGenerator::Sobol) + { + return MakeUnique(); + + } + else + { + check(Spawner.RandomGenerator == EVoxelSpawnerConfigElementRandomGenerator::Halton); + return MakeUnique(); + } +} + +FIntVector FVoxelSpawnerUtilities::GetClosestNotEmptyPoint(const FVoxelConstDataAccelerator& Accelerator, const FVoxelVector& Position) +{ + FIntVector ClosestPoint; + v_flt Distance = MAX_vflt; + for (auto& Neighbor : FVoxelUtilities::GetNeighbors(Position)) + { + if (!Accelerator.GetValue(Neighbor, 0).IsEmpty()) + { + const v_flt PointDistance = (FVoxelVector(Neighbor) - Position).SizeSquared(); + if (PointDistance < Distance) + { + Distance = PointDistance; + ClosestPoint = Neighbor; + } + } + } + if (/*ensure*/(Distance < 100)) + { + return ClosestPoint; + } + else + { + return FVoxelUtilities::RoundToInt(Position); + } +} + +void FVoxelSpawnerUtilities::GetSphereBasisFromBounds( + const FVoxelIntBox& Bounds, + FVoxelVector& OutBasisX, + FVoxelVector& OutBasisY, + FVoxelVector& OutBasisZ) +{ + // Find closest corner + const FVoxelVector Direction = -Bounds.GetCenter().GetSafeNormal(); + + const FVoxelVector AbsDirection = Direction.GetAbs(); + const float Max = AbsDirection.GetMax(); + const FVoxelVector Vector = + Max == AbsDirection.X + ? FVoxelVector(0, 1, 0) + : Max == AbsDirection.Y + ? FVoxelVector(0, 0, 1) + : FVoxelVector(1, 0, 0); + + OutBasisX = Direction ^ Vector; + OutBasisY = Direction ^ OutBasisX; + OutBasisZ = Direction; + + OutBasisX.Normalize(); + OutBasisY.Normalize(); + + ensure(OutBasisX.GetAbsMax() > KINDA_SMALL_NUMBER); + ensure(OutBasisY.GetAbsMax() > KINDA_SMALL_NUMBER); +} + +void FVoxelSpawnerUtilities::GetBasisFromBounds( + const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig, + const FVoxelIntBox& Bounds, + FVoxelVector& OutBasisX, + FVoxelVector& OutBasisY) +{ + if (ThreadSafeConfig.WorldType == EVoxelSpawnerConfigRayWorldType::Flat) + { + OutBasisX = FVoxelVector::RightVector; + OutBasisY = FVoxelVector::ForwardVector; + } + else + { + check(ThreadSafeConfig.WorldType == EVoxelSpawnerConfigRayWorldType::Sphere); + FVoxelVector OutBasisZ; + GetSphereBasisFromBounds(Bounds, OutBasisX, OutBasisY, OutBasisZ); + + OutBasisX *= 1.5; // Hack to avoid holes + OutBasisY *= 1.5; + } +} + +FVoxelVector FVoxelSpawnerUtilities::GetRayDirection(const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig, const FVoxelVector& Start, const FIntVector& ChunkPosition) +{ + if (ThreadSafeConfig.WorldType == EVoxelSpawnerConfigRayWorldType::Flat) + { + return -FVoxelVector::UpVector; + } + else + { + check(ThreadSafeConfig.WorldType == EVoxelSpawnerConfigRayWorldType::Sphere); + return -(FVoxelVector(ChunkPosition) + Start).GetSafeNormal(); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.h new file mode 100644 index 00000000..47aee624 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerUtilities.h @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelVector; +struct FVoxelIntBox; + +struct FVoxelSpawnerConfigSpawner; +struct FVoxelSpawnerThreadSafeConfig; + +class FVoxelConstDataAccelerator; +class FVoxelSpawnerRandomGenerator; + +struct FVoxelSpawnerUtilities +{ + static TUniquePtr GetRandomGenerator(const FVoxelSpawnerConfigSpawner& Spawner); + + static FIntVector GetClosestNotEmptyPoint(const FVoxelConstDataAccelerator& Accelerator, const FVoxelVector& Position); + + static void GetSphereBasisFromBounds( + const FVoxelIntBox& Bounds, + FVoxelVector& OutBasisX, + FVoxelVector& OutBasisY, + FVoxelVector& OutBasisZ); + + static void GetBasisFromBounds( + const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig, + const FVoxelIntBox& Bounds, + FVoxelVector& OutBasisX, + FVoxelVector& OutBasisY); + + static FVoxelVector GetRayDirection( + const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig, + const FVoxelVector& Start, + const FIntVector& ChunkPosition); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelStaticWorld.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelStaticWorld.cpp new file mode 100644 index 00000000..c13fc379 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelStaticWorld.cpp @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelStaticWorld.h" +#include "VoxelMinimal.h" +#include "Components/StaticMeshComponent.h" + +AVoxelStaticWorld::AVoxelStaticWorld() +{ +} + +#if WITH_EDITOR +void AVoxelStaticWorld::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty && + PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_STATIC(AVoxelStaticWorld, BaseMesh) && + PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + for (auto* Mesh : Meshes) + { + auto* StaticMesh = Mesh->GetStaticMesh(); + auto RelativeTransform = Mesh->GetRelativeTransform(); + Mesh->ReinitializeProperties(BaseMesh); + Mesh->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform); + Mesh->SetStaticMesh(StaticMesh); + Mesh->SetRelativeTransform(RelativeTransform); + Mesh->RegisterComponent(); + } + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTexture.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTexture.cpp new file mode 100644 index 00000000..93b3b7b5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTexture.cpp @@ -0,0 +1,422 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTexture.h" +#include "VoxelMessages.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "Engine/Texture2D.h" +#include "Engine/TextureRenderTarget2D.h" + +DEFINE_VOXEL_MEMORY_STAT(STAT_VoxelTextureMemory); + +static FAutoConsoleCommand CmdClearCache( + TEXT("voxel.texture.ClearCache"), + TEXT("Clears the voxel textures memory cache"), + FConsoleCommandDelegate::CreateStatic(&FVoxelTextureUtilities::ClearCache)); + +struct FVoxelTextureCacheKey +{ + TWeakObjectPtr Texture; + // Channel, in case it's a color texture converted to float + EVoxelRGBA Channel = EVoxelRGBA(-1); + + FVoxelTextureCacheKey() = default; + explicit FVoxelTextureCacheKey(TWeakObjectPtr Texture) + : Texture(Texture) + { + } + explicit FVoxelTextureCacheKey(TWeakObjectPtr Texture, EVoxelRGBA Channel) + : Texture(Texture) + , Channel(Channel) + { + } + + bool operator==(const FVoxelTextureCacheKey& Other) const + { + return Texture == Other.Texture && Channel == Other.Channel; + } + friend uint32 GetTypeHash(const FVoxelTextureCacheKey& Key) + { + return HashCombine(GetTypeHash(Key.Texture), GetTypeHash(Key.Channel)); + } +}; + +template +inline auto& GetVoxelTextureCacheMap() +{ + check(IsInGameThread()); + static TMap::FTextureData>> Map; + return Map; +} + +inline void ExtractTextureData(UTexture* Texture, int32& OutSizeX, int32& OutSizeY, TArray& OutData) +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsInGameThread()); + + if (auto* Texture2D = Cast(Texture)) + { + FTexture2DMipMap& Mip = Texture2D->PlatformData->Mips[0]; + OutSizeX = Mip.SizeX; + OutSizeY = Mip.SizeY; + + const int32 Size = OutSizeX * OutSizeY; + OutData.SetNumUninitialized(Size); + + auto& BulkData = Mip.BulkData; + if (!ensureAlways(BulkData.GetBulkDataSize() > 0)) + { + OutSizeX = 1; + OutSizeY = 1; + OutData.SetNum(1); + return; + } + + void* Data = BulkData.Lock(LOCK_READ_ONLY); + if (!ensureAlways(Data)) + { + Mip.BulkData.Unlock(); + OutSizeX = 1; + OutSizeY = 1; + OutData.SetNum(1); + return; + } + + FMemory::Memcpy(OutData.GetData(), Data, Size * sizeof(FColor)); + Mip.BulkData.Unlock(); + return; + } + + if (auto* TextureRenderTarget = Cast(Texture)) + { + FRenderTarget* RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource(); + if (ensure(RenderTarget)) + { + const auto Format = TextureRenderTarget->GetFormat(); + + OutSizeX = TextureRenderTarget->GetSurfaceWidth(); + OutSizeY = TextureRenderTarget->GetSurfaceHeight(); + + const int32 Size = OutSizeX * OutSizeY; + OutData.SetNumUninitialized(Size); + + switch (Format) + { + case PF_B8G8R8A8: + { + if (ensure(RenderTarget->ReadPixels(OutData))) return; + break; + } + case PF_R8G8B8A8: + { + if (ensure(RenderTarget->ReadPixels(OutData))) return; + break; + } + case PF_FloatRGBA: + { + TArray LinearColors; + LinearColors.SetNumUninitialized(Size); + if (ensure(RenderTarget->ReadLinearColorPixels(LinearColors))) + { + for (int32 Index = 0; Index < Size; Index++) + { + OutData[Index] = LinearColors[Index].ToFColor(false); + } + return; + } + break; + } + default: + ensure(false); + } + } + } + + ensure(false); + OutSizeX = 1; + OutSizeY = 1; + OutData.SetNum(1); +} + +TVoxelTexture FVoxelTextureUtilities::CreateFromTexture_Color(UTexture* Texture) +{ + VOXEL_FUNCTION_COUNTER(); + + auto& Data = GetVoxelTextureCacheMap().FindOrAdd(FVoxelTextureCacheKey(Texture)); + if (!Data.IsValid()) + { + FString Error; + if (!CanCreateFromTexture(Texture, Error)) + { + FVoxelMessages::Error("Can't create Voxel Texture: " + Error, Texture); + return {}; + } + + int32 SizeX = -1; + int32 SizeY = -1; + TArray TextureData; + ExtractTextureData(Texture, SizeX, SizeY, TextureData); + + Data = MakeVoxelShared::FTextureData>(); + Data->SetSize(SizeX, SizeY); + for (int32 Index = 0; Index < SizeX * SizeY; Index++) + { + Data->SetValue(Index, TextureData[Index]); + } + } + + return TVoxelTexture(Data.ToSharedRef()); +} + +TVoxelTexture FVoxelTextureUtilities::CreateFromTexture_Float(UTexture* Texture, EVoxelRGBA Channel) +{ + VOXEL_FUNCTION_COUNTER(); + + auto& Data = GetVoxelTextureCacheMap().FindOrAdd(FVoxelTextureCacheKey(Texture, Channel)); + if (!Data.IsValid()) + { + FString Error; + if (!CanCreateFromTexture(Texture, Error)) + { + FVoxelMessages::Error("Can't create Voxel Texture: " + Error, Texture); + return {}; + } + + const auto ColorTexture = CreateFromTexture_Color(Texture); + + Data = MakeVoxelShared::FTextureData>(); + Data->SetSize(ColorTexture.GetSizeX(), ColorTexture.GetSizeY()); + + const int32 Num = ColorTexture.GetSizeX() * ColorTexture.GetSizeY(); + for (int32 Index = 0; Index < Num; Index++) + { + const FColor Color = ColorTexture.GetTextureData()[Index]; + uint8 Result = 0; + switch (Channel) + { + case EVoxelRGBA::R: + Result = Color.R; + break; + case EVoxelRGBA::G: + Result = Color.G; + break; + case EVoxelRGBA::B: + Result = Color.B; + break; + case EVoxelRGBA::A: + Result = Color.A; + break; + } + const float Value = FVoxelUtilities::UINT8ToFloat(Result); + Data->SetValue(Index, Value); + } + } + return TVoxelTexture(Data.ToSharedRef()); +} + +bool FVoxelTextureUtilities::CanCreateFromTexture(UTexture* Texture, FString& OutError) +{ + if (!Texture) + { + OutError = "Invalid texture"; + return false; + } + if (auto* Texture2D = Cast(Texture)) + { +#if WITH_EDITORONLY_DATA + if (Texture2D->MipGenSettings != TextureMipGenSettings::TMGS_NoMipmaps) + { + OutError = "Texture MipGenSettings must be NoMipmaps"; + return false; + } +#endif + if (Texture2D->CompressionSettings != TextureCompressionSettings::TC_VectorDisplacementmap && + Texture2D->CompressionSettings != TextureCompressionSettings::TC_EditorIcon) + { + OutError = "Texture CompressionSettings must be VectorDisplacementmap or UserInterface2D"; + return false; + } + if (Texture2D->GetPixelFormat() != PF_B8G8R8A8) + { + OutError = "Texture pixel format must be B8G8R8A8, try switching CompressionSettings to VectorDisplacementmap"; + return false; + } + return true; + } + if (auto* TextureRenderTarget = Cast(Texture)) + { + const auto Format = TextureRenderTarget->GetFormat(); + if (Format != EPixelFormat::PF_R8G8B8A8 && + Format != EPixelFormat::PF_FloatRGBA && + Format != EPixelFormat::PF_B8G8R8A8) + { + OutError = "Render Target PixelFormat must be R8G8B8A8, B8G8R8A8 or R8G8B8A8 (is " + FString(GPixelFormats[Format].Name) + ")"; + return false; + } + if (!TextureRenderTarget->GameThread_GetRenderTargetResource()) + { + OutError = "Render Target resource must be created"; + return false; + } + return true; + } + OutError = "Texture must be a Texture2D or a TextureRenderTarget2D"; + return false; +} + +void FVoxelTextureUtilities::FixTexture(UTexture* Texture) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(Texture)) + { + return; + } +#if WITH_EDITORONLY_DATA + Texture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; +#endif + Texture->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap; + Texture->UpdateResource(); + Texture->MarkPackageDirty(); +} + +void FVoxelTextureUtilities::ClearCache() +{ + VOXEL_FUNCTION_COUNTER(); + + GetVoxelTextureCacheMap().Empty(); + GetVoxelTextureCacheMap().Empty(); +} + +void FVoxelTextureUtilities::ClearCache(UTexture* Texture) +{ + VOXEL_FUNCTION_COUNTER(); + + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture)); + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture)); + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture, EVoxelRGBA::R)); + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture, EVoxelRGBA::G)); + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture, EVoxelRGBA::B)); + GetVoxelTextureCacheMap().Remove(FVoxelTextureCacheKey(Texture, EVoxelRGBA::A)); +} + +void FVoxelTextureUtilities::CreateOrUpdateUTexture2D(const TVoxelTexture& Texture, UTexture2D*& InOutTexture) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!InOutTexture || + !InOutTexture->PlatformData || + InOutTexture->PlatformData->Mips.Num() == 0 || + InOutTexture->PlatformData->PixelFormat != EPixelFormat::PF_R32_FLOAT || + InOutTexture->GetSizeX() != Texture.GetSizeX() || + InOutTexture->GetSizeY() != Texture.GetSizeY()) + { + InOutTexture = UTexture2D::CreateTransient(Texture.GetSizeX(), Texture.GetSizeY(), EPixelFormat::PF_R32_FLOAT); + InOutTexture->CompressionSettings = TC_HDR; + InOutTexture->SRGB = false; + InOutTexture->Filter = TF_Bilinear; + } + + FTexture2DMipMap& Mip = InOutTexture->PlatformData->Mips[0]; + float* Data = reinterpret_cast(Mip.BulkData.Lock(LOCK_READ_WRITE)); + if (!ensureAlways(Data)) return; + + FMemory::Memcpy(Data, Texture.GetTextureData().GetData(), Texture.GetSizeX() * Texture.GetSizeY() * sizeof(float)); + + Mip.BulkData.Unlock(); + + InOutTexture->UpdateResource(); +} + +void FVoxelTextureUtilities::CreateOrUpdateUTexture2D(const TVoxelTexture& Texture, UTexture2D*& InOutTexture) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!InOutTexture || + !InOutTexture->PlatformData || + InOutTexture->PlatformData->Mips.Num() == 0 || + InOutTexture->PlatformData->PixelFormat != EPixelFormat::PF_B8G8R8A8 || + InOutTexture->GetSizeX() != Texture.GetSizeX() || + InOutTexture->GetSizeY() != Texture.GetSizeY()) + { + InOutTexture = UTexture2D::CreateTransient(Texture.GetSizeX(), Texture.GetSizeY(), EPixelFormat::PF_B8G8R8A8); + InOutTexture->CompressionSettings = TC_VectorDisplacementmap; + InOutTexture->SRGB = false; + InOutTexture->Filter = TF_Bilinear; + } + + FTexture2DMipMap& Mip = InOutTexture->PlatformData->Mips[0]; + FColor* Data = reinterpret_cast(Mip.BulkData.Lock(LOCK_READ_WRITE)); + if (!ensureAlways(Data)) return; + + FMemory::Memcpy(Data, Texture.GetTextureData().GetData(), Texture.GetSizeX() * Texture.GetSizeY() * sizeof(FColor)); + + Mip.BulkData.Unlock(); + + InOutTexture->UpdateResource(); +} + +TVoxelTexture FVoxelTextureUtilities::CreateColorTextureFromFloatTexture(const TVoxelTexture& Texture, EVoxelRGBA Channel, bool bNormalize) +{ + VOXEL_FUNCTION_COUNTER(); + + const auto GetColor = [&](float Value) + { + if (bNormalize) + { + Value = (Value - Texture.GetMin()) / (Texture.GetMax() - Texture.GetMin()); + } + const float ByteValue = FVoxelUtilities::FloatToUINT8(Value); + + FColor Color(ForceInit); + switch (Channel) + { + case EVoxelRGBA::R: + Color.R = ByteValue; + break; + case EVoxelRGBA::G: + Color.G = ByteValue; + break; + case EVoxelRGBA::B: + Color.B = ByteValue; + break; + case EVoxelRGBA::A: + Color.A = ByteValue; + break; + } + return Color; + }; + + auto Data = MakeVoxelShared::FTextureData>(); + Data->SetSize(Texture.GetSizeX(), Texture.GetSizeY()); + Data->SetBounds(GetColor(Texture.GetMin()), GetColor(Texture.GetMax())); + + const int32 Num = Texture.GetSizeX() * Texture.GetSizeY(); + for (int32 Index = 0; Index < Num; Index++) + { + const float Value = Texture.GetTextureData()[Index]; + Data->SetValue_NoBounds(Index, GetColor(Value)); + } + + return TVoxelTexture(Data); +} + +TVoxelTexture FVoxelTextureUtilities::Normalize(const TVoxelTexture& Texture) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + auto Data = MakeVoxelShared::FTextureData>(); + Data->SetSize(Texture.GetSizeX(), Texture.GetSizeY()); + Data->SetBounds(0, 1); + + const float Min = Texture.GetMin(); + const float Max = Texture.GetMax(); + const int32 Num = Texture.GetSizeX() * Texture.GetSizeY(); + for (int32 Index = 0; Index < Num; Index++) + { + const float Value = Texture.GetTextureData()[Index]; + Data->SetValue_NoBounds(Index,(Value - Min) / (Max - Min)); + } + + return TVoxelTexture(Data); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTextureUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTextureUtilities.cpp new file mode 100644 index 00000000..7cdc80b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTextureUtilities.cpp @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelTextureUtilities.h" +#include "VoxelMinimal.h" +#include "Engine/Texture2D.h" + +void FVoxelTextureUtilities::UpdateColorTexture(UTexture2D*& Texture, const FIntPoint& Size, const TArray& Colors) +{ + VOXEL_FUNCTION_COUNTER(); + + check(Colors.Num() == Size.X * Size.Y); + + if (!Texture || Texture->GetSizeX() != Size.X || Texture->GetSizeY() != Size.Y) + { + Texture = UTexture2D::CreateTransient(Size.X, Size.Y); + if (!ensure(Texture)) + { + return; + } + Texture->CompressionSettings = TC_HDR; + Texture->SRGB = false; + } + FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0]; + { + void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(Data, Colors.GetData(), Colors.Num() * sizeof(FColor)); + } + Mip.BulkData.Unlock(); + Texture->UpdateResource(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelThreadPool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelThreadPool.cpp new file mode 100644 index 00000000..a9bf64af --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelThreadPool.cpp @@ -0,0 +1,456 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelThreadPool.h" +#include "VoxelQueuedWork.h" +#include "VoxelMinimal.h" +#include "IVoxelPool.h" + +#include "HAL/Event.h" +#include "HAL/Runnable.h" +#include "HAL/RunnableThread.h" +#include "Misc/ScopeLock.h" +#include "Misc/ScopeExit.h" +#include "Async/TaskGraphInterfaces.h" + +DECLARE_DWORD_COUNTER_STAT(TEXT("VoxelThreadPoolDummyCounter"), STAT_VoxelThreadPoolDummyCounter, STATGROUP_ThreadPoolAsyncTasks); +DECLARE_DWORD_COUNTER_STAT(TEXT("Recomputed Voxel Tasks Priorities"), STAT_RecomputedVoxelTasksPriorities, STATGROUP_VoxelCounters); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelQueuedThreadPoolStats& FVoxelQueuedThreadPoolStats::Get() +{ + static FVoxelQueuedThreadPoolStats Stats; + return Stats; +} + +void FVoxelQueuedThreadPoolStats::Report(FName Name, double Time) +{ + FScopeLock Lock(&Section); + Times.FindOrAdd(Name) += Time; +} + +void FVoxelQueuedThreadPoolStats::LogTimes() const +{ + FScopeLock Lock(&Section); + LOG_VOXEL(Log, TEXT("#############################################")); + LOG_VOXEL(Log, TEXT("########## Voxel Thread Pool Stats ##########")); + LOG_VOXEL(Log, TEXT("#############################################")); + for (const auto& It : Times) + { + LOG_VOXEL(Log, TEXT("%s: %fs"), *It.Key.ToString(), It.Value); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FScopeLockWithStats +{ +public: + FScopeLockWithStats(FCriticalSection& InSynchObject) + : SynchObject(InSynchObject) + { + VOXEL_ASYNC_SCOPE_COUNTER("Lock"); + SynchObject.Lock(); + } + ~FScopeLockWithStats() + { + VOXEL_ASYNC_SCOPE_COUNTER("Unlock"); + SynchObject.Unlock(); + } + +private: + FCriticalSection& SynchObject; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelQueuedThread : public FRunnable +{ +public: + const FString ThreadName; + FVoxelQueuedThreadPool* const ThreadPool; + /** The event that tells the thread there is work to do. */ + FEvent* const DoWorkEvent; + + FVoxelQueuedThread(FVoxelQueuedThreadPool* Pool, const FString& ThreadName, uint32 StackSize, EThreadPriority ThreadPriority); + ~FVoxelQueuedThread(); + + //~ Begin FRunnable Interface + virtual uint32 Run() override; + //~ End FRunnable Interface + +private: + /** If true, the thread should exit. */ + FThreadSafeBool TimeToDie; + /** The work this thread is doing. */ + TAtomic QueuedWork; + + const TUniquePtr Thread; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelQueuedThread::FVoxelQueuedThread(FVoxelQueuedThreadPool* Pool, const FString& ThreadName, uint32 StackSize, EThreadPriority ThreadPriority) + : ThreadName(ThreadName) + , ThreadPool(Pool) + , DoWorkEvent(FPlatformProcess::GetSynchEventFromPool()) // Create event BEFORE thread + , TimeToDie(false) // BEFORE creating thread + , QueuedWork(nullptr) + , Thread(FRunnableThread::Create(this, *ThreadName, StackSize, ThreadPriority, FPlatformAffinity::GetPoolThreadMask())) +{ + check(Thread.IsValid()); +} + +FVoxelQueuedThread::~FVoxelQueuedThread() +{ + // Tell the thread it needs to die + TimeToDie = true; + // Trigger the thread so that it will come out of the wait state if + // it isn't actively doing work + DoWorkEvent->Trigger(); + // If waiting was specified, wait the amount of time. If that fails, + // brute force kill that thread. Very bad as that might leak. + Thread->WaitForCompletion(); + // Clean up the event + FPlatformProcess::ReturnSynchEventToPool(DoWorkEvent); +} + +uint32 FVoxelQueuedThread::Run() +{ + while (!TimeToDie) + { + // This will force sending the stats packet from the previous frame. + SET_DWORD_STAT(STAT_VoxelThreadPoolDummyCounter, 0); + // We need to wait for shorter amount of time + bool bContinueWaiting = true; + while (bContinueWaiting) + { + VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER("FVoxelQueuedThread::Run.WaitForWork"); + + // Wait for some work to do + bContinueWaiting = !DoWorkEvent->Wait(10); + } + + if (!TimeToDie) + { + IVoxelQueuedWork* LocalQueuedWork = ThreadPool->ReturnToPoolOrGetNextJob(this); + + while (LocalQueuedWork) + { + const FName Name = LocalQueuedWork->Name; + + const double StartTime = FPlatformTime::Seconds(); + + LocalQueuedWork->DoThreadedWork(); + // IMPORTANT: LocalQueuedWork should be considered as deleted after this line + + const double EndTime = FPlatformTime::Seconds(); + + FVoxelQueuedThreadPoolStats::Get().Report(Name, EndTime - StartTime); + + LocalQueuedWork = ThreadPool->ReturnToPoolOrGetNextJob(this); + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelQueuedThreadPoolSettings::FVoxelQueuedThreadPoolSettings( + const FString& PoolName, + uint32 NumThreads, + uint32 StackSize, + EThreadPriority ThreadPriority, + bool bConstantPriorities) + : PoolName(PoolName) + , NumThreads(NumThreads) + , StackSize(StackSize) + , ThreadPriority(ThreadPriority) + , bConstantPriorities(bConstantPriorities) +{ +} + +inline TArray> CreateThreads(FVoxelQueuedThreadPool* Pool) +{ +#if ENGINE_MINOR_VERSION < 26 + TRACE_THREAD_GROUP_SCOPE("VoxelThreadPool"); +#else + Trace::ThreadGroupBegin(TEXT("VoxelThreadPool")); + ON_SCOPE_EXIT + { + Trace::ThreadGroupEnd(); + }; +#endif + + auto& Settings = Pool->Settings; + const uint32 NumThreads = Settings.NumThreads; + + TArray> Threads; + Threads.Reserve(NumThreads); + for (uint32 ThreadIndex = 0; ThreadIndex < NumThreads; ThreadIndex++) + { + const FString Name = FString::Printf(TEXT("%s Thread %d"), *Settings.PoolName, ThreadIndex); + Threads.Add(MakeUnique(Pool, Name, Settings.StackSize, Settings.ThreadPriority)); + } + return Threads; +} + +FVoxelQueuedThreadPool::FVoxelQueuedThreadPool(const FVoxelQueuedThreadPoolSettings& Settings) + : Settings(Settings) + , AllThreads(CreateThreads(this)) +{ + QueuedThreads.Reserve(Settings.NumThreads); + for (auto& Thread : AllThreads) + { + QueuedThreads.Add(Thread.Get()); + } +} + +TVoxelSharedRef FVoxelQueuedThreadPool::Create(const FVoxelQueuedThreadPoolSettings& Settings) +{ + const auto Pool = TVoxelSharedRef(new FVoxelQueuedThreadPool(Settings)); + + TFunction ShutdownCallback = [WeakPool = MakeVoxelWeakPtr(Pool)]() + { + auto PoolPtr = WeakPool.Pin(); + if (PoolPtr.IsValid()) + { + PoolPtr->AbandonAllTasks(); + } + }; + FTaskGraphInterface::Get().AddShutdownCallback(ShutdownCallback); + + return Pool; +} + +FVoxelQueuedThreadPool::~FVoxelQueuedThreadPool() +{ + if (!TimeToDie) + { + AbandonAllTasks(); + } +} + +inline uint32 AddPriorityOffset(uint32 Priority, int32 PriorityOffset) +{ + return FMath::Clamp(int64(Priority) + PriorityOffset, MIN_uint32, MAX_uint32); +} + +FORCEINLINE void FVoxelQueuedThreadPool::FQueuedWorkInfo::RecomputePriority(double Time) +{ + Priority = AddPriorityOffset(Work->GetPriority(), PriorityOffset); + NextPriorityUpdateTime = Time + Work->PriorityDuration; +} + +void FVoxelQueuedThreadPool::AddQueuedWork(IVoxelQueuedWork* InQueuedWork, uint32 PriorityCategory, int32 PriorityOffset) +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsInGameThread()); + check(InQueuedWork); + + if (TimeToDie) + { + InQueuedWork->Abandon(); + return; + } + + FQueuedWorkInfo WorkInfo; + { + VOXEL_SCOPE_COUNTER("Compute Priority"); + WorkInfo = FQueuedWorkInfo(InQueuedWork, PriorityCategory, PriorityOffset); + } + + { + VOXEL_SCOPE_COUNTER("Lock"); + Section.Lock(); + } + { + VOXEL_SCOPE_COUNTER("Add Work"); + if (Settings.bConstantPriorities) + { + WorkInfo.RecomputePriority(FPlatformTime::Seconds()); + StaticQueuedWorks.push(WorkInfo); + } + else + { + QueuedWorks.Add(WorkInfo); + } + } + + { + VOXEL_SCOPE_COUNTER("Wake up threads"); + for (auto* QueuedThread : QueuedThreads) + { + QueuedThread->DoWorkEvent->Trigger(); + } + QueuedThreads.Reset(); + } + + { + VOXEL_SCOPE_COUNTER("Unlock"); + Section.Unlock(); + } +} + +void FVoxelQueuedThreadPool::AddQueuedWorks(const TArray& InQueuedWorks, uint32 PriorityCategory, int32 PriorityOffset) +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsInGameThread()); + + if (TimeToDie) + { + for (auto* InQueuedWork : InQueuedWorks) + { + InQueuedWork->Abandon(); + } + return; + } + + { + VOXEL_SCOPE_COUNTER("Lock"); + Section.Lock(); + } + + { + if (!Settings.bConstantPriorities) + { + VOXEL_SCOPE_COUNTER("Reserve"); + QueuedWorks.Reserve(QueuedWorks.Num() + InQueuedWorks.Num()); + } + VOXEL_SCOPE_COUNTER("Add Works"); + for (auto* InQueuedWork : InQueuedWorks) + { + FQueuedWorkInfo WorkInfo(InQueuedWork, PriorityCategory, PriorityOffset); + + if (Settings.bConstantPriorities) + { + WorkInfo.RecomputePriority(FPlatformTime::Seconds()); + StaticQueuedWorks.push(WorkInfo); + } + else + { + QueuedWorks.Add(WorkInfo); + } + } + } + + { + VOXEL_SCOPE_COUNTER("Wake up threads"); + for (auto* QueuedThread : QueuedThreads) + { + QueuedThread->DoWorkEvent->Trigger(); + } + QueuedThreads.Reset(); + } + + { + VOXEL_SCOPE_COUNTER("Unlock"); + Section.Unlock(); + } +} + +IVoxelQueuedWork* FVoxelQueuedThreadPool::ReturnToPoolOrGetNextJob(FVoxelQueuedThread* InQueuedThread) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(InQueuedThread); + + FScopeLockWithStats Lock(Section); + + if (QueuedWorks.Num() > 0) + { + check(!Settings.bConstantPriorities); + check(!TimeToDie); + + VOXEL_ASYNC_SCOPE_COUNTER("Voxel Thread Pool Recompute Priorities"); + + // Find best work. We recompute every priorities as the priorities can change (eg, the camera might have moved) + int32 BestIndex = -1; + uint64 BestPriority = 0; + int32 NumRecomputed = 0; + const double Time = FPlatformTime::Seconds(); + for (int32 Index = 0; Index < QueuedWorks.Num(); Index++) + { + auto& WorkInfo = QueuedWorks.GetData()[Index]; + if (WorkInfo.NextPriorityUpdateTime < Time) + { + NumRecomputed++; + WorkInfo.RecomputePriority(Time); + } + const uint64 Priority = WorkInfo.GetPriority(); + if (Priority >= BestPriority) + { + BestPriority = Priority; + BestIndex = Index; + } + } + + INC_DWORD_STAT_BY(STAT_RecomputedVoxelTasksPriorities, NumRecomputed); + + auto* Work = QueuedWorks[BestIndex].Work; + QueuedWorks.RemoveAtSwap(BestIndex); + check(Work); + return Work; + } + else if (!StaticQueuedWorks.empty()) + { + check(Settings.bConstantPriorities); + auto* Work = StaticQueuedWorks.top().Work; + StaticQueuedWorks.pop(); + check(Work); + return Work; + } + else + { + QueuedThreads.Add(InQueuedThread); + return nullptr; + } +} + +void FVoxelQueuedThreadPool::AbandonAllTasks() +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + ensure(!TimeToDie); + + { + FScopeLockWithStats Lock(Section); + TimeToDie = true; + // Clean up all queued objects + for (auto& WorkInfo : QueuedWorks) + { + WorkInfo.Work->Abandon(); + } + QueuedWorks.Reset(); + while (!StaticQueuedWorks.empty()) + { + StaticQueuedWorks.top().Work->Abandon(); + StaticQueuedWorks.pop(); + } + } + // Wait for all threads to finish up + while (true) + { + { + FScopeLockWithStats Lock(Section); + if (AllThreads.Num() == QueuedThreads.Num()) + { + break; + } + } + FPlatformProcess::Sleep(0.0f); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelBoxTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelBoxTools.cpp new file mode 100644 index 00000000..9c82341e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelBoxTools.cpp @@ -0,0 +1,270 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Gen/VoxelBoxTools.h" +#include "VoxelTools/Gen/VoxelGeneratedTools.h" +#include "VoxelTools/Impl/VoxelBoxToolsImpl.h" +#include "VoxelTools/Impl/VoxelBoxToolsImpl.inl" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelBoxTools::SetValueBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealValue = FVoxelValue(Value); + + GENERATED_TOOL_CALL(Value, FVoxelBoxToolsImpl::SetValueBox(Data, Bounds, RealValue)); +} + +void UVoxelBoxTools::SetValueBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealValue = FVoxelValue(Value); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelBoxToolsImpl::SetValueBox(Data, Bounds, RealValue)); +} + +void UVoxelBoxTools::SetValueBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealValue = FVoxelValue(Value); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelBoxToolsImpl::SetValueBox(Data, Bounds, RealValue)); +} + +void UVoxelBoxTools::SetValueBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealValue = FVoxelValue(Value); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelBoxToolsImpl::SetValueBox(Data, Bounds, RealValue)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelBoxTools::AddBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + GENERATED_TOOL_CALL(Value, FVoxelBoxToolsImpl::AddBox(Data, Bounds)); +} + +void UVoxelBoxTools::AddBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelBoxToolsImpl::AddBox(Data, Bounds)); +} + +void UVoxelBoxTools::AddBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelBoxToolsImpl::AddBox(Data, Bounds)); +} + +void UVoxelBoxTools::AddBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelBoxToolsImpl::AddBox(Data, Bounds)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelBoxTools::RemoveBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + GENERATED_TOOL_CALL(Value, FVoxelBoxToolsImpl::RemoveBox(Data, Bounds)); +} + +void UVoxelBoxTools::RemoveBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelBoxToolsImpl::RemoveBox(Data, Bounds)); +} + +void UVoxelBoxTools::RemoveBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelBoxToolsImpl::RemoveBox(Data, Bounds)); +} + +void UVoxelBoxTools::RemoveBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelBoxToolsImpl::RemoveBox(Data, Bounds)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelBoxTools::SetMaterialBox( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + GENERATED_TOOL_CALL(Material, FVoxelBoxToolsImpl::SetMaterialBox(Data, Bounds, PaintMaterial)); +} + +void UVoxelBoxTools::SetMaterialBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelBoxToolsImpl::SetMaterialBox(Data, Bounds, PaintMaterial)); +} + +void UVoxelBoxTools::SetMaterialBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelBoxToolsImpl::SetMaterialBox(Data, Bounds, PaintMaterial)); +} + +void UVoxelBoxTools::SetMaterialBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelBoxToolsImpl::SetMaterialBox(Data, Bounds, PaintMaterial)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelGeneratedTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelGeneratedTools.h new file mode 100644 index 00000000..19797d76 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelGeneratedTools.h @@ -0,0 +1,166 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" + +#define GENERATED_TOOL_FUNCTION_IMPL(Type) \ + if (!VoxelWorld) \ + { \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Voxel World is invalid!"), *FString(__FUNCTION__))); \ + return; \ + } \ + if (!VoxelWorld->IsCreated()) \ + { \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Voxel World isn't created!"), *FString(__FUNCTION__))); \ + return; \ + } + +#define GENERATED_TOOL_FUNCTION(Type) \ + VOXEL_FUNCTION_COUNTER(); \ + GENERATED_TOOL_FUNCTION_IMPL(Type) + +#define GENERATED_TOOL_FUNCTION_ASYNC(Type) GENERATED_TOOL_FUNCTION(Type) + +#define GENERATED_TOOL_FUNCTION_CPP(Type) \ + VOXEL_FUNCTION_COUNTER(); \ + GENERATED_TOOL_FUNCTION_IMPL(Type) + +#define GENERATED_TOOL_FUNCTION_ASYNC_CPP(Type) GENERATED_TOOL_FUNCTION_CPP(Type) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GENERATED_TOOL_PREFIX(Type) \ + if (!Bounds.IsValid()) \ + { \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Invalid Bounds! %s"), *FString(__FUNCTION__), *Bounds.ToString())); \ + return; \ + } + +#define GENERATED_TOOL_SUFFIX(Type) \ + if (bUpdateRender) \ + { \ + FVoxelToolHelpers::UpdateWorld(VoxelWorld, Bounds); \ + } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GENERATED_TOOL_CALL(Type, ...) \ + GENERATED_TOOL_PREFIX(Type) \ + EditedBounds = Bounds; \ + auto& WorldData = VoxelWorld->GetData(); \ + { \ + FVoxelWriteScopeLock Lock(WorldData, Bounds, FUNCTION_FNAME); \ + auto Data = TVoxelDataImpl(WorldData, bMultiThreaded, bRecordModified##Type##s); \ + __VA_ARGS__; \ + Modified##Type##s = MoveTemp(Data.ModifiedValues); \ + } \ + GENERATED_TOOL_SUFFIX(Type) + +#define GENERATED_TOOL_CALL_ASYNC(Type, ...) \ + GENERATED_TOOL_PREFIX(Type) \ + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld_WithValue( \ + WorldContextObject, \ + LatentInfo, \ + VoxelWorld, \ + FUNCTION_FNAME, \ + bHideLatentWarnings, \ + Modified##Type##s, \ + [=](FVoxelData& WorldData, TArray& OutModified) \ + { \ + FVoxelWriteScopeLock Lock(WorldData, Bounds, FUNCTION_FNAME); \ + auto Data = TVoxelDataImpl(WorldData, bMultiThreaded, bRecordModified##Type##s); \ + __VA_ARGS__; \ + OutModified = MoveTemp(Data.ModifiedValues); \ + }, \ + bUpdateRender ? EVoxelUpdateRender::UpdateRender : EVoxelUpdateRender::DoNotUpdateRender, \ + Bounds, \ + [=, &EditedBounds]() \ + { \ + EditedBounds = Bounds; \ + }); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GENERATED_TOOL_CALL_CPP(Type, ...) \ + GENERATED_TOOL_PREFIX(Type) \ + if (OutEditedBounds) *OutEditedBounds = Bounds; \ + auto& WorldData = VoxelWorld->GetData(); \ + { \ + FVoxelWriteScopeLock Lock(WorldData, Bounds, FUNCTION_FNAME); \ + auto Data = TVoxelDataImpl(WorldData, bMultiThreaded, OutModified##Type##s != nullptr); \ + __VA_ARGS__; \ + if (OutModified##Type##s) \ + { \ + if (OutModified##Type##s->Num() == 0) \ + { \ + *OutModified##Type##s = MoveTemp(Data.ModifiedValues); \ + } \ + else \ + { \ + OutModified##Type##s->Append(MoveTemp(Data.ModifiedValues)); \ + } \ + } \ + } \ + GENERATED_TOOL_SUFFIX(Type) + +#define GENERATED_TOOL_CALL_ASYNC_CPP(Type, ...) \ + GENERATED_TOOL_PREFIX(Type) \ + if (OutEditedBounds) *OutEditedBounds = Bounds; \ + const auto GameThreadTasks = VoxelWorld->GetGameThreadTasks(); \ + auto* Work = new FVoxelToolAsyncWork(FUNCTION_FNAME, *VoxelWorld, [=](FVoxelData& WorldData) \ + { \ + FVoxelWriteScopeLock Lock(WorldData, Bounds, FUNCTION_FNAME); \ + auto Data = TVoxelDataImpl(WorldData, bMultiThreaded, bRecordModified##Type##s); \ + __VA_ARGS__; \ + GameThreadTasks->AddTask([=, Modified = MoveTemp(Data.ModifiedValues)]() \ + { \ + check(IsInGameThread()); \ + /* Validity of Voxel world is guaranteed by it being queued on the world */ \ + GENERATED_TOOL_SUFFIX(Type); \ + Callback.ExecuteIfBound(Modified); \ + }); \ + }); \ + FVoxelToolHelpers::StartAsyncEditTask(VoxelWorld, Work); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Will autodelete +class FVoxelToolAsyncWork : public FVoxelAsyncWork +{ +public: + const TVoxelWeakPtr Data; + const TFunction Function; + + explicit FVoxelToolAsyncWork(FName Name, AVoxelWorld& World, TFunction&& Function) + : FVoxelAsyncWork(Name, 1e9, true) + , Data(World.GetDataSharedPtr()) + , Function(MoveTemp(Function)) + { + } + + //~ Begin IVoxelQueuedWork Interface + virtual uint32 GetPriority() const override + { + return 0; + } + virtual void DoWork() override + { + const auto PinnedData = Data.Pin(); + if (PinnedData.IsValid()) + { + Function(*PinnedData); + } + } + //~ End IVoxelQueuedWork Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelLevelTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelLevelTools.cpp new file mode 100644 index 00000000..08e97708 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelLevelTools.cpp @@ -0,0 +1,112 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Gen/VoxelLevelTools.h" +#include "VoxelTools/Gen/VoxelGeneratedTools.h" +#include "VoxelTools/Impl/VoxelLevelToolsImpl.h" +#include "VoxelTools/Impl/VoxelLevelToolsImpl.inl" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelLevelTools::Level( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealHeight = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Height, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelLevelToolsImpl::GetBounds(RealPosition, RealRadius, RealHeight, bAdditive); + + GENERATED_TOOL_CALL(Value, FVoxelLevelToolsImpl::Level(Data, RealPosition, RealRadius, Falloff, RealHeight, bAdditive)); +} + +void UVoxelLevelTools::LevelAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealHeight = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Height, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelLevelToolsImpl::GetBounds(RealPosition, RealRadius, RealHeight, bAdditive); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelLevelToolsImpl::Level(Data, RealPosition, RealRadius, Falloff, RealHeight, bAdditive)); +} + +void UVoxelLevelTools::Level( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealHeight = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Height, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelLevelToolsImpl::GetBounds(RealPosition, RealRadius, RealHeight, bAdditive); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelLevelToolsImpl::Level(Data, RealPosition, RealRadius, Falloff, RealHeight, bAdditive)); +} + +void UVoxelLevelTools::LevelAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealHeight = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Height, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelLevelToolsImpl::GetBounds(RealPosition, RealRadius, RealHeight, bAdditive); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelLevelToolsImpl::Level(Data, RealPosition, RealRadius, Falloff, RealHeight, bAdditive)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSphereTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSphereTools.cpp new file mode 100644 index 00000000..f600de51 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSphereTools.cpp @@ -0,0 +1,1132 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Gen/VoxelSphereTools.h" +#include "VoxelTools/Gen/VoxelGeneratedTools.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.inl" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::SetValueSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealValue = FVoxelValue(Value); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::SetValueSphere(Data, RealPosition, RealRadius, RealValue)); +} + +void UVoxelSphereTools::SetValueSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealValue = FVoxelValue(Value); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::SetValueSphere(Data, RealPosition, RealRadius, RealValue)); +} + +void UVoxelSphereTools::SetValueSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealValue = FVoxelValue(Value); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::SetValueSphere(Data, RealPosition, RealRadius, RealValue)); +} + +void UVoxelSphereTools::SetValueSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + const auto RealValue = FVoxelValue(Value); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::SetValueSphere(Data, RealPosition, RealRadius, RealValue)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::RemoveSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::RemoveSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::RemoveSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::RemoveSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::RemoveSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::RemoveSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::RemoveSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::RemoveSphere(Data, RealPosition, RealRadius)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::AddSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::AddSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::AddSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::AddSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::AddSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::AddSphere(Data, RealPosition, RealRadius)); +} + +void UVoxelSphereTools::AddSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::AddSphere(Data, RealPosition, RealRadius)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::SetMaterialSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Material, FVoxelSphereToolsImpl::SetMaterialSphere(Data, RealPosition, RealRadius, PaintMaterial, Strength, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SetMaterialSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelSphereToolsImpl::SetMaterialSphere(Data, RealPosition, RealRadius, PaintMaterial, Strength, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SetMaterialSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength, + EVoxelFalloff FalloffType, + float Falloff, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelSphereToolsImpl::SetMaterialSphere(Data, RealPosition, RealRadius, PaintMaterial, Strength, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SetMaterialSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength, + EVoxelFalloff FalloffType, + float Falloff, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelSphereToolsImpl::SetMaterialSphere(Data, RealPosition, RealRadius, PaintMaterial, Strength, FalloffType, Falloff)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::ApplyKernelSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::ApplyKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyKernelSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::ApplyKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyKernelSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::ApplyKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyKernelSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::ApplyKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, FVoxelLambdaUtilities::ConstantStrength)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::ApplyMaterialKernelSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + int32 Mask, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Material, FVoxelSphereToolsImpl::ApplyMaterialKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, Mask, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyMaterialKernelSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + int32 Mask, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelSphereToolsImpl::ApplyMaterialKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, Mask, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyMaterialKernelSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + uint32 Mask, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelSphereToolsImpl::ApplyMaterialKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, Mask, FVoxelLambdaUtilities::ConstantStrength)); +} + +void UVoxelSphereTools::ApplyMaterialKernelSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + uint32 Mask, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelSphereToolsImpl::ApplyMaterialKernelSphere(Data, RealPosition, RealRadius, CenterMultiplier, FirstDegreeNeighborMultiplier, SecondDegreeNeighborMultiplier, ThirdDegreeNeighborMultiplier, NumIterations, Mask, FVoxelLambdaUtilities::ConstantStrength)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::SmoothSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::SmoothSphere(Data, RealPosition, RealRadius, Strength, NumIterations, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::SmoothSphere(Data, RealPosition, RealRadius, Strength, NumIterations, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + EVoxelFalloff FalloffType, + float Falloff, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::SmoothSphere(Data, RealPosition, RealRadius, Strength, NumIterations, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + EVoxelFalloff FalloffType, + float Falloff, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::SmoothSphere(Data, RealPosition, RealRadius, Strength, NumIterations, FalloffType, Falloff)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::SmoothMaterialSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + int32 Mask, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Material, FVoxelSphereToolsImpl::SmoothMaterialSphere(Data, RealPosition, RealRadius, Strength, NumIterations, Mask, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothMaterialSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + int32 Mask, + EVoxelFalloff FalloffType, + float Falloff, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelSphereToolsImpl::SmoothMaterialSphere(Data, RealPosition, RealRadius, Strength, NumIterations, Mask, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothMaterialSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + uint32 Mask, + EVoxelFalloff FalloffType, + float Falloff, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelSphereToolsImpl::SmoothMaterialSphere(Data, RealPosition, RealRadius, Strength, NumIterations, Mask, FalloffType, Falloff)); +} + +void UVoxelSphereTools::SmoothMaterialSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations, + uint32 Mask, + EVoxelFalloff FalloffType, + float Falloff, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelSphereToolsImpl::SmoothMaterialSphere(Data, RealPosition, RealRadius, Strength, NumIterations, Mask, FalloffType, Falloff)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::TrimSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::TrimSphere(Data, RealPosition, Normal, RealRadius, Falloff, bAdditive)); +} + +void UVoxelSphereTools::TrimSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::TrimSphere(Data, RealPosition, Normal, RealRadius, Falloff, bAdditive)); +} + +void UVoxelSphereTools::TrimSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::TrimSphere(Data, RealPosition, Normal, RealRadius, Falloff, bAdditive)); +} + +void UVoxelSphereTools::TrimSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::TrimSphere(Data, RealPosition, Normal, RealRadius, Falloff, bAdditive)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::RevertSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::RevertSphere(Data, RealPosition, RealRadius, HistoryPosition, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::RevertSphere(Data, RealPosition, RealRadius, HistoryPosition, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::RevertSphere(Data, RealPosition, RealRadius, HistoryPosition, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::RevertSphere(Data, RealPosition, RealRadius, HistoryPosition, bRevertValues, bRevertMaterials)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSphereTools::RevertSphereToGenerator( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL(Value, FVoxelSphereToolsImpl::RevertSphereToGenerator(Data, RealPosition, RealRadius, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphereToGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSphereToolsImpl::RevertSphereToGenerator(Data, RealPosition, RealRadius, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphereToGenerator( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSphereToolsImpl::RevertSphereToGenerator(Data, RealPosition, RealRadius, bRevertValues, bRevertMaterials)); +} + +void UVoxelSphereTools::RevertSphereToGeneratorAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + const auto RealPosition = FVoxelToolHelpers::GetRealPosition(VoxelWorld, Position, bConvertToVoxelSpace); + const auto RealRadius = FVoxelToolHelpers::GetRealDistance(VoxelWorld, Radius, bConvertToVoxelSpace); + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(RealPosition, RealRadius); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSphereToolsImpl::RevertSphereToGenerator(Data, RealPosition, RealRadius, bRevertValues, bRevertMaterials)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSurfaceEditTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSurfaceEditTools.cpp new file mode 100644 index 00000000..d799d493 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Gen/VoxelSurfaceEditTools.cpp @@ -0,0 +1,312 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Gen/VoxelSurfaceEditTools.h" +#include "VoxelTools/Gen/VoxelGeneratedTools.h" +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h" +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceEditTools::EditVoxelValues( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Value); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + const FVoxelHardnessHandler HardnessHandler(*VoxelWorld); + + GENERATED_TOOL_CALL(Value, FVoxelSurfaceEditToolsImpl::EditVoxelValues(Data, HardnessHandler, Bounds, ProcessedVoxels, DistanceDivisor)); +} + +void UVoxelSurfaceEditTools::EditVoxelValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Value); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + const FVoxelHardnessHandler HardnessHandler(*VoxelWorld); + + GENERATED_TOOL_CALL_ASYNC(Value, FVoxelSurfaceEditToolsImpl::EditVoxelValues(Data, HardnessHandler, Bounds, ProcessedVoxels, DistanceDivisor)); +} + +void UVoxelSurfaceEditTools::EditVoxelValues( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor, + TArray* OutModifiedValues, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Value); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + const FVoxelHardnessHandler HardnessHandler(*VoxelWorld); + + GENERATED_TOOL_CALL_CPP(Value, FVoxelSurfaceEditToolsImpl::EditVoxelValues(Data, HardnessHandler, Bounds, ProcessedVoxels, DistanceDivisor)); +} + +void UVoxelSurfaceEditTools::EditVoxelValuesAsync( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor, + const FOnVoxelToolComplete_WithModifiedValues& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedValues, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Value); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + const FVoxelHardnessHandler HardnessHandler(*VoxelWorld); + + GENERATED_TOOL_CALL_ASYNC_CPP(Value, FVoxelSurfaceEditToolsImpl::EditVoxelValues(Data, HardnessHandler, Bounds, ProcessedVoxels, DistanceDivisor)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceEditTools::EditVoxelMaterials( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL(Material, FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(Data, Bounds, PaintMaterial, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::EditVoxelMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(Data, Bounds, PaintMaterial, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::EditVoxelMaterials( + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(Data, Bounds, PaintMaterial, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::EditVoxelMaterialsAsync( + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(Data, Bounds, PaintMaterial, ProcessedVoxels)); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceEditTools::PropagateVoxelMaterials( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION(Material); + + if (!ProcessedVoxels.Info.bHasSurfacePositions) + { + FVoxelMessages::Error(FUNCTION_ERROR("PropagateVoxelMaterials needs surface positions! Use FindSurfaceVoxelsFromDistanceField")); + return; + } + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL(Material, FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(Data, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::PropagateVoxelMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + GENERATED_TOOL_FUNCTION_ASYNC(Material); + + if (!ProcessedVoxels.Info.bHasSurfacePositions) + { + FVoxelMessages::Error(FUNCTION_ERROR("PropagateVoxelMaterials needs surface positions! Use FindSurfaceVoxelsFromDistanceField")); + return; + } + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_ASYNC(Material, FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(Data, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::PropagateVoxelMaterials( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + TArray* OutModifiedMaterials, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_CPP(Material); + + if (!ProcessedVoxels.Info.bHasSurfacePositions) + { + FVoxelMessages::Error(FUNCTION_ERROR("PropagateVoxelMaterials needs surface positions! Use FindSurfaceVoxelsFromDistanceField")); + return; + } + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_CPP(Material, FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(Data, ProcessedVoxels)); +} + +void UVoxelSurfaceEditTools::PropagateVoxelMaterialsAsync( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback, + FVoxelIntBox* OutEditedBounds, + bool bMultiThreaded, + bool bRecordModifiedMaterials, + bool bUpdateRender) +{ + GENERATED_TOOL_FUNCTION_ASYNC_CPP(Material); + + if (!ProcessedVoxels.Info.bHasSurfacePositions) + { + FVoxelMessages::Error(FUNCTION_ERROR("PropagateVoxelMaterials needs surface positions! Use FindSurfaceVoxelsFromDistanceField")); + return; + } + + if (!FVoxelSurfaceEditToolsImpl::ShouldCompute(ProcessedVoxels)) + { + return; + } + + const FVoxelIntBox Bounds = FVoxelSurfaceEditToolsImpl::GetBounds(ProcessedVoxels); + + GENERATED_TOOL_CALL_ASYNC_CPP(Material, FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(Data, ProcessedVoxels)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Impl/VoxelToolsBaseImpl.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Impl/VoxelToolsBaseImpl.cpp new file mode 100644 index 00000000..cbaadc54 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Impl/VoxelToolsBaseImpl.cpp @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" + +static TAutoConsoleVariable CVarLogEditToolsTimes( + TEXT("voxel.tools.LogEditTimes"), + 0, + TEXT("Log edit tools times"), + ECVF_Default); + +FScopeToolsTimeLogger::~FScopeToolsTimeLogger() +{ + const double EndTime = FPlatformTime::Seconds(); + if (CVarLogEditToolsTimes.GetValueOnAnyThread() != 0) + { + const double ElapsedInSeconds = (EndTime - StartTime); + const double ElapsedInMilliseconds = ElapsedInSeconds * 1000; + if (NumVoxels < 0) + { + LOG_VOXEL(Log, TEXT("%s took %fms"), *FString(Name), ElapsedInMilliseconds); + } + else + { + LOG_VOXEL(Log, TEXT("%s took %fms for %lld voxels (%f G/s)"), *FString(Name), ElapsedInMilliseconds, NumVoxels, NumVoxels / ElapsedInSeconds / 1e9); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelFlattenTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelFlattenTool.cpp new file mode 100644 index 00000000..d0558d5c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelFlattenTool.cpp @@ -0,0 +1,173 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelFlattenTool.h" + +#include "VoxelWorld.h" +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl" +#include "VoxelTools/VoxelProjectionTools.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelHardnessHandler.h" + +#include "DrawDebugHelpers.h" +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelFlattenTool::UVoxelFlattenTool() +{ + ToolName = TEXT("Flatten"); + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolRenderingMaterial_Flatten")); + ToolMaterial = ToolMaterialFinder.Object; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelFlattenTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + OutConfig.OverlayMaterial = ToolMaterial; +} + +void UVoxelFlattenTool::Tick() +{ + Super::Tick(); + + Falloff = GetValueAfterAxisInput(FVoxelToolAxes::Falloff, Falloff); + Strength = GetValueAfterAxisInput(FVoxelToolAxes::Strength, Strength); +} + +void UVoxelFlattenTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!OverlayMaterialInstance) + { + return; + } + + const float Radius = SharedConfig->BrushSize / 2; + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Radius"), Radius); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("Position"), GetToolPreviewPosition()); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Falloff"), Falloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("EnableFalloff"), bEnableFalloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("FalloffType"), int32(FalloffType)); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + SetToolOverlayBounds(FBox(GetToolPreviewPosition() - Radius, GetToolPreviewPosition() + Radius)); +} + +FVoxelIntBoxWithValidity UVoxelFlattenTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + constexpr float DistanceDivisor = 4.f; + const float Radius = SharedConfig->BrushSize / 2; + + const FVoxelIntBox Bounds = UVoxelBlueprintLibrary::MakeIntBoxFromGlobalPositionAndRadius(GetVoxelWorld(), GetToolPosition(), Radius); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + FVector FlattenPosition; + FVector FlattenNormal; + if (bUseAverage) + { + FVoxelLineTraceParameters Parameters; + Parameters.CollisionChannel = GetTickData().CollisionChannel; + Parameters.DrawDebugType = SharedConfig->bDebug ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None; + + const float RaysRadius = FMath::Max(Radius, GetVoxelWorld()->VoxelSize); + const FVector RayDirection = GetTickData().GetRayDirection(); + + TArray Hits; + UVoxelProjectionTools::FindProjectionVoxels( + Hits, + GetVoxelWorld(), + Parameters, + GetToolPosition() - RayDirection * RaysRadius, + RayDirection, + RaysRadius, + EVoxelProjectionShape::Circle, + 100.f, + 2 * RaysRadius); + + FlattenPosition = UVoxelProjectionTools::GetHitsAveragePosition(Hits); + FlattenNormal = UVoxelProjectionTools::GetHitsAverageNormal(Hits); + } + else + { + FlattenPosition = GetToolPosition(); + FlattenNormal = GetToolNormal(); + } + + if (bUseFixedRotation) + { + FlattenNormal = FixedRotation.RotateVector(FVector::UpVector).GetSafeNormal(); + } + + if (!GetLastFrameTickData().bEdit) + { + LastClickFlattenPosition = FlattenPosition; + LastClickFlattenNormal = FlattenNormal; + } + + auto& Data = GetVoxelWorld()->GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + const FVoxelSurfaceEditsVoxels Voxels = UVoxelSurfaceTools::FindSurfaceVoxelsFromDistanceFieldImpl(Data, Bounds, SharedConfig->bMultiThreaded, SharedConfig->GetComputeDevice()); + + FVoxelSurfaceEditsStack Stack; + + if (bEnableFalloff) + { + Stack.Add(UVoxelSurfaceTools::ApplyFalloff( + GetVoxelWorld(), + FalloffType, + GetToolPosition(), + Radius, + Falloff)); + } + + Stack.Add(UVoxelSurfaceTools::ApplyConstantStrength(Strength)); + + const FVector PlanePoint = bFreezeOnClick ? LastClickFlattenPosition : FlattenPosition; + const FVector PlaneNormal = bFreezeOnClick ? LastClickFlattenNormal : FlattenNormal; + + Stack.Add(UVoxelSurfaceTools::ApplyFlatten( + GetVoxelWorld(), + PlanePoint, + PlaneNormal, + GetTickData().IsAlternativeMode() ? EVoxelSDFMergeMode::Intersection : EVoxelSDFMergeMode::Union)); + + const auto ProcessedVoxels = Stack.Execute(Voxels, false); + + if (bPropagateMaterials) + { + FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(Data, ProcessedVoxels); + } + + FVoxelSurfaceEditToolsImpl::EditVoxelValues(DataImpl, FVoxelHardnessHandler(*GetVoxelWorld()), Bounds, ProcessedVoxels, DistanceDivisor); + + if (SharedConfig->bDebug) + { + UVoxelSurfaceTools::DebugSurfaceVoxels(GetVoxelWorld(), ProcessedVoxels, 2 * GetDeltaTime()); + + DrawDebugSolidPlane( + GetVoxelWorld()->GetWorld(), + FPlane(PlanePoint, PlaneNormal), + PlanePoint, + 1000000, + FColor::Red, + false, + 1.5f * GetDeltaTime()); + } + + return Bounds; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelLevelTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelLevelTool.cpp new file mode 100644 index 00000000..00edbd9a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelLevelTool.cpp @@ -0,0 +1,103 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelLevelTool.h" +#include "VoxelTools/Impl/VoxelLevelToolsImpl.inl" +#include "VoxelWorld.h" + +#include "Engine/StaticMesh.h" +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelLevelTool::UVoxelLevelTool() +{ + ToolName = TEXT("Level"); + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolMeshMaterial_Level")); + static ConstructorHelpers::FObjectFinder CylinderMeshFinder(TEXT("/Engine/BasicShapes/Cylinder")); + ToolMaterial = ToolMaterialFinder.Object; + CylinderMesh = CylinderMeshFinder.Object; +} + +void UVoxelLevelTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + OutConfig.MeshMaterial = ToolMaterial; + + OutConfig.Stride = Stride; + OutConfig.bHasAlignment = true; + OutConfig.Alignment = EVoxelToolAlignment::Ground; + OutConfig.DistanceToCamera = 1e5; +} + +void UVoxelLevelTool::Tick() +{ + Super::Tick(); + + Falloff = GetValueAfterAxisInput(FVoxelToolAxes::Falloff, Falloff); + Height = GetValueAfterAxisInput(FVoxelToolAxes::Strength, Height, 0, 10000); +} + +void UVoxelLevelTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!MeshMaterialInstance) + { + return; + } + + const bool bAlternativeMode = GetTickData().IsAlternativeMode(); + + const float ScaleXY = SharedConfig->BrushSize / 100.f; + const float ScaleZ = Height / 100.f + 0.001f; + const FTransform PreviewTransform( + FQuat::Identity, + GetToolPreviewPosition() + GetToolOffset() + FVector(0, 0, (bAlternativeMode ? Height : -Height) / 2), + FVector(ScaleXY, ScaleXY, ScaleZ)); + + MeshMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + UpdateToolMesh( + CylinderMesh, + MeshMaterialInstance, + PreviewTransform); +} + +FVoxelIntBoxWithValidity UVoxelLevelTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + auto& World = *GetVoxelWorld(); + const bool bAlternativeMode = GetTickData().IsAlternativeMode(); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(GetToolPosition() + GetToolOffset()); + const float VoxelRadius = SharedConfig->BrushSize / 2 / World.VoxelSize; + const float VoxelHeight = Height / World.VoxelSize; + + const FVoxelIntBox Bounds = FVoxelLevelToolsImpl::GetBounds(VoxelPosition, VoxelRadius, VoxelHeight, !bAlternativeMode); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + FVoxelLevelToolsImpl::Level( + DataImpl, + VoxelPosition, + VoxelRadius, + Falloff, + VoxelHeight, + !bAlternativeMode); + + return Bounds; +} + +FVector UVoxelLevelTool::GetToolOffset() const +{ + return FVector(0.f, 0.f, Offset * Height * (GetTickData().IsAlternativeMode() ? -1 : 1)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp new file mode 100644 index 00000000..cb18c266 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp @@ -0,0 +1,404 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelMeshTool.h" + +#include "VoxelTools/VoxelDataTools.inl" +#include "VoxelTools/VoxelAssetTools.inl" +#include "VoxelData/VoxelData.h" +#include "VoxelUtilities/VoxelSDFUtilities.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" +#include "VoxelWorld.h" + +#include "Engine/Engine.h" +#include "Engine/StaticMesh.h" +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelMeshTool::UVoxelMeshTool() +{ + ToolName = TEXT("Mesh"); + bShowPaintMaterial = false; + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolMeshMaterial_Mesh")); + ToolMaterial = ToolMaterialFinder.Object; + + ColorsMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color")); + UVsMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs")); + Mesh = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Importers/Chair/VoxelExample_SM_Chair")); +} + +void UVoxelMeshTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + Super::GetToolConfig(OutConfig); + + OutConfig.MeshMaterial = ToolMaterial; + OutConfig.Stride = Stride; +} + +void UVoxelMeshTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!MeshMaterialInstance) + { + return; + } + + auto* MeshData = GetMeshData(); + if (!MeshData) + { + return; + } + + FVector MeshScale; + FTransform TransformNoTranslation; + FTransform TransformWithTranslation; + GetTransform(*MeshData, MeshScale, TransformNoTranslation, TransformWithTranslation); + + MeshMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + UpdateToolMesh( + Mesh, + MeshMaterialInstance, + TransformWithTranslation); +} + +FVoxelIntBoxWithValidity UVoxelMeshTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + auto* MeshData = GetMeshData(); + if (!MeshData) + { + return {}; + } + + FVector MeshScale; + FTransform TransformNoTranslation; + FTransform TransformWithTranslation; + GetTransform(*MeshData, MeshScale, TransformNoTranslation, TransformWithTranslation); + + auto& World = *GetVoxelWorld(); + const bool bAlternativeMode = GetTickData().IsAlternativeMode(); + + const auto MaterialConfig = World.MaterialConfig; + + const uint8 MeshSettings_U0 = FVoxelUtilities::FloatToUINT8(UV0ToPaint.X); + const uint8 MeshSettings_V0 = FVoxelUtilities::FloatToUINT8(UV0ToPaint.Y); + const uint8 MeshSettings_U1 = FVoxelUtilities::FloatToUINT8(UV1ToPaint.X); + const uint8 MeshSettings_V1 = FVoxelUtilities::FloatToUINT8(UV1ToPaint.Y); + + const auto GetMaterialImpl = [&](const float OldValue, const float NewValue, const FVoxelMaterial OldMaterial, bool bAllowMeshImport, const FVoxelMaterial InstanceMaterial) + { + if ((bAlternativeMode ? OldValue >= NewValue : OldValue <= NewValue) || NewValue > 0) + { + return OldMaterial; + } + + FVoxelMaterial NewMaterial = OldMaterial; + if (bPaintColors) + { + const FColor Color = bImportColorsFromMesh && bAllowMeshImport ? InstanceMaterial.GetColor() : ColorToPaint; + if (MaterialConfig == EVoxelMaterialConfig::RGB) + { + NewMaterial.SetR(Color.R); + NewMaterial.SetG(Color.G); + NewMaterial.SetB(Color.B); + NewMaterial.SetA(Color.A); + } + else if (MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + NewMaterial.SetR(Color.R); + NewMaterial.SetG(Color.G); + NewMaterial.SetB(Color.B); + } + } + if (bPaintUVs && MaterialConfig != EVoxelMaterialConfig::MultiIndex) + { + NewMaterial.SetU0(bImportUVsFromMesh && bAllowMeshImport ? InstanceMaterial.GetU0() : MeshSettings_U0); + NewMaterial.SetU1(bImportUVsFromMesh && bAllowMeshImport ? InstanceMaterial.GetU1() : MeshSettings_U1); + NewMaterial.SetV0(bImportUVsFromMesh && bAllowMeshImport ? InstanceMaterial.GetV0() : MeshSettings_V0); + NewMaterial.SetV1(bImportUVsFromMesh && bAllowMeshImport ? InstanceMaterial.GetV1() : MeshSettings_V1); + } + if (bPaintIndex && MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + NewMaterial.SetSingleIndex(IndexToPaint); + } + if (bPaintIndex && MaterialConfig == EVoxelMaterialConfig::MultiIndex) + { + NewMaterial.SetMultiIndex_Index0(IndexToPaint); + NewMaterial.SetMultiIndex_Blend0_AsFloat(0); + NewMaterial.SetMultiIndex_Blend1_AsFloat(0); + NewMaterial.SetMultiIndex_Blend2_AsFloat(0); + } + + auto Result = OldMaterial; + Result.CopyFrom(NewMaterial, PaintMask); + return Result; + }; + + if (bSmoothImport || bProgressiveStamp) + { + const float ActualSmoothness = Smoothness * MeshScale.GetMax() * MeshData->Bounds.GetSize().GetMax() / World.VoxelSize; + const float BoxExtension = bProgressiveStamp ? 0 : (ActualSmoothness + 4); + + // Fixup import settings + MeshImporterSettings.VoxelSize = World.VoxelSize; + + if (!DistanceFieldData.IsValid() || + !DistanceFieldData->Transform.Equals(TransformNoTranslation) || + DistanceFieldData->BoxExtension != BoxExtension || + DistanceFieldData->ImporterSettings != MeshImporterSettings) + { + auto NewDistanceFieldData = MakeUnique(); + + NewDistanceFieldData->Transform = TransformNoTranslation; + NewDistanceFieldData->BoxExtension = BoxExtension; + NewDistanceFieldData->ImporterSettings = MeshImporterSettings; + + int32 NumLeaks; + UVoxelMeshImporterLibrary::ConvertMeshToDistanceField( + MeshData->Data, + TransformNoTranslation, + MeshImporterSettings, + BoxExtension, + NewDistanceFieldData->Data, + NewDistanceFieldData->SurfacePositions, + NewDistanceFieldData->Size, + NewDistanceFieldData->PositionOffset, + NumLeaks, + SharedConfig->GetComputeDevice(), + SharedConfig->bMultiThreaded); + + if (SharedConfig->bDebug) + { + GEngine->AddOnScreenDebugMessage( + OBJECT_LINE_ID(), + 1.5f * GetDeltaTime(), + FColor::Yellow, + "Converting mesh to distance field"); + } + + DistanceFieldData = MoveTemp(NewDistanceFieldData); + } + check(DistanceFieldData.IsValid()); + + const FIntVector VoxelPosition = World.GlobalToLocal(GetToolPosition()) + DistanceFieldData->PositionOffset; + const FVoxelIntBox Bounds = FVoxelIntBox(VoxelPosition, VoxelPosition + DistanceFieldData->Size); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache.Extend(1) /* See MergeDistanceFieldImpl */, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + const FIntVector Size = DistanceFieldData->Size; + const auto& DistanceField = DistanceFieldData->Data; + + FVoxelDebug::Broadcast("MeshDistances", Bounds.Size(), DistanceField); + + const auto GetSDF = FVoxelUtilities::Create3DGetter(DistanceField, Size, Bounds.Min); + + const auto MergeSDF = [&](float A, float B) -> float + { + if (bProgressiveStamp) + { + if (bAlternativeMode) + { + return FMath::Lerp(A, FMath::Max(A, -B), Speed); + } + else + { + return FMath::Lerp(A, FMath::Min(A, B), Speed); + } + } + else + { + ensureVoxelSlow(bSmoothImport); + if (bAlternativeMode) + { + return FVoxelSDFUtilities::opSmoothSubtraction(B, A, ActualSmoothness); + } + else + { + return FVoxelSDFUtilities::opSmoothUnion(B, A, ActualSmoothness); + } + } + }; + const auto GetMaterial = [&](float OldValue, float NewValue, FVoxelMaterial PreviousMaterial) + { + return GetMaterialImpl(OldValue, NewValue, PreviousMaterial, false, {}); + }; + + UVoxelDataTools::MergeDistanceFieldImpl(Data, Bounds, GetSDF, MergeSDF, SharedConfig->bMultiThreaded, bPaint, GetMaterial); + + return Bounds; + } + else + { + FVoxelMeshImporterSettings ActualImporterSettings(MeshImporterSettings); + ActualImporterSettings.VoxelSize = World.VoxelSize; + + ActualImporterSettings.bImportColors = bPaint && bPaintColors && ColorsMaterial && World.MaterialConfig != EVoxelMaterialConfig::MultiIndex; + ActualImporterSettings.ColorsMaterial = ColorsMaterial; + + ActualImporterSettings.bImportUVs = bPaint && bPaintUVs && UVsMaterial && World.MaterialConfig != EVoxelMaterialConfig::MultiIndex; + ActualImporterSettings.UVsMaterial = UVsMaterial; + + ActualImporterSettings.RenderTargetSize = RenderTargetSize; + + if (!AssetData.IsValid() || + !TransformNoTranslation.Equals(AssetData->Transform) || + ActualImporterSettings != AssetData_ImporterSettings) + { + auto NewAssetData = MakeUnique(); + + NewAssetData->Transform = TransformNoTranslation; + AssetData_ImporterSettings = ActualImporterSettings; + + int32 NumLeaks; + UVoxelMeshImporterLibrary::ConvertMeshToVoxels( + GetVoxelWorld(), + MeshData->Data, + TransformNoTranslation, + ActualImporterSettings, + RenderTargetCache, + NewAssetData->Data, + NewAssetData->PositionOffset, + NumLeaks); + + ColorsRenderTarget = RenderTargetCache.ColorsRenderTarget; + UVsRenderTarget = RenderTargetCache.UVsRenderTarget; + + if (SharedConfig->bDebug) + { + GEngine->AddOnScreenDebugMessage( + OBJECT_LINE_ID(), + 1.5f * GetDeltaTime(), + FColor::Yellow, + "Converting mesh to voxels"); + } + + AssetData = MoveTemp(NewAssetData); + } + check(AssetData.IsValid()); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(GetToolPosition()) + FVector(AssetData->PositionOffset); + const FVoxelIntBox Bounds = FVoxelIntBox(VoxelPosition, VoxelPosition + FVector(AssetData->Data.GetSize())); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + const auto GetValue = [&] (float OldValue, float InstanceValue) + { + return bAlternativeMode ? FMath::Max(OldValue, -InstanceValue) : FMath::Min(OldValue, InstanceValue); + }; + const auto GetMaterial = [&](const float OldValue, const float NewValue, const FVoxelMaterial OldMaterial, const float InstanceValue, const FVoxelMaterial InstanceMaterial) + { + return GetMaterialImpl(OldValue, NewValue, OldMaterial, true, InstanceMaterial); + }; + // Note: bSubtractive needs to be false here as we are manually inverting the asset above + UVoxelAssetTools::ImportDataAssetImpl(DataImpl, VoxelPosition, AssetData->Data, false, GetValue, bPaint, GetMaterial); + + return Bounds; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const UVoxelMeshTool::FMeshData* UVoxelMeshTool::GetMeshData() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Mesh) + { + return nullptr; + } + + if (CachedMeshData.IsValid() && CachedMeshData->StaticMesh != Mesh) + { + CachedMeshData.Reset(); + AssetData.Reset(); + DistanceFieldData.Reset(); + } + + if (!CachedMeshData.IsValid()) + { + auto MeshData = MakeUnique(); + MeshData->StaticMesh = Mesh; + UVoxelMeshImporterLibrary::CreateMeshDataFromStaticMesh(Mesh, MeshData->Data); + + MeshData->Bounds = FBox(ForceInit); + for (auto& Vertex : MeshData->Data.Vertices) + { + MeshData->Bounds += Vertex; + } + + if (MeshData->Data.Vertices.Num() == 0) + { + FVoxelMessages::Error("Mesh Tool: Failed to extract mesh data!"); + } + + CachedMeshData = MoveTemp(MeshData); + } + check(CachedMeshData.IsValid()); + + if (CachedMeshData->Data.Vertices.Num() > 0) + { + return CachedMeshData.Get(); + } + else + { + return nullptr; + } +} + +void UVoxelMeshTool::GetTransform( + const FMeshData& MeshData, + FVector& OutMeshScale, + FTransform& OutTransformNoTranslation, + FTransform& OutTransformWithTranslation) const +{ + VOXEL_FUNCTION_COUNTER(); + + OutMeshScale = + bAbsoluteScale + ? Scale + : SharedConfig->BrushSize * Scale / MeshData.Bounds.GetSize().GetMax(); + + const auto GetRotationMatrix = [&]() + { + const FVector X = bAlignToMovement ? GetToolDirection() : FVector::ForwardVector; + const FVector Z = bAlignToNormal ? GetToolNormal() : FVector::UpVector; + const FVector Y = (Z ^ X).GetSafeNormal(); + return FMatrix(Y ^ Z, Y, Z, FVector(0)); + }; + + // Matrix and Transform multiplications are left to right! + + const FMatrix ScaleMatrix = FScaleMatrix(OutMeshScale); + const FVector ScaledPositionOffset = PositionOffset * ScaleMatrix.TransformVector(MeshData.Bounds.GetSize()); + + OutTransformNoTranslation = FTransform( + ScaleMatrix * + FTranslationMatrix(ScaledPositionOffset) * + FRotationMatrix(RotationOffset) * + GetRotationMatrix()); + + OutTransformWithTranslation = OutTransformNoTranslation * FTransform(GetToolPreviewPosition()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelRevertTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelRevertTool.cpp new file mode 100644 index 00000000..c71ba064 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelRevertTool.cpp @@ -0,0 +1,65 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelRevertTool.h" + +#include "VoxelTools/Impl/VoxelSphereToolsImpl.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.inl" +#include "VoxelWorld.h" + +UVoxelRevertTool::UVoxelRevertTool() +{ + ToolName = TEXT("Revert"); +} + +void UVoxelRevertTool::Tick() +{ + Super::Tick(); + + CurrentHistoryPosition = GetVoxelWorld()->GetData().GetHistoryPosition(); + HistoryPosition = FMath::Clamp(HistoryPosition, 0, CurrentHistoryPosition); +} + +FVoxelIntBoxWithValidity UVoxelRevertTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + auto& World = *GetVoxelWorld(); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(GetToolPosition()); + const float VoxelRadius = SharedConfig->BrushSize / 2.f / World.VoxelSize; + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(VoxelPosition, VoxelRadius); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + if (GetTickData().IsAlternativeMode()) + { + FVoxelSphereToolsImpl::RevertSphereToGenerator( + DataImpl, + VoxelPosition, + VoxelRadius, + bRevertValues, + bRevertMaterials); + } + else + { + FVoxelSphereToolsImpl::RevertSphere( + DataImpl, + VoxelPosition, + VoxelRadius, + HistoryPosition, + bRevertValues, + bRevertMaterials); + } + + return Bounds; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSmoothTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSmoothTool.cpp new file mode 100644 index 00000000..bfff31ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSmoothTool.cpp @@ -0,0 +1,106 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelSmoothTool.h" +#include "VoxelTools/Tools/VoxelToolLibary.h" + +#include "VoxelMessages.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.inl" +#include "VoxelWorld.h" +#include "VoxelData/VoxelData.h" + +#include "Materials/MaterialInstanceDynamic.h" +#include "UObject/ConstructorHelpers.h" + +UVoxelSmoothTool::UVoxelSmoothTool() +{ + ToolName = TEXT("Smooth"); + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolRenderingMaterial_Smooth")); + ToolMaterial = ToolMaterialFinder.Object; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSmoothTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + OutConfig.OverlayMaterial = ToolMaterial; +} + +void UVoxelSmoothTool::Tick() +{ + Super::Tick(); + + Falloff = GetValueAfterAxisInput(FVoxelToolAxes::Falloff, Falloff); + Strength = GetValueAfterAxisInput(FVoxelToolAxes::Strength, Strength); +} + +void UVoxelSmoothTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + UVoxelToolLibrary::UpdateSphereOverlayMaterial(this, OverlayMaterialInstance, FalloffType, Falloff); +} + +FVoxelIntBoxWithValidity UVoxelSmoothTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + auto& World = *GetVoxelWorld(); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(GetToolPosition()); + const float VoxelRadius = SharedConfig->BrushSize / 2.f / World.VoxelSize; + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(VoxelPosition, VoxelRadius); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + const int32 CurrentNumIterations = GetTickData().IsAlternativeMode() ? 1 : NumIterations; + + if (bSculpt) + { + FVoxelSphereToolsImpl::SmoothSphere( + DataImpl, + VoxelPosition, + VoxelRadius, + Strength, + CurrentNumIterations, + FalloffType, + Falloff); + } + + if (bPaint) + { + uint32 Mask = PaintMask; + if (GetVoxelWorld()->MaterialConfig == EVoxelMaterialConfig::SingleIndex) + { + Mask &= ~EVoxelMaterialMask::SingleIndex; + } + else if (GetVoxelWorld()->MaterialConfig == EVoxelMaterialConfig::MultiIndex) + { + FVoxelMessages::Warning("MultiIndex is not supported by smooth material", this); + Mask = EVoxelMaterialMask::None; + } + + FVoxelSphereToolsImpl::SmoothMaterialSphere( + DataImpl, + VoxelPosition, + VoxelRadius, + Strength, + CurrentNumIterations, + Mask, + FalloffType, + Falloff); + } + + return Bounds; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereTool.cpp new file mode 100644 index 00000000..e3360399 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereTool.cpp @@ -0,0 +1,120 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelSphereTool.h" +#include "VoxelTools/Tools/VoxelToolLibary.h" + +#include "VoxelTools/Impl/VoxelSphereToolsImpl.inl" +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl" +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelData/VoxelData.h" +#include "VoxelWorld.h" + +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelSphereTool::UVoxelSphereTool() +{ + ToolName = TEXT("Sphere"); + bShowPaintMaterial = true; + + static ConstructorHelpers::FObjectFinder OverlayMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolRenderingMaterial_Sphere")); + OverlayMaterial = OverlayMaterialFinder.Object; +} + +void UVoxelSphereTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + Super::GetToolConfig(OutConfig); + + if (bPaint && !bSculpt) + { + OutConfig.OverlayMaterial = OverlayMaterial; + } +} + +void UVoxelSphereTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + Super::UpdateRender(OverlayMaterialInstance, MeshMaterialInstance); + + UVoxelToolLibrary::UpdateSphereOverlayMaterial(this, OverlayMaterialInstance, FalloffType, Falloff); +} + +FVoxelIntBoxWithValidity UVoxelSphereTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + AVoxelWorld& World = *GetVoxelWorld(); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(GetToolPosition()); + const float VoxelRadius = SharedConfig->BrushSize / 2.f / World.VoxelSize; + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(VoxelPosition, VoxelRadius); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + FVoxelData& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + if (bSculpt) + { + if (GetTickData().IsAlternativeMode()) + { + FVoxelSphereToolsImpl::RemoveSphere( + DataImpl, + VoxelPosition, + VoxelRadius); + } + else + { + if (bPaint) + { + auto RecordingDataImpl = GetDataImpl(Data); + FVoxelSphereToolsImpl::AddSphere( + RecordingDataImpl, + VoxelPosition, + VoxelRadius); + + TArray Materials; + Materials.Reserve(RecordingDataImpl.ModifiedValues.Num()); + for (auto& Voxel : RecordingDataImpl.ModifiedValues) + { + if (Voxel.OldValue > 0 && Voxel.NewValue <= 0) + { + FVoxelSurfaceEditsVoxel NewVoxel; + NewVoxel.Position = Voxel.Position; + NewVoxel.Strength = PaintStrength; + Materials.Emplace(NewVoxel); + } + } + FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(DataImpl, Bounds, SharedConfig->PaintMaterial, Materials); + } + else + { + FVoxelSphereToolsImpl::AddSphere( + DataImpl, + VoxelPosition, + VoxelRadius); + } + } + } + else if (bPaint) + { + const float Strength = GetTickData().IsAlternativeMode() ? -PaintStrength : PaintStrength; + + FVoxelSphereToolsImpl::SetMaterialSphere( + DataImpl, + VoxelPosition, + VoxelRadius, + SharedConfig->PaintMaterial, + Strength, + FalloffType, + Falloff); + } + + return Bounds; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereToolBase.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereToolBase.cpp new file mode 100644 index 00000000..787fafb8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereToolBase.cpp @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelSphereToolBase.h" + +#include "Engine/StaticMesh.h" +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelSphereToolBase::UVoxelSphereToolBase() +{ + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolMeshMaterial_Sphere")); + static ConstructorHelpers::FObjectFinder SphereMeshFinder(TEXT("/Engine/BasicShapes/Sphere")); + ToolMaterial = ToolMaterialFinder.Object; + SphereMesh = SphereMeshFinder.Object; +} + +void UVoxelSphereToolBase::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + Super::GetToolConfig(OutConfig); + + OutConfig.MeshMaterial = ToolMaterial; +} + +void UVoxelSphereToolBase::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!MeshMaterialInstance) + { + return; + } + + const float Scale = SharedConfig->BrushSize / 100.f; + const FTransform PreviewTransform(FQuat::Identity, GetToolPreviewPosition(), FVector(Scale)); + + MeshMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + UpdateToolMesh(SphereMesh, MeshMaterialInstance, PreviewTransform); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp new file mode 100644 index 00000000..64d64bdf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp @@ -0,0 +1,398 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelSurfaceTool.h" +#include "VoxelWorld.h" +#include "VoxelMessages.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" +#include "VoxelGenerators/VoxelGeneratorTools.h" +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl" +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelTools/VoxelSurfaceToolsImpl.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelHardnessHandler.h" + +#include "UObject/ConstructorHelpers.h" +#include "Engine/Texture2D.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelSurfaceTool::UVoxelSurfaceTool() +{ + ToolName = TEXT("Surface"); + bShowPaintMaterial = true; + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolRenderingMaterial_Surface")); + ToolMaterial = ToolMaterialFinder.Object; + Mask.Texture = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/VoxelDefaultBrushMask")); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + OutConfig.Stride = Stride; + + OutConfig.OverlayMaterial = ToolMaterial; + + OutConfig.bUseFixedDirection = !bAlignToMovement; + OutConfig.FixedDirection = FixedDirection; + + OutConfig.bUseFixedNormal = b2DBrush; + OutConfig.FixedNormal = FVector::UpVector; +} + +void UVoxelSurfaceTool::Tick() +{ + Super::Tick(); + + Falloff = GetValueAfterAxisInput(FVoxelToolAxes::Falloff, Falloff); + if (bSculpt) + { + SculptStrength = GetValueAfterAxisInput(FVoxelToolAxes::Strength, SculptStrength); + } + else if (bPaint) + { + PaintStrength = GetValueAfterAxisInput(FVoxelToolAxes::Strength, PaintStrength); + } +} + +void UVoxelSurfaceTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!OverlayMaterialInstance) + { + return; + } + + const float VoxelSize = GetVoxelWorld()->VoxelSize; + + if (ShouldUseMask()) + { + TVoxelTexture MaskTexture; + float MaskScaleX = 1; + float MaskScaleY = 1; + if (GetMaskData(false, MaskTexture, MaskScaleX, MaskScaleY)) + { + FVector MaskPlaneX; + FVector MaskPlaneY; + FVoxelSurfaceToolsImpl::GetStrengthMaskBasisImpl(GetToolNormal(), GetToolDirection(), MaskPlaneX, MaskPlaneY); + + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("MaskX"), MaskPlaneX); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("MaskY"), MaskPlaneY); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("MaskUp"), GetToolNormal()); + + { + const FVector TextureMaskScale = + VoxelSize * + FVector( + MaskScaleX * MaskTexture.GetSizeX(), + MaskScaleY * MaskTexture.GetSizeY(), + 0); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("MaskScale"), TextureMaskScale); + } + + { + FVector4 MaskChannelVector = FVector4(ForceInit); + MaskChannelVector[int32(Mask.Type == EVoxelSurfaceToolMaskType::Texture ? Mask.Channel : EVoxelRGBA::R)] = 1; + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("MaskChannel"), FLinearColor(MaskChannelVector)); + } + + OverlayMaterialInstance->SetTextureParameterValue(STATIC_FNAME("MaskTexture"), Mask.Type == EVoxelSurfaceToolMaskType::Texture ? Mask.Texture : MaskGeneratorCache_RenderTexture); + } + } + + const float Radius = SharedConfig->BrushSize / 2.f; + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Radius"), Radius); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("Position"), GetToolPreviewPosition()); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Falloff"), Falloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("EnableFalloff"), bEnableFalloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("FalloffType"), int32(FalloffType)); + + float SignedSculptStrength; + float SignedPaintStrength; + GetStrengths(SignedSculptStrength, SignedPaintStrength); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("SculptHeight"), bSculpt ? SignedSculptStrength * VoxelSize : 0.f); + + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("UseMask"), ShouldUseMask()); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("2DBrush"), b2DBrush); + + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + SetToolOverlayBounds(FBox(GetToolPreviewPosition() - Radius, GetToolPreviewPosition() + Radius)); +} + +FVoxelIntBoxWithValidity UVoxelSurfaceTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + constexpr float DistanceDivisor = 4.f; + + float SignedSculptStrength; + float SignedPaintStrength; + GetStrengths(SignedSculptStrength, SignedPaintStrength); + + const float Radius = SharedConfig->BrushSize / 2.f; + const FVoxelIntBox BoundsToDoEditsIn = UVoxelBlueprintLibrary::MakeIntBoxFromGlobalPositionAndRadius(GetVoxelWorld(), GetToolPosition(), Radius); + const FVoxelIntBox BoundsWhereEditsHappen = + b2DBrush + ? BoundsToDoEditsIn.Extend(FIntVector(0, 0, FMath::CeilToInt(FMath::Abs(SignedSculptStrength) + DistanceDivisor + 2))) + : BoundsToDoEditsIn; + + if (!BoundsToDoEditsIn.IsValid() || !BoundsWhereEditsHappen.IsValid()) + { + FVoxelMessages::Error("Invalid tool bounds!", this); + return {}; + } + + // Don't cache the entire column + const auto BoundsToCache = GetBoundsToCache(BoundsToDoEditsIn); + + FVoxelData& Data = GetVoxelWorld()->GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsWhereEditsHappen.Union(BoundsToCache), FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + FVoxelSurfaceEditsVoxels Voxels; + if (b2DBrush) + { + Voxels = UVoxelSurfaceTools::FindSurfaceVoxels2DImpl(Data, BoundsToDoEditsIn, false); + } + else + { + if (bSculpt) + { + Voxels = UVoxelSurfaceTools::FindSurfaceVoxelsFromDistanceFieldImpl(Data, BoundsToDoEditsIn, SharedConfig->bMultiThreaded, SharedConfig->GetComputeDevice()); + } + else + { + // No need to compute the distance field for paint + // Only select voxels inside the surface + Voxels = UVoxelSurfaceTools::FindSurfaceVoxelsImpl(Data, BoundsToDoEditsIn, false, true); + } + } + + FVoxelSurfaceEditsStack Stack; + + if (bEnableFalloff) + { + Stack.Add(UVoxelSurfaceTools::ApplyFalloff( + GetVoxelWorld(), + FalloffType, + GetToolPosition(), + Radius, + Falloff)); + } + + if (ShouldUseMask()) + { + TVoxelTexture MaskTexture; + float MaskScaleX = 1; + float MaskScaleY = 1; + if (GetMaskData(true, MaskTexture, MaskScaleX, MaskScaleY)) + { + Stack.Add(UVoxelSurfaceTools::ApplyStrengthMask( + GetVoxelWorld(), + MaskTexture, + GetToolPosition(), + MaskScaleX, + MaskScaleY, + GetToolNormal(), + GetToolDirection(), + EVoxelSamplerMode::Tile)); + } + } + + const FVoxelHardnessHandler HardnessHandler(*GetVoxelWorld()); + + FVoxelSurfaceEditsProcessedVoxels ProcessedVoxels; + if (bSculpt && bPaint) + { + auto SculptStack = Stack; + SculptStack.Add(UVoxelSurfaceTools::ApplyConstantStrength(-SignedSculptStrength)); + ProcessedVoxels = SculptStack.Execute(Voxels, false); + + auto RecordingDataImpl = GetDataImpl(Data); + FVoxelSurfaceEditToolsImpl::EditVoxelValues(DataImpl, HardnessHandler, BoundsWhereEditsHappen, ProcessedVoxels, DistanceDivisor); + + TArray Materials; + Materials.Reserve(RecordingDataImpl.ModifiedValues.Num()); + for (auto& Voxel : RecordingDataImpl.ModifiedValues) + { + if (Voxel.OldValue > 0 && Voxel.NewValue <= 0) + { + FVoxelSurfaceEditsVoxel NewVoxel; + NewVoxel.Position = Voxel.Position; + NewVoxel.Strength = PaintStrength; + Materials.Add(NewVoxel); + } + } + FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(DataImpl, BoundsWhereEditsHappen, SharedConfig->PaintMaterial, Materials); + } + else if (bSculpt) + { + auto SculptStack = Stack; + SculptStack.Add(UVoxelSurfaceTools::ApplyConstantStrength(-SignedSculptStrength)); + ProcessedVoxels = SculptStack.Execute(Voxels, false); + + if (bPropagateMaterials && !b2DBrush) + { + FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials(DataImpl, ProcessedVoxels); + } + + FVoxelSurfaceEditToolsImpl::EditVoxelValues(DataImpl, HardnessHandler, BoundsWhereEditsHappen, ProcessedVoxels, DistanceDivisor); + } + else if (bPaint) + { + // Note: Painting behaves the same with 2D edit on/off + auto PaintStack = Stack; + PaintStack.Add(UVoxelSurfaceTools::ApplyConstantStrength(SignedPaintStrength)); + ProcessedVoxels = PaintStack.Execute(Voxels, false); + + FVoxelSurfaceEditToolsImpl::EditVoxelMaterials(DataImpl, BoundsWhereEditsHappen, SharedConfig->PaintMaterial, *ProcessedVoxels.Voxels); + } + + if (SharedConfig->bDebug) + { + UVoxelSurfaceTools::DebugSurfaceVoxels(GetVoxelWorld(), ProcessedVoxels, Stride > 0 ? 1 : 2 * GetDeltaTime()); + } + + return BoundsWhereEditsHappen; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelSurfaceTool::GetMaskData(bool bShowNotification, TVoxelTexture& OutTexture, float& OutScaleX, float& OutScaleY) +{ + if (SharedConfig->BrushSize <= 0) + { + return false; + } + + const float VoxelSize = GetVoxelWorld()->VoxelSize; + + if (Mask.Type == EVoxelSurfaceToolMaskType::Texture) + { + FString Error; + if (!FVoxelTextureUtilities::CanCreateFromTexture(Mask.Texture, Error)) + { + if (bShowNotification) + { + FVoxelMessages::FNotification Notification; + Notification.UniqueId = OBJECT_LINE_ID(); + Notification.Message = Error; + + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Fix Now"; + Button.Tooltip = "Fix the texture"; + Button.OnClick = FSimpleDelegate::CreateWeakLambda(Mask.Texture, [Mask = Mask.Texture]() { FVoxelTextureUtilities::FixTexture(Mask); }); + + FVoxelMessages::ShowNotification(Notification); + } + return false; + } + OutTexture = FVoxelTextureUtilities::CreateFromTexture_Float(Mask.Texture, Mask.Channel); + + const float MaskWantedSize = SharedConfig->BrushSize / VoxelSize * Mask.Scale; + OutScaleX = MaskWantedSize / OutTexture.GetSizeX(); + OutScaleY = MaskWantedSize / OutTexture.GetSizeY() * Mask.Ratio; + + return true; + } + else + { + bool bNeedToRebuild = false; + bool bNeedToRebuildSeeds = false; + if (LastFrameCanEdit() && LastFrameCanEdit() != CanEdit()) + { + // Reset if we stopped clicking, or if stride edit just ended + bNeedToRebuild = true; + bNeedToRebuildSeeds = true; + } + if (!MaskGeneratorCache.CachedConfig.HasSameGeneratorSettings(Mask) || + (Mask.bScaleWithBrushSize && MaskGeneratorCache.BrushSize != SharedConfig->BrushSize)) + { + bNeedToRebuild = true; + } + ensure(!bNeedToRebuildSeeds || bNeedToRebuild); + + if (bNeedToRebuild) + { + MaskGeneratorCache.CachedConfig = Mask; + MaskGeneratorCache.BrushSize = SharedConfig->BrushSize; + + const int32 Size = SharedConfig->BrushSize / VoxelSize; + + if (bNeedToRebuildSeeds || MaskGeneratorCache.Seeds.Num() == 0) + { + MaskGeneratorCache.Seeds.Reset(); + for (auto& SeedName : Mask.SeedsToRandomize) + { + MaskGeneratorCache.Seeds.Add(SeedName, FMath::Rand()); + } + } + + auto Picker = Mask.Generator; + for (auto& It : MaskGeneratorCache.Seeds) + { + Picker.Parameters.Add(It.Key, LexToString(It.Value)); + } + auto* GeneratorInstance = UVoxelGeneratorTools::MakeGeneratorInstance(Picker.GetGenerator(), GetVoxelWorld()->GetGeneratorInit()); + + FVoxelFloatTexture FloatTexture; + UVoxelGeneratorTools::CreateFloatTextureFromGenerator( + FloatTexture, + GeneratorInstance, + "Value", + Size, + Size, + Mask.Scale * (Mask.bScaleWithBrushSize ? 100.f / (SharedConfig->BrushSize / 2.f / VoxelSize) : 1.f), + -Size / 2, -Size / 2); + + // We want the data to be between 0 and 1 for sculpt strength to be coherent + MaskGeneratorCache.VoxelTexture = FVoxelTextureUtilities::Normalize(FloatTexture.Texture); + + const auto ColorTexture = FVoxelTextureUtilities::CreateColorTextureFromFloatTexture(MaskGeneratorCache.VoxelTexture, EVoxelRGBA::R, false); + + FVoxelTextureUtilities::CreateOrUpdateUTexture2D(ColorTexture, MaskGeneratorCache_RenderTexture); + } + + Mask.GeneratorDebugTexture = MaskGeneratorCache_RenderTexture; + + OutTexture = MaskGeneratorCache.VoxelTexture; + OutScaleX = 1.f; + OutScaleY = Mask.Ratio; + + return true; + } +} + +bool UVoxelSurfaceTool::ShouldUseMask() const +{ + return bUseMask && (Mask.Type == EVoxelSurfaceToolMaskType::Texture ? Mask.Texture != nullptr : Mask.Generator.IsValid()); +} + +void UVoxelSurfaceTool::GetStrengths(float& OutSignedSculptStrength, float& OutSignedPaintStrength) const +{ + const bool bIsStrideEnabled = Stride != 0; + const bool bUseDeltaTimeForSculpt = bModulateStrengthByDeltaTime && !bIsStrideEnabled; + const bool bUseDeltaTimeForPaint = bUseDeltaTimeForSculpt && PaintStrength < 1.f; + + const float MovementStrengthMultiplier = bMovementAffectsStrength ? GetMouseMovementSize() / 100 : 1; + const float RadiusMultiplier = bIsStrideEnabled ? SharedConfig->BrushSize / 2.f / GetVoxelWorld()->VoxelSize : 1.f; + + // Default paint/sculpt strengths are too low to feel good + const float SculptStrengthStaticMultiplier = bIsStrideEnabled ? 1.f : 50.f; + const float PaintStrengthStaticMultiplier = 10.f; + + const float ActualSculptStrength = SculptStrength * MovementStrengthMultiplier * (bUseDeltaTimeForSculpt ? GetDeltaTime() : 1.f) * SculptStrengthStaticMultiplier * RadiusMultiplier; + const float ActualPaintStrength = PaintStrength * MovementStrengthMultiplier * (bUseDeltaTimeForPaint ? GetDeltaTime() : 1.f) * PaintStrengthStaticMultiplier; + + const bool bAlternativeMode = GetTickData().IsAlternativeMode(); + OutSignedSculptStrength = ActualSculptStrength * (bAlternativeMode ? -1 : 1); + OutSignedPaintStrength = ActualPaintStrength * (bAlternativeMode ? -1 : 1); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTool.cpp new file mode 100644 index 00000000..040bb272 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTool.cpp @@ -0,0 +1,336 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelTool.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelWorld.h" + +#include "Engine/StaticMesh.h" +#include "Engine/LocalPlayer.h" +#include "Materials/MaterialInterface.h" +#include "Kismet/GameplayStatics.h" +#include "GameFramework/PlayerController.h" +#include "UObject/ConstructorHelpers.h" + +static bool GVoxelToolsAreFrozen = false; + +static void FreezeVoxelTools() +{ + if (!GVoxelToolsAreFrozen) + { + GVoxelToolsAreFrozen = true; + LOG_VOXEL(Log, TEXT("Freezing tool manager")); + } + else + { + GVoxelToolsAreFrozen = false; + LOG_VOXEL(Log, TEXT("Unfreezing tool manager")); + } +} + +static FAutoConsoleCommand CmdFreeze( + TEXT("voxel.tools.Freeze"), + TEXT(""), + FConsoleCommandDelegate::CreateStatic(&FreezeVoxelTools)); + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelToolSharedConfig::UVoxelToolSharedConfig() +{ + static ConstructorHelpers::FObjectFinder PlaneMeshFinder(TEXT("/Engine/BasicShapes/Plane")); + static ConstructorHelpers::FObjectFinder PlaneMaterialFinder(TEXT("/Voxel/ToolMaterials/ViewportPlaneMaterial")); + + PlaneMesh = PlaneMeshFinder.Object; + PlaneMaterial = PlaneMaterialFinder.Object; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTool::EnableTool() +{ + ensure(!bEnabled); + bEnabled = true; + + if (!SharedConfig) + { + SharedConfig = NewObject(this); + } +} + +void UVoxelTool::DisableTool() +{ + ensure(bEnabled); + bEnabled = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTool::K2_AdvancedTick(UWorld* World, const FVoxelToolTickData& TickData, const FDoEditDynamicOverride& DoEditOverride) +{ + FDoEditOverride DoEditOverrideCpp; + if (DoEditOverride.IsBound()) + { + DoEditOverrideCpp.BindLambda([&](FVector Position, FVector Normal) { DoEditOverride.Execute(Position, Normal); }); + } + AdvancedTick(World, TickData, DoEditOverrideCpp); +} + +void UVoxelTool::AdvancedTick(UWorld* World, const FVoxelToolTickData& TickData, const FDoEditOverride& DoEditOverride) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!bEnabled) + { + EnableTool(); + } + + if (!World) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid World!")); + return; + } + + const auto CanEditWorld = [&](AVoxelWorld* InWorld) + { + return InWorld && InWorld->IsCreated() && (SharedConfig->WorldsToEdit.Num() == 0 || SharedConfig->WorldsToEdit.Contains(InWorld)); + }; + + const FVector Start = TickData.GetRayOrigin(); + const FVector End = TickData.GetRayOrigin() + float(WORLD_MAX) * TickData.GetRayDirection(); + + FHitResult HitResult; + World->GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, TickData.CollisionChannel); + + AVoxelWorld* VoxelWorld = Cast(HitResult.Actor); + if (!CanEditWorld(VoxelWorld)) + { + VoxelWorld = nullptr; + } + +#if WITH_EDITOR + if (VoxelWorld) + { + SharedConfig->PaintMaterial.bRestrictType = true; + if (SharedConfig->PaintMaterial.MaterialConfigToRestrictTo != VoxelWorld->MaterialConfig) + { + SharedConfig->PaintMaterial.MaterialConfigToRestrictTo = VoxelWorld->MaterialConfig; + SharedConfig->RefreshDetails.Broadcast(); + } + if (SharedConfig->PaintMaterial.PreviewMaterialCollection != VoxelWorld->MaterialCollection) + { + SharedConfig->PaintMaterial.PreviewMaterialCollection = VoxelWorld->MaterialCollection; + SharedConfig->RefreshDetails.Broadcast(); + } + } +#endif + + if (!GVoxelToolsAreFrozen) + { + FrozenTickData = TickData; + } + + FCallToolParameters Parameters; + Parameters.Mode = ECallToolMode::Tick; + Parameters.Position = HitResult.ImpactPoint; + Parameters.Normal = HitResult.ImpactNormal; + Parameters.bBlockingHit = HitResult.bBlockingHit; + + if (DoEditOverride.IsBound()) + { + Parameters.DoEditOverride = [&](FVector Position, FVector Normal) { DoEditOverride.Execute(Position, Normal); }; + } + + CallTool(VoxelWorld, GVoxelToolsAreFrozen ? FrozenTickData : TickData, Parameters); +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTool::K2_SimpleTick( + APlayerController* PlayerController, + bool bEdit, + const TMap& Keys, + const TMap& Axes, + const FDoEditDynamicOverride& DoEditOverride, + ECollisionChannel CollisionChannel) +{ + FDoEditOverride DoEditOverrideCpp; + if (DoEditOverride.IsBound()) + { + DoEditOverrideCpp.BindLambda([&](FVector Position, FVector Normal) { DoEditOverride.Execute(Position, Normal); }); + } + SimpleTick(PlayerController, bEdit, Keys, Axes, DoEditOverrideCpp, CollisionChannel); +} + +void UVoxelTool::SimpleTick( + APlayerController* PlayerController, + bool bEdit, + const TMap& Keys, + const TMap& Axes, + const FDoEditOverride& DoEditOverride, + ECollisionChannel CollisionChannel) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!PlayerController) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid PlayerController!")); + return; + } + + ULocalPlayer* const LocalPlayer = PlayerController->GetLocalPlayer(); + if (!LocalPlayer) + { + FVoxelMessages::Warning(FUNCTION_ERROR("Invalid LocalPlayer!")); + return; + } + + auto* ViewportClient = LocalPlayer->ViewportClient; + if (!ViewportClient) + { + FVoxelMessages::Warning(FUNCTION_ERROR("Invalid ViewportClient!")); + return; + } + + FVector2D ScreenPosition; + if (!ViewportClient->GetMousePosition(ScreenPosition)) + { + // This happen when the mouse is over the Unreal UI: use the center + FVector2D Size; + ViewportClient->GetViewportSize(Size); + ScreenPosition = Size / 2; + + // Make sure to do nothing when clicking on the UI + bEdit = false; + } + + APlayerCameraManager* PlayerCameraManager = PlayerController->PlayerCameraManager; + if (!PlayerCameraManager) + { + FVoxelMessages::Warning(FUNCTION_ERROR("Invalid PlayerCameraManager!")); + return; + } + + FVoxelToolTickData TickData; + TickData.MousePosition = ScreenPosition; + TickData.CameraViewDirection = PlayerCameraManager->GetCameraRotation().Vector(); + TickData.bEdit = bEdit; + TickData.Keys = Keys; + TickData.Axes = Axes; + TickData.CollisionChannel = CollisionChannel; + + const auto Deproject = [PlayerController = MakeWeakObjectPtr(PlayerController)]( + const FVector2D& InScreenPosition, + FVector& OutWorldPosition, + FVector& OutWorldDirection) + { + return UGameplayStatics::DeprojectScreenToWorld(PlayerController.Get(), InScreenPosition, OutWorldPosition, OutWorldDirection); + }; + TickData.Init(Deproject); + + AdvancedTick(PlayerController->GetWorld(), TickData, DoEditOverride); +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTool::Apply( + AVoxelWorld* World, + FVector Position, + FVector Normal, + const TMap& Keys, + const TMap& Axes) +{ + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (!bEnabled) + { + EnableTool(); + } + + Normal = Normal.GetSafeNormal(); + if (Normal.IsNearlyZero()) + { + FVoxelMessages::Warning(FUNCTION_ERROR("Normal is zero, using UpVector instead")); + Normal = FVector::UpVector; + } + + FVoxelToolTickData TickData; + TickData.bEdit = true; + TickData.Keys = Keys; + TickData.Axes = Axes; + + FCallToolParameters Parameters; + Parameters.Mode = ECallToolMode::Apply; + Parameters.Position = Position; + Parameters.Normal = Normal; + Parameters.bBlockingHit = true; + + CallTool(World, TickData, Parameters); +} + +/////////////////////////////////////////////////////////////////////////////// + +FName UVoxelTool::GetToolName() const +{ + return ToolName.IsNone() ? GetClass()->GetFName() : ToolName; +} + +/////////////////////////////////////////////////////////////////////////////// + +TMap UVoxelTool::MakeToolKeys(bool bAlternativeMode) +{ + return { { FVoxelToolKeys::AlternativeMode, bAlternativeMode } }; +} + +TMap UVoxelTool::MakeToolAxes(float BrushSizeDelta, float FalloffDelta, float StrengthDelta) +{ + return + { + { + FVoxelToolAxes::BrushSize, + BrushSizeDelta + }, + { + FVoxelToolAxes::Falloff, + FalloffDelta + }, + { + FVoxelToolAxes::Strength, + StrengthDelta + } + }; +} + +UVoxelTool* UVoxelTool::MakeVoxelTool(TSubclassOf ToolClass) +{ + if (!ToolClass) + { + FVoxelMessages::Error(FUNCTION_ERROR("null ToolClass")); + return nullptr; + } + if (ToolClass->HasAllClassFlags(CLASS_Abstract)) + { + FVoxelMessages::Error(FUNCTION_ERROR("ToolClass is abstract")); + return nullptr; + } + + auto* Tool = NewObject(GetTransientPackage(), ToolClass); + if (!ensure(Tool)) + { + return nullptr; + } + + Tool->SharedConfig = NewObject(Tool); + return Tool; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTool::BeginDestroy() +{ + // Make sure to not leave any mesh behind + if (bEnabled) + { + DisableTool(); + } + + Super::BeginDestroy(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp new file mode 100644 index 00000000..e04dba2f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp @@ -0,0 +1,636 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelWorld.h" +#include "VoxelData/VoxelData.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelRender/IVoxelLODManager.h" + +#include "Materials/MaterialInstanceDynamic.h" +#include "DrawDebugHelpers.h" +#include "Engine/Engine.h" +#include "Engine/StaticMesh.h" +#include "Engine/StaticMeshActor.h" +#include "Components/StaticMeshComponent.h" + +static TAutoConsoleVariable CVarDebugMaterialUnderCursor( + TEXT("voxel.tools.DebugMaterialUnderCursor"), + 0, + TEXT("If true, will show the values of the voxel material under the cursor"), + ECVF_Default); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelToolBase::Tick() +{ + SharedConfig->BrushSize = GetValueAfterAxisInput(FVoxelToolAxes::BrushSize, SharedConfig->BrushSize, 0, 20000); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelToolBase::EnableTool() +{ + Super::EnableTool(); + + ensure(!VoxelWorld); +} + +void UVoxelToolBase::DisableTool() +{ + Super::DisableTool(); + + ClearVoxelWorld(); +} + +void UVoxelToolBase::CallTool(AVoxelWorld* InVoxelWorld, const FVoxelToolTickData& InTickData, const FCallToolParameters& Parameters) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(SharedConfig)) + { + return; + } + + if (InVoxelWorld && VoxelWorld != InVoxelWorld) + { + ClearVoxelWorld(); + VoxelWorld = InVoxelWorld; + } + + if (!VoxelWorld || !VoxelWorld->IsCreated()) + { + return; + } + + LastFrameTickData = TickData; + TickData = InTickData; + + K2_GetToolConfig({}, ToolBaseConfig); + + if (Parameters.Mode == ECallToolMode::Apply) + { + MouseMovementSize = 1; + } + else + { + MouseMovementSize = (LastFrameTickData.MousePosition - TickData.MousePosition).Size(); + } + + // Update position/normal to the hit ones + if (Parameters.Mode == ECallToolMode::Apply || Parameters.bBlockingHit) + { + CurrentPosition = Parameters.Position; + CurrentNormal = Parameters.Normal; + } + + // Viewport-aligned movement + if (Parameters.Mode == ECallToolMode::Tick && ToolBaseConfig.bHasAlignment) + { + bool bShowPlane = false; + if (ToolBaseConfig.Alignment == EVoxelToolAlignment::Surface) + { + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(TickData.bEdit); + + // No need to override position/normal + } + else + { + if (!LastFrameTickData.bEdit) + { + FVector Normal = FVector::UpVector; + switch (ToolBaseConfig.Alignment) + { + case EVoxelToolAlignment::View: + Normal = -TickData.CameraViewDirection; + break; + case EVoxelToolAlignment::Ground: + Normal = FVector::UpVector; + break; + case EVoxelToolAlignment::Up: + Normal = -TickData.CameraViewDirection; + Normal.Z = 0; + if (!Normal.Normalize()) + { + ensure(TickData.CameraViewDirection.GetAbs().Equals(FVector::UpVector)); + Normal = FVector::RightVector; + } + break; + default: ensure(false); + } + + const bool bAirMode = !Parameters.bBlockingHit || ToolBaseConfig.bAirMode; + const FVector Point = bAirMode + ? TickData.GetRayOrigin() + TickData.GetRayDirection() * ToolBaseConfig.DistanceToCamera + : FVector(Parameters.Position); + ViewportSpaceMovement.LastClickPlane = FPlane(Point, Normal); + ViewportSpaceMovement.LastClickPoint = Point; + ViewportSpaceMovement.LastClickNormal = bAirMode ? FVector::UpVector : Parameters.Normal; + } + + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(false); + + // Override position/normal + CurrentPosition = FMath::RayPlaneIntersection(TickData.GetRayOrigin(), TickData.GetRayDirection(), ViewportSpaceMovement.LastClickPlane); + CurrentNormal = ViewportSpaceMovement.LastClickNormal; + + if (TickData.bEdit) + { + bShowPlane = true; + } + } + + static const FName PlaneMeshId = "ViewportMovementPlane"; + if (bShowPlane && ToolBaseConfig.bShowPlanePreview && SharedConfig->PlaneMesh && SharedConfig->PlaneMaterial) + { + const FVector PlaneNormal = FVector(ViewportSpaceMovement.LastClickPlane); + const FTransform Transform( + FRotationMatrix::MakeFromZ(PlaneNormal).ToQuat(), + ViewportSpaceMovement.LastClickPoint + PlaneNormal * 0.5f, // Prevent Z fighting + FVector(10000.f)); + + if (!PlaneMeshMaterialInstance) + { + PlaneMeshMaterialInstance = UMaterialInstanceDynamic::Create(SharedConfig->PlaneMaterial, GetTransientPackage()); + } + if (!ensure(PlaneMeshMaterialInstance)) + { + return; + } + PlaneMeshMaterialInstance->SetScalarParameterValue(STATIC_FNAME("VoxelSize"), VoxelWorld->VoxelSize); + + UpdateToolMesh( + SharedConfig->PlaneMesh, + PlaneMeshMaterialInstance, + Transform, + PlaneMeshId); + } + else + { + UpdateToolMesh(nullptr, nullptr, {}, PlaneMeshId); + } + } + + // Movement direction + { + const FVector NewMovementTangent = (CurrentPosition - LastPositionUsedForTangent).GetSafeNormal(); + MovementTangent = FMath::Lerp( + MovementTangent, + NewMovementTangent, + FMath::Clamp((1 - SharedConfig->AlignToMovementSmoothness) * GetMouseMovementSize() / 10, 0.f, 1.f)).GetSafeNormal(); + LastPositionUsedForTangent = CurrentPosition; + } + + // Fixed normal + if (ToolBaseConfig.bUseFixedNormal) + { + CurrentNormal = ToolBaseConfig.FixedNormal; + } + + // Stride + if (Parameters.Mode == ECallToolMode::Apply || + ToolBaseConfig.Stride == 0.f || + !LastFrameTickData.bEdit || // If not clicking always keep the position under the cursor + FVector::Dist(CurrentPosition, StridePosition) >= ToolBaseConfig.Stride * SharedConfig->BrushSize) + { + StridePosition = CurrentPosition; + StrideNormal = CurrentNormal; + StrideDirection = MovementTangent; + + bCanEdit = TickData.bEdit; + + if (Parameters.Mode == ECallToolMode::Tick && !ToolBaseConfig.bHasAlignment) + { + // When we can edit, we flush the collisions and freeze them again + // This is so that when we cannot edit, we do the raycasts on the old geometry + // and the tool travel distance isn't artificially done by the edits + // Without that, you can get a continuous stream of edits when keeping click pressed + // without moving the mouse, which is unexpected when using stride + // Not needed in viewport space movement + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(false); + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(true); + } + } + else + { + bCanEdit = false; + } + + // End of click + if (!TickData.bEdit) + { + ensure(Parameters.Mode == ECallToolMode::Tick); + ApplyPendingFrameBounds(); + } + + // Debug + if (SharedConfig->bDebug) + { + DrawDebugDirectionalArrow( + VoxelWorld->GetWorld(), + GetToolPosition(), + GetToolPosition() + GetToolDirection() * VoxelWorld->VoxelSize * 5, + VoxelWorld->VoxelSize * 5, + FColor::Red, + false, + 1.5f * GetDeltaTime(), + 0, + VoxelWorld->VoxelSize / 2); + DrawDebugDirectionalArrow( + VoxelWorld->GetWorld(), + GetToolPosition(), + GetToolPosition() + GetToolNormal() * VoxelWorld->VoxelSize * 5, + VoxelWorld->VoxelSize * 5, + FColor::Blue, + false, + 1.5f * GetDeltaTime(), + 0, + VoxelWorld->VoxelSize / 2); + } + if (Parameters.Mode == ECallToolMode::Tick && CVarDebugMaterialUnderCursor.GetValueOnGameThread() && Parameters.bBlockingHit) + { + FVoxelFindClosestNonEmptyVoxelResult Result; + UVoxelDataTools::FindClosestNonEmptyVoxel(Result, VoxelWorld, Parameters.Position); + + if (Result.bSuccess) + { + FVoxelMaterial Material; + UVoxelDataTools::GetMaterial(Material, VoxelWorld, Result.Position); + + FString Message; + Message += FString::Printf(TEXT("R: %d; G: %d; B: %d; A: %d\n"), Material.GetR(), Material.GetG(), Material.GetB(), Material.GetA()); + if (VOXEL_MATERIAL_ENABLE_UV0) Message += FString::Printf(TEXT("U0: %d; V0: %d\n"), Material.GetU0(), Material.GetV0()); + if (VOXEL_MATERIAL_ENABLE_UV1) Message += FString::Printf(TEXT("U1: %d; V1: %d\n"), Material.GetU1(), Material.GetV1()); + if (VOXEL_MATERIAL_ENABLE_UV2) Message += FString::Printf(TEXT("U2: %d; V2: %d\n"), Material.GetU2(), Material.GetV2()); + if (VOXEL_MATERIAL_ENABLE_UV3) Message += FString::Printf(TEXT("U3: %d; V3: %d\n"), Material.GetU3(), Material.GetV3()); + + if (VoxelWorld->MaterialConfig == EVoxelMaterialConfig::RGB) + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetFiveWayBlendStrengths(Material); + + Message += FString::Printf(TEXT("Blend0: %.4f\n"), Strengths[0]); + Message += FString::Printf(TEXT("Blend1: %.4f\n"), Strengths[1]); + Message += FString::Printf(TEXT("Blend2: %.4f\n"), Strengths[2]); + Message += FString::Printf(TEXT("Blend3: %.4f\n"), Strengths[3]); + Message += FString::Printf(TEXT("Blend4: %.4f\n"), Strengths[4]); + } + else if (VoxelWorld->MaterialConfig == EVoxelMaterialConfig::RGB) + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetFourWayBlendStrengths(Material); + + Message += FString::Printf(TEXT("Blend0: %.4f\n"), Strengths[0]); + Message += FString::Printf(TEXT("Blend1: %.4f\n"), Strengths[1]); + Message += FString::Printf(TEXT("Blend2: %.4f\n"), Strengths[2]); + Message += FString::Printf(TEXT("Blend3: %.4f\n"), Strengths[3]); + } + else if (VoxelWorld->MaterialConfig == EVoxelMaterialConfig::MultiIndex) + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + + Message += FString::Printf(TEXT("Index0: %d; Blend0: %.4f\n"), Material.GetMultiIndex_Index0(), Strengths[0]); + Message += FString::Printf(TEXT("Index1: %d; Blend1: %.4f\n"), Material.GetMultiIndex_Index1(), Strengths[1]); + Message += FString::Printf(TEXT("Index2: %d; Blend2: %.4f\n"), Material.GetMultiIndex_Index2(), Strengths[2]); + Message += FString::Printf(TEXT("Index3: %d; Blend3: %.4f\n"), Material.GetMultiIndex_Index3(), Strengths[3]); + } + + GEngine->AddOnScreenDebugMessage(OBJECT_LINE_ID(), GetDeltaTime() * 1.5f, FColor::Green, Message); + } + } + + if (Parameters.Mode == ECallToolMode::Tick) + { + // Tool material overlay on the voxel world + if (ToolBaseConfig.OverlayMaterial) + { + auto& ToolRenderingManager = VoxelWorld->GetToolRenderingManager(); + if (!ToolRenderingId.IsValid() || !ToolRenderingManager.IsValidTool(ToolRenderingId)) + { + ToolRenderingId = ToolRenderingManager.CreateTool(true); + } + + if (!ToolOverlayMaterialInstance || ToolOverlayMaterialInstance->Parent != ToolBaseConfig.OverlayMaterial) + { + ToolOverlayMaterialInstance = UMaterialInstanceDynamic::Create(ToolBaseConfig.OverlayMaterial, GetTransientPackage()); + if (!ensure(ToolOverlayMaterialInstance)) return; + } + check(ToolOverlayMaterialInstance); + + ToolRenderingManager.EditTool(ToolRenderingId, [&](FVoxelToolRendering& Tool) + { + if (!Tool.Material.IsValid() || Tool.Material->GetMaterial() != ToolOverlayMaterialInstance) + { + Tool.Material = FVoxelMaterialInterfaceManager::Get().CreateMaterial(ToolOverlayMaterialInstance); + } + }); + } + else if (ToolRenderingId.IsValid()) + { + // Some tools can enabled/disable overlay + auto& ToolRenderingManager = VoxelWorld->GetToolRenderingManager(); + ToolRenderingManager.RemoveTool(ToolRenderingId); + ToolRenderingId.Reset(); + + ToolOverlayMaterialInstance = nullptr; + } + + if (ToolBaseConfig.MeshMaterial) + { + if (!ToolMeshMaterialInstance || ToolMeshMaterialInstance->Parent != ToolBaseConfig.MeshMaterial) + { + ToolMeshMaterialInstance = UMaterialInstanceDynamic::Create(ToolBaseConfig.MeshMaterial, GetTransientPackage()); + if (!ensure(ToolMeshMaterialInstance)) return; + } + } + else + { + ToolMeshMaterialInstance = nullptr; + } + } + + Tick(); + K2_Tick(); + + if (Parameters.Mode == ECallToolMode::Tick) + { + K2_UpdateRender(ToolOverlayMaterialInstance, ToolMeshMaterialInstance); + } + + if (Parameters.Mode == ECallToolMode::Tick && SharedConfig->bWaitForUpdates && NumPendingUpdates > 0) + { + if (SharedConfig->bDebug) + { + GEngine->AddOnScreenDebugMessage( + OBJECT_LINE_ID(), + 1.5f * VoxelWorld->GetWorld()->GetDeltaSeconds(), + FColor::Yellow, + FString::Printf(TEXT("Waiting for %d updates"), NumPendingUpdates)); + } + } + else + { + if (Parameters.Mode == ECallToolMode::Apply) + { + // Can't use LastTickTime in Apply + DeltaTime = SharedConfig->FixedDeltaTime; + } + else + { + const double Now = FPlatformTime::Seconds(); + DeltaTime = float(Now - LastTickTime); + LastTickTime = Now; + } + + if (bCanEdit) + { + FVoxelIntBoxWithValidity ModifiedBounds; + + if (Parameters.DoEditOverride) + { + // Do not do the actual edit, just call the override + Parameters.DoEditOverride(GetToolPosition(), GetToolNormal()); + } + else + { + ModifiedBounds = K2_DoEdit(); + } + + if (ModifiedBounds.IsValid()) + { + PendingFrameBounds += ModifiedBounds; + + NumPendingUpdates += VoxelWorld->GetLODManager().UpdateBounds_OnAllFinished( + ModifiedBounds.GetBox(), + FSimpleDelegate::CreateWeakLambda(this, [this, ModifiedBounds = ModifiedBounds.GetBox(), OldVoxelWorld = VoxelWorld]() + { + if (VoxelWorld == OldVoxelWorld) + { + SharedConfig->OnBoundsUpdated.Broadcast(VoxelWorld, ModifiedBounds); + if (SharedConfig->bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(VoxelWorld, ModifiedBounds, FTransform(), 0.1f); + } + } + }), + FVoxelOnChunkUpdateFinished::FDelegate::CreateWeakLambda(this, [this](const FVoxelIntBox& /*ChunkBounds*/) + { + NumPendingUpdates--; + ensure(NumPendingUpdates >= 0); + })); + } + } + + // Only copy it when we're not waiting for tasks, as that's the value that matters + LastFrameTickData = TickData; + bLastFrameCanEdit = bCanEdit; + } + + if (Parameters.Mode == ECallToolMode::Apply) + { + // Apply right away, tick won't be called again + ApplyPendingFrameBounds(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +FVector UVoxelToolBase::GetToolPosition() const +{ + return StridePosition; +} + +FVector UVoxelToolBase::GetToolPreviewPosition() const +{ + // Ignore stride for preview + return CurrentPosition; +} + +FVector UVoxelToolBase::GetToolNormal() const +{ + return StrideNormal; +} + +FVector UVoxelToolBase::GetToolDirection() const +{ + if (ToolBaseConfig.bUseFixedDirection) + { + return ToolBaseConfig.FixedDirection.Vector(); + } + else + { + return StrideDirection; + } +} + +void UVoxelToolBase::SetToolOverlayBounds(const FBox& Bounds) +{ + if (ensure(VoxelWorld) && ensure(VoxelWorld->IsCreated())) + { + VoxelWorld->GetToolRenderingManager().EditTool(ToolRenderingId, [&](FVoxelToolRendering& Tool) + { + Tool.WorldBounds = Bounds; + }); + } +} + +void UVoxelToolBase::UpdateToolMesh( + UStaticMesh* Mesh, + UMaterialInterface* Material, + const FTransform& Transform, + FName Id) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!ensure(VoxelWorld)) + { + return; + } + + auto& StaticMeshActor = StaticMeshActors.FindOrAdd(Id); + if (!StaticMeshActor.IsValid()) + { + FActorSpawnParameters SpawnParameters; + SpawnParameters.bDeferConstruction = true; + SpawnParameters.ObjectFlags = RF_Transient; + StaticMeshActor = VoxelWorld->GetWorld()->SpawnActor(SpawnParameters); + StaticMeshActor->SetMobility(EComponentMobility::Movable); +#if WITH_EDITOR + StaticMeshActor->SetActorLabel("VoxelToolMeshActor"); +#endif + StaticMeshActor->SetActorEnableCollision(false); + + UStaticMeshComponent* MeshComponent = StaticMeshActor->GetStaticMeshComponent(); + if (ensure(MeshComponent)) + { + MeshComponent->CastShadow = false; + } + + StaticMeshActor->FinishSpawning(Transform); + } + + if (!ensure(StaticMeshActor.IsValid())) + { + return; + } + + UStaticMeshComponent* MeshComponent = StaticMeshActor->GetStaticMeshComponent(); + if (!ensure(MeshComponent)) + { + return; + } + + if (MeshComponent->GetStaticMesh() != Mesh) + { + MeshComponent->SetStaticMesh(Mesh); + for (int32 Index = 0; Index < MeshComponent->GetNumMaterials(); Index++) + { + MeshComponent->SetMaterial(Index, Material); + } + } + + StaticMeshActor->SetActorTransform(Transform); +} + +FVoxelIntBox UVoxelToolBase::GetBoundsToCache(const FVoxelIntBox& Bounds) const +{ + const auto BoundsToCache = UVoxelBlueprintLibrary::GetRenderBoundsOverlappingDataBounds(VoxelWorld, Bounds); + + if (SharedConfig->bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(VoxelWorld, Bounds, 1.5f * GetDeltaTime(), 0, FColor::Green); + UVoxelDebugUtilities::DrawDebugIntBox(VoxelWorld, BoundsToCache, 1.5f * GetDeltaTime(), 0, FColor::Red); + } + + return BoundsToCache; +} + +float UVoxelToolBase::GetValueAfterAxisInput(FName AxisName, float CurrentValue, float Min, float Max) const +{ + const float AxisValue = GetTickData().GetAxis(AxisName); + if (AxisValue != 0) + { + return FMath::Clamp(CurrentValue * (1.f + SharedConfig->ControlSpeed * AxisValue), Min, Max); + } + else + { + // Don't clamp if no axis input! + return CurrentValue; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelToolBase::ClearVoxelWorld() +{ + VOXEL_FUNCTION_COUNTER(); + + if (VoxelWorld && VoxelWorld->IsCreated()) + { + ApplyPendingFrameBounds(); + + if (ToolRenderingId.IsValid()) + { + auto& ToolRenderingManager = VoxelWorld->GetToolRenderingManager(); + if (ToolRenderingManager.IsValidTool(ToolRenderingId)) // Could be invalid if the voxel world was toggled off & on + { + ToolRenderingManager.RemoveTool(ToolRenderingId); + } + } + } + VoxelWorld = nullptr; + ToolRenderingId.Reset(); + + for (auto& It : StaticMeshActors) + { + auto& StaticMeshActor = It.Value; + if (StaticMeshActor.IsValid()) + { + StaticMeshActor->Destroy(); + } + } + StaticMeshActors.Empty(); + + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(false); +} + +void UVoxelToolBase::ApplyPendingFrameBounds() +{ + if (!PendingFrameBounds.IsValid()) + { + return; + } + + if (ensure(VoxelWorld) && ensure(VoxelWorld->IsCreated())) + { + auto& Data = VoxelWorld->GetData(); + if (Data.bEnableUndoRedo) + { + Data.SaveFrame(PendingFrameBounds.GetBox()); + SharedConfig->RegisterTransaction.Broadcast(GetToolName(), VoxelWorld); + } + } + + if (SharedConfig->bRegenerateSpawners) + { + UVoxelBlueprintLibrary::RegenerateSpawners(VoxelWorld, PendingFrameBounds.GetBox()); + } + + if (SharedConfig->bCheckForSingleValues) + { + UVoxelDataTools::CheckForSingleValues(VoxelWorld, PendingFrameBounds.GetBox()); + UVoxelDataTools::CheckForSingleMaterials(VoxelWorld, PendingFrameBounds.GetBox()); + } + + if (SharedConfig->bDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(VoxelWorld, PendingFrameBounds.GetBox(), 0.5f, 0, FColor::Purple); + } + + PendingFrameBounds.Reset(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolLibrary.cpp new file mode 100644 index 00000000..958a49dd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelToolLibrary.cpp @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelToolLibary.h" +#include "VoxelTools/Tools/VoxelToolBase.h" + +#include "Materials/MaterialInstanceDynamic.h" + +void UVoxelToolLibrary::UpdateSphereOverlayMaterial(UVoxelToolBase* Tool, UMaterialInstanceDynamic* OverlayMaterialInstance, EVoxelFalloff FalloffType, float Falloff) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Tool || !Tool->SharedConfig || !OverlayMaterialInstance) + { + return; + } + + const FVector Position = Tool->GetToolPreviewPosition(); + + const float Radius = Tool->SharedConfig->BrushSize / 2.f; + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Radius"), Radius); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("Position"), Position); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), Tool->SharedConfig->ToolOpacity); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Falloff"), Falloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("EnableFalloff"), true); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("FalloffType"), int32(FalloffType)); + + Tool->SetToolOverlayBounds(FBox(Position - Radius, Position + Radius)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTrimTool.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTrimTool.cpp new file mode 100644 index 00000000..2bcf5cc0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/Tools/VoxelTrimTool.cpp @@ -0,0 +1,109 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Tools/VoxelTrimTool.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.h" +#include "VoxelTools/Impl/VoxelSphereToolsImpl.inl" +#include "VoxelTools/VoxelProjectionTools.h" +#include "VoxelWorld.h" +#include "VoxelData/VoxelData.h" + +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInstanceDynamic.h" + +UVoxelTrimTool::UVoxelTrimTool() +{ + ToolName = TEXT("Trim"); + + static ConstructorHelpers::FObjectFinder ToolMaterialFinder(TEXT("/Voxel/ToolMaterials/ToolRenderingMaterial_Trim")); + ToolMaterial = ToolMaterialFinder.Object; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelTrimTool::GetToolConfig(FVoxelToolBaseConfig& OutConfig) const +{ + OutConfig.OverlayMaterial = ToolMaterial; +} + +void UVoxelTrimTool::Tick() +{ + VOXEL_FUNCTION_COUNTER(); + + Super::Tick(); + + FVoxelLineTraceParameters Parameters; + Parameters.CollisionChannel = GetTickData().CollisionChannel; + Parameters.DrawDebugType = SharedConfig->bDebug ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None; + + TArray Hits; + const float RaysRadius = FMath::Max(SharedConfig->BrushSize / 2 * (1 - Roughness), GetVoxelWorld()->VoxelSize); + const FVector RayDirection = GetTickData().GetRayDirection(); + + UVoxelProjectionTools::FindProjectionVoxels( + Hits, + GetVoxelWorld(), + Parameters, + GetToolPosition() - RayDirection * RaysRadius, + RayDirection, + RaysRadius, + EVoxelProjectionShape::Circle, + 100.f, + 2 * RaysRadius); + + Position = UVoxelProjectionTools::GetHitsAveragePosition(Hits); + Normal = UVoxelProjectionTools::GetHitsAverageNormal(Hits); +} + +void UVoxelTrimTool::UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!OverlayMaterialInstance) + { + return; + } + + const float Radius = SharedConfig->BrushSize / 2; + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Radius"), Radius); + OverlayMaterialInstance->SetVectorParameterValue(STATIC_FNAME("Position"), Position); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Falloff"), Falloff); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Roughness"), Roughness); + OverlayMaterialInstance->SetScalarParameterValue(STATIC_FNAME("Opacity"), SharedConfig->ToolOpacity); + + SetToolOverlayBounds(FBox(Position - Radius, Position + Radius)); +} + +FVoxelIntBoxWithValidity UVoxelTrimTool::DoEdit() +{ + VOXEL_FUNCTION_COUNTER(); + + auto& World = *GetVoxelWorld(); + + const FVoxelVector VoxelPosition = World.GlobalToLocalFloat(Position); + const float VoxelRadius = SharedConfig->BrushSize / 2 / World.VoxelSize; + + const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(VoxelPosition, VoxelRadius); + if (!Bounds.IsValid()) + { + return {}; + } + const auto BoundsToCache = GetBoundsToCache(Bounds); + + auto& Data = World.GetData(); + auto DataImpl = GetDataImpl(Data); + + FVoxelWriteScopeLock Lock(Data, BoundsToCache, FUNCTION_FNAME); + CacheData(Data, BoundsToCache); + + FVoxelSphereToolsImpl::TrimSphere( + DataImpl, + VoxelPosition, + Normal, + VoxelRadius, + Falloff, + !GetTickData().IsAlternativeMode()); + + return Bounds; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelAssetTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelAssetTools.cpp new file mode 100644 index 00000000..ac55fdd7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelAssetTools.cpp @@ -0,0 +1,763 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelAssetTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelGenerators/VoxelGeneratorInstanceWrapper.h" +#include "VoxelWorld.h" + +inline TVoxelSharedPtr ImportAssetHelper( + const FString& FunctionName, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform& Transform, + FVoxelIntBox& Bounds, + bool bConvertToVoxelSpace) +{ + Transform = FVoxelToolHelpers::GetRealTransform(World, Transform, bConvertToVoxelSpace); + + if (!Asset || !Asset->IsValid()) + { + FVoxelMessages::Error(FunctionName + ": Invalid asset"); + return nullptr; + } + if (!Bounds.IsValid()) + { + if (Asset->Instance->HasBounds()) + { + Bounds = Asset->Instance->GetBounds().ApplyTransform(Transform); + } + else + { + FVoxelMessages::Error(FunctionName + ": Invalid Bounds, and cannot deduce them from Asset"); + return nullptr; + } + } + + return Asset->Instance; +} + +void UVoxelAssetTools::ImportAssetAsReference( + FVoxelAssetItemReference& Reference, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + int32 Priority, + bool bConvertToVoxelSpace, + bool bUpdateRender) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + auto& Data = World->GetData(); + + { + FVoxelWriteScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + Reference.Bounds = Bounds; + Reference.Item = Data.AddItem( + AssetInstance.ToSharedRef(), + Bounds, + Transform, + Priority); + } + + if (bUpdateRender) + { + FVoxelToolHelpers::UpdateWorld(World, Bounds); + } +} + +void UVoxelAssetTools::ImportAssetAsReferenceAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelAssetItemReference& Reference, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + int32 Priority, + bool bConvertToVoxelSpace, + bool bUpdateRender, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld_WithValue( + WorldContextObject, + LatentInfo, + World, + FUNCTION_FNAME, + bHideLatentWarnings, + Reference, + [Bounds, AssetInstance, Transform, Priority](FVoxelData& Data, FVoxelAssetItemReference& InReference) + { + FVoxelWriteScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + InReference.Bounds = Bounds; + InReference.Item = Data.AddItem( + AssetInstance.ToSharedRef(), + Bounds, + Transform, + Priority); + }, + bUpdateRender ? EVoxelUpdateRender::UpdateRender : EVoxelUpdateRender::DoNotUpdateRender, + Bounds); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::ImportModifierAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FTransform& Transform, + const FVoxelTransformableGeneratorInstance& Instance, + bool bModifyValues, + bool bModifyMaterials) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelOctreeUtilities::IterateTreeInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + ensureThreadSafe(Tree.IsLockedForWrite()); + auto& Leaf = Tree.AsLeaf(); + FVoxelDataUtilities::AddAssetItemToLeafData(Data, Leaf, Instance, Bounds, Transform, bModifyValues, bModifyMaterials); + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); +} + +void UVoxelAssetTools::ImportModifierAsset( + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bModifyValues, + bool bModifyMaterials, + bool bLockEntireWorld, + bool bConvertToVoxelSpace) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + auto& Data = World->GetData(); + { + FVoxelWriteScopeLock Lock(Data, bLockEntireWorld ? FVoxelIntBox::Infinite : Bounds, FUNCTION_FNAME); + ImportModifierAssetImpl(Data, Bounds, Transform, *AssetInstance, bModifyValues, bModifyMaterials); + } + FVoxelToolHelpers::UpdateWorld(World, Bounds); +} + +void UVoxelAssetTools::ImportModifierAssetAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bModifyValues, + bool bModifyMaterials, + bool bLockEntireWorld, + bool bConvertToVoxelSpace, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld( + WorldContextObject, + LatentInfo, + World, + FUNCTION_FNAME, + bHideLatentWarnings, + [=](FVoxelData& Data) + { + FVoxelWriteScopeLock Lock(Data, bLockEntireWorld ? FVoxelIntBox::Infinite : Bounds, FUNCTION_FNAME); + ImportModifierAssetImpl(Data, Bounds, Transform, *AssetInstance, bModifyValues, bModifyMaterials); + }, + EVoxelUpdateRender::UpdateRender, Bounds); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void ImportAssetImplImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + TA GetInstanceValue, + TB GetInstanceMaterial, + uint32 MaterialMask) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + const auto GetInstanceMaterialImpl = [&](int32 X, int32 Y, int32 Z, FVoxelMaterial Material) + { + const auto NewMaterial = GetInstanceMaterial(X, Y, Z); + Material.CopyFrom(NewMaterial, MaterialMask); + return Material; + }; + + switch (MergeMode) + { + case EVoxelAssetMergeMode::AllValues: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + Value = GetInstanceValue(X, Y, Z); + }); + break; + } + case EVoxelAssetMergeMode::AllMaterials: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelMaterial& Material) + { + Material = GetInstanceMaterialImpl(X, Y, Z, Material); + }); + break; + } + case EVoxelAssetMergeMode::AllValuesAndAllMaterials: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material) + { + Value = GetInstanceValue(X, Y, Z); + Material = GetInstanceMaterialImpl(X, Y, Z, Material); + }); + break; + } + case EVoxelAssetMergeMode::InnerValues: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const auto InstanceValue = GetInstanceValue(X, Y, Z); + Value = FVoxelUtilities::MergeAsset(Value, InstanceValue, bSubtractive); + }); + break; + } + case EVoxelAssetMergeMode::InnerMaterials: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, const FVoxelValue& Value, FVoxelMaterial& Material) + { + const auto InstanceValue = GetInstanceValue(X, Y, Z); + const auto NewValue = FVoxelUtilities::MergeAsset(Value, InstanceValue, bSubtractive); + if (NewValue == InstanceValue) + { + Material = GetInstanceMaterialImpl(X, Y, Z, Material); + } + }); + break; + } + case EVoxelAssetMergeMode::InnerValuesAndInnerMaterials: + { + Data.Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material) + { + const auto InstanceValue = GetInstanceValue(X, Y, Z); + const auto NewValue = FVoxelUtilities::MergeAsset(Value, InstanceValue, bSubtractive); + Value = NewValue; + if (NewValue == InstanceValue) + { + Material = GetInstanceMaterialImpl(X, Y, Z, Material); + } + }); + break; + } + default: ensure(false); + } +} + +void UVoxelAssetTools::ImportAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FTransform& Transform, + const FVoxelTransformableGeneratorInstance& Instance, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + uint32 MaterialMask) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + bool bNeedValues; + bool bNeedMaterials; + switch (MergeMode) + { + case EVoxelAssetMergeMode::AllValues: + { + bNeedValues = true; + bNeedMaterials = false; + break; + } + case EVoxelAssetMergeMode::AllMaterials: + { + bNeedValues = false; + bNeedMaterials = true; + break; + } + case EVoxelAssetMergeMode::AllValuesAndAllMaterials: + { + bNeedValues = true; + bNeedMaterials = true; + break; + } + case EVoxelAssetMergeMode::InnerValues: + { + bNeedValues = true; + bNeedMaterials = false; + break; + } + case EVoxelAssetMergeMode::InnerMaterials: + { + bNeedValues = true; + bNeedMaterials = true; + break; + } + case EVoxelAssetMergeMode::InnerValuesAndInnerMaterials: + { + bNeedValues = true; + bNeedMaterials = true; + break; + } + default: + { + bNeedValues = true; + bNeedMaterials = true; + ensure(false); + } + } + + const auto Size = Bounds.Size(); + check(FVoxelUtilities::CountIs32Bits(Size)); + + TArray Values; + if (bNeedValues) + { + Values.SetNumUninitialized(Size.X * Size.Y * Size.Z); + TVoxelQueryZone QueryZone(Bounds, Values); + Instance.GetValues_Transform(Transform, QueryZone, 0, FVoxelItemStack::Empty); + } + + TArray Materials; + if (bNeedMaterials) + { + Materials.SetNumUninitialized(Size.X * Size.Y * Size.Z); + TVoxelQueryZone QueryZone(Bounds, Materials); + Instance.GetMaterials_Transform(Transform, QueryZone, 0, FVoxelItemStack::Empty); + } + + const auto GetIndex = [&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(Bounds.Contains(X, Y, Z)); + const int32 LX = X - Bounds.Min.X; + const int32 LY = Y - Bounds.Min.Y; + const int32 LZ = Z - Bounds.Min.Z; + checkVoxelSlow(0 <= LX && LX < Size.X); + checkVoxelSlow(0 <= LY && LY < Size.Y); + checkVoxelSlow(0 <= LZ && LZ < Size.Z); + return LX + LY * Size.X + LZ * Size.X * Size.Y; + }; + + const auto GetInstanceValue = [&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(bNeedValues); + const int32 Index = GetIndex(X, Y, Z); + checkVoxelSlow(Values.IsValidIndex(Index)); + return Values.GetData()[Index]; + }; + const auto GetInstanceMaterial = [&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(bNeedMaterials); + const int32 Index = GetIndex(X, Y, Z); + checkVoxelSlow(Materials.IsValidIndex(Index)); + return Materials.GetData()[Index]; + }; + + ImportAssetImplImpl( + Data, + Bounds, + bSubtractive, + MergeMode, + GetInstanceValue, + GetInstanceMaterial, + MaterialMask); +} + +void UVoxelAssetTools::ImportDataAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FVoxelVector& Position, + const FVoxelDataAssetData& AssetData, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + uint32 MaterialMask) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + ensure(Bounds == FVoxelIntBox(Position, Position + AssetData.GetSize())); + + const FVoxelValue DefaultValue = bSubtractive ? FVoxelValue::Full() : FVoxelValue::Empty(); + const auto GetInstanceValue = [&](int32 X, int32 Y, int32 Z) + { + const v_flt NewValueFlt = AssetData.GetInterpolatedValue( + X - Position.X, + Y - Position.Y, + Z - Position.Z, + DefaultValue); + return FVoxelValue(NewValueFlt); + }; + const auto GetInstanceMaterial = [&](int32 X, int32 Y, int32 Z) + { + return AssetData.HasMaterials() ? AssetData.GetInterpolatedMaterial( + X - Position.X, + Y - Position.Y, + Z - Position.Z) : FVoxelMaterial::Default(); + }; + ImportAssetImplImpl( + Data, + Bounds, + bSubtractive, + MergeMode, + GetInstanceValue, + GetInstanceMaterial, + MaterialMask); +} + +void UVoxelAssetTools::ImportAsset( + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + bool bConvertToVoxelSpace) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + VOXEL_TOOL_HELPER_BODY(Write, UpdateRender, + ImportAssetImpl( + Data, + Bounds, + Transform, + *AssetInstance, + bSubtractive, + MergeMode, + EVoxelMaterialMask::All)); +} + +void UVoxelAssetTools::ImportAssetAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + bool bConvertToVoxelSpace, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const auto AssetInstance = ImportAssetHelper(__FUNCTION__, World, Asset, Transform, Bounds, bConvertToVoxelSpace); + if (!AssetInstance) return; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + VOXEL_TOOL_LATENT_HELPER_BODY(Write, UpdateRender, + ImportAssetImpl( + Data, + Bounds, + Transform, + *AssetInstance, + bSubtractive, + MergeMode, + EVoxelMaterialMask::All)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::ImportDataAssetFast( + AVoxelWorld* World, + UVoxelDataAsset* Asset, + FVector InPosition, + EVoxelAssetMergeMode MergeMode, + bool bConvertToVoxelSpace) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (!Asset) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid asset")); + return; + } + + FVoxelVector Position = FVoxelToolHelpers::GetRealPosition(World, InPosition, bConvertToVoxelSpace); + Position += Asset->PositionOffset; + + const auto AssetData = Asset->GetData(); + const FVoxelIntBox Bounds(Position, Position + AssetData->GetSize()); + const bool bSubtractiveAsset = Asset->bSubtractiveAsset; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + VOXEL_TOOL_HELPER_BODY(Write, UpdateRender, + ImportDataAssetImpl( + Data, + Bounds, + Position, + *AssetData, + bSubtractiveAsset, + MergeMode, + EVoxelMaterialMask::All)); +} + +void UVoxelAssetTools::ImportDataAssetFastAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelDataAsset* Asset, + FVector InPosition, + EVoxelAssetMergeMode MergeMode, + bool bConvertToVoxelSpace, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (!Asset) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid asset")); + return; + } + + FVoxelVector Position = FVoxelToolHelpers::GetRealPosition(World, InPosition, bConvertToVoxelSpace); + Position += Asset->PositionOffset; + const auto AssetData = Asset->GetData(); + const FVoxelIntBox Bounds(Position, Position + AssetData->GetSize()); + const bool bSubtractiveAsset = Asset->bSubtractiveAsset; + + CHECK_BOUNDS_ARE_VALID_VOID(); + CHECK_BOUNDS_ARE_32BITS_VOID(); + + VOXEL_TOOL_LATENT_HELPER_BODY(Write, UpdateRender, + ImportDataAssetImpl( + Data, + Bounds, + Position, + *AssetData, + bSubtractiveAsset, + MergeMode, + EVoxelMaterialMask::All)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::InvertDataAssetImpl(const FVoxelDataAssetData& AssetData, FVoxelDataAssetData& InvertedAssetData) +{ + VOXEL_TOOL_FUNCTION_COUNTER(AssetData.GetSize().X * AssetData.GetSize().Y * AssetData.GetSize().Z); + + InvertedAssetData.SetSize(AssetData.GetSize(), AssetData.HasMaterials()); + const int32 Num = AssetData.GetRawValues().Num(); +#if VOXEL_DEBUG + const auto& Src = AssetData.GetRawValues(); + auto& Dst = InvertedAssetData.GetRawValues(); +#else + const auto* RESTRICT Src = AssetData.GetRawValues().GetData(); + auto* RESTRICT Dst = InvertedAssetData.GetRawValues().GetData(); +#endif + for (int32 Index = 0; Index < Num; Index++) + { + Dst[Index] = Src[Index].GetInverse(); + } + + InvertedAssetData.GetRawMaterials() = AssetData.GetRawMaterials(); +} + +void UVoxelAssetTools::InvertDataAsset(UVoxelDataAsset* Asset, UVoxelDataAsset*& InvertedAsset) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Asset) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid asset")); + return; + } + + const auto InvertedAssetData = MakeVoxelShared(); + InvertDataAssetImpl(*Asset->GetData(), *InvertedAssetData); + + InvertedAsset = NewObject(GetTransientPackage()); + InvertedAsset->SetData(InvertedAssetData); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::SetDataAssetMaterialImpl( + const FVoxelDataAssetData& AssetData, + FVoxelDataAssetData& NewAssetData, + FVoxelMaterial Material) +{ + VOXEL_TOOL_FUNCTION_COUNTER(AssetData.GetSize().X * AssetData.GetSize().Y * AssetData.GetSize().Z); + + NewAssetData.SetSize(AssetData.GetSize(), true); + NewAssetData.GetRawValues() = AssetData.GetRawValues(); + const int32 Num = AssetData.GetRawValues().Num(); +#if VOXEL_DEBUG + auto& Ptr = NewAssetData.GetRawMaterials(); +#else + auto* RESTRICT Ptr = NewAssetData.GetRawMaterials().GetData(); +#endif + for (int32 Index = 0; Index < Num; Index++) + { + Ptr[Index] = Material; + } +} + +void UVoxelAssetTools::SetDataAssetMaterial(UVoxelDataAsset* Asset, UVoxelDataAsset*& NewAsset, FVoxelMaterial Material) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Asset) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid asset")); + return; + } + + const auto NewAssetData = MakeVoxelShared(); + SetDataAssetMaterialImpl(*Asset->GetData(), *NewAssetData, Material); + + NewAsset = NewObject(GetTransientPackage()); + NewAsset->SetData(NewAssetData); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::CreateDataAssetFromWorldSectionImpl( + const FVoxelData& Data, + const FVoxelIntBox& Bounds, + const bool bCopyMaterials, + FVoxelDataAssetData& AssetData) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + AssetData.SetSize(Bounds.Size(), bCopyMaterials); + + { + TVoxelQueryZone QueryZone(Bounds, AssetData.GetRawValues()); + Data.Get(QueryZone, 0); + } + if (bCopyMaterials) + { + TVoxelQueryZone QueryZone(Bounds, AssetData.GetRawMaterials()); + Data.Get(QueryZone, 0); + } +} + +UVoxelDataAsset* UVoxelAssetTools::CreateDataAssetFromWorldSection( + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bCopyMaterials) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + CHECK_BOUNDS_ARE_VALID(); + + const auto AssetData = MakeVoxelShared(); + + VOXEL_TOOL_HELPER_BODY(Read, DoNotUpdateRender, CreateDataAssetFromWorldSectionImpl(Data, Bounds, bCopyMaterials, *AssetData)); + + auto* Asset = NewObject(GetTransientPackage()); + Asset->SetData(AssetData); + return Asset; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelAssetTools::AddDisableEditsBox( + FVoxelDisableEditsBoxItemReference& Reference, + AVoxelWorld* World, + FVoxelIntBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + VOXEL_TOOL_HELPER_BODY(Write, DoNotUpdateRender, + { + Reference.Bounds = Bounds; + Reference.Item = Data.AddItem(Bounds); + }); +} + +void UVoxelAssetTools::AddDisableEditsBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelDisableEditsBoxItemReference& Reference, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE_BODY(Reference, Write, DoNotUpdateRender, + { + InReference.Bounds = Bounds; + InReference.Item = Data.AddItem(Bounds); + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp new file mode 100644 index 00000000..d3e335f4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp @@ -0,0 +1,1285 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelIntBox.h" +#include "VoxelWorld.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelDataUtilities.h" +#include "VoxelData/VoxelDataUtilities.inl" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelRender/VoxelChunkMesh.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelSpawners/IVoxelSpawnerManager.h" +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h" +#include "VoxelEvents/VoxelEventManager.h" +#include "VoxelAssets/VoxelDataAssetData.h" +#include "VoxelAssets/VoxelHeightmapAssetData.h" +#include "IVoxelPool.h" +#include "VoxelDefaultPool.h" +#include "VoxelMessages.h" +#include "VoxelUtilities/VoxelGeneratorUtilities.h" + +#include "Async/Async.h" +#include "Engine/StaticMesh.h" +#include "EngineUtils.h" + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelBlueprintLibrary::IsVoxelPluginPro() +{ + return true; +} + +void UVoxelBlueprintLibrary::RaiseInfo(FString Message, UObject* Object) +{ + FVoxelMessages::Info(Message, Object); +} + +void UVoxelBlueprintLibrary::RaiseWarning(FString Message, UObject* Object) +{ + FVoxelMessages::Warning(Message, Object); +} + +void UVoxelBlueprintLibrary::RaiseError(FString Message, UObject* Object) +{ + FVoxelMessages::Error(Message, Object); +} + +int32 UVoxelBlueprintLibrary::NumberOfCores() +{ + return FPlatformMisc::NumberOfCores(); +} + +float UVoxelBlueprintLibrary::GetMemoryUsageInMB(EVoxelMemoryUsageType Type) +{ +#if ENABLE_VOXEL_MEMORY_STATS +#define CASE(X) return X.GetValue() / double(1 << 20); + switch (Type) + { + case EVoxelMemoryUsageType::Total: + CASE(STAT_TotalVoxelMemory_MemoryUsage); + case EVoxelMemoryUsageType::VoxelsDirtyValuesData: + CASE(STAT_VoxelDataOctreeDirtyValuesMemory_MemoryUsage); + case EVoxelMemoryUsageType::VoxelsDirtyMaterialsData: + CASE(STAT_VoxelDataOctreeDirtyMaterialsMemory_MemoryUsage); + case EVoxelMemoryUsageType::VoxelsCachedValuesData: + CASE(STAT_VoxelDataOctreeCachedValuesMemory_MemoryUsage); + case EVoxelMemoryUsageType::VoxelsCachedMaterialsData: + CASE(STAT_VoxelDataOctreeCachedMaterialsMemory_MemoryUsage); + case EVoxelMemoryUsageType::UndoRedo: + CASE(STAT_VoxelUndoRedoMemory_MemoryUsage); + case EVoxelMemoryUsageType::Multiplayer: + CASE(STAT_VoxelMultiplayerMemory_MemoryUsage); + case EVoxelMemoryUsageType::IntermediateBuffers: + CASE(STAT_VoxelChunkMeshMemory_MemoryUsage); + case EVoxelMemoryUsageType::MeshesIndices: + CASE(STAT_VoxelProcMeshMemory_Indices_MemoryUsage); + case EVoxelMemoryUsageType::MeshesTessellationIndices: + CASE(STAT_VoxelProcMeshMemory_Adjacency_MemoryUsage); + case EVoxelMemoryUsageType::MeshesVertices: + CASE(STAT_VoxelProcMeshMemory_Positions_MemoryUsage); + case EVoxelMemoryUsageType::MeshesColors: + CASE(STAT_VoxelProcMeshMemory_Colors_MemoryUsage); + case EVoxelMemoryUsageType::MeshesUVsAndTangents: + CASE(STAT_VoxelProcMeshMemory_UVs_Tangents_MemoryUsage); + case EVoxelMemoryUsageType::DataAssets: + CASE(STAT_VoxelDataAssetMemory_MemoryUsage); + case EVoxelMemoryUsageType::HeightmapAssets: + CASE(STAT_VoxelHeightmapAssetMemory_MemoryUsage); + case EVoxelMemoryUsageType::UncompressedSaves: + CASE(STAT_VoxelUncompressedSavesMemory_MemoryUsage); + case EVoxelMemoryUsageType::CompressedSaves: + CASE(STAT_VoxelCompressedSavesMemory_MemoryUsage); + default: + ensure(false); + return 0.f; + } +#undef CASE +#else + FVoxelMessages::Error(FUNCTION_ERROR("Requires ENABLE_VOXEL_MEMORY_STATS=1")); + return 0.f; +#endif +} + +float UVoxelBlueprintLibrary::GetPeakMemoryUsageInMB(EVoxelMemoryUsageType Type) +{ +#if ENABLE_VOXEL_MEMORY_STATS +#define CASE(X) return X.GetValue() / double(1 << 20); + switch (Type) + { + case EVoxelMemoryUsageType::Total: + CASE(STAT_TotalVoxelMemory_MemoryPeak); + case EVoxelMemoryUsageType::VoxelsDirtyValuesData: + CASE(STAT_VoxelDataOctreeDirtyValuesMemory_MemoryPeak); + case EVoxelMemoryUsageType::VoxelsDirtyMaterialsData: + CASE(STAT_VoxelDataOctreeDirtyMaterialsMemory_MemoryPeak); + case EVoxelMemoryUsageType::VoxelsCachedValuesData: + CASE(STAT_VoxelDataOctreeCachedValuesMemory_MemoryPeak); + case EVoxelMemoryUsageType::VoxelsCachedMaterialsData: + CASE(STAT_VoxelDataOctreeCachedMaterialsMemory_MemoryPeak); + case EVoxelMemoryUsageType::UndoRedo: + CASE(STAT_VoxelUndoRedoMemory_MemoryPeak); + case EVoxelMemoryUsageType::Multiplayer: + CASE(STAT_VoxelMultiplayerMemory_MemoryPeak); + case EVoxelMemoryUsageType::IntermediateBuffers: + CASE(STAT_VoxelChunkMeshMemory_MemoryPeak); + case EVoxelMemoryUsageType::MeshesIndices: + CASE(STAT_VoxelProcMeshMemory_Indices_MemoryPeak); + case EVoxelMemoryUsageType::MeshesTessellationIndices: + CASE(STAT_VoxelProcMeshMemory_Adjacency_MemoryPeak); + case EVoxelMemoryUsageType::MeshesVertices: + CASE(STAT_VoxelProcMeshMemory_Positions_MemoryPeak); + case EVoxelMemoryUsageType::MeshesColors: + CASE(STAT_VoxelProcMeshMemory_Colors_MemoryPeak); + case EVoxelMemoryUsageType::MeshesUVsAndTangents: + CASE(STAT_VoxelProcMeshMemory_UVs_Tangents_MemoryPeak); + case EVoxelMemoryUsageType::DataAssets: + CASE(STAT_VoxelDataAssetMemory_MemoryPeak); + case EVoxelMemoryUsageType::HeightmapAssets: + CASE(STAT_VoxelHeightmapAssetMemory_MemoryPeak); + case EVoxelMemoryUsageType::UncompressedSaves: + CASE(STAT_VoxelUncompressedSavesMemory_MemoryPeak); + case EVoxelMemoryUsageType::CompressedSaves: + CASE(STAT_VoxelCompressedSavesMemory_MemoryPeak); + default: + ensure(false); + return 0.f; + } +#undef CASE +#else + FVoxelMessages::Error(FUNCTION_ERROR("Requires ENABLE_VOXEL_MEMORY_STATS=1")); + return 0.f; +#endif +} + +void UVoxelBlueprintLibrary::LogMemoryStats() +{ +#if ENABLE_VOXEL_MEMORY_STATS + struct FNameAndUsage + { + const TCHAR* Name = nullptr; + int64 Usage = 0; + }; + TArray Usages; + TArray Peaks; + for (auto& It : GetVoxelMemoryCounters()) + { + Usages.Add(FNameAndUsage{ It.Key, It.Value.UsageCounterPtr->GetValue() }); + Peaks.Add(FNameAndUsage{ It.Key, It.Value.PeakCounterPtr->GetValue() }); + } + + Usages.Sort([](FNameAndUsage A, FNameAndUsage B) { return A.Usage > B.Usage; }); + Peaks.Sort([](FNameAndUsage A, FNameAndUsage B) { return A.Usage > B.Usage; }); + + LOG_VOXEL(Log, TEXT("--------------------------------------")); + LOG_VOXEL(Log, TEXT("Voxel Memory Usage:")); + for (auto& Usage : Usages) + { + LOG_VOXEL(Log, TEXT("%50s: %6fMB"), Usage.Name, Usage.Usage / double(1 << 20)); + } + LOG_VOXEL(Log, TEXT("--------------------------------------")); + + LOG_VOXEL(Log, TEXT("--------------------------------------")); + LOG_VOXEL(Log, TEXT("Voxel Memory Peaks:")); + for (auto& Usage : Peaks) + { + LOG_VOXEL(Log, TEXT("%50s: %6fMB"), Usage.Name, Usage.Usage / double(1 << 20)); + } + LOG_VOXEL(Log, TEXT("--------------------------------------")); +#else + FVoxelMessages::Error(FUNCTION_ERROR("Requires ENABLE_VOXEL_MEMORY_STATS=1")); + return 0.f; +#endif +} + +float UVoxelBlueprintLibrary::GetEstimatedCollisionsMemoryUsageInMB(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + uint64 MemoryUsage = 0; + for (auto* Component : World->GetComponents()) + { + auto* ProcMeshComponent = Cast(Component); + if (!ProcMeshComponent) continue; + ProcMeshComponent->IterateSections([&](auto& Settings, const FVoxelProcMeshBuffers& Buffers) + { + MemoryUsage += Buffers.IndexBuffer.GetAllocatedSize(); + MemoryUsage += Buffers.VertexBuffers.PositionVertexBuffer.GetNumVertices() * Buffers.VertexBuffers.PositionVertexBuffer.GetStride(); + }); + } + return MemoryUsage / double(1 << 20); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox UVoxelBlueprintLibrary::TransformGlobalBoxToVoxelBox(AVoxelWorld* World, FBox Box) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + FVoxelIntBoxWithValidity Result; + + const auto Add = [&](const FVector& Position) + { + const FVoxelVector LocalPosition = World->GlobalToLocalFloat(Position); + Result += FVoxelUtilities::FloorToInt(LocalPosition); + Result += FVoxelUtilities::CeilToInt(LocalPosition); + }; + + Add({ Box.Min.X, Box.Min.Y, Box.Min.Z }); + Add({ Box.Max.X, Box.Min.Y, Box.Min.Z }); + Add({ Box.Min.X, Box.Max.Y, Box.Min.Z }); + Add({ Box.Max.X, Box.Max.Y, Box.Min.Z }); + Add({ Box.Min.X, Box.Min.Y, Box.Max.Z }); + Add({ Box.Max.X, Box.Min.Y, Box.Max.Z }); + Add({ Box.Min.X, Box.Max.Y, Box.Max.Z }); + Add({ Box.Max.X, Box.Max.Y, Box.Max.Z }); + + return Result.GetBox(); +} + +FBox UVoxelBlueprintLibrary::TransformVoxelBoxToGlobalBox(AVoxelWorld* World, FVoxelIntBox Box) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + FBox Result(ForceInit); + + for (const auto& Corner : Box.GetCorners(1)) + { + Result += World->LocalToGlobal(Corner); + } + + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +AVoxelWorld* UVoxelBlueprintLibrary::GetVoxelWorldContainingPosition(UObject* WorldContextObject, FVector Position) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!WorldContextObject) + { + FVoxelMessages::Error(FUNCTION_ERROR("No world context!")); + return {}; + } + + const auto VoxelWorlds = GetAllVoxelWorldsContainingPosition(WorldContextObject, Position); + if (VoxelWorlds.Num() == 0) + { + return nullptr; + } + + if (VoxelWorlds.Num() > 1) + { + FVoxelMessages::Warning(FUNCTION_ERROR("More than one voxel world is containing position! Consider using GetAllVoxelWorldsContainingPosition instead")); + } + + return VoxelWorlds[0]; +} + +TArray UVoxelBlueprintLibrary::GetAllVoxelWorldsContainingPosition(UObject* WorldContextObject, FVector Position) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!WorldContextObject) + { + FVoxelMessages::Error(FUNCTION_ERROR("No world context!")); + return {}; + } + + TArray Result; + for (auto* VoxelWorld : TActorRange(WorldContextObject->GetWorld())) + { + if (VoxelWorld->IsCreated()) + { + const FVoxelIntBox WorldBounds = VoxelWorld->GetWorldBounds(); + const FVoxelVector LocalPosition = VoxelWorld->GlobalToLocalFloat(Position); + if (WorldBounds.ContainsFloat(LocalPosition)) + { + Result.Add(VoxelWorld); + } + } + } + return Result; +} + +AVoxelWorld* UVoxelBlueprintLibrary::GetVoxelWorldOverlappingBox(UObject* WorldContextObject, FBox Box) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!WorldContextObject) + { + FVoxelMessages::Error(FUNCTION_ERROR("No world context!")); + return {}; + } + + const auto VoxelWorlds = GetAllVoxelWorldsOverlappingBox(WorldContextObject, Box); + if (VoxelWorlds.Num() == 0) + { + return nullptr; + } + + if (VoxelWorlds.Num() > 1) + { + FVoxelMessages::Warning(FUNCTION_ERROR("More than one voxel world is overlapping box! Consider using GetAllVoxelWorldsOverlappingBox instead")); + } + + return VoxelWorlds[0]; +} + +TArray UVoxelBlueprintLibrary::GetAllVoxelWorldsOverlappingBox(UObject* WorldContextObject, FBox Box) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!WorldContextObject) + { + FVoxelMessages::Error(FUNCTION_ERROR("No world context!")); + return {}; + } + + TArray Result; + for (auto* VoxelWorld : TActorRange(WorldContextObject->GetWorld())) + { + if (VoxelWorld->IsCreated()) + { + const FVoxelIntBox WorldBounds = VoxelWorld->GetWorldBounds(); + const FVoxelIntBox LocalBox = TransformGlobalBoxToVoxelBox(VoxelWorld, Box); + if (WorldBounds.Intersect(LocalBox)) + { + Result.Add(VoxelWorld); + } + } + } + return Result; +} + +AVoxelWorld* UVoxelBlueprintLibrary::GetVoxelWorldOverlappingActor(AActor* Actor) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Actor) + { + FVoxelMessages::Error(FUNCTION_ERROR("No Actor!")); + return {}; + } + + const auto VoxelWorlds = GetAllVoxelWorldsOverlappingActor(Actor); + if (VoxelWorlds.Num() == 0) + { + return nullptr; + } + + if (VoxelWorlds.Num() > 1) + { + FVoxelMessages::Warning(FUNCTION_ERROR("More than one voxel world is overlapping actor! Consider using GetAllVoxelWorldsOverlappingActor instead")); + } + + return VoxelWorlds[0]; +} + +TArray UVoxelBlueprintLibrary::GetAllVoxelWorldsOverlappingActor(AActor* Actor) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!Actor) + { + FVoxelMessages::Error(FUNCTION_ERROR("No Actor!")); + return {}; + } + + return GetAllVoxelWorldsOverlappingBox(Actor, Actor->GetComponentsBoundingBox(true)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::SpawnVoxelSpawnerActorsInArea( + TArray& OutActors, + AVoxelWorld* World, + FVoxelIntBox Bounds, + EVoxelSpawnerActorSpawnType SpawnType) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + OutActors.Reset(); + + World->GetInstancedMeshManager().SpawnActorsInArea(Bounds, World->GetData(), SpawnType, OutActors); +} + +AVoxelSpawnerActor* UVoxelBlueprintLibrary::SpawnVoxelSpawnerActorByInstanceIndex( + AVoxelWorld* World, + UVoxelHierarchicalInstancedStaticMeshComponent* Component, + int32 InstanceIndex) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + if (!Component) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Component")); + return nullptr; + } + if (Component->GetOwner() != World) + { + FVoxelMessages::Error(FUNCTION_ERROR("Component is not a component of World")); + return nullptr; + } + + return World->GetInstancedMeshManager().SpawnActorByIndex(Component, InstanceIndex); +} + +void UVoxelBlueprintLibrary::AddInstances( + AVoxelWorld* const World, + UStaticMesh* const Mesh, + const TArray& Transforms, + const TArray& Colors, + FVoxelInstancedMeshSettings InstanceSettings, + FVoxelSpawnerActorSettings ActorSettings, + const FVector FloatingDetectionOffset) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (!Mesh) + { + FVoxelMessages::Error("AddInstances: Mesh is null!"); + return; + } + + if (Transforms.Num() == 0) + { + return; + } + + FVoxelIntBoxWithValidity BoundsWithValidity; + for (auto& Transform : Transforms) + { + BoundsWithValidity += World->GlobalToLocal(Transform.GetTranslation()); + } + const FVoxelIntBox Bounds = BoundsWithValidity.GetBox(); + + FVoxelSpawnerTransforms VoxelSpawnerTransforms; + VoxelSpawnerTransforms.TransformsOffset = World->GetInstancedMeshManager().ComputeTransformsOffset(Bounds); + VoxelSpawnerTransforms.Matrices.Reserve(Transforms.Num()); + + for (int32 Index = 0; Index < Transforms.Num(); Index++) + { + FTransform Transform = Transforms[Index]; + Transform.AddToTranslation(-World->VoxelSize * FVector(VoxelSpawnerTransforms.TransformsOffset + World->GetWorldOffset())); + + FVoxelSpawnerMatrix Matrix(Transform.ToMatrixWithScale()); + if (Colors.IsValidIndex(Index)) + { + const FVoxelMaterial Material = FVoxelMaterial::CreateFromColor(Colors[Index]); + Matrix.SetRandomInstanceId(Material.GetPackedColor()); + } + Matrix.SetPositionOffset(-FloatingDetectionOffset); + VoxelSpawnerTransforms.Matrices.Add(Matrix); + } + + FVoxelInstancedMeshAndActorSettings MeshAndActorSettings; + MeshAndActorSettings.Mesh = Mesh; + MeshAndActorSettings.MeshSettings = InstanceSettings; + MeshAndActorSettings.ActorSettings = ActorSettings; + + World->GetInstancedMeshManager().AddInstances(MeshAndActorSettings, VoxelSpawnerTransforms, Bounds); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::RegenerateSpawners(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + World->GetSpawnerManager().Regenerate(Bounds); +} + +void UVoxelBlueprintLibrary::MarkSpawnersDirty(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + + World->GetSpawnerManager().MarkDirty(Bounds); +} + +FVoxelSpawnersSave UVoxelBlueprintLibrary::GetSpawnersSave(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + FVoxelSpawnersSave Save; + World->GetSpawnerManager().SaveTo(Save.NewMutable()); + return Save; +} + +void UVoxelBlueprintLibrary::LoadFromSpawnersSave(AVoxelWorld* World, const FVoxelSpawnersSave& Save) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + World->GetSpawnerManager().LoadFrom(Save.Const()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelBlueprintLibrary::Undo(AVoxelWorld* World, TArray& OutUpdatedBounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + if (!Data.bEnableUndoRedo) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableUndoRedo is false!")); + return false; + } + if (!Data.IsCurrentFrameEmpty()) + { + FVoxelMessages::Error("Undo: Undo called but edits have been made since last SaveFrame. Please call SaveFrame after every edits"); + return false; + } + + OutUpdatedBounds.Reset(); + if (!Data.Undo(OutUpdatedBounds)) + { + return false; + } + + World->GetLODManager().UpdateBounds(OutUpdatedBounds); + return true; +} + +bool UVoxelBlueprintLibrary::Undo(AVoxelWorld* World) +{ + TArray Dummy; + return Undo(World, Dummy); +} + +bool UVoxelBlueprintLibrary::Redo(AVoxelWorld* World, TArray& OutUpdatedBounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + if (!Data.bEnableUndoRedo) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableUndoRedo is false!")); + return false; + } + if (!Data.IsCurrentFrameEmpty()) + { + FVoxelMessages::Error("Redo: Redo called but edits have been made since last SaveFrame. Please call SaveFrame after every edits"); + return false; + } + + OutUpdatedBounds.Reset(); + if (!Data.Redo(OutUpdatedBounds)) + { + return false; + } + + World->GetLODManager().UpdateBounds(OutUpdatedBounds); + return true; +} + +bool UVoxelBlueprintLibrary::Redo(AVoxelWorld* World) +{ + TArray Dummy; + return Redo(World, Dummy); +} + +void UVoxelBlueprintLibrary::SaveFrame(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto& Data = World->GetData(); + + if (!Data.bEnableUndoRedo) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableUndoRedo is false!")); + return; + } + + Data.SaveFrame(FVoxelIntBox::Infinite); +} + +void UVoxelBlueprintLibrary::ClearFrames(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto& Data = World->GetData(); + + if (!Data.bEnableUndoRedo) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableUndoRedo is false!")); + return; + } + Data.ClearFrames(); +} + +int32 UVoxelBlueprintLibrary::GetHistoryPosition(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + if (!Data.bEnableUndoRedo) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableUndoRedo is false!")); + return 0; + } + return Data.GetHistoryPosition(); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVector UVoxelBlueprintLibrary::GetNormal(AVoxelWorld* World, FIntVector Position) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + const auto& Data = World->GetData(); + FVoxelReadScopeLock Lock(Data, FVoxelIntBox(Position - FIntVector(1), Position + FIntVector(2)), "GetNormal"); + return FVoxelDataUtilities::GetGradientFromGetValue(FVoxelDataUtilities::MakeFloatData(Data), Position.X, Position.Y, Position.Z, 0); +} + +float UVoxelBlueprintLibrary::GetFloatOutput(AVoxelWorld* World, FName Name, float X, float Y, float Z, float DefaultValue) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + if (!Data.Generator->GetOutputsPtrMap().Contains(Name)) + { + FVoxelMessages::Error(FUNCTION_ERROR(FVoxelUtilities::GetMissingGeneratorOutputErrorString(Name, *Data.Generator))); + return 0; + } + + return Data.GetCustomOutput(DefaultValue, Name, X, Y, Z, 0); +} + +int32 UVoxelBlueprintLibrary::GetIntOutput(AVoxelWorld* World, FName Name, float X, float Y, float Z, int32 DefaultValue) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + if (!Data.Generator->GetOutputsPtrMap().Contains(Name)) + { + FVoxelMessages::Error(FUNCTION_ERROR(FVoxelUtilities::GetMissingGeneratorOutputErrorString(Name, *Data.Generator))); + return 0; + } + + return Data.GetCustomOutput(DefaultValue, Name, X, Y, Z, 0); +} + +FVoxelIntBox UVoxelBlueprintLibrary::GetBounds(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + return World->GetData().WorldBounds; +} + +void UVoxelBlueprintLibrary::ClearAllData(AVoxelWorld* World, bool bUpdateRender) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + World->GetData().ClearData(); + + if (bUpdateRender) + { + World->GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); + } +} + +void UVoxelBlueprintLibrary::ClearValueData(AVoxelWorld* World, bool bUpdateRender) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto& Data = World->GetData(); + + { + FVoxelWriteScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelDataUtilities::ClearData(Data); + } + + if (bUpdateRender) + { + World->GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); + } +} + +void UVoxelBlueprintLibrary::ClearMaterialData(AVoxelWorld* World, bool bUpdateRender) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto& Data = World->GetData(); + + { + FVoxelWriteScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelDataUtilities::ClearData(Data); + } + + if (bUpdateRender) + { + World->GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); + } +} + +bool UVoxelBlueprintLibrary::HasValueData(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + FVoxelReadScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + return FVoxelDataUtilities::HasData(World->GetData()); +} + +bool UVoxelBlueprintLibrary::HasMaterialData(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + FVoxelReadScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + return FVoxelDataUtilities::HasData(Data); +} + +void UVoxelBlueprintLibrary::ClearDirtyData(AVoxelWorld* World, bool bUpdateRender) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + auto& Data = World->GetData(); + + TArray OutBoundsToUpdate; + + { + FVoxelWriteScopeLock Lock(Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); + Data.ClearOctreeData(OutBoundsToUpdate); + } + + if (bUpdateRender) + { + World->GetLODManager().UpdateBounds(OutBoundsToUpdate); + } +} + +void UVoxelBlueprintLibrary::ScaleData(AVoxelWorld* World, const FVector& Scale) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + auto& SourceData = World->GetData(); + const auto DestData = SourceData.Clone(); + + { + FVoxelReadScopeLock LockA(SourceData, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelWriteScopeLock LockB(*DestData, FVoxelIntBox::Infinite, FUNCTION_FNAME); + FVoxelDataUtilities::ScaleWorldData(SourceData, *DestData, Scale); + } + + World->DestroyWorld(); + + FVoxelWorldCreateInfo Info; + Info.bOverrideData = true; + Info.DataOverride_Raw = DestData; + World->CreateWorld(Info); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::UpdatePosition(AVoxelWorld* World, FIntVector Position) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + World->GetLODManager().UpdateBounds(FVoxelIntBox(Position)); +} + +void UVoxelBlueprintLibrary::UpdateBounds(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_BOUNDS_ARE_VALID_VOID(); + World->GetLODManager().UpdateBounds(Bounds); +} + +void UVoxelBlueprintLibrary::UpdateAll(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + World->GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); +} + +void UVoxelBlueprintLibrary::ApplyLODSettings(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + World->UpdateDynamicLODSettings(); + World->GetLODManager().ForceLODsUpdate(); +} + +bool UVoxelBlueprintLibrary::AreCollisionsEnabled(AVoxelWorld* World, FVector InPosition, int32& LOD, bool bConvertToVoxelSpace /*= true*/) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + LOD = 0; + const FVoxelVector Position = FVoxelToolHelpers::GetRealPosition(World, InPosition, bConvertToVoxelSpace); + + auto& LODManager = World->GetLODManager(); + if (!LODManager.Settings.WorldBounds.ContainsFloat(Position)) + { + return false; + } + + uint8 OutLOD; + const bool bResult = LODManager.AreCollisionsEnabled(FVoxelUtilities::RoundToInt(Position), OutLOD); + LOD = OutLOD; + + return bResult; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelBlueprintLibrary::GetTaskCount(AVoxelWorld* World) +{ + return World && World->IsCreated() ? FMath::Max(World->GetRenderer().GetTaskCount(), 0) : 0; +} + +bool UVoxelBlueprintLibrary::IsVoxelWorldMeshLoading(AVoxelWorld* World) +{ + return World && World->IsCreated() && World->GetRenderer().GetTaskCount() > 0; +} + +bool UVoxelBlueprintLibrary::IsVoxelWorldFoliageLoading(AVoxelWorld* World) +{ + return World && World->IsCreated() && World->GetSpawnerManager().GetTaskCount() > 0; +} + +void UVoxelBlueprintLibrary::ApplyNewMaterials(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + World->UpdateDynamicRendererSettings(); + World->GetRenderer().ApplyNewMaterials(); +} + +void UVoxelBlueprintLibrary::RecreateRender(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + World->RecreateRender(); +} + +void UVoxelBlueprintLibrary::RecreateSpawners(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + World->RecreateSpawners(); +} + +void UVoxelBlueprintLibrary::Recreate(AVoxelWorld* World, bool bSaveData) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + FVoxelScopedFastSaveLoad FastSaveScope; + + const bool bDataIsDirty = World->GetData().IsDirty(); + + FVoxelWorldCreateInfo Info; + if (bSaveData) + { + Info.bOverrideSave = true; + UVoxelDataTools::GetSave(World, Info.SaveOverride); + + // Clear dirty flag to avoid popup + World->GetData().ClearDirtyFlag(); + } + + World->RecreateAll(Info); + + if (bSaveData && bDataIsDirty) + { + // Set back dirty flag + World->GetData().MarkAsDirty(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::BindVoxelChunkEvents( + AVoxelWorld* World, + FChunkDynamicDelegate OnActivate, + FChunkDynamicDelegate OnDeactivate, + bool bFireExistingOnes, + int32 ChunkSize, + int32 ActivationDistanceInChunks) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto OnActivateLambda = [OnActivate](const FVoxelIntBox& Bounds) + { + OnActivate.ExecuteIfBound(Bounds); + }; + auto OnDeactivateLambda = [OnDeactivate](const FVoxelIntBox& Bounds) + { + OnDeactivate.ExecuteIfBound(Bounds); + }; + + World->GetEventManager().BindEvent( + bFireExistingOnes, + FMath::Max(1, ChunkSize), + FMath::Max(0, ActivationDistanceInChunks), + FChunkDelegate::CreateLambda(OnActivateLambda), + FChunkDelegate::CreateLambda(OnDeactivateLambda)); +} + +void UVoxelBlueprintLibrary::BindVoxelGenerationEvent( + AVoxelWorld* World, + FChunkDynamicDelegate OnGenerate, + bool bFireExistingOnes, + int32 ChunkSize, + int32 GenerationDistanceInChunks) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto OnGenerateLambda = [OnGenerate](const FVoxelIntBox& Bounds) + { + OnGenerate.ExecuteIfBound(Bounds); + }; + + World->GetEventManager().BindGenerationEvent( + bFireExistingOnes, + FMath::Max(1, ChunkSize), + FMath::Max(0, GenerationDistanceInChunks), + FChunkDelegate::CreateLambda(OnGenerateLambda)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelBlueprintLibrary::IsValidRef(AVoxelWorld* World, FVoxelToolRenderingRef Ref) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + return Ref.Id.IsValid() && World->GetToolRenderingManager().IsValidTool(Ref.Id); +} + +FVoxelToolRenderingRef UVoxelBlueprintLibrary::CreateToolRendering(AVoxelWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + return { World->GetToolRenderingManager().CreateTool() }; +} + +#define CHECK_TOOL_RENDERING_REF() \ + if (!Ref.Id.IsValid()) { FVoxelMessages::Error(FUNCTION_ERROR("Unitilialized tool rendering reference")); return; } \ + if (!World->GetToolRenderingManager().IsValidTool(Ref.Id)) { FVoxelMessages::Error(FUNCTION_ERROR("Outdated tool rendering reference")); return; } + +void UVoxelBlueprintLibrary::DestroyToolRendering(AVoxelWorld* World, FVoxelToolRenderingRef Ref) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_TOOL_RENDERING_REF(); + + World->GetToolRenderingManager().RemoveTool(Ref.Id); +} + +void UVoxelBlueprintLibrary::SetToolRenderingMaterial(AVoxelWorld* World, FVoxelToolRenderingRef Ref, UMaterialInterface* Material) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_TOOL_RENDERING_REF(); + + World->GetToolRenderingManager().EditTool(Ref.Id, [&](auto& Tool) { Tool.Material = FVoxelMaterialInterfaceManager::Get().CreateMaterial(Material); }); +} + +void UVoxelBlueprintLibrary::SetToolRenderingBounds(AVoxelWorld* World, FVoxelToolRenderingRef Ref, FBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_TOOL_RENDERING_REF(); + + World->GetToolRenderingManager().EditTool(Ref.Id, [&](auto& Tool) { Tool.WorldBounds = Bounds; }); +} + +void UVoxelBlueprintLibrary::SetToolRenderingEnabled(AVoxelWorld* World, FVoxelToolRenderingRef Ref, bool bEnabled) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + CHECK_TOOL_RENDERING_REF(); + + World->GetToolRenderingManager().EditTool(Ref.Id, [&](auto& Tool) { Tool.bEnabled = bEnabled; }); +} + +#undef CHECK_TOOL_RENDERING_REF + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::CreateGlobalVoxelThreadPool( + const TMap& PriorityCategoriesOverrides, + const TMap& PriorityOffsetsOverrides, + int32 NumberOfThreads, + bool bConstantPriorities) +{ + VOXEL_FUNCTION_COUNTER(); + + if (IsGlobalVoxelPoolCreated()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Global pool already created")); + return; + } + + const auto Pool = FVoxelDefaultPool::Create( + FMath::Max(1, NumberOfThreads), + bConstantPriorities, + PriorityCategoriesOverrides, + PriorityOffsetsOverrides); + IVoxelPool::SetGlobalPool(Pool, __FUNCTION__); +} + +void UVoxelBlueprintLibrary::DestroyGlobalVoxelThreadPool() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!IsGlobalVoxelPoolCreated()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Global pool not created")); + return; + } + IVoxelPool::DestroyGlobalPool(); +} + +bool UVoxelBlueprintLibrary::IsGlobalVoxelPoolCreated() +{ + return IVoxelPool::GetGlobalPool().IsValid(); +} + +void UVoxelBlueprintLibrary::CreateWorldVoxelThreadPool( + UWorld* World, + const TMap& PriorityCategoriesOverrides, + const TMap& PriorityOffsetsOverrides, + int32 NumberOfThreads, + bool bConstantPriorities) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!World) + { + FVoxelMessages::Error(FUNCTION_ERROR("World is NULL")); + return; + } + + if (IsWorldVoxelPoolCreated(World)) + { + FVoxelMessages::Error(FUNCTION_ERROR("Pool already created for this world")); + return; + } + + const auto Pool = FVoxelDefaultPool::Create( + FMath::Max(1, NumberOfThreads), + bConstantPriorities, + PriorityCategoriesOverrides, + PriorityOffsetsOverrides); + IVoxelPool::SetWorldPool(World, Pool, __FUNCTION__); +} + +void UVoxelBlueprintLibrary::DestroyWorldVoxelThreadPool(UWorld* World) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!World) + { + FVoxelMessages::Error(FUNCTION_ERROR("World is NULL")); + return; + } + + if (!IsWorldVoxelPoolCreated(World)) + { + FVoxelMessages::Error(FUNCTION_ERROR("No voxel pool created for this world")); + return; + } + IVoxelPool::DestroyWorldPool(World); +} + +bool UVoxelBlueprintLibrary::IsWorldVoxelPoolCreated(UWorld* World) +{ + return IVoxelPool::GetWorldPool(World).IsValid(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox UVoxelBlueprintLibrary::MakeIntBoxFromGlobalPositionAndRadius(AVoxelWorld* World, FVector GlobalPosition, float Radius) +{ + CHECK_VOXELWORLD_IS_CREATED(); + return FVoxelIntBox::SafeConstruct( + World->GlobalToLocal(GlobalPosition - Radius, EVoxelWorldCoordinatesRounding::RoundDown), + World->GlobalToLocal(GlobalPosition + Radius, EVoxelWorldCoordinatesRounding::RoundUp) + ); +} + +FVoxelIntBox UVoxelBlueprintLibrary::GetRenderBoundsOverlappingDataBounds(AVoxelWorld* World, FVoxelIntBox Bounds, int32 LOD) +{ + CHECK_VOXELWORLD_IS_CREATED(); + CHECK_BOUNDS_ARE_VALID(); + + LOD = FVoxelUtilities::ClampDepth(LOD); + + Bounds = Bounds.MakeMultipleOfBigger(RENDER_CHUNK_SIZE << LOD); + + return Bounds.Extend(2 << LOD); // Account for the normals reads +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelPaintMaterial UVoxelBlueprintLibrary::CreateFiveWayBlendPaintMaterial(FVoxelPaintMaterialFiveWayBlend FiveWayBlend) +{ + if (!(0 <= FiveWayBlend.Channel && FiveWayBlend.Channel < 5)) + { + FVoxelMessages::Error(FUNCTION_ERROR("Channel needs to be between 0 and 4")); + FiveWayBlend.Channel = 0; + } + + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::FiveWayBlend; + PaintMaterial.FiveWayBlend = FiveWayBlend; + return PaintMaterial; +} + +void UVoxelBlueprintLibrary::GetMultiIndex( + FVoxelMaterial Material, + bool bSortByStrength, + float& Strength0, uint8& Index0, + float& Strength1, uint8& Index1, + float& Strength2, uint8& Index2, + float& Strength3, uint8& Index3, + float& Wetness) +{ + const TVoxelStaticArray Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + + Strength0 = Strengths[0]; + Strength1 = Strengths[1]; + Strength2 = Strengths[2]; + Strength3 = Strengths[3]; + + Index0 = Material.GetMultiIndex_Index0(); + Index1 = Material.GetMultiIndex_Index1(); + Index2 = Material.GetMultiIndex_Index2(); + Index3 = Material.GetMultiIndex_Index3(); + + Wetness = Material.GetMultiIndex_Wetness_AsFloat(); + + if (bSortByStrength) + { +#define SWAP(A, B) if (Strength##A < Strength##B) { Swap(Strength##A, Strength##B); Swap(Index##A, Index##B); } + SWAP(0, 1); + SWAP(2, 3); + SWAP(0, 2); + SWAP(1, 3); + SWAP(1, 2); +#undef SWAP + + ensure(Strength0 >= Strength1 && Strength1 >= Strength2 && Strength2 >= Strength3); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::AddNeighborsToSet(const TSet& InSet, TSet& OutSet) +{ + VOXEL_FUNCTION_COUNTER(); + + OutSet.Reset(); + for (auto& P : InSet) + { + OutSet.Add(FIntVector(P.X - 1, P.Y - 1, P.Z - 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y - 1, P.Z - 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y - 1, P.Z - 1)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 0, P.Z - 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 0, P.Z - 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 0, P.Z - 1)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 1, P.Z - 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 1, P.Z - 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 1, P.Z - 1)); + + OutSet.Add(FIntVector(P.X - 1, P.Y - 1, P.Z + 0)); + OutSet.Add(FIntVector(P.X - 0, P.Y - 1, P.Z + 0)); + OutSet.Add(FIntVector(P.X + 1, P.Y - 1, P.Z + 0)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 0, P.Z + 0)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 0, P.Z + 0)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 0, P.Z + 0)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 1, P.Z + 0)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 1, P.Z + 0)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 1, P.Z + 0)); + + OutSet.Add(FIntVector(P.X - 1, P.Y - 1, P.Z + 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y - 1, P.Z + 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y - 1, P.Z + 1)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 0, P.Z + 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 0, P.Z + 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 0, P.Z + 1)); + OutSet.Add(FIntVector(P.X - 1, P.Y + 1, P.Z + 1)); + OutSet.Add(FIntVector(P.X - 0, P.Y + 1, P.Z + 1)); + OutSet.Add(FIntVector(P.X + 1, P.Y + 1, P.Z + 1)); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelDataTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelDataTools.cpp new file mode 100644 index 00000000..4682817b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelDataTools.cpp @@ -0,0 +1,1173 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelData/VoxelSaveUtilities.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h" +#include "VoxelFeedbackContext.h" + +#define VOXEL_DATA_TOOL_PREFIX const FVoxelIntBox Bounds(Position); + +void UVoxelDataTools::GetValue(float& Value, AVoxelWorld* World, FIntVector Position) +{ + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, VOXEL_DATA_TOOL_PREFIX, Value = Data.GetValue(Position, 0).ToFloat()); +} + +void UVoxelDataTools::GetInterpolatedValue(float& Value, AVoxelWorld* World, FVector Position) +{ + const FVoxelIntBox Bounds = FVoxelIntBox(Position).Extend(2); + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, NO_PREFIX, Value = FVoxelDataUtilities::MakeBilinearInterpolatedData(Data).GetValue(Position, 0)); +} + +void UVoxelDataTools::SetValue(AVoxelWorld* World, FIntVector Position, float Value) +{ + VOXEL_TOOL_HELPER(Write, UpdateRender, VOXEL_DATA_TOOL_PREFIX, Data.SetValue(Position, FVoxelValue(Value))); +} + +void UVoxelDataTools::GetMaterial(FVoxelMaterial& Material, AVoxelWorld* World, FIntVector Position) +{ + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, VOXEL_DATA_TOOL_PREFIX, Material = Data.GetMaterial(Position, 0)); +} + +void UVoxelDataTools::SetMaterial(AVoxelWorld* World, FIntVector Position, FVoxelMaterial Material) +{ + VOXEL_TOOL_HELPER(Write, UpdateRender, VOXEL_DATA_TOOL_PREFIX, Data.SetMaterial(Position, Material)); +} + +void UVoxelDataTools::CacheValues(AVoxelWorld* World, FVoxelIntBox Bounds, bool bMultiThreaded) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CacheBounds(Bounds, bMultiThreaded)); +} + +void UVoxelDataTools::CacheMaterials(AVoxelWorld* World, FVoxelIntBox Bounds, bool bMultiThreaded) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CacheBounds(Bounds, bMultiThreaded)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::GetValueAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + float& Value, + AVoxelWorld* World, + FIntVector Position, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Value, Read, DoNotUpdateRender, VOXEL_DATA_TOOL_PREFIX, InValue = Data.GetValue(Position, 0).ToFloat()); +} + +void UVoxelDataTools::SetValueAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FIntVector Position, + float Value, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, UpdateRender, VOXEL_DATA_TOOL_PREFIX, Data.SetValue(Position, FVoxelValue(Value))); +} + +void UVoxelDataTools::GetMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelMaterial& Material, + AVoxelWorld* World, + FIntVector Position, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Material, Read, DoNotUpdateRender, VOXEL_DATA_TOOL_PREFIX, InMaterial = Data.GetMaterial(Position, 0)); +} + +void UVoxelDataTools::SetMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FIntVector Position, + FVoxelMaterial Material, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, UpdateRender, VOXEL_DATA_TOOL_PREFIX, Data.SetMaterial(Position, Material)); +} + +void UVoxelDataTools::CacheValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CacheBounds(Bounds, false)); +} + +void UVoxelDataTools::CacheMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CacheBounds(Bounds, false)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +inline bool CheckSave(const FVoxelData& Data, const T& Save) +{ + if (Save.GetDepth() == -1) + { + FVoxelMessages::Error("LoadFromSave: Invalid save (Depth == -1). You're trying to load a save object that wasn't initialized"); + return false; + } + if (Save.GetDepth() > Data.Depth) + { + FVoxelMessages::Warning("LoadFromSave: Save depth is bigger than world depth, the save data outside world bounds will be ignored"); + } + return true; +} + +#define CHECK_SAVE() \ +if (!CheckSave(World->GetData(), Save)) \ +{ \ + return false; \ +} + +void UVoxelDataTools::GetSave(AVoxelWorld* World, FVoxelUncompressedWorldSave& OutSave) +{ + GetSave(World, OutSave.NewMutable(), OutSave.Objects); +} + +void UVoxelDataTools::GetSave(AVoxelWorld* World, FVoxelUncompressedWorldSaveImpl& OutSave, TArray& OutObjects) +{ + CHECK_VOXELWORLD_IS_CREATED_VOID(); + World->GetData().GetSave(OutSave, OutObjects); +} + +void UVoxelDataTools::GetCompressedSave(AVoxelWorld* World, FVoxelCompressedWorldSave& OutSave) +{ + GetCompressedSave(World, OutSave.NewMutable(), OutSave.Objects); +} + +void UVoxelDataTools::GetCompressedSave(AVoxelWorld* World, FVoxelCompressedWorldSaveImpl& OutSave, TArray& OutObjects) +{ + CHECK_VOXELWORLD_IS_CREATED_VOID(); + FVoxelUncompressedWorldSaveImpl Save; + World->GetData().GetSave(Save, OutObjects); + UVoxelSaveUtilities::CompressVoxelSave(Save, OutSave); +} + +void UVoxelDataTools::GetSaveAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelUncompressedWorldSave& OutSave, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld_WithValue( + WorldContextObject, + LatentInfo, + World, + FUNCTION_FNAME, + bHideLatentWarnings, + OutSave, + [](FVoxelData& Data, FVoxelUncompressedWorldSave& Save) + { + Data.GetSave(Save.NewMutable(), Save.Objects); + }, + EVoxelUpdateRender::DoNotUpdateRender, + {}); +} + +void UVoxelDataTools::GetCompressedSaveAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelCompressedWorldSave& OutSave, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld_WithValue( + WorldContextObject, + LatentInfo, + World, + FUNCTION_FNAME, + bHideLatentWarnings, + OutSave, + [](FVoxelData& Data, FVoxelCompressedWorldSave& CompressedSave) + { + FVoxelUncompressedWorldSaveImpl Save; + Data.GetSave(Save, CompressedSave.Objects); + UVoxelSaveUtilities::CompressVoxelSave(Save, CompressedSave.NewMutable()); + }, + EVoxelUpdateRender::DoNotUpdateRender, + {}); +} + +bool UVoxelDataTools::LoadFromSave(const AVoxelWorld* World, const FVoxelUncompressedWorldSave& Save) +{ + return LoadFromSave(World, Save.Const(), Save.Objects); +} + +bool UVoxelDataTools::LoadFromSave(const AVoxelWorld* World, const FVoxelUncompressedWorldSaveImpl& Save, const TArray& Objects) +{ + CHECK_VOXELWORLD_IS_CREATED(); + CHECK_SAVE(); + + TArray BoundsToUpdate; + auto& Data = World->GetData(); + + const FVoxelGeneratorInit WorldInit = World->GetGeneratorInit(); + const FVoxelPlaceableItemLoadInfo LoadInfo{ &WorldInit, &Objects }; + + const bool bSuccess = Data.LoadFromSave(Save, LoadInfo, &BoundsToUpdate); + + World->GetLODManager().UpdateBounds(BoundsToUpdate); + + return bSuccess; +} + +bool UVoxelDataTools::LoadFromCompressedSave(const AVoxelWorld* World, const FVoxelCompressedWorldSave& Save) +{ + return LoadFromCompressedSave(World, Save.Const(), Save.Objects); +} + +bool UVoxelDataTools::LoadFromCompressedSave(const AVoxelWorld* World, const FVoxelCompressedWorldSaveImpl& Save, const TArray& Objects) +{ + CHECK_VOXELWORLD_IS_CREATED(); + CHECK_SAVE(); + + FVoxelUncompressedWorldSaveImpl UncompressedSave; + UVoxelSaveUtilities::DecompressVoxelSave(Save, UncompressedSave); + + return LoadFromSave(World, UncompressedSave, Objects); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::RoundVoxelsImpl(FVoxelData& Data, const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + int32 NumDirtyLeaves = 0; + uint64 NumVoxels = 0; + { + VOXEL_ASYNC_SCOPE_COUNTER("Record stats"); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty() && Leaf.GetData().HasAllocation()) + { + NumDirtyLeaves++; + NumVoxels += VOXELS_PER_DATA_CHUNK; + } + }); + } + + FVoxelScopedSlowTask SlowTask(NumDirtyLeaves, VOXEL_LOCTEXT("Rounding voxels")); + FScopeToolsTimeLogger ToolsLogger(__FUNCTION__, NumVoxels); + + FVoxelMutableDataAccelerator OctreeAccelerator(Data, Bounds.Extend(2)); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty() && Leaf.GetData().HasAllocation()) + { + SlowTask.EnterProgressFrame(); + + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + LeafBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(LeafBounds.Min, X, Y, Z); + const FVoxelValue& Value = Leaf.GetData().Get(Index); + + if (Value.IsTotallyEmpty() || Value.IsTotallyFull()) return; + + const bool bEmpty = Value.IsEmpty(); + for (int32 OtherX = X - 2; OtherX <= X + 2; OtherX++) + { + for (int32 OtherY = Y - 2; OtherY <= Y + 2; OtherY++) + { + for (int32 OtherZ = Z - 2; OtherZ <= Z + 2; OtherZ++) + { + if (OtherX == X && OtherY == Y && OtherZ == Z) continue; + const auto OtherValue = OctreeAccelerator.GetValue(OtherX, OtherY, OtherZ, 0); + if (OtherValue.IsEmpty() != bEmpty) return; + } + } + } + OctreeAccelerator.SetValue(X, Y, Z, bEmpty ? FVoxelValue::Empty() : FVoxelValue::Full()); + }); + } + }); +} + +void UVoxelDataTools::RoundVoxels(AVoxelWorld* World, FVoxelIntBox InBounds) +{ + const FVoxelIntBox Bounds = InBounds.Extend(2); + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, RoundVoxelsImpl(Data, InBounds)); +} + +void UVoxelDataTools::RoundVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox InBounds, + bool bHideLatentWarnings) +{ + const FVoxelIntBox Bounds = InBounds.Extend(2); + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, RoundVoxelsImpl(Data, InBounds)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::ClearUnusedMaterialsImpl(FVoxelData& Data, const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + int32 NumDirtyLeaves = 0; + uint64 NumVoxels = 0; + { + VOXEL_ASYNC_SCOPE_COUNTER("Record stats"); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty() && Leaf.GetData().HasAllocation()) + { + NumDirtyLeaves++; + NumVoxels += VOXELS_PER_DATA_CHUNK; + } + }); + } + + FVoxelScopedSlowTask SlowTask(NumDirtyLeaves, VOXEL_LOCTEXT("Clearing unused materials")); + FScopeToolsTimeLogger ToolsLogger(__FUNCTION__, NumVoxels); + + FVoxelMutableDataAccelerator OctreeAccelerator(Data, Bounds.Extend(2)); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty() && Leaf.GetData().HasAllocation()) + { + SlowTask.EnterProgressFrame(); + + bool bEdited = false; + + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + LeafBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(LeafBounds.Min, X, Y, Z); + const FVoxelMaterial Material = Leaf.GetData().Get(Index); + + if (Material == FVoxelMaterial::Default()) return; + + const FVoxelValue Value = OctreeAccelerator.GetValue(X, Y, Z, 0); + if (!Value.IsEmpty()) // Only not empty voxels materials can affect the surface + { + for (int32 OtherX = X - 1; OtherX <= X + 1; OtherX++) + { + for (int32 OtherY = Y - 1; OtherY <= Y + 1; OtherY++) + { + for (int32 OtherZ = Z - 1; OtherZ <= Z + 1; OtherZ++) + { + if (OtherX == X && OtherY == Y && OtherZ == Z) continue; + const auto OtherValue = OctreeAccelerator.GetValue(OtherX, OtherY, OtherZ, 0); + if (OtherValue.IsEmpty()) return; + } + } + } + } + OctreeAccelerator.SetMaterial(X, Y, Z, FVoxelMaterial::Default()); + bEdited = true; + }); + + if (bEdited) + { + Leaf.GetData().Compress(Data); + } + } + }); +} + +void UVoxelDataTools::ClearUnusedMaterials(AVoxelWorld* World, FVoxelIntBox InBounds) +{ + const FVoxelIntBox Bounds = InBounds.Extend(1); + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, ClearUnusedMaterialsImpl(Data, InBounds)); +} + +void UVoxelDataTools::ClearUnusedMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox InBounds, + bool bHideLatentWarnings) +{ + const FVoxelIntBox Bounds = InBounds.Extend(1); + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, ClearUnusedMaterialsImpl(Data, InBounds)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::GetVoxelsValueAndMaterialImpl( + FVoxelData& Data, + TArray& Voxels, + const FVoxelIntBox& Bounds, + const TArray& Positions) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Positions.Num()); + const FVoxelMutableDataAccelerator OctreeAccelerator(Data, Bounds); + for (auto& Position : Positions) + { + if (Data.IsInWorld(Position)) + { + FVoxelValueMaterial Voxel; + Voxel.Position = Position; + Voxel.Value = OctreeAccelerator.GetValue(Position, 0).ToFloat(); + Voxel.Material = OctreeAccelerator.GetMaterial(Position, 0); + Voxels.Add(Voxel); + } + } +} + +void UVoxelDataTools::GetVoxelsValueAndMaterial( + TArray& Voxels, + AVoxelWorld* World, + const TArray& Positions) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (Positions.Num() == 0) + { + Voxels.Reset(); + return; + } + + const FVoxelIntBox Bounds(Positions); + CHECK_BOUNDS_ARE_VALID_VOID(); + VOXEL_TOOL_HELPER_BODY(Read, DoNotUpdateRender, GetVoxelsValueAndMaterialImpl(Data, Voxels, Bounds, Positions)); +} + +void UVoxelDataTools::GetVoxelsValueAndMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& Voxels, + AVoxelWorld* World, + const TArray& Positions, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + if (Positions.Num() == 0) + { + Voxels.Reset(); + return; + } + + const FVoxelIntBox Bounds(Positions); + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Voxels, Read, DoNotUpdateRender, NO_PREFIX, GetVoxelsValueAndMaterialImpl(Data, InVoxels, Bounds, Positions)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelDataMemoryUsageInMB UVoxelDataTools::GetDataMemoryUsageInMB(AVoxelWorld* World) +{ + CHECK_VOXELWORLD_IS_CREATED(); + VOXEL_FUNCTION_COUNTER(); + + constexpr double OneMB = double(1 << 20); + + auto& Data = World->GetData(); + + FVoxelDataMemoryUsageInMB MemoryUsage; + + MemoryUsage.DirtyValues = Data.GetDirtyMemory().Values.GetValue() / OneMB; + MemoryUsage.DirtyMaterials = Data.GetDirtyMemory().Materials.GetValue() / OneMB; + + MemoryUsage.CachedValues = Data.GetCachedMemory().Values.GetValue() / OneMB; + MemoryUsage.CachedMaterials = Data.GetCachedMemory().Materials.GetValue() / OneMB; + + return MemoryUsage; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::ClearCachedValues( + AVoxelWorld* World, + FVoxelIntBox Bounds) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.ClearCacheInBounds(Bounds)); +} + +void UVoxelDataTools::ClearCachedValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.ClearCacheInBounds(Bounds)); +} + +void UVoxelDataTools::ClearCachedMaterials( + AVoxelWorld* World, + FVoxelIntBox Bounds) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.ClearCacheInBounds(Bounds)); +} + +void UVoxelDataTools::ClearCachedMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.ClearCacheInBounds(Bounds)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::CheckForSingleValues( + AVoxelWorld* World, + FVoxelIntBox Bounds) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CheckIsSingle(Bounds)); +} + +void UVoxelDataTools::CheckForSingleValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CheckIsSingle(Bounds)); +} + +void UVoxelDataTools::CheckForSingleMaterials( + AVoxelWorld* World, + FVoxelIntBox Bounds) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CheckIsSingle(Bounds)); +} + +void UVoxelDataTools::CheckForSingleMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, Data.CheckIsSingle(Bounds)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void UVoxelDataTools::CompressIntoHeightmapImpl(FVoxelData& Data, TVoxelHeightmapAssetSamplerWrapper& Wrapper, const bool bCheckAllLeaves) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + ensure(Wrapper.Scale == 1.f); + + FVoxelScopedSlowTask SlowTask(2, VOXEL_LOCTEXT("Compressing into heightmap")); + SlowTask.EnterProgressFrame(); + + TMap> LeavesColumns; + { + if (bCheckAllLeaves) + { + // Need to subdivide + FVoxelOctreeUtilities::IterateEntireTree(Data.GetOctree(), [&](FVoxelDataOctreeBase& Tree) + { + if (!Tree.IsLeaf()) + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); + } + + int32 NumLeaves = 0; + FVoxelOctreeUtilities::IterateAllLeaves(Data.GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty() || bCheckAllLeaves) + { + NumLeaves++; + } + }); + + FVoxelScopedSlowTask LocalSlowTask(NumLeaves, VOXEL_LOCTEXT("Caching data")); + + FVoxelOctreeUtilities::IterateAllLeaves(Data.GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForWrite()); + + auto& DataHolder = Leaf.GetData(); + if (bCheckAllLeaves) + { + if (!DataHolder.HasData()) + { + DataHolder.CreateData(Data, [&](FVoxelValue* RESTRICT DataPtr) + { + TVoxelQueryZone QueryZone(Leaf.GetBounds(), DataPtr); + Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + }); + // Reduce memory usage + DataHolder.Compress(Data); + } + } + else + { + if (!DataHolder.IsDirty()) + { + // Flush cache + DataHolder.ClearData(Data); + return; + } + } + + LocalSlowTask.EnterProgressFrame(); + + const FIntVector Min = Leaf.GetMin(); + + auto& Column = LeavesColumns.FindOrAdd(FIntPoint(Min.X, Min.Y)); + check(!Column.Contains(Min.Z)); + Column.Add(Min.Z, &Leaf); + }); + } + + SlowTask.EnterProgressFrame(); + FVoxelScopedSlowTask LocalSlowTask(LeavesColumns.Num(), VOXEL_LOCTEXT("Finding heights")); + + FVoxelMutableDataAccelerator Accelerator(Data, FVoxelIntBox::Infinite); + for (auto& ColumnsIt : LeavesColumns) + { + LocalSlowTask.EnterProgressFrame(); + + const FIntPoint LeafMinXY = ColumnsIt.Key; + auto& Leaves = ColumnsIt.Value; + + int32 MinLeafMinZ = MAX_int32; + int32 MaxLeafMinZ = MIN_int32; + for (auto& LeavesIt : Leaves) + { + MinLeafMinZ = FMath::Min(LeavesIt.Key, MinLeafMinZ); + MaxLeafMinZ = FMath::Max(LeavesIt.Key, MaxLeafMinZ); + } + check(MinLeafMinZ != MAX_int32); + check(MaxLeafMinZ != MIN_int32); + + const auto GetHeightmapPosition = [&](int32 X, int32 Y) + { + // Note: HeightmapAssets are offset by (-Wrapper.GetWidth() / 2, -Wrapper.GetHeight() / 2) + return FIntPoint(LeafMinXY.X + X + Wrapper.GetWidth() / 2, LeafMinXY.Y + Y + Wrapper.GetHeight() / 2); + }; + const auto IsInBounds = [&](const FIntPoint& HeightmapPosition) + { + return + HeightmapPosition.X >= 0 && + HeightmapPosition.Y >= 0 && + HeightmapPosition.X < Wrapper.GetWidth() && + HeightmapPosition.Y < Wrapper.GetHeight(); + }; + + TStaticArray NewHeights; + for (int32 X = 0; X < DATA_CHUNK_SIZE; X++) + { + for (int32 Y = 0; Y < DATA_CHUNK_SIZE; Y++) + { + const FIntPoint HeightmapPosition = GetHeightmapPosition(X, Y); + if (!IsInBounds(HeightmapPosition)) continue; + + const auto GetNewHeight = [&]() + { + const float HeightmapHeight = Wrapper.GetHeight(HeightmapPosition.X, HeightmapPosition.Y, EVoxelSamplerMode::Clamp); + if (HeightmapHeight >= MaxLeafMinZ + DATA_CHUNK_SIZE) + { + // Heightmap is above all leaves, can't do anything + return HeightmapHeight; + } + + // Go down chunk by chunk until we find the height + for (int32 LeafMinZ = MaxLeafMinZ; LeafMinZ >= MinLeafMinZ; LeafMinZ -= DATA_CHUNK_SIZE) + { + auto* Leaf = Leaves.FindRef(LeafMinZ); + if (!Leaf) + { + checkVoxelSlow(HeightmapHeight <= LeafMinZ + DATA_CHUNK_SIZE); + if (LeafMinZ < HeightmapHeight) + { + // HeightmapHeight above all remaining leaves, can't do anything + return HeightmapHeight; + } + } + else + { + // Go down until we find the height + auto& DataHolder = Leaf->GetData(); + if (DataHolder.IsSingleValue() && !DataHolder.GetSingleValue().IsEmpty()) + { + // Fast path + continue; + } + + for (int32 Z = DATA_CHUNK_SIZE - 1; Z >= 0; Z--) + { + const FVoxelValue Value = DataHolder.Get(FVoxelDataOctreeUtilities::IndexFromCoordinates(X, Y, Z)); + if (!Value.IsEmpty()) + { + FVoxelValue ValueAbove; + if (Z + 1 < DATA_CHUNK_SIZE) + { + ValueAbove = DataHolder.Get(FVoxelDataOctreeUtilities::IndexFromCoordinates(X, Y, Z + 1)); + } + else + { + ValueAbove = Accelerator.GetValue(Leaf->GetMin() + FIntVector(X, Y, Z + 1), 0); + } + // Note: not true on world upper bound + ensure(ValueAbove.IsEmpty()); + + const float NewHeight = LeafMinZ + Z + FVoxelUtilities::GetAbsDistanceFromDensities(Value.ToFloat(), ValueAbove.ToFloat()); + const float OldHeight = HeightmapHeight; + + // Mark leaves that will have their value changed by the new height as dirty so that they don't change + const int32 StartLeafMinZ = FMath::FloorToInt(FMath::Min(NewHeight, OldHeight) / DATA_CHUNK_SIZE) * DATA_CHUNK_SIZE; + const int32 EndLeafMinZ = FMath::CeilToInt(FMath::Max(NewHeight, OldHeight) / DATA_CHUNK_SIZE) * DATA_CHUNK_SIZE; + for (int32 ItLeafMinZ = StartLeafMinZ; ItLeafMinZ <= EndLeafMinZ; ItLeafMinZ += DATA_CHUNK_SIZE) + { + if (Leaves.Contains(ItLeafMinZ)) continue; // Values already correctly stored + + // Leaf is defaulting to generator value, but this value is going to change + // Mark the leaf as dirty + + const FIntVector LeafPosition = FIntVector(LeafMinXY.X, LeafMinXY.Y, ItLeafMinZ) + DATA_CHUNK_SIZE / 2; + if (!Data.IsInWorld(LeafPosition)) continue; + + auto* ItLeaf = FVoxelOctreeUtilities::GetLeaf(Data.GetOctree(), LeafPosition); + check(ItLeaf); + check(!ItLeaf->GetData().HasData()); + + ItLeaf->InitForEdit(Data); + ItLeaf->GetData().SetIsDirty(true, Data); + + // & Add it to the map + Leaves.Add(ItLeafMinZ, ItLeaf); + + // Update min/max as well + MinLeafMinZ = FMath::Min(ItLeafMinZ, MinLeafMinZ); + MaxLeafMinZ = FMath::Max(ItLeafMinZ, MaxLeafMinZ); + } + return NewHeight; + } + } + } + } + // Not found anything, default to old height + return HeightmapHeight; + }; + NewHeights[X + DATA_CHUNK_SIZE * Y] = GetNewHeight(); + } + } + + // Write the heights + for (int32 X = 0; X < DATA_CHUNK_SIZE; X++) + { + for (int32 Y = 0; Y < DATA_CHUNK_SIZE; Y++) + { + const FIntPoint HeightmapPosition = GetHeightmapPosition(X, Y); + if (!IsInBounds(HeightmapPosition)) continue; + + Wrapper.SetHeight(HeightmapPosition.X, HeightmapPosition.Y, NewHeights[X + DATA_CHUNK_SIZE * Y]); + } + } + } +} + +template VOXEL_API void UVoxelDataTools::CompressIntoHeightmapImpl(FVoxelData& Data, TVoxelHeightmapAssetSamplerWrapper& Wrapper, bool bCheckAllLeaves); +template VOXEL_API void UVoxelDataTools::CompressIntoHeightmapImpl(FVoxelData& Data, TVoxelHeightmapAssetSamplerWrapper& Wrapper, bool bCheckAllLeaves); + +void UVoxelDataTools::CompressIntoHeightmap( + AVoxelWorld* World, + UVoxelHeightmapAsset* HeightmapAsset, + bool bHeightmapAssetMatchesWorld) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + auto& Data = World->GetData(); + FVoxelWriteScopeLock Lock(Data, FVoxelIntBox::Infinite, ""); + + bool bCheckAllLeaves = false; + if (!HeightmapAsset) + { + if (World->Generator.IsObject()) // We don't want to edit the default object otherwise + { + HeightmapAsset = Cast(World->Generator.Object); + } + } + else + { + if (!bHeightmapAssetMatchesWorld) + { + bCheckAllLeaves = true; + + const int32 Size = FVoxelUtilities::GetSizeFromDepth(Data.Depth); + if (Size > 20000) + { + FVoxelMessages::Error(FUNCTION_ERROR("Heightmap size would be too large!")); + return; + } + if (auto* Heightmap = Cast(HeightmapAsset)) + { + HeightmapAsset->HeightOffset = -Size / 2; // Height = 0 should be bottom of the world since all heights are positive + HeightmapAsset->HeightScale = float(Size) / MAX_uint16; // Distribute the heights + HeightmapAsset->AdditionalThickness = Size; // Just to be safe + + // Init the heightmap data + Heightmap->GetData().SetSize(Size, Size, false, {}); + Heightmap->GetData().SetAllHeightsTo(0); + } + if (auto* Heightmap = Cast(HeightmapAsset)) + { + HeightmapAsset->HeightOffset = 0; // Heights can be negative too here + HeightmapAsset->HeightScale = 1.f; // No need to distribute + HeightmapAsset->AdditionalThickness = Size; // Fill below + + // Init the heightmap data + Heightmap->GetData().SetSize(Size, Size, false, {}); + Heightmap->GetData().SetAllHeightsTo(0); + } + } + } + + if (auto* UINT16Heightmap = Cast(HeightmapAsset)) + { + TVoxelHeightmapAssetSamplerWrapper Wrapper(UINT16Heightmap); + CompressIntoHeightmapImpl(Data, Wrapper, bCheckAllLeaves); + UINT16Heightmap->Save(); + } + else if (auto* FloatHeightmap = Cast(HeightmapAsset)) + { + TVoxelHeightmapAssetSamplerWrapper Wrapper(FloatHeightmap); + CompressIntoHeightmapImpl(Data, Wrapper, bCheckAllLeaves); + FloatHeightmap->Save(); + } + else + { + FVoxelMessages::Error(FUNCTION_ERROR("Generator is not an heightmap!")); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::RoundToGeneratorImpl(FVoxelData& Data, const FVoxelIntBox& Bounds, bool bPreserveNormals) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + int32 NumDirtyLeaves = 0; + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty()) + { + NumDirtyLeaves++; + } + }); + + FVoxelScopedSlowTask SlowTask(NumDirtyLeaves, VOXEL_LOCTEXT("Round To Generator")); + + FVoxelMutableDataAccelerator OctreeAccelerator(Data, Bounds.Extend(2)); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + if (!Leaf.GetData().IsDirty()) + { + return; + } + + SlowTask.EnterProgressFrame(); + + // Do not try to round if single value + if (!Leaf.GetData().IsSingleValue()) + { + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + LeafBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(LeafBounds.Min, X, Y, Z); + const FVoxelValue Value = Leaf.GetData().Get(Index); + const FVoxelValue GeneratorValue = Data.Generator->Get(X, Y, Z, 0, FVoxelItemStack::Empty); + + if (Value == GeneratorValue) return; + if (Value.IsEmpty() != GeneratorValue.IsEmpty()) return; + + const auto CheckNeighbor = [&](int32 DX, int32 DY, int32 DZ) + { + const FVoxelValue OtherValue = OctreeAccelerator.GetValue(X + DX, Y + DY, Z + DZ, 0); + const FVoxelValue OtherGeneratorValue = Data.Generator->Get(X + DX, Y + DY, Z + DZ, 0, FVoxelItemStack::Empty); + return OtherValue.IsEmpty() == OtherGeneratorValue.IsEmpty(); + }; + + if (bPreserveNormals) + { + for (int32 DX = -1; DX <= 1; DX++) + { + for (int32 DY = -1; DY <= 1; DY++) + { + for (int32 DZ = -1; DZ <= 1; DZ++) + { + if (DX == 0 && DY == 0 && DZ == 0) continue; + if (!CheckNeighbor(DX, DY, DZ)) return; + } + } + } + } + else + { + if (!CheckNeighbor(-1, 0, 0)) return; + if (!CheckNeighbor(+1, 0, 0)) return; + if (!CheckNeighbor(0, -1, 0)) return; + if (!CheckNeighbor(0, +1, 0)) return; + if (!CheckNeighbor(0, 0, -1)) return; + if (!CheckNeighbor(0, 0, +1)) return; + } + + OctreeAccelerator.SetValue(X, Y, Z, GeneratorValue); + }); + } + + // But always check this, as else we get a lot of space used by single values! + FVoxelDataUtilities::CheckIfSameAsGenerator(Data, Leaf); + }); +} + +void UVoxelDataTools::RoundToGenerator(AVoxelWorld* World, FVoxelIntBox Bounds, bool bPreserveNormals) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, RoundToGeneratorImpl(Data, Bounds, bPreserveNormals)); +} + +void UVoxelDataTools::RoundToGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bPreserveNormals, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, RoundToGeneratorImpl(Data, Bounds, bPreserveNormals)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelDataTools::CheckIfSameAsGeneratorImpl(FVoxelData& Data, const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + int32 NumLeaves = 0; + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + NumLeaves++; + }); + + FVoxelScopedSlowTask SlowTask(NumLeaves, VOXEL_LOCTEXT("Check If Same As Generator")); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + SlowTask.EnterProgressFrame(); + ensureThreadSafe(Leaf.IsLockedForWrite()); + if (Leaf.GetData().IsDirty()) + { + FVoxelDataUtilities::CheckIfSameAsGenerator(Data, Leaf); + } + if (Leaf.GetData().IsDirty()) + { + FVoxelDataUtilities::CheckIfSameAsGenerator(Data, Leaf); + } + }); +} + +void UVoxelDataTools::CheckIfSameAsGenerator(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, CheckIfSameAsGeneratorImpl(Data, Bounds)); +} + +void UVoxelDataTools::CheckIfSameAsGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, CheckIfSameAsGeneratorImpl(Data, Bounds)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void UVoxelDataTools::SetBoxAsDirtyImpl(FVoxelData& Data, const FVoxelIntBox& Bounds, bool bCompress) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const int32 Count = Bounds.Overlap(Data.WorldBounds).MakeMultipleOfRoundUp(DATA_CHUNK_SIZE).Count() / int64(VOXELS_PER_DATA_CHUNK); + FVoxelScopedSlowTask SlowTask(Count, VOXEL_LOCTEXT("Set Box as Dirty")); + FVoxelOctreeUtilities::IterateTreeInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + SlowTask.EnterProgressFrame(); + + auto& Leaf = Tree.AsLeaf(); + ensureThreadSafe(Leaf.IsLockedForWrite()); + + Leaf.InitForEdit(Data); + if (bCompress) + { + // Else memory usage explodes + Leaf.GetData().Compress(Data); + } + Leaf.GetData().SetIsDirty(true, Data); + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); +} + +template VOXEL_API void UVoxelDataTools::SetBoxAsDirtyImpl(FVoxelData&, const FVoxelIntBox&, bool); +template VOXEL_API void UVoxelDataTools::SetBoxAsDirtyImpl(FVoxelData&, const FVoxelIntBox&, bool); + +void UVoxelDataTools::SetBoxAsDirty( + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bDirtyValues, + bool bDirtyMaterials) +{ + VOXEL_TOOL_HELPER(Write, DoNotUpdateRender, NO_PREFIX, + if (bDirtyValues) + { + SetBoxAsDirtyImpl(Data, Bounds, true); + } + if (bDirtyMaterials) + { + SetBoxAsDirtyImpl(Data, Bounds, true); + }); +} + +void UVoxelDataTools::SetBoxAsDirtyAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bDirtyValues, + bool bDirtyMaterials, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER(Write, DoNotUpdateRender, NO_PREFIX, + if (bDirtyValues) + { + SetBoxAsDirtyImpl(Data, Bounds, true); + } + if (bDirtyMaterials) + { + SetBoxAsDirtyImpl(Data, Bounds, true); + }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define FINDCLOSESTNONEMPTYVOXEL_PREFIX \ + const FVoxelVector Position = FVoxelToolHelpers::GetRealTemplate(World, InPosition, bConvertToVoxelSpace); \ + const FVoxelIntBox Bounds = FVoxelIntBox(Position, Position + 1); + +FVoxelFindClosestNonEmptyVoxelResult UVoxelDataTools::FindClosestNonEmptyVoxelImpl( + FVoxelData& Data, + const FVoxelVector& Position, + bool bReadMaterial) +{ + FVoxelFindClosestNonEmptyVoxelResult Result; + + const FVoxelConstDataAccelerator Accelerator(Data); + + v_flt Distance = MAX_vflt; + for (auto& Neighbor : FVoxelUtilities::GetNeighbors(Position)) + { + const FVoxelValue Value = Accelerator.GetValue(Neighbor, 0); + if (!Value.IsEmpty()) + { + const v_flt PointDistance = (FVoxelVector(Neighbor) - Position).SizeSquared(); + if (PointDistance < Distance) + { + Distance = PointDistance; + Result.bSuccess = true; + Result.Position = Neighbor; + Result.Value = Value.ToFloat(); + } + } + } + + if (Result.bSuccess && bReadMaterial) + { + Result.Material = Accelerator.GetMaterial(Result.Position, 0); + } + + return Result; +} + +void UVoxelDataTools::FindClosestNonEmptyVoxel( + FVoxelFindClosestNonEmptyVoxelResult& Result, + AVoxelWorld* World, + FVector InPosition, + bool bReadMaterial, + bool bConvertToVoxelSpace) +{ + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, FINDCLOSESTNONEMPTYVOXEL_PREFIX, Result = FindClosestNonEmptyVoxelImpl(Data, Position, bReadMaterial)); +} + +void UVoxelDataTools::FindClosestNonEmptyVoxelAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelFindClosestNonEmptyVoxelResult& Result, + AVoxelWorld* World, + FVector InPosition, + bool bReadMaterial, + bool bConvertToVoxelSpace, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Result, Read, DoNotUpdateRender, FINDCLOSESTNONEMPTYVOXEL_PREFIX, InResult = FindClosestNonEmptyVoxelImpl(Data, Position, bReadMaterial)); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelHardnessHandler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelHardnessHandler.cpp new file mode 100644 index 00000000..4af6f5e6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelHardnessHandler.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelHardnessHandler.h" + +FVoxelHardnessHandler::FVoxelHardnessHandler(const AVoxelWorld& World) + : MaterialConfig(World.MaterialConfig) + , RGBHardness(World.RGBHardness) +{ + for (float& It : Hardness) + { + It = 1; + } + for (auto& It : World.MaterialsHardness) + { + Hardness[FMath::Clamp(TCString::Atoi(*It.Key), 0, 255)] = It.Value; + } + + if (MaterialConfig == EVoxelMaterialConfig::RGB) + { + if (RGBHardness == EVoxelRGBHardness::FourWayBlend || RGBHardness == EVoxelRGBHardness::FiveWayBlend) + { + bNeedsToCompute = World.MaterialsHardness.Num() > 0; + } + else + { + bNeedsToCompute = true; + } + } + else + { + bNeedsToCompute = World.MaterialsHardness.Num() > 0; + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelMathLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelMathLibrary.cpp new file mode 100644 index 00000000..f581b990 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelMathLibrary.cpp @@ -0,0 +1,67 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelMathLibrary.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +FVector UVoxelMathLibrary::GetUnitVectorFromRandom(FVector2D Random) +{ + // From Raytracing Gems, Chapter 16 + + // Compute radius r (branchless). + Random = 2 * Random - 1; + const float d = 1 - (FMath::Abs(Random.X) + FMath::Abs(Random.Y)); + const float Radius = 1 - FMath::Abs(d); + + // Compute phi in the first quadrant (branchless, except for the division-by-zero test), + // using sign(random) to map the result to the correct quadrant below + const float Phi = (Radius == 0) ? 0 : (PI / 4) * ((FMath::Abs(Random.Y) - FMath::Abs(Random.X)) / Radius + 1); + const float f = Radius * FMath::Sqrt(2 - Radius * Radius); + + FVector Result; + Result.X = f * FMath::Sign(Random.X) * FMath::Cos(Phi); + Result.Y = f * FMath::Sign(Random.Y) * FMath::Sin(Phi); + Result.Z = FMath::Sign(d) * (1 - Radius * Radius); + + ensure(Result.IsUnit()); + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelHaltonStream UVoxelMathLibrary::MakeHaltonStream(int32 InitialSeed) +{ + return { InitialSeed, uint32(FMath::Abs(InitialSeed)) }; +} + +void UVoxelMathLibrary::ResetHaltonStream(const FVoxelHaltonStream& Stream) +{ + Stream.Seed = Stream.InitialSeed; +} + +float UVoxelMathLibrary::GetHalton1D(const FVoxelHaltonStream& Stream) +{ + const float Value = FVoxelUtilities::Halton<2>(Stream.Seed); + Stream.Seed++; + return Value; +} + +FVector2D UVoxelMathLibrary::GetHalton2D(const FVoxelHaltonStream& Stream) +{ + FVector2D Value; + Value.X = FVoxelUtilities::Halton<2>(Stream.Seed); + Value.Y = FVoxelUtilities::Halton<3>(Stream.Seed); + Stream.Seed++; + return Value; +} + +FVector UVoxelMathLibrary::GetHalton3D(const FVoxelHaltonStream& Stream) +{ + FVector Value; + Value.X = FVoxelUtilities::Halton<2>(Stream.Seed); + Value.Y = FVoxelUtilities::Halton<3>(Stream.Seed); + Value.Z = FVoxelUtilities::Halton<5>(Stream.Seed); + Stream.Seed++; + return Value; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPaintMaterial.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPaintMaterial.cpp new file mode 100644 index 00000000..6e78c08f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPaintMaterial.cpp @@ -0,0 +1,309 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +void FVoxelPaintMaterial::ApplyToMaterial(FVoxelMaterial& Material, float Strength) const +{ + // Else goes way beyond target + Strength = FMath::Clamp(Strength, -1.f, 1.f); + + switch (Type) + { + case EVoxelPaintMaterialType::Color: + { + // Note: the voxel colors are in linear space, even if they are stored in FColors + FColor ColorToPaint = Color.bUseLinearColor ? Color.LinearColor.ToFColor(false) : Color.Color; + if (Strength < 0) + { + Strength = -Strength; + ColorToPaint = FColor(0, 0, 0, 0); + } + if (Color.bPaintR) + { + Material.SetR(FVoxelUtilities::LerpUINT8(Material.GetR(), ColorToPaint.R, Strength)); + } + if (Color.bPaintG) + { + Material.SetG(FVoxelUtilities::LerpUINT8(Material.GetG(), ColorToPaint.G, Strength)); + } + if (Color.bPaintB) + { + Material.SetB(FVoxelUtilities::LerpUINT8(Material.GetB(), ColorToPaint.B, Strength)); + } + if (Color.bPaintA) + { + Material.SetA(FVoxelUtilities::LerpUINT8(Material.GetA(), ColorToPaint.A, Strength)); + } + break; + } + case EVoxelPaintMaterialType::FiveWayBlend: + { + float TargetValue = Strength > 0 ? FiveWayBlend.TargetValue : 1.f - FiveWayBlend.TargetValue; + Strength = FMath::Abs(Strength); + + if (FiveWayBlend.bFourWayBlend) + { + const int32 Channel = FMath::Clamp(FiveWayBlend.Channel, 0, 3); + + const float R = Material.GetR_AsFloat(); + const float G = Material.GetG_AsFloat(); + const float B = Material.GetB_AsFloat(); + + TVoxelStaticArray Strengths = FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>({ R, G, B }); + + // Add locked channels to ChannelsToKeepIntact, + // and make TargetValue relative to the strength that's left to edit + uint32 ChannelsToKeepIntact = 1u << Channel; + { + float LockedChannelsStrength = 0.f; + for (uint8 LockedChannel : FiveWayBlend.LockedChannels) + { + LockedChannel = FMath::Clamp(LockedChannel, 0, 3); + ChannelsToKeepIntact |= 1u << LockedChannel; + LockedChannelsStrength += Strengths[LockedChannel]; + } + TargetValue *= 1.f - LockedChannelsStrength; + } + + Strengths[Channel] = FMath::Clamp(FMath::Lerp(Strengths[Channel], TargetValue, Strength), 0.f, 1.f); + + const TVoxelStaticArray Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(Strengths, ChannelsToKeepIntact); + + Material.SetR(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[0], R)); + Material.SetG(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[1], G)); + Material.SetB(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[2], B)); + } + else + { + const int32 Channel = FMath::Clamp(FiveWayBlend.Channel, 0, 4); + + const float R = Material.GetR_AsFloat(); + const float G = Material.GetG_AsFloat(); + const float B = Material.GetB_AsFloat(); + const float A = Material.GetA_AsFloat(); + + TVoxelStaticArray Strengths = FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<5>({ R, G, B, A }); + + // Add locked channels to ChannelsToKeepIntact, + // and make TargetValue relative to the strength that's left to edit + uint32 ChannelsToKeepIntact = 1u << Channel; + { + float LockedChannelsStrength = 0.f; + for (uint8 LockedChannel : FiveWayBlend.LockedChannels) + { + LockedChannel = FMath::Clamp(LockedChannel, 0, 4); + ChannelsToKeepIntact |= 1u << LockedChannel; + LockedChannelsStrength += Strengths[LockedChannel]; + } + TargetValue *= 1.f - LockedChannelsStrength; + } + + Strengths[Channel] = FMath::Clamp(FMath::Lerp(Strengths[Channel], TargetValue, Strength), 0.f, 1.f); + + const TVoxelStaticArray Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<5>(Strengths, ChannelsToKeepIntact); + + Material.SetR(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[0], R)); + Material.SetG(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[1], G)); + Material.SetB(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[2], B)); + Material.SetA(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[3], A)); + } + + break; + } + case EVoxelPaintMaterialType::SingleIndex: + { + Material.SetSingleIndex(SingleIndex.Channel.Channel); + break; + } + case EVoxelPaintMaterialType::MultiIndex: + { + float TargetValue = Strength > 0 ? MultiIndex.TargetValue : 1.f - MultiIndex.TargetValue; + Strength = FMath::Abs(Strength); + + const float Blend0 = Material.GetMultiIndex_Blend0_AsFloat(); + const float Blend1 = Material.GetMultiIndex_Blend1_AsFloat(); + const float Blend2 = Material.GetMultiIndex_Blend2_AsFloat(); + + TVoxelStaticArray Strengths = FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>({ Blend0, Blend1, Blend2 }); + + uint32 ChannelsToKeepIntact = 0; + + // Propagate strengths backward if the indices are equal, and mark them as fixed: + // we want them to always be 0, and the "main" one have all the strength instead + // This is to handle cases where indices are eg 1 0 0 0 + { + if (Material.GetMultiIndex_Index3() == Material.GetMultiIndex_Index2()) + { + Strengths[2] += Strengths[3]; + Strengths[3] = 0; + ChannelsToKeepIntact |= 1u << 3; + } + else if (Material.GetMultiIndex_Index3() == Material.GetMultiIndex_Index1()) + { + Strengths[1] += Strengths[3]; + Strengths[3] = 0; + ChannelsToKeepIntact |= 1u << 3; + } + else if (Material.GetMultiIndex_Index3() == Material.GetMultiIndex_Index0()) + { + Strengths[0] += Strengths[3]; + Strengths[3] = 0; + ChannelsToKeepIntact |= 1u << 3; + } + + if (Material.GetMultiIndex_Index2() == Material.GetMultiIndex_Index1()) + { + Strengths[1] += Strengths[2]; + Strengths[2] = 0; + ChannelsToKeepIntact |= 1u << 2; + } + else if (Material.GetMultiIndex_Index2() == Material.GetMultiIndex_Index0()) + { + Strengths[0] += Strengths[2]; + Strengths[2] = 0; + ChannelsToKeepIntact |= 1u << 2; + } + + if (Material.GetMultiIndex_Index1() == Material.GetMultiIndex_Index0()) + { + Strengths[0] += Strengths[1]; + Strengths[1] = 0; + ChannelsToKeepIntact |= 1u << 1; + } + } + + // Add locked channels to ChannelsToKeepIntact, + // and make TargetValue relative to the strength that's left to edit + { + float LockedChannelsStrength = 0.f; + for (uint8 LockedChannel : MultiIndex.LockedChannels) + { + if (Material.GetMultiIndex_Index0() == LockedChannel) + { + ChannelsToKeepIntact |= 1u << 0; + LockedChannelsStrength += Strengths[0]; + } + else if (Material.GetMultiIndex_Index1() == LockedChannel) + { + ChannelsToKeepIntact |= 1u << 1; + LockedChannelsStrength += Strengths[1]; + } + else if (Material.GetMultiIndex_Index2() == LockedChannel) + { + ChannelsToKeepIntact |= 1u << 2; + LockedChannelsStrength += Strengths[2]; + } + else if (Material.GetMultiIndex_Index3() == LockedChannel) + { + ChannelsToKeepIntact |= 1u << 3; + LockedChannelsStrength += Strengths[3]; + } + } + TargetValue *= 1.f - LockedChannelsStrength; + } + + int32 ChannelIndex; + if (MultiIndex.Channel == Material.GetMultiIndex_Index0()) + { + ChannelIndex = 0; + } + else if (MultiIndex.Channel == Material.GetMultiIndex_Index1()) + { + ChannelIndex = 1; + } + else if (MultiIndex.Channel == Material.GetMultiIndex_Index2()) + { + ChannelIndex = 2; + } + else if (MultiIndex.Channel == Material.GetMultiIndex_Index3()) + { + ChannelIndex = 3; + } + else + { + ChannelIndex = 0; + { + float MinStrength = Strengths[0]; + if (Strengths[1] < MinStrength) { MinStrength = Strengths[1]; ChannelIndex = 1; } + if (Strengths[2] < MinStrength) { MinStrength = Strengths[2]; ChannelIndex = 2; } + if (Strengths[3] < MinStrength) { ChannelIndex = 3; } + } + + Strengths[ChannelIndex] = 0.f; + + switch (ChannelIndex) + { + case 0: Material.SetMultiIndex_Index0(MultiIndex.Channel.Channel); break; + case 1: Material.SetMultiIndex_Index1(MultiIndex.Channel.Channel); break; + case 2: Material.SetMultiIndex_Index2(MultiIndex.Channel.Channel); break; + case 3: Material.SetMultiIndex_Index3(MultiIndex.Channel.Channel); break; + default: ensureVoxelSlow(false); + } + } + + // Do not modify the channel we are setting + ChannelsToKeepIntact |= 1u << ChannelIndex; + + Strengths[ChannelIndex] = FMath::Clamp(FMath::Lerp(Strengths[ChannelIndex], TargetValue, Strength), 0.f, 1.f); + + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(Strengths, ChannelsToKeepIntact); + + Material.SetMultiIndex_Blend0(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[0], Blend0)); + Material.SetMultiIndex_Blend1(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[1], Blend1)); + Material.SetMultiIndex_Blend2(FVoxelUtilities::FloatToUINT8_ForLerp(Alphas[2], Blend2)); + + break; + } + case EVoxelPaintMaterialType::MultiIndexWetness: + { + float TargetValue = MultiIndexWetness.TargetValue; + if (Strength < 0) + { + Strength = -Strength; + TargetValue = 1.f - TargetValue; + } + Material.SetMultiIndex_Wetness(FVoxelUtilities::LerpUINT8(Material.GetMultiIndex_Wetness(), FVoxelUtilities::FloatToUINT8(TargetValue), Strength)); + break; + } + case EVoxelPaintMaterialType::MultiIndexRaw: + { + TVoxelStaticArray Strengths; + Strengths[0] = MultiIndexRaw.Strength0; + Strengths[1] = MultiIndexRaw.Strength1; + Strengths[2] = MultiIndexRaw.Strength2; + Strengths[3] = MultiIndexRaw.Strength3; + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(Strengths); + + Material.SetMultiIndex_Blend0_AsFloat(Alphas[0]); + Material.SetMultiIndex_Blend1_AsFloat(Alphas[1]); + Material.SetMultiIndex_Blend2_AsFloat(Alphas[2]); + + Material.SetMultiIndex_Index0(MultiIndexRaw.Channel0.Channel); + Material.SetMultiIndex_Index1(MultiIndexRaw.Channel1.Channel); + Material.SetMultiIndex_Index2(MultiIndexRaw.Channel2.Channel); + Material.SetMultiIndex_Index3(MultiIndexRaw.Channel3.Channel); + + break; + } + case EVoxelPaintMaterialType::UV: + { + FVector2D TargetUV = UV.UV; + if (Strength < 0) + { + Strength = -Strength; + TargetUV = FVector2D(1, 1) - TargetUV; + } + if (UV.bPaintU) + { + Material.SetU(UV.Channel, FVoxelUtilities::LerpUINT8(Material.GetU(UV.Channel), FVoxelUtilities::FloatToUINT8(TargetUV.X), Strength)); + } + if (UV.bPaintV) + { + Material.SetV(UV.Channel, FVoxelUtilities::LerpUINT8(Material.GetV(UV.Channel), FVoxelUtilities::FloatToUINT8(TargetUV.Y), Strength)); + } + break; + } + default: ensure(false); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp new file mode 100644 index 00000000..b8437014 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp @@ -0,0 +1,426 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelPhysics.h" +#include "VoxelTools/VoxelPhysicsPartSpawner.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelDebug/VoxelDebugUtilities.h" + +#include "VoxelWorld.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" + +#include "Async/Async.h" +#include "DrawDebugHelpers.h" + +FORCEINLINE uint32 GetIndex(const FVoxelIntBox& Box, const FIntVector& Size, int32 X, int32 Y, int32 Z) +{ + checkVoxelSlow(Box.Contains(X, Y, Z)); + checkVoxelSlow(Box.Size() == Size); + return (X - Box.Min.X) + (Y - Box.Min.Y) * Size.X + (Z - Box.Min.Z) * Size.X * Size.Y; +} +FORCEINLINE uint32 GetIndex(const FVoxelIntBox& Box, const FIntVector& Size, const FIntVector& P) +{ + return GetIndex(Box, Size, P.X, P.Y, P.Z); +} + +template +inline void CreateParts( + TArray& Queue, + TArray& FloatingPoints, + TArray& Visited, + const TArray& Values, + const TArray& Materials, + const FVoxelIntBox& Bounds, + const FIntVector& Size, + T1 InitNewPart, + T2 AddVoxel, + T3 FinishPart) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + while (FloatingPoints.Num() > 0) + { + const FIntVector NewPartCenter = FloatingPoints.Pop(false); + const int32 NewPartCenterIndex = GetIndex(Bounds, Size, NewPartCenter); + if (Visited[NewPartCenterIndex]) + { + continue; + } + Visited[NewPartCenterIndex] = true; + Queue.Reset(); + + auto NewPart = InitNewPart(NewPartCenter); + + ensure(!Values[NewPartCenterIndex].IsEmpty()); + AddVoxel(NewPart, FIntVector(0), Values[NewPartCenterIndex], Materials[NewPartCenterIndex]); + Queue.Add(NewPartCenter); + + while (Queue.Num() > 0) + { + const FIntVector QueuePosition = Queue.Pop(false); + checkVoxelSlow(Bounds.Contains(QueuePosition)); + checkVoxelSlow(Visited[GetIndex(Bounds, Size, QueuePosition)] && !Values[GetIndex(Bounds, Size, QueuePosition)].IsEmpty()); + + const auto Lambda = [&](const int32 LocalX, const int32 LocalY, const int32 LocalZ) + { + const FIntVector Position(QueuePosition.X + LocalX, QueuePosition.Y + LocalY, QueuePosition.Z + LocalZ); + if (!Bounds.Contains(Position)) return; + + const int32 Index = GetIndex(Bounds, Size, Position); + if (Visited[Index]) return; + + Visited[Index] = true; + const auto Value = Values[Index]; + AddVoxel(NewPart, Position - NewPartCenter, Value, Materials[Index]); + + if (!Value.IsEmpty()) + { + Queue.Add(Position); + } + }; + Lambda(+0, +0, -1); + Lambda(+0, +0, +1); + Lambda(+0, -1, +0); + Lambda(+0, +1, +0); + Lambda(-1, +0, +0); + Lambda(+1, +0, +0); + } + + FinishPart(NewPart); + } +} + +void UVoxelPhysicsTools::RemoveFloatingParts( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const int32 MinParts, + const bool bCreateData, + const bool bCreateVoxels, + const bool bDebug, + const TWeakObjectPtr DebugWorld, + FVoxelRemoveFloatingPartsResult& OutResult) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + const FIntVector Size = Bounds.Size(); + + // TODO bit array + TArray Visited; + Visited.SetNumZeroed(Size.X * Size.Y * Size.Z); + + TArray FloatingPoints; + + TArray Queue; + Queue.Reserve(Size.X * Size.Y * Size.Z); + + TArray Values; + TArray Materials; + { + FVoxelPromotableReadScopeLock Lock(Data, Bounds, "SearchForFloatingBlocks"); + + Values = Data.GetValues(Bounds); + + // Fill Visited array + { + VOXEL_ASYNC_SCOPE_COUNTER("Fill Visited array"); + + // Add borders + for (int32 X = Bounds.Min.X; X < Bounds.Max.X; X++) + { + for (int32 Y = Bounds.Min.Y; Y < Bounds.Max.Y; Y++) + { + Queue.Emplace(X, Y, Bounds.Min.Z); + Queue.Emplace(X, Y, Bounds.Max.Z - 1); + } + for (int32 Z = Bounds.Min.Z; Z < Bounds.Max.Z; Z++) + { + Queue.Emplace(X, Bounds.Min.Y, Z); + Queue.Emplace(X, Bounds.Max.Y - 1, Z); + } + } + for (int32 Y = Bounds.Min.Y; Y < Bounds.Max.Y; Y++) + { + for (int32 Z = Bounds.Min.Z; Z < Bounds.Max.Z; Z++) + { + Queue.Emplace(Bounds.Min.X, Y, Z); + Queue.Emplace(Bounds.Max.X - 1, Y, Z); + } + } + + while (Queue.Num() > 0) + { + const FIntVector Position = Queue.Pop(false); + + if (Bounds.Contains(Position)) + { + const int32 Index = GetIndex(Bounds, Size, Position); + if (!Visited[Index] && !Values[Index].IsEmpty()) + { + Visited[Index] = true; + FVoxelUtilities::AddImmediateNeighborsToArray(Position, Queue); + } + } + } + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Remove not visited voxels"); + FVoxelMutableDataAccelerator OctreeAccelerator(Data, Bounds); + for (int32 Z = Bounds.Min.Z; Z < Bounds.Max.Z; Z++) + { + for (int32 Y = Bounds.Min.Y; Y < Bounds.Max.Y; Y++) + { + for (int32 X = Bounds.Min.X; X < Bounds.Max.X; X++) + { + const int32 Index = GetIndex(Bounds, Size, X, Y, Z); + + if (Visited[Index]) continue; + + if (Values[Index].IsEmpty()) continue; + + // Write lock is expensive, so we avoid calling it until we really have to + if (!Lock.IsPromoted()) + { + Lock.Promote(); + Materials.SetNumUninitialized(Size.X * Size.Y * Size.Z); + } + + Materials[Index] = OctreeAccelerator.GetMaterial(X, Y, Z, 0); + OctreeAccelerator.SetValue(X, Y, Z, FVoxelValue::Empty()); + + FloatingPoints.Emplace(X, Y, Z); + OutResult.BoxToUpdate += FIntVector(X, Y, Z); + } + } + } + } + } + + if (FloatingPoints.Num() < MinParts) + { + return; + } + + if (bDebug) + { + AsyncTask(ENamedThreads::GameThread, [FloatingPoints, Bounds, DebugWorld]() + { + if (!DebugWorld.IsValid()) return; + + UVoxelDebugUtilities::DrawDebugIntBox(DebugWorld.Get(), Bounds, 10); + + for (auto& Point : FloatingPoints) + { + DrawDebugPoint( + DebugWorld->GetWorld(), + DebugWorld->LocalToGlobal(Point), + 10, + FColor::Red, + false, + 10); + } + }); + } + + { + VOXEL_ASYNC_SCOPE_COUNTER("Create Parts"); + + // Need to find all connected points to every floating point to create different parts + + ensure(!(bCreateData && bCreateVoxels)); + + if (bCreateData) + { + const uint8 Depth = FVoxelUtilities::GetDepthFromSize(Size.GetMax()); + const auto Generator = MakeVoxelShared(1, Data.Generator); + + struct FNewPart + { + const FIntVector NewPartCenter; + const TVoxelSharedRef NewPartData; + TUniquePtr Lock; + TUniquePtr OctreeAccelerator; + + FNewPart( + const FIntVector& NewPartCenter, + uint8 InDepth, + const TVoxelSharedRef& InGenerator, + bool bEnableMultiplayer, + bool bEnableUndoRedo) + : NewPartCenter(NewPartCenter) + , NewPartData(FVoxelData::Create(FVoxelDataSettings(InDepth, InGenerator, bEnableMultiplayer, bEnableUndoRedo))) + , Lock(MakeUnique(*NewPartData, FVoxelIntBox::Infinite, STATIC_FNAME("FloatingBlocks"))) // Make checks happy + , OctreeAccelerator(MakeUnique(*NewPartData, FVoxelIntBox::Infinite)) + { + } + }; + + const auto InitNewPart = [Depth, &Generator, &Data](const FIntVector& NewPartCenter) + { + return FNewPart(NewPartCenter, Depth, Generator, Data.bEnableMultiplayer, Data.bEnableUndoRedo); + }; + const auto AddVoxel = [](FNewPart& NewPart, const FIntVector& Position, const FVoxelValue& Value, const FVoxelMaterial& Material) + { + if (Value.IsEmpty()) + { + // Just copy the value (to have a great looking part) + NewPart.OctreeAccelerator->SetValue(Position, Value); + } + else + { + // Copy the value & add it to the queue + NewPart.OctreeAccelerator->SetValue(Position, Value); + NewPart.OctreeAccelerator->SetMaterial(Position, Material); + } + }; + const auto FinishPart = [&OutResult](FNewPart& NewPart) + { + OutResult.Parts.Add({ NewPart.NewPartCenter, NewPart.NewPartData, {} }); + }; + + CreateParts(Queue, FloatingPoints, Visited, Values, Materials, Bounds, Size, InitNewPart, AddVoxel, FinishPart); + } + + if (bCreateVoxels) + { + const auto InitNewPart = [](const FIntVector& NewPartCenter) + { + return FVoxelFloatingPart{ NewPartCenter }; + }; + const auto AddVoxel = [](FVoxelFloatingPart& NewPart, const FIntVector& Position, const FVoxelValue& Value, const FVoxelMaterial& Material) + { + if (!Value.IsEmpty()) + { + NewPart.Voxels.Add({ Position, Value.ToFloat(), Material }); + } + }; + const auto FinishPart = [&OutResult](FVoxelFloatingPart& NewPart) + { + OutResult.Parts.Add(MoveTemp(NewPart)); + }; + + CreateParts(Queue, FloatingPoints, Visited, Values, Materials, Bounds, Size, InitNewPart, AddVoxel, FinishPart); + } + } +} + +TArray> UVoxelPhysicsTools::SpawnFloatingPartsAndUpdateWorld( + IVoxelPhysicsPartSpawner& PartSpawner, + AVoxelWorld* const World, + FVoxelRemoveFloatingPartsResult&& RemoveFloatingPartsResult) +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + TArray> Results; + + TArray> OnWorldUpdateDoneDelegates; + for (auto& NewPart : RemoveFloatingPartsResult.Parts) + { + TVoxelSharedPtr OnWorldUpdateDone; + const auto Result = PartSpawner.SpawnPart(OnWorldUpdateDone, World, MoveTemp(NewPart.Data), MoveTemp(NewPart.Voxels), NewPart.PartCenter); + if (Result) + { + Results.Add(Result); + } + if (OnWorldUpdateDone.IsValid()) + { + OnWorldUpdateDoneDelegates.Add(OnWorldUpdateDone); + } + } + + const auto Finish = [OnWorldUpdateDoneDelegates]() + { + for (auto& Delegate : OnWorldUpdateDoneDelegates) + { + if (ensure(Delegate.IsValid())) + { + Delegate->ExecuteIfBound(); + } + } + }; + + if (ensure(RemoveFloatingPartsResult.BoxToUpdate.IsValid())) + { + World->GetLODManager().UpdateBounds_OnAllFinished(RemoveFloatingPartsResult.BoxToUpdate.GetBox(), FSimpleDelegate::CreateLambda(Finish)); + } + + return Results; +} + +void UVoxelPhysicsTools::ApplyVoxelPhysics( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray>& OutResults, + AVoxelWorld* World, + FVoxelIntBox Bounds, + TScriptInterface PartSpawner, + int32 MinParts, + bool bDebug, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + const bool bCreateData = PartSpawner && PartSpawner->NeedData(); + const bool bCreateVoxels = PartSpawner && PartSpawner->NeedVoxels(); + + const auto PartSpawnerUnsafe = PartSpawner.GetObject(); + const auto PartSpawnerWeakPtr = MakeWeakObjectPtr(PartSpawner.GetObject()); + const auto VoxelWorldWeakPtr = MakeWeakObjectPtr(World); + + using FWork = TVoxelLatentActionAsyncWork_WithWorld_WithValue; + + FVoxelToolHelpers::StartAsyncLatentActionImpl( + WorldContextObject, + LatentInfo, + World, + FUNCTION_FNAME, + bHideLatentWarnings, + [&]() + { + return new FWork( + FUNCTION_FNAME, + World, + [=](FVoxelData& Data, FVoxelRemoveFloatingPartsResult& Result) + { + RemoveFloatingParts( + Data, + Bounds, + MinParts, + bCreateData, + bCreateVoxels, + bDebug, + VoxelWorldWeakPtr, + Result); + }); + }, + [=, WeakWorldContextObject = MakeWeakObjectPtr(WorldContextObject), &OutResults] + (FWork& Work) + { + if (!VoxelWorldWeakPtr.IsValid()) return; + + if (PartSpawnerUnsafe != PartSpawnerWeakPtr.Get()) + { + ensure(!PartSpawnerWeakPtr.IsValid()); + FVoxelMessages::Error(FUNCTION_ERROR("Part spawner was deleted by garbage collection before the async function ended. To avoid this, store a ref to the spawner in your BP")); + return; + } + + TArray> Results; + + auto* IPartSpawner = Cast(PartSpawnerWeakPtr.Get()); + if (PartSpawnerWeakPtr.IsValid() && ensure(IPartSpawner) && Work.Value.Parts.Num() > 0) + { + Results = SpawnFloatingPartsAndUpdateWorld(*IPartSpawner, VoxelWorldWeakPtr.Get(), MoveTemp(Work.Value)); + } + + if (WeakWorldContextObject.IsValid()) + { + OutResults = MoveTemp(Results); + } + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp new file mode 100644 index 00000000..792380a2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp @@ -0,0 +1,170 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelPhysicsPartSpawner.h" +#include "VoxelData/VoxelDataUtilities.h" +#include "VoxelWorld.h" +#include "VoxelWorldRootComponent.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" + +#include "Engine/StaticMeshActor.h" +#include "Engine/StaticMesh.h" +#include "UObject/ConstructorHelpers.h" +#include "Components/StaticMeshComponent.h" +#include "Materials/MaterialInstanceDynamic.h" + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TScriptInterface UVoxelPhysicsPartSpawner_VoxelWorlds::SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) +{ + check(Data.IsValid()); + + UWorld* GameWorld = World->GetWorld(); + + FActorSpawnParameters ActorSpawnParameters; + ActorSpawnParameters.Owner = World; + ActorSpawnParameters.bDeferConstruction = true; + + UClass* Class = VoxelWorldClass == nullptr ? AVoxelWorld::StaticClass() : VoxelWorldClass.Get(); + FTransform Transform = World->GetTransform(); + Transform.SetLocation(World->LocalToGlobal(PartPosition)); + AVoxelWorld* NewWorld = GameWorld->SpawnActor( + Class, + Transform, + ActorSpawnParameters); + if (!ensure(NewWorld)) return nullptr; + + // Can't use the world as a template: this would require the new world to have the same class, which we really don't want if the world is eg a BP + for (TFieldIterator It(AVoxelWorld::StaticClass(), EFieldIteratorFlags::ExcludeSuper); It; ++It) + { + auto* Property = *It; + const FName Name = Property->GetFName(); + if (!Property->HasAnyPropertyFlags(EPropertyFlags::CPF_Transient) && + Name != STATIC_FNAME("WorldRoot") && + Name != STATIC_FNAME("OnWorldLoaded") && + Name != STATIC_FNAME("OnWorldDestroyed")) + { + Property->CopyCompleteValue(Property->ContainerPtrToValuePtr(NewWorld), Property->ContainerPtrToValuePtr(World)); + } + } + NewWorld->bCreateWorldAutomatically = false; + NewWorld->FinishSpawning(Transform); + + NewWorld->GetWorldRoot().BodyInstance.bSimulatePhysics = true; + NewWorld->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseSimpleAndComplex; + + ConfigureVoxelWorld.ExecuteIfBound(NewWorld); + + if (!ensure(!NewWorld->IsCreated())) return nullptr; + + NewWorld->bCreateWorldAutomatically = false; + NewWorld->SetRenderOctreeDepth(FVoxelUtilities::ConvertDepth(Data->Depth)); + NewWorld->bEnableUndoRedo = Data->bEnableUndoRedo; + NewWorld->bEnableMultiplayer = Data->bEnableMultiplayer; + NewWorld->bCreateGlobalPool = false; + + OutOnWorldUpdateDone = MakeVoxelShared(); + OutOnWorldUpdateDone->BindLambda([VoxelWorld = TWeakObjectPtr(NewWorld), Data] + { + if (VoxelWorld.IsValid() && ensure(!VoxelWorld->IsCreated())) + { + FVoxelWorldCreateInfo Info; + Info.bOverrideData = true; + Info.DataOverride_Raw = Data; + VoxelWorld->CreateWorld(Info); + } + }); + + auto* Result = NewObject(); + Result->VoxelWorld = NewWorld; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelPhysicsPartSpawner_Cubes::UVoxelPhysicsPartSpawner_Cubes() +{ + static ConstructorHelpers::FObjectFinder MeshFinder(TEXT("/Engine/BasicShapes/Cube")); + CubeMesh = MeshFinder.Object; + + Material = FVoxelExampleUtilities::LoadExampleObject(TEXT("Material'/Voxel/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.M_VoxelMaterial_Colors_Parameter'")); +} + +TScriptInterface UVoxelPhysicsPartSpawner_Cubes::SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) +{ + auto* Result = NewObject(); + + const FRotator Rotation = FRotator(World->GetTransform().GetRotation()); + for (const auto& Voxel : Voxels) + { + if (SpawnProbability < 1.f && FMath::FRand() > SpawnProbability) + { + continue; + } + + auto* StaticMeshActor = GetWorld()->SpawnActor( + World->LocalToGlobal(Voxel.Position + PartPosition), + Rotation); + if (!ensure(StaticMeshActor)) continue; + + StaticMeshActor->SetActorScale3D(FVector(World->VoxelSize / 100)); + StaticMeshActor->SetMobility(EComponentMobility::Movable); + + UStaticMeshComponent* StaticMeshComponent = StaticMeshActor->GetStaticMeshComponent(); + if (!ensure(StaticMeshComponent)) continue; + + StaticMeshComponent->SetStaticMesh(CubeMesh); + StaticMeshComponent->SetSimulatePhysics(false); // False until world is updated + { + auto* Instance = UMaterialInstanceDynamic::Create(Material, StaticMeshActor); + Instance->SetVectorParameterValue(STATIC_FNAME("VertexColor"), Voxel.Material.GetLinearColor()); + StaticMeshComponent->SetMaterial(0, Instance); + } + Result->Cubes.Add(StaticMeshActor); + } + + OutOnWorldUpdateDone = MakeVoxelShared(); + OutOnWorldUpdateDone->BindLambda([Cubes = TArray>(Result->Cubes)] + { + for (auto& Cube : Cubes) + { + if (Cube.IsValid()) + { + UStaticMeshComponent* StaticMeshComponent = Cube->GetStaticMeshComponent(); + if (!ensure(StaticMeshComponent)) continue; + StaticMeshComponent->SetSimulatePhysics(true); + } + } + }); + + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TScriptInterface UVoxelPhysicsPartSpawner_GetVoxels::SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& InVoxels, + const FIntVector& PartPosition) +{ + auto* Result = NewObject(); + Result->Voxels = MoveTemp(InVoxels); + return Result; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelProjectionTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelProjectionTools.cpp new file mode 100644 index 00000000..46416a8b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelProjectionTools.cpp @@ -0,0 +1,469 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelProjectionTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelData/VoxelDataAccelerator.h" + +#include "DrawDebugHelpers.h" +#include "Engine/Engine.h" + +struct FHitsBuilder +{ + struct FPlanePosition + { + FVector2D PlanePosition; + float DistanceSquared; + FHitResult Hit; + }; + TMap PlanePositions; + + TArray GetHits() const + { + TArray Result; + Result.Reserve(PlanePositions.Num()); + for (auto& It : PlanePositions) + { + Result.Add(FVoxelProjectionHit{ It.Key, It.Value.PlanePosition, It.Value.Hit }); + } + return Result; + } + + inline void Add(AVoxelWorld* World, const FHitResult& Hit, const FVector2D& PlanePosition) + { + const FVoxelVector LocalPosition = World->GlobalToLocalFloat(Hit.ImpactPoint); + for (auto& Point : FVoxelUtilities::GetNeighbors(LocalPosition)) + { + const float DistanceSquared = (Point - LocalPosition).SizeSquared(); + auto* const Existing = PlanePositions.Find(Point); + if (Existing) + { + if (Existing->DistanceSquared > DistanceSquared) + { + Existing->PlanePosition = PlanePosition; + Existing->DistanceSquared = DistanceSquared; + Existing->Hit = Hit; + } + } + else + { + PlanePositions.Add(Point, { PlanePosition, DistanceSquared, Hit }); + } + } + } +}; + +class FAsyncLinetracesLatentAction : public FPendingLatentAction +{ +public: + const FName ExecutionFunction; + const int32 OutputLink; + const FWeakObjectPtr CallbackTarget; + TArray* const OutHits; + const TWeakObjectPtr VoxelWorld; + const TWeakObjectPtr World; + const FVoxelLineTraceParameters Parameters; + + FHitsBuilder Builder; + uint32 NumTraces = 0; + uint32 NumCompletedTraces = 0; + + struct FLocalTraceData + { + FVector Start; + FVector End; + FVector2D PlanePosition; + }; + TMap TracesLocalData; + + struct FManualWeakRef + { + FAsyncLinetracesLatentAction& Ptr; + + void TraceDone(const FTraceHandle& TraceHandle, FTraceDatum& TraceData) + { + Ptr.TraceDone(TraceHandle, TraceData); + } + }; + const TSharedRef WeakRef = MakeShareable(new FManualWeakRef{ *this }); + + template + FAsyncLinetracesLatentAction( + const FLatentActionInfo& LatentInfo, + TArray* OutHits, + AVoxelWorld* VoxelWorld, + FVoxelLineTraceParameters Parameters, + T GenerateRaysLambda) + : ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + , OutHits(OutHits) + , VoxelWorld(VoxelWorld) + , World(VoxelWorld->GetWorld()) + , Parameters(Parameters) + { + check(VoxelWorld); + + const FCollisionQueryParams Params = Parameters.GetParams(); + const FCollisionResponseContainer ResponseContainer = Parameters.GetResponseContainer(); + + FTraceDelegate TraceDelegate; + TraceDelegate.BindSP(WeakRef, &FManualWeakRef::TraceDone); + + GenerateRaysLambda([&](const FVector& Start, const FVector& End, const FVector2D& Position) + { + VOXEL_SCOPE_COUNTER("Start Async Linetrace"); + const FTraceHandle Handle = World->AsyncLineTraceByChannel( + EAsyncTraceType::Single, + Start, + End, + Parameters.CollisionChannel, + Params, + ResponseContainer, + &TraceDelegate); + TracesLocalData.Add(Handle, { Start, End, Position }); + NumTraces++; + }); + } + + virtual void UpdateOperation(FLatentResponse& Response) override + { + const bool bFinished = NumCompletedTraces == NumTraces || !VoxelWorld.IsValid(); + if (bFinished) + { + *OutHits = Builder.GetHits(); + } + Response.FinishAndTriggerIf(bFinished, ExecutionFunction, OutputLink, CallbackTarget); + } + +#if WITH_EDITOR + // Returns a human readable description of the latent operation's current state + virtual FString GetDescription() const override + { + return FString::Printf(TEXT("Trace %d/%d"), NumCompletedTraces, NumTraces); + } +#endif + + void TraceDone(const FTraceHandle& TraceHandle, FTraceDatum& TraceData) + { + VOXEL_SCOPE_COUNTER("Trace Done"); + + NumCompletedTraces++; + + ensure(TraceData.OutHits.Num() <= 1); + + if (!VoxelWorld.IsValid()) + { + return; + } + + const auto LocalData = TracesLocalData.FindChecked(TraceHandle); + + bool bHit = false; + FHitResult OutHit; + for (auto& Hit : TraceData.OutHits) + { + if (Hit.Actor == VoxelWorld) + { + bHit = true; + OutHit = Hit; + Builder.Add(VoxelWorld.Get(), Hit, LocalData.PlanePosition); + break; + } + } + + if (ensure(World.IsValid())) + { + Parameters.DrawDebug(World.Get(), LocalData.Start, LocalData.End, bHit, OutHit); + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FCollisionQueryParams FVoxelLineTraceParameters::GetParams() const +{ + FCollisionQueryParams Params(STATIC_FNAME("FindProjectionVoxels"), SCENE_QUERY_STAT_ONLY(FindProjectionVoxels), true); + Params.AddIgnoredActors(ActorsToIgnore); + return Params; +} + +FCollisionResponseContainer FVoxelLineTraceParameters::GetResponseContainer() const +{ + FCollisionResponseContainer ResponseContainer; + for (auto& CollisionChannelToIgnore : CollisionChannelsToIgnore) + { + ResponseContainer.SetResponse(CollisionChannelToIgnore, ECollisionResponse::ECR_Ignore); + } + return ResponseContainer; +} + +void FVoxelLineTraceParameters::DrawDebug(const UWorld* World, const FVector& Start, const FVector& End, bool bHit, const FHitResult& OutHit) const +{ +#if ENABLE_DRAW_DEBUG + if (DrawDebugType != EDrawDebugTrace::None) + { + bool bPersistent = DrawDebugType == EDrawDebugTrace::Persistent; + float LifeTime = (DrawDebugType == EDrawDebugTrace::ForDuration) ? DrawTime : 0.f; + + // @fixme, draw line with thickness = 2.f? + if (bHit && OutHit.bBlockingHit) + { + // Red up to the blocking hit, green thereafter + DrawDebugLine(World, Start, OutHit.ImpactPoint, TraceColor.ToFColor(true), bPersistent, LifeTime); + DrawDebugLine(World, OutHit.ImpactPoint, End, TraceHitColor.ToFColor(true), bPersistent, LifeTime); + static const float KISMET_TRACE_DEBUG_IMPACTPOINT_SIZE = 16.f; + DrawDebugPoint(World, OutHit.ImpactPoint, KISMET_TRACE_DEBUG_IMPACTPOINT_SIZE, TraceColor.ToFColor(true), bPersistent, LifeTime); + } + else + { + // no hit means all red + DrawDebugLine(World, Start, End, TraceColor.ToFColor(true), bPersistent, LifeTime); + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelLineTraceParameters UVoxelProjectionTools::MakeVoxelLineTraceParameters( + TArray> CollisionChannelsToIgnore, + TArray ActorsToIgnore, + TEnumAsByte CollisionChannel, + TEnumAsByte DrawDebugType, + FLinearColor TraceColor, + FLinearColor TraceHitColor, + float DrawTime) +{ + return + { + CollisionChannel, + CollisionChannelsToIgnore, + ActorsToIgnore, + DrawDebugType, + TraceColor, + TraceHitColor, + DrawTime + }; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelProjectionTools::FindProjectionVoxels( + TArray& Hits, + AVoxelWorld* World, + FVoxelLineTraceParameters Parameters, + FVector Position, + FVector Direction, + float Radius, + EVoxelProjectionShape Shape, + float NumRays, + float MaxDistance) +{ + VOXEL_FUNCTION_COUNTER(); + + Hits.Reset(); + + CHECK_VOXELWORLD_IS_CREATED(); + + if (!Direction.Normalize()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Direction!")); + return 0; + } + + UWorld* const WorldPtr = World->GetWorld(); + const FCollisionQueryParams Params = Parameters.GetParams(); + const FCollisionResponseContainer ResponseContainer = Parameters.GetResponseContainer(); + FHitsBuilder Builder; + + const auto Lambda = [&](const FVector& Start, const FVector& End, const FVector2D& PlanePosition) + { + VOXEL_SCOPE_COUNTER("Linetrace"); + FHitResult OutHit; + const bool bHit = WorldPtr->LineTraceSingleByChannel( + OutHit, + Start, + End, + Parameters.CollisionChannel, + Params, + ResponseContainer); + Parameters.DrawDebug(WorldPtr, Start, End, bHit, OutHit); + if (bHit) + { + Builder.Add(World, OutHit, PlanePosition); + } + }; + + const int32 NumTraced = GenerateRays(Position, Direction, Radius, Shape, NumRays, MaxDistance, Lambda); + + Hits = Builder.GetHits(); + + return NumTraced; +} + +int32 UVoxelProjectionTools::FindProjectionVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& Hits, + AVoxelWorld* World, + FVoxelLineTraceParameters Parameters, + FVector Position, + FVector Direction, + float Radius, + EVoxelProjectionShape Shape, + float NumRays, + float MaxDistance, + bool bHideLatentWarnings) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + if (!Direction.Normalize()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Direction!")); + return 0; + } + + int32 NumTraced = 0; + const auto Lambda = [&]() + { + return new FAsyncLinetracesLatentAction(LatentInfo, &Hits, World, Parameters, [&](auto In) + { + NumTraced = GenerateRays(Position, Direction, Radius, Shape, NumRays, MaxDistance, In); + }); + }; + FVoxelToolHelpers::StartLatentAction( + WorldContextObject, + LatentInfo, + FUNCTION_FNAME, + bHideLatentWarnings, + Lambda); + + return NumTraced; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TArray UVoxelProjectionTools::GetHitsPositions(const TArray& Hits) +{ + VOXEL_FUNCTION_COUNTER(); + + TArray Voxels; + Voxels.Reserve(Hits.Num()); + for (auto& Hit : Hits) + { + Voxels.Add(Hit.VoxelPosition); + } + return Voxels; +} + +FVector UVoxelProjectionTools::GetHitsAverageNormal(const TArray& Hits) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Hits.Num() == 0) + { + return FVector::UpVector; + } + FVector N = Hits[0].Hit.ImpactNormal; + for (int32 Index = 1; Index < Hits.Num(); ++Index) + { + N += Hits[Index].Hit.ImpactNormal; + } + if (!ensure(!FMath::IsNaN(N.X + N.Y + N.Z))) + { + return FVector::UpVector; + } + return N.GetSafeNormal(); +} + +FVector UVoxelProjectionTools::GetHitsAveragePosition(const TArray& Hits) +{ + VOXEL_FUNCTION_COUNTER(); + + if (Hits.Num() == 0) + { + return FVector::ZeroVector; + } + FVector Position = Hits[0].Hit.ImpactPoint; + for (int32 Index = 1; Index < Hits.Num(); ++Index) + { + Position += Hits[Index].Hit.ImpactPoint; + } + if (!ensure(!FMath::IsNaN(Position.X + Position.Y + Position.Z))) + { + return FVector::UpVector; + } + return Position / Hits.Num(); +} + +FVoxelSurfaceEditsVoxels UVoxelProjectionTools::CreateSurfaceVoxelsFromHits(const TArray& Hits) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Hits.Num()); + + TArray Voxels; + Voxels.Reserve(Hits.Num()); + for (auto& Hit : Hits) + { + FVoxelSurfaceEditsVoxelBase Voxel; + Voxel.Position = Hit.VoxelPosition; + Voxel.Normal = Hit.Hit.Normal; + Voxels.Add(Voxel); + } + + FVoxelSurfaceEditsVoxels EditsVoxels; + EditsVoxels.Info.bHasNormals = true; + + EditsVoxels.Voxels = MakeVoxelSharedCopy(MoveTemp(Voxels)); + + return EditsVoxels; +} + +FVoxelSurfaceEditsVoxels UVoxelProjectionTools::CreateSurfaceVoxelsFromHitsWithExactValues(AVoxelWorld* World, const TArray& Hits) +{ + CHECK_VOXELWORLD_IS_CREATED(); + VOXEL_TOOL_FUNCTION_COUNTER(Hits.Num()); + + if (Hits.Num() == 0) + { + return {}; + } + + TArray Voxels; + Voxels.Reserve(Hits.Num()); + + FVoxelIntBox Bounds = FVoxelIntBox(Hits[0].VoxelPosition); + for (int32 Index = 1; Index < Hits.Num(); Index++) + { + Bounds = Bounds + Hits[Index].VoxelPosition; + } + + auto& Data = World->GetData(); + FVoxelReadScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + const FVoxelConstDataAccelerator Accelerator(Data, Bounds); + + for (auto& Hit : Hits) + { + FVoxelSurfaceEditsVoxelBase Voxel; + Voxel.Position = Hit.VoxelPosition; + Voxel.Normal = Hit.Hit.Normal; + Voxel.Value = Accelerator.GetValue(Hit.VoxelPosition, 0).ToFloat(); + Voxels.Add(Voxel); + } + + FVoxelSurfaceEditsVoxels EditsVoxels; + EditsVoxels.Info.bHasValues = true; + EditsVoxels.Info.bHasNormals = true; + + EditsVoxels.Voxels = MakeVoxelSharedCopy(MoveTemp(Voxels)); + + return EditsVoxels; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp new file mode 100644 index 00000000..99a4abcd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp @@ -0,0 +1,575 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelTools/VoxelHardnessHandler.h" +#include "VoxelTools/VoxelSurfaceToolsImpl.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelUtilities/VoxelRichCurveUtilities.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" +#include "VoxelDirection.h" + +#include "Curves/CurveFloat.h" +#include "Async/ParallelFor.h" +#include "DrawDebugHelpers.h" + +bool FVoxelSurfaceEditsStack::HasErrors(const FVoxelSurfaceEditsVoxels& Voxels, FString& OutErrors) const +{ + OutErrors.Reset(); + + for (const auto& Element : Stack) + { + if ((Element.Flags & EVoxelSurfaceEditsStackElementFlags::NeedValues) && !Voxels.Info.bHasValues) + { + OutErrors += FString::Printf(TEXT("%s needs values to be computed!\n"), *Element.Name); + } + + if ((Element.Flags & EVoxelSurfaceEditsStackElementFlags::NeedNormals) && !Voxels.Info.bHasNormals) + { + OutErrors += FString::Printf(TEXT("%s needs normals to be computed!\n"), *Element.Name); + } + + if ((Element.Flags & EVoxelSurfaceEditsStackElementFlags::ShouldBeLast) && &Element != &Stack.Last()) + { + OutErrors += FString::Printf(TEXT("%s needs to be the last element of the stack!\n"), *Element.Name); + } + } + + return !OutErrors.IsEmpty(); +} + +FVoxelSurfaceEditsProcessedVoxels FVoxelSurfaceEditsStack::Execute(const FVoxelSurfaceEditsVoxels& Voxels, bool bComputeBounds) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + auto ProcessedVoxels = TArray(*Voxels.Voxels); + for (const auto& Element : Stack) + { + Element.Apply(Voxels.Info, ProcessedVoxels); + } + + FVoxelSurfaceEditsProcessedVoxels Result; + + if (ProcessedVoxels.Num() > 0 && bComputeBounds) + { + VOXEL_ASYNC_SCOPE_COUNTER("ComputeBounds"); + + Result.Bounds = FVoxelIntBox(ProcessedVoxels[0].Position); + for (auto& Voxel : ProcessedVoxels) + { + Result.Bounds = Result.Bounds + Voxel.Position; + } + } + + Result.Info = Voxels.Info; + Result.Voxels = MakeVoxelSharedCopy(MoveTemp(ProcessedVoxels)); + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FVoxelSurfaceEditsVoxels UVoxelSurfaceTools::FindSurfaceVoxelsImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bComputeNormals_Dynamic, + bool bOnlyOutputNonEmptyVoxels_Dynamic) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + const FIntVector Size = Bounds.Size(); + + const TArray Values = Data.GetValues(Bounds); + + const auto GetValue = [&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(0 <= X && X < Size.X); + checkVoxelSlow(0 <= Y && Y < Size.Y); + checkVoxelSlow(0 <= Z && Z < Size.Z); + const int32 Index = X + Y * Size.X + Z * Size.X * Size.Y; + return Values.GetData()[Index]; + }; + + TArray OutVoxels; + OutVoxels.Reserve(Bounds.Count()); + + FVoxelUtilities::StaticBranch(bComputeNormals_Dynamic, bOnlyOutputNonEmptyVoxels_Dynamic, [&](auto bComputeNormals_Static, auto bOnlyOutputNonEmptyVoxels_Static) + { + for (int32 X = 1; X < Size.X - 1; X++) + { + for (int32 Y = 1; Y < Size.Y - 1; Y++) + { + for (int32 Z = 1; Z < Size.Z - 1; Z++) + { + const FVoxelValue Value = GetValue(X, Y, Z); + if (bOnlyOutputNonEmptyVoxels_Static && Value.IsEmpty()) + { + continue; + } + + bool bAdd = false; + const auto GetOtherValue = [&](uint8 Direction, int32 DX, int32 DY, int32 DZ) + { + const FVoxelValue OtherValue = GetValue(X + DX, Y + DY, Z + DZ); + if (!DirectionMask || (!Value.IsEmpty() && (Direction & DirectionMask))) + { + bAdd |= Value.IsEmpty() != OtherValue.IsEmpty(); + } + return OtherValue.ToFloat(); + }; + + // Note: if bComputeNormals = false, could be faster by not computing other values once bAdd = true + const float GradientX = GetOtherValue(EVoxelDirectionFlag::XMax, 1, 0, 0) - GetOtherValue(EVoxelDirectionFlag::XMin, -1, 0, 0); + const float GradientY = GetOtherValue(EVoxelDirectionFlag::YMax, 0, 1, 0) - GetOtherValue(EVoxelDirectionFlag::YMin, 0, -1, 0); + const float GradientZ = GetOtherValue(EVoxelDirectionFlag::ZMax, 0, 0, 1) - GetOtherValue(EVoxelDirectionFlag::ZMin, 0, 0, -1); + + if (bAdd) + { + FVector Normal; + if (bComputeNormals_Static) + { + Normal = FVector(GradientX, GradientY, GradientZ).GetSafeNormal(); + } + else + { + Normal = FVector(ForceInit); + } + OutVoxels.Add({ Bounds.Min + FIntVector(X, Y, Z), Normal, Value.ToFloat() }); + } + } + } + } + }); + + FVoxelSurfaceEditsVoxels EditsVoxels; + EditsVoxels.Info.bHasValues = true; + EditsVoxels.Info.bHasNormals = bComputeNormals_Dynamic; + + EditsVoxels.Voxels = MakeVoxelSharedCopy(MoveTemp(OutVoxels)); + + return EditsVoxels; +} + +template VOXEL_API FVoxelSurfaceEditsVoxels UVoxelSurfaceTools::FindSurfaceVoxelsImpl<0>(FVoxelData&, const FVoxelIntBox&, bool, bool); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSurfaceEditsVoxels UVoxelSurfaceTools::FindSurfaceVoxelsFromDistanceFieldImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const bool bMultiThreaded, + const EVoxelComputeDevice ComputeDevice) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + // Else ParallelFor might crash + if (!Bounds.IsValid()) + { + return {}; + } + + const FIntVector Size = Bounds.Size(); + + const TArray Values = Data.ParallelGet(Bounds.Extend(1), !bMultiThreaded); + + TArray Distances; + TArray SurfacePositions; + FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(Size, Values, Distances, SurfacePositions); + FVoxelDistanceFieldUtilities::JumpFlood(Size, SurfacePositions, ComputeDevice); + FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(Size, SurfacePositions, Distances); + + VOXEL_ASYNC_SCOPE_COUNTER("Create OutVoxels"); + TArray OutVoxels; + OutVoxels.Empty(Bounds.Count()); + OutVoxels.SetNumUninitialized(Bounds.Count()); + + ParallelFor(Size.X, [&](int32 X) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + const int32 Index = FVoxelUtilities::Get3DIndex(Size, X, Y, Z); + + FVoxelSurfaceEditsVoxelBase Voxel; + Voxel.Position = Bounds.Min + FIntVector(X, Y, Z); + Voxel.Normal = FVector(ForceInit); + Voxel.Value = FVoxelUtilities::Get(Distances, Index); + Voxel.SurfacePosition = FVector(Bounds.Min) + FVoxelUtilities::Get(SurfacePositions, Index); + + FVoxelUtilities::Get(OutVoxels, Index) = Voxel; + } + } + }, !bMultiThreaded); + + FVoxelSurfaceEditsVoxels EditsVoxels; + EditsVoxels.Info.bHasValues = true; + EditsVoxels.Info.bHasExactDistanceField = true; + EditsVoxels.Info.bHasSurfacePositions = true; + + EditsVoxels.Voxels = MakeVoxelSharedCopy(MoveTemp(OutVoxels)); + + return EditsVoxels; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSurfaceEditsVoxels UVoxelSurfaceTools::FindSurfaceVoxels2DImpl(FVoxelData& Data, const FVoxelIntBox& Bounds, bool bComputeNormals) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + FVoxelSurfaceEditsVoxels EditsVoxels = FindSurfaceVoxelsImpl(Data, Bounds, bComputeNormals); + + TMap Columns; + Columns.Reserve(Bounds.Size().X * Bounds.Size().Y); + + for (const auto& Voxel : *EditsVoxels.Voxels) + { + const FIntPoint Point(Voxel.Position.X, Voxel.Position.Y); + if (auto* Existing = Columns.Find(Point)) + { + if (Existing->Position.Z < Voxel.Position.Z) + { + *Existing = Voxel; + } + } + else + { + Columns.Add(Point, Voxel); + } + } + + TArray OutVoxels; + Columns.GenerateValueArray(OutVoxels); + + EditsVoxels.Voxels = MakeVoxelSharedCopy(MoveTemp(OutVoxels)); + EditsVoxels.Info.bIs2D = true; + + return EditsVoxels; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceTools::FindSurfaceVoxels( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals) +{ + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, NO_PREFIX, Voxels = FindSurfaceVoxelsImpl(Data, Bounds, bComputeNormals)); +} + +void UVoxelSurfaceTools::FindSurfaceVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Voxels, Read, DoNotUpdateRender, NO_PREFIX, InVoxels = FindSurfaceVoxelsImpl(Data, Bounds, bComputeNormals)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceTools::FindSurfaceVoxelsFromDistanceField( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bMultiThreaded, + EVoxelComputeDevice ComputeDevice) +{ + // TODO compute normals + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, NO_PREFIX, Voxels = FindSurfaceVoxelsFromDistanceFieldImpl(Data, Bounds, bMultiThreaded, ComputeDevice)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceTools::FindSurfaceVoxels2D( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals) +{ + VOXEL_TOOL_HELPER(Read, DoNotUpdateRender, NO_PREFIX, Voxels = FindSurfaceVoxels2DImpl(Data, Bounds, bComputeNormals)); +} + +void UVoxelSurfaceTools::FindSurfaceVoxels2DAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals, + bool bHideLatentWarnings) +{ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(Voxels, Read, DoNotUpdateRender, NO_PREFIX, InVoxels = FindSurfaceVoxels2DImpl(Data, Bounds, bComputeNormals)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSurfaceEditsStack UVoxelSurfaceTools::AddToStack(FVoxelSurfaceEditsStack Stack, FVoxelSurfaceEditsStackElement Element) +{ + Stack.Add(Element); + return Stack; +} + +FVoxelSurfaceEditsProcessedVoxels UVoxelSurfaceTools::ApplyStack(FVoxelSurfaceEditsVoxels Voxels, FVoxelSurfaceEditsStack Stack) +{ + FString Error; + if (Stack.HasErrors(Voxels, Error)) + { + FVoxelMessages::Warning(FString::Printf(TEXT("ApplyStack: %s"), *Error)); + } + return Stack.Execute(Voxels); +} + +void UVoxelSurfaceTools::ApplyStackAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + FVoxelSurfaceEditsVoxels Voxels, + FVoxelSurfaceEditsStack Stack, + bool bHideLatentWarnings) +{ + FString Error; + if (Stack.HasErrors(Voxels, Error)) + { + FVoxelMessages::Warning(FString::Printf(TEXT("ApplyStackAsync: %s"), *Error)); + } + + FVoxelToolHelpers::StartAsyncLatentAction_WithoutWorld_WithValue( + WorldContextObject, + LatentInfo, + FUNCTION_FNAME, + bHideLatentWarnings, + ProcessedVoxels, + [=](FVoxelSurfaceEditsProcessedVoxels& InProcessedVoxels) + { + InProcessedVoxels = Stack.Execute(Voxels); + }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyConstantStrength(float Strength) +{ + return + { + "ApplyConstantStrength", + EVoxelSurfaceEditsStackElementFlags::None, + [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyConstantStrengthImpl(Voxels, Strength); + } + }; +} + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyStrengthCurve( + AVoxelWorld* World, + FVector InCenter, + float InRadius, + UCurveFloat* StrengthCurve, + bool bConvertToVoxelSpace) +{ + CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE(); + + if (!StrengthCurve) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid Strength Curve!")); + return {}; + } + + const FVoxelVector Center = GET_VOXEL_TOOL_REAL(InCenter); + const float Radius = GET_VOXEL_TOOL_REAL(InRadius); + + return + { + "ApplyStrengthCurve", + EVoxelSurfaceEditsStackElementFlags::None, + [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyDistanceStrengthFunctionImpl(Voxels, Center, Info.bIs2D, [=](float Distance) + { + return FVoxelRichCurveUtilities::Eval(StrengthCurve->FloatCurve, Distance / Radius); + }); + } + }; +} + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyFalloff( + AVoxelWorld* World, + EVoxelFalloff FalloffType, + FVector InCenter, + float InRadius, + float Falloff, + bool bConvertToVoxelSpace) +{ + CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE(); + + const FVoxelVector Center = GET_VOXEL_TOOL_REAL(InCenter); + const float Radius = GET_VOXEL_TOOL_REAL(InRadius); + + return + { + "ApplyFalloff", + EVoxelSurfaceEditsStackElementFlags::None, + FVoxelUtilities::DispatchFalloff(FalloffType, Radius, Falloff, [&](auto GetFalloff) -> FVoxelSurfaceEditsStackElement::FApply + { + return [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyDistanceStrengthFunctionImpl(Voxels, Center, Info.bIs2D, GetFalloff); + }; + }) + }; +} + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyStrengthMask( + AVoxelWorld* World, + FVoxelFloatTexture Mask, + FVector InEditPosition, + float ScaleX, + float ScaleY, + FVector PlaneNormal, + FVector PlaneTangent, + EVoxelSamplerMode SamplerMode, + bool bConvertToVoxelSpace) +{ + CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE(); + + const FVoxelVector EditPosition = GET_VOXEL_TOOL_REAL(InEditPosition); + + FVector OutPlaneX, OutPlaneY; + FVoxelSurfaceToolsImpl::GetStrengthMaskBasisImpl(PlaneNormal, PlaneTangent, OutPlaneX, OutPlaneY); + + return + { + "ApplyStrengthMask", + EVoxelSurfaceEditsStackElementFlags::None, + [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyStrengthMaskImpl(Info, Voxels, + Mask.Texture, + EditPosition, + ScaleX, + ScaleY, + OutPlaneX, + OutPlaneY, + SamplerMode); + } + }; +} + +void UVoxelSurfaceTools::GetStrengthMaskScale( + float& ScaleX, + float& ScaleY, + AVoxelWorld* World, + FVoxelFloatTexture Mask, + float SizeX, + float SizeY, + bool bConvertToVoxelSpace) +{ + CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE_VOID(); + + SizeX = GET_VOXEL_TOOL_REAL(SizeX); + SizeY = GET_VOXEL_TOOL_REAL(SizeY); + + ScaleX = SizeX / Mask.Texture.GetSizeX(); + ScaleY = SizeY / Mask.Texture.GetSizeY(); +} + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyTerrace( + int32 TerraceHeightInVoxels, + float Angle, + int32 ImmutableVoxels) +{ + if (TerraceHeightInVoxels < 1) + { + FVoxelMessages::Error("TerraceHeightInVoxels must be >= 1"); + return {}; + } + + return + { + "ApplyTerrace", + EVoxelSurfaceEditsStackElementFlags::NeedNormals, + [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyTerraceImpl(Voxels, TerraceHeightInVoxels, Angle, ImmutableVoxels); + } + }; +} + +FVoxelSurfaceEditsStackElement UVoxelSurfaceTools::ApplyFlatten( + AVoxelWorld* World, + FVector PlanePoint, + FVector PlaneNormal, + EVoxelSDFMergeMode MergeMode, + bool bConvertToVoxelSpace) +{ + CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE(); + + PlanePoint = GET_VOXEL_TOOL_REAL(PlanePoint).ToFloat(); + + if (bConvertToVoxelSpace) + { + PlaneNormal = World->GetActorTransform().InverseTransformVector(PlaneNormal).GetSafeNormal(); + } + + return + { + "ApplyFlatten", + EVoxelSurfaceEditsStackElementFlags::NeedValues | EVoxelSurfaceEditsStackElementFlags::ShouldBeLast, + [=](auto& Info, auto& Voxels) + { + FVoxelSurfaceToolsImpl::ApplyFlattenImpl(Info, Voxels, FPlane(PlanePoint, PlaneNormal.GetSafeNormal()), MergeMode); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelSurfaceTools::DebugSurfaceVoxels( + AVoxelWorld* World, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float Lifetime) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_VOID(); + + for (auto& Voxel : *ProcessedVoxels.Voxels) + { + const FVector Position = World->LocalToGlobal(Voxel.Position); + const FLinearColor Color = FMath::Lerp( + FLinearColor::Black, + Voxel.Strength > 0 ? FLinearColor::Red : FLinearColor::Green, + FMath::Clamp(FMath::Abs(Voxel.Strength), 0.f, 1.f)); + DrawDebugPoint( + World->GetWorld(), + Position, + World->VoxelSize / 20, + Color.ToFColor(false), + false, + Lifetime); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTestLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTestLibrary.cpp new file mode 100644 index 00000000..5a527d8e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTestLibrary.cpp @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelTestLibrary.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelData/VoxelDataIncludes.h" + +FVoxelTestValues UVoxelTestLibrary::ReadValues(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED(); + + auto& Data = World->GetData(); + + FVoxelReadScopeLock Lock(Data, Bounds, FUNCTION_FNAME); + return { MakeSharedCopy(Data.GetValues(Bounds)) }; +} + +void UVoxelTestLibrary::TestValues(FVoxelTestValues ValuesA, FVoxelTestValues ValuesB) +{ + VOXEL_FUNCTION_COUNTER(); + + if (ValuesA.Values->Num() != ValuesB.Values->Num()) + { + ensure(false); + return; + } + + for (int32 Index = 0; Index < ValuesA.Values->Num(); Index++) + { + const auto ValueA = (*ValuesA.Values)[Index]; + const auto ValueB = (*ValuesB.Values)[Index]; + + if (ValueA != ValueB) + { + ensure(false); + return; + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTextureTools.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTextureTools.cpp new file mode 100644 index 00000000..18b84a99 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelTextureTools.cpp @@ -0,0 +1,60 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelTextureTools.h" +#include "VoxelTools/VoxelToolHelpers.h" + +enum class EMinMax : uint8 +{ + Min, + Max +}; + +template +FVoxelFloatTexture MinMaxImpl(const FVoxelFloatTexture& Texture, const float Radius) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Texture.Texture.GetSizeX() * Texture.Texture.GetSizeY()); + + auto& Data = Texture.Texture; + const int32 SizeX = Data.GetSizeX(); + const int32 SizeY = Data.GetSizeY(); + + const auto NewTextureDataPtr = MakeVoxelShared::FTextureData>(); + auto& NewTextureData = *NewTextureDataPtr; + NewTextureData.SetSize(SizeX, SizeY); + + const int32 CeilRadius = FMath::CeilToInt(Radius); + const int32 RadiusSquared = FMath::CeilToInt(FMath::Square(Radius)); + for (int32 X = 0; X < SizeX; X++) + { + for (int32 Y = 0; Y < SizeY; Y++) + { + float MinMaxValue = MinMax == EMinMax::Min ? MAX_flt : -MAX_flt; + for (int32 U = -CeilRadius; U <= CeilRadius; U++) + { + for (int32 V = -CeilRadius; V <= CeilRadius; V++) + { + if (U * U + V * V <= RadiusSquared) + { + const float OtherValue = Data.SampleRaw(X + U, Y + V, EVoxelSamplerMode::Clamp); + MinMaxValue = MinMax == EMinMax::Min ? FMath::Min(MinMaxValue, OtherValue) : FMath::Max(MinMaxValue, OtherValue); + } + } + } + NewTextureData.SetValue(X, Y, MinMaxValue); + } + } + + return FVoxelFloatTexture{ TVoxelTexture{NewTextureDataPtr} }; +} + +FVoxelFloatTexture UVoxelTextureTools::Minimum(FVoxelFloatTexture Texture, float Radius) +{ + VOXEL_FUNCTION_COUNTER(); + return MinMaxImpl(Texture, Radius); +} + +FVoxelFloatTexture UVoxelTextureTools::Maximum(FVoxelFloatTexture Texture, float Radius) +{ + VOXEL_FUNCTION_COUNTER(); + return MinMaxImpl(Texture, Radius); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolHelpers.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolHelpers.cpp new file mode 100644 index 00000000..cc65afcf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolHelpers.cpp @@ -0,0 +1,213 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "IVoxelPool.h" + +#include "Engine/Engine.h" +#include "Async/Async.h" + +FVoxelLatentActionAsyncWork::FVoxelLatentActionAsyncWork(FName Name) + : FVoxelAsyncWorkWithWait(Name, 1e9) +{ +} + +uint32 FVoxelLatentActionAsyncWork::GetPriority() const +{ + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelLatentActionAsyncWork_WithWorld::FVoxelLatentActionAsyncWork_WithWorld( + FName Name, + TWeakObjectPtr World, + TFunction Function) + : FVoxelLatentActionAsyncWork(Name) + , World(World) + , Data(World->GetDataSharedPtr()) + , Function(MoveTemp(Function)) +{ +} + +void FVoxelLatentActionAsyncWork_WithWorld::DoWork() +{ + const auto PinnedData = Data.Pin(); + if (PinnedData.IsValid()) + { + Function(*PinnedData); + } +} + +bool FVoxelLatentActionAsyncWork_WithWorld::IsValid() const +{ + return World.IsValid() && Data.IsValid(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelLatentActionAsyncWork_WithoutWorld::FVoxelLatentActionAsyncWork_WithoutWorld(FName Name, TFunction Function, TFunction IsValidLambda) + : FVoxelLatentActionAsyncWork(Name) + , Function(MoveTemp(Function)) + , IsValidLambda(MoveTemp(IsValidLambda)) +{ +} + +void FVoxelLatentActionAsyncWork_WithoutWorld::DoWork() +{ + Function(); +} + +bool FVoxelLatentActionAsyncWork_WithoutWorld::IsValid() const +{ + return IsValidLambda(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelToolHelpers::UpdateWorld(AVoxelWorld* World, const FVoxelIntBox& Bounds) +{ + check(World); + World->GetLODManager().UpdateBounds(Bounds); +} + +void FVoxelToolHelpers::StartAsyncEditTask(AVoxelWorld* World, IVoxelQueuedWork* Work) +{ + if (World) + { + World->GetPool().QueueTask(EVoxelTaskType::AsyncEditFunctions, Work); + } + else + { + // Should be safe as async tasks should be flushed on close + AsyncTask(ENamedThreads::AnyThread, [Work]() { Work->DoThreadedWork(); }); + } +} + +float FVoxelToolHelpers::GetRealDistance(AVoxelWorld* World, float Distance, bool bConvertToVoxelSpace) +{ + if (bConvertToVoxelSpace) + { + return Distance / World->VoxelSize; + } + else + { + return Distance; + } +} + +FVoxelVector FVoxelToolHelpers::GetRealPosition(AVoxelWorld* World, const FVector& Position, bool bConvertToVoxelSpace) +{ + if (bConvertToVoxelSpace) + { + return World->GlobalToLocalFloat(Position); + } + else + { + return Position; + } +} + +FTransform FVoxelToolHelpers::GetRealTransform(AVoxelWorld* World, FTransform Transform, bool bConvertToVoxelSpace) +{ + if (bConvertToVoxelSpace) + { + Transform *= World->GetActorTransform().Inverse(); + Transform.ScaleTranslation(1.f / World->VoxelSize); + } + return Transform; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FVoxelToolHelpers::StartLatentAction(UObject* WorldContextObject, FLatentActionInfo LatentInfo, FName Name, bool bHideLatentWarnings, TFunction CreateLatentAction) +{ + if (UWorld* WorldContext = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) + { + FLatentActionManager& LatentActionManager = WorldContext->GetLatentActionManager(); + if (!LatentActionManager.FindExistingAction(LatentInfo.CallbackTarget, LatentInfo.UUID)) + { + auto* LatentAction = CreateLatentAction(); + LatentActionManager.AddNewAction( + LatentInfo.CallbackTarget, + LatentInfo.UUID, + LatentAction); + return true; + } + else + { + if (!bHideLatentWarnings) + { + FVoxelMessages::Info( + FString::Printf( + TEXT("%s: task already pending for this node (tick HideLatentWarnings on the node to hide this message)."), + *Name.ToString())); + } + return false; + } + } + else + { + FVoxelMessages::Info( + FString::Printf( + TEXT("%s: invalid world context object."), + *Name.ToString())); + return false; + } +} + +bool FVoxelToolHelpers::StartAsyncLatentAction_WithWorld( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FName Name, + bool bHideLatentWarnings, + TFunction DoWork, + EVoxelUpdateRender UpdateRender, + const FVoxelIntBox& BoundsToUpdate) +{ + return StartAsyncLatentActionImpl( + WorldContextObject, + LatentInfo, + World, + Name, + bHideLatentWarnings, + [&]() { return new FVoxelLatentActionAsyncWork_WithWorld(Name, World, DoWork); }, + [=](FVoxelLatentActionAsyncWork_WithWorld& Work) + { + if (UpdateRender == EVoxelUpdateRender::UpdateRender && Work.World.IsValid()) + { + UpdateWorld(Work.World.Get(), BoundsToUpdate); + } + }); +} + +bool FVoxelToolHelpers::StartAsyncLatentAction_WithoutWorld( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FName Name, + bool bHideLatentWarnings, + TFunction DoWork, + TFunction IsValid) +{ + return StartAsyncLatentActionImpl( + WorldContextObject, + LatentInfo, + nullptr, + Name, + bHideLatentWarnings, + [&]() { return new FVoxelLatentActionAsyncWork_WithoutWorld(Name, DoWork, MoveTemp(IsValid)); }, + [=](auto&) {}); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolManager.cpp new file mode 100644 index 00000000..14ac0008 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolManager.cpp @@ -0,0 +1,120 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/VoxelToolManager.h" +#include "VoxelTools/Tools/VoxelTool.h" + +#include "Engine/Blueprint.h" +#include "UObject/UObjectHash.h" +#include "AssetRegistryModule.h" + +UVoxelToolManager::UVoxelToolManager() +{ + SharedConfig = CreateDefaultSubobject(STATIC_FNAME("SharedConfig")); +} + +void UVoxelToolManager::CreateDefaultTools(bool bLoadBlueprints) +{ + VOXEL_FUNCTION_COUNTER(); + + ActiveTool = nullptr; + Tools.Empty(); + + if (bLoadBlueprints) + { + TArray ToolClasses; + GetDerivedClasses(UVoxelTool::StaticClass(), ToolClasses); + + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + + FARFilter Filter; + Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName()); + + for (auto* Class : ToolClasses) + { + if (Class->HasAnyClassFlags(CLASS_Native)) + { + Filter.TagsAndValues.Add(FBlueprintTags::NativeParentClassPath, FString::Printf(TEXT("%s'%s'"), *UClass::StaticClass()->GetName(), *Class->GetPathName())); + } + } + + TArray Assets; + AssetRegistryModule.Get().GetAssets(Filter, Assets); + + for (auto& Asset : Assets) + { + Asset.GetAsset(); + } + } + + TArray ToolClasses; + GetDerivedClasses(UVoxelTool::StaticClass(), ToolClasses); + + ToolClasses.RemoveAllSwap([](UClass* Class) + { + return Class->HasAnyClassFlags(CLASS_Abstract) || !Class->GetDefaultObject()->bShowInDropdown; + }); + + for (auto* Class : ToolClasses) + { + // Skip SKEL and REINST classes. + if (Class->GetName().StartsWith(TEXT("SKEL_")) || + Class->GetName().StartsWith(TEXT("REINST_"))) + { + continue; + } + + auto* Tool = NewObject(this, Class, NAME_None, GetMaskedFlags(RF_Transient | RF_Transactional)); + Tool->SharedConfig = SharedConfig; + Tools.Add(Tool); + } +} + +void UVoxelToolManager::SetActiveTool(UVoxelTool* NewActiveTool) +{ + if (ActiveTool == NewActiveTool) + { + return; + } + + if (ActiveTool) + { + ActiveTool->DisableTool(); + } + + ActiveTool = NewActiveTool; + + if (ActiveTool) + { + ActiveTool->EnableTool(); + } +} + +void UVoxelToolManager::SetActiveToolByClass(TSubclassOf NewActiveTool) +{ + if (!NewActiveTool) + { + SetActiveTool(nullptr); + return; + } + + for (auto* Tool : Tools) + { + if (Tool->GetClass() == NewActiveTool) + { + SetActiveTool(Tool); + return; + } + } +} + +void UVoxelToolManager::SetActiveToolByName(FName NewActiveTool) +{ + for (auto* Tool : Tools) + { + if (Tool->ToolName == NewActiveTool) + { + SetActiveTool(Tool); + return; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolsBase.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolsBase.cpp new file mode 100644 index 00000000..4e1a1222 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelTools/VoxelToolsBase.cpp @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelTools/Gen/VoxelToolsBase.h" + +template +FVoxelIntBox GetModifiedVoxelsBounds(const TArray& ModifiedVoxels) +{ + FVoxelIntBoxWithValidity Bounds; + for (auto& ModifiedVoxel : ModifiedVoxels) + { + Bounds += ModifiedVoxel.Position; + } + return Bounds.IsValid() ? Bounds.GetBox() : FVoxelIntBox(); +} + +FVoxelIntBox UVoxelToolsBase::GetModifiedVoxelValuesBounds(const TArray& ModifiedVoxels) +{ + VOXEL_FUNCTION_COUNTER(); + return GetModifiedVoxelsBounds(ModifiedVoxels); +} + +FVoxelIntBox UVoxelToolsBase::GetModifiedVoxelMaterialsBounds(const TArray& ModifiedVoxels) +{ + VOXEL_FUNCTION_COUNTER(); + return GetModifiedVoxelsBounds(ModifiedVoxels); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelConfigUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelConfigUtilities.cpp new file mode 100644 index 00000000..bcea3ccd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelConfigUtilities.cpp @@ -0,0 +1,52 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelConfigUtilities.h" +#include "VoxelMinimal.h" + +#include "Misc/ConfigCacheIni.h" +#include "UObject/UnrealType.h" +#include "UObject/PropertyPortFlags.h" + +void FVoxelConfigUtilities::SaveConfig(UObject* Object, const FString& BaseSectionName, const FString& Filename) +{ + UClass* Class = Object->GetClass(); + const UObject* CDO = Class->GetDefaultObject(); + + for (TFieldIterator It(Class, EFieldIteratorFlags::IncludeSuper); It; ++It) + { + auto& Property = **It; + if (Property.HasAnyPropertyFlags(CPF_Transient)) continue; + if (!ensure(Property.ArrayDim == 1)) continue; + + const FString Section = BaseSectionName + TEXT(".") + Class->GetName(); + + FString Value; + if (Property.ExportText_InContainer(0, Value, Object, CDO, Object, PPF_None)) + { + GConfig->SetString(*Section, *Property.GetName(), *Value, Filename); + } + else + { + GConfig->RemoveKey(*Section, *Property.GetName(), Filename); + } + } +} + +void FVoxelConfigUtilities::LoadConfig(UObject* Object, const FString& BaseSectionName, const FString& Filename) +{ + UClass* Class = Object->GetClass(); + for (TFieldIterator It(Class, EFieldIteratorFlags::IncludeSuper); It; ++It) + { + auto& Property = **It; + if (Property.HasAnyPropertyFlags(CPF_Transient)) continue; + if (!ensure(Property.ArrayDim == 1)) continue; + + const FString Section = BaseSectionName + TEXT(".") + Class->GetName(); + + FString Value; + if (GConfig->GetString(*Section, *Property.GetName(), Value, Filename)) + { + Property.ImportText(*Value, Property.ContainerPtrToValuePtr(Object), PPF_None, Object); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelDistanceFieldUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelDistanceFieldUtilities.cpp new file mode 100644 index 00000000..3d80b942 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelUtilities/VoxelDistanceFieldUtilities.cpp @@ -0,0 +1,290 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.inl" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelShaders/VoxelDistanceFieldShader.h" + +#include "Async/ParallelFor.h" + +FColor FVoxelDistanceFieldUtilities::GetDistanceFieldColor(float Value) +{ + // Credit for this snippet goes to Inigo Quilez + + FLinearColor Color = FLinearColor::White - FMath::Sign(Value) * FLinearColor(0.1, 0.4, 0.7, 0.f); + Color *= 1.0 - FMath::Exp(-3.0 * FMath::Abs(Value)); + Color *= 0.8 + 0.2 * FMath::Cos(150.0 * Value); + Color = FMath::Lerp(Color, FLinearColor::White, 1.0 - FMath::SmoothStep(0.0, 0.01, FMath::Abs(Value))); + Color.A = 1.f; + return FLinearColor(Color.ToFColor(false)).ToFColor(false); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDistanceFieldUtilities::JumpFlood(const FIntVector& Size, TArray& InOutSurfacePositions, EVoxelComputeDevice Device, bool bMultiThreaded, int32 MaxPasses_Debug) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(InOutSurfacePositions.Num() == Size.X * Size.Y * Size.Z); + + if (Device == EVoxelComputeDevice::GPU) + { + const auto DataPtr = MakeVoxelShared>(MoveTemp(InOutSurfacePositions)); + + const auto Helper = MakeVoxelShared(); + Helper->StartCompute(Size, DataPtr, MaxPasses_Debug); + Helper->WaitForCompletion(); + + InOutSurfacePositions = MoveTemp(*DataPtr); + } + else + { + bool bUseTempAsSrc = false; + + TArray Temp; + Temp.Empty(InOutSurfacePositions.Num()); + Temp.SetNumUninitialized(InOutSurfacePositions.Num()); + + const int32 PowerOfTwo = FMath::CeilLogTwo(Size.GetMax()); + for (int32 Pass = 0; Pass < PowerOfTwo; Pass++) + { + if (MaxPasses_Debug == Pass) + { + break; + } + + // -1: we want to start with half the size + const int32 Step = 1 << (PowerOfTwo - 1 - Pass); + JumpFloodStep_CPU( + Size, + bUseTempAsSrc ? Temp : InOutSurfacePositions, + bUseTempAsSrc ? InOutSurfacePositions : Temp, + Step, + bMultiThreaded); + + bUseTempAsSrc = !bUseTempAsSrc; + } + + if (bUseTempAsSrc) + { + InOutSurfacePositions = MoveTemp(Temp); + } + } +} + +void FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(const FIntVector& Size, TArrayView SurfacePositions, TArrayView InOutDistances) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(SurfacePositions.Num() == InOutDistances.Num()); + check(SurfacePositions.Num() == Size.X * Size.Y * Size.Z); + + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + float& Distance = FVoxelUtilities::Get3D(InOutDistances, Size, X, Y, Z); + + const FVector SurfacePosition = FVoxelUtilities::Get3D(SurfacePositions, Size, X, Y, Z); + ensureVoxelSlow(IsSurfacePositionValid(SurfacePosition)); + + // Keep sign + Distance = FVector::Distance(FVector(X, Y, Z), SurfacePosition) * FMath::Sign(Distance); + ensureVoxelSlow(FMath::Abs(Distance) < Size.Size() * 2); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArrayView OutDistances, TArrayView OutSurfacePositions) +{ + GetSurfacePositionsFromDensities(Size, Densities, OutDistances, OutSurfacePositions, [](float F) { return F; }); +} + +void FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArrayView OutDistances, TArrayView OutSurfacePositions) +{ + GetSurfacePositionsFromDensities(Size, Densities, OutDistances, OutSurfacePositions, [](FVoxelValue F) { return F.ToFloat(); }); +} + +void FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArray& OutDistances, TArray& OutSurfacePositions) +{ + const int32 Num = Size.X * Size.Y * Size.Z; + OutDistances.Empty(Num); + OutDistances.SetNumUninitialized(Num); + OutSurfacePositions.Empty(Num); + OutSurfacePositions.SetNumUninitialized(Num); + GetSurfacePositionsFromDensities(Size, Densities, TArrayView(OutDistances), TArrayView(OutSurfacePositions)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDistanceFieldUtilities::DownSample( + const FIntVector& Size, + TArrayView InDistances, + TArrayView InSurfacePositions, + TArrayView OutDistances, + TArrayView OutSurfacePositions, + int32 Divisor, + bool bShrink) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(Divisor >= 1); + check(InDistances.GetData() != OutDistances.GetData()); + check(InSurfacePositions.GetData() != OutSurfacePositions.GetData()); + + const FIntVector LowSize = FVoxelUtilities::DivideCeil(Size, Divisor); + + for (int32 LowX = 0; LowX < LowSize.X; LowX++) + { + for (int32 LowY = 0; LowY < LowSize.Y; LowY++) + { + for (int32 LowZ = 0; LowZ < LowSize.Z; LowZ++) + { + float BestDistance = MAX_flt; + FVector BestSurfacePosition = MakeInvalidSurfacePosition(); + float Sign = FVoxelUtilities::Get3D(InDistances, Size, LowX * Divisor, LowY * Divisor, LowZ * Divisor); + + for (int32 HighX = LowX * Divisor; HighX < FMath::Min(Size.X, (LowX + 1) * Divisor); HighX++) + { + for (int32 HighY = LowY * Divisor; HighY < FMath::Min(Size.Y, (LowY + 1) * Divisor); HighY++) + { + for (int32 HighZ = LowZ * Divisor; HighZ < FMath::Min(Size.Z, (LowZ + 1) * Divisor); HighZ++) + { + FVector NeighborSurfacePosition = FVoxelUtilities::Get3D(InSurfacePositions, Size, HighX, HighY, HighZ); + + if (IsSurfacePositionValid(NeighborSurfacePosition)) + { + // Make sure to / Divisor AFTER IsSurfacePositionValid + NeighborSurfacePosition /= Divisor; + const float Distance = (NeighborSurfacePosition - FVector(LowX, LowY, LowZ)).SizeSquared(); + if (Distance < BestDistance) + { + BestDistance = Distance; + BestSurfacePosition = NeighborSurfacePosition; + Sign = FVoxelUtilities::Get3D(InDistances, Size, HighX, HighY, HighZ); + } + } + } + } + } + + FVoxelUtilities::Get3D(OutSurfacePositions, LowSize, LowX, LowY, LowZ) = BestSurfacePosition; + FVoxelUtilities::Get3D(OutDistances, LowSize, LowX, LowY, LowZ) = Sign; + } + } + } +} + +void FVoxelDistanceFieldUtilities::DownSample( + FIntVector& Size, + TArray& Distances, + TArray& SurfacePositions, + int32 Divisor, + bool bShrink) +{ + ensure(Divisor >= 1); + if (Divisor <= 1) + { + return; + } + + const FIntVector LowSize = FVoxelUtilities::DivideCeil(Size, Divisor); + + const int32 NewSize = LowSize.X * LowSize.Y * LowSize.Z; + + TArray NewDistances; + TArray NewSurfacePositions; + NewDistances.Empty(NewSize); + NewDistances.SetNumUninitialized(NewSize); + NewSurfacePositions.Empty(NewSize); + NewSurfacePositions.SetNumUninitialized(NewSize); + + DownSample(Size, Distances, SurfacePositions, NewDistances, NewSurfacePositions, Divisor, bShrink); + + Size = LowSize; + Distances = MoveTemp(NewDistances); + SurfacePositions = MoveTemp(NewSurfacePositions); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDistanceFieldUtilities::JumpFloodStep_CPU(const FIntVector& Size, TArrayView InData, TArrayView OutData, int32 Step, bool bMultiThreaded) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + check(InData.Num() == OutData.Num()); + check(InData.Num() == Size.X * Size.Y * Size.Z); + + const auto DoWork = [&](int32 X) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + const FIntVector Position(X, Y, Z); + + float BestDistance = MAX_flt; + FVector BestSurfacePosition = MakeInvalidSurfacePosition(); + + for (int32 DX = -1; DX <= 1; ++DX) + { + for (int32 DY = -1; DY <= 1; ++DY) + { + for (int32 DZ = -1; DZ <= 1; ++DZ) + { + const FIntVector NeighborPosition = Position + FIntVector(DX, DY, DZ) * Step; + + if (NeighborPosition.X < 0 || + NeighborPosition.Y < 0 || + NeighborPosition.Z < 0 || + NeighborPosition.X >= Size.X || + NeighborPosition.Y >= Size.Y || + NeighborPosition.Z >= Size.Z) + { + continue; + } + + const FVector NeighborSurfacePosition = FVoxelUtilities::Get3D(InData, Size, NeighborPosition); + + if (IsSurfacePositionValid(NeighborSurfacePosition)) + { + const float Distance = (NeighborSurfacePosition - FVector(Position)).SizeSquared(); + if (Distance < BestDistance) + { + BestDistance = Distance; + BestSurfacePosition = NeighborSurfacePosition; + } + } + } + } + } + FVoxelUtilities::Get3D(OutData, Size, Position) = BestSurfacePosition; + } + } + }; + + if (bMultiThreaded) + { + ParallelFor(Size.X, DoWork); + } + else + { + for (int32 X = 0; X < Size.X; X++) + { + DoWork(X); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorld.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorld.cpp new file mode 100644 index 00000000..07c25030 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorld.cpp @@ -0,0 +1,1753 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelWorld.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelGenerators/VoxelGeneratorCache.h" +#include "IVoxelPool.h" +#include "VoxelSettings.h" +#include "VoxelDefaultPool.h" +#include "VoxelWorldRootComponent.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelRender/IVoxelLODManager.h" +#include "VoxelRender/VoxelToolRendering.h" +#include "VoxelRender/LODManager/VoxelDefaultLODManager.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h" +#include "VoxelRender/Renderers/VoxelDefaultRenderer.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelSaveUtilities.h" +#include "VoxelMultiplayer/VoxelMultiplayerManager.h" +#include "VoxelMultiplayer/VoxelMultiplayerTcp.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" +#include "VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h" +#include "VoxelPlaceableItems/Actors/VoxelAssetActor.h" +#include "VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h" +#include "VoxelComponents/VoxelInvokerComponent.h" +#include "VoxelSpawners/VoxelInstancedMeshManager.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelDebug/VoxelDebugManager.h" +#include "VoxelDebug/VoxelLineBatchComponent.h" +#include "VoxelEvents/VoxelEventManager.h" +#include "VoxelMessages.h" +#include "VoxelFeedbackContext.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" + +#include "EngineUtils.h" +#include "Engine/World.h" +#include "Engine/Engine.h" +#include "Engine/NetDriver.h" +#include "Engine/NetConnection.h" + +#include "Misc/MessageDialog.h" +#include "Misc/FileHelper.h" +#include "Misc/FeedbackContext.h" +#include "Components/BillboardComponent.h" +#include "Components/HierarchicalInstancedStaticMeshComponent.h" +#include "UObject/ConstructorHelpers.h" +#include "HAL/PlatformFilemanager.h" +#include "TimerManager.h" +#include "Materials/Material.h" + +#include "Framework/Notifications/NotificationManager.h" +#include "Framework/Application/SlateApplication.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Serialization/BufferArchive.h" +#include "Serialization/MemoryReader.h" + +void AVoxelWorld::FGameThreadTasks::Flush() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsInGameThread()); + + TFunction Task; + while (Tasks.Dequeue(Task)) + { + Task(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +AVoxelWorld::AVoxelWorld() + : LODDynamicSettings(MakeVoxelShared()) + , RendererDynamicSettings(MakeVoxelShared()) +{ + MultiplayerInterface = UVoxelMultiplayerTcpInterface::StaticClass(); + + PrimaryActorTick.bCanEverTick = true; + PrimaryActorTick.bHighPriority = true; + + WorldRoot = CreateDefaultSubobject("Root"); + LineBatchComponent = CreateDefaultSubobject("LineBatchComponent"); + + RootComponent = WorldRoot; + +#if WITH_EDITOR + auto* SpriteComponent = CreateEditorOnlyDefaultSubobject(TEXT("Sprite")); + if (SpriteComponent) + { + static ConstructorHelpers::FObjectFinder SpriteFinder(TEXT("/Engine/EditorResources/S_Terrain")); + SpriteComponent->Sprite = SpriteFinder.Object; + SpriteComponent->SetRelativeScale3D(FVector(0.5f)); + SpriteComponent->bHiddenInGame = true; + SpriteComponent->bIsScreenSizeScaled = true; + SpriteComponent->SpriteInfo.Category = TEXT("Voxel World"); + SpriteComponent->SpriteInfo.DisplayName = FText::FromString("Voxel World"); + SpriteComponent->SetupAttachment(RootComponent); + SpriteComponent->bReceivesDecals = false; + } + + // Automatically refresh material on property change/material recompile + const auto RefreshMaterial = [=](UMaterialInterface* Material) + { + if (!Material || !bAutomaticallyRefreshMaterials || !IsCreated()) + { + return; + } + + bool bUsed = false; + if (MaterialConfig == EVoxelMaterialConfig::RGB) + { + if (VoxelMaterial == Material) + { + bUsed = true; + } + else + { + for (auto& LODMaterial : LODMaterials) + { + if (LODMaterial.Material == Material) + { + bUsed = true; + break; + } + } + } + } + else + { + if (MaterialCollection && + MaterialCollection->GetMaterialIndex(Material->GetFName()) != -1) + { + bUsed = true; + } + else + { + for (auto& LODMaterialCollection : LODMaterialCollections) + { + if (LODMaterialCollection.MaterialCollection && + LODMaterialCollection.MaterialCollection->GetMaterialIndex(Material->GetFName()) != -1) + { + bUsed = true; + break; + } + } + } + } + + if (bUsed) + { + UVoxelBlueprintLibrary::ApplyNewMaterials(this); + } + }; + UMaterial::OnMaterialCompilationFinished().AddWeakLambda(this, RefreshMaterial); + + const auto TryRefreshMaterials = [=](UObject* Object, FPropertyChangedEvent& PropertyChangedEvent) + { + if (!bAutomaticallyRefreshMaterials) + { + return; + } + + RefreshMaterial(Cast(Object)); + + if (MaterialConfig != EVoxelMaterialConfig::RGB && Object->IsA()) + { + bool bUsed = false; + if (MaterialCollection == Object) + { + bUsed = true; + } + else + { + for (auto& It : LODMaterialCollections) + { + if (It.MaterialCollection == Object) + { + bUsed = true; + break; + } + } + } + + if (auto* MaterialCollectionInstance = Cast(MaterialCollection)) + { + if (MaterialCollectionInstance->LayersSource == Object) + { + bUsed = true; + } + } + + if (bUsed) + { + UVoxelBlueprintLibrary::ApplyNewMaterials(this); + + if (PropertyChangedEvent.MemberProperty && + PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelInstancedMaterialCollection, MaxMaterialsToBlendAtOnce)) + { + // Need to recompute the chunks if MaxMaterialsToBlendAtOnce changed, as they are built with the wrong number of indices + UVoxelBlueprintLibrary::UpdateAll(this); + } + } + } + }; + + const auto TryRefreshFoliage = [=](UObject* Object, FPropertyChangedEvent& PropertyChangedEvent) + { + if (!bAutomaticallyRefreshFoliage || !SpawnerConfig) + { + return; + } + + if (SpawnerConfig == Object || SpawnerConfig->NeedsToRebuild(Object, PropertyChangedEvent)) + { + UVoxelBlueprintLibrary::RecreateSpawners(this); + } + }; + + FCoreUObjectDelegates::OnObjectPropertyChanged.AddWeakLambda(this, [=](UObject* Object, FPropertyChangedEvent& PropertyChangedEvent) + { + if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) + { + return; + } + + if (!Object || !IsCreated()) + { + return; + } + + TryRefreshMaterials(Object, PropertyChangedEvent); + TryRefreshFoliage(Object, PropertyChangedEvent); + }); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelWorld::CreateWorld(FVoxelWorldCreateInfo Info) +{ + VOXEL_FUNCTION_COUNTER(); + + if (IsCreated()) + { + FVoxelMessages::Error("Can't create world: already created"); + return; + } + +#if WITH_EDITOR + if (GetWorld()->WorldType == EWorldType::Editor || + GetWorld()->WorldType == EWorldType::EditorPreview) + { + // Called in editor - probably by a BP construction script + // Forward to CreateInEditor + CreateInEditor(Info); + return; + } +#endif + + PlayType = EVoxelPlayType::Game; + CreateWorldInternal(Info); + if (bUseCameraIfNoInvokersFound && UVoxelInvokerComponentBase::GetInvokers(GetWorld()).Num() == 0) + { + const auto NetMode = GetWorld()->GetNetMode(); + if (NetMode != ENetMode::NM_Standalone) + { + if (NetMode != ENetMode::NM_DedicatedServer) // Not spawned yet + { + FVoxelMessages::Warning("Voxel World: Can't use camera as invoker in multiplayer! You need to add a VoxelInvokerComponent to your character"); + } + } + else + { + auto* CameraInvoker = NewObject(this, NAME_None, RF_Transient); + CameraInvoker->SetupAttachment(GetRootComponent(), NAME_None); + CameraInvoker->RegisterComponent(); + + LOG_VOXEL(Log, TEXT("No Voxel Invoker found, using camera as invoker")); + } + } +} + +void AVoxelWorld::DestroyWorld() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!IsCreated()) + { + FVoxelMessages::Error("Can't destroy world: not created"); + return; + } + + OnWorldDestroyed.Broadcast(); + + DestroyWorldInternal(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox AVoxelWorld::GetWorldBounds() const +{ + if (bUseCustomWorldBounds) + { + return + FVoxelUtilities::GetCustomBoundsForDepth( + FVoxelIntBox::SafeConstruct(CustomWorldBounds.Min, CustomWorldBounds.Max), + RenderOctreeDepth); + } + else + { + return FVoxelUtilities::GetBoundsFromDepth(RenderOctreeDepth); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelWorld::SetGeneratorObject(UVoxelGenerator* NewGenerator) +{ + Generator = NewGenerator; +} + +void AVoxelWorld::SetGeneratorClass(TSubclassOf NewGeneratorClass) +{ + Generator = NewGeneratorClass.Get(); +} + +void AVoxelWorld::SetRenderOctreeDepth(int32 NewDepth) +{ + RenderOctreeDepth = FMath::Max(1, FVoxelUtilities::ClampDepth(NewDepth)); + WorldSizeInVoxel = FVoxelUtilities::GetSizeFromDepth(RenderOctreeDepth); +} + +void AVoxelWorld::SetWorldSize(int32 NewWorldSizeInVoxels) +{ + SetWorldSize(uint32(FMath::Max(0, NewWorldSizeInVoxels))); +} +void AVoxelWorld::SetWorldSize(uint32 NewWorldSizeInVoxels) +{ + SetRenderOctreeDepth(FVoxelUtilities::GetDepthFromSize(NewWorldSizeInVoxels)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FIntVector AVoxelWorld::GlobalToLocal(const FVector& Position, EVoxelWorldCoordinatesRounding Rounding) const +{ + const FVector LocalPosition = GetTransform().InverseTransformPosition(Position) / VoxelSize; + + FIntVector VoxelPosition; + switch (Rounding) + { + case EVoxelWorldCoordinatesRounding::RoundToNearest: VoxelPosition = FVoxelUtilities::RoundToInt(LocalPosition); break; + case EVoxelWorldCoordinatesRounding::RoundUp: VoxelPosition = FVoxelUtilities::CeilToInt(LocalPosition); break; + case EVoxelWorldCoordinatesRounding::RoundDown: VoxelPosition = FVoxelUtilities::FloorToInt(LocalPosition); break; + default: ensure(false); + } + return VoxelPosition - *WorldOffset; +} + +FVector AVoxelWorld::GlobalToLocalFloatBP(const FVector& Position) const +{ + return GlobalToLocalFloat(Position).ToFloat(); +} + +FVoxelVector AVoxelWorld::GlobalToLocalFloat(const FVector& Position) const +{ + return GetTransform().InverseTransformPosition(Position) / VoxelSize - FVoxelVector(*WorldOffset); +} + +FVector AVoxelWorld::LocalToGlobal(const FIntVector& Position) const +{ + return GetTransform().TransformPosition(VoxelSize * FVector(Position + *WorldOffset)); +} + +FVector AVoxelWorld::LocalToGlobalFloatBP(const FVector& Position) const +{ + return LocalToGlobalFloat(Position); +} + +FVector AVoxelWorld::LocalToGlobalFloat(const FVoxelVector& Position) const +{ + return GetTransform().TransformPosition((VoxelSize * (Position + *WorldOffset)).ToFloat()); +} + +TArray AVoxelWorld::GetNeighboringPositions(const FVector& GlobalPosition) const +{ + return TArray(FVoxelUtilities::GetNeighbors(GlobalToLocalFloat(GlobalPosition))); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelWorld::SetOffset(const FIntVector& OffsetInVoxels) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_IMPL(this,); + + *WorldOffset = OffsetInVoxels; + Renderer->RecomputeMeshPositions(); + InstancedMeshManager->RecomputeMeshPositions(); +} + +void AVoxelWorld::AddOffset(const FIntVector& OffsetInVoxels, bool bMoveActor) +{ + VOXEL_FUNCTION_COUNTER(); + CHECK_VOXELWORLD_IS_CREATED_IMPL(this,); + + if (bMoveActor) + { + SetActorLocation(GetTransform().TransformPosition(VoxelSize * FVector(OffsetInVoxels))); + } + + SetOffset(*WorldOffset - OffsetInVoxels); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelGeneratorInit AVoxelWorld::GetGeneratorInit() const +{ + return FVoxelGeneratorInit( + VoxelSize, + FVoxelUtilities::GetSizeFromDepth(RenderOctreeDepth), + RenderType, + MaterialConfig, + MaterialCollection, + this); +} + +UVoxelMultiplayerInterface* AVoxelWorld::CreateMultiplayerInterfaceInstance() +{ + VOXEL_FUNCTION_COUNTER(); + + if (IsCreated()) + { + FVoxelMessages::Error(FUNCTION_ERROR("Can't call once the Voxel World is created!"), this); + return nullptr; + } + + if (!bEnableMultiplayer) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableMultiplayer = false!"), this); + return nullptr; + } + + auto* const Class = MultiplayerInterface.Get(); + if (!Class) + { + FVoxelMessages::Error(FUNCTION_ERROR("Invalid MultiplayerInterface class!"), this); + return nullptr; + } + if (Class->HasAnyClassFlags(CLASS_Abstract)) + { + FVoxelMessages::Error(FUNCTION_ERROR("MultiplayerInterface class is abstract!"), this); + return nullptr; + } + if (MultiplayerInterfaceInstance) + { + FVoxelMessages::Error(FUNCTION_ERROR("MultiplayerInterfaceInstance already created!"), this); + return MultiplayerInterfaceInstance; + } + + MultiplayerInterfaceInstance = NewObject(this, Class, NAME_None, RF_Transient); + return MultiplayerInterfaceInstance; +} + +UVoxelMultiplayerInterface* AVoxelWorld::GetMultiplayerInterfaceInstance() const +{ + if (!bEnableMultiplayer) + { + FVoxelMessages::Error(FUNCTION_ERROR("bEnableMultiplayer = false!"), this); + return nullptr; + } + + return MultiplayerInterfaceInstance; +} + +void AVoxelWorld::SetCollisionResponseToChannel(ECollisionChannel Channel, ECollisionResponse NewResponse) +{ + VOXEL_FUNCTION_COUNTER(); + + CollisionPresets.SetResponseToChannel(Channel, NewResponse); + + if (IsCreated()) + { + GetWorldRoot().SetCollisionResponseToChannel(Channel, NewResponse); + + Renderer->ApplyToAllMeshes([&](UVoxelProceduralMeshComponent& MeshComponent) + { + MeshComponent.SetCollisionResponseToChannel(Channel, NewResponse); + }); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelWorld::BeginPlay() +{ + VOXEL_FUNCTION_COUNTER(); + + Super::BeginPlay(); + + UpdateCollisionProfile(); + + if (!IsCreated() && bCreateWorldAutomatically) + { + // Allow other actors begin play to run before creating the world, if they need to create the global pool + auto& TimerManager = GetWorld()->GetTimerManager(); + TimerManager.SetTimerForNextTick(this, &AVoxelWorld::CreateWorld); + } +} + +void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + VOXEL_FUNCTION_COUNTER(); + + Super::EndPlay(EndPlayReason); + + if (IsCreated()) + { + DestroyWorld(); + } +} + +void AVoxelWorld::Tick(float DeltaTime) +{ + VOXEL_FUNCTION_COUNTER(); + + if (GetWorld()->WorldType != EWorldType::Editor && GetWorld()->WorldType != EWorldType::EditorPreview) // We don't want to tick the BP in preview + { + Super::Tick(DeltaTime); + } + + if (IsCreated()) + { + WorldRoot->TickWorldRoot(); + GameThreadTasks->Flush(); +#if WITH_EDITOR + if (PlayType == EVoxelPlayType::Preview && Data->IsDirty()) + { + MarkPackageDirty(); + } +#endif + } +} + +void AVoxelWorld::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) +{ + VOXEL_FUNCTION_COUNTER(); + + if (!IsCreated() || !bEnableCustomWorldRebasing) + { + Super::ApplyWorldOffset(InOffset, bWorldShift); + } + else + { + const FVector RelativeOffset = InOffset / VoxelSize; + const FIntVector IntegerOffset = FVoxelUtilities::RoundToInt(RelativeOffset); + const FVector GlobalIntegerOffset = FVector(IntegerOffset) * VoxelSize; + const FVector Diff = InOffset - GlobalIntegerOffset; + + Super::ApplyWorldOffset(Diff, bWorldShift); + + *WorldOffset += IntegerOffset; + + Renderer->RecomputeMeshPositions(); + InstancedMeshManager->RecomputeMeshPositions(); + } +} + +void AVoxelWorld::OnConstruction(const FTransform& Transform) +{ + Super::OnConstruction(Transform); + +#if WITH_EDITOR + if (bIsToggled && + !IsCreated() && + !GetDefault()->bDisableAutoPreview && + (GetWorld()->WorldType == EWorldType::EditorPreview || + GetWorld()->WorldType == EWorldType::Editor || + GetWorld()->WorldType == EWorldType::GamePreview)) + { + CreateInEditor(); + } +#endif +} + +void AVoxelWorld::UnregisterAllComponents(bool bForReregister) +{ +#if WITH_EDITOR + if (bDisableComponentUnregister) + { + Super::UnregisterAllComponents(true); // Do as if it was a reregister so that proc mesh are ignored + } + else +#endif + { + Super::UnregisterAllComponents(bForReregister); + } +} + +#if WITH_EDITOR +bool AVoxelWorld::ShouldTickIfViewportsOnly() const +{ + return true; +} +#endif + +void AVoxelWorld::BeginDestroy() +{ + if (IsCreated()) + { + DestroyWorld(); + } + + // Forward to BeginDestroy AFTER destroying the world, else the components are invalid + // Note: when exiting the components are still invalid. This doesn't seem to be really useful, but might as well do it here still + Super::BeginDestroy(); +} + +void AVoxelWorld::Serialize(FArchive& Ar) +{ + Super::Serialize(Ar); + + // Temp fix + if (!IsTemplate()) + { + CollisionPresets.FixupData(this); + } +} + +void AVoxelWorld::PostLoad() +{ + Super::PostLoad(); + + if (!ProcMeshClass) + { + ProcMeshClass = UVoxelProceduralMeshComponent::StaticClass(); + } + + FVoxelDefaultPool::FixPriorityCategories(PriorityCategories); + FVoxelDefaultPool::FixPriorityOffsets(PriorityOffsets); + + SetRenderOctreeDepth(RenderOctreeDepth); + + if (int32(UVConfig) >= int32(EVoxelUVConfig::Max)) + { + UVConfig = EVoxelUVConfig::GlobalUVs; + } +} + +#if WITH_EDITOR + +void AVoxelWorld::PreEditChange(FProperty* PropertyThatWillChange) +{ + bDisableComponentUnregister = true; + Super::PreEditChange(PropertyThatWillChange); + bDisableComponentUnregister = false; +} + +void AVoxelWorld::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (!ProcMeshClass) + { + ProcMeshClass = UVoxelProceduralMeshComponent::StaticClass(); + } + + if (auto* Property = PropertyChangedEvent.Property) + { + const FName Name = Property->GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(FVoxelLODMaterialsBase, StartLOD)) + { + for (auto& It : LODMaterials) + { + It.EndLOD = FMath::Max(It.StartLOD, It.EndLOD); + } + for (auto& It : LODMaterialCollections) + { + It.EndLOD = FMath::Max(It.StartLOD, It.EndLOD); + } + } + else if (Name == GET_MEMBER_NAME_STATIC(FVoxelLODMaterialsBase, EndLOD)) + { + for (auto& It : LODMaterials) + { + It.StartLOD = FMath::Min(It.StartLOD, It.EndLOD); + } + for (auto& It : LODMaterialCollections) + { + It.StartLOD = FMath::Min(It.StartLOD, It.EndLOD); + } + } + } + + if (auto* Property = PropertyChangedEvent.MemberProperty) + { + const FName Name = Property->GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, RenderOctreeDepth)) + { + SetRenderOctreeDepth(RenderOctreeDepth); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, WorldSizeInVoxel)) + { + SetWorldSize(WorldSizeInVoxel); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, MaterialsHardness)) + { + for (auto& It : MaterialsHardness) + { + if (!It.Key.IsEmpty()) + { + It.Key = FString::FromInt(FMath::Clamp(TCString::Atoi(*It.Key), 0, 255)); + } + else if (It.Value == 0) + { + It.Value = 1; + } + It.Value = FMath::Max(It.Value, 0.f); + } + MaterialsHardness.KeySort([](const FString& A, const FString& B) { return TCString::Atoi(*A) < TCString::Atoi(*B); }); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, SpawnersCollisionDistanceInVoxel)) + { + SpawnersCollisionDistanceInVoxel = FMath::CeilToInt(SpawnersCollisionDistanceInVoxel / 32.f) * 32; + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, ChunksClustersSize)) + { + ChunksClustersSize = FMath::Max(ChunksClustersSize, RENDER_CHUNK_SIZE); + const int32 PowerOf2 = FMath::RoundToInt(FMath::Log2(ChunksClustersSize)); + ChunksClustersSize = 1 << PowerOf2; + ChunksClustersSize = FMath::Max(ChunksClustersSize, RENDER_CHUNK_SIZE); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, PriorityCategories)) + { + FVoxelDefaultPool::FixPriorityCategories(PriorityCategories); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, PriorityOffsets)) + { + FVoxelDefaultPool::FixPriorityOffsets(PriorityOffsets); + } + else if (Name == GET_MEMBER_NAME_STATIC(AVoxelWorld, MaterialConfig)) + { + if (IsCreated() && UVoxelBlueprintLibrary::HasMaterialData(this)) + { + const auto Result = FMessageDialog::Open( + EAppMsgType::YesNo, + VOXEL_LOCTEXT("Changing the material config with existing material data! Do you want to clear that data (recommended)?")); + + if (Result == EAppReturnType::Yes) + { + UVoxelBlueprintLibrary::ClearMaterialData(this); + GetData().MarkAsDirty(); + } + } + } + + OnPropertyChanged_Interactive.Broadcast(); + + if (IsCreated() && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + OnPropertyChanged.Broadcast(); + + if (Property->HasMetaData("Recreate")) + { + UVoxelBlueprintLibrary::Recreate(this, true); + } + else if (Property->HasMetaData("RecreateRender")) + { + RecreateRender(); + } + else if (Property->HasMetaData("RecreateSpawners")) + { + RecreateSpawners(); + } + else if (Property->HasMetaData("UpdateAll")) + { + UpdateDynamicLODSettings(); + GetLODManager().UpdateBounds(FVoxelIntBox::Infinite); + } + else if (Property->HasMetaData("UpdateLODs")) + { + UpdateDynamicLODSettings(); + GetLODManager().ForceLODsUpdate(); + } + else if (Property->HasMetaData("UpdateRenderer")) + { + UpdateDynamicRendererSettings(); + GetRenderer().ApplyNewMaterials(); + } + } + } + + if (bUseCustomWorldBounds) + { + CustomWorldBounds = GetWorldBounds(); + } + + bDisableComponentUnregister = true; + Super::PostEditChangeProperty(PropertyChangedEvent); + bDisableComponentUnregister = false; +} +#endif + +void AVoxelWorld::UpdateCollisionProfile() +{ +#if WITH_EDITOR + // This is needed to restore transient collision profile data. + CollisionPresets.LoadProfileData(false); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AVoxelWorld::OnWorldLoadedCallback() +{ + LOG_VOXEL(Log, TEXT("%s took %fs to generate"), *GetName(), FPlatformTime::Seconds() - TimeOfCreation); + bIsLoaded = true; + OnWorldLoaded.Broadcast(); +} + +TVoxelSharedRef AVoxelWorld::CreateDebugManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelDebugManager::Create(FVoxelDebugManagerSettings(this, PlayType, Pool.ToSharedRef(), Data.ToSharedRef())); +} + +TVoxelSharedRef AVoxelWorld::CreateData() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelData::Create(FVoxelDataSettings(this, PlayType), DataOctreeInitialSubdivisionDepth); +} + +TVoxelSharedRef AVoxelWorld::CreatePool() const +{ + VOXEL_FUNCTION_COUNTER(); + + const auto CreateOwnPool = [&](int32 InNumberOfThreads, bool bInConstantPriorities) + { + return FVoxelDefaultPool::Create( + FMath::Max(1, InNumberOfThreads), + bInConstantPriorities, + PriorityCategories, + PriorityOffsets); + }; + + if (PlayType == EVoxelPlayType::Preview) + { + return CreateOwnPool(NumberOfThreadsForPreview, true); + } + else + { + if (bCreateGlobalPool) + { + const auto ExistingPool = IVoxelPool::GetPoolForWorld(GetWorld()); + if (!ExistingPool.IsValid()) + { + const auto NewPool = CreateOwnPool(NumberOfThreads, bConstantPriorities); + IVoxelPool::SetWorldPool(GetWorld(), NewPool, GetName()); + return NewPool; + } + else + { + FVoxelMessages::Warning( + "CreateGlobalPool = true but global or world pool is already created! Using existing one, NumberOfThreads will be ignored.\n" + "Consider setting CreateGlobalPool to false and calling CreateWorldVoxelThreadPool at BeginPlay (for instance in your level blueprint).", + this); + return ExistingPool.ToSharedRef(); + } + } + else + { + const auto ExistingPool = IVoxelPool::GetPoolForWorld(GetWorld()); + if (ExistingPool.IsValid()) + { + return ExistingPool.ToSharedRef(); + } + else + { + FVoxelMessages::Warning( + "CreateGlobalPool = false but global pool isn't created! Creating it with default setting NumberOfThreads = 2. " + "You need to call CreateWorldVoxelThreadPool at BeginPlay (for instance in your level blueprint).", + this); + + const auto NewPool = CreateOwnPool(NumberOfThreads, bConstantPriorities); + IVoxelPool::SetWorldPool(GetWorld(), NewPool, GetName()); + return NewPool; + } + } + } +} + +TVoxelSharedRef AVoxelWorld::CreateRenderer() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelDefaultRenderer::Create(FVoxelRendererSettings( + this, + PlayType, + WorldRoot, + Data.ToSharedRef(), + Pool.ToSharedRef(), + ToolRenderingManager.ToSharedRef(), + DebugManager.ToSharedRef(), + false)); +} + +TVoxelSharedRef AVoxelWorld::CreateLODManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelDefaultLODManager::Create( + FVoxelLODSettings(this, + PlayType, + Renderer.ToSharedRef(), + Pool.ToSharedRef()), + this, + LODDynamicSettings); +} + +TVoxelSharedPtr AVoxelWorld::CreateEventManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelEventManager::Create(FVoxelEventManagerSettings(this, PlayType)); +} + +TVoxelSharedPtr AVoxelWorld::CreateToolRenderingManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return MakeVoxelShared(); +} + +TVoxelSharedRef AVoxelWorld::CreateMultiplayerManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelMultiplayerManager::Create(FVoxelMultiplayerSettings( + this, + Data.ToSharedRef(), + DebugManager.ToSharedRef(), + LODManager.ToSharedRef())); +} + +TVoxelSharedRef AVoxelWorld::CreateInstancedMeshManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelInstancedMeshManager::Create(FVoxelInstancedMeshManagerSettings(this, Pool.ToSharedRef(), EventManager.ToSharedRef())); +} + +TVoxelSharedRef AVoxelWorld::CreateSpawnerManager() const +{ + VOXEL_FUNCTION_COUNTER(); + return FVoxelSpawnerManager::Create(FVoxelSpawnerSettings( + this, + PlayType, + Pool.ToSharedRef(), + DebugManager.ToSharedRef(), + Data.ToSharedRef(), + LODManager.ToSharedRef(), + Renderer.ToSharedRef(), + InstancedMeshManager.ToSharedRef(), + EventManager.ToSharedRef())); +} + +void AVoxelWorld::CreateWorldInternal(const FVoxelWorldCreateInfo& Info) +{ + VOXEL_FUNCTION_COUNTER(); + + check(!IsCreated()); + + LOG_VOXEL(Log, TEXT("Loading world")); + + bIsCreated = true; + bIsLoaded = false; + TimeOfCreation = FPlatformTime::Seconds(); + + if (!Generator.IsValid()) + { + FVoxelMessages::Error("Invalid generator!", this); + } + + // Setup root + ApplyCollisionSettingsToRoot(); + + if (PlayType == EVoxelPlayType::Game && WorldRoot->BodyInstance.bSimulatePhysics && CollisionTraceFlag == ECollisionTraceFlag::CTF_UseComplexAsSimple) + { + FVoxelMessages::Error("Simulate physics requires using Simple collisions (either 'Simple And Complex' or 'Use Simple Collision As Complex')", this); + } + + GetLineBatchComponent().Flush(); + + *WorldOffset = FIntVector::ZeroValue; + UpdateDynamicLODSettings(); + UpdateDynamicRendererSettings(); + + ensure(!GeneratorCache); + GeneratorCache = NewObject(this); + GeneratorCache->SetGeneratorInit(GetGeneratorInit()); + + GameThreadTasks = MakeVoxelShared(); + + if (Info.bOverrideData) + { + ensure(!(Info.DataOverride && Info.DataOverride_Raw)); + if (Info.DataOverride) + { + if (Info.DataOverride->IsCreated()) + { + Data = Info.DataOverride->GetDataSharedPtr(); + } + else + { + FVoxelMessages::Warning(FUNCTION_ERROR("Info.DataOverride is not created! Can't copy data from it."), this); + } + } + else if (Info.DataOverride_Raw) + { + Data = Info.DataOverride_Raw; + } + else + { + FVoxelMessages::Warning(FUNCTION_ERROR("Info.bOverrideData is true, but DataOverride is null!"), this); + } + } + if (!Data) + { + Data = CreateData(); + } + Pool = CreatePool(); + DebugManager = CreateDebugManager(); + EventManager = CreateEventManager(); + ToolRenderingManager = CreateToolRenderingManager(); + Renderer = CreateRenderer(); + Renderer->OnWorldLoaded.AddUObject(this, &AVoxelWorld::OnWorldLoadedCallback); + LODManager = CreateLODManager(); + + InstancedMeshManager = CreateInstancedMeshManager(); + SpawnerManager = CreateSpawnerManager(); + + if (bEnableMultiplayer) + { + if (PlayType == EVoxelPlayType::Game) + { + MultiplayerManager = CreateMultiplayerManager(); + } + } + + if (Info.bOverrideData && Info.bOverrideSave) + { + FVoxelMessages::Warning(FUNCTION_ERROR("Cannot use Info.bOverrideSave if Info.bOverrideData is true!"), this); + } + + if (!Info.bOverrideData) + { + // Load if possible + if (Info.bOverrideSave) + { + UVoxelDataTools::LoadFromSave(this, Info.SaveOverride); + } + else if (SaveObject) + { + LoadFromSaveObject(); + if (!IsCreated()) // if LoadFromSaveObject destroyed the world + { + return; + } + } + + // Add placeable items AFTER loading + if (!PlaceableItemManager) + { + // This lets objects adds new items without having to specify a custom class + PlaceableItemManager = NewObject(); + } + PlaceableItemManager->SetExternalGeneratorCache(GeneratorCache); + PlaceableItemManager->Clear(); + PlaceableItemManager->Generate(); + PlaceableItemManager->ApplyToData(GetData()); + PlaceableItemManager->DrawDebug(*this, GetLineBatchComponent()); + + // Do that after Clear/Generate + ApplyPlaceableItems(); + + // Let events add other items + OnGenerateWorld.Broadcast(); + + ensure(!PlaceableItemActorHelper); + PlaceableItemActorHelper = NewObject(this); + PlaceableItemActorHelper->Initialize(); + } + + if (PlayType == EVoxelPlayType::Preview) + { + UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(false); + } +} + +void AVoxelWorld::DestroyWorldInternal() +{ + VOXEL_FUNCTION_COUNTER(); + + check(IsCreated()); + +#if WITH_EDITOR + if (PlayType == EVoxelPlayType::Preview) + { + SaveData(); + } +#endif + + LOG_VOXEL(Log, TEXT("Unloading world")); + + bIsCreated = false; + bIsLoaded = false; + + Data.Reset(); + Pool.Reset(); + + DebugManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), DebugManager); + + EventManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), EventManager); + + Renderer->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), Renderer); + + LODManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), LODManager); + + InstancedMeshManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), InstancedMeshManager); + + SpawnerManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), SpawnerManager); + + if (MultiplayerManager.IsValid()) + { + MultiplayerManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), MultiplayerManager); + } + + WorldOffset = MakeVoxelShared(FIntVector::ZeroValue); + + GameThreadTasks->Flush(); + GameThreadTasks.Reset(); + + // Clear generator cache to avoid keeping instances alive + if (ensure(GeneratorCache)) + { + GeneratorCache->ClearCache(); + GeneratorCache->MarkPendingKill(); + GeneratorCache = nullptr; + } + + if (PlaceableItemActorHelper) + { + PlaceableItemActorHelper->MarkPendingKill(); + PlaceableItemActorHelper = nullptr; + } + + DestroyVoxelComponents(); + + if (LineBatchComponent) + { + // Note: components might be destroyed if called in BeginDestroy (eg if actor deleted in the editor) + LineBatchComponent->Flush(); + } +} + +void AVoxelWorld::DestroyVoxelComponents() +{ + VOXEL_FUNCTION_COUNTER(); + + auto Components = GetComponents(); // need a copy as we are modifying it when destroying comps + for (auto& Component : Components) + { + if (IsValid(Component) && Component->HasAnyFlags(RF_Transient) && (Component->IsA() || Component->IsA())) + { + Component->DestroyComponent(); + } + } +} + +void AVoxelWorld::LoadFromSaveObject() +{ + VOXEL_FUNCTION_COUNTER(); + + check(SaveObject); + + if (SaveObject->Depth == -1) + { + // Not saved yet + return; + } + + FVoxelUncompressedWorldSave Save; + UVoxelSaveUtilities::DecompressVoxelSave(SaveObject->Save, Save); + + if (Save.Const().GetDepth() == -1) + { + FVoxelMessages::Error("Invalid Save Object!", this); + return; + } + if (Save.Const().GetDepth() > Data->Depth) + { + LOG_VOXEL(Warning, TEXT("Save Object depth is bigger than world depth, the save data outside world bounds will be ignored")); + } + + if (!UVoxelDataTools::LoadFromSave(this, Save)) + { + const auto Result = FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("Some errors occured when loading from the save object. Do you want to continue? This might corrupt the save object\n" + "Note: always keep your voxel world toggled when renaming assets referenced by it, else the references won't be updated")); + + if (Result != EAppReturnType::Yes) + { + Data->ClearData(); + PlayType = EVoxelPlayType::Game; // Hack + DestroyWorldInternal(); + } + } +} + +void AVoxelWorld::ApplyPlaceableItems() +{ + VOXEL_FUNCTION_COUNTER(); + + TArray PlaceableItemActors; + for (TActorIterator ActorItr(GetWorld()); ActorItr; ++ActorItr) + { + auto* Actor = *ActorItr; + if (Actor->IsA() && !bMergeAssetActors) + { + continue; + } + if (Actor->IsA() && !bMergeDisableEditsBoxes) + { + continue; + } + if (Actor->bOnlyImportIntoPreviewWorld && Actor->PreviewWorld != this) + { + continue; + } + PlaceableItemActors.Add(Actor); + } + + TGuardValue AllowScriptsInEditor(GAllowActorScriptExecutionInEditor, true); + + TMap Priorities; + for (auto* It : PlaceableItemActors) + { + Priorities.Add(It, It->K2_GetPriority()); + } + PlaceableItemActors.Sort([&](auto& ActorA, auto& ActorB) { return Priorities[&ActorA] < Priorities[&ActorB]; }); + + for (auto* PlaceableItemActor : PlaceableItemActors) + { + PlaceableItemActor->K2_AddItemToWorld(this); + } +} + +void AVoxelWorld::UpdateDynamicLODSettings() const +{ + LODDynamicSettings->MinLOD = FVoxelUtilities::ClampMesherDepth(MinLOD); + LODDynamicSettings->MaxLOD = FVoxelUtilities::ClampMesherDepth(MaxLOD); + + LODDynamicSettings->InvokerDistanceThreshold = InvokerDistanceThreshold; + + LODDynamicSettings->ChunksCullingLOD = FVoxelUtilities::ClampDepth(ChunksCullingLOD); + + LODDynamicSettings->bEnableRender = bRenderWorld; + + LODDynamicSettings->bEnableCollisions = PlayType == EVoxelPlayType::Game ? bEnableCollisions : true; + LODDynamicSettings->bComputeVisibleChunksCollisions = PlayType == EVoxelPlayType::Game ? bComputeVisibleChunksCollisions : true; + LODDynamicSettings->VisibleChunksCollisionsMaxLOD = FVoxelUtilities::ClampDepth(PlayType == EVoxelPlayType::Game ? VisibleChunksCollisionsMaxLOD : 32); + + LODDynamicSettings->bEnableNavmesh = bEnableNavmesh; // bEnableNavmesh is needed for path previews in editor + + LODDynamicSettings->bComputeVisibleChunksNavmesh = bComputeVisibleChunksNavmesh; + LODDynamicSettings->VisibleChunksNavmeshMaxLOD = FVoxelUtilities::ClampDepth(VisibleChunksNavmeshMaxLOD); +} + +void AVoxelWorld::UpdateDynamicRendererSettings() const +{ + VOXEL_FUNCTION_COUNTER(); + + TArray MaterialCollectionsToInitialize; + for (int32 LOD = 0; LOD < 32; LOD++) + { + auto& LODData = RendererDynamicSettings->LODData[LOD]; + + // Copy materials + LODData.Material = nullptr; + for (auto& It : LODMaterials) + { + if (It.StartLOD <= LOD && LOD <= It.EndLOD) + { + if (LODData.Material.IsValid()) + { + FVoxelMessages::Warning(FString::Printf(TEXT("Multiple materials are assigned to LOD %d!"), LOD), this); + } + LODData.Material = It.Material; + } + } + if (!LODData.Material.IsValid()) + { + LODData.Material = VoxelMaterial; + } + + // Copy material collection + LODData.MaterialCollection = nullptr; + for (auto& It : LODMaterialCollections) + { + if (It.StartLOD <= LOD && LOD <= It.EndLOD) + { + if (LODData.MaterialCollection.IsValid()) + { + FVoxelMessages::Warning(FString::Printf(TEXT("Multiple material collections are assigned to LOD %d!"), LOD), this); + } + LODData.MaterialCollection = It.MaterialCollection; + } + } + if (!LODData.MaterialCollection.IsValid()) + { + LODData.MaterialCollection = MaterialCollection; + } + + // Set MaxMaterialIndices + if (auto* Collection = LODData.MaterialCollection.Get()) + { + LODData.MaxMaterialIndices.Set(FMath::Max(Collection->GetMaxMaterialIndices(), 1)); + MaterialCollectionsToInitialize.AddUnique(Collection); + } + else + { + LODData.MaxMaterialIndices.Set(1); + } + } + + // Initialize all used collections + for (auto* Collection : MaterialCollectionsToInitialize) + { + Collection->InitializeCollection(); + } +} + +void AVoxelWorld::ApplyCollisionSettingsToRoot() const +{ + VOXEL_FUNCTION_COUNTER(); + + WorldRoot->CollisionTraceFlag = CollisionTraceFlag; + WorldRoot->CanCharacterStepUpOn = CanCharacterStepUpOn; + WorldRoot->SetGenerateOverlapEvents(bGenerateOverlapEvents); + // Only copy collision data - we don't want to override the physics settings + WorldRoot->BodyInstance.CopyRuntimeBodyInstancePropertiesFrom(&CollisionPresets); + WorldRoot->BodyInstance.SetObjectType(CollisionPresets.GetObjectType()); + WorldRoot->BodyInstance.SetPhysMaterialOverride(PhysMaterialOverride); + WorldRoot->BodyInstance.bNotifyRigidBodyCollision = bNotifyRigidBodyCollision; + WorldRoot->BodyInstance.bUseCCD = bUseCCD; + WorldRoot->RecreatePhysicsState(); +} + +void AVoxelWorld::RecreateRender() +{ + VOXEL_FUNCTION_COUNTER(); + check(IsCreated()); + + UpdateDynamicLODSettings(); + UpdateDynamicRendererSettings(); + + if (MultiplayerManager.IsValid()) + { + MultiplayerManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), MultiplayerManager); + } + + SpawnerManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), SpawnerManager); + + InstancedMeshManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), InstancedMeshManager); + + LODManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), LODManager); + + Renderer->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), Renderer); + + DestroyVoxelComponents(); + + Renderer = CreateRenderer(); + LODManager = CreateLODManager(); + + InstancedMeshManager = CreateInstancedMeshManager(); + SpawnerManager = CreateSpawnerManager(); + + if (PlayType == EVoxelPlayType::Game && bEnableMultiplayer) + { + MultiplayerManager = CreateMultiplayerManager(); + } +} + +void AVoxelWorld::RecreateSpawners() +{ + SpawnerManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), SpawnerManager); + + InstancedMeshManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), InstancedMeshManager); + + InstancedMeshManager = CreateInstancedMeshManager(); + SpawnerManager = CreateSpawnerManager(); +} + +void AVoxelWorld::RecreateAll(const FVoxelWorldCreateInfo& Info) +{ + check(IsCreated()); + + DestroyWorldInternal(); + CreateWorldInternal(Info); +} + +#if WITH_EDITOR +void AVoxelWorld::Toggle() +{ + MarkPackageDirty(); + if (IsCreated()) + { + // ensure(bIsToggled); + bIsToggled = false; + DestroyWorldInternal(); + } + else + { + // ensure(!bIsToggled); + bIsToggled = true; + CreateInEditor(); + if (!IsCreated()) // Load failed + { + bIsToggled = false; + } + } +} + +void AVoxelWorld::CreateInEditor(const FVoxelWorldCreateInfo& Info) +{ + if (!IVoxelWorldEditor::GetVoxelWorldEditor()) + { + return; + } + if (!VoxelWorldEditor) + { + UClass* VoxelWorldEditorClass = IVoxelWorldEditor::GetVoxelWorldEditor()->GetVoxelWorldEditorClass(); + + if (VoxelWorldEditorClass) + { + // Create/Find VoxelWorldEditor + for (TActorIterator It(GetWorld(), VoxelWorldEditorClass); It; ++It) + { + VoxelWorldEditor = *It; + break; + } + if (!VoxelWorldEditor) + { + FActorSpawnParameters Transient; + Transient.ObjectFlags = RF_Transient; + VoxelWorldEditor = GetWorld()->SpawnActor(VoxelWorldEditorClass, Transient); + } + } + } + + BindEditorDelegates(this); + + if (IsCreated()) + { + DestroyWorldInternal(); + } + PlayType = EVoxelPlayType::Preview; + CreateWorldInternal(Info); +} + +void AVoxelWorld::SaveData() +{ + if (IsGarbageCollecting()) + { + return; + } + check(IsCreated()); + if (Data->IsDirty() && GetTransientPackage() != nullptr) + { + if (!SaveObject && ensure(IVoxelWorldEditor::GetVoxelWorldEditor())) + { + const EAppReturnType::Type Result = FMessageDialog::Open( + EAppMsgType::YesNo, + VOXEL_LOCTEXT("The voxel world Save Object is null. You need to create one now if you don't want to lose your changes.\n\nSave changes?")); + + if (Result == EAppReturnType::No) + { + // Clear dirty flag so we don't get more annoying popups + Data->ClearDirtyFlag(); + return; + } + + Modify(); + SaveObject = IVoxelWorldEditor::GetVoxelWorldEditor()->CreateSaveObject(); + } + if (SaveObject) + { + { + FVoxelScopedSlowTask Progress(3); + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Rounding voxels")); + if (GetDefault()->bRoundBeforeSaving) + { + UVoxelDataTools::RoundVoxels(this, FVoxelIntBox::Infinite); + } + + FVoxelUncompressedWorldSave Save; + + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Creating save")); + UVoxelDataTools::GetSave(this, Save); + + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Compressing save")); + UVoxelSaveUtilities::CompressVoxelSave(Save, SaveObject->Save); + } + + SaveObject->CopyDepthFromSave(); + SaveObject->MarkPackageDirty(); + + FNotificationInfo Info = FNotificationInfo(VOXEL_LOCTEXT("Voxel world saved!")); + Info.CheckBoxState = ECheckBoxState::Checked; + FSlateNotificationManager::Get().AddNotification(Info); + + Data->ClearDirtyFlag(); + } + else + { + FNotificationInfo Info = FNotificationInfo(VOXEL_LOCTEXT("Voxel world not saved: no Save Object!")); + Info.CheckBoxState = ECheckBoxState::Unchecked; + FSlateNotificationManager::Get().AddNotification(Info); + } + + if (bAutomaticallySaveToFile) + { + FText Error; + if (!SaveToFile(GetDefaultFilePath(), Error)) + { + FMessageDialog::Open(EAppMsgType::Ok, Error); + } + } + } +} + +inline bool CanLoad(const TVoxelSharedPtr& Data) +{ + if (Data->IsDirty()) + { + const auto Result = FMessageDialog::Open(EAppMsgType::YesNoCancel, VOXEL_LOCTEXT("There are unsaved changes. Loading will overwrite them. Confirm?")); + if (Result != EAppReturnType::Yes) + { + return false; + } + } + return true; +} + +void AVoxelWorld::LoadFromSaveObjectEditor() +{ + check(SaveObject); + if (!CanLoad(Data)) + { + return; + } + + LoadFromSaveObject(); + + if (IsCreated()) + { + const FNotificationInfo Info(VOXEL_LOCTEXT("Loaded!")); + FSlateNotificationManager::Get().AddNotification(Info); + } +} + +bool AVoxelWorld::SaveToFile(const FString& Path, FText& Error) +{ + check(IsCreated()); + + if (Path.IsEmpty()) + { + Error = VOXEL_LOCTEXT("Empty Save File Path"); + return false; + } + else if (!FPaths::ValidatePath(Path, &Error)) + { + return false; + } + + IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); + + PlatformFile.CreateDirectoryTree(*FPaths::GetPath(Path)); + + FBufferArchive Archive(true); + + FVoxelCompressedWorldSave CompressedSave; + UVoxelDataTools::GetCompressedSave(this, CompressedSave); + CompressedSave.Serialize(Archive); + + if (FFileHelper::SaveArrayToFile(Archive, *Path)) + { + const FNotificationInfo Info(FText::Format(VOXEL_LOCTEXT("{0} was successfully created"), FText::FromString(Path))); + FSlateNotificationManager::Get().AddNotification(Info); + + if (CompressedSave.Objects.Num() > 0) + { + const FNotificationInfo OtherInfo(FText::Format(VOXEL_LOCTEXT("The voxel data has {0} placeable items (eg, data assets)! These will not be saved"), CompressedSave.Objects.Num())); + FSlateNotificationManager::Get().AddNotification(OtherInfo); + } + + return true; + } + else + { + Error = FText::Format(VOXEL_LOCTEXT("Error when creating {0}"), FText::FromString(Path)); + return false; + } +} + +bool AVoxelWorld::LoadFromFile(const FString& Path, FText& Error) +{ + if (!CanLoad(Data)) + { + Error = VOXEL_LOCTEXT("Canceled"); + return false; + } + + TArray Array; + if (!FFileHelper::LoadFileToArray(Array, *Path)) + { + Error = FText::Format(VOXEL_LOCTEXT("Error when reading {0}"), FText::FromString(Path)); + return false; + } + + FMemoryReader Reader(Array); + FVoxelCompressedWorldSave CompressedSave; + CompressedSave.Serialize(Reader); + UVoxelDataTools::LoadFromCompressedSave(this, CompressedSave); + + const FNotificationInfo Info(FText::Format(VOXEL_LOCTEXT("{0} was successfully loaded"), FText::FromString(Path))); + FSlateNotificationManager::Get().AddNotification(Info); + return true; +} + +FString AVoxelWorld::GetDefaultFilePath() const +{ + FString Path = SaveFilePath; + if (Path.IsEmpty()) + { + return Path; + } + Path.RemoveFromEnd(".voxelsave"); + int32 Year = 0, Month = 0, DayOfWeek = 0, Day = 0, Hour = 0, Min = 0, Sec = 0, MSec = 0; + FPlatformTime::SystemTime(Year, Month, DayOfWeek, Day, Hour, Min, Sec, MSec); + const FString Sep = TEXT("_"); + const FString Date = FString::FromInt(Year) + + Sep + FString::FromInt(Month) + + Sep + FString::FromInt(Day) + + Sep + FString::FromInt(Hour) + + Sep + FString::FromInt(Min) + + Sep + FString::FromInt(Sec) + + Sep + FString::FromInt(MSec); + Path += Date; + Path += ".voxelsave"; + return Path; +} + +void AVoxelWorld::OnPreSaveWorld(uint32 SaveFlags, UWorld* World) +{ + if (IsCreated() && PlayType == EVoxelPlayType::Preview) + { + SaveData(); + } +} + +void AVoxelWorld::OnPreBeginPIE(bool bIsSimulating) +{ + if (PlayType == EVoxelPlayType::Preview && IsCreated()) + { + DestroyWorldInternal(); + } +} + +void AVoxelWorld::OnEndPIE(bool bIsSimulating) +{ + if (PlayType == EVoxelPlayType::Preview && ensure(!IsCreated()) && bIsToggled) + { + CreateInEditor(); + } +} + +void AVoxelWorld::OnPrepareToCleanseEditorObject(UObject* Object) +{ + if (PlayType == EVoxelPlayType::Preview && IsCreated()) + { + DestroyWorldInternal(); + } +} + +void AVoxelWorld::OnPreExit() +{ + bIsToggled = false; // Disable saving data +} + +void AVoxelWorld::OnApplyObjectToActor(UObject* Object, AActor* Actor) +{ + if (Actor != this) + { + return; + } + + if (auto* CastedObject = Cast(Object)) + { + MarkPackageDirty(); + + VoxelMaterial = CastedObject; + MaterialConfig = EVoxelMaterialConfig::RGB; + RecreateRender(); + } +} + +void IVoxelWorldEditor::SetVoxelWorldEditor(TSharedPtr InVoxelWorldEditor) +{ + check(!VoxelWorldEditor.IsValid()); + VoxelWorldEditor = InVoxelWorldEditor; +} + +TSharedPtr IVoxelWorldEditor::VoxelWorldEditor; +IVoxelEditorDelegatesInterface::FBindEditorDelegates IVoxelEditorDelegatesInterface::BindEditorDelegatesDelegate; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorldRootComponent.cpp b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorldRootComponent.cpp new file mode 100644 index 00000000..62bbc794 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Private/VoxelWorldRootComponent.cpp @@ -0,0 +1,382 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelWorldRootComponent.h" +#include "VoxelMinimal.h" +#include "PhysXIncludes.h" +#include "PrimitiveSceneProxy.h" +#include "Engine/Engine.h" +#include "Materials/Material.h" + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +UVoxelWorldRootComponent::FConvexElements::FConvexElements( + const FBox& Bounds, + TArray&& InConvexElements, + TArray&& InConvexMeshes) + : Bounds(Bounds) + , ConvexElements(MoveTemp(InConvexElements)) + , ConvexMeshes(MoveTemp(InConvexMeshes)) +{ + ensure(Bounds.IsValid); + ensure(ConvexElements.Num() == ConvexMeshes.Num()); + for (auto* ConvexMesh : ConvexMeshes) + { + ConvexMesh->acquireReference(); + } +} + +UVoxelWorldRootComponent::FConvexElements::~FConvexElements() +{ + for (auto* ConvexMesh : ConvexMeshes) + { + ConvexMesh->release(); + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelWorldRootComponent::UVoxelWorldRootComponent() +{ + // Tricky: the voxel world will hack to skip registering components when they have bAllowReregistration = false + // Else it's laggy as hell when editing voxel world properties (too many components) + // And only registering this one & not the proc meshes messes up the physics + bAllowReregistration = false; +} + +UVoxelWorldRootComponent::~UVoxelWorldRootComponent() +{ +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + ensure(Elements.Num() == 0); +#endif +} + +UBodySetup* UVoxelWorldRootComponent::GetBodySetup() +{ + VOXEL_FUNCTION_COUNTER(); + + if (!BodySetup) + { + BodySetup = NewObject(this); + BodySetup->bGenerateMirroredCollision = false; + } + BodySetup->CollisionTraceFlag = CollisionTraceFlag; + return BodySetup; +} + +FBoxSphereBounds UVoxelWorldRootComponent::CalcBounds(const FTransform& LocalToWorld) const +{ + return LocalBounds.TransformBy(LocalToWorld); +} + +void UVoxelWorldRootComponent::TickWorldRoot() +{ + VOXEL_FUNCTION_COUNTER(); + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + if (bRebuildQueued) + { + bRebuildQueued = false; + RebuildConvexCollision(); + } +#endif +} + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX +void UVoxelWorldRootComponent::UpdateConvexCollision( + uint64 Id, + const FBox& InBounds, + TArray&& ConvexElements, + TArray&& ConvexMeshes) +{ + ensure(CollisionTraceFlag != CTF_UseComplexAsSimple); + ensure(ConvexElements.Num() == ConvexMeshes.Num()); + + if (ConvexElements.Num() == 0) + { + if (Elements.Remove(Id) == 1) + { + bRebuildQueued = true; + } + return; + } + + Elements.Add(Id, MakeUnique(InBounds, MoveTemp(ConvexElements), MoveTemp(ConvexMeshes))); + bRebuildQueued = true; +} + +void UVoxelWorldRootComponent::RebuildConvexCollision() +{ + VOXEL_FUNCTION_COUNTER(); + ensure(CollisionTraceFlag != CTF_UseComplexAsSimple); + + { + VOXEL_SCOPE_COUNTER("Update bounds"); + FBox LocalBox(ForceInit); + for (auto& It : Elements) + { + LocalBox += It.Value->Bounds; + } + LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) : FBoxSphereBounds(ForceInit); // fallback to reset box sphere bounds + UpdateBounds(); + } + + // Create body setup + GetBodySetup(); + + // Note: we do not need to call ClearPhysicsMeshes, as we are setting the convex meshes manually & handling the ref count ourselves + + auto& ConvexElements = BodySetup->AggGeom.ConvexElems; + + int32 NumConvexElements = 0; + { + VOXEL_SCOPE_COUNTER("Count"); + for(auto& It : Elements) + { + NumConvexElements += It.Value->ConvexElements.Num(); + } + } + if (NumConvexElements == 0) return; + + { + VOXEL_SCOPE_COUNTER("Lock"); + BodySetupLock.Lock(); + } + + ConvexElements.Reset(); + { + VOXEL_SCOPE_COUNTER("Reserve"); + ConvexElements.Reserve(NumConvexElements); + } + + { + VOXEL_SCOPE_COUNTER("Create"); + for(auto& It : Elements) + { + auto& Part = *It.Value; + for (int32 Index = 0; Index < Part.ConvexMeshes.Num(); Index++) + { + auto& NewElement = *new (ConvexElements) FKConvexElem(); + // No need to copy the vertices + NewElement.ElemBox = Part.ConvexElements[Index].ElemBox; + NewElement.SetConvexMesh(Part.ConvexMeshes[Index]); + } + } + } + { + VOXEL_SCOPE_COUNTER("Unlock"); + BodySetupLock.Unlock(); + } + + { + // Must not be locked! + VOXEL_SCOPE_COUNTER("FreeRenderInfo"); + BodySetup->AggGeom.FreeRenderInfo(); + } + + BodySetup->bCreatedPhysicsMeshes = true; + + bool bHasVelocity = false; + FVector Velocity; + FVector AngularVelocity; + if (BodyInstance.IsValidBodyInstance()) + { + bHasVelocity = FPhysicsCommand::ExecuteRead(BodyInstance.ActorHandle, [&](const FPhysicsActorHandle& Actor) + { + Velocity = FPhysicsInterface::GetLinearVelocity_AssumesLocked(Actor); + AngularVelocity = FPhysicsInterface::GetAngularVelocity_AssumesLocked(Actor); + }); + BodyInstance.TermBody(); + } + + BodyInstance.InitBody(BodySetup, GetComponentTransform(), this, GetWorld()->GetPhysicsScene()); + + if (bHasVelocity) + { + // Restore velocity + FPhysicsCommand::ExecuteWrite(BodyInstance.ActorHandle, [&](const FPhysicsActorHandle& Actor) + { + FPhysicsInterface::SetLinearVelocity_AssumesLocked(Actor, Velocity); + FPhysicsInterface::SetAngularVelocity_AssumesLocked(Actor, AngularVelocity); + }); + } +} + +#if ENGINE_MINOR_VERSION >= 24 +class UMRMeshComponent +{ +public: + static void FinishCreatingPhysicsMeshes(UBodySetup* Body, const TArray& ConvexMeshes, const TArray& ConvexMeshesNegX, const TArray& TriMeshes) + { + Body->FinishCreatingPhysicsMeshes_PhysX(ConvexMeshes, ConvexMeshesNegX, TriMeshes); + } +}; +#endif + +void UVoxelWorldRootComponent::SetCookedTriMeshes(const TArray& TriMeshes) +{ + VOXEL_FUNCTION_COUNTER(); + + // Create body setup + GetBodySetup(); + +#if ENGINE_MINOR_VERSION < 24 + BodySetup->FinishCreatingPhysicsMeshes({}, {}, TriMeshes); +#else + UMRMeshComponent::FinishCreatingPhysicsMeshes(BodySetup, {}, {}, TriMeshes); +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelRenderSimpleCollisionSceneProxy : public FPrimitiveSceneProxy +{ +public: + FVoxelRenderSimpleCollisionSceneProxy(UVoxelWorldRootComponent* Component) + : FPrimitiveSceneProxy(Component) + , Component(Component) + , CollisionResponse(Component->GetCollisionResponseToChannels()) + , CollisionTraceFlag(Component->CollisionTraceFlag) + { + } + + //~ Begin FPrimitiveSceneProxy Interface + virtual void GetDynamicMeshElements( + const TArray& Views, + const FSceneViewFamily& ViewFamily, + uint32 VisibilityMap, + FMeshElementCollector& Collector) const override + { + VOXEL_RENDER_FUNCTION_COUNTER(); + + // Render bounds + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (VisibilityMap & (1 << ViewIndex)) + { + RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected()); + } + } + + if (!DrawSimpleCollision(ViewFamily.EngineShowFlags)) return; + + const UBodySetup* BodySetup = Component->BodySetup; // Not entirely thread safe, but it's for debug so w/e + if (!BodySetup) return; + + // Catch this here or otherwise GeomTransform below will assert + if (FMath::Abs(GetLocalToWorld().Determinant()) < SMALL_NUMBER) return; + + const FColor SimpleCollisionColor = FColor(157, 149, 223, 255); + + // Make a material for drawing solid collision stuff + auto* SolidMaterialInstance = new FColoredMaterialRenderProxy( + GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(), + SimpleCollisionColor); + Collector.RegisterOneFrameMaterialProxy(SolidMaterialInstance); + + const FTransform GeomTransform(GetLocalToWorld()); + + FScopeLock Lock(&Component->BodySetupLock); + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + BodySetup->AggGeom.GetAggGeom( + GeomTransform, + SimpleCollisionColor, + SolidMaterialInstance, + true, + true, + DrawsVelocity(), + ViewIndex, + Collector); + } + +#if 0 + // Draw wireframe to have one color per hull + auto* WireframeMaterialInstance = new FColoredMaterialRenderProxy( + GEngine->WireframeMaterial->GetRenderProxy(), + SimpleCollisionColor); + Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance); + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (!(VisibilityMap & (1 << ViewIndex))) continue; + BodySetup->AggGeom.GetAggGeom( + GeomTransform, + SimpleCollisionColor, + WireframeMaterialInstance, + true, + false, + DrawsVelocity(), + ViewIndex, + Collector); + } +#endif + } + virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override + { + FPrimitiveViewRelevance Result; + Result.bDrawRelevance = true; + Result.bShadowRelevance = false; + Result.bDynamicRelevance = true; + Result.bRenderInMainPass = true; + Result.bUsesLightingChannels = false; + Result.bRenderCustomDepth = false; + Result.bTranslucentSelfShadow = false; + Result.bVelocityRelevance = false; + return Result; + } + virtual bool CanBeOccluded() const override + { + return false; + } + virtual uint32 GetMemoryFootprint() const override + { + return sizeof(*this); + } + virtual SIZE_T GetTypeHash() const override + { + static size_t UniquePointer; + return reinterpret_cast(&UniquePointer); + } + +private: + UVoxelWorldRootComponent* const Component; + const FCollisionResponseContainer CollisionResponse; + const ECollisionTraceFlag CollisionTraceFlag; + + bool DrawSimpleCollision(const FEngineShowFlags& EngineShowFlags) const + { + const bool bInCollisionView = EngineShowFlags.CollisionVisibility || EngineShowFlags.CollisionPawn; + + // If in a 'collision view' and collision is enabled + if (bInCollisionView && IsCollisionEnabled()) + { + // See if we have a response to the interested channel + bool bHasResponse = EngineShowFlags.CollisionPawn && CollisionResponse.GetResponse(ECC_Pawn) != ECR_Ignore; + bHasResponse |= EngineShowFlags.CollisionVisibility && CollisionResponse.GetResponse(ECC_Visibility) != ECR_Ignore; + + if (bHasResponse) + { + return + (EngineShowFlags.CollisionPawn && CollisionTraceFlag != ECollisionTraceFlag::CTF_UseComplexAsSimple) || + (EngineShowFlags.CollisionVisibility && CollisionTraceFlag == ECollisionTraceFlag::CTF_UseSimpleAsComplex); + } + } + + return false; + } +}; + +FPrimitiveSceneProxy* UVoxelWorldRootComponent::CreateSceneProxy() +{ +#if (UE_BUILD_SHIPPING || UE_BUILD_TEST) + return nullptr; +#else + return new FVoxelRenderSimpleCollisionSceneProxy(this); +#endif +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise.h new file mode 100644 index 00000000..2df0c372 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise.h @@ -0,0 +1,8 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise.h" + +UE_DEPRECATED(4.24, "Please use FVoxelFastNoise instead of FastNoise, and include FastNoise/VoxelFastNoise.inl instead of FastNoise.h") +typedef FVoxelFastNoise FastNoise; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/CrossPlatformSTD.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/CrossPlatformSTD.h new file mode 100644 index 00000000..ac32a00a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/CrossPlatformSTD.h @@ -0,0 +1,499 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include +#include + +// While random generators are standardized, samplers aren't +// To avoid issues, we use the microsoft std version on all platforms +// License: + +/* + The Microsoft C++ Standard Library is under the Apache License v2.0 with LLVM Exception: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +*/ + +namespace cross_platform_std +{ + // CLASS TEMPLATE _Rng_from_urng + template + class _Rng_from_urng { // wrap a URNG as an RNG + public: + using _Ty0 = std::make_unsigned_t<_Diff>; + using _Ty1 = typename _Urng::result_type; + + using _Udiff = std::conditional_t < sizeof(_Ty1) < sizeof(_Ty0), _Ty0, _Ty1 > ; + + explicit _Rng_from_urng(_Urng& _Func) : _Ref(_Func), _Bits(8 * sizeof(_Udiff)), _Bmask(_Udiff(-1)) { + for (; (_Urng::max)() - (_Urng::min)() < _Bmask; _Bmask >>= 1) { + --_Bits; + } + } + + _Diff operator()(_Diff _Index) { // adapt _Urng closed range to [0, _Index) + for (;;) { // try a sample random value + _Udiff _Ret = 0; // random bits + _Udiff _Mask = 0; // 2^N - 1, _Ret is within [0, _Mask] + + while (_Mask < _Udiff(_Index - 1)) { // need more random bits + _Ret <<= _Bits - 1; // avoid full shift + _Ret <<= 1; + _Ret |= _Get_bits(); + _Mask <<= _Bits - 1; // avoid full shift + _Mask <<= 1; + _Mask |= _Bmask; + } + + // _Ret is [0, _Mask], _Index - 1 <= _Mask, return if unbiased + if (_Ret / _Index < _Mask / _Index || _Mask % _Index == _Udiff(_Index - 1)) { + return static_cast<_Diff>(_Ret % _Index); + } + } + } + + _Udiff _Get_all_bits() { // return a random value + _Udiff _Ret = 0; + + for (size_t _Num = 0; _Num < 8 * sizeof(_Udiff); _Num += _Bits) { // don't mask away any bits + _Ret <<= _Bits - 1; // avoid full shift + _Ret <<= 1; + _Ret |= _Get_bits(); + } + + return _Ret; + } + + _Rng_from_urng(const _Rng_from_urng&) = delete; + _Rng_from_urng& operator=(const _Rng_from_urng&) = delete; + + private: + _Udiff _Get_bits() { // return a random value within [0, _Bmask] + for (;;) { // repeat until random value is in range + _Udiff _Val = _Ref() - (_Urng::min)(); + + if (_Val <= _Bmask) { + return _Val; + } + } + } + + _Urng& _Ref; // reference to URNG + size_t _Bits; // number of random bits generated by _Get_bits() + _Udiff _Bmask; // 2^_Bits - 1 + }; + + // CLASS TEMPLATE uniform_int + template + class uniform_int { // uniform integer distribution + public: + using result_type = _Ty; + + struct param_type { // parameter package + using distribution_type = uniform_int; + + explicit param_type(result_type _Min0 = 0, result_type _Max0 = 9) { // construct from parameters + _Init(_Min0, _Max0); + } + + bool operator==(const param_type& _Right) const { // test for equality + return _Min == _Right._Min && _Max == _Right._Max; + } + + bool operator!=(const param_type& _Right) const { // test for inequality + return !(*this == _Right); + } + + result_type a() const { // return a value + return _Min; + } + + result_type b() const { // return b value + return _Max; + } + + void _Init(_Ty _Min0, _Ty _Max0) { // set internal state + _Min = _Min0; + _Max = _Max0; + } + + result_type _Min; + result_type _Max; + }; + + explicit uniform_int(_Ty _Min0 = 0, _Ty _Max0 = 9) : _Par(_Min0, _Max0) { // construct from parameters + } + + explicit uniform_int(const param_type& _Par0) : _Par(_Par0) { // construct from parameter package + } + + result_type a() const { // return a value + return _Par.a(); + } + + result_type b() const { // return b value + return _Par.b(); + } + + param_type param() const { // return parameter package + return _Par; + } + + void param(const param_type& _Par0) { // set parameter package + _Par = _Par0; + } + + result_type(min)() const { // return minimum possible generated value + return _Par._Min; + } + + result_type(max)() const { // return maximum possible generated value + return _Par._Max; + } + + void reset() { // clear internal state + } + + template + result_type operator()(_Engine& _Eng) const { // return next value + return _Eval(_Eng, _Par._Min, _Par._Max); + } + + template + result_type operator()( + _Engine& _Eng, const param_type& _Par0) const { // return next value, given parameter package + return _Eval(_Eng, _Par0._Min, _Par0._Max); + } + + template + result_type operator()(_Engine& _Eng, result_type _Nx) const { // return next value + return _Eval(_Eng, 0, _Nx - 1); + } + + template + std::basic_istream<_Elem, _Traits>& _Read(std::basic_istream<_Elem, _Traits>& _Istr) { // read state from _Istr + _Ty _Min0; + _Ty _Max0; + _Istr >> _Min0 >> _Max0; + _Par._Init(_Min0, _Max0); + return _Istr; + } + + template + std::basic_ostream<_Elem, _Traits>& _Write(std::basic_ostream<_Elem, _Traits>& _Ostr) const { // write state to _Ostr + return _Ostr << _Par._Min << ' ' << _Par._Max; + } + + private: + using _Uty = std::make_unsigned_t<_Ty>; + + template + result_type _Eval(_Engine& _Eng, _Ty _Min, _Ty _Max) const { // compute next value in range [_Min, _Max] + _Rng_from_urng<_Uty, _Engine> _Rng(_Eng); + + const _Uty _Umin = _Adjust(_Uty(_Min)); + const _Uty _Umax = _Adjust(_Uty(_Max)); + + _Uty _Uret; + + if (_Umax - _Umin == _Uty(-1)) { + _Uret = static_cast<_Uty>(_Rng._Get_all_bits()); + } + else { + _Uret = static_cast<_Uty>(_Rng(static_cast<_Uty>(_Umax - _Umin + 1))); + } + + return _Ty(_Adjust(static_cast<_Uty>(_Uret + _Umin))); + } + + static _Uty _Adjust(_Uty _Uval) { // convert signed ranges to unsigned ranges and vice versa + return _Adjust(_Uval, std::is_signed<_Ty>()); + } + + static _Uty _Adjust(_Uty _Uval, std::true_type) { // convert signed ranges to unsigned ranges and vice versa + const _Uty _Adjuster = (_Uty(-1) >> 1) + 1; // 2^(N-1) + + if (_Uval < _Adjuster) { + return static_cast<_Uty>(_Uval + _Adjuster); + } + else { + return static_cast<_Uty>(_Uval - _Adjuster); + } + } + + static _Uty _Adjust(_Uty _Uval, std::false_type) { // _Ty is already unsigned, do nothing + return _Uval; + } + + param_type _Par; + }; + + template + std::basic_istream<_Elem, _Traits>& operator>>(std::basic_istream<_Elem, _Traits>& _Istr, + uniform_int<_Ty>& _Dist) { // read state from _Istr + return _Dist._Read(_Istr); + } + + template + std::basic_ostream<_Elem, _Traits>& operator<<(std::basic_ostream<_Elem, _Traits>& _Ostr, + const uniform_int<_Ty>& _Dist) { // write state to _Ostr + return _Dist._Write(_Ostr); + } + + // CLASS TEMPLATE uniform_int_distribution + template + class uniform_int_distribution : public uniform_int<_Ty> { // uniform integer distribution + public: + using _Mybase = uniform_int<_Ty>; + using _Mypbase = typename _Mybase::param_type; + using result_type = typename _Mybase::result_type; + + struct param_type : public _Mypbase { // parameter package + using distribution_type = uniform_int_distribution; + + explicit param_type(result_type _Min0 = 0, result_type _Max0 = (std::numeric_limits<_Ty>::max)()) + : _Mypbase(_Min0, _Max0) { // construct from parameters + } + + param_type(const _Mypbase& _Right) : _Mypbase(_Right) { // construct from base + } + }; + + explicit uniform_int_distribution(_Ty _Min0 = 0, _Ty _Max0 = (std::numeric_limits<_Ty>::max)()) + : _Mybase(_Min0, _Max0) { // construct from parameters + } + + explicit uniform_int_distribution(const param_type& _Par0) : _Mybase(_Par0) { // construct from parameter package + } + }; + + template + bool operator==(const uniform_int_distribution<_Ty>& _Left, + const uniform_int_distribution<_Ty>& _Right) { // test for equality + return _Left.param() == _Right.param(); + } + + template + bool operator!=(const uniform_int_distribution<_Ty>& _Left, + const uniform_int_distribution<_Ty>& _Right) { // test for inequality + return !(_Left == _Right); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.h new file mode 100644 index 00000000..356af149 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.h @@ -0,0 +1,124 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "FastNoise/VoxelFastNoise_CubicNoise.h" +#include "FastNoise/VoxelFastNoise_ValueNoise.h" +#include "FastNoise/VoxelFastNoise_WhiteNoise.h" +#include "FastNoise/VoxelFastNoise_PerlinNoise.h" +#include "FastNoise/VoxelFastNoise_SimplexNoise.h" +#include "FastNoise/VoxelFastNoise_CellularNoise.h" +#include "FastNoise/VoxelFastNoise_GradientPerturb.h" + +// You will need to include FastNoise/VoxelFastNoise.inl to use fast noise functions +class FVoxelFastNoise : public + TVoxelFastNoise_CubicNoise< + TVoxelFastNoise_ValueNoise< + TVoxelFastNoise_WhiteNoise< + TVoxelFastNoise_PerlinNoise< + TVoxelFastNoise_SimplexNoise< + TVoxelFastNoise_CellularNoise< + TVoxelFastNoise_GradientPerturb< + FVoxelFastNoiseBase>>>>>>> +{ +public: + FVoxelFastNoise() = default; + + UE_DEPRECATED(4.24, "Use SetInterpolation instead. If this is a compiled graph, recompile the graph") + void SetInterp(EVoxelNoiseInterpolation NewInterpolation) { SetInterpolation(NewInterpolation); } + + UE_DEPRECATED(4.24, "Use EVoxelNoiseInterpolation::Linear instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseInterpolation Linear = EVoxelNoiseInterpolation::Linear; + UE_DEPRECATED(4.24, "Use EVoxelNoiseInterpolation::Hermite instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseInterpolation Hermite = EVoxelNoiseInterpolation::Hermite; + UE_DEPRECATED(4.24, "Use EVoxelNoiseInterpolation::Quintic instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseInterpolation Quintic = EVoxelNoiseInterpolation::Quintic; + + UE_DEPRECATED(4.24, "Use EVoxelNoiseFractalType::FBM instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseFractalType FBM = EVoxelNoiseFractalType::FBM; + UE_DEPRECATED(4.24, "Use EVoxelNoiseFractalType::Billow instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseFractalType Billow = EVoxelNoiseFractalType::Billow; + UE_DEPRECATED(4.24, "Use EVoxelNoiseFractalType::RigidMulti instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelNoiseFractalType RigidMulti = EVoxelNoiseFractalType::RigidMulti; + + UE_DEPRECATED(4.24, "Use EVoxelCellularDistanceFunction::Euclidean instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularDistanceFunction Euclidean = EVoxelCellularDistanceFunction::Euclidean; + UE_DEPRECATED(4.24, "Use EVoxelCellularDistanceFunction::Manhattan instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularDistanceFunction Manhattan = EVoxelCellularDistanceFunction::Manhattan; + UE_DEPRECATED(4.24, "Use EVoxelCellularDistanceFunction::Natural instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularDistanceFunction Natural = EVoxelCellularDistanceFunction::Natural; + + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::CellValue instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType CellValue = EVoxelCellularReturnType::CellValue; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance = EVoxelCellularReturnType::Distance; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance2 instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance2 = EVoxelCellularReturnType::Distance2; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance2Add instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance2Add = EVoxelCellularReturnType::Distance2Add; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance2Sub instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance2Sub = EVoxelCellularReturnType::Distance2Sub; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance2Mul instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance2Mul = EVoxelCellularReturnType::Distance2Mul; + UE_DEPRECATED(4.24, "Use EVoxelCellularReturnType::Distance2Div instead. If this is a compiled graph, recompile the graph") + static constexpr EVoxelCellularReturnType Distance2Div = EVoxelCellularReturnType::Distance2Div; + + UE_DEPRECATED(4.24, "Use IQNoise_2D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt IQNoiseDeriv_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const + { + return IQNoise_2D_Deriv(x, y, frequency, octaves, outDx, outDy); + } + UE_DEPRECATED(4.24, "Use IQNoise_3D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt IQNoiseDeriv_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const + { + return IQNoise_3D_Deriv(x, y, z, frequency, octaves, outDx, outDy, outDz); + } + + UE_DEPRECATED(4.24, "Use GetValue_2D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetValueDeriv_2D(v_flt x, v_flt y, v_flt frequency, v_flt& outDx, v_flt& outDy) const + { + return GetValue_2D_Deriv(x, y, frequency, outDx, outDy); + } + UE_DEPRECATED(4.24, "Use GetValue_3D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetValueDeriv_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, v_flt& outDx, v_flt& outDy, v_flt& outDz) const + { + return GetValue_3D_Deriv(x, y, z, frequency, outDx, outDy, outDz); + } + + UE_DEPRECATED(4.24, "Use GetValueFractal_2D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetValueFractalDeriv_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const + { + return GetValueFractal_2D_Deriv(x, y, frequency, octaves, outDx, outDy); + } + UE_DEPRECATED(4.24, "Use GetValueFractal_3D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetValueFractalDeriv_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const + { + return GetValueFractal_3D_Deriv(x, y, z, frequency, octaves, outDx, outDy, outDz); + } + + UE_DEPRECATED(4.24, "Use GetPerlin_2D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetPerlinDeriv_2D(v_flt x, v_flt y, v_flt frequency, v_flt& outDx, v_flt& outDy) const + { + return GetPerlin_2D_Deriv(x, y, frequency, outDx, outDy); + } + UE_DEPRECATED(4.24, "Use GetPerlin_3D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetPerlinDeriv_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, v_flt& outDx, v_flt& outDy, v_flt& outDz) const + { + return GetPerlin_3D_Deriv(x, y, z, frequency, outDx, outDy, outDz); + } + + UE_DEPRECATED(4.24, "Use GetPerlinFractal_2D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetPerlinFractalDeriv_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const + { + return GetPerlinFractal_2D_Deriv(x, y, frequency, octaves, outDx, outDy); + } + UE_DEPRECATED(4.24, "Use GetPerlinFractal_3D_Deriv instead. If this is a compiled graph, recompile the graph") + v_flt GetPerlinFractalDeriv_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const + { + return GetPerlinFractal_3D_Deriv(x, y, z, frequency, octaves, outDx, outDy, outDz); + } + + UE_DEPRECATED(4.24, "GetSimplex_4D has been removed") + v_flt GetSimplex_4D(v_flt x, v_flt y, v_flt z, v_flt w, v_flt frequency) const { return 0; } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.inl new file mode 100644 index 00000000..ffff508f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise.inl @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise.h" +#include "FastNoise/VoxelFastNoise_CubicNoise.inl" +#include "FastNoise/VoxelFastNoise_ValueNoise.inl" +#include "FastNoise/VoxelFastNoise_WhiteNoise.inl" +#include "FastNoise/VoxelFastNoise_PerlinNoise.inl" +#include "FastNoise/VoxelFastNoise_SimplexNoise.inl" +#include "FastNoise/VoxelFastNoise_CellularNoise.inl" +#include "FastNoise/VoxelFastNoise_GradientPerturb.inl" \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.h new file mode 100644 index 00000000..d2acc5ec --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.h @@ -0,0 +1,290 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Math/TransformCalculus2D.h" +#include "Math/TransformCalculus3D.h" +#include "FastNoise/VoxelFastNoiseLUT.h" +#include "FastNoise/VoxelFastNoiseMath.h" +#include "VoxelFastNoiseBase.generated.h" + +UENUM(BlueprintType) +enum class EVoxelNoiseInterpolation : uint8 +{ + Linear, + Hermite, + Quintic +}; + +UENUM(BlueprintType) +enum class EVoxelNoiseFractalType : uint8 +{ + FBM, + Billow, + RigidMulti +}; + +UENUM(BlueprintType) +enum class EVoxelCellularDistanceFunction : uint8 +{ + Euclidean, + Manhattan, + Natural +}; + +UENUM(BlueprintType) +enum class EVoxelCellularReturnType : uint8 +{ + CellValue, + Distance, + Distance2, + Distance2Add, + Distance2Sub, + Distance2Mul, + Distance2Div +}; + +#if INTELLISENSE_PARSER +#define FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(op) \ + op(EVoxelCellularDistanceFunction::Euclidean) \ + op(EVoxelCellularDistanceFunction::Manhattan) \ + op(EVoxelCellularDistanceFunction::Natural) +#endif + +#define DEFINE_VOXEL_NOISE_CLASS() \ + FORCEINLINE const FVoxelFastNoiseBase& This() const \ + { \ + return static_cast(*this); \ + } \ + using FNoiseMath = FVoxelFastNoiseMath; + + +class FVoxelFastNoiseBase : public FVoxelFastNoiseLUT +{ +public: + DEFINE_VOXEL_NOISE_CLASS(); + + // Changes the interpolation method used to smooth between noise values + // Possible interpolation methods (lowest to highest quality) : + // - Linear + // - Hermite + // - Quintic + // Used in Value, Perlin Noise and Position Warping + // Default: Quintic + void SetInterpolation(EVoxelNoiseInterpolation NewInterpolation) { Interpolation = NewInterpolation; } + EVoxelNoiseInterpolation GetInterpolation() const { return Interpolation; } + + // Sets octave lacunarity for all fractal noise types + // Default: 2.0 + void SetFractalLacunarity(v_flt NewLacunarity) { Lacunarity = NewLacunarity; } + v_flt GetFractalLacunarity() const { return Lacunarity; } + + // Sets octave gain for all fractal noise types + // Default: 0.5 + void SetFractalOctavesAndGain(int32 Octaves, v_flt NewGain) { Gain = NewGain; CalculateFractalBounding(Octaves); } + v_flt GetFractalGain() const { return Gain; } + + // Sets method for combining octaves in all fractal noise types + // Default: FBM + void SetFractalType(EVoxelNoiseFractalType NewFractalType) { FractalType = NewFractalType; } + EVoxelNoiseFractalType GetFractalType() const { return FractalType; } + + // Used by IQ noise + void SetMatrix(const FMatrix2x2& NewMatrix) { Matrix2 = NewMatrix; } + void SetMatrix(const FMatrix& NewMatrix) { Matrix3 = NewMatrix; } + + void SetMatrixFromRotation_2D(float RotationInDegrees) { Matrix2 = FMatrix2x2(FQuat2D(FMath::DegreesToRadians(RotationInDegrees))); } + void SetMatrixFromRotation_3D(const FRotator& Rotation) { Matrix3 = ToMatrix(Rotation); } + + // Sets distance function used in cellular noise calculations + // Default: Euclidean + void SetCellularDistanceFunction(EVoxelCellularDistanceFunction NewCellularDistanceFunction) { CellularDistanceFunction = NewCellularDistanceFunction; } + EVoxelCellularDistanceFunction GetCellularDistanceFunction() const { return CellularDistanceFunction; } + + // Sets return type from cellular noise calculations + // Note: NoiseLookup requires another FVoxelFastNoise object be set with SetCellularNoiseLookup() to function + // Default: CellValue + void SetCellularReturnType(EVoxelCellularReturnType NewCellularReturnType) { CellularReturnType = NewCellularReturnType; } + EVoxelCellularReturnType GetCellularReturnType() const { return CellularReturnType; } + + // Sets the maximum distance a cellular point can move from its grid position + // Setting this high will make artifacts more common + // Default: 0.45 + void SetCellularJitter(v_flt NewCellularJitter) { CellularJitter = NewCellularJitter; } + v_flt GetCellularJitter() const { return CellularJitter; } + + // Higher value = flatter crater borders + // 0 to disable (much faster disabled) + void SetCraterFalloffExponent(v_flt NewCraterFalloffExponent) { CraterFalloffExponent = NewCraterFalloffExponent; } + v_flt GetCraterFalloffExponent() const { return CraterFalloffExponent; } + +protected: + EVoxelNoiseInterpolation Interpolation = EVoxelNoiseInterpolation::Quintic; + + v_flt Lacunarity = v_flt(2); + v_flt Gain = v_flt(0.5); + EVoxelNoiseFractalType FractalType = EVoxelNoiseFractalType::FBM; + v_flt FractalBounding = 1; + + FMatrix2x2 Matrix2; + FMatrix Matrix3; + + EVoxelCellularDistanceFunction CellularDistanceFunction = EVoxelCellularDistanceFunction::Euclidean; + EVoxelCellularReturnType CellularReturnType = EVoxelCellularReturnType::CellValue; + v_flt CellularJitter = v_flt(0.45); + v_flt CraterFalloffExponent = 0.f; + +protected: + template + void Interpolate_2D( + T fx, T fy, + T& xs, T& ys) const; + template + void Interpolate_2D_Deriv( + T fx, T fy, + T& xs, T& ys, + T& dx, T& dy) const; + + template + void Interpolate_3D( + T fx, T fy, T fz, + T& xs, T& ys, T& zs) const; + template + void Interpolate_3D_Deriv( + T fx, T fy, T fz, + T& xs, T& ys, T& zs, + T& dx, T& dy, T& dz) const; + +protected: + template + v_flt Fractal_2D(T GetNoise, v_flt x, v_flt y, v_flt frequency, int32 octaves) const; + template + v_flt Fractal_2D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const; + +private: + template + v_flt FractalFBM_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const; + template + v_flt FractalFBM_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const; + + template + v_flt FractalBillow_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const; + template + v_flt FractalBillow_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const; + + template + v_flt FractalRigidMulti_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const; + template + v_flt FractalRigidMulti_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const; + +protected: + template + v_flt Fractal_3D(T GetNoise, v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves) const; + template + v_flt Fractal_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; + +private: + template + v_flt FractalFBM_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const; + template + v_flt FractalFBM_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; + + template + v_flt FractalBillow_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const; + template + v_flt FractalBillow_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; + + template + v_flt FractalRigidMulti_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const; + template + v_flt FractalRigidMulti_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; + +private: + void CalculateFractalBounding(int32 Octaves); + + template friend class TVoxelFastNoise_ValueNoise; + template friend class TVoxelFastNoise_CubicNoise; + template friend class TVoxelFastNoise_WhiteNoise; + template friend class TVoxelFastNoise_PerlinNoise; + template friend class TVoxelFastNoise_SimplexNoise; + template friend class TVoxelFastNoise_CellularNoise; + template friend class TVoxelFastNoise_GradientPerturb; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define DEFINE_VOXEL_NOISE_LAMBDA(ClassName, Function) \ + struct FLambda_##Function \ + { \ + const ClassName& This; \ + \ + template \ + FN_FORCEINLINE_MATH decltype(auto) operator()(TArgs&&... Args) const \ + { \ + return This.Function(Forward(Args)...); \ + } \ + }; \ + friend FLambda_##Function; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GENERATED_VOXEL_NOISE_FUNCTION_2D(FunctionName) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## _2D(v_flt x, v_flt y, v_flt frequency) const \ + { \ + return Single ## FunctionName ## _2D(0, x * frequency, y * frequency); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_2D_DERIV(FunctionName) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## _2D_Deriv(v_flt x, v_flt y, v_flt frequency, v_flt& outDx, v_flt& outDy) const \ + { \ + return Single ## FunctionName ## _2D_Deriv(0, x * frequency, y * frequency, outDx, outDy); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_3D(FunctionName) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## _3D(v_flt x, v_flt y, v_flt z, v_flt frequency) const \ + { \ + return Single ## FunctionName ## _3D(0, x * frequency, y * frequency, z * frequency); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_3D_DERIV(FunctionName) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## _3D_Deriv(v_flt x, v_flt y, v_flt z, v_flt frequency, v_flt& outDx, v_flt& outDy, v_flt& outDz) const \ + { \ + return Single ## FunctionName ## _3D_Deriv(0, x * frequency, y * frequency, z * frequency, outDx, outDy, outDz); \ + } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(ClassName, FunctionName) \ + DEFINE_VOXEL_NOISE_LAMBDA(TVoxelFastNoise_ ## ClassName ## Noise, Single ## FunctionName ## _2D) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## Fractal_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves) const \ + { \ + return This().Fractal_2D(FLambda_ ## Single ## FunctionName ## _2D { *this }, x, y, frequency, octaves); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D_DERIV(ClassName, FunctionName) \ + DEFINE_VOXEL_NOISE_LAMBDA(TVoxelFastNoise_ ## ClassName ## Noise, Single ## FunctionName ## _2D_Deriv) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## Fractal_2D_Deriv(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const \ + { \ + return This().Fractal_2D_Deriv(FLambda_ ## Single ## FunctionName ## _2D_Deriv { *this }, x, y, frequency, octaves, outDx, outDy); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(ClassName, FunctionName) \ + DEFINE_VOXEL_NOISE_LAMBDA(TVoxelFastNoise_ ## ClassName ## Noise, Single ## FunctionName ## _3D) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## Fractal_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves) const \ + { \ + return This().Fractal_3D(FLambda_ ## Single ## FunctionName ## _3D { *this }, x, y, z, frequency, octaves); \ + } + +#define GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D_DERIV(ClassName, FunctionName) \ + DEFINE_VOXEL_NOISE_LAMBDA(TVoxelFastNoise_ ## ClassName ## Noise, Single ## FunctionName ## _3D_Deriv) \ + FN_FORCEINLINE v_flt Get ## FunctionName ## Fractal_3D_Deriv(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const \ + { \ + return This().Fractal_3D_Deriv(FLambda_ ## Single ## FunctionName ## _3D_Deriv { *this }, x, y, z, frequency, octaves, outDx, outDy, outDz); \ + } \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.inl new file mode 100644 index 00000000..0bbf35c0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.inl @@ -0,0 +1,544 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoiseBase.h" +#include "FastNoise/VoxelFastNoiseLUT.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +#define VOXEL_FRACTAL_TYPE_SWITCH(Macro) \ + switch (This().FractalType) \ + { \ + default: ensureVoxelSlow(false); \ + case EVoxelNoiseFractalType::FBM: \ + return Macro(FBM); \ + case EVoxelNoiseFractalType::Billow: \ + return Macro(Billow); \ + case EVoxelNoiseFractalType::RigidMulti: \ + return Macro(RigidMulti); \ + } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH void FVoxelFastNoiseBase::CalculateFractalBounding(int32 Octaves) +{ + float amp = Gain; + float ampFractal = 1.0f; + for (int32 Index = 1; Index < Octaves; Index++) + { + ampFractal += amp; + amp *= Gain; + } + FractalBounding = 1.0f / ampFractal; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_MATH void FVoxelFastNoiseBase::Interpolate_2D(T fx, T fy, T& xs, T& ys) const +{ + switch (Interpolation) + { + default: ensureVoxelSlow(false); + case EVoxelNoiseInterpolation::Linear: + xs = fx; + ys = fy; + break; + case EVoxelNoiseInterpolation::Hermite: + xs = FNoiseMath::InterpHermiteFunc(fx); + ys = FNoiseMath::InterpHermiteFunc(fy); + break; + case EVoxelNoiseInterpolation::Quintic: + xs = FNoiseMath::InterpQuinticFunc(fx); + ys = FNoiseMath::InterpQuinticFunc(fy); + break; + } +} + +template +FN_FORCEINLINE_MATH void FVoxelFastNoiseBase::Interpolate_2D_Deriv(T fx, T fy, T& xs, T& ys, T& dx, T& dy) const +{ + switch (Interpolation) + { + default: ensureVoxelSlow(false); + case EVoxelNoiseInterpolation::Linear: + xs = fx; + ys = fy; + dx = 1; + dy = 1; + break; + case EVoxelNoiseInterpolation::Hermite: + xs = FNoiseMath::InterpHermiteFunc(fx); + ys = FNoiseMath::InterpHermiteFunc(fy); + dx = FNoiseMath::InterpHermiteFuncDeriv(fx); + dy = FNoiseMath::InterpHermiteFuncDeriv(fy); + break; + case EVoxelNoiseInterpolation::Quintic: + xs = FNoiseMath::InterpQuinticFunc(fx); + ys = FNoiseMath::InterpQuinticFunc(fy); + dx = FNoiseMath::InterpQuinticFuncDeriv(fx); + dy = FNoiseMath::InterpQuinticFuncDeriv(fy); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_MATH void FVoxelFastNoiseBase::Interpolate_3D(T fx, T fy, T fz, T& xs, T& ys, T& zs) const +{ + switch (Interpolation) + { + default: ensureVoxelSlow(false); + case EVoxelNoiseInterpolation::Linear: + xs = fx; + ys = fy; + zs = fz; + break; + case EVoxelNoiseInterpolation::Hermite: + xs = FNoiseMath::InterpHermiteFunc(fx); + ys = FNoiseMath::InterpHermiteFunc(fy); + zs = FNoiseMath::InterpHermiteFunc(fz); + break; + case EVoxelNoiseInterpolation::Quintic: + xs = FNoiseMath::InterpQuinticFunc(fx); + ys = FNoiseMath::InterpQuinticFunc(fy); + zs = FNoiseMath::InterpQuinticFunc(fz); + break; + } +} + +template +FN_FORCEINLINE_MATH void FVoxelFastNoiseBase::Interpolate_3D_Deriv(T fx, T fy, T fz, T& xs, T& ys, T& zs, T& dx, T& dy, T& dz) const +{ + switch (Interpolation) + { + default: ensureVoxelSlow(false); + case EVoxelNoiseInterpolation::Linear: + xs = fx; + ys = fy; + zs = fz; + dx = 1; + dy = 1; + dz = 1; + break; + case EVoxelNoiseInterpolation::Hermite: + xs = FNoiseMath::InterpHermiteFunc(fx); + ys = FNoiseMath::InterpHermiteFunc(fy); + zs = FNoiseMath::InterpHermiteFunc(fz); + dx = FNoiseMath::InterpHermiteFuncDeriv(fx); + dy = FNoiseMath::InterpHermiteFuncDeriv(fy); + dz = FNoiseMath::InterpHermiteFuncDeriv(fz); + break; + case EVoxelNoiseInterpolation::Quintic: + xs = FNoiseMath::InterpQuinticFunc(fx); + ys = FNoiseMath::InterpQuinticFunc(fy); + zs = FNoiseMath::InterpQuinticFunc(fz); + dx = FNoiseMath::InterpQuinticFuncDeriv(fx); + dy = FNoiseMath::InterpQuinticFuncDeriv(fy); + dz = FNoiseMath::InterpQuinticFuncDeriv(fz); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::Fractal_2D(T GetNoise, v_flt x, v_flt y, v_flt frequency, int32 octaves) const +{ +#define Macro(Type) Fractal##Type##_2D(GetNoise, x * frequency, y * frequency, octaves) + VOXEL_FRACTAL_TYPE_SWITCH(Macro) +#undef Macro +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::Fractal_2D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const +{ +#define Macro(Type) Fractal##Type##_2D_Deriv(GetNoise, x * frequency, y * frequency, octaves, outDx, outDy) + VOXEL_FRACTAL_TYPE_SWITCH(Macro) +#undef Macro +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalFBM_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const +{ + v_flt sum = GetNoise(Perm[0], x, y); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + + amp *= Gain; + + sum += GetNoise(Perm[i], x, y) * amp; + } + + return sum * FractalBounding; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalFBM_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const +{ + v_flt sum = GetNoise(Perm[0], x, y, outDx, outDy); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + + amp *= Gain; + + v_flt dx, dy; + sum += GetNoise(Perm[i], x, y, dx, dy) * amp; + + outDx += amp * dx; + outDy += amp * dy; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + return sum * FractalBounding; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalBillow_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const +{ + v_flt sum = FNoiseMath::FastAbs(GetNoise(Perm[0], x, y)) * 2 - 1; + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + amp *= Gain; + sum += (FNoiseMath::FastAbs(GetNoise(Perm[i], x, y)) * 2 - 1) * amp; + } + + return sum * FractalBounding; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalBillow_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const +{ + v_flt dx, dy; + v_flt value = GetNoise(Perm[0], x, y, dx, dy); + + v_flt sum = FNoiseMath::FastAbs(value) * 2 - 1; + outDx = FNoiseMath::FastAbsDeriv(value, dx) * 2; + outDy = FNoiseMath::FastAbsDeriv(value, dy) * 2; + + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + amp *= Gain; + + value = GetNoise(Perm[i], x, y, dx, dy); + + sum += (FNoiseMath::FastAbs(value) * 2 - 1) * amp; + outDx += FNoiseMath::FastAbsDeriv(value, dx) * 2 * amp; + outDy += FNoiseMath::FastAbsDeriv(value, dy) * 2 * amp; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + return sum * FractalBounding; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalRigidMulti_2D(T GetNoise, v_flt x, v_flt y, int32 octaves) const +{ + v_flt sum = 1 - FNoiseMath::FastAbs(GetNoise(Perm[0], x, y)); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + + amp *= Gain; + sum -= (1 - FNoiseMath::FastAbs(GetNoise(Perm[i], x, y))) * amp; + } + + return sum; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalRigidMulti_2D_Deriv(T GetNoise, v_flt x, v_flt y, int32 octaves, v_flt& outDx, v_flt& outDy) const +{ + v_flt dx, dy; + v_flt value = GetNoise(Perm[0], x, y, dx, dy); + + v_flt sum = 1 - FNoiseMath::FastAbs(value); + outDx = -FNoiseMath::FastAbsDeriv(value, dx); + outDy = -FNoiseMath::FastAbsDeriv(value, dy); + + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + amp *= Gain; + + value = GetNoise(Perm[i], x, y, dx, dy); + + sum -= (1 - FNoiseMath::FastAbs(value)) * amp; + outDx -= -FNoiseMath::FastAbsDeriv(value, dx) * amp; + outDy -= -FNoiseMath::FastAbsDeriv(value, dy) * amp; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + return sum; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::Fractal_3D(T GetNoise, v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves) const +{ +#define Macro(Type) Fractal##Type##_3D(GetNoise, x * frequency, y * frequency, z * frequency, octaves) + VOXEL_FRACTAL_TYPE_SWITCH(Macro) +#undef Macro +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::Fractal_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ +#define Macro(Type) Fractal##Type##_3D_Deriv(GetNoise, x * frequency, y * frequency, z * frequency, octaves, outDx, outDy, outDz) + VOXEL_FRACTAL_TYPE_SWITCH(Macro) +#undef Macro +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalFBM_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const +{ + v_flt sum = GetNoise(Perm[0], x, y, z); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + + amp *= Gain; + + sum += GetNoise(Perm[i], x, y, z) * amp; + } + + return sum * FractalBounding; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalFBM_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + v_flt sum = GetNoise(Perm[0], x, y, z, outDx, outDy, outDz); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + + amp *= Gain; + + v_flt dx, dy, dz; + sum += GetNoise(Perm[i], x, y, z, dx, dy, dz) * amp; + + outDx += amp * dx; + outDy += amp * dy; + outDz += amp * dz; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + outDz *= FractalBounding; + return sum * FractalBounding; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalBillow_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const +{ + v_flt sum = FNoiseMath::FastAbs(GetNoise(Perm[0], x, y, z)) * 2 - 1; + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + amp *= Gain; + sum += (FNoiseMath::FastAbs(GetNoise(Perm[i], x, y, z)) * 2 - 1) * amp; + } + + return sum * FractalBounding; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalBillow_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + v_flt dx, dy, dz; + v_flt value = GetNoise(Perm[0], x, y, z, dx, dy, dz); + + v_flt sum = FNoiseMath::FastAbs(value) * 2 - 1; + outDx = FNoiseMath::FastAbsDeriv(value, dx) * 2; + outDy = FNoiseMath::FastAbsDeriv(value, dy) * 2; + outDz = FNoiseMath::FastAbsDeriv(value, dz) * 2; + + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + amp *= Gain; + + value = GetNoise(Perm[i], x, y, z, dx, dy, dz); + + sum += (FNoiseMath::FastAbs(value) * 2 - 1) * amp; + outDx += FNoiseMath::FastAbsDeriv(value, dx) * 2 * amp; + outDy += FNoiseMath::FastAbsDeriv(value, dy) * 2 * amp; + outDz += FNoiseMath::FastAbsDeriv(value, dz) * 2 * amp; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + outDz *= FractalBounding; + return sum * FractalBounding; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalRigidMulti_3D(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves) const +{ + v_flt sum = 1 - FNoiseMath::FastAbs(GetNoise(Perm[0], x, y, z)); + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + + amp *= Gain; + sum -= (1 - FNoiseMath::FastAbs(GetNoise(Perm[i], x, y, z))) * amp; + } + + return sum; +} + +template +FN_FORCEINLINE v_flt FVoxelFastNoiseBase::FractalRigidMulti_3D_Deriv(T GetNoise, v_flt x, v_flt y, v_flt z, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + v_flt dx, dy, dz; + v_flt value = GetNoise(Perm[0], x, y, z, dx, dy, dz); + + v_flt sum = 1 - FNoiseMath::FastAbs(value); + outDx = -FNoiseMath::FastAbsDeriv(value, dx); + outDy = -FNoiseMath::FastAbsDeriv(value, dy); + outDz = -FNoiseMath::FastAbsDeriv(value, dz); + + v_flt amp = 1; + int32 i = 0; + + while (++i < octaves) + { + x *= Lacunarity; + y *= Lacunarity; + z *= Lacunarity; + amp *= Gain; + + value = GetNoise(Perm[i], x, y, z, dx, dy, dz); + + sum -= (1 - FNoiseMath::FastAbs(value)) * amp; + outDx -= -FNoiseMath::FastAbsDeriv(value, dx) * amp; + outDy -= -FNoiseMath::FastAbsDeriv(value, dy) * amp; + outDz -= -FNoiseMath::FastAbsDeriv(value, dz) * amp; + } + + outDx *= FractalBounding; + outDy *= FractalBounding; + outDz *= FractalBounding; + return sum; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.h new file mode 100644 index 00000000..6ebf453d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.h @@ -0,0 +1,211 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" + +class FVoxelFastNoiseLUT +{ +public: + FVoxelFastNoiseLUT() = default; + + VOXEL_API void SetSeed(int32 NewSeed); + int32 GetSeed() const { return Seed; } + +protected: + TVoxelStaticArray Perm; + TVoxelStaticArray Perm12; + + int32 Seed = 0; + +protected: + uint8 Index2D_12(uint8 offset, int32 x, int32 y) const; + uint8 Index3D_12(uint8 offset, int32 x, int32 y, int32 z) const; + uint8 Index4D_32(uint8 offset, int32 x, int32 y, int32 z, int32 w) const; + uint8 Index2D_256(uint8 offset, int32 x, int32 y) const; + uint8 Index3D_256(uint8 offset, int32 x, int32 y, int32 z) const; + uint8 Index4D_256(uint8 offset, int32 x, int32 y, int32 z, int32 w) const; + + v_flt ValCoord2DFast(uint8 offset, int32 x, int32 y) const; + v_flt ValCoord3DFast(uint8 offset, int32 x, int32 y, int32 z) const; + v_flt GradCoord2D(uint8 offset, int32 x, int32 y, v_flt xd, v_flt yd) const; + v_flt GradCoord2D(uint8 offset, int32 x, int32 y, v_flt xd, v_flt yd, v_flt& outGradX, v_flt& outGradY) const; + v_flt GradCoord3D(uint8 offset, int32 x, int32 y, int32 z, v_flt xd, v_flt yd, v_flt zd) const; + v_flt GradCoord3D(uint8 offset, int32 x, int32 y, int32 z, v_flt xd, v_flt yd, v_flt zd, v_flt& outGradX, v_flt& outGradY, v_flt& outGradZ) const; + v_flt GradCoord4D(uint8 offset, int32 x, int32 y, int32 z, int32 w, v_flt xd, v_flt yd, v_flt zd, v_flt wd) const; + +protected: + VectorRegister ValCoord2DFast(VectorRegisterInt offset, VectorRegisterInt x, VectorRegisterInt y) const; + +protected: + // Hashing + static constexpr int32 X_PRIME = 1619; + static constexpr int32 Y_PRIME = 31337; + static constexpr int32 Z_PRIME = 6971; + static constexpr int32 W_PRIME = 1013; + + static v_flt ValCoord2D(int32 seed, int32 x, int32 y); + static v_flt ValCoord3D(int32 seed, int32 x, int32 y, int32 z); + static v_flt ValCoord4D(int32 seed, int32 x, int32 y, int32 z, int32 w); + +protected: + static VectorRegister ValCoord2D(VectorRegisterInt seed, VectorRegisterInt x, VectorRegisterInt y); + +protected: +#if VOXEL_DEBUG || PLATFORM_MAC // Remove this if you're working on OSX, this is just to work on the epic build servers +#define DECLARE_LUT(Name, Size) const TVoxelStaticArray Name = +#else +#define DECLARE_LUT(Name, Size) static constexpr v_flt Name[Size] = +#endif + + DECLARE_LUT(GRAD_X, 12) + { + 1, -1, 1, -1, + 1, -1, 1, -1, + 0, 0, 0, 0 + }; + DECLARE_LUT(GRAD_Y, 12) + { + 1, 1, -1, -1, + 0, 0, 0, 0, + 1, -1, 1, -1 + }; + DECLARE_LUT(GRAD_Z, 12) + { + 0, 0, 0, 0, + 1, 1, -1, -1, + 1, 1, -1, -1 + }; + + DECLARE_LUT(GRAD_4D, 128) + { + 0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, + 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, + 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1, + -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1, + 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1, + -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1, + 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0, + -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0 + }; + + DECLARE_LUT(VAL_LUT, 256) + { + v_flt(0.3490196078), v_flt(0.4352941176), v_flt(-0.4509803922), v_flt(0.6392156863), v_flt(0.5843137255), v_flt(-0.1215686275), v_flt(0.7176470588), v_flt(-0.1058823529), v_flt(0.3960784314), v_flt(0.0431372549), v_flt(-0.03529411765), v_flt(0.3176470588), v_flt(0.7254901961), v_flt(0.137254902), v_flt(0.8588235294), v_flt(-0.8196078431), + v_flt(-0.7960784314), v_flt(-0.3333333333), v_flt(-0.6705882353), v_flt(-0.3882352941), v_flt(0.262745098), v_flt(0.3254901961), v_flt(-0.6470588235), v_flt(-0.9215686275), v_flt(-0.5294117647), v_flt(0.5294117647), v_flt(-0.4666666667), v_flt(0.8117647059), v_flt(0.3803921569), v_flt(0.662745098), v_flt(0.03529411765), v_flt(-0.6156862745), + v_flt(-0.01960784314), v_flt(-0.3568627451), v_flt(-0.09019607843), v_flt(0.7490196078), v_flt(0.8352941176), v_flt(-0.4039215686), v_flt(-0.7490196078), v_flt(0.9529411765), v_flt(-0.0431372549), v_flt(-0.9294117647), v_flt(-0.6549019608), v_flt(0.9215686275), v_flt(-0.06666666667), v_flt(-0.4431372549), v_flt(0.4117647059), v_flt(-0.4196078431), + v_flt(-0.7176470588), v_flt(-0.8117647059), v_flt(-0.2549019608), v_flt(0.4901960784), v_flt(0.9137254902), v_flt(0.7882352941), v_flt(-1.0), v_flt(-0.4745098039), v_flt(0.7960784314), v_flt(0.8509803922), v_flt(-0.6784313725), v_flt(0.4588235294), v_flt(1.0), v_flt(-0.1843137255), v_flt(0.4509803922), v_flt(0.1450980392), + v_flt(-0.231372549), v_flt(-0.968627451), v_flt(-0.8588235294), v_flt(0.4274509804), v_flt(0.003921568627), v_flt(-0.003921568627), v_flt(0.2156862745), v_flt(0.5058823529), v_flt(0.7647058824), v_flt(0.2078431373), v_flt(-0.5921568627), v_flt(0.5764705882), v_flt(-0.1921568627), v_flt(-0.937254902), v_flt(0.08235294118), v_flt(-0.08235294118), + v_flt(0.9058823529), v_flt(0.8274509804), v_flt(0.02745098039), v_flt(-0.168627451), v_flt(-0.7803921569), v_flt(0.1137254902), v_flt(-0.9450980392), v_flt(0.2), v_flt(0.01960784314), v_flt(0.5607843137), v_flt(0.2705882353), v_flt(0.4431372549), v_flt(-0.9607843137), v_flt(0.6156862745), v_flt(0.9294117647), v_flt(-0.07450980392), + v_flt(0.3098039216), v_flt(0.9921568627), v_flt(-0.9137254902), v_flt(-0.2941176471), v_flt(-0.3411764706), v_flt(-0.6235294118), v_flt(-0.7647058824), v_flt(-0.8901960784), v_flt(0.05882352941), v_flt(0.2392156863), v_flt(0.7333333333), v_flt(0.6549019608), v_flt(0.2470588235), v_flt(0.231372549), v_flt(-0.3960784314), v_flt(-0.05098039216), + v_flt(-0.2235294118), v_flt(-0.3725490196), v_flt(0.6235294118), v_flt(0.7019607843), v_flt(-0.8274509804), v_flt(0.4196078431), v_flt(0.07450980392), v_flt(0.8666666667), v_flt(-0.537254902), v_flt(-0.5058823529), v_flt(-0.8039215686), v_flt(0.09019607843), v_flt(-0.4823529412), v_flt(0.6705882353), v_flt(-0.7882352941), v_flt(0.09803921569), + v_flt(-0.6078431373), v_flt(0.8039215686), v_flt(-0.6), v_flt(-0.3254901961), v_flt(-0.4117647059), v_flt(-0.01176470588), v_flt(0.4823529412), v_flt(0.168627451), v_flt(0.8745098039), v_flt(-0.3647058824), v_flt(-0.1607843137), v_flt(0.568627451), v_flt(-0.9921568627), v_flt(0.9450980392), v_flt(0.5137254902), v_flt(0.01176470588), + v_flt(-0.1450980392), v_flt(-0.5529411765), v_flt(-0.5764705882), v_flt(-0.1137254902), v_flt(0.5215686275), v_flt(0.1607843137), v_flt(0.3725490196), v_flt(-0.2), v_flt(-0.7254901961), v_flt(0.631372549), v_flt(0.7098039216), v_flt(-0.568627451), v_flt(0.1294117647), v_flt(-0.3098039216), v_flt(0.7411764706), v_flt(-0.8509803922), + v_flt(0.2549019608), v_flt(-0.6392156863), v_flt(-0.5607843137), v_flt(-0.3176470588), v_flt(0.937254902), v_flt(0.9843137255), v_flt(0.5921568627), v_flt(0.6941176471), v_flt(0.2862745098), v_flt(-0.5215686275), v_flt(0.1764705882), v_flt(0.537254902), v_flt(-0.4901960784), v_flt(-0.4588235294), v_flt(-0.2078431373), v_flt(-0.2156862745), + v_flt(0.7725490196), v_flt(0.3647058824), v_flt(-0.2392156863), v_flt(0.2784313725), v_flt(-0.8823529412), v_flt(0.8980392157), v_flt(0.1215686275), v_flt(0.1058823529), v_flt(-0.8745098039), v_flt(-0.9843137255), v_flt(-0.7019607843), v_flt(0.9607843137), v_flt(0.2941176471), v_flt(0.3411764706), v_flt(0.1529411765), v_flt(0.06666666667), + v_flt(-0.9764705882), v_flt(0.3019607843), v_flt(0.6470588235), v_flt(-0.5843137255), v_flt(0.05098039216), v_flt(-0.5137254902), v_flt(-0.137254902), v_flt(0.3882352941), v_flt(-0.262745098), v_flt(-0.3019607843), v_flt(-0.1764705882), v_flt(-0.7568627451), v_flt(0.1843137255), v_flt(-0.5450980392), v_flt(-0.4980392157), v_flt(-0.2784313725), + v_flt(-0.9529411765), v_flt(-0.09803921569), v_flt(0.8901960784), v_flt(-0.2862745098), v_flt(-0.3803921569), v_flt(0.5529411765), v_flt(0.7803921569), v_flt(-0.8352941176), v_flt(0.6862745098), v_flt(0.7568627451), v_flt(0.4980392157), v_flt(-0.6862745098), v_flt(-0.8980392157), v_flt(-0.7725490196), v_flt(-0.7098039216), v_flt(-0.2470588235), + v_flt(-0.9058823529), v_flt(0.9764705882), v_flt(0.1921568627), v_flt(0.8431372549), v_flt(-0.05882352941), v_flt(0.3568627451), v_flt(0.6078431373), v_flt(0.5450980392), v_flt(0.4039215686), v_flt(-0.7333333333), v_flt(-0.4274509804), v_flt(0.6), v_flt(0.6784313725), v_flt(-0.631372549), v_flt(-0.02745098039), v_flt(-0.1294117647), + v_flt(0.3333333333), v_flt(-0.8431372549), v_flt(0.2235294118), v_flt(-0.3490196078), v_flt(-0.6941176471), v_flt(0.8823529412), v_flt(0.4745098039), v_flt(0.4666666667), v_flt(-0.7411764706), v_flt(-0.2705882353), v_flt(0.968627451), v_flt(0.8196078431), v_flt(-0.662745098), v_flt(-0.4352941176), v_flt(-0.8666666667), v_flt(-0.1529411765), + }; + + DECLARE_LUT(CELL_2D_X, 256) + { + v_flt(-0.6440658039), v_flt(-0.08028078721), v_flt(0.9983546168), v_flt(0.9869492062), v_flt(0.9284746418), v_flt(0.6051097552), v_flt(-0.794167404), v_flt(-0.3488667991), v_flt(-0.943136526), v_flt(-0.9968171318), v_flt(0.8740961579), v_flt(0.1421139764), v_flt(0.4282553608), v_flt(-0.9986665833), v_flt(0.9996760121), v_flt(-0.06248383632), + v_flt(0.7120139305), v_flt(0.8917660409), v_flt(0.1094842955), v_flt(-0.8730880804), v_flt(0.2594811489), v_flt(-0.6690063346), v_flt(-0.9996834972), v_flt(-0.8803608671), v_flt(-0.8166554937), v_flt(0.8955599676), v_flt(-0.9398321388), v_flt(0.07615451399), v_flt(-0.7147270565), v_flt(0.8707354457), v_flt(-0.9580008579), v_flt(0.4905965632), + v_flt(0.786775944), v_flt(0.1079711577), v_flt(0.2686638979), v_flt(0.6113487322), v_flt(-0.530770584), v_flt(-0.7837268286), v_flt(-0.8558691039), v_flt(-0.5726093896), v_flt(-0.9830740914), v_flt(0.7087766359), v_flt(0.6807027153), v_flt(-0.08864708788), v_flt(0.6704485923), v_flt(-0.1350735482), v_flt(-0.9381333003), v_flt(0.9756655376), + v_flt(0.4231433671), v_flt(-0.4959787385), v_flt(0.1005554325), v_flt(-0.7645857281), v_flt(-0.5859053796), v_flt(-0.9751154306), v_flt(-0.6972258572), v_flt(0.7907012002), v_flt(-0.9109899213), v_flt(-0.9584307894), v_flt(-0.8269529333), v_flt(0.2608264719), v_flt(-0.7773760119), v_flt(0.7606456974), v_flt(-0.8961083758), v_flt(-0.9838134719), + v_flt(0.7338893576), v_flt(0.2161226729), v_flt(0.673509891), v_flt(-0.5512056873), v_flt(0.6899744332), v_flt(0.868004831), v_flt(0.5897430311), v_flt(-0.8950444221), v_flt(-0.3595752773), v_flt(0.8209486981), v_flt(-0.2912360132), v_flt(-0.9965011374), v_flt(0.9766994634), v_flt(0.738790822), v_flt(-0.4730947722), v_flt(0.8946479441), + v_flt(-0.6943628971), v_flt(-0.6620468182), v_flt(-0.0887255502), v_flt(-0.7512250855), v_flt(-0.5322986898), v_flt(0.5226295385), v_flt(0.2296318375), v_flt(0.7915307344), v_flt(-0.2756485999), v_flt(-0.6900234522), v_flt(0.07090588086), v_flt(0.5981278485), v_flt(0.3033429312), v_flt(-0.7253142797), v_flt(-0.9855874307), v_flt(-0.1761843396), + v_flt(-0.6438468325), v_flt(-0.9956136595), v_flt(0.8541580762), v_flt(-0.9999807666), v_flt(-0.02152416253), v_flt(-0.8705983095), v_flt(-0.1197138014), v_flt(-0.992107781), v_flt(-0.9091181546), v_flt(0.788610536), v_flt(-0.994636402), v_flt(0.4211256853), v_flt(0.3110430857), v_flt(-0.4031127839), v_flt(0.7610684239), v_flt(0.7685674467), + v_flt(0.152271555), v_flt(-0.9364648723), v_flt(0.1681333739), v_flt(-0.3567427907), v_flt(-0.418445483), v_flt(-0.98774778), v_flt(0.8705250765), v_flt(-0.8911701067), v_flt(-0.7315350966), v_flt(0.6030885658), v_flt(-0.4149130821), v_flt(0.7585339481), v_flt(0.6963196535), v_flt(0.8332685012), v_flt(-0.8086815232), v_flt(0.7518116724), + v_flt(-0.3490535894), v_flt(0.6972110903), v_flt(-0.8795676928), v_flt(-0.6442331882), v_flt(0.6610236811), v_flt(-0.9853565782), v_flt(-0.590338458), v_flt(0.09843602117), v_flt(0.5646534882), v_flt(-0.6023259233), v_flt(-0.3539248861), v_flt(0.5132728656), v_flt(0.9380385118), v_flt(-0.7599270056), v_flt(-0.7425936564), v_flt(-0.6679610562), + v_flt(-0.3018497816), v_flt(0.814478266), v_flt(0.03777430269), v_flt(-0.7514235086), v_flt(0.9662556939), v_flt(-0.4720194901), v_flt(-0.435054126), v_flt(0.7091901235), v_flt(0.929379209), v_flt(0.9997434357), v_flt(0.8306320299), v_flt(-0.9434019629), v_flt(-0.133133759), v_flt(0.5048413216), v_flt(0.3711995273), v_flt(0.98552091), + v_flt(0.7401857005), v_flt(-0.9999981398), v_flt(-0.2144033253), v_flt(0.4808624681), v_flt(-0.413835885), v_flt(0.644229305), v_flt(0.9626648696), v_flt(0.1833665934), v_flt(0.5794129), v_flt(0.01404446873), v_flt(0.4388494993), v_flt(0.5213612322), v_flt(-0.5281609948), v_flt(-0.9745306846), v_flt(-0.9904373013), v_flt(0.9100232252), + v_flt(-0.9914057719), v_flt(0.7892627765), v_flt(0.3364421659), v_flt(-0.9416099764), v_flt(0.7802732656), v_flt(0.886302871), v_flt(0.6524471291), v_flt(0.5762186726), v_flt(-0.08987644664), v_flt(-0.2177026782), v_flt(-0.9720345052), v_flt(-0.05722538858), v_flt(0.8105983127), v_flt(0.3410261032), v_flt(0.6452309645), v_flt(-0.7810612152), + v_flt(0.9989395718), v_flt(-0.808247815), v_flt(0.6370177929), v_flt(0.5844658772), v_flt(0.2054070861), v_flt(0.055960522), v_flt(-0.995827561), v_flt(0.893409165), v_flt(-0.931516824), v_flt(0.328969469), v_flt(-0.3193837488), v_flt(0.7314755657), v_flt(-0.7913517714), v_flt(-0.2204109786), v_flt(0.9955900414), v_flt(-0.7112353139), + v_flt(-0.7935008741), v_flt(-0.9961918204), v_flt(-0.9714163995), v_flt(-0.9566188669), v_flt(0.2748495632), v_flt(-0.4681743221), v_flt(-0.9614449642), v_flt(0.585194072), v_flt(0.4532946061), v_flt(-0.9916113176), v_flt(0.942479587), v_flt(-0.9813704753), v_flt(-0.6538429571), v_flt(0.2923335053), v_flt(-0.2246660704), v_flt(-0.1800781949), + v_flt(-0.9581216256), v_flt(0.552215082), v_flt(-0.9296791922), v_flt(0.643183699), v_flt(0.9997325981), v_flt(-0.4606920354), v_flt(-0.2148721265), v_flt(0.3482070809), v_flt(0.3075517813), v_flt(0.6274756393), v_flt(0.8910881765), v_flt(-0.6397771309), v_flt(-0.4479080125), v_flt(-0.5247665011), v_flt(-0.8386507094), v_flt(0.3901291416), + v_flt(0.1458336921), v_flt(0.01624613149), v_flt(-0.8273199879), v_flt(0.5611100679), v_flt(-0.8380219841), v_flt(-0.9856122234), v_flt(-0.861398618), v_flt(0.6398413916), v_flt(0.2694510795), v_flt(0.4327334514), v_flt(-0.9960265354), v_flt(-0.939570655), v_flt(-0.8846996446), v_flt(0.7642113189), v_flt(-0.7002080528), v_flt(0.664508256), + }; + DECLARE_LUT(CELL_2D_Y, 256) + { + v_flt(0.7649700911), v_flt(0.9967722885), v_flt(0.05734160033), v_flt(-0.1610318741), v_flt(0.371395799), v_flt(-0.7961420628), v_flt(0.6076990492), v_flt(-0.9371723195), v_flt(0.3324056156), v_flt(0.07972205329), v_flt(-0.4857529277), v_flt(-0.9898503007), v_flt(0.9036577593), v_flt(0.05162417479), v_flt(-0.02545330525), v_flt(-0.998045976), + v_flt(-0.7021653386), v_flt(-0.4524967717), v_flt(-0.9939885256), v_flt(-0.4875625128), v_flt(-0.9657481729), v_flt(-0.7432567015), v_flt(0.02515761212), v_flt(0.4743044842), v_flt(0.5771254669), v_flt(0.4449408324), v_flt(0.3416365773), v_flt(0.9970960285), v_flt(0.6994034849), v_flt(0.4917517499), v_flt(0.286765333), v_flt(0.8713868327), + v_flt(0.6172387009), v_flt(0.9941540269), v_flt(0.9632339851), v_flt(-0.7913613129), v_flt(0.847515538), v_flt(0.6211056739), v_flt(0.5171924952), v_flt(-0.8198283277), v_flt(-0.1832084353), v_flt(0.7054329737), v_flt(0.7325597678), v_flt(0.9960630973), v_flt(0.7419559859), v_flt(0.9908355749), v_flt(-0.346274329), v_flt(0.2192641299), + v_flt(-0.9060627411), v_flt(-0.8683346653), v_flt(0.9949314574), v_flt(-0.6445220433), v_flt(-0.8103794704), v_flt(-0.2216977607), v_flt(0.7168515217), v_flt(0.612202264), v_flt(-0.412428616), v_flt(0.285325116), v_flt(0.56227115), v_flt(-0.9653857009), v_flt(-0.6290361962), v_flt(0.6491672535), v_flt(0.443835306), v_flt(-0.1791955706), + v_flt(-0.6792690269), v_flt(-0.9763662173), v_flt(0.7391782104), v_flt(0.8343693968), v_flt(0.7238337389), v_flt(0.4965557504), v_flt(0.8075909592), v_flt(-0.4459769977), v_flt(-0.9331160806), v_flt(-0.5710019572), v_flt(0.9566512346), v_flt(-0.08357920318), v_flt(0.2146116448), v_flt(-0.6739348049), v_flt(0.8810115417), v_flt(0.4467718167), + v_flt(-0.7196250184), v_flt(-0.749462481), v_flt(0.9960561112), v_flt(0.6600461127), v_flt(-0.8465566164), v_flt(-0.8525598897), v_flt(-0.9732775654), v_flt(0.6111293616), v_flt(-0.9612584717), v_flt(-0.7237870097), v_flt(-0.9974830104), v_flt(-0.8014006968), v_flt(0.9528814544), v_flt(-0.6884178931), v_flt(-0.1691668301), v_flt(0.9843571905), + v_flt(0.7651544003), v_flt(-0.09355982605), v_flt(-0.5200134429), v_flt(-0.006202125807), v_flt(-0.9997683284), v_flt(0.4919944954), v_flt(-0.9928084436), v_flt(-0.1253880012), v_flt(-0.4165383308), v_flt(-0.6148930171), v_flt(-0.1034332049), v_flt(-0.9070022917), v_flt(-0.9503958117), v_flt(0.9151503065), v_flt(-0.6486716073), v_flt(0.6397687707), + v_flt(-0.9883386937), v_flt(0.3507613761), v_flt(0.9857642561), v_flt(-0.9342026446), v_flt(-0.9082419159), v_flt(0.1560587169), v_flt(0.4921240607), v_flt(-0.453669308), v_flt(0.6818037859), v_flt(0.7976742329), v_flt(0.9098610522), v_flt(0.651633524), v_flt(0.7177318024), v_flt(-0.5528685241), v_flt(0.5882467118), v_flt(0.6593778956), + v_flt(0.9371027648), v_flt(-0.7168658839), v_flt(-0.4757737632), v_flt(0.7648291307), v_flt(0.7503650398), v_flt(0.1705063456), v_flt(-0.8071558121), v_flt(-0.9951433815), v_flt(-0.8253280792), v_flt(-0.7982502628), v_flt(0.9352738503), v_flt(0.8582254747), v_flt(-0.3465310238), v_flt(0.65000842), v_flt(-0.6697422351), v_flt(0.7441962291), + v_flt(-0.9533555), v_flt(0.5801940659), v_flt(-0.9992862963), v_flt(-0.659820211), v_flt(0.2575848092), v_flt(0.881588113), v_flt(-0.9004043022), v_flt(-0.7050172826), v_flt(0.369126382), v_flt(-0.02265088836), v_flt(0.5568217228), v_flt(-0.3316515286), v_flt(0.991098079), v_flt(-0.863212164), v_flt(-0.9285531277), v_flt(0.1695539323), + v_flt(-0.672402505), v_flt(-0.001928841934), v_flt(0.9767452145), v_flt(-0.8767960349), v_flt(0.9103515037), v_flt(-0.7648324016), v_flt(0.2706960452), v_flt(-0.9830446035), v_flt(0.8150341657), v_flt(-0.9999013716), v_flt(-0.8985605806), v_flt(0.8533360801), v_flt(0.8491442537), v_flt(-0.2242541966), v_flt(-0.1379635899), v_flt(-0.4145572694), + v_flt(0.1308227633), v_flt(0.6140555916), v_flt(0.9417041303), v_flt(-0.336705587), v_flt(-0.6254387508), v_flt(0.4631060578), v_flt(-0.7578342456), v_flt(-0.8172955655), v_flt(-0.9959529228), v_flt(-0.9760151351), v_flt(0.2348380732), v_flt(-0.9983612848), v_flt(0.5856025746), v_flt(-0.9400538266), v_flt(-0.7639875669), v_flt(0.6244544645), + v_flt(0.04604054566), v_flt(0.5888424828), v_flt(0.7708490978), v_flt(-0.8114182882), v_flt(0.9786766212), v_flt(-0.9984329822), v_flt(0.09125496582), v_flt(-0.4492438803), v_flt(-0.3636982357), v_flt(0.9443405575), v_flt(-0.9476254645), v_flt(-0.6818676535), v_flt(-0.6113610831), v_flt(0.9754070948), v_flt(-0.0938108173), v_flt(-0.7029540015), + v_flt(-0.6085691109), v_flt(-0.08718862881), v_flt(-0.237381926), v_flt(0.2913423132), v_flt(0.9614872426), v_flt(0.8836361266), v_flt(-0.2749974196), v_flt(-0.8108932717), v_flt(-0.8913607575), v_flt(0.129255541), v_flt(-0.3342637104), v_flt(-0.1921249337), v_flt(-0.7566302845), v_flt(-0.9563164339), v_flt(-0.9744358146), v_flt(0.9836522982), + v_flt(-0.2863615732), v_flt(0.8337016872), v_flt(0.3683701937), v_flt(0.7657119102), v_flt(-0.02312427772), v_flt(0.8875600535), v_flt(0.976642191), v_flt(0.9374176384), v_flt(0.9515313457), v_flt(-0.7786361937), v_flt(-0.4538302125), v_flt(-0.7685604874), v_flt(-0.8940796454), v_flt(-0.8512462154), v_flt(0.5446696133), v_flt(0.9207601495), + v_flt(-0.9893091197), v_flt(-0.9998680229), v_flt(0.5617309299), v_flt(-0.8277411985), v_flt(0.545636467), v_flt(0.1690223212), v_flt(-0.5079295433), v_flt(0.7685069899), v_flt(-0.9630140787), v_flt(0.9015219132), v_flt(0.08905695279), v_flt(-0.3423550559), v_flt(-0.4661614943), v_flt(-0.6449659371), v_flt(0.7139388509), v_flt(0.7472809229), + }; + DECLARE_LUT(CELL_3D_X, 256) + { + v_flt(0.3752498686), v_flt(0.687188096), v_flt(0.2248135212), v_flt(0.6692006647), v_flt(-0.4376476931), v_flt(0.6139972552), v_flt(0.9494563929), v_flt(0.8065108882), v_flt(-0.2218812853), v_flt(0.8484661167), v_flt(0.5551817596), v_flt(0.2133903499), v_flt(0.5195126593), v_flt(-0.6440141975), v_flt(-0.5192897331), v_flt(-0.3697654077), + v_flt(-0.07927779647), v_flt(0.4187757321), v_flt(-0.750078731), v_flt(0.6579554632), v_flt(-0.6859803838), v_flt(-0.6878407087), v_flt(0.9490848347), v_flt(0.5795829433), v_flt(-0.5325976529), v_flt(-0.1363699466), v_flt(0.417665879), v_flt(-0.9108236468), v_flt(0.4438605427), v_flt(0.819294887), v_flt(-0.4033873915), v_flt(-0.2817317705), + v_flt(0.3969665622), v_flt(0.5323450134), v_flt(-0.6833017297), v_flt(0.3881436661), v_flt(-0.7119144767), v_flt(-0.2306979838), v_flt(-0.9398873022), v_flt(0.1701906676), v_flt(-0.4261839496), v_flt(-0.003712295499), v_flt(-0.734675004), v_flt(-0.3195046015), v_flt(0.7345307424), v_flt(0.9766246496), v_flt(-0.02003735175), v_flt(-0.4824156342), + v_flt(0.4245892007), v_flt(0.9072427669), v_flt(0.593346808), v_flt(-0.8911762541), v_flt(-0.7657571834), v_flt(-0.5268198896), v_flt(-0.8801903279), v_flt(-0.6296409617), v_flt(-0.09492481344), v_flt(-0.4920470525), v_flt(0.7307666154), v_flt(-0.2514540636), v_flt(-0.3356210347), v_flt(-0.3522787894), v_flt(0.87847885), v_flt(-0.7424096346), + v_flt(0.5757585274), v_flt(0.4519299338), v_flt(0.6420368628), v_flt(-0.1128478447), v_flt(0.499874883), v_flt(0.5291681739), v_flt(-0.5098837195), v_flt(0.5639583502), v_flt(-0.8456386526), v_flt(-0.9657134875), v_flt(-0.576437342), v_flt(-0.5666013014), v_flt(0.5667702405), v_flt(-0.481316582), v_flt(0.7313389916), v_flt(-0.3805628566), + v_flt(-0.6512675909), v_flt(-0.2787156951), v_flt(0.8648059114), v_flt(-0.9730216276), v_flt(-0.8335820906), v_flt(0.2673159641), v_flt(0.231150148), v_flt(0.01286214638), v_flt(0.6774953261), v_flt(0.6542885718), v_flt(-0.02545450161), v_flt(0.2101238586), v_flt(-0.5572105885), v_flt(0.813705672), v_flt(-0.7546026951), v_flt(-0.2502500006), + v_flt(-0.9979289381), v_flt(0.7024037039), v_flt(0.08990874624), v_flt(0.8170812432), v_flt(0.4226980265), v_flt(-0.2442153475), v_flt(-0.9183326731), v_flt(0.6068222411), v_flt(0.818676691), v_flt(-0.7236735282), v_flt(-0.5383903295), v_flt(-0.6269337242), v_flt(-0.0939331121), v_flt(0.9203878539), v_flt(-0.7256396824), v_flt(0.6292431149), + v_flt(0.4234156978), v_flt(0.006685688024), v_flt(-0.2598694113), v_flt(0.6408036421), v_flt(0.05899871622), v_flt(0.7090281418), v_flt(-0.5905222072), v_flt(0.3128214264), v_flt(-0.691925826), v_flt(0.3634019349), v_flt(-0.6772511147), v_flt(-0.3204583896), v_flt(-0.3906740409), v_flt(-0.3342190395), v_flt(-0.517779592), v_flt(-0.6817711267), + v_flt(0.6422383105), v_flt(0.4388482478), v_flt(0.2968562611), v_flt(-0.2019778353), v_flt(0.6014865048), v_flt(0.9519280722), v_flt(0.3398889569), v_flt(0.8179709354), v_flt(0.2365522154), v_flt(0.3262175096), v_flt(-0.8060715954), v_flt(-0.2068642503), v_flt(0.6208057279), v_flt(-0.5274282502), v_flt(-0.3722334928), v_flt(-0.8923412971), + v_flt(0.5341834201), v_flt(-0.3663701513), v_flt(-0.6114600319), v_flt(0.5026307556), v_flt(0.8396151729), v_flt(0.9245042467), v_flt(-0.7994843957), v_flt(-0.5357200589), v_flt(-0.6283359739), v_flt(-0.61351886), v_flt(-0.875632008), v_flt(-0.5278879423), v_flt(0.9087491985), v_flt(-0.03500215466), v_flt(-0.261365798), v_flt(-0.579523541), + v_flt(-0.3765052689), v_flt(-0.74398252), v_flt(0.4257318052), v_flt(-0.1214508921), v_flt(0.8561809753), v_flt(0.6802835104), v_flt(-0.5452131039), v_flt(-0.1997156478), v_flt(0.4562348357), v_flt(-0.811704301), v_flt(0.67793962), v_flt(-0.9237819106), v_flt(0.6973511259), v_flt(-0.5189506), v_flt(0.5517320032), v_flt(-0.396710831), + v_flt(0.5493762815), v_flt(-0.2507853002), v_flt(0.4788634005), v_flt(0.387333516), v_flt(-0.2176515694), v_flt(0.6749832419), v_flt(0.2148283022), v_flt(-0.7521815872), v_flt(0.4697000159), v_flt(0.7890593699), v_flt(-0.7606162952), v_flt(0.01083397843), v_flt(0.5254091908), v_flt(-0.6748025877), v_flt(0.751091524), v_flt(0.05259056135), + v_flt(0.01889481232), v_flt(-0.6037423727), v_flt(-0.6542965129), v_flt(0.08873301081), v_flt(-0.6191345671), v_flt(0.4331858488), v_flt(-0.3858351946), v_flt(-0.1429059747), v_flt(0.4118221036), v_flt(-0.6247153214), v_flt(-0.611423014), v_flt(0.5542939606), v_flt(-0.9432768808), v_flt(-0.4567870451), v_flt(-0.7349133547), v_flt(0.399304489), + v_flt(-0.7474927672), v_flt(0.02589419753), v_flt(0.783915821), v_flt(0.6138668752), v_flt(0.4276376047), v_flt(-0.4347886353), v_flt(0.02947841302), v_flt(-0.833742746), v_flt(0.3817221742), v_flt(-0.8743368359), v_flt(-0.3823443796), v_flt(-0.6829243811), v_flt(-0.3681903049), v_flt(-0.367626833), v_flt(-0.434583373), v_flt(0.235891995), + v_flt(-0.6874880269), v_flt(-0.5115661773), v_flt(-0.5534962601), v_flt(0.5632777056), v_flt(0.686191532), v_flt(-0.05095871588), v_flt(-0.06865785057), v_flt(-0.5975288531), v_flt(-0.6429790056), v_flt(-0.3729361548), v_flt(0.2237917666), v_flt(0.6046773225), v_flt(-0.5041542295), v_flt(-0.03972191174), v_flt(0.7028828406), v_flt(-0.5560856498), + v_flt(0.5898328456), v_flt(-0.9308076766), v_flt(0.4617069864), v_flt(0.3190983137), v_flt(0.9116567753), v_flt(-0.45029554), v_flt(0.3346334459), v_flt(0.8525005645), v_flt(0.2528483381), v_flt(-0.8306630147), v_flt(-0.6880390622), v_flt(0.7448684026), v_flt(-0.1963355843), v_flt(-0.5900257974), v_flt(0.9097057294), v_flt(-0.2509196808), + }; + DECLARE_LUT(CELL_3D_Y, 256) + { + v_flt(-0.6760585049), v_flt(-0.09136176499), v_flt(0.1681325679), v_flt(-0.6688468686), v_flt(-0.4822753902), v_flt(-0.7891068824), v_flt(-0.1877509944), v_flt(0.548470914), v_flt(-0.463339443), v_flt(-0.4050542082), v_flt(0.3218158513), v_flt(0.2546493823), v_flt(-0.3753271935), v_flt(0.4745384887), v_flt(0.481254652), v_flt(-0.8934416489), + v_flt(-0.6737085076), v_flt(0.7469917228), v_flt(0.3826230411), v_flt(0.6751013678), v_flt(-0.7248119515), v_flt(-0.3224276742), v_flt(-0.02076190936), v_flt(-0.6404268166), v_flt(-0.5292028444), v_flt(0.7151414636), v_flt(-0.6144655059), v_flt(-0.369912124), v_flt(0.6942067212), v_flt(-0.4481558248), v_flt(-0.6366894559), v_flt(0.5956568471), + v_flt(0.564274539), v_flt(0.7145584688), v_flt(0.6871918316), v_flt(0.5657918509), v_flt(-0.6275978114), v_flt(0.4146983062), v_flt(0.2638993789), v_flt(-0.792633138), v_flt(0.5706133514), v_flt(0.8606546462), v_flt(0.6490900316), v_flt(-0.8242699196), v_flt(0.6765819124), v_flt(0.1959534069), v_flt(-0.8426769757), v_flt(-0.5917672797), + v_flt(0.7517364266), v_flt(0.03252559226), v_flt(0.0883617105), v_flt(0.4475064813), v_flt(-0.1418643552), v_flt(0.7343428473), v_flt(0.3870192548), v_flt(-0.7716703522), v_flt(0.4839898327), v_flt(0.7437439055), v_flt(-0.5989573348), v_flt(-0.8357068955), v_flt(0.6086049038), v_flt(0.9194627258), v_flt(0.4718297238), v_flt(-0.2650335884), + v_flt(-0.6470352599), v_flt(-0.5555181303), v_flt(0.1222351235), v_flt(0.7802044684), v_flt(-0.8636947022), v_flt(-0.2341352163), v_flt(0.683030874), v_flt(-0.5005858287), v_flt(0.2334616211), v_flt(0.2576877608), v_flt(0.6666816727), v_flt(-0.7663996863), v_flt(0.794201982), v_flt(0.6189308788), v_flt(0.6071033261), v_flt(-0.4206058253), + v_flt(-0.3957336915), v_flt(-0.8170257484), v_flt(-0.1043240417), v_flt(0.0002167596213), v_flt(0.1816339018), v_flt(-0.6838094939), v_flt(-0.2495341969), v_flt(-0.7116756954), v_flt(-0.03361673621), v_flt(-0.3350836431), v_flt(0.2137186039), v_flt(0.2557996786), v_flt(0.7490117093), v_flt(0.4942936549), v_flt(-0.352686853), v_flt(-0.3952445435), + v_flt(-0.0459964767), v_flt(-0.7115787471), v_flt(0.08022899756), v_flt(0.5362268157), v_flt(-0.8258613686), v_flt(0.1114171723), v_flt(0.3882823051), v_flt(-0.7915404457), v_flt(0.3250957662), v_flt(0.6401346464), v_flt(-0.2662724517), v_flt(-0.6727907114), v_flt(-0.994730818), v_flt(-0.3596358977), v_flt(0.2344610069), v_flt(-0.6645215546), + v_flt(-0.7107590611), v_flt(-0.4646617327), v_flt(0.6717191355), v_flt(0.5101893498), v_flt(0.1185768238), v_flt(0.236005093), v_flt(-0.7811024061), v_flt(0.5089325193), v_flt(0.6073187658), v_flt(-0.7930732557), v_flt(-0.6822767155), v_flt(0.3201532885), v_flt(0.7545302807), v_flt(0.1072664448), v_flt(0.6784033173), v_flt(-0.6595924967), + v_flt(0.7276509498), v_flt(0.5586689436), v_flt(-0.6498636788), v_flt(0.6789333174), v_flt(0.7105966551), v_flt(-0.2872214155), v_flt(0.496746217), v_flt(-0.3880337977), v_flt(0.7324070604), v_flt(-0.9326634749), v_flt(-0.5867839255), v_flt(0.8003043651), v_flt(-0.1631882481), v_flt(-0.6796374681), v_flt(-0.8066678503), v_flt(0.4238177418), + v_flt(0.7715863549), v_flt(0.5455367347), v_flt(-0.03205115397), v_flt(-0.6005545066), v_flt(-0.5423640002), v_flt(0.3569205906), v_flt(-0.582071752), v_flt(0.6407354361), v_flt(0.7777142984), v_flt(-0.09956428618), v_flt(0.1100002681), v_flt(0.8136349123), v_flt(0.2923431904), v_flt(0.9735794425), v_flt(0.8324974864), v_flt(-0.6179617717), + v_flt(-0.9248386523), v_flt(-0.6448780771), v_flt(-0.5274402761), v_flt(-0.7862170565), v_flt(0.2682099744), v_flt(-0.5848777694), v_flt(-0.6364561467), v_flt(-0.7167402514), v_flt(-0.8677012494), v_flt(0.4205286707), v_flt(-0.7007832749), v_flt(0.243272451), v_flt(-0.1899846085), v_flt(-0.6146124977), v_flt(-0.8093357692), v_flt(-0.03545096987), + v_flt(-0.7191590868), v_flt(0.7478645848), v_flt(0.3623517328), v_flt(0.8436992512), v_flt(-0.2445711729), v_flt(0.6897356637), v_flt(-0.1708070787), v_flt(0.4639272368), v_flt(-0.7917186656), v_flt(0.02980025428), v_flt(0.6334156172), v_flt(-0.9815544807), v_flt(-0.2307217304), v_flt(0.1080823318), v_flt(0.5167601798), v_flt(-0.845120016), + v_flt(0.441572562), v_flt(0.5876789172), v_flt(-0.6365908737), v_flt(0.68350166), v_flt(0.5849723959), v_flt(0.1164114357), v_flt(-0.7379813884), v_flt(-0.9613237178), v_flt(-0.9071943084), v_flt(-0.7682111105), v_flt(0.639074459), v_flt(-0.619358298), v_flt(0.2807257131), v_flt(-0.01800868791), v_flt(0.3776607289), v_flt(0.7207567823), + v_flt(0.5536661486), v_flt(-0.9974053117), v_flt(-0.02047200006), v_flt(-0.6739453804), v_flt(-0.5607471297), v_flt(0.8815553192), v_flt(0.8275977415), v_flt(0.3928902456), v_flt(0.550991396), v_flt(0.4247623676), v_flt(-0.3436948871), v_flt(-0.3653537677), v_flt(0.3181702902), v_flt(-0.6067173171), v_flt(-0.8984128477), v_flt(0.4220839766), + v_flt(0.7238407199), v_flt(-0.7766913695), v_flt(0.6460037842), v_flt(0.2544775664), v_flt(0.6488840578), v_flt(0.805016833), v_flt(-0.9183807036), v_flt(0.4144046357), v_flt(0.270587208), v_flt(-0.8813684494), v_flt(0.6985971877), v_flt(-0.7795603017), v_flt(-0.8624480731), v_flt(0.5532697017), v_flt(0.711179521), v_flt(-0.7798160574), + v_flt(0.5225859041), v_flt(0.1261859368), v_flt(0.3398033582), v_flt(-0.7472173667), v_flt(-0.4032647119), v_flt(-0.4246578154), v_flt(0.8481212377), v_flt(-0.2144838537), v_flt(0.3431714491), v_flt(0.5310188231), v_flt(0.6682978632), v_flt(0.3110433206), v_flt(0.9263293599), v_flt(-0.6155600569), v_flt(0.07169784399), v_flt(0.8985888773), + }; + DECLARE_LUT(CELL_3D_Z, 256) + { + v_flt(-0.6341391283), v_flt(-0.7207118346), v_flt(0.9597866014), v_flt(0.3237504235), v_flt(-0.7588642466), v_flt(-0.01782410481), v_flt(0.2515593809), v_flt(0.2207257205), v_flt(-0.8579541106), v_flt(0.3406410681), v_flt(0.7669470462), v_flt(-0.9431957648), v_flt(0.7676171537), v_flt(-0.6000491115), v_flt(-0.7062096948), v_flt(0.2550207115), + v_flt(0.7347325213), v_flt(0.5163625202), v_flt(-0.5394270162), v_flt(0.3336656285), v_flt(-0.0638635111), v_flt(-0.6503195787), v_flt(0.3143356798), v_flt(-0.5039217245), v_flt(0.6605180464), v_flt(-0.6855479011), v_flt(-0.6693185756), v_flt(0.1832083647), v_flt(-0.5666258437), v_flt(0.3576482138), v_flt(-0.6571949095), v_flt(-0.7522101635), + v_flt(-0.7238865886), v_flt(0.4538887323), v_flt(0.2467106257), v_flt(0.7274778869), v_flt(0.3151170655), v_flt(-0.8802293764), v_flt(-0.2167232729), v_flt(0.5854637865), v_flt(0.7019741052), v_flt(0.5091756071), v_flt(0.1973189533), v_flt(0.46743546), v_flt(0.05197599597), v_flt(0.088354718), v_flt(0.5380464843), v_flt(-0.6458224544), + v_flt(-0.5045952393), v_flt(0.419347884), v_flt(0.8000823542), v_flt(-0.07445020656), v_flt(-0.6272881641), v_flt(-0.428020311), v_flt(-0.2747382083), v_flt(-0.08987283726), v_flt(0.8699098354), v_flt(0.4524761885), v_flt(-0.3274603257), v_flt(0.4882262167), v_flt(-0.7189983256), v_flt(0.1746079907), v_flt(0.0751772698), v_flt(-0.6152927202), + v_flt(0.4998474673), v_flt(-0.6979677227), v_flt(0.7568667263), v_flt(-0.6152612058), v_flt(0.06447140991), v_flt(-0.8155744872), v_flt(-0.5229602449), v_flt(0.6567836838), v_flt(-0.4799905631), v_flt(0.03153534591), v_flt(0.4724992466), v_flt(-0.3026458097), v_flt(-0.2191225827), v_flt(-0.620692287), v_flt(0.3107552588), v_flt(0.8235670294), + v_flt(0.6474915988), v_flt(-0.5047637941), v_flt(0.4911488878), v_flt(-0.2307138167), v_flt(-0.5216800015), v_flt(0.6789305939), v_flt(0.9403734863), v_flt(0.702390397), v_flt(0.7347584625), v_flt(0.6779567958), v_flt(0.9765635805), v_flt(-0.9436177661), v_flt(-0.358465925), v_flt(-0.3058706624), v_flt(0.5533414464), v_flt(-0.8838306897), + v_flt(0.04496841812), v_flt(0.01687374963), v_flt(-0.9927133148), v_flt(-0.211752318), v_flt(0.3732015249), v_flt(0.9632990593), v_flt(-0.07682417004), v_flt(-0.07232213047), v_flt(0.4733721775), v_flt(0.2579229713), v_flt(0.7995216286), v_flt(0.3928189967), v_flt(0.04107517667), v_flt(0.1534542912), v_flt(0.6468965045), v_flt(0.4030684878), + v_flt(-0.5617300988), v_flt(-0.885463029), v_flt(0.693729985), v_flt(-0.5736527866), v_flt(-0.9911905409), v_flt(-0.66451538), v_flt(0.2028855685), v_flt(0.8019541421), v_flt(-0.3903877149), v_flt(-0.4888495114), v_flt(-0.2753714057), v_flt(-0.8915202143), v_flt(0.5273119089), v_flt(0.9363714773), v_flt(-0.5212228249), v_flt(-0.31642672), + v_flt(0.2409440761), v_flt(-0.703776404), v_flt(-0.6996810411), v_flt(-0.7058714505), v_flt(-0.3650566783), v_flt(0.1064744278), v_flt(0.7985729102), v_flt(0.424680257), v_flt(-0.6384535592), v_flt(0.1540161646), v_flt(-0.07702731943), v_flt(-0.5627789132), v_flt(-0.7667919169), v_flt(-0.509815999), v_flt(0.4590525092), v_flt(0.1552595611), + v_flt(0.345402042), v_flt(0.7537656024), v_flt(0.7906259247), v_flt(-0.6218493452), v_flt(0.02979350071), v_flt(-0.1337893489), v_flt(-0.1483818606), v_flt(0.549965562), v_flt(0.01882482408), v_flt(-0.7833783002), v_flt(0.4702855809), v_flt(0.2435827372), v_flt(0.2978428332), v_flt(0.2256499906), v_flt(0.4885036897), v_flt(0.5312962584), + v_flt(0.05401156992), v_flt(0.1749922158), v_flt(-0.7352273018), v_flt(0.6058980284), v_flt(0.4416079111), v_flt(0.4417378638), v_flt(0.5455879807), v_flt(-0.6681295324), v_flt(0.1973431441), v_flt(0.4053292055), v_flt(0.2220375492), v_flt(0.2957118467), v_flt(0.6910913512), v_flt(0.5940890106), v_flt(-0.2014135283), v_flt(-0.9172588213), + v_flt(-0.4254361401), v_flt(-0.6146586825), v_flt(-0.7996193253), v_flt(-0.3716777111), v_flt(-0.9448876842), v_flt(-0.2620349924), v_flt(0.9615995749), v_flt(-0.4679683524), v_flt(0.3905937144), v_flt(0.613593722), v_flt(0.1422937358), v_flt(0.1908754211), v_flt(0.8189704912), v_flt(-0.7300408736), v_flt(-0.4108776451), v_flt(-0.5319834504), + v_flt(-0.8970265651), v_flt(-0.5386359045), v_flt(0.4082255906), v_flt(0.7245356676), v_flt(0.5239080873), v_flt(-0.8937552226), v_flt(-0.553637673), v_flt(0.2354455182), v_flt(-0.0860293075), v_flt(-0.1399373318), v_flt(-0.4666323327), v_flt(0.5560157407), v_flt(0.1772619533), v_flt(-0.8893937725), v_flt(-0.5632714576), v_flt(-0.5666264959), + v_flt(-0.3670263736), v_flt(-0.06717242579), v_flt(0.6205295181), v_flt(-0.4110536264), v_flt(0.7090054553), v_flt(0.183899597), v_flt(-0.5605470555), v_flt(0.3879565548), v_flt(0.7420893903), v_flt(-0.2347595118), v_flt(-0.8577217497), v_flt(0.6325590203), v_flt(-0.8736152276), v_flt(0.7048011129), v_flt(-0.06317948268), v_flt(0.8753285574), + v_flt(-0.05843650473), v_flt(-0.3674922622), v_flt(-0.5256624401), v_flt(0.7861039337), v_flt(0.3287714416), v_flt(0.5910593099), v_flt(-0.3896960134), v_flt(0.6864605361), v_flt(0.7164918431), v_flt(-0.290014277), v_flt(-0.6796169617), v_flt(0.1632515592), v_flt(0.04485347486), v_flt(0.8320545697), v_flt(0.01339408056), v_flt(-0.2874989857), + v_flt(0.615630723), v_flt(0.3430367014), v_flt(0.8193658136), v_flt(-0.5829600957), v_flt(0.07911697781), v_flt(0.7854296063), v_flt(-0.4107442306), v_flt(0.4766964066), v_flt(-0.9045999527), v_flt(-0.1673856787), v_flt(0.2828077348), v_flt(-0.5902737632), v_flt(-0.321506229), v_flt(-0.5224513133), v_flt(-0.4090169985), v_flt(-0.3599685311), + }; + +#undef DECLARE_LUT +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.inl new file mode 100644 index 00000000..0eef3915 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.inl @@ -0,0 +1,199 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoiseLUT.h" +#include "FastNoise/VoxelFastNoiseMath.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index2D_12(uint8 offset, int32 x, int32 y) const +{ + return Perm12[(x & 0xff) + Perm[(y & 0xff) + offset]]; +} +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index3D_12(uint8 offset, int32 x, int32 y, int32 z) const +{ + return Perm12[(x & 0xff) + Perm[(y & 0xff) + Perm[(z & 0xff) + offset]]]; +} +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index4D_32(uint8 offset, int32 x, int32 y, int32 z, int32 w) const +{ + return Perm[(x & 0xff) + Perm[(y & 0xff) + Perm[(z & 0xff) + Perm[(w & 0xff) + offset]]]] & 31; +} +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index2D_256(uint8 offset, int32 x, int32 y) const +{ + return Perm[(x & 0xff) + Perm[(y & 0xff) + offset]]; +} +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index3D_256(uint8 offset, int32 x, int32 y, int32 z) const +{ + return Perm[(x & 0xff) + Perm[(y & 0xff) + Perm[(z & 0xff) + offset]]]; +} +FN_FORCEINLINE_MATH uint8 FVoxelFastNoiseLUT::Index4D_256(uint8 offset, int32 x, int32 y, int32 z, int32 w) const +{ + return Perm[(x & 0xff) + Perm[(y & 0xff) + Perm[(z & 0xff) + Perm[(w & 0xff) + offset]]]]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::ValCoord2DFast(uint8 offset, int32 x, int32 y) const +{ + return VAL_LUT[Index2D_256(offset, x, y)]; +} +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::ValCoord3DFast(uint8 offset, int32 x, int32 y, int32 z) const +{ + return VAL_LUT[Index3D_256(offset, x, y, z)]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::GradCoord2D(uint8 offset, int32 x, int32 y, v_flt xd, v_flt yd) const +{ + const uint8 lutPos = Index2D_12(offset, x, y); + return xd * GRAD_X[lutPos] + yd * GRAD_Y[lutPos]; +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::GradCoord2D(uint8 offset, int32 x, int32 y, v_flt xd, v_flt yd, v_flt& outGradX, v_flt& outGradY) const +{ + uint8 lutPos = Index2D_12(offset, x, y); + + outGradX = GRAD_X[lutPos]; + outGradY = GRAD_Y[lutPos]; + + return xd * GRAD_X[lutPos] + yd * GRAD_Y[lutPos]; +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::GradCoord3D(uint8 offset, int32 x, int32 y, int32 z, v_flt xd, v_flt yd, v_flt zd) const +{ + uint8 lutPos = Index3D_12(offset, x, y, z); + + return xd * GRAD_X[lutPos] + yd * GRAD_Y[lutPos] + zd * GRAD_Z[lutPos]; +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::GradCoord3D(uint8 offset, int32 x, int32 y, int32 z, v_flt xd, v_flt yd, v_flt zd, v_flt& outGradX, v_flt& outGradY, v_flt& outGradZ) const +{ + uint8 lutPos = Index3D_12(offset, x, y, z); + + outGradX = GRAD_X[lutPos]; + outGradY = GRAD_Y[lutPos]; + outGradZ = GRAD_Z[lutPos]; + + return xd * GRAD_X[lutPos] + yd * GRAD_Y[lutPos] + zd * GRAD_Z[lutPos]; +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::GradCoord4D(uint8 offset, int32 x, int32 y, int32 z, int32 w, v_flt xd, v_flt yd, v_flt zd, v_flt wd) const +{ + uint8 lutPos = Index4D_32(offset, x, y, z, w) << 2; + + return xd * GRAD_4D[lutPos] + yd * GRAD_4D[lutPos + 1] + zd * GRAD_4D[lutPos + 2] + wd * GRAD_4D[lutPos + 3]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH VectorRegister FVoxelFastNoiseLUT::ValCoord2DFast(VectorRegisterInt offset, VectorRegisterInt x, VectorRegisterInt y) const +{ + x = VectorIntAnd(x, MakeVectorRegisterInt(0xFF, 0xFF, 0xFF, 0xFF)); + y = VectorIntAnd(y, MakeVectorRegisterInt(0xFF, 0xFF, 0xFF, 0xFF)); + + y = VectorIntAdd(y, offset); + + int32 xv[4]; + int32 yv[4]; + VectorIntStore(x, xv); + VectorIntStore(y, yv); + + float Result[4]; + Result[0] = VAL_LUT[Perm[xv[0] + Perm[yv[0]]]]; + Result[1] = VAL_LUT[Perm[xv[1] + Perm[yv[1]]]]; + Result[2] = VAL_LUT[Perm[xv[2] + Perm[yv[2]]]]; + Result[3] = VAL_LUT[Perm[xv[3] + Perm[yv[3]]]]; + + return VectorLoad(Result); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::ValCoord2D(int32 seed, int32 x, int32 y) +{ + int32 n = seed; + n ^= X_PRIME * x; + n ^= Y_PRIME * y; + + return (n * n * n * 60493) / v_flt(2147483648); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::ValCoord3D(int32 seed, int32 x, int32 y, int32 z) +{ + int32 n = seed; + n ^= X_PRIME * x; + n ^= Y_PRIME * y; + n ^= Z_PRIME * z; + + return (n * n * n * 60493) / v_flt(2147483648); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseLUT::ValCoord4D(int32 seed, int32 x, int32 y, int32 z, int32 w) +{ + int32 n = seed; + n ^= X_PRIME * x; + n ^= Y_PRIME * y; + n ^= Z_PRIME * z; + n ^= W_PRIME * w; + + return (n * n * n * 60493) / v_flt(2147483648); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH VectorRegister FVoxelFastNoiseLUT::ValCoord2D(VectorRegisterInt seed, VectorRegisterInt x, VectorRegisterInt y) +{ + VectorRegisterInt hash = seed; + + // Note: can be removed + x = VectorIntMultiply(x, MakeVectorRegisterInt(X_PRIME, X_PRIME, X_PRIME, X_PRIME)); + y = VectorIntMultiply(y, MakeVectorRegisterInt(Y_PRIME, Y_PRIME, Y_PRIME, Y_PRIME)); + + hash = VectorIntXor(x, hash); + hash = VectorIntXor(y, hash); + + hash = VectorIntMultiply(VectorIntMultiply(VectorIntMultiply(hash, hash), MakeVectorRegisterInt(60493, 60493, 60493, 60493)), hash); + + return VectorMultiply(MakeVectorRegister(1.f / 2147483648.f, 1.f / 2147483648.f, 1.f / 2147483648.f, 1.f / 2147483648.f), VectorIntToFloat(hash)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.h new file mode 100644 index 00000000..5ebe83e2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.h @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +// Slower when inlining everything +#define FN_FORCEINLINE inline +#define FN_FORCEINLINE_MATH FORCEINLINE +// 5% faster for fractals +#define FN_FORCEINLINE_SINGLE FORCEINLINE + +class FVoxelFastNoiseMath +{ +public: + static int32 FastFloor(v_flt f); + static int32 FastRound(v_flt f); + static int32 FastAbs(int32 i); + static v_flt FastAbs(v_flt f); + static v_flt FastAbsDeriv(v_flt x, v_flt dx); + static v_flt Lerp(v_flt a, v_flt b, v_flt t); + static v_flt InterpHermiteFunc(v_flt t); + static v_flt InterpHermiteFuncDeriv(v_flt t); + static v_flt InterpQuinticFunc(v_flt t); + static v_flt InterpQuinticFuncDeriv(v_flt t); + static v_flt CubicLerp(v_flt a, v_flt b, v_flt c, v_flt d, v_flt t); + +public: + static VectorRegister Lerp(VectorRegister a, VectorRegister b, VectorRegister t); + static VectorRegister InterpHermiteFunc(VectorRegister t); + static VectorRegister InterpQuinticFunc(VectorRegister t); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.inl new file mode 100644 index 00000000..795a066a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.inl @@ -0,0 +1,136 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoiseMath.h" +#include + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +FN_FORCEINLINE_MATH int32 FVoxelFastNoiseMath::FastFloor(v_flt f) +{ + // Faster than FMath::FloorToInt + return (f >= 0 ? (int32)f : (int32)f - 1); +} +FN_FORCEINLINE_MATH int32 FVoxelFastNoiseMath::FastRound(v_flt f) +{ + return (f >= 0) ? (int32)(f + v_flt(0.5)) : (int32)(f - v_flt(0.5)); +} +FN_FORCEINLINE_MATH int32 FVoxelFastNoiseMath::FastAbs(int32 i) +{ + return abs(i); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::FastAbs(v_flt f) +{ + return fabs(f); +} +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::FastAbsDeriv(v_flt x, v_flt dx) +{ + return dx * (x < 0 ? -1 : 1); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::Lerp(v_flt a, v_flt b, v_flt t) +{ + return a + t * (b - a); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::InterpHermiteFunc(v_flt t) +{ + return t*t*(3 - 2 * t); +} +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::InterpHermiteFuncDeriv(v_flt t) +{ + return -6 * t * (t - 1); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::InterpQuinticFunc(v_flt t) +{ + return t*t*t*(t*(t * 6 - 15) + 10); +} +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::InterpQuinticFuncDeriv(v_flt t) +{ + return 30 * t * t * (t - 1) * (t - 1); +} + +FN_FORCEINLINE_MATH v_flt FVoxelFastNoiseMath::CubicLerp(v_flt a, v_flt b, v_flt c, v_flt d, v_flt t) +{ + v_flt p = (d - c) - (a - b); + return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FN_FORCEINLINE_MATH VectorRegister FVoxelFastNoiseMath::Lerp(VectorRegister a, VectorRegister b, VectorRegister t) +{ + // b - a + VectorRegister r = VectorSubtract(b, a); + // t * (b - a) + r = VectorMultiply(t, r); + // a + t * (b - a) + r = VectorAdd(a, r); + return r; +} + +FN_FORCEINLINE_MATH VectorRegister FVoxelFastNoiseMath::InterpHermiteFunc(VectorRegister t) +{ + // 2 * t + VectorRegister r = VectorMultiply(t, MakeVectorRegister(2.f, 2.f, 2.f, 2.f)); + // 3 - 2 * t + r = VectorSubtract(MakeVectorRegister(3.f, 3.f, 3.f, 3.f), r); + // t * (3 - 2 * t) + r = VectorMultiply(r, t); + // t * t * (3 - 2 * t) + r = VectorMultiply(r, t); + return r; +} + +FN_FORCEINLINE_MATH VectorRegister FVoxelFastNoiseMath::InterpQuinticFunc(VectorRegister t) +{ + // t * 6 + VectorRegister r = VectorMultiply(t, MakeVectorRegister(6.f, 6.f, 6.f, 6.f)); + // t * 6 - 15 + r = VectorSubtract(r, MakeVectorRegister(15.f, 15.f, 15.f, 15.f)); + // t * (t * 6 - 15) + r = VectorMultiply(r, t); + // t * (t * 6 - 15) + 10 + r = VectorAdd(r, MakeVectorRegister(10.f, 10.f, 10.f, 10.f)); + // t * (t * (t * 6 - 15) + 10) + r = VectorMultiply(r, t); + // t * t * (t * (t * 6 - 15) + 10) + r = VectorMultiply(r, t); + // t * t * t * (t * (t * 6 - 15) + 10) + r = VectorMultiply(r, t); + return r; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseTest.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseTest.h new file mode 100644 index 00000000..29703112 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoiseTest.h @@ -0,0 +1,78 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "FastNoise/VoxelFastNoise.inl" + +class FVoxelFastNoiseTest : public FVoxelFastNoise +{ +public: + FORCENOINLINE v_flt SingleValue_2D_NoInline(uint8 offset, v_flt x, v_flt y) const + { + return SingleValue_2D(offset, x, y); + } + FORCENOINLINE VectorRegister SingleValue_2D_NoInline(VectorRegisterInt offset, VectorRegister x, VectorRegister y) const + { + return SingleValue_2D(offset, x, y); + } + + static void Test() + { + FVoxelFastNoiseTest FastNoise; + FastNoise.SetSeed(0); + + const int32 Num = 10000000; + + v_flt Results[256][4]; + for (int32 Index = 0; Index < 256; Index++) + { + Results[Index][0] = FastNoise.SingleValue_2D_NoInline(Index, 0.5f, 4.5f); + Results[Index][1] = FastNoise.SingleValue_2D_NoInline(Index, 1.5f, 5.5f); + Results[Index][2] = FastNoise.SingleValue_2D_NoInline(Index, 2.5f, 6.5f); + Results[Index][3] = FastNoise.SingleValue_2D_NoInline(Index, 3.5f, 7.5f); + } + + for (int32 LoopIndex = 0; LoopIndex < 10; LoopIndex++) + { + { + const double StartTime = FPlatformTime::Seconds(); + for (int32 Index = 0; Index < Num; Index++) + { + const uint8 Byte = Index; + + v_flt Result[4]; + Result[0] = FastNoise.SingleValue_2D_NoInline(Byte, 0.5f, 4.5f); + Result[1] = FastNoise.SingleValue_2D_NoInline(Byte, 1.5f, 5.5f); + Result[2] = FastNoise.SingleValue_2D_NoInline(Byte, 2.5f, 6.5f); + Result[3] = FastNoise.SingleValue_2D_NoInline(Byte, 3.5f, 7.5f); + + ensure(Result[0] == Results[Byte][0] && Result[1] == Results[Byte][1] && Result[2] == Results[Byte][2] && Result[3] == Results[Byte][3]); + } + const double EndTime = FPlatformTime::Seconds(); + LOG_VOXEL(Log, TEXT("no SIMD took %fs (%fns)"), EndTime - StartTime, (EndTime - StartTime) / Num * 1e9); + } + { + const double StartTime = FPlatformTime::Seconds(); + for (int32 Index = 0; Index < Num; Index++) + { + const uint8 Byte = Index; + + const auto Offset = MakeVectorRegisterInt(Byte, Byte, Byte, Byte); + const auto X = MakeVectorRegister(0.5f, 1.5f, 2.5f, 3.5f); + const auto Y = MakeVectorRegister(4.5f, 5.5f, 6.5f, 7.5f); + const auto VectorResult = FastNoise.SingleValue_2D_NoInline(Offset, X, Y); + + float Result[4]; + VectorStore(VectorResult, Result); + + ensure(Result[0] == Results[Byte][0] && Result[1] == Results[Byte][1] && Result[2] == Results[Byte][2] && Result[3] == Results[Byte][3]); + } + const double EndTime = FPlatformTime::Seconds(); + LOG_VOXEL(Log, TEXT("SIMD took %fs (%fns)"), EndTime - StartTime, (EndTime - StartTime) / Num * 1e9); + } + } + + UE_DEBUG_BREAK(); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.h new file mode 100644 index 00000000..e77ce8c8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.h @@ -0,0 +1,73 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_CellularNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + +public: + v_flt GetCellular_2D(v_flt x, v_flt y, v_flt frequency) const; + v_flt GetCellular_3D(v_flt x, v_flt y, v_flt z, v_flt frequency) const; + + void GetVoronoi_2D(v_flt x, v_flt y, v_flt m_jitter, v_flt& out_x, v_flt& out_y) const; + void GetVoronoiNeighbors_2D( + v_flt x, v_flt y, + v_flt m_jitter, + v_flt& out_x0, v_flt& out_y0, + v_flt& out_x1, v_flt& out_y1, v_flt& out_distance1, + v_flt& out_x2, v_flt& out_y2, v_flt& out_distance2, + v_flt& out_x3, v_flt& out_y3, v_flt& out_distance3) const; + + GENERATED_VOXEL_NOISE_FUNCTION_2D(Crater) + GENERATED_VOXEL_NOISE_FUNCTION_3D(Crater) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(Cellular, Crater) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(Cellular, Crater) + + FN_FORCEINLINE v_flt GetGavoronoi_2D(v_flt x, v_flt y, v_flt frequency, v_flt dirX, v_flt dirY, v_flt dirVariation) const + { + return SingleGavoronoi_2D(0, x * frequency, y * frequency, dirX, dirY, dirVariation); + } + FN_FORCEINLINE v_flt GetGavoronoiFractal_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt dirX, v_flt dirY, v_flt dirVariation) const + { + return This().Fractal_2D([&](auto in_offset, auto in_x, auto in_y) + { + return SingleGavoronoi_2D(in_offset, in_x, in_y, dirX, dirY, dirVariation); + }, x, y, frequency, octaves); + } + + v_flt GetErosion_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt noise_dx, v_flt noise_dy, v_flt& outDx, v_flt& outDy) const; + +protected: + template + v_flt SingleCellular_2D(v_flt x, v_flt y) const; + template + v_flt SingleCellular_3D(v_flt x, v_flt y, v_flt z) const; + + template + v_flt SingleCellular2Edge_2D(v_flt x, v_flt y) const; + template + v_flt SingleCellular2Edge_3D(v_flt x, v_flt y, v_flt z) const; + + template + void SingleVoronoi_2D(v_flt x, v_flt y, v_flt m_jitter, v_flt& out_x, v_flt& out_y) const; + + v_flt SingleCrater_2D(uint8 offset, v_flt x, v_flt y) const; + v_flt SingleCrater_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const; + + v_flt SingleGavoronoi_2D(uint8 offset, v_flt x, v_flt y, v_flt dirX, v_flt dirY, v_flt dirVariation) const; + v_flt SingleGavoronoi_Erosion_2D(uint8 offset, v_flt x, v_flt y, v_flt dirX, v_flt dirY, v_flt& outDx, v_flt& outDy) const; + +protected: + template + static v_flt CellularDistance_2D(v_flt vecX, v_flt vecY); + template + static v_flt CellularDistance_3D(v_flt vecX, v_flt vecY, v_flt vecZ); + + void AccumulateCrater(v_flt sqDistance, v_flt& va, v_flt& wt) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.inl new file mode 100644 index 00000000..21c5c224 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.inl @@ -0,0 +1,712 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_CellularNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_CellularNoise::GetCellular_2D(v_flt x, v_flt y, v_flt frequency) const +{ + x *= frequency; + y *= frequency; + + switch (This().CellularReturnType) + { + case EVoxelCellularReturnType::CellValue: + case EVoxelCellularReturnType::Distance: + { + switch (This().CellularDistanceFunction) + { + default: ensureVoxelSlow(false); +#define Macro(Enum) case Enum: return SingleCellular_2D(x, y); + FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(Macro) +#undef Macro + } + } + default: + { + switch (This().CellularDistanceFunction) + { + default: ensureVoxelSlow(false); +#define Macro(Enum) case Enum: return SingleCellular2Edge_2D(x, y); + FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(Macro) +#undef Macro + } + } + } +} + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_CellularNoise::GetCellular_3D(v_flt x, v_flt y, v_flt z, v_flt frequency) const +{ + x *= frequency; + y *= frequency; + z *= frequency; + + switch (This().CellularReturnType) + { + case EVoxelCellularReturnType::CellValue: + case EVoxelCellularReturnType::Distance: + { + switch (This().CellularDistanceFunction) + { + default: ensureVoxelSlow(false); +#define Macro(Enum) case Enum: return SingleCellular_3D(x, y, z); + FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(Macro) +#undef Macro + } + } + default: + { + switch (This().CellularDistanceFunction) + { + default: ensureVoxelSlow(false); +#define Macro(Enum) case Enum: return SingleCellular2Edge_3D(x, y, z); + FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(Macro) +#undef Macro + } + } + } +} + +template +FN_FORCEINLINE void TVoxelFastNoise_CellularNoise::GetVoronoi_2D(v_flt x, v_flt y, v_flt m_jitter, v_flt& out_x, v_flt& out_y) const +{ + switch (This().CellularDistanceFunction) + { + default: ensureVoxelSlow(false); +#define Macro(Enum) case Enum: return SingleVoronoi_2D(x, y, m_jitter, out_x, out_y); + FOREACH_ENUM_EVOXELCELLULARDISTANCEFUNCTION(Macro) +#undef Macro + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCellular_2D(v_flt x, v_flt y) const +{ + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + + v_flt distance = 999999; + int32 xc = 0; + int32 yc = 0; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + const uint8 lutPos = This().Index2D_256(0, xi, yi); + + const v_flt vecX = xi - x + This().CELL_2D_X[lutPos] * This().CellularJitter; + const v_flt vecY = yi - y + This().CELL_2D_Y[lutPos] * This().CellularJitter; + + const v_flt newDistance = CellularDistance_2D(vecX, vecY); + if (newDistance < distance) + { + distance = newDistance; + xc = xi; + yc = yi; + } + } + } + + switch (This().CellularReturnType) + { + default: ensureVoxelSlow(false); + case EVoxelCellularReturnType::CellValue: + return This().ValCoord2D(This().Seed, xc, yc); + case EVoxelCellularReturnType::Distance: + return distance; + } +} + +template +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCellular_3D(v_flt x, v_flt y, v_flt z) const +{ + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + const int32 zr = FNoiseMath::FastRound(z); + + v_flt distance = 999999; + int32 xc = 0; + int32 yc = 0; + int32 zc = 0; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + for (int32 zi = zr - 1; zi <= zr + 1; zi++) + { + const uint8 lutPos = This().Index3D_256(0, xi, yi, zi); + + const v_flt vecX = xi - x + This().CELL_3D_X[lutPos] * This().CellularJitter; + const v_flt vecY = yi - y + This().CELL_3D_Y[lutPos] * This().CellularJitter; + const v_flt vecZ = zi - z + This().CELL_3D_Z[lutPos] * This().CellularJitter; + + const v_flt newDistance = CellularDistance_3D(vecX, vecY, vecZ); + if (newDistance < distance) + { + distance = newDistance; + xc = xi; + yc = yi; + zc = zi; + } + } + } + } + + switch (This().CellularReturnType) + { + default: ensureVoxelSlow(false); + case EVoxelCellularReturnType::CellValue: + return This().ValCoord3D(This().Seed, xc, yc, zc); + case EVoxelCellularReturnType::Distance: + return distance; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCellular2Edge_2D(v_flt x, v_flt y) const +{ + // TODO expose? + constexpr int32 m_cellularDistanceIndex0 = 0; + constexpr int32 m_cellularDistanceIndex1 = 1; + static_assert(m_cellularDistanceIndex1 > m_cellularDistanceIndex0, ""); + + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + + v_flt distance[m_cellularDistanceIndex1 + 1]; + for (auto& it : distance) it = 999999; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + const uint8 lutPos = This().Index2D_256(0, xi, yi); + + const v_flt vecX = xi - x + This().CELL_2D_X[lutPos] * This().CellularJitter; + const v_flt vecY = yi - y + This().CELL_2D_Y[lutPos] * This().CellularJitter; + + const v_flt newDistance = CellularDistance_2D(vecX, vecY); + for (int32 i = m_cellularDistanceIndex1; i > 0; i--) + distance[i] = FMath::Max(FMath::Min(distance[i], newDistance), distance[i - 1]); + distance[0] = FMath::Min(distance[0], newDistance); + } + } + + switch (This().CellularReturnType) + { + default: ensureVoxelSlow(false); + case EVoxelCellularReturnType::Distance2: + return distance[m_cellularDistanceIndex1]; + case EVoxelCellularReturnType::Distance2Add: + return distance[m_cellularDistanceIndex1] + distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Sub: + return distance[m_cellularDistanceIndex1] - distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Mul: + return distance[m_cellularDistanceIndex1] * distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Div: + return distance[m_cellularDistanceIndex0] / distance[m_cellularDistanceIndex1]; + } +} + +template +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCellular2Edge_3D(v_flt x, v_flt y, v_flt z) const +{ + // TODO expose? + constexpr int32 m_cellularDistanceIndex0 = 0; + constexpr int32 m_cellularDistanceIndex1 = 1; + static_assert(m_cellularDistanceIndex1 > m_cellularDistanceIndex0, ""); + + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + const int32 zr = FNoiseMath::FastRound(z); + + v_flt distance[m_cellularDistanceIndex1 + 1]; + for (auto& it : distance) it = 999999; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + for (int32 zi = zr - 1; zi <= zr + 1; zi++) + { + const uint8 lutPos = This().Index3D_256(0, xi, yi, zi); + + const v_flt vecX = xi - x + This().CELL_3D_X[lutPos] * This().CellularJitter; + const v_flt vecY = yi - y + This().CELL_3D_Y[lutPos] * This().CellularJitter; + const v_flt vecZ = zi - z + This().CELL_3D_Z[lutPos] * This().CellularJitter; + + const v_flt newDistance = CellularDistance_3D(vecX, vecY, vecZ); + + for (int32 i = m_cellularDistanceIndex1; i > 0; i--) + distance[i] = FMath::Max(FMath::Min(distance[i], newDistance), distance[i - 1]); + distance[0] = FMath::Min(distance[0], newDistance); + } + } + } + + switch (This().CellularReturnType) + { + default: ensureVoxelSlow(false); + case EVoxelCellularReturnType::Distance2: + return distance[m_cellularDistanceIndex1]; + case EVoxelCellularReturnType::Distance2Add: + return distance[m_cellularDistanceIndex1] + distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Sub: + return distance[m_cellularDistanceIndex1] - distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Mul: + return distance[m_cellularDistanceIndex1] * distance[m_cellularDistanceIndex0]; + case EVoxelCellularReturnType::Distance2Div: + return distance[m_cellularDistanceIndex0] / distance[m_cellularDistanceIndex1]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +template +FN_FORCEINLINE_SINGLE void TVoxelFastNoise_CellularNoise::SingleVoronoi_2D(v_flt x, v_flt y, v_flt m_jitter, v_flt& out_x, v_flt& out_y) const +{ + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + + v_flt distance = MAX_flt; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + const uint8 lutPos = This().Index2D_256(0, xi, yi); + + const v_flt newX = xi + This().CELL_2D_X[lutPos] * m_jitter; + const v_flt newY = yi + This().CELL_2D_Y[lutPos] * m_jitter; + const v_flt vecX = x - newX; + const v_flt vecY = y - newY; + + const v_flt newDistance = CellularDistance_2D(vecX, vecY); + if (newDistance < distance) + { + distance = newDistance; + out_x = newX; + out_y = newY; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCrater_2D(uint8 offset, v_flt x, v_flt y) const +{ + const int32 xi = FNoiseMath::FastFloor(x); + const int32 yi = FNoiseMath::FastFloor(y); + const v_flt xf = x - xi; + const v_flt yf = y - yi; + + v_flt va = 0.f; + v_flt wt = 0.f; + + for (int32 i = -1; i <= 1; i++) + { + for (int32 j = -1; j <= 1; j++) + { + const uint8 lutPos = This().Index2D_256(offset, xi + i, yi + j); + + const v_flt vecX = xf - (i + 0.5f + This().CELL_3D_X[lutPos] * This().CellularJitter); + const v_flt vecY = yf - (j + 0.5f + This().CELL_3D_Y[lutPos] * This().CellularJitter); + + const v_flt sqDistance = vecX * vecX + vecY * vecY; + AccumulateCrater(sqDistance, va, wt); + } + } + + return wt == 0 ? 0 : FNoiseMath::FastAbs(va / wt); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleCrater_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const +{ + const int32 xi = FNoiseMath::FastFloor(x); + const int32 yi = FNoiseMath::FastFloor(y); + const int32 zi = FNoiseMath::FastFloor(z); + const v_flt xf = x - xi; + const v_flt yf = y - yi; + const v_flt zf = z - zi; + + v_flt va = 0.f; + v_flt wt = 0.f; + + for (int32 i = -1; i <= 1; i++) + { + for (int32 j = -1; j <= 1; j++) + { + for (int32 k = -1; k <= 1; k++) + { + const uint8 lutPos = This().Index3D_256(offset, xi + i, yi + j, zi + k); + + const v_flt vecX = xf - (i + 0.5f + This().CELL_3D_X[lutPos] * This().CellularJitter); + const v_flt vecY = yf - (j + 0.5f + This().CELL_3D_Y[lutPos] * This().CellularJitter); + const v_flt vecZ = zf - (k + 0.5f + This().CELL_3D_Z[lutPos] * This().CellularJitter); + + const v_flt sqDistance = vecX * vecX + vecY * vecY + vecZ * vecZ; + AccumulateCrater(sqDistance, va, wt); + } + } + } + + return wt == 0 ? 0 : FNoiseMath::FastAbs(va / wt); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleGavoronoi_2D(uint8 offset, v_flt x, v_flt y, v_flt dirX, v_flt dirY, v_flt dirVariation) const +{ + // From https://www.shadertoy.com/view/llsGWl + + const int32 xi = FNoiseMath::FastFloor(x); + const int32 yi = FNoiseMath::FastFloor(y); + const v_flt xf = x - xi; + const v_flt yf = y - yi; + + v_flt va = 0.f; + v_flt wt = 0.f; + + for (int32 i = -1; i <= 1; i++) + { + for (int32 j = -1; j <= 1; j++) + { + const uint8 lutPos = This().Index2D_256(offset, xi + i, yi + j); + const v_flt jitterX = This().CELL_3D_X[lutPos]; + const v_flt jitterY = This().CELL_3D_Y[lutPos]; + + const v_flt vecX = xf - (i + 0.5f + jitterX * This().CellularJitter); + const v_flt vecY = yf - (j + 0.5f + jitterY * This().CellularJitter); + + const v_flt sqDistance = vecX * vecX + vecY * vecY; + const v_flt distance = std::sqrt(sqDistance); + + const v_flt w = std::exp(-4.f * distance); + + const v_flt noisyDirectionX = dirX + jitterX * dirVariation; + const v_flt noisyDirectionY = dirY + jitterY * dirVariation; + + va += w * std::cos((vecX * noisyDirectionX + vecY * noisyDirectionY) * 2.f * PI); + wt += w; + } + } + + return wt == 0 ? 0 : FNoiseMath::FastAbs(va / wt); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CellularNoise::SingleGavoronoi_Erosion_2D(uint8 offset, v_flt x, v_flt y, v_flt dirX, v_flt dirY, v_flt& outDx, v_flt& outDy) const +{ + // From https://www.shadertoy.com/view/MtGcWh + + const int32 xi = FNoiseMath::FastFloor(x); + const int32 yi = FNoiseMath::FastFloor(y); + const v_flt xf = x - xi; + const v_flt yf = y - yi; + + v_flt va = 0.f; + v_flt va_dx = 0.f; + v_flt va_dy = 0.f; + v_flt wt = 0.f; + + for (int32 i = -2; i <= 2; i++) + { + for (int32 j = -2; j <= 2; j++) + { + const uint8 lutPos = This().Index2D_256(offset, xi + i, yi + j); + const v_flt jitterX = This().CELL_3D_X[lutPos]; + const v_flt jitterY = This().CELL_3D_Y[lutPos]; + + const v_flt vecX = xf - (i + 0.5f + jitterX * This().CellularJitter); + const v_flt vecY = yf - (j + 0.5f + jitterY * This().CellularJitter); + + const v_flt sqDistance = vecX * vecX + vecY * vecY; + + const v_flt w = std::exp(-2.f * sqDistance); + + const v_flt sample_pos = (vecX * dirX + vecY * dirY) * 2.f * PI; + const v_flt cos = std::cos(sample_pos); + const v_flt sin = std::sin(sample_pos); + + va += w * cos; + va_dx += -w * sin * (vecX + dirX); + va_dy += -w * sin * (vecY + dirY); + wt += w; + } + } + + if (wt == 0) + { + outDx = 0; + outDy = 0; + return 0; + } + else + { + outDx = va_dx / wt; + outDy = va_dy / wt; + return va / wt; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE void TVoxelFastNoise_CellularNoise::GetVoronoiNeighbors_2D( + v_flt x, v_flt y, + v_flt m_jitter, + v_flt& out_x0, v_flt& out_y0, + v_flt& out_x1, v_flt& out_y1, v_flt& out_distance1, + v_flt& out_x2, v_flt& out_y2, v_flt& out_distance2, + v_flt& out_x3, v_flt& out_y3, v_flt& out_distance3) const +{ + const int32 xr = FNoiseMath::FastRound(x); + const int32 yr = FNoiseMath::FastRound(y); + + const FVector2D Position(x, y); + + FVector2D BestCenter; + { + v_flt BestDistance = MAX_flt; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + const uint8 lutPos = This().Index2D_256(0, xi, yi); + + const v_flt NewX = xi + This().CELL_2D_X[lutPos] * m_jitter; + const v_flt NewY = yi + This().CELL_2D_Y[lutPos] * m_jitter; + const FVector2D Center(NewX, NewY); + + const v_flt NewDistance = (Position - Center).SizeSquared(); + + if (BestDistance > NewDistance) + { + BestDistance = NewDistance; + BestCenter = Center; + } + } + } + } + + constexpr int32 num_dists = 4; + v_flt distance[num_dists] = { MAX_flt, MAX_flt, MAX_flt, MAX_flt }; + v_flt xs[num_dists]; + v_flt ys[num_dists]; + + for (int32 xi = xr - 1; xi <= xr + 1; xi++) + { + for (int32 yi = yr - 1; yi <= yr + 1; yi++) + { + const uint8 lutPos = This().Index2D_256(0, xi, yi); + + v_flt NewX = xi + This().CELL_2D_X[lutPos] * m_jitter; + v_flt NewY = yi + This().CELL_2D_Y[lutPos] * m_jitter; + const FVector2D Center(NewX, NewY); + + v_flt NewDistance = FMath::Abs(FVector2D::DotProduct(Position - (Center + BestCenter) / 2, (BestCenter - Center).GetSafeNormal())); + + for (int32 i = 0; i < num_dists; ++i) + { + if (distance[i] > NewDistance) + { + Swap(distance[i], NewDistance); + Swap(xs[i], NewX); + Swap(ys[i], NewY); + } + } + } + } + + out_x0 = xs[0]; + out_x1 = xs[1]; + out_x2 = xs[2]; + out_x3 = xs[3]; + + out_y0 = ys[0]; + out_y1 = ys[1]; + out_y2 = ys[2]; + out_y3 = ys[3]; + + ensureVoxelSlow(FMath::IsNearlyZero(distance[0])); + // distance0 is always 0 as it's the distance to the border but we're inside out_distance0 = distance[0]; + out_distance1 = distance[1]; + out_distance2 = distance[2]; + out_distance3 = distance[3]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_CellularNoise::CellularDistance_2D(v_flt vecX, v_flt vecY) +{ + switch (CellularDistance) + { + default: ensureVoxelSlow(false); + case EVoxelCellularDistanceFunction::Euclidean: + return vecX * vecX + vecY * vecY; + case EVoxelCellularDistanceFunction::Manhattan: + return FNoiseMath::FastAbs(vecX) + FNoiseMath::FastAbs(vecY); + case EVoxelCellularDistanceFunction::Natural: + return (FNoiseMath::FastAbs(vecX) + FNoiseMath::FastAbs(vecY)) + (vecX * vecX + vecY * vecY); + } +} + +template +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_CellularNoise::CellularDistance_3D(v_flt vecX, v_flt vecY, v_flt vecZ) +{ + switch (CellularDistance) + { + default: ensureVoxelSlow(false); + case EVoxelCellularDistanceFunction::Euclidean: + return vecX * vecX + vecY * vecY + vecZ * vecZ; + case EVoxelCellularDistanceFunction::Manhattan: + return FNoiseMath::FastAbs(vecX) + FNoiseMath::FastAbs(vecY) + FNoiseMath::FastAbs(vecZ); + case EVoxelCellularDistanceFunction::Natural: + return (FNoiseMath::FastAbs(vecX) + FNoiseMath::FastAbs(vecY) + FNoiseMath::FastAbs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_CellularNoise::GetErosion_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt noise_dx, v_flt noise_dy, v_flt& outDx, v_flt& outDy) const +{ + // From https://www.shadertoy.com/view/MtGcWh + + x *= frequency; + y *= frequency; + + // Take the curl of the normal to get the gradient facing down the slope + const v_flt dir_x = noise_dy; + const v_flt dir_y = -noise_dx; + + v_flt r = 0; + v_flt r_dx = 0; + v_flt r_dy = 0; + + // Now we compute another fbm type noise + // erosion is a type of noise with a strong directionality + // we pass in the direction based on the slope of the terrain + // erosion also returns the slope. we add that to a running total + // so that the direction of successive layers are based on the + // past layers + v_flt amp = 1.f; + for (int32 i = 0; i < octaves; i++) + { + v_flt erosion_dx, erosion_dy; + const v_flt erosion = SingleGavoronoi_Erosion_2D(i, x, y, dir_x + r_dy, dir_y - r_dx, erosion_dx, erosion_dy); + + r += erosion * amp; + r_dx += erosion_dx * amp; + r_dy += erosion_dy * amp; + + x *= This().Lacunarity; + y *= This().Lacunarity; + amp *= This().Gain; + } + + outDx = r_dx; + outDy = r_dy; + return r; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_MATH void TVoxelFastNoise_CellularNoise::AccumulateCrater(const v_flt sqDistance, v_flt& va, v_flt& wt) const +{ + // This is heavily inspired from https://www.shadertoy.com/view/MtjGRD + + // Early cull far craters + if (sqDistance >= 1) return; + + const v_flt distance = std::sqrt(sqDistance); + + // Make sure that the function is nulled beyond 1, else we get issue when only iterating nearby craters + v_flt w = FMath::Clamp((1.f - distance) * 2.f, 0, 1); + + // Exponential decrease + w *= std::exp(-4.f * distance); + + v_flt multiplier = 1; + if (This().CraterFalloffExponent != 0) + { + multiplier = std::exp(-This().CraterFalloffExponent * distance); + } + + // Sin for the bump + // + 0.055f so we don't get a bump in the middle of the crater + va += w * std::sin(2.f * PI * std::sqrt(distance + 0.055f)) * multiplier; + wt += w; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.h new file mode 100644 index 00000000..24ea525a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.h @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_CubicNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + GENERATED_VOXEL_NOISE_FUNCTION_2D(Cubic) + GENERATED_VOXEL_NOISE_FUNCTION_3D(Cubic) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(Cubic, Cubic) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(Cubic, Cubic) + +protected: + static constexpr v_flt CUBIC_2D_BOUNDING = 1 / (v_flt(1.5) * v_flt(1.5)); + static constexpr v_flt CUBIC_3D_BOUNDING = 1 / (v_flt(1.5) * v_flt(1.5) * v_flt(1.5)); + + v_flt SingleCubic_2D(uint8 offset, v_flt x, v_flt y) const; + v_flt SingleCubic_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.inl new file mode 100644 index 00000000..545dbd34 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.inl @@ -0,0 +1,110 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_CubicNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CubicNoise::SingleCubic_2D(uint8 offset, v_flt x, v_flt y) const +{ + const int32 x1 = FNoiseMath::FastFloor(x); + const int32 y1 = FNoiseMath::FastFloor(y); + + const int32 x0 = x1 - 1; + const int32 y0 = y1 - 1; + const int32 x2 = x1 + 1; + const int32 y2 = y1 + 1; + const int32 x3 = x1 + 2; + const int32 y3 = y1 + 2; + + const v_flt xs = x - v_flt(x1); + const v_flt ys = y - v_flt(y1); + + return FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp(This().ValCoord2DFast(offset, x0, y0), This().ValCoord2DFast(offset, x1, y0), This().ValCoord2DFast(offset, x2, y0), This().ValCoord2DFast(offset, x3, y0), xs), + FNoiseMath::CubicLerp(This().ValCoord2DFast(offset, x0, y1), This().ValCoord2DFast(offset, x1, y1), This().ValCoord2DFast(offset, x2, y1), This().ValCoord2DFast(offset, x3, y1), xs), + FNoiseMath::CubicLerp(This().ValCoord2DFast(offset, x0, y2), This().ValCoord2DFast(offset, x1, y2), This().ValCoord2DFast(offset, x2, y2), This().ValCoord2DFast(offset, x3, y2), xs), + FNoiseMath::CubicLerp(This().ValCoord2DFast(offset, x0, y3), This().ValCoord2DFast(offset, x1, y3), This().ValCoord2DFast(offset, x2, y3), This().ValCoord2DFast(offset, x3, y3), xs), + ys) * CUBIC_2D_BOUNDING; +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_CubicNoise::SingleCubic_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const +{ + const int32 x1 = FNoiseMath::FastFloor(x); + const int32 y1 = FNoiseMath::FastFloor(y); + const int32 z1 = FNoiseMath::FastFloor(z); + + const int32 x0 = x1 - 1; + const int32 y0 = y1 - 1; + const int32 z0 = z1 - 1; + const int32 x2 = x1 + 1; + const int32 y2 = y1 + 1; + const int32 z2 = z1 + 1; + const int32 x3 = x1 + 2; + const int32 y3 = y1 + 2; + const int32 z3 = z1 + 2; + + const v_flt xs = x - v_flt(x1); + const v_flt ys = y - v_flt(y1); + const v_flt zs = z - v_flt(z1); + + return FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y0, z0), This().ValCoord3DFast(offset, x1, y0, z0), This().ValCoord3DFast(offset, x2, y0, z0), This().ValCoord3DFast(offset, x3, y0, z0), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y1, z0), This().ValCoord3DFast(offset, x1, y1, z0), This().ValCoord3DFast(offset, x2, y1, z0), This().ValCoord3DFast(offset, x3, y1, z0), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y2, z0), This().ValCoord3DFast(offset, x1, y2, z0), This().ValCoord3DFast(offset, x2, y2, z0), This().ValCoord3DFast(offset, x3, y2, z0), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y3, z0), This().ValCoord3DFast(offset, x1, y3, z0), This().ValCoord3DFast(offset, x2, y3, z0), This().ValCoord3DFast(offset, x3, y3, z0), xs), + ys), + FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y0, z1), This().ValCoord3DFast(offset, x1, y0, z1), This().ValCoord3DFast(offset, x2, y0, z1), This().ValCoord3DFast(offset, x3, y0, z1), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y1, z1), This().ValCoord3DFast(offset, x1, y1, z1), This().ValCoord3DFast(offset, x2, y1, z1), This().ValCoord3DFast(offset, x3, y1, z1), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y2, z1), This().ValCoord3DFast(offset, x1, y2, z1), This().ValCoord3DFast(offset, x2, y2, z1), This().ValCoord3DFast(offset, x3, y2, z1), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y3, z1), This().ValCoord3DFast(offset, x1, y3, z1), This().ValCoord3DFast(offset, x2, y3, z1), This().ValCoord3DFast(offset, x3, y3, z1), xs), + ys), + FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y0, z2), This().ValCoord3DFast(offset, x1, y0, z2), This().ValCoord3DFast(offset, x2, y0, z2), This().ValCoord3DFast(offset, x3, y0, z2), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y1, z2), This().ValCoord3DFast(offset, x1, y1, z2), This().ValCoord3DFast(offset, x2, y1, z2), This().ValCoord3DFast(offset, x3, y1, z2), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y2, z2), This().ValCoord3DFast(offset, x1, y2, z2), This().ValCoord3DFast(offset, x2, y2, z2), This().ValCoord3DFast(offset, x3, y2, z2), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y3, z2), This().ValCoord3DFast(offset, x1, y3, z2), This().ValCoord3DFast(offset, x2, y3, z2), This().ValCoord3DFast(offset, x3, y3, z2), xs), + ys), + FNoiseMath::CubicLerp( + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y0, z3), This().ValCoord3DFast(offset, x1, y0, z3), This().ValCoord3DFast(offset, x2, y0, z3), This().ValCoord3DFast(offset, x3, y0, z3), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y1, z3), This().ValCoord3DFast(offset, x1, y1, z3), This().ValCoord3DFast(offset, x2, y1, z3), This().ValCoord3DFast(offset, x3, y1, z3), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y2, z3), This().ValCoord3DFast(offset, x1, y2, z3), This().ValCoord3DFast(offset, x2, y2, z3), This().ValCoord3DFast(offset, x3, y2, z3), xs), + FNoiseMath::CubicLerp(This().ValCoord3DFast(offset, x0, y3, z3), This().ValCoord3DFast(offset, x1, y3, z3), This().ValCoord3DFast(offset, x2, y3, z3), This().ValCoord3DFast(offset, x3, y3, z3), xs), + ys), + zs) * CUBIC_3D_BOUNDING; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.h new file mode 100644 index 00000000..695845cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.h @@ -0,0 +1,23 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_GradientPerturb : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + + void GradientPerturb_2D(v_flt& x, v_flt& y, v_flt frequency, v_flt m_gradientPerturbAmp) const; + void GradientPerturb_3D(v_flt& x, v_flt& y, v_flt& z, v_flt frequency, v_flt m_gradientPerturbAmp) const; + + void GradientPerturbFractal_2D(v_flt& x, v_flt& y, v_flt frequency, int32 octaves, v_flt m_gradientPerturbAmp) const; + void GradientPerturbFractal_3D(v_flt& x, v_flt& y, v_flt& z, v_flt frequency, int32 octaves, v_flt m_gradientPerturbAmp) const; + +protected: + void SingleGradientPerturb_2D(uint8 offset, v_flt warpAmp, v_flt frequency, v_flt& x, v_flt& y) const; + void SingleGradientPerturb_3D(uint8 offset, v_flt warpAmp, v_flt frequency, v_flt& x, v_flt& y, v_flt& z) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.inl new file mode 100644 index 00000000..1c1268b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.inl @@ -0,0 +1,185 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_GradientPerturb.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + + +template +FN_FORCEINLINE void TVoxelFastNoise_GradientPerturb::GradientPerturb_2D(v_flt& x, v_flt& y, v_flt frequency, v_flt m_gradientPerturbAmp) const +{ + SingleGradientPerturb_2D(0, m_gradientPerturbAmp, frequency, x, y); +} + +template +FN_FORCEINLINE void TVoxelFastNoise_GradientPerturb::GradientPerturb_3D(v_flt& x, v_flt& y, v_flt& z, v_flt frequency, v_flt m_gradientPerturbAmp) const +{ + SingleGradientPerturb_3D(0, m_gradientPerturbAmp, frequency, x, y, z); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE void TVoxelFastNoise_GradientPerturb::GradientPerturbFractal_2D(v_flt& x, v_flt& y, v_flt frequency, int32 octaves, v_flt m_gradientPerturbAmp) const +{ + v_flt amp = m_gradientPerturbAmp * This().FractalBounding; + v_flt freq = frequency; + int32 i = 0; + + SingleGradientPerturb_2D(This().Perm[0], amp, frequency, x, y); + + while (++i < octaves) + { + freq *= This().Lacunarity; + amp *= This().Gain; + SingleGradientPerturb_2D(This().Perm[i], amp, freq, x, y); + } +} + +template +FN_FORCEINLINE void TVoxelFastNoise_GradientPerturb::GradientPerturbFractal_3D(v_flt& x, v_flt& y, v_flt& z, v_flt frequency, int32 octaves, v_flt m_gradientPerturbAmp) const +{ + v_flt amp = m_gradientPerturbAmp * This().FractalBounding; + v_flt freq = frequency; + int32 i = 0; + + SingleGradientPerturb_3D(This().Perm[0], amp, frequency, x, y, z); + + while (++i < octaves) + { + freq *= This().Lacunarity; + amp *= This().Gain; + SingleGradientPerturb_3D(This().Perm[i], amp, freq, x, y, z); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_SINGLE void TVoxelFastNoise_GradientPerturb::SingleGradientPerturb_2D(uint8 offset, v_flt warpAmp, v_flt frequency, v_flt& x, v_flt& y) const +{ + const v_flt xf = x * frequency; + const v_flt yf = y * frequency; + + const int32 x0 = FNoiseMath::FastFloor(xf); + const int32 y0 = FNoiseMath::FastFloor(yf); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + + const v_flt fx = xf - x0; + const v_flt fy = yf - y0; + + v_flt xs, ys; + This().Interpolate_2D(fx, fy, xs, ys); + + int32 lutPos0 = This().Index2D_256(offset, x0, y0); + int32 lutPos1 = This().Index2D_256(offset, x1, y0); + + const v_flt lx0x = FNoiseMath::Lerp(This().CELL_2D_X[lutPos0], This().CELL_2D_X[lutPos1], xs); + const v_flt ly0x = FNoiseMath::Lerp(This().CELL_2D_Y[lutPos0], This().CELL_2D_Y[lutPos1], xs); + + lutPos0 = This().Index2D_256(offset, x0, y1); + lutPos1 = This().Index2D_256(offset, x1, y1); + + const v_flt lx1x = FNoiseMath::Lerp(This().CELL_2D_X[lutPos0], This().CELL_2D_X[lutPos1], xs); + const v_flt ly1x = FNoiseMath::Lerp(This().CELL_2D_Y[lutPos0], This().CELL_2D_Y[lutPos1], xs); + + x += FNoiseMath::Lerp(lx0x, lx1x, ys) * warpAmp; + y += FNoiseMath::Lerp(ly0x, ly1x, ys) * warpAmp; +} + +template +FN_FORCEINLINE_SINGLE void TVoxelFastNoise_GradientPerturb::SingleGradientPerturb_3D(uint8 offset, v_flt warpAmp, v_flt frequency, v_flt& x, v_flt& y, v_flt& z) const +{ + const v_flt xf = x * frequency; + const v_flt yf = y * frequency; + const v_flt zf = z * frequency; + + const int32 x0 = FNoiseMath::FastFloor(xf); + const int32 y0 = FNoiseMath::FastFloor(yf); + const int32 z0 = FNoiseMath::FastFloor(zf); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + const int32 z1 = z0 + 1; + + const v_flt fx = xf - x0; + const v_flt fy = yf - y0; + const v_flt fz = zf - z0; + + v_flt xs, ys, zs; + This().Interpolate_3D(fx, fy, fz, xs, ys, zs); + + int32 lutPos0 = This().Index3D_256(offset, x0, y0, z0); + int32 lutPos1 = This().Index3D_256(offset, x1, y0, z0); + + v_flt lx0x = FNoiseMath::Lerp(This().CELL_3D_X[lutPos0], This().CELL_3D_X[lutPos1], xs); + v_flt ly0x = FNoiseMath::Lerp(This().CELL_3D_Y[lutPos0], This().CELL_3D_Y[lutPos1], xs); + v_flt lz0x = FNoiseMath::Lerp(This().CELL_3D_Z[lutPos0], This().CELL_3D_Z[lutPos1], xs); + + lutPos0 = This().Index3D_256(offset, x0, y1, z0); + lutPos1 = This().Index3D_256(offset, x1, y1, z0); + + v_flt lx1x = FNoiseMath::Lerp(This().CELL_3D_X[lutPos0], This().CELL_3D_X[lutPos1], xs); + v_flt ly1x = FNoiseMath::Lerp(This().CELL_3D_Y[lutPos0], This().CELL_3D_Y[lutPos1], xs); + v_flt lz1x = FNoiseMath::Lerp(This().CELL_3D_Z[lutPos0], This().CELL_3D_Z[lutPos1], xs); + + const v_flt lx0y = FNoiseMath::Lerp(lx0x, lx1x, ys); + const v_flt ly0y = FNoiseMath::Lerp(ly0x, ly1x, ys); + const v_flt lz0y = FNoiseMath::Lerp(lz0x, lz1x, ys); + + lutPos0 = This().Index3D_256(offset, x0, y0, z1); + lutPos1 = This().Index3D_256(offset, x1, y0, z1); + + lx0x = FNoiseMath::Lerp(This().CELL_3D_X[lutPos0], This().CELL_3D_X[lutPos1], xs); + ly0x = FNoiseMath::Lerp(This().CELL_3D_Y[lutPos0], This().CELL_3D_Y[lutPos1], xs); + lz0x = FNoiseMath::Lerp(This().CELL_3D_Z[lutPos0], This().CELL_3D_Z[lutPos1], xs); + + lutPos0 = This().Index3D_256(offset, x0, y1, z1); + lutPos1 = This().Index3D_256(offset, x1, y1, z1); + + lx1x = FNoiseMath::Lerp(This().CELL_3D_X[lutPos0], This().CELL_3D_X[lutPos1], xs); + ly1x = FNoiseMath::Lerp(This().CELL_3D_Y[lutPos0], This().CELL_3D_Y[lutPos1], xs); + lz1x = FNoiseMath::Lerp(This().CELL_3D_Z[lutPos0], This().CELL_3D_Z[lutPos1], xs); + + x += FNoiseMath::Lerp(lx0y, FNoiseMath::Lerp(lx0x, lx1x, ys), zs) * warpAmp; + y += FNoiseMath::Lerp(ly0y, FNoiseMath::Lerp(ly0x, ly1x, ys), zs) * warpAmp; + z += FNoiseMath::Lerp(lz0y, FNoiseMath::Lerp(lz0x, lz1x, ys), zs) * warpAmp; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.h new file mode 100644 index 00000000..663b11fb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_PerlinNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + GENERATED_VOXEL_NOISE_FUNCTION_2D(Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_2D_DERIV(Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_3D(Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_3D_DERIV(Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(Perlin, Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D_DERIV(Perlin, Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(Perlin, Perlin) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D_DERIV(Perlin, Perlin) + +protected: + v_flt SinglePerlin_2D(uint8 offset, v_flt x, v_flt y) const; + v_flt SinglePerlin_2D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt& outDx, v_flt& outDy) const; + + v_flt SinglePerlin_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const; + v_flt SinglePerlin_3D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt z, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.inl new file mode 100644 index 00000000..e091e654 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.inl @@ -0,0 +1,244 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_PerlinNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_PerlinNoise::SinglePerlin_2D(uint8 offset, v_flt x, v_flt y) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + + v_flt xs, ys; + This().Interpolate_2D(fx, fy, xs, ys); + + const v_flt xd0 = fx; + const v_flt yd0 = fy; + + const v_flt xd1 = xd0 - 1; + const v_flt yd1 = yd0 - 1; + + const v_flt xf0 = FNoiseMath::Lerp(This().GradCoord2D(offset, x0, y0, xd0, yd0), This().GradCoord2D(offset, x1, y0, xd1, yd0), xs); + const v_flt xf1 = FNoiseMath::Lerp(This().GradCoord2D(offset, x0, y1, xd0, yd1), This().GradCoord2D(offset, x1, y1, xd1, yd1), xs); + + return FNoiseMath::Lerp(xf0, xf1, ys); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_PerlinNoise::SinglePerlin_2D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt& outDx, v_flt& outDy) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + + v_flt xs, ys; + v_flt dx, dy; + This().Interpolate_2D_Deriv(fx, fy, xs, ys, dx, dy); + + const v_flt xd0 = fx; + const v_flt yd0 = fy; + const v_flt xd1 = xd0 - 1; + const v_flt yd1 = yd0 - 1; + + v_flt gax, gay; + v_flt gbx, gby; + v_flt gcx, gcy; + v_flt gdx, gdy; + + const v_flt va = This().GradCoord2D(offset, x0, y0, xd0, yd0, gax, gay); + const v_flt vb = This().GradCoord2D(offset, x1, y0, xd1, yd0, gbx, gby); + const v_flt vc = This().GradCoord2D(offset, x0, y1, xd0, yd1, gcx, gcy); + const v_flt vd = This().GradCoord2D(offset, x1, y1, xd1, yd1, gdx, gdy); + + outDx = gax + xs * (gbx - gax) + ys * (gcx - gax) + xs * ys * (gax - gbx - gcx + gdx) + dx * (ys * (va - vb - vc + vd) + vb - va); + outDy = gay + xs * (gby - gay) + ys * (gcy - gay) + xs * ys * (gay - gby - gcy + gdy) + dy * (xs * (va - vb - vc + vd) + vc - va); + + return va + xs * (vb - va) + ys * (vc - va) + xs * ys * (va - vb - vc + vd); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_PerlinNoise::SinglePerlin_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + const int32 z0 = FNoiseMath::FastFloor(z); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + const int32 z1 = z0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + const v_flt fz = z - z0; + + v_flt xs, ys, zs; + This().Interpolate_3D(fx, fy, fz, xs, ys, zs); + + const v_flt xd0 = fx; + const v_flt yd0 = fy; + const v_flt zd0 = fz; + const v_flt xd1 = xd0 - 1; + const v_flt yd1 = yd0 - 1; + const v_flt zd1 = zd0 - 1; + + const v_flt xf00 = FNoiseMath::Lerp(This().GradCoord3D(offset, x0, y0, z0, xd0, yd0, zd0), This().GradCoord3D(offset, x1, y0, z0, xd1, yd0, zd0), xs); + const v_flt xf10 = FNoiseMath::Lerp(This().GradCoord3D(offset, x0, y1, z0, xd0, yd1, zd0), This().GradCoord3D(offset, x1, y1, z0, xd1, yd1, zd0), xs); + const v_flt xf01 = FNoiseMath::Lerp(This().GradCoord3D(offset, x0, y0, z1, xd0, yd0, zd1), This().GradCoord3D(offset, x1, y0, z1, xd1, yd0, zd1), xs); + const v_flt xf11 = FNoiseMath::Lerp(This().GradCoord3D(offset, x0, y1, z1, xd0, yd1, zd1), This().GradCoord3D(offset, x1, y1, z1, xd1, yd1, zd1), xs); + + const v_flt yf0 = FNoiseMath::Lerp(xf00, xf10, ys); + const v_flt yf1 = FNoiseMath::Lerp(xf01, xf11, ys); + + return FNoiseMath::Lerp(yf0, yf1, zs); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_PerlinNoise::SinglePerlin_3D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt z, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + const int32 z0 = FNoiseMath::FastFloor(z); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + const int32 z1 = z0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + const v_flt fz = z - z0; + + v_flt xs, ys, zs; + v_flt dx, dy, dz; + This().Interpolate_3D_Deriv(fx, fy, fz, xs, ys, zs, dx, dy, dz); + + const v_flt xd0 = fx; + const v_flt yd0 = fy; + const v_flt zd0 = fz; + const v_flt xd1 = xd0 - 1; + const v_flt yd1 = yd0 - 1; + const v_flt zd1 = zd0 - 1; + + v_flt gax, gay, gaz; + v_flt gbx, gby, gbz; + v_flt gcx, gcy, gcz; + v_flt gdx, gdy, gdz; + v_flt gex, gey, gez; + v_flt gfx, gfy, gfz; + v_flt ggx, ggy, ggz; + v_flt ghx, ghy, ghz; + + const v_flt va = This().GradCoord3D(offset, x0, y0, z0, xd0, yd0, zd0, gax, gay, gaz); + const v_flt vb = This().GradCoord3D(offset, x1, y0, z0, xd1, yd0, zd0, gbx, gby, gbz); + const v_flt vc = This().GradCoord3D(offset, x0, y1, z0, xd0, yd1, zd0, gcx, gcy, gcz); + const v_flt vd = This().GradCoord3D(offset, x1, y1, z0, xd1, yd1, zd0, gdx, gdy, gdz); + const v_flt ve = This().GradCoord3D(offset, x0, y0, z1, xd0, yd0, zd1, gex, gey, gez); + const v_flt vf = This().GradCoord3D(offset, x1, y0, z1, xd1, yd0, zd1, gfx, gfy, gfz); + const v_flt vg = This().GradCoord3D(offset, x0, y1, z1, xd0, yd1, zd1, ggx, ggy, ggz); + const v_flt vh = This().GradCoord3D(offset, x1, y1, z1, xd1, yd1, zd1, ghx, ghy, ghz); + + outDx = + gax + + xs * (gbx - gax) + + ys * (gcx - gax) + + zs * (gex - gax) + + xs * ys * (gax - gbx - gcx + gdx) + + ys * zs * (gax - gcx - gex + ggx) + + zs * xs * (gax - gbx - gex + gfx) + + xs * ys * zs * (-gax + gbx + gcx - gdx + gex - gfx - ggx + ghx) + + + dx * ( + vb - va + + ys * (va - vb - vc + vd) + + zs * (va - vb - ve + vf) + + ys * zs * (-va + vb + vc - vd + ve - vf - vg + vh)); + + outDy = + gay + + xs * (gby - gay) + + ys * (gcy - gay) + + zs * (gey - gay) + + xs * ys * (gay - gby - gcy + gdy) + + ys * zs * (gay - gcy - gey + ggy) + + zs * xs * (gay - gby - gey + gfy) + + xs * ys * zs * (-gay + gby + gcy - gdy + gey - gfy - ggy + ghy) + + + dy * ( + vc - va + + zs * (va - vc - ve + vg) + + xs * (va - vb - vc + vd) + + zs * xs * (-va + vb + vc - vd + ve - vf - vg + vh)); + + outDz = + gaz + + xs * (gbz - gaz) + + ys * (gcz - gaz) + + zs * (gez - gaz) + + xs * ys * (gaz - gbz - gcz + gdz) + + ys * zs * (gaz - gcz - gez + ggz) + + zs * xs * (gaz - gbz - gez + gfz) + + xs * ys * zs * (-gaz + gbz + gcz - gdz + gez - gfz - ggz + ghz) + + + dz * ( + ve - va + + xs * (va - vb - ve + vf) + + ys * (va - vc - ve + vg) + + xs * ys * (-va + vb + vc - vd + ve - vf - vg + vh)); + + return + va + + xs * (vb - va) + + ys * (vc - va) + + zs * (ve - va) + + xs * ys * (va - vb - vc + vd) + + ys * zs * (va - vc - ve + vg) + + zs * xs * (va - vb - ve + vf) + + xs * ys * zs * (-va + vb + vc - vd + ve - vf - vg + vh); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.h new file mode 100644 index 00000000..b27bd537 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_SimplexNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + GENERATED_VOXEL_NOISE_FUNCTION_2D(Simplex) + GENERATED_VOXEL_NOISE_FUNCTION_3D(Simplex) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(Simplex, Simplex) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(Simplex, Simplex) + +protected: + static constexpr v_flt SQRT3 = v_flt(1.7320508075688772935274463415059); + static constexpr v_flt F2 = v_flt(0.5) * (SQRT3 - v_flt(1.0)); + static constexpr v_flt G2 = (v_flt(3.0) - SQRT3) / v_flt(6.0); + + static constexpr v_flt F3 = 1 / v_flt(3); + static constexpr v_flt G3 = 1 / v_flt(6); + + v_flt SingleSimplex_2D(uint8 offset, v_flt x, v_flt y) const; + v_flt SingleSimplex_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.inl new file mode 100644 index 00000000..35439ed0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.inl @@ -0,0 +1,216 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_SimplexNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_SimplexNoise::SingleSimplex_2D(uint8 offset, v_flt x, v_flt y) const +{ + v_flt t = (x + y) * F2; + const int32 i = FNoiseMath::FastFloor(x + t); + const int32 j = FNoiseMath::FastFloor(y + t); + + t = (i + j) * G2; + const v_flt X0 = i - t; + const v_flt Y0 = j - t; + + const v_flt x0 = x - X0; + const v_flt y0 = y - Y0; + + int32 i1, j1; + if (x0 > y0) + { + i1 = 1; j1 = 0; + } + else + { + i1 = 0; j1 = 1; + } + + const v_flt x1 = x0 - v_flt(i1) + G2; + const v_flt y1 = y0 - v_flt(j1) + G2; + const v_flt x2 = x0 - 1 + 2 * G2; + const v_flt y2 = y0 - 1 + 2 * G2; + + v_flt n0, n1, n2; + + t = v_flt(0.5) - x0 * x0 - y0 * y0; + if (t < 0) + { + n0 = 0; + } + else + { + t *= t; + n0 = t * t * This().GradCoord2D(offset, i, j, x0, y0); + } + + t = v_flt(0.5) - x1 * x1 - y1 * y1; + if (t < 0) + { + n1 = 0; + } + else + { + t *= t; + n1 = t * t * This().GradCoord2D(offset, i + i1, j + j1, x1, y1); + } + + t = v_flt(0.5) - x2 * x2 - y2 * y2; + if (t < 0) + { + n2 = 0; + } + else + { + t *= t; + n2 = t * t * This().GradCoord2D(offset, i + 1, j + 1, x2, y2); + } + + return 70 * (n0 + n1 + n2); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_SimplexNoise::SingleSimplex_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const +{ + v_flt t = (x + y + z) * F3; + const int32 i = FNoiseMath::FastFloor(x + t); + const int32 j = FNoiseMath::FastFloor(y + t); + const int32 k = FNoiseMath::FastFloor(z + t); + + t = (i + j + k) * G3; + const v_flt X0 = i - t; + const v_flt Y0 = j - t; + const v_flt Z0 = k - t; + + const v_flt x0 = x - X0; + const v_flt y0 = y - Y0; + const v_flt z0 = z - Z0; + + int32 i1, j1, k1; + int32 i2, j2, k2; + + if (x0 >= y0) + { + if (y0 >= z0) + { + i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; + } + else if (x0 >= z0) + { + i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; + } + else // x0 < z0 + { + i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; + } + } + else // x0 < y0 + { + if (y0 < z0) + { + i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; + } + else if (x0 < z0) + { + i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; + } + else // x0 >= z0 + { + i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; + } + } + + const v_flt x1 = x0 - i1 + G3; + const v_flt y1 = y0 - j1 + G3; + const v_flt z1 = z0 - k1 + G3; + + const v_flt x2 = x0 - i2 + 2 * G3; + const v_flt y2 = y0 - j2 + 2 * G3; + const v_flt z2 = z0 - k2 + 2 * G3; + + const v_flt x3 = x0 - 1 + 3 * G3; + const v_flt y3 = y0 - 1 + 3 * G3; + const v_flt z3 = z0 - 1 + 3 * G3; + + v_flt n0, n1, n2, n3; + + t = v_flt(0.6) - x0 * x0 - y0 * y0 - z0 * z0; + if (t < 0) + { + n0 = 0; + } + else + { + t *= t; + n0 = t * t * This().GradCoord3D(offset, i, j, k, x0, y0, z0); + } + + t = v_flt(0.6) - x1 * x1 - y1 * y1 - z1 * z1; + if (t < 0) + { + n1 = 0; + } + else + { + t *= t; + n1 = t * t * This().GradCoord3D(offset, i + i1, j + j1, k + k1, x1, y1, z1); + } + + t = v_flt(0.6) - x2 * x2 - y2 * y2 - z2 * z2; + if (t < 0) + { + n2 = 0; + } + else + { + t *= t; + n2 = t * t * This().GradCoord3D(offset, i + i2, j + j2, k + k2, x2, y2, z2); + } + + t = v_flt(0.6) - x3 * x3 - y3 * y3 - z3 * z3; + if (t < 0) + { + n3 = 0; + } + else + { + t *= t; + n3 = t * t * This().GradCoord3D(offset, i + 1, j + 1, k + 1, x3, y3, z3); + } + + return 32 * (n0 + n1 + n2 + n3); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.h new file mode 100644 index 00000000..12d6e3fc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.h @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_ValueNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + GENERATED_VOXEL_NOISE_FUNCTION_2D(Value) + GENERATED_VOXEL_NOISE_FUNCTION_2D_DERIV(Value) + GENERATED_VOXEL_NOISE_FUNCTION_3D(Value) + GENERATED_VOXEL_NOISE_FUNCTION_3D_DERIV(Value) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D(Value, Value) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_2D_DERIV(Value, Value) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D(Value, Value) + GENERATED_VOXEL_NOISE_FUNCTION_FRACTAL_3D_DERIV(Value, Value) + +protected: + v_flt SingleValue_2D(uint8 offset, v_flt x, v_flt y) const; + v_flt SingleValue_2D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt& outDx, v_flt& outDy) const; + + v_flt SingleValue_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const; + v_flt SingleValue_3D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt z, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; + + VectorRegister SingleValue_2D(VectorRegisterInt offset, VectorRegister x, VectorRegister y) const; + +public: + v_flt IQNoise_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves) const; + v_flt IQNoise_2D_Deriv(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const; + + v_flt IQNoise_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves) const; + v_flt IQNoise_3D_Deriv(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.inl new file mode 100644 index 00000000..69ec7811 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.inl @@ -0,0 +1,307 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_ValueNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_ValueNoise::SingleValue_2D(uint8 offset, v_flt x, v_flt y) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + + v_flt xs, ys; + This().Interpolate_2D(fx, fy, xs, ys); + + const v_flt xf0 = FNoiseMath::Lerp(This().ValCoord2DFast(offset, x0, y0), This().ValCoord2DFast(offset, x1, y0), xs); + const v_flt xf1 = FNoiseMath::Lerp(This().ValCoord2DFast(offset, x0, y1), This().ValCoord2DFast(offset, x1, y1), xs); + + return FNoiseMath::Lerp(xf0, xf1, ys); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_ValueNoise::SingleValue_2D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt& outDx, v_flt& outDy) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + + v_flt xs, ys; + v_flt dx, dy; + This().Interpolate_2D_Deriv(fx, fy, xs, ys, dx, dy); + + const v_flt a = This().ValCoord2DFast(offset, x0, y0); + const v_flt b = This().ValCoord2DFast(offset, x1, y0); + const v_flt c = This().ValCoord2DFast(offset, x0, y1); + const v_flt d = This().ValCoord2DFast(offset, x1, y1); + + outDx = dx * (ys * (a - b - c + d) + b - a); + outDy = dy * (xs * (a - b - c + d) + c - a); + + return a + (b - a) * xs + (c - a) * ys + (a - b - c + d) * xs * ys; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_ValueNoise::SingleValue_3D(uint8 offset, v_flt x, v_flt y, v_flt z) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + const int32 z0 = FNoiseMath::FastFloor(z); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + const int32 z1 = z0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + const v_flt fz = z - z0; + + v_flt xs, ys, zs; + This().Interpolate_3D(fx, fy, fz, xs, ys, zs); + + const v_flt xf00 = FNoiseMath::Lerp(This().ValCoord3DFast(offset, x0, y0, z0), This().ValCoord3DFast(offset, x1, y0, z0), xs); + const v_flt xf10 = FNoiseMath::Lerp(This().ValCoord3DFast(offset, x0, y1, z0), This().ValCoord3DFast(offset, x1, y1, z0), xs); + const v_flt xf01 = FNoiseMath::Lerp(This().ValCoord3DFast(offset, x0, y0, z1), This().ValCoord3DFast(offset, x1, y0, z1), xs); + const v_flt xf11 = FNoiseMath::Lerp(This().ValCoord3DFast(offset, x0, y1, z1), This().ValCoord3DFast(offset, x1, y1, z1), xs); + + const v_flt yf0 = FNoiseMath::Lerp(xf00, xf10, ys); + const v_flt yf1 = FNoiseMath::Lerp(xf01, xf11, ys); + + return FNoiseMath::Lerp(yf0, yf1, zs); +} + +template +FN_FORCEINLINE_SINGLE v_flt TVoxelFastNoise_ValueNoise::SingleValue_3D_Deriv(uint8 offset, v_flt x, v_flt y, v_flt z, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + const int32 x0 = FNoiseMath::FastFloor(x); + const int32 y0 = FNoiseMath::FastFloor(y); + const int32 z0 = FNoiseMath::FastFloor(z); + + const int32 x1 = x0 + 1; + const int32 y1 = y0 + 1; + const int32 z1 = z0 + 1; + + const v_flt fx = x - x0; + const v_flt fy = y - y0; + const v_flt fz = z - z0; + + v_flt xs, ys, zs; + v_flt dx, dy, dz; + This().Interpolate_3D_Deriv(fx, fy, fz, xs, ys, zs, dx, dy, dz); + + const v_flt a = This().ValCoord3DFast(offset, x0, y0, z0); + const v_flt b = This().ValCoord3DFast(offset, x1, y0, z0); + const v_flt c = This().ValCoord3DFast(offset, x0, y1, z0); + const v_flt d = This().ValCoord3DFast(offset, x1, y1, z0); + const v_flt e = This().ValCoord3DFast(offset, x0, y0, z1); + const v_flt f = This().ValCoord3DFast(offset, x1, y0, z1); + const v_flt g = This().ValCoord3DFast(offset, x0, y1, z1); + const v_flt h = This().ValCoord3DFast(offset, x1, y1, z1); + + const v_flt k0 = a; + const v_flt k1 = b - a; + const v_flt k2 = c - a; + const v_flt k3 = e - a; + const v_flt k4 = a - b - c + d; + const v_flt k5 = a - c - e + g; + const v_flt k6 = a - b - e + f; + const v_flt k7 = -a + b + c - d + e - f - g + h; + + outDx = dx * (k1 + k4 * ys + k6 * zs + k7 * ys * zs); + outDy = dy * (k2 + k5 * zs + k4 * xs + k7 * zs * xs); + outDz = dz * (k3 + k6 * xs + k5 * ys + k7 * xs * ys); + + return k0 + k1 * xs + k2 * ys + k3 * zs + k4 * xs * ys + k5 * ys * zs + k6 * zs * xs + k7 * xs * ys * zs; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +VectorRegister TVoxelFastNoise_ValueNoise::SingleValue_2D(VectorRegisterInt offset, VectorRegister x, VectorRegister y) const +{ + const VectorRegister x0f = VectorFloor(x); + const VectorRegister y0f = VectorFloor(y); + + const VectorRegisterInt x0 = VectorFloatToInt(x0f); + const VectorRegisterInt y0 = VectorFloatToInt(y0f); + + const VectorRegisterInt x1 = VectorIntAdd(x0, GlobalVectorConstants::IntOne); + const VectorRegisterInt y1 = VectorIntAdd(y0, GlobalVectorConstants::IntOne); + + const VectorRegister fx = VectorSubtract(x, x0f); + const VectorRegister fy = VectorSubtract(y, y0f); + + VectorRegister xs, ys; + This().Interpolate_2D(fx, fy, xs, ys); + + const VectorRegister xf0 = FNoiseMath::Lerp(This().ValCoord2DFast(offset, x0, y0), This().ValCoord2DFast(offset, x1, y0), xs); + const VectorRegister xf1 = FNoiseMath::Lerp(This().ValCoord2DFast(offset, x0, y1), This().ValCoord2DFast(offset, x1, y1), xs); + + return FNoiseMath::Lerp(xf0, xf1, ys); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_ValueNoise::IQNoise_2D(v_flt x, v_flt y, v_flt frequency, int32 octaves) const +{ + v_flt dx, dy; + return IQNoise_2D_Deriv(x, y, frequency, octaves, dx, dy); +} + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_ValueNoise::IQNoise_2D_Deriv(v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy) const +{ + x *= frequency; + y *= frequency; + + v_flt sum = SingleValue_2D_Deriv(This().Perm[0], x, y, outDx, outDy); + v_flt amp = 1; + int32 i = 0; + + v_flt localDx = outDx; + v_flt localDy = outDy; + + while (++i < octaves) + { + x *= This().Lacunarity; + y *= This().Lacunarity; + const FVector2D P = This().Matrix2.TransformPoint({ float(x), float(y) }); + x = P.X; + y = P.Y; + + amp *= This().Gain; + + v_flt dx, dy; + const v_flt value = SingleValue_2D_Deriv(This().Perm[i], x, y, dx, dy); + + localDx += dx; + localDy += dy; + + const v_flt multiplier = amp / (1 + localDx * localDx + localDy * localDy);; + sum += value * multiplier; + + // Not exact, but still gives good results + outDx += dx * multiplier; + outDy += dy * multiplier; + } + + outDx *= This().FractalBounding; + outDy *= This().FractalBounding; + return sum * This().FractalBounding; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_ValueNoise::IQNoise_3D(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves) const +{ + v_flt dx, dy, dz; + return IQNoise_3D_Deriv(x, y, z, frequency, octaves, dx, dy, dz); +} + +template +FN_FORCEINLINE v_flt TVoxelFastNoise_ValueNoise::IQNoise_3D_Deriv(v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz) const +{ + x *= frequency; + y *= frequency; + z *= frequency; + + v_flt sum = SingleValue_3D_Deriv(This().Perm[0], x, y, z, outDx, outDy, outDz); + v_flt amp = 1; + int32 i = 0; + + v_flt localDx = outDx; + v_flt localDy = outDy; + v_flt localDz = outDz; + + while (++i < octaves) + { + x *= This().Lacunarity; + y *= This().Lacunarity; + z *= This().Lacunarity; + + const FVector4 P = This().Matrix3.TransformPosition({ float(x), float(y), float(z) }); + x = P.X; + y = P.Y; + z = P.Z; + + amp *= This().Gain; + + v_flt dx, dy, dz; + const v_flt value = SingleValue_3D_Deriv(This().Perm[i], x, y, z, dx, dy, dz); + + localDx += dx; + localDy += dy; + localDz += dz; + + const v_flt multiplier = amp / (1 + localDx * localDx + localDy * localDy + localDz * localDz);; + sum += value * multiplier; + + // Not exact, but still gives good results + outDx += dx * multiplier; + outDy += dy * multiplier; + outDy += dz * multiplier; + } + + outDx *= This().FractalBounding; + outDy *= This().FractalBounding; + outDz *= This().FractalBounding; + return sum * This().FractalBounding; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.h new file mode 100644 index 00000000..ea58f37b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.h @@ -0,0 +1,22 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelFastNoiseBase.h" + +template +class TVoxelFastNoise_WhiteNoise : public T +{ +public: + DEFINE_VOXEL_NOISE_CLASS() + + v_flt GetWhiteNoise_2D(v_flt x, v_flt y) const; + v_flt GetWhiteNoiseInt_2D(int32 x, int32 y) const; + + v_flt GetWhiteNoise_3D(v_flt x, v_flt y, v_flt z) const; + v_flt GetWhiteNoiseInt_3D(int32 x, int32 y, int32 z) const; + + v_flt GetWhiteNoise_4D(v_flt x, v_flt y, v_flt z, v_flt w) const; + v_flt GetWhiteNoiseInt_4D(int32 x, int32 y, int32 z, int32 w) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.inl new file mode 100644 index 00000000..3d26325d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.inl @@ -0,0 +1,90 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "FastNoise/VoxelFastNoise_WhiteNoise.h" +#include "FastNoise/VoxelFastNoiseBase.inl" + +// THIS CODE IS A MODIFIED VERSION OF FAST NOISE: SEE LICENSE BELOW +// +// MIT License +// +// Copyright(c) 2017 Jordan Peck +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// The developer's email is jorzixdan.me2@gzixmail.com (for great email, take +// off every 'zix'.) +// +// VERSION: 0.4.1 + +// ReSharper disable CppUE4CodingStandardNamingViolationWarning + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoise_2D(v_flt x, v_flt y) const +{ + return This().ValCoord2D(This().Seed, + *reinterpret_cast(&x) ^ (*reinterpret_cast(&x) >> 16), + *reinterpret_cast(&y) ^ (*reinterpret_cast(&y) >> 16)); +} + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoiseInt_2D(int32 x, int32 y) const +{ + return This().ValCoord2D(This().Seed, x, y); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoise_3D(v_flt x, v_flt y, v_flt z) const +{ + return This().ValCoord3D(This().Seed, + *reinterpret_cast(&x) ^ (*reinterpret_cast(&x) >> 16), + *reinterpret_cast(&y) ^ (*reinterpret_cast(&y) >> 16), + *reinterpret_cast(&z) ^ (*reinterpret_cast(&z) >> 16)); +} + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoiseInt_3D(int32 x, int32 y, int32 z) const +{ + return This().ValCoord3D(This().Seed, x, y, z); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoise_4D(v_flt x, v_flt y, v_flt z, v_flt w) const +{ + return This().ValCoord4D(This().Seed, + *reinterpret_cast(&x) ^ (*reinterpret_cast(&x) >> 16), + *reinterpret_cast(&y) ^ (*reinterpret_cast(&y) >> 16), + *reinterpret_cast(&z) ^ (*reinterpret_cast(&z) >> 16), + *reinterpret_cast(&w) ^ (*reinterpret_cast(&w) >> 16)); +} + +template +FN_FORCEINLINE_MATH v_flt TVoxelFastNoise_WhiteNoise::GetWhiteNoiseInt_4D(int32 x, int32 y, int32 z, int32 w) const +{ + return This().ValCoord4D(This().Seed, x, y, z, w); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/IVoxelPool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/IVoxelPool.h new file mode 100644 index 00000000..65f654db --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/IVoxelPool.h @@ -0,0 +1,111 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "IVoxelPool.generated.h" + +UENUM(BlueprintType) +enum class EVoxelTaskType : uint8 +{ + // Meshing of chunks that don't have collisions and are not visible + ChunksMeshing, + // Meshing of not visible chunks that have collisions + CollisionsChunksMeshing, + // Meshing of visible chunks that don't have collisions + VisibleChunksMeshing, + // Meshing of visible chunks that have collisions + VisibleCollisionsChunksMeshing, + // PhysX collision cooking, once the meshing task is done + CollisionCooking, + // Height spawners + FoliageBuild, + // Building of the instanced mesh components culling tree, used for spawners + // The meshes are not updated until the build is done + HISMBuild, + // Async edit functions such as AddSphereAsync + AsyncEditFunctions, + // Mesh merge tasks are used after meshing to create the render buffers + // Note: they are also used if bMergeChunks = false! + MeshMerge, + // The render octree is used to determine the LODs to display + // Should be done as fast as possible to start meshing tasks + RenderOctree +}; + +namespace EVoxelTaskType_DefaultPriorityCategories +{ + enum Type : int32 + { + Min = 0, + Max = 1000000, + + ChunksMeshing = 0, + CollisionsChunksMeshing = 1, + VisibleChunksMeshing = 10, + VisibleCollisionsChunksMeshing = 100, + CollisionCooking = 100, + FoliageBuild = 100, + HISMBuild = 1000, + AsyncEditFunctions = 50, + MeshMerge = 100000, + RenderOctree = 1000000 + }; +} + +namespace EVoxelTaskType_DefaultPriorityOffsets +{ + enum Type : int32 + { + ChunksMeshing = 0, + CollisionsChunksMeshing = 0, + VisibleChunksMeshing = 0, + // By default, do collision cooking slightly before collision meshing, and foliage slightly after + VisibleCollisionsChunksMeshing = 0, + CollisionCooking = +32, + FoliageBuild = -32, + HISMBuild = 0, + AsyncEditFunctions = 0, + MeshMerge = 0, + RenderOctree = 0 + }; +} + +class FVoxelQueuedThreadPool; +class FQueuedThreadPool; +class IVoxelQueuedWork; +class UWorld; + +class VOXEL_API IVoxelPool +{ +public: + virtual ~IVoxelPool() {} + + //~ Begin IVoxelPool Interface + virtual void QueueTask(EVoxelTaskType Type, IVoxelQueuedWork* Task) = 0; + virtual void QueueTasks(EVoxelTaskType Type, const TArray& Tasks) = 0; + + virtual int32 GetNumTasks() const = 0; + //~ End IVoxelPool Interface + +public: + static TVoxelSharedPtr GetWorldPool(UWorld* World); + static TVoxelSharedPtr GetGlobalPool(); + + static TVoxelSharedPtr GetPoolForWorld(UWorld* World); + +public: + static void SetWorldPool(UWorld* World, const TVoxelSharedRef& Pool, const FString& Creator); + static void SetGlobalPool(const TVoxelSharedRef& Pool, const FString& Creator); + +public: + static void DestroyWorldPool(UWorld* World); + static void DestroyGlobalPool(); + + static void Shutdown(); + +private: + static TMap, TVoxelSharedPtr> WorldsPools; + static TVoxelSharedPtr GlobalPool; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAsset.h new file mode 100644 index 00000000..8b3fa901 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAsset.h @@ -0,0 +1,152 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelDataAsset.generated.h" + +class AVoxelWorld; +class UTexture2D; +class UVoxelDataAsset; +class FVoxelDataAssetInstance; +struct FVoxelDataAssetData; + +#define DATA_ASSET_THUMBNAIL_RES 128 + +UENUM() +enum class EVoxelDataAssetImportSource +{ + None, + MagicaVox, + RawVox, + Mesh +}; + +USTRUCT() +struct FVoxelDataAssetImportSettings_MagicaVox +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Category = "Import") + bool bUsePalette = false; +}; + +/** + * A Data Asset stores the values of every voxel inside it + */ +UCLASS(HideDropdown, BlueprintType) +class VOXEL_API UVoxelDataAsset : public UVoxelTransformableGeneratorWithBounds +{ + GENERATED_BODY() + +public: + // If true, asset can be used to make holes in the world + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Config") + bool bSubtractiveAsset = false; + + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Config") + FIntVector PositionOffset; + + // When sampled, positions that are close to a whole number will be rounded + // Tolerance defines the threshold + // Automatically set to 0.1 in Cubic + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Config", AdvancedDisplay, meta = (UIMin = 0.f, UIMax = 1.f)) + float Tolerance = 0.0001f; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Config") + FIntVector Size; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Config") + float UncompressedSizeInMB = 0; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Config") + float CompressedSizeInMB = 0; + +public: + UPROPERTY(VisibleAnywhere, Category = "Import") + EVoxelDataAssetImportSource Source; + + UPROPERTY(VisibleAnywhere, Category = "Import") + TArray Paths; + + UPROPERTY(VisibleAnywhere, Category = "Import") + FVoxelDataAssetImportSettings_MagicaVox ImportSettings_MagicaVox; + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Data Asset") + FIntVector GetSize() const + { + return Size; + } + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data Asset") + virtual FVoxelIntBox GetBounds() const override + { + return FVoxelIntBox(PositionOffset, PositionOffset + Size); + } + +public: + UVoxelDataAsset(); + + //~ Begin UVoxelGenerator Interface + virtual TVoxelSharedRef GetInstance() override; + virtual TVoxelSharedRef GetTransformableInstance() override final; + //~ End UVoxelGenerator Interface + +public: + TVoxelSharedRef GetData(); + void SetData(const TVoxelSharedRef& InData); + + TVoxelSharedRef GetInstanceImpl(); + +protected: + void Save(); + void Load(); + + void TryLoad(); + void SyncProperties(); + +protected: + virtual void Serialize(FArchive& Ar) override; + +private: + TVoxelSharedPtr Data; + +private: + UPROPERTY() + int32 VoxelCustomVersion; + + UPROPERTY() + uint32 ValueConfigFlag; + + UPROPERTY() + uint32 MaterialConfigFlag; + + TArray CompressedData; + +private: +#if WITH_EDITORONLY_DATA + UPROPERTY(NonTransactional) + TArray ThumbnailSave; + + UPROPERTY(Transient) + UTexture2D* ThumbnailTexture; +#endif + +public: +#if WITH_EDITOR + void SetThumbnail(TArray&& Colors); + UTexture2D* GetThumbnail(); +#endif + +public: +#if WITH_EDITORONLY_DATA + // If true, new data assets will be created with these preview settings + UPROPERTY(EditAnywhere, Category = "Preview Settings") + bool bUseSettingsAsDefault = true; + + UPROPERTY() + AVoxelWorld* VoxelWorldTemplate; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.h new file mode 100644 index 00000000..26baa361 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.h @@ -0,0 +1,158 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" + +class AVoxelWorld; +class UTexture2D; +class FVoxelDataAssetInstance; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Data Assets Memory"), STAT_VoxelDataAssetMemory, STATGROUP_VoxelMemory, VOXEL_API); + +namespace FVoxelDataAssetDataVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +}; + +struct VOXEL_API FVoxelDataAssetData +{ + FVoxelDataAssetData() = default; + ~FVoxelDataAssetData() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataAssetMemory, AllocatedSize); + } + +public: + FORCEINLINE FIntVector GetSize() const + { + return Size; + } + + void SetSize(const FIntVector& NewSize, bool bCreateMaterials); + + FORCEINLINE bool HasMaterials() const + { + return Materials.Num() > 0; + } + FORCEINLINE bool IsEmpty() const + { + return Values.Num() <= 1 && Materials.Num() <= 1; + } + +public: + FORCEINLINE int32 GetIndex(int32 X, int32 Y, int32 Z) const + { + checkVoxelSlow(IsValidIndex(X, Y, Z)); + return X + Size.X * Y + Size.X * Size.Y * Z; + } + FORCEINLINE bool IsValidIndex(int32 X, int32 Y, int32 Z) const + { + return (0 <= X && X < Size.X) && + (0 <= Y && Y < Size.Y) && + (0 <= Z && Z < Size.Z); + } + FORCEINLINE bool IsValidIndex(float X, float Y, float Z) const + { + return (0 <= X && X <= Size.X - 1) && + (0 <= Y && Y <= Size.Y - 1) && + (0 <= Z && Z <= Size.Z - 1); + } + + FORCEINLINE void SetValue(int32 X, int32 Y, int32 Z, const FVoxelValue& NewValue) + { + checkVoxelSlow(Values.IsValidIndex(GetIndex(X, Y, Z))); + Values.GetData()[GetIndex(X, Y, Z)] = NewValue; + } + FORCEINLINE void SetMaterial(int32 X, int32 Y, int32 Z, const FVoxelMaterial& NewMaterial) + { + checkVoxelSlow(Materials.IsValidIndex(GetIndex(X, Y, Z))); + Materials.GetData()[GetIndex(X, Y, Z)] = NewMaterial; + } + + template + FORCEINLINE FVoxelValue GetValueUnsafe(T X, T Y, T Z) const + { + static_assert(TIsSame::Value, "should be int32"); + checkVoxelSlow(Values.IsValidIndex(GetIndex(X, Y, Z))); + return Values.GetData()[GetIndex(X, Y, Z)]; + } + template + FORCEINLINE FVoxelMaterial GetMaterialUnsafe(T X, T Y, T Z) const + { + static_assert(TIsSame::Value, "should be int32"); + checkVoxelSlow(Materials.IsValidIndex(GetIndex(X, Y, Z))); + return Materials.GetData()[GetIndex(X, Y, Z)]; + } + +public: + FVoxelValue GetValue(int32 X, int32 Y, int32 Z, FVoxelValue DefaultValue) const; + FVoxelMaterial GetMaterial(int32 X, int32 Y, int32 Z) const; + + template + FVoxelValue GetValue(T X, T Y, T Z, FVoxelValue DefaultValue) const = delete; + template + FVoxelMaterial GetMaterial(T X, T Y, T Z) const = delete; + + float GetInterpolatedValue(float X, float Y, float Z, FVoxelValue DefaultValue, float Tolerance = 0.0001f) const; + FVoxelMaterial GetInterpolatedMaterial(float X, float Y, float Z, float Tolerance = 0.0001f) const; + +public: + void Serialize(FArchive& Ar, uint32 ValueConfigFlag, uint32 MaterialConfigFlag, FVoxelDataAssetDataVersion::Type Version); + +public: + TNoGrowArray& GetRawValues() + { + return Values; + } + TNoGrowArray& GetRawMaterials() + { + return Materials; + } + const TNoGrowArray& GetRawValues() const + { + return Values; + } + const TNoGrowArray& GetRawMaterials() const + { + return Materials; + } + +public: + int64 GetAllocatedSize() const + { + return AllocatedSize; + } + +private: + // Not 0 to avoid crashes if empty + FIntVector Size = FIntVector(1, 1, 1); + TNoGrowArray Values = { FVoxelValue::Empty() }; + TNoGrowArray Materials = { FVoxelMaterial::Default() }; + mutable int64 AllocatedSize = 0; + + void UpdateStats() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.inl new file mode 100644 index 00000000..13c1ed12 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.inl @@ -0,0 +1,112 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssets/VoxelDataAssetData.h" + +FORCEINLINE FVoxelValue FVoxelDataAssetData::GetValue(int32 X, int32 Y, int32 Z, FVoxelValue DefaultValue) const +{ + if (IsValidIndex(X, Y, Z)) + { + return GetValueUnsafe(X, Y, Z); + } + else + { + return DefaultValue; + } +} + +FORCEINLINE FVoxelMaterial FVoxelDataAssetData::GetMaterial(int32 X, int32 Y, int32 Z) const +{ + if (IsValidIndex(X, Y, Z)) + { + return GetMaterialUnsafe(X, Y, Z); + } + else + { + return FVoxelMaterial::Default(); + } +} + +inline float FVoxelDataAssetData::GetInterpolatedValue(float X, float Y, float Z, FVoxelValue DefaultValue, float Tolerance) const +{ + const int32 RoundedX = FMath::RoundToInt(X); + const int32 RoundedY = FMath::RoundToInt(Y); + const int32 RoundedZ = FMath::RoundToInt(Z); + if (FMath::IsNearlyEqual(X, RoundedX, Tolerance) && + FMath::IsNearlyEqual(Y, RoundedY, Tolerance) && + FMath::IsNearlyEqual(Z, RoundedZ, Tolerance)) + { + return GetValue(RoundedX, RoundedY, RoundedZ, DefaultValue).ToFloat(); + } + + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + const int32 MinZ = FMath::FloorToInt(Z); + + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + const int32 MaxZ = FMath::CeilToInt(Z); + + const float AlphaX = X - MinX; + const float AlphaY = Y - MinY; + const float AlphaZ = Z - MinZ; + + return FVoxelUtilities::TrilinearInterpolation( + GetValue(MinX, MinY, MinZ, DefaultValue).ToFloat(), + GetValue(MaxX, MinY, MinZ, DefaultValue).ToFloat(), + GetValue(MinX, MaxY, MinZ, DefaultValue).ToFloat(), + GetValue(MaxX, MaxY, MinZ, DefaultValue).ToFloat(), + GetValue(MinX, MinY, MaxZ, DefaultValue).ToFloat(), + GetValue(MaxX, MinY, MaxZ, DefaultValue).ToFloat(), + GetValue(MinX, MaxY, MaxZ, DefaultValue).ToFloat(), + GetValue(MaxX, MaxY, MaxZ, DefaultValue).ToFloat(), + AlphaX, + AlphaY, + AlphaZ); +} + +inline FVoxelMaterial FVoxelDataAssetData::GetInterpolatedMaterial(float X, float Y, float Z, float Tolerance) const +{ + const int32 RoundedX = FMath::RoundToInt(X); + const int32 RoundedY = FMath::RoundToInt(Y); + const int32 RoundedZ = FMath::RoundToInt(Z); + if (FMath::IsNearlyEqual(X, RoundedX, Tolerance) && + FMath::IsNearlyEqual(Y, RoundedY, Tolerance) && + FMath::IsNearlyEqual(Z, RoundedZ, Tolerance)) + { + return GetMaterial(RoundedX, RoundedY, RoundedZ); + } + + // Note: might get better results by interpolating the material colors/UVs + + X = FMath::Clamp(X, 0, Size.X - 1); + Y = FMath::Clamp(Y, 0, Size.Y - 1); + Z = FMath::Clamp(Z, 0, Size.Z - 1); + + auto* RESTRICT const ValuesPtr = Values.GetData(); + auto* RESTRICT const MaterialsPtr = Materials.GetData(); + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + const int32 MinZ = FMath::FloorToInt(Z); + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + const int32 MaxZ = FMath::CeilToInt(Z); + for (int32 ItX = MinX; ItX <= MaxX; ItX++) + { + for (int32 ItY = MinY; ItY <= MaxY; ItY++) + { + for (int32 ItZ = MinZ; ItZ <= MaxZ; ItZ++) + { + checkVoxelSlow(IsValidIndex(ItX, ItY, ItZ)); + const int32 Index = GetIndex(ItX, ItY, ItZ); + checkVoxelSlow(Values.IsValidIndex(Index)); + checkVoxelSlow(Materials.IsValidIndex(Index)); + if (ValuesPtr[Index].IsEmpty()) continue; + return MaterialsPtr[Index]; + } + } + } + return MaterialsPtr[GetIndex(MinX, MinY, MinZ)]; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetInstance.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetInstance.h new file mode 100644 index 00000000..bbf7fc88 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelDataAssetInstance.h @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" + +class FVoxelDataAssetInstance : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + const TVoxelSharedRef Data; + const bool bSubtractiveAsset; + const FIntVector PositionOffset; + float Tolerance = 0.f; + +public: + FVoxelDataAssetInstance(UVoxelDataAsset& Asset) + : Super(&Asset) + , Data(Asset.GetData()) + , bSubtractiveAsset(Asset.bSubtractiveAsset) + , PositionOffset(Asset.PositionOffset) + , Tolerance(Asset.Tolerance) + { + } + + //~ Begin FVoxelGeneratorInstance Interface + virtual void Init(const FVoxelGeneratorInit& InitStruct) override + { + if (InitStruct.RenderType == EVoxelRenderType::Cubic) + { + Tolerance = FMath::Max(Tolerance, 0.1f); + } + } + + v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + X -= PositionOffset.X; + Y -= PositionOffset.Y; + Z -= PositionOffset.Z; + + return Data->GetInterpolatedValue(X, Y, Z, bSubtractiveAsset ? FVoxelValue::Full() : FVoxelValue::Empty(), Tolerance); + } + + FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + X -= PositionOffset.X; + Y -= PositionOffset.Y; + Z -= PositionOffset.Z; + + if (Data->HasMaterials()) + { + return Data->GetInterpolatedMaterial(X, Y, Z, Tolerance); + } + else + { + return FVoxelMaterial::Default(); + } + } + + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + if (Bounds.Intersect(GetLocalBounds())) + { + return { -1, 1 }; + } + else + { + return bSubtractiveAsset ? -1 : 1; + } + } + FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return FVector::UpVector; + } + //~ End FVoxelGeneratorInstance Interface + +private: + FORCEINLINE FVoxelIntBox GetLocalBounds() const + { + return FVoxelIntBox(PositionOffset, PositionOffset + Data->GetSize()); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAsset.h new file mode 100644 index 00000000..41abbaab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAsset.h @@ -0,0 +1,201 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "Engine/EngineTypes.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelHeightmapAsset.generated.h" + +class UTexture2D; + +template +struct TVoxelHeightmapAssetData; +template +class TVoxelHeightmapAssetInstance; + +UENUM() +enum class EVoxelHeightmapImporterMaterialConfig : uint8 +{ + RGB, + FourWayBlend, + FiveWayBlend, + SingleIndex, + MultiIndex +}; + +/** + * Asset that holds 2D information. + */ +UCLASS(Abstract, BlueprintType) +class VOXEL_API UVoxelHeightmapAsset : public UVoxelTransformableGeneratorWithBounds +{ + GENERATED_BODY() + +public: + // XY Scale of the heightmap + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Asset Settings", meta = (ClampMin = 0, DisplayName = "XY Scale")) + float Scale = 1; + + // Height multiplier + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Asset Settings", DisplayName = "Z Scale") + float HeightScale = 1; + + // In voxels, applied after Z Scale + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Asset Settings") + float HeightOffset = 0; + + // If false, will have meshes on the sides. If true, will extend infinitely. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Generator Settings") + bool bInfiniteExtent = false; + + // Additional thickness in voxels below the heightmap + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Generator Settings", meta = (EditCondition = "!bInfiniteExtent")) + float AdditionalThickness = 0; + + // Higher precision can improve render quality, but voxel values are lower (hardness not constant) + // Set this to the max delta height you can have between 2 adjacent pixels, in voxels + // Need to be increased if the shadows/normals aren't nice, and decreased if the edit speed isn't coherent + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heightmap Generator Settings", meta = (ClampMin = 1)) + float Precision = 4; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Heightmap Asset") + int32 GetWidth() const + { + return Width; + } + UFUNCTION(BlueprintCallable, Category = "Voxel|Heightmap Asset") + int32 GetHeight() const + { + return Height; + } + +protected: + template + void TryLoad(TVoxelHeightmapAssetData& Data); + + template + void SaveData(const TVoxelHeightmapAssetData& Data); + + template + void LoadData(TVoxelHeightmapAssetData& Data); + + template + void SyncProperties(const TVoxelHeightmapAssetData& Data); + + template + FVoxelIntBox GetBoundsImpl() const; + +protected: + virtual void Serialize(FArchive& Ar) override; + +private: + UPROPERTY(VisibleAnywhere, Category = "Heightmap Info") + int32 Width; + UPROPERTY(VisibleAnywhere, Category = "Heightmap Info") + int32 Height; + + UPROPERTY() + int32 VoxelCustomVersion; + + UPROPERTY() + uint32 MaterialConfigFlag; + + TArray CompressedData; + +private: +#if WITH_EDITORONLY_DATA + UPROPERTY(NonTransactional) + TArray ThumbnailSave; + + UPROPERTY(Transient) + UTexture2D* ThumbnailTexture; +#endif + +#if WITH_EDITOR +protected: + template + UTexture2D* GetThumbnailInternal(); + +public: + UTexture2D* GetThumbnail(); +#endif +}; + +UCLASS(HideDropdown) +class VOXEL_API UVoxelHeightmapAssetFloat : public UVoxelHeightmapAsset +{ + GENERATED_BODY() + +public: + UVoxelHeightmapAssetFloat(); + + TVoxelHeightmapAssetData& GetData(); + TVoxelSharedRef> GetDataSharedPtr(); + void Save(); + + TVoxelSharedRef> GetInstanceImpl(); + + //~ Begin UVoxelGenerator Interface + virtual TVoxelSharedRef GetInstance() override; + virtual TVoxelSharedRef GetTransformableInstance() override; + virtual FVoxelIntBox GetBounds() const override; + //~ End UVoxelGenerator Interface + +private: + TVoxelSharedPtr> Data; +}; + +USTRUCT() +struct FVoxelHeightmapImporterWeightmapInfos +{ + GENERATED_BODY() + + // The weightmap + UPROPERTY(EditAnywhere, Category = "Voxel") + FFilePath File; + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelRGBA Layer = EVoxelRGBA::R; + + UPROPERTY(EditAnywhere, Category = "Voxel") + uint8 Index = 0; +}; + +UCLASS(HideDropdown) +class VOXEL_API UVoxelHeightmapAssetUINT16 : public UVoxelHeightmapAsset +{ + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, Category = "Import configuration") + FString Heightmap; + + UPROPERTY(VisibleAnywhere, Category = "Import configuration") + EVoxelHeightmapImporterMaterialConfig MaterialConfig; + + UPROPERTY(VisibleAnywhere, Category = "Import configuration") + TArray Weightmaps; + + UPROPERTY() + TArray WeightmapsInfos; + +public: + UVoxelHeightmapAssetUINT16(); + + TVoxelHeightmapAssetData& GetData(); + TVoxelSharedRef> GetDataSharedPtr(); + void Save(); + + TVoxelSharedRef> GetInstanceImpl(); + + //~ Begin UVoxelGenerator Interface + virtual TVoxelSharedRef GetInstance() override; + virtual TVoxelSharedRef GetTransformableInstance() override; + virtual FVoxelIntBox GetBounds() const override; + //~ End UVoxelGenerator Interface + +private: + TVoxelSharedPtr> Data; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.h new file mode 100644 index 00000000..a019226f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.h @@ -0,0 +1,225 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterial.h" +#include "VoxelRange.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Heightmap Assets Memory"), STAT_VoxelHeightmapAssetMemory, STATGROUP_VoxelMemory, VOXEL_API); + +namespace FVoxelHeightmapAssetDataVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + SHARED_RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + SHARED_ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + NoVoxelMaterialInHeightmapAssets, + FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + UseTArray64, + SerializeHeightRangeMips, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +}; + +template +struct TVoxelHeightmapAssetData +{ +public: + TVoxelHeightmapAssetData() + { + ClearData(); + } + ~TVoxelHeightmapAssetData() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHeightmapAssetMemory, AllocatedSize); + } + +public: + int64 GetWidth() const + { + return Width; + } + int64 GetHeight() const + { + return Height; + } + inline T GetMinHeight() const + { + return MinHeight; + } + inline T GetMaxHeight() const + { + return MaxHeight; + } + +public: + void SetSize(int64 NewWidth, int64 NewHeight, bool bCreateMaterials, EVoxelMaterialConfig InMaterialConfig); + void SetAllHeightsTo(T NewHeight); + + void ClearData() + { + // Make it safe to sample an empty asset + SetSize(2, 2, false, {}); + SetAllHeightsTo(0); + } + +public: + bool HasMaterials() const + { + return Materials.Num() > 0; + } + bool IsEmpty() const + { + return Heights.Num() <= 4 && Materials.Num() == 0; + } + int64 GetAllocatedSize() const + { + return AllocatedSize; + } + +public: + int64 GetNumHeightRangeMips() const + { + return HeightRangeMips.Num(); + } + + const TVoxelRange& GetHeightRange(int64 X, int64 Y, int64 Mip, EVoxelSamplerMode SamplerMode) const; + // Max: excluded + TVoxelRange GetHeightRange(TVoxelRange X, TVoxelRange Y, EVoxelSamplerMode SamplerMode) const; + +private: + void GetHeightRangeLocalCoordinates(int64 Mip, int64 X, int64 Y, int64& LocalX, int64& LocalY) const; + + FORCEINLINE TVoxelRange& GetHeightRangeLocal(int64 Mip, int64 LocalX, int64 LocalY) + { + return HeightRangeMips[Mip].Get(LocalX, LocalY); + } + FORCEINLINE const TVoxelRange& GetHeightRangeLocal(int64 Mip, int64 LocalX, int64 LocalY) const + { + return HeightRangeMips[Mip].Get(LocalX, LocalY); + } + + void FixHeightRangeLocalCoordinates(int64 Mip, int64& LocalX, int64& LocalY, EVoxelSamplerMode SamplerMode) const; + + void InitializeHeightRangeMips(); + +public: + FORCEINLINE bool IsValidIndex(int64 X, int64 Y) const + { + return (0 <= X && X < Width) && (0 <= Y && Y < Height); + } + FORCEINLINE int64 GetIndex(int64 X, int64 Y) const + { + checkVoxelSlow(IsValidIndex(X, Y)); + return X + Width * Y; + } + + void SetHeight(int64 X, int64 Y, T NewHeight); + + void SetMaterial_RGB(int64 X, int64 Y, FColor Color); + void SetMaterial_SingleIndex(int64 X, int64 Y, uint8 SingleIndex); + void SetMaterial_MultiIndex(int64 X, int64 Y, const FVoxelMaterial& Material); + + FORCEINLINE T GetHeightUnsafe(int64 X, int64 Y) const + { + return GetHeightUnsafe(GetIndex(X, Y)); + } + FORCEINLINE FVoxelMaterial GetMaterialUnsafe(int64 X, int64 Y) const + { + return GetMaterialUnsafe(GetIndex(X, Y)); + } + FORCEINLINE T GetHeightUnsafe(int64 Index) const + { + return Heights[Index]; + } + FVoxelMaterial GetMaterialUnsafe(int64 Index) const; + +public: + void TileCoordinates(int64& X, int64& Y) const; + void ClampCoordinates(int64& X, int64& Y) const; + + T GetHeight(int64 X, int64 Y, EVoxelSamplerMode Mode) const; + FVoxelMaterial GetMaterial(int64 X, int64 Y, EVoxelSamplerMode Mode) const; + + T GetHeight(int32 X, int32 Y, EVoxelSamplerMode Mode) const + { + return GetHeight(int64(X), int64(Y), Mode); + } + FVoxelMaterial GetMaterial(int32 X, int32 Y, EVoxelSamplerMode Mode) const + { + return GetMaterial(int64(X), int64(Y), Mode); + } + + float GetHeight(float X, float Y, EVoxelSamplerMode Mode) const; + FVoxelMaterial GetMaterial(float X, float Y, EVoxelSamplerMode Mode) const; + +public: + const auto& GetRawHeights() const + { + return Heights; + } + +public: + void Serialize(FArchive& Ar, uint32 MaterialConfigFlag, FVoxelHeightmapAssetDataVersion::Type Version, bool& bNeedToSave); + +private: + TNoGrowArray64 Heights; + TNoGrowArray64 Materials; + + // In theory these fit in int32, but it's safer to use 64 bit math everywhere + int64 Width = -1; + int64 Height = -1; + + T MinHeight = 0; + T MaxHeight = 0; + + EVoxelMaterialConfig MaterialConfig{}; + + struct FHeightRangeMip + { + int64 Width = -1; + int64 Height = -1; + + TArray> Data; + + TVoxelRange& Get(int64 X, int64 Y) + { + checkVoxelSlow(0 <= X && X < Width && 0 <= Y && Y < Height); + return Data[X + Width * Y]; + } + const TVoxelRange& Get(int64 X, int64 Y) const + { + checkVoxelSlow(0 <= X && X < Width && 0 <= Y && Y < Height); + return Data[X + Width * Y]; + } + + friend FArchive& operator<<(FArchive& Ar, FHeightRangeMip& Mip) + { + Ar << Mip.Width; + Ar << Mip.Height; + Ar << Mip.Data; + return Ar; + } + }; + TArray> HeightRangeMips; + +private: + int64 AllocatedSize = 0; + + void UpdateStats(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.inl new file mode 100644 index 00000000..200f7b35 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.inl @@ -0,0 +1,616 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMessages.h" +#include "VoxelFeedbackContext.h" +#include "VoxelAssets/VoxelHeightmapAssetData.h" +#include "VoxelUtilities/VoxelSerializationUtilities.h" + +template +void TVoxelHeightmapAssetData::SetSize(int64 NewWidth, int64 NewHeight, bool bCreateMaterials, EVoxelMaterialConfig InMaterialConfig) +{ + VOXEL_FUNCTION_COUNTER(); + + check(NewWidth > 0 && NewHeight > 0); + + const int64 NumHeights = NewWidth * NewHeight; + Heights.Empty(NumHeights); + Heights.SetNumUninitialized(NumHeights); + + if (bCreateMaterials) + { + int64 NumMaterials = NewWidth * NewHeight; + switch (InMaterialConfig) + { + case EVoxelMaterialConfig::RGB: NumMaterials *= 4; break; + case EVoxelMaterialConfig::SingleIndex: NumMaterials *= 1; break; + case EVoxelMaterialConfig::MultiIndex: NumMaterials *= 7; break; + default: ensure(false); + } + + Materials.Empty(NumMaterials); + Materials.SetNumUninitialized(NumMaterials); + } + else + { + Materials.Empty(); + } + + Width = NewWidth; + Height = NewHeight; + + MinHeight = PositiveInfinity(); + MaxHeight = NegativeInfinity(); + + MaterialConfig = InMaterialConfig; + + UpdateStats(); + InitializeHeightRangeMips(); +} + +template +void TVoxelHeightmapAssetData::SetAllHeightsTo(T NewHeight) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (auto& HeightIt : Heights) + { + HeightIt = NewHeight; + } + for (auto& HeightRangeMip : HeightRangeMips) + { + for (auto& HeightRange : HeightRangeMip.Data) + { + HeightRange = NewHeight; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +const TVoxelRange& TVoxelHeightmapAssetData::GetHeightRange(int64 X, int64 Y, int64 Mip, EVoxelSamplerMode SamplerMode) const +{ + int64 LocalX; + int64 LocalY; + GetHeightRangeLocalCoordinates(Mip, X, Y, LocalX, LocalY); + + FixHeightRangeLocalCoordinates(Mip, LocalX, LocalY, SamplerMode); + + return GetHeightRangeLocal(Mip, LocalX, LocalY); +} + +template +TVoxelRange TVoxelHeightmapAssetData::GetHeightRange(TVoxelRange X, TVoxelRange Y, EVoxelSamplerMode SamplerMode) const +{ + if (!ensure(X.Min < X.Max && Y.Min < Y.Max) || !ensure(GetNumHeightRangeMips() > 0)) + { + return { MinHeight, MaxHeight }; + } + + int64 MinX = X.Min; + int64 MaxX = X.Max; + + int64 MinY = Y.Min; + int64 MaxY = Y.Max; + + if (SamplerMode == EVoxelSamplerMode::Clamp) + { + // Clamp min and max + + MinX = FMath::Clamp(MinX, 0, GetWidth() - 1); + MinY = FMath::Clamp(MinY, 0, GetHeight() - 1); + + MaxX = FMath::Clamp(MaxX, 1, GetWidth()); + MaxY = FMath::Clamp(MaxY, 1, GetHeight()); + } + else + { + // Tile min, and set max to be max one tile after + // The coordinates will be tiled again when sampling the mips + + int64 SizeX = MaxX - MinX; + int64 SizeY = MaxY - MinY; + + TileCoordinates(MinX, MinY); + + // Also tile Size to make sure it's under the heightmap size + TileCoordinates(SizeX, SizeY); + ensureVoxelSlow(SizeX < GetWidth()); + ensureVoxelSlow(SizeY < GetHeight()); + + // If the size is a multiple or ours, fixup + if (SizeX == 0) SizeX = GetWidth(); + if (SizeY == 0) SizeY = GetHeight(); + + MaxX = MinX + SizeX; + MaxY = MinY + SizeY; + + ensureVoxelSlowNoSideEffects(MaxX - MinX <= GetWidth()); + ensureVoxelSlowNoSideEffects(MaxY - MinY <= GetHeight()); + } + + const int64 SizeX = MaxX - MinX; + const int64 SizeY = MaxY - MinY; + + const int64 MaxSize = FMath::Max(SizeX, SizeY); + + int64 Mip = FVoxelUtilities::GetDepthFromSize(MaxSize); + Mip = FMath::Clamp(Mip, 0, GetNumHeightRangeMips() - 1); + + const int64 MipPixelSize = RENDER_CHUNK_SIZE << Mip; + + const int64 LocalMinX = FVoxelUtilities::DivideFloor(MinX, MipPixelSize); + const int64 LocalMinY = FVoxelUtilities::DivideFloor(MinY, MipPixelSize); + + // Note: since MaxX/Y are excluded, LocalMaxX/Y are too + const int64 LocalMaxX = FVoxelUtilities::DivideCeil(MaxX, MipPixelSize); + const int64 LocalMaxY = FVoxelUtilities::DivideCeil(MaxY, MipPixelSize); + + const int64 LocalSizeX = LocalMaxX - LocalMinX; + const int64 LocalSizeY = LocalMaxY - LocalMinY; + + checkVoxelSlow(0 < LocalSizeX); + checkVoxelSlow(0 < LocalSizeY); + + ensureVoxelSlow(LocalSizeX <= 2); + ensureVoxelSlow(LocalSizeY <= 2); + + // Combine the range of all the overlapping pixels + TOptional> Range; + for (int64 LocalX = LocalMinX; LocalX < LocalMaxX; LocalX++) + { + for (int64 LocalY = LocalMinY; LocalY < LocalMaxY; LocalY++) + { + int64 FixedLocalX = LocalX; + int64 FixedLocalY = LocalY; + + // Tile the coordinates if needed + FixHeightRangeLocalCoordinates(Mip, FixedLocalX, FixedLocalY, SamplerMode); + ensureVoxelSlow(SamplerMode == EVoxelSamplerMode::Tile || (LocalX == FixedLocalX && LocalY == FixedLocalY)); + + const auto LocalRange = GetHeightRangeLocal(Mip, FixedLocalX, FixedLocalY); + + Range = Range.IsSet() ? TVoxelRange::Union(Range.GetValue(), LocalRange) : LocalRange; + } + } + + // Crashed + ensureMsgfVoxelSlowNoSideEffects( + Range.IsSet(), + TEXT("LocalMinX: %lld; LocalMaxX: %lld; LocalMinY: %lld; LocalMaxY: %lld; Width: %lld; Height: %lld; MinX: %lld; MaxX: %lld; MinY: %lld; MaxY: %lld"), + LocalMinX, LocalMaxX, LocalMinY, LocalMaxY, Width, Height, MinX, MaxX, MinY, MaxY); + return Range.Get({ MinHeight, MaxHeight }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE void TVoxelHeightmapAssetData::GetHeightRangeLocalCoordinates(int64 Mip, int64 X, int64 Y, int64& LocalX, int64& LocalY) const +{ + checkVoxelSlow(IsValidIndex(X, Y)); + checkVoxelSlow(HeightRangeMips.IsValidIndex(Mip)); + + constexpr int64 BaseMipDepth = FVoxelUtilities::IntLog2(RENDER_CHUNK_SIZE); + const int64 MipDepth = BaseMipDepth + Mip; + + // Find the mip coordinates by dividing and flooring + LocalX = X >> MipDepth; + LocalY = Y >> MipDepth; +} + +template +void TVoxelHeightmapAssetData::FixHeightRangeLocalCoordinates(int64 Mip, int64& LocalX, int64& LocalY, EVoxelSamplerMode SamplerMode) const +{ + auto& HeightRangeMip = HeightRangeMips[Mip]; + + if (SamplerMode == EVoxelSamplerMode::Clamp) + { + LocalX = FMath::Clamp(LocalX, 0, HeightRangeMip.Width - 1); + LocalY = FMath::Clamp(LocalY, 0, HeightRangeMip.Height - 1); + } + else + { + LocalX = FVoxelUtilities::PositiveMod(LocalX, HeightRangeMip.Width); + LocalY = FVoxelUtilities::PositiveMod(LocalY, HeightRangeMip.Height); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void TVoxelHeightmapAssetData::InitializeHeightRangeMips() +{ + const int64 NumHeightRangeMips = 1 + FVoxelUtilities::GetDepthFromSize(FMath::Max(Width, Height)); + check(NumHeightRangeMips >= 1); + + HeightRangeMips.Empty(); + for (int64 MipIndex = 0; MipIndex < NumHeightRangeMips; MipIndex++) + { + const int64 MipPixelSize = RENDER_CHUNK_SIZE << MipIndex; + + TVoxelRange Range; + Range.Min = PositiveInfinity(); + Range.Max = NegativeInfinity(); + + FHeightRangeMip NewMip; + NewMip.Width = FVoxelUtilities::DivideCeil(Width, MipPixelSize); + NewMip.Height = FVoxelUtilities::DivideCeil(Height, MipPixelSize); + NewMip.Data.Init(Range, NewMip.Width * NewMip.Height); + + HeightRangeMips.Add(NewMip); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void TVoxelHeightmapAssetData::SetHeight(int64 X, int64 Y, T NewHeight) +{ + Heights[GetIndex(X, Y)] = NewHeight; + + MaxHeight = FMath::Max(MaxHeight, NewHeight); + MinHeight = FMath::Min(MinHeight, NewHeight); + + for (int64 Mip = 0; Mip < GetNumHeightRangeMips(); Mip++) + { + int64 LocalX; + int64 LocalY; + GetHeightRangeLocalCoordinates(Mip, X, Y, LocalX, LocalY); + + auto& Range = GetHeightRangeLocal(Mip, LocalX, LocalY); + Range.Min = FMath::Min(Range.Min, NewHeight); + Range.Max = FMath::Max(Range.Max, NewHeight); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void TVoxelHeightmapAssetData::SetMaterial_RGB(int64 X, int64 Y, FColor Color) +{ + checkVoxelSlow(MaterialConfig == EVoxelMaterialConfig::RGB); + const int64 Index = GetIndex(X, Y); + + Materials[4 * Index + 0] = Color.R; + Materials[4 * Index + 1] = Color.G; + Materials[4 * Index + 2] = Color.B; + Materials[4 * Index + 3] = Color.A; +} + +template +void TVoxelHeightmapAssetData::SetMaterial_SingleIndex(int64 X, int64 Y, uint8 SingleIndex) +{ + checkVoxelSlow(MaterialConfig == EVoxelMaterialConfig::SingleIndex); + Materials[GetIndex(X, Y)] = SingleIndex; +} + +template +void TVoxelHeightmapAssetData::SetMaterial_MultiIndex(int64 X, int64 Y, const FVoxelMaterial& Material) +{ + checkVoxelSlow(MaterialConfig == EVoxelMaterialConfig::MultiIndex); + const int64 Index = GetIndex(X, Y); + + Materials[7 * Index + 0] = Material.GetMultiIndex_Blend0(); + Materials[7 * Index + 1] = Material.GetMultiIndex_Blend1(); + Materials[7 * Index + 2] = Material.GetMultiIndex_Blend2(); + Materials[7 * Index + 3] = Material.GetMultiIndex_Index0(); + Materials[7 * Index + 4] = Material.GetMultiIndex_Index1(); + Materials[7 * Index + 5] = Material.GetMultiIndex_Index2(); + Materials[7 * Index + 6] = Material.GetMultiIndex_Index3(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE FVoxelMaterial TVoxelHeightmapAssetData::GetMaterialUnsafe(int64 Index) const +{ + FVoxelMaterial Material(ForceInit); + switch (MaterialConfig) + { + case EVoxelMaterialConfig::RGB: + Material.SetR(Materials[4 * Index + 0]); + Material.SetG(Materials[4 * Index + 1]); + Material.SetB(Materials[4 * Index + 2]); + Material.SetA(Materials[4 * Index + 3]); + break; + case EVoxelMaterialConfig::SingleIndex: + Material.SetSingleIndex(Materials[Index]); + break; + case EVoxelMaterialConfig::MultiIndex: + default: + Material.SetMultiIndex_Blend0(Materials[7 * Index + 0]); + Material.SetMultiIndex_Blend1(Materials[7 * Index + 1]); + Material.SetMultiIndex_Blend2(Materials[7 * Index + 2]); + Material.SetMultiIndex_Index0(Materials[7 * Index + 3]); + Material.SetMultiIndex_Index1(Materials[7 * Index + 4]); + Material.SetMultiIndex_Index2(Materials[7 * Index + 5]); + Material.SetMultiIndex_Index3(Materials[7 * Index + 6]); + break; + } + return Material; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE void TVoxelHeightmapAssetData::ClampCoordinates(int64& X, int64& Y) const +{ + X = FMath::Clamp(X, 0, GetWidth() - 1); + Y = FMath::Clamp(Y, 0, GetHeight() - 1); +} + +template +FORCEINLINE void TVoxelHeightmapAssetData::TileCoordinates(int64& X, int64& Y) const +{ + X = FVoxelUtilities::PositiveMod(X, GetWidth()); + Y = FVoxelUtilities::PositiveMod(Y, GetHeight()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +T TVoxelHeightmapAssetData::GetHeight(int64 X, int64 Y, EVoxelSamplerMode Mode) const +{ + if (!IsValidIndex(X, Y)) + { + if (Mode == EVoxelSamplerMode::Tile) + { + TileCoordinates(X, Y); + } + else + { + ClampCoordinates(X, Y); + } + checkVoxelSlow(IsValidIndex(X, Y)); + } + return GetHeightUnsafe(X, Y); +} + +template +FVoxelMaterial TVoxelHeightmapAssetData::GetMaterial(int64 X, int64 Y, EVoxelSamplerMode Mode) const +{ + if (!IsValidIndex(X, Y)) + { + if (Mode == EVoxelSamplerMode::Tile) + { + TileCoordinates(X, Y); + } + else + { + ClampCoordinates(X, Y); + } + checkVoxelSlow(IsValidIndex(X, Y)); + } + return GetMaterialUnsafe(X, Y); +} + +template +float TVoxelHeightmapAssetData::GetHeight(float X, float Y, EVoxelSamplerMode Mode) const +{ + const int64 MinX = FMath::FloorToInt(X); + const int64 MinY = FMath::FloorToInt(Y); + + const int64 MaxX = FMath::CeilToInt(X); + const int64 MaxY = FMath::CeilToInt(Y); + + const float AlphaX = X - MinX; + const float AlphaY = Y - MinY; + + return FVoxelUtilities::BilinearInterpolation( + GetHeight(MinX, MinY, Mode), + GetHeight(MaxX, MinY, Mode), + GetHeight(MinX, MaxY, Mode), + GetHeight(MaxX, MaxY, Mode), + AlphaX, + AlphaY); +} + +template +FVoxelMaterial TVoxelHeightmapAssetData::GetMaterial(float X, float Y, EVoxelSamplerMode Mode) const +{ + if (HasMaterials()) + { + return GetMaterial(FMath::RoundToInt(X), FMath::RoundToInt(Y), Mode); + } + else + { + return FVoxelMaterial::Default(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void TVoxelHeightmapAssetData::Serialize(FArchive& Ar, uint32 MaterialConfigFlag, FVoxelHeightmapAssetDataVersion::Type Version, bool& bNeedToSave) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelScopedSlowTask Serializing(3.f); + + Serializing.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing heights")); + if (Version < FVoxelHeightmapAssetDataVersion::UseTArray64) + { + TArray OldHeights; + if (Version == FVoxelHeightmapAssetDataVersion::BeforeCustomVersionWasAdded) + { + Ar << OldHeights; + } + else + { + OldHeights.BulkSerialize(Ar); + } + Heights = OldHeights; + } + else + { + Heights.BulkSerialize(Ar); + } + + Serializing.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Serializing materials")); + if (Version < FVoxelHeightmapAssetDataVersion::NoVoxelMaterialInHeightmapAssets) + { + TNoGrowArray LegacyMaterials; + // Note: don't do the cast for newer versions + FVoxelSerializationUtilities::SerializeMaterials(Ar, LegacyMaterials, MaterialConfigFlag, FVoxelSerializationVersion::Type(Version)); + + // Assume RGB + Materials.Reserve(LegacyMaterials.Num() * 4); + for (auto& Material : LegacyMaterials) + { + const FColor Color = Material.GetColor(); + Materials.Add(Color.R); + Materials.Add(Color.G); + Materials.Add(Color.B); + Materials.Add(Color.A); + } + } + else if (Version < FVoxelHeightmapAssetDataVersion::FixMissingMaterialsInHeightmapAssets) + { + // Do nothing + } + else if (Version < FVoxelHeightmapAssetDataVersion::UseTArray64) + { + TArray OldMaterials; + OldMaterials.BulkSerialize(Ar); + Materials = OldMaterials; + } + else + { + Materials.BulkSerialize(Ar); + } + + if (Version < FVoxelHeightmapAssetDataVersion::UseTArray64) + { + int32 Width32; + int32 Height32; + Ar << Width32; + Ar << Height32; + Width = Width32; + Height = Height32; + } + else + { + Ar << Width; + Ar << Height; + } + + Ar << MaxHeight; + Ar << MinHeight; + + if (Version >= FVoxelHeightmapAssetDataVersion::NoVoxelMaterialInHeightmapAssets) + { + Ar << MaterialConfig; + } + + if (Width * Height != Heights.Num()) + { + Ar.SetError(); + } + + if (Materials.Num() > 0) + { + int64 MaterialSize = 0; + switch (MaterialConfig) + { + case EVoxelMaterialConfig::RGB: + MaterialSize = 4; + break; + case EVoxelMaterialConfig::SingleIndex: + MaterialSize = 1; + break; + case EVoxelMaterialConfig::DoubleIndex_DEPRECATED: + MaterialSize = 0; + MaterialConfig = EVoxelMaterialConfig::RGB; + Materials.Empty(); + FVoxelMessages::Error("Cannot load double index heightmap materials, removing them. You'll need to reimport your weightmaps"); + break; + case EVoxelMaterialConfig::MultiIndex: + MaterialSize = 7; + break; + default: + Ar.SetError(); + } + + if (MaterialSize * Width * Height != Materials.Num()) + { + Ar.SetError(); + } + } + + Serializing.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Recomputing height range mips")); + if (Version < FVoxelHeightmapAssetDataVersion::SerializeHeightRangeMips) + { + VOXEL_SCOPE_COUNTER("Recomputing height range mips"); + + const double StartTime = FPlatformTime::Seconds(); + + InitializeHeightRangeMips(); + for (int64 X = 0; X < Width; X++) + { + for (int64 Y = 0; Y < Height; Y++) + { + const T LocalHeight = GetHeightUnsafe(X, Y); + for (int64 Mip = 0; Mip < GetNumHeightRangeMips(); Mip++) + { + int64 LocalX; + int64 LocalY; + GetHeightRangeLocalCoordinates(Mip, X, Y, LocalX, LocalY); + + auto& Range = GetHeightRangeLocal(Mip, LocalX, LocalY); + Range.Min = FMath::Min(Range.Min, LocalHeight); + Range.Max = FMath::Max(Range.Max, LocalHeight); + } + } + } + + const double EndTime = FPlatformTime::Seconds(); + + bNeedToSave = true; + + int64 Size = HeightRangeMips.GetAllocatedSize(); + for (auto& Mip : HeightRangeMips) + { + Size += Mip.Data.GetAllocatedSize(); + } + LOG_VOXEL(Log, TEXT("Recomputing height range mips took %fs. Using %fMB for %lldx%lld"), EndTime - StartTime, Size / double(1 << 20), Width, Height); + } + else + { + Ar << HeightRangeMips; + } + + UpdateStats(); +} + +template +void TVoxelHeightmapAssetData::UpdateStats() +{ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHeightmapAssetMemory, AllocatedSize); + AllocatedSize = Heights.GetAllocatedSize() + Materials.GetAllocatedSize() + HeightRangeMips.GetAllocatedSize(); + for (auto& Mip : HeightRangeMips) + { + AllocatedSize += Mip.Data.GetAllocatedSize(); + } + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelHeightmapAssetMemory, AllocatedSize); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h new file mode 100644 index 00000000..0336941b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h @@ -0,0 +1,125 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" + +template +struct TVoxelHeightmapAssetSelector; + +template<> +struct TVoxelHeightmapAssetSelector +{ + using Type = UVoxelHeightmapAssetFloat; +}; + +template<> +struct TVoxelHeightmapAssetSelector +{ + using Type = UVoxelHeightmapAssetUINT16; +}; + +template +class TVoxelHeightmapAssetInstance : public TVoxelGeneratorInstanceHelper, typename TVoxelHeightmapAssetSelector::Type> +{ +public: + using Super = TVoxelGeneratorInstanceHelper, typename TVoxelHeightmapAssetSelector::Type>; + + const TVoxelHeightmapAssetSamplerWrapper Wrapper; + const float Precision; + const bool bInfiniteExtent; + const FVoxelIntBox WorldBounds; + +public: + explicit TVoxelHeightmapAssetInstance(typename TVoxelHeightmapAssetSelector::Type& Asset) + : Super(&Asset) + , Wrapper(&Asset) + , Precision(Asset.Precision) + , bInfiniteExtent(Asset.bInfiniteExtent) + , WorldBounds(Asset.GetBounds()) + { + } + + //~ Begin FVoxelGeneratorInstance Interface + FORCEINLINE v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + if (bInfiniteExtent || WorldBounds.ContainsFloat(X, Y, Z)) // Note: it's safe to access outside the bounds + { + const float Height = Wrapper.GetHeight(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp); + return (Z - Height) / Precision; + } + else + { + // Outside asset bounds + return 1; + } + } + FORCEINLINE FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return Wrapper.GetMaterial(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp); + } + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + if (!bInfiniteExtent && !Bounds.Intersect(WorldBounds)) + { + return 1; + } + + const bool bEntirelyContained = WorldBounds.Contains(Bounds); + + const auto XRange = TVoxelRange(Bounds.Min.X, Bounds.Max.X) + Wrapper.GetWidth() / 2; + const auto YRange = TVoxelRange(Bounds.Min.Y, Bounds.Max.Y) + Wrapper.GetHeight() / 2; + const auto ZRange = TVoxelRange(Bounds.Min.Z, Bounds.Max.Z); + + auto HeightRange = TVoxelRange(Wrapper.GetHeightRange(XRange, YRange, EVoxelSamplerMode::Clamp)); + + if (!bEntirelyContained && bInfiniteExtent) + { + // Height will be 0 outside the boundaries if bInfiniteExtent = true + HeightRange = TVoxelRange::Union(HeightRange, 0.f); + } + + auto Range = (ZRange - HeightRange) / Precision; + + if (!bEntirelyContained && !bInfiniteExtent) + { + // Intersects with the boundary + Range = TVoxelRange::Union(1.f, Range); + } + + return Range; + } + virtual void GetValues(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + const float Height = Wrapper.GetHeight(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp); + + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + FVoxelValue Value; + if (bInfiniteExtent || WorldBounds.Contains(X, Y, Z)) + { + Value = FVoxelValue((Z - Height) / Precision); + } + else + { + // Outside asset bounds + Value = FVoxelValue::Empty(); + } + QueryZone.Set(X, Y, Z, Value); + } + } + } + } + virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return FVector::UpVector; + } + //~ End FVoxelGeneratorInstance Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h new file mode 100644 index 00000000..0587a5d1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssets/VoxelHeightmapAssetData.h" +#include "VoxelAssets/VoxelHeightmapAssetData.inl" + +template +struct TVoxelHeightmapAssetSamplerWrapper +{ + const float Scale; + const float HeightScale; + const float HeightOffset; + const TVoxelSharedRef> Data; + + explicit VOXEL_API TVoxelHeightmapAssetSamplerWrapper(UVoxelHeightmapAsset* Asset); + + float GetHeight(v_flt X, v_flt Y, EVoxelSamplerMode SamplerMode) const + { + return HeightOffset + HeightScale * Data->GetHeight(float(X / Scale), float(Y / Scale), SamplerMode); + } + FVoxelMaterial GetMaterial(v_flt X, v_flt Y, EVoxelSamplerMode SamplerMode) const + { + return Data->GetMaterial(float(X / Scale), float(Y / Scale), SamplerMode); + } + + TVoxelRange GetHeightRange(TVoxelRange X, TVoxelRange Y, EVoxelSamplerMode SamplerMode) const + { + return HeightOffset + HeightScale * TVoxelRange(Data->GetHeightRange( + { FMath::FloorToInt(X.Min / Scale), FMath::CeilToInt(X.Max / Scale) }, + { FMath::FloorToInt(Y.Min / Scale), FMath::CeilToInt(Y.Max / Scale) }, + SamplerMode)); + } + + void SetHeight(int32 X, int32 Y, float Height) + { + ensureVoxelSlowNoSideEffects(Scale == 1.f); + Height -= HeightOffset; + Height /= HeightScale; + Height = FMath::Clamp(Height, TNumericLimits::Lowest(), TNumericLimits::Max()); + if (TIsSame::Value) + { + Data->SetHeight(X, Y, Height); + } + else + { + Data->SetHeight(X, Y, FMath::RoundToInt(Height)); + } + } + + float GetMinHeight() const + { + return HeightOffset + HeightScale * Data->GetMinHeight(); + } + float GetMaxHeight() const + { + return HeightOffset + HeightScale * Data->GetMaxHeight(); + } + + float GetWidth() const + { + return Scale * Data->GetWidth(); + } + float GetHeight() const + { + return Scale * Data->GetHeight(); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAsyncWork.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAsyncWork.h new file mode 100644 index 00000000..22a99254 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelAsyncWork.h @@ -0,0 +1,116 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelQueuedWork.h" + +class VOXEL_API FVoxelAsyncWork : public IVoxelQueuedWork +{ +public: + FORCEINLINE FVoxelAsyncWork(FName Name, double PriorityDuration, bool bAutoDelete = false) + : IVoxelQueuedWork(Name, PriorityDuration) + , bAutodelete(bAutoDelete) + { + } + + //~ Begin IVoxelQueuedWork Interface + virtual void DoThreadedWork() override final; + virtual void Abandon() override final; + //~ End IVoxelQueuedWork Interface + + //~ Begin FVoxelAsyncWork Interface + virtual void DoWork() = 0; + virtual void PostDoWork() {} // Will be called when IsDone is true + //~ End FVoxelAsyncWork Interface + + // @return: IsDone and PostDoWork was called + bool CancelAndAutodelete(); + + bool IsDone() const + { + return IsDoneCounter.GetValue() > 0; + } + bool WasAbandoned() const + { + return WasAbandonedCounter.GetValue() > 0; + } + +protected: + // Important: do not allow public delete + ~FVoxelAsyncWork() override; + + bool IsCanceled() const + { + return CanceledCounter.GetValue() > 0; + } + void SetIsDone(bool bIsDone) + { + check(!bAutodelete); + IsDoneCounter.Set(bIsDone ? 1 : 0); + } + + void WaitForDoThreadedWorkToExit(); + +private: + struct FSafeCriticalSection + { + FCriticalSection Section; + FThreadSafeCounter IsLocked; + + FORCEINLINE void Lock() + { + Section.Lock(); + ensure(IsLocked.Set(1) == 0); + } + FORCEINLINE void Unlock() + { + ensure(IsLocked.Set(0) == 1); + Section.Unlock(); + } + }; + + FThreadSafeCounter IsDoneCounter; + FSafeCriticalSection DoneSection; + + FThreadSafeCounter CanceledCounter; + bool bAutodelete = false; + + FThreadSafeCounter WasAbandonedCounter; +}; + +template +struct TVoxelAsyncWorkDelete +{ + void operator()(T* Ptr) const + { + if (Ptr) + { + Ptr->WaitForDoThreadedWorkToExit(); + } + delete Ptr; + } +}; + +class VOXEL_API FVoxelAsyncWorkWithWait : public FVoxelAsyncWork +{ +public: + FVoxelAsyncWorkWithWait(FName Name, double PriorityDuration, bool bAutoDelete = false); + + //~ Begin IVoxelQueuedWork Interface + virtual void PostDoWork() override final; + //~ End IVoxelQueuedWork Interface + + //~ Begin FVoxelAsyncWorkWithWait Interface + virtual void PostDoWorkBeforeTrigger() {}; + //~ End FVoxelAsyncWorkWithWait Interface + + void WaitForCompletion(); + +protected: + // Important: do not allow public delete + virtual ~FVoxelAsyncWorkWithWait() override; + +private: + FEvent* DoneEvent; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelBoolVector.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelBoolVector.h new file mode 100644 index 00000000..f9fc3cc0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelBoolVector.h @@ -0,0 +1,43 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelBoolVector.generated.h" + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelBoolVector +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bX = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bY = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bZ = false; + + inline FVector ToFVector() const + { + return FVector(bX, bY, bZ); + } + inline FVoxelBoolVector operator!() const + { + return { !bX, !bY, !bZ }; + } + + friend inline bool operator==(const FVoxelBoolVector& Lhs, const FVoxelBoolVector& Rhs) + { + return Lhs.bX == Rhs.bX + && Lhs.bY == Rhs.bY + && Lhs.bZ == Rhs.bZ; + } + friend inline bool operator!=(const FVoxelBoolVector& Lhs, const FVoxelBoolVector& Rhs) + { + return Lhs.bX != Rhs.bX + || Lhs.bY != Rhs.bY + || Lhs.bZ != Rhs.bZ; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCancelCounter.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCancelCounter.h new file mode 100644 index 00000000..b0e6669c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCancelCounter.h @@ -0,0 +1,25 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSharedPtr.h" + +class FVoxelCancelCounter +{ +public: + explicit FVoxelCancelCounter(const TVoxelSharedRef& Counter) + : Counter(Counter) + , Threshold(Counter->GetValue()) + { + } + + bool IsCanceled() const + { + return Counter->GetValue() > Threshold; + } + +private: + const TVoxelSharedRef Counter; + const int64 Threshold; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCharacter.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCharacter.h new file mode 100644 index 00000000..3b19f3fb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCharacter.h @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "GameFramework/Character.h" +#include "Components/PrimitiveComponent.h" +#include "VoxelCharacter.generated.h" + +UCLASS(BlueprintType) +class VOXEL_API AVoxelCharacter : public ACharacter +{ + GENERATED_BODY() + +public: + // You can copy/paste this function to your own character class + // You might have to add + // #include "Components/PrimitiveComponent.h" + // at the top of your character header + // This function is required for base replication to work properly, as voxel world components are generated at runtime & not replicated + virtual void SetBase(UPrimitiveComponent* NewBase, FName BoneName, bool bNotifyActor) override + { + if (NewBase) + { + AActor* BaseOwner = NewBase->GetOwner(); + // LoadClass to not depend on the voxel module + static UClass* VoxelWorldClass = LoadClass(nullptr, TEXT("/Script/Voxel.VoxelWorld")); + if (ensure(VoxelWorldClass) && BaseOwner && BaseOwner->IsA(VoxelWorldClass)) + { + NewBase = Cast(BaseOwner->GetRootComponent()); + ensure(NewBase); + } + } + Super::SetBase(NewBase, BoneName, bNotifyActor); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelInvokerComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelInvokerComponent.h new file mode 100644 index 00000000..3eba97af --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelInvokerComponent.h @@ -0,0 +1,239 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "Components/SceneComponent.h" +#include "GameFramework/Volume.h" +#include "VoxelInvokerSettings.h" +#include "VoxelInvokerComponent.generated.h" + +class AVoxelWorldInterface; + +// Voxel Invokers are used to configure the voxel world LOD, collisions and navmesh +UCLASS(Abstract, Blueprintable, ClassGroup = Voxel) +class VOXEL_API UVoxelInvokerComponentBase : public USceneComponent +{ + GENERATED_BODY() + +public: + // Whether or not this invoker will be used to trigger voxel events + // Example of voxel events include: + // - foliage spawning + // - foliage collisions + // - manually bound events + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Events") + bool bUseForEvents = true; + + // Whether to use to compute the tasks priorities + // If true, the task priorities will be higher if they are closer to this + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Priority") + bool bUseForPriorities = true; + +protected: + // Whether to enable the invoker when spawned + // If not, you'll need to call EnableInvoker + UPROPERTY(EditDefaultsOnly, Category = "Voxel Invoker") + bool bStartsEnabled = true; + +public: + // Is this invoker local? If false, bUseForLOD will always be considered as false + // Useful for multiplayer, to only compute the LOD for the local player + // Defaults to Cast(GetOwner())->IsLocallyControlled() + UFUNCTION(BlueprintNativeEvent, Category = "Voxel|Invoker") + bool IsLocalInvoker() const; + + // Used to detect if the invoker has moved + // Also used for events + UFUNCTION(BlueprintNativeEvent, Category = "Voxel|Invoker") + FIntVector GetInvokerVoxelPosition(AVoxelWorldInterface* VoxelWorld) const; + FIntVector GetInvokerVoxelPosition(const AVoxelWorldInterface* VoxelWorld) const; + + // Get the invoker settings + // All the bounds are in voxel space + UFUNCTION(BlueprintNativeEvent, Category = "Voxel|Invoker") + FVoxelInvokerSettings GetInvokerSettings(AVoxelWorldInterface* VoxelWorld) const; + FVoxelInvokerSettings GetInvokerSettings(const AVoxelWorldInterface* VoxelWorld) const; + +public: + //~ Begin UVoxelInvokerComponentBase Interface + virtual bool IsLocalInvoker_Implementation() const; + virtual FIntVector GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const; + virtual FVoxelInvokerSettings GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const; + //~ End UVoxelInvokerComponentBase Interface + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Invoker") + void EnableInvoker(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Invoker") + void DisableInvoker(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Invoker") + bool IsInvokerEnabled() const; + +protected: + //~ Begin UActorComponent Interface + virtual void OnRegister() override; + virtual void OnUnregister() override; + //~ End UActorComponent Interface + +private: + bool bIsInvokerEnabled = false; + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Invoker") + static void RefreshAllVoxelInvokers(); + + static const TArray>& GetInvokers(UWorld* World); + static FSimpleMulticastDelegate OnForceRefreshInvokers; + +private: + static TMap, TArray>> Components; +}; + +// Voxel Invokers are used to configure the voxel world LOD, collisions and navmesh +// Simple position based invoker +UCLASS(ClassGroup = Voxel, meta = (BlueprintSpawnableComponent)) +class VOXEL_API UVoxelSimpleInvokerComponent : public UVoxelInvokerComponentBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|LOD") + bool bUseForLOD = true; + + // You should leave this to 0 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|LOD", meta = (DisplayName = "LOD to Set", EditCondition = bUseForLOD, ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26)) + int32 LODToSet = 0; + + // In cm. Will set LODToSet around the invoker on this distance + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|LOD", meta = (DisplayName = "LOD Range", EditCondition = bUseForLOD, ClampMin = 0)) + float LODRange = 1000; + + // Will enable high res collisions around the invoker + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Collisions") + bool bUseForCollisions = true; + + // In cm. Will enable high res collisions on chunks under this distance from this invoker + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Collisions", meta = (EditCondition = bUseForCollisions, ClampMin = 0)) + float CollisionsRange = 1000; + + // Will enable high res navmesh around the invoker + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Navmesh") + bool bUseForNavmesh = true; + + // In cm. Will enable high res navmesh on chunks under this distance from this invoker + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Navmesh", meta = (EditCondition = bUseForNavmesh, ClampMin = 0)) + float NavmeshRange = 1000; + +public: + // VoxelSimpleInvokerComponent's GetInvokerVoxelPosition and GetInvokerSettings functions are calling GetInvokerGlobalPosition to find the global position of the invoker + // Defaults to GetComponentPosition + UFUNCTION(BlueprintNativeEvent, Category = "Voxel|Invoker") + FVector GetInvokerGlobalPosition() const; + +public: + //~ Begin UVoxelInvokerComponentBase Interface + virtual FIntVector GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const override; + virtual FVoxelInvokerSettings GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const override; + //~ End UVoxelInvokerComponentBase Interface + +protected: + //~ Begin UVoxelSimpleInvokerComponent Interface + virtual FVector GetInvokerGlobalPosition_Implementation() const; + //~ End UVoxelSimpleInvokerComponent Interface +}; + +// Voxel Invokers are used to configure the voxel world LOD, collisions and navmesh +// Same as simple invoker, but optionally use the velocity to predict the position +UCLASS(ClassGroup = Voxel, meta = (BlueprintSpawnableComponent)) +class VOXEL_API UVoxelInvokerWithPredictionComponent : public UVoxelSimpleInvokerComponent +{ + GENERATED_BODY() + +public: + // Will use the speed of the owner to determine the position to use + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Prediction") + bool bEnablePrediction = false; + + // Will multiply the velocity by this to get the new position + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Prediction", meta = (EditCondition = bEnablePrediction, ClampMin = 0)) + float PredictionTime = 1; + +protected: + //~ Begin UVoxelSimpleInvokerComponent Interface + virtual FVector GetInvokerGlobalPosition_Implementation() const override; + //~ End UVoxelSimpleInvokerComponent Interface +}; + +// Voxel Invokers are used to configure the voxel world LOD, collisions and navmesh +// Will find the camera and use it to set its position +UCLASS(ClassGroup = Voxel, meta = (BlueprintSpawnableComponent)) +class VOXEL_API UVoxelInvokerAutoCameraComponent : public UVoxelSimpleInvokerComponent +{ + GENERATED_BODY() + +protected: + //~ Begin UVoxelSimpleInvokerComponent Interface + virtual FVector GetInvokerGlobalPosition_Implementation() const override; + //~ End UVoxelSimpleInvokerComponent Interface +}; + +UCLASS(Within = VoxelLODVolume) +class VOXEL_API UVoxelLODVolumeInvokerComponent : public UVoxelInvokerComponentBase +{ + GENERATED_BODY() + +public: + UVoxelLODVolumeInvokerComponent(); + + // Will set the LOD in the volume to a fixed value + // Note that the displayed LOD might have a higher resolution than this if another invoker is close + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Volume", DisplayName = "Use for LOD") + bool bUseForLOD = true; + + // Will set the LOD in the volume to a fixed value + // Note that the displayed LOD might have a higher resolution than this if another invoker is close + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Volume", DisplayName = "LOD to Set", meta = (EditCondition = bUseForLOD)) + int32 LODToSet = 0; + + // Will compute high res collision in the volume + // Note that collisions might still be computed by the voxel world even if this is false + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Volume") + bool bUseForCollisions = false; + + // Will compute high res navmesh in the volume + // Note that navmesh might still be computed by the voxel world even if this is false + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel Invoker|Volume") + bool bUseForNavmesh = false; + +protected: + //~ Begin UVoxelInvokerComponentBase Interface + virtual bool IsLocalInvoker_Implementation() const override; + virtual FIntVector GetInvokerVoxelPosition_Implementation(AVoxelWorldInterface* VoxelWorld) const override; + virtual FVoxelInvokerSettings GetInvokerSettings_Implementation(AVoxelWorldInterface* VoxelWorld) const override; + //~ End UVoxelInvokerComponentBase Interface +}; + +// Volume with a voxel invoker +// Sets the LOD of the voxels in a volume, or always enable collision/navmesh in a volume +UCLASS(hidecategories=(Advanced, Attachment, Collision, Volume), DisplayName = "Voxel LOD Volume") +class VOXEL_API AVoxelLODVolume : public AVolume +{ + GENERATED_BODY() + +public: + AVoxelLODVolume(); + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel") + UVoxelLODVolumeInvokerComponent* InvokerComponent; + +protected: + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + //~ End UObject Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelNoClippingComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelNoClippingComponent.h new file mode 100644 index 00000000..da422c83 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelNoClippingComponent.h @@ -0,0 +1,122 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Async/Future.h" +#include "Components/SceneComponent.h" +#include "VoxelNoClippingComponent.generated.h" + +class FVoxelData; +class AVoxelWorld; +class UCharacterMovementComponent; +struct FVoxelVector; + +// Add this to your player to prevent it from falling through the voxel world when digging under its feet +// +// On tick, will check if the player is inside the voxel world surface +// If it is, it will search for the nearest safe position +// - if found: teleport there, resetting its velocity. OnTeleported will be fired. +// - if not: the MoveTowardsSurface event will be fired every tick, ignoring TickRate. Bind this and eg move the player upwards. +// When the player will be safe again, StopMovingTowardsSurface will be fired, eg to reset the velocity. +// +// If you set EnableDefaultBehavior to true, you don't need to bind any of the event, Characters will be automatically handled +UCLASS(ClassGroup = Voxel, meta = (BlueprintSpawnableComponent)) +class VOXEL_API UVoxelNoClippingComponent : public USceneComponent +{ + GENERATED_BODY() + +public: + UVoxelNoClippingComponent(); + +public: + // Delay in seconds between checks + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Config") + float TickRate = 0.1f; + + // How far in voxels to search for an empty voxel + // Keep low! + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Config", meta = (UIMin = 1, UIMax = 32)) + int32 SearchRange = 5; + +public: + // If true, you don't need to implement any of the events, just set the settings below + // If the owner is a character, it will be automatically detected & the correct behavior will be applied + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Default Behavior") + bool bEnableDefaultBehavior = true; + + // Speed in unreal units per second at which to move towards the surface + // If you set this too high, we might overshot! + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Default Behavior", meta = (EditCondition = "bEnableDefaultBehavior")) + float Speed = 6000; + + // If true, will move away from a point instead of upwards + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Default Behavior", meta = (EditCondition = "bEnableDefaultBehavior")) + bool bIsPlanet = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Default Behavior", meta = (EditCondition = "bEnableDefaultBehavior && bIsPlanet")) + FVector PlanetCenter = FVector::ZeroVector; + +public: + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMoveTowardsSurface); + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStopMovingTowardsSurface); + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTeleported); + + // Will be called when we're clipping, but no safe position can be found + // You can bind this to eg move the player upward + // When the player will be safe again, StopMovingTowardsSurface will be fired + UPROPERTY(BlueprintAssignable) + FOnMoveTowardsSurface MoveTowardsSurface; + + // Will be fired once we're safe again + UPROPERTY(BlueprintAssignable) + FOnStopMovingTowardsSurface StopMovingTowardsSurface; + + // Called when we teleported to a safe location. You usually want to clear the velocity in there to avoid issues + UPROPERTY(BlueprintAssignable) + FOnTeleported OnTeleported; + +public: + // Implement this to select which voxel worlds to consider + UFUNCTION(BlueprintNativeEvent, Category = "Voxel") + bool ShouldUseVoxelWorld(AVoxelWorld* VoxelWorld); + + virtual bool ShouldUseVoxelWorld_Implementation(AVoxelWorld* VoxelWorld) + { + // Use all voxel worlds by default + return true; + } + +public: + // True if we are currently inside the voxel world surface + UPROPERTY(BlueprintReadOnly, Category = "Voxel") + bool bIsInsideSurface = false; + +protected: + //~ Begin UActorComponent Interface + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + //~ End UActorComponent Interface + +private: + double LastTickTime = 0; + + struct FAsyncResult + { + bool bInsideSurface = false; + TOptional ClosestSafeLocation; + }; + TFuture> AsyncResult; + TArray> PendingVoxelWorlds; + + void StartAsyncTask(); + + void BroadcastMoveTowardsSurface() const; + void BroadcastStopMovingTowardsSurface() const; + void BroadcastOnTeleported() const; + + UCharacterMovementComponent* GetCharacterMovement() const; + + static void ResetVelocity(UCharacterMovementComponent& CharacterMovement); + static FAsyncResult AsyncTask(const FVoxelData& Data, const FVoxelVector& ComponentLocation, int32 SearchRange); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelPhysicsRelevancyComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelPhysicsRelevancyComponent.h new file mode 100644 index 00000000..6ade649b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelComponents/VoxelPhysicsRelevancyComponent.h @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "Engine/EngineTypes.h" +#include "VoxelPhysicsRelevancyComponent.generated.h" + +class UPrimitiveComponent; + +/** + * Disable physics on actors that are out of the Voxel World collision range + */ +UCLASS(ClassGroup = (Voxel), meta = (BlueprintSpawnableComponent, Keywords = "voxel auto disable component")) +class VOXEL_API UVoxelPhysicsRelevancyComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UVoxelPhysicsRelevancyComponent(); + + // Inclusive + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (ClampMin = "0", ClampMax = "24", UIMin = "0", UIMax = "24"), DisplayName = "Max LOD For Physics") + uint8 MaxVoxelChunksLODForPhysics = 2; + + // Delay to allow the voxel chunks collisions to be updated. In seconds + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float TimeToWaitBeforeActivating = 1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float TickInterval = 0.1; + +protected: + //~ Begin UActorComponent Interface + void BeginPlay() override; + void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + //~ End UActorComponent Interface + +private: + TArray PrimitiveComponentsWithPhysicsEnabled; + bool bArePhysicsEnabled = true; + FTimerHandle Handle; + bool bWaitingToBeActivated = false; + + void ActivatePhysics(); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/NoGrowArray.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/NoGrowArray.h new file mode 100644 index 00000000..ccf46808 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/NoGrowArray.h @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +template +class TNoGrowAllocator : public T +{ +public: + class ForAnyElementType : public T::ForAnyElementType + { + public: + FORCEINLINE typename T::SizeType CalculateSlackGrow(typename T::SizeType NumElements, typename T::SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const + { + ensure(false); + return T::ForAnyElementType::CalculateSlackGrow(NumElements, NumAllocatedElements, NumBytesPerElement); + } + }; + + template + class ForElementType : public T::template ForAnyElementType + { + public: + FORCEINLINE typename T::SizeType CalculateSlackGrow(typename T::SizeType NumElements, typename T::SizeType NumAllocatedElements, SIZE_T NumBytesPerElement) const + { + ensure(false); + return T::template ForAnyElementType::CalculateSlackGrow(NumElements, NumAllocatedElements, NumBytesPerElement); + } + }; +}; + +template +using TNoGrowArray = TArray>; + +template +using TNoGrowArray64 = TArray>; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArray3.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArray3.h new file mode 100644 index 00000000..b37a0982 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArray3.h @@ -0,0 +1,64 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +template +struct TVoxelArray3 +{ + FIntVector Size = FIntVector(ForceInit); + TArray Data; + + TVoxelArray3() = default; + explicit TVoxelArray3(const FIntVector& NewSize) + { + Resize(NewSize); + } + + FORCEINLINE const T& operator()(const FIntVector& P) const + { + return (*this)(P.X, P.Y, P.Z); + } + FORCEINLINE T& operator()(const FIntVector& P) + { + return (*this)(P.X, P.Y, P.Z); + } + + FORCEINLINE const T& operator()(int32 I, int32 J, int32 K) const + { + return const_cast&>(*this)(I, J, K); + } + FORCEINLINE T& operator()(int32 I, int32 J, int32 K) + { + checkVoxelSlow(0 <= I && I < Size.X); + checkVoxelSlow(0 <= J && J < Size.Y); + checkVoxelSlow(0 <= K && K < Size.Z); + return Data.GetData()[I + J * Size.X + K * Size.X * Size.Y]; + } + + void Resize(const FIntVector& NewSize) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + check(int64(NewSize.X) * int64(NewSize.Y) * int64(NewSize.Z) < MAX_int32); + Size = NewSize; + Data.Empty(Size.X * Size.Y * Size.Z); + Data.SetNumUninitialized(Size.X * Size.Y * Size.Z); + } + + void Assign(const T& Value) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + for (auto& X : Data) + { + X = Value; + } + } + + void Memzero() + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + FMemory::Memzero(Data.GetData(), Data.Num() * sizeof(T)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArrayView.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArrayView.h new file mode 100644 index 00000000..73b95c9a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelArrayView.h @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +template +struct TVoxelArrayView +{ + TVoxelArrayView() = default; + + TVoxelArrayView(TArray& Other) + : DataPtr(Other.GetData()) + , ArrayNum(Other.Num()) + { + } + template::Value>::Type> + TVoxelArrayView(const TArray::Type>& Other) + : DataPtr(Other.GetData()) + , ArrayNum(Other.Num()) + { + } + + FORCEINLINE bool IsValidIndex(int32 Index) const + { + return (Index >= 0) && (Index < ArrayNum); + } + FORCEINLINE int32 Num() const + { + return ArrayNum; + } + FORCEINLINE const T& operator[](int32 Index) const + { + checkVoxelSlow(IsValidIndex(Index)); + return DataPtr[Index]; + } + +private: + T* RESTRICT DataPtr = nullptr; + int32 ArrayNum = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelSparseArray.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelSparseArray.h new file mode 100644 index 00000000..245c083f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelSparseArray.h @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/SparseArray.h" + +// Default one does not do check on element access +template +class TVoxelSparseArray : public TSparseArray +{ +public: + using Super = TSparseArray; + + // Accessors. + inline InElementType& operator[](int32 Index) + { + check(Index >= 0 && Index < this->GetMaxIndex() && this->IsValidIndex(Index)); + return Super::operator[](Index); + } + inline const InElementType& operator[](int32 Index) const + { + check(Index >= 0 && Index < this->GetMaxIndex() && this->IsValidIndex(Index)); + return Super::operator[](Index); + } +}; + +// UniqueClass: to forbid copying ids from different classes +template +class TVoxelTypedSparseArrayId +{ +public: + TVoxelTypedSparseArrayId() = default; + + bool operator==(TVoxelTypedSparseArrayId Other) const { return Other.Index == Index; } + bool operator!=(TVoxelTypedSparseArrayId Other) const { return Other.Index != Index; } + +public: + bool IsValid() const + { + return Index != 0; + } + void Reset() + { + Index = 0; + } + uint32 GetDebugValue() const + { + return Index; + } + +public: + friend uint32 GetTypeHash(TVoxelTypedSparseArrayId Value) + { + return GetTypeHash(Value.Index); + } + +private: + TVoxelTypedSparseArrayId(uint32 Index) : Index(Index) {} + + uint32 Index = 0; + + template + friend class TVoxelTypedSparseArray; +}; + +#define DEFINE_TYPED_VOXEL_SPARSE_ARRAY_ID(Name) using Name = TVoxelTypedSparseArrayId; + +template +class TVoxelTypedSparseArray +{ +public: + inline InElementType& operator[](InKeyType Index) + { + check(Index.IsValid()); + return Storage[Index.Index - 1]; + } + inline const InElementType& operator[](InKeyType Index) const + { + check(Index.IsValid()); + return Storage[Index.Index - 1]; + } + inline bool IsValidIndex(InKeyType Index) const + { + return Index.IsValid() && Storage.IsValidIndex(Index.Index - 1); + } + inline InKeyType Add(const InElementType& Element) + { + return { uint32(Storage.Add(Element)) + 1 }; + } + inline InKeyType Add(InElementType&& Element) + { + return { uint32(Storage.Add(MoveTemp(Element))) + 1 }; + } + inline void RemoveAt(InKeyType Index) + { + check(Index.IsValid()); + Storage.RemoveAt(Index.Index - 1); + } + inline int32 Num() const + { + return Storage.Num(); + } + +public: + inline auto begin() { return Storage.begin(); } + inline auto begin() const { return Storage.begin(); } + inline auto end () { return Storage.end(); } + inline auto end () const { return Storage.end(); } + +private: + TVoxelSparseArray Storage; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelStaticArray.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelStaticArray.h new file mode 100644 index 00000000..92dfece7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelContainers/VoxelStaticArray.h @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +template +class alignas(Alignment) TVoxelStaticArray +{ +public: + using ElementType = T; + + TVoxelStaticArray() + { + } + FORCEINLINE explicit TVoxelStaticArray(EForceInit) + { + for (auto& Element : *this) + { + new (&Element) T{}; + } + } + template + FORCEINLINE TVoxelStaticArray(TArgs... Args) + { + static_assert(sizeof...(Args) == Size, ""); + SetFromVariadicArgs(Args...); + } + + FORCEINLINE static constexpr uint32 Num() + { + return Size; + } + FORCEINLINE static constexpr uint32 GetTypeSize() + { + return sizeof(T); + } + + FORCEINLINE void Memzero() + { + FMemory::Memzero(GetData(), Size * sizeof(T)); + } + + FORCEINLINE T* RESTRICT GetData() + { + return reinterpret_cast(Data); + } + FORCEINLINE const T* RESTRICT GetData() const + { + return reinterpret_cast(Data); + } + + template + void CopyFromArray(const TArray& Array, bool bInitializeEnd = true) + { + check(Size >= Array.Num()); + for (int32 Index = 0; Index < Array.Num(); Index++) + { + (*this)[Index] = Array[Index]; + } + if (bInitializeEnd) + { + for (int32 Index = Array.Num(); Index < Size; Index++) + { + (*this)[Index] = T{}; + } + } + } + + FORCEINLINE T& operator[](int32 Index) + { + checkVoxelSlow(0 <= Index && Index < Size); + return GetData()[Index]; + } + FORCEINLINE const T& operator[](int32 Index) const + { + checkVoxelSlow(0 <= Index && Index < Size); + return GetData()[Index]; + } + + operator TArray() const + { + return TArray(GetData(), Num()); + } + + operator TArrayView() + { + return TArrayView(GetData(), Num()); + } + operator TArrayView() const + { + return TArrayView(GetData(), Num()); + } + + FORCEINLINE T* begin() { return GetData(); } + FORCEINLINE T* end() { return GetData() + Size; } + + FORCEINLINE const T* begin() const { return GetData(); } + FORCEINLINE const T* end() const { return GetData() + Size; } + + template + FORCEINLINE void SetFromVariadicArgs(T Arg, TArgs... Args) + { + static_assert(0 <= Index && Index < Size, ""); + static_assert(sizeof...(Args) == Size - 1 - Index, ""); + (*this)[Index] = Arg; + SetFromVariadicArgs(Args...); + } + template + FORCEINLINE void SetFromVariadicArgs(T Arg) + { + static_assert(Index == Size - 1, ""); + (*this)[Index] = Arg; + } + +private: + uint8 Data[Size * sizeof(T)]; +}; + +template +struct TIsContiguousContainer> +{ + enum { Value = true }; +}; + +template +class TVoxelStaticBitArray +{ +public: + TVoxelStaticBitArray() = default; + TVoxelStaticBitArray(EForceInit) + { + Clear(); + } + + void Clear() + { + Array.Memzero(); + } + + FORCEINLINE void Set(uint32 Index) + { + checkVoxelSlow(Index < Size); + Array[Index / 32] |= (1u << (Index % 32)); + } + FORCEINLINE void Clear(uint32 Index) + { + checkVoxelSlow(Index < Size); + Array[Index / 32] &= ~(1u << (Index % 32)); + } + FORCEINLINE bool Test(uint32 Index) const + { + checkVoxelSlow(Index < Size); + return Array[Index / 32] & (1u << (Index % 32)); + } + +private: + TVoxelStaticArray Array; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookedData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookedData.h new file mode 100644 index 00000000..a80b6b3a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookedData.h @@ -0,0 +1,101 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelSaveStruct.h" +#include "VoxelCookedData.generated.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Cooked Data Memory"), STAT_VoxelCookedDataMemory, STATGROUP_VoxelMemory, VOXEL_API); + +namespace FVoxelCookedDataVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + SHARED_RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + SHARED_ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +}; + +struct VOXEL_API FVoxelCookedDataImpl +{ + FVoxelCookedDataImpl() = default; + ~FVoxelCookedDataImpl(); + + bool operator==(const FVoxelCookedDataImpl& Other) const + { + return Guid == Other.Guid; + } + + bool Serialize(FArchive& Ar); + void UpdateAllocatedSize() const; + +public: + struct FChunk + { + TArray Data; + + friend FArchive& operator<<(FArchive& Ar, FChunk& Chunk) + { + Chunk.Data.BulkSerialize(Ar); + return Ar; + } + }; + + void SetNumChunks(int32 Num) + { + Chunks.SetNum(Num); + } + FChunk& GetChunk(int32 Index) + { + return Chunks[Index]; + } + void RemoveEmptyChunks(); + + const TArray& GetChunks() const + { + return Chunks; + } + bool IsEmpty() const + { + return Chunks.Num() == 0; + } + +private: + int32 Version; + FGuid Guid; + + TArray Chunks; + + mutable int64 AllocatedSize = 0; +}; + +// Blueprint wrapper that's cheap to copy around +USTRUCT(BlueprintType, Category = Voxel) +struct VOXEL_API FVoxelCookedData +#if CPP + : public TVoxelSaveStruct +#endif +{ + GENERATED_BODY() +}; + +DEFINE_VOXEL_SAVE_STRUCT(FVoxelCookedData); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookingLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookingLibrary.h new file mode 100644 index 00000000..5bf7a1e2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelCooking/VoxelCookingLibrary.h @@ -0,0 +1,79 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCookedData.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelData/VoxelSave.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelCookingLibrary.generated.h" + +class AVoxelWorld; + +USTRUCT(BlueprintType) +struct FVoxelCookingSettings +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + int32 ThreadCount = 2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + int32 RenderOctreeDepth = 5; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float VoxelSize = 100.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + EVoxelRenderType RenderType = EVoxelRenderType::MarchingCubes; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelGeneratorPicker Generator; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", AdvancedDisplay) + bool bLogProgress = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", AdvancedDisplay) + bool bFastCollisionCook = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", AdvancedDisplay) + bool bCleanCollisionMesh = false; +}; + +UCLASS() +class VOXEL_API UVoxelCookingLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + static FVoxelCookedData CookVoxelDataImpl(const FVoxelCookingSettings& Settings, const FVoxelUncompressedWorldSaveImpl* Save = nullptr); + + // Cook collision meshes and save the result to VoxelCookedData + // Can then be loaded using LoadCookedVoxelData + // Useful for servers + UFUNCTION(BlueprintCallable, Category = "Voxel|Cooking") + static FVoxelCookedData CookVoxelData(FVoxelCookingSettings Settings) + { + return CookVoxelDataImpl(Settings, nullptr); + } + // Cook collision meshes and save the result to VoxelCookedData + // Can then be loaded using LoadCookedVoxelData + // Useful for servers + UFUNCTION(BlueprintCallable, Category = "Voxel|Cooking") + static FVoxelCookedData CookVoxelDataWithSave(FVoxelCookingSettings Settings, FVoxelUncompressedWorldSave Save) + { + return CookVoxelDataImpl(Settings, &Save.Const()); + } + + UFUNCTION(BlueprintPure, Category = "Voxel|Cooking", meta = (DefaultToSelf = "World")) + static FVoxelCookingSettings MakeVoxelCookingSettingsFromVoxelWorld(AVoxelWorld* World, int32 ThreadCount = 2); + +public: + // Loads collision cooked with CookVoxelData + // The voxel world must not be created: it won't ever be created, collision meshes will be loaded directly + // Note: Only the voxel world collision settings will be applied + // Useful for servers + UFUNCTION(BlueprintCallable, Category = "Voxel|Cooking", meta = (DefaultToSelf = "World")) + static void LoadCookedVoxelData(FVoxelCookedData CookedData, AVoxelWorld* World); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/IVoxelData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/IVoxelData.h new file mode 100644 index 00000000..88be7b11 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/IVoxelData.h @@ -0,0 +1,55 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" + +class FVoxelGeneratorInstance; + +class IVoxelDataOctreeMemory +{ +public: + struct FDataOctreeMemory + { + uint8 PadToAvoidContention0[PLATFORM_CACHE_LINE_SIZE]; + FThreadSafeCounter64 Values; + uint8 PadToAvoidContention1[PLATFORM_CACHE_LINE_SIZE]; + FThreadSafeCounter64 Materials; + uint8 PadToAvoidContention2[PLATFORM_CACHE_LINE_SIZE]; + }; + + const FDataOctreeMemory& GetCachedMemory() const { return CachedMemory; } + const FDataOctreeMemory& GetDirtyMemory() const { return DirtyMemory; } + +private: + mutable FDataOctreeMemory CachedMemory{}; + mutable FDataOctreeMemory DirtyMemory{}; + + template + friend struct TVoxelDataOctreeLeafMemoryUsage; +}; + +class IVoxelData : public IVoxelDataOctreeMemory +{ +public: + const int32 Depth; + const FVoxelIntBox WorldBounds; + const bool bEnableMultiplayer; + const bool bEnableUndoRedo; + const TVoxelSharedRef Generator; + + IVoxelData( + int32 Depth, + const FVoxelIntBox& WorldBounds, + bool bEnableMultiplayer, + bool bEnableUndoRedo, + const TVoxelSharedRef& Generator) + : Depth(Depth) + , WorldBounds(WorldBounds) + , bEnableMultiplayer(bEnableMultiplayer) + , bEnableUndoRedo(bEnableUndoRedo) + , Generator(Generator) + { + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.h new file mode 100644 index 00000000..0b93e131 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.h @@ -0,0 +1,416 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelSharedMutex.h" +#include "VoxelData/IVoxelData.h" +#include "HAL/ConsoleManager.h" + +class AVoxelWorld; +class FVoxelData; +class FVoxelDataLockInfo; +class FVoxelDataOctreeBase; +class FVoxelDataOctreeLeaf; +class FVoxelDataOctreeParent; +class FVoxelGeneratorInstance; +class FVoxelTransformableGeneratorInstance; + +struct FVoxelDataItem; +struct FVoxelAssetItem; +struct FVoxelObjectArchiveEntry; +struct FVoxelDisableEditsBoxItem; +struct FVoxelPlaceableItemLoadInfo; +struct FVoxelUncompressedWorldSaveImpl; + +template +struct TVoxelRange; +template +class TVoxelQueryZone; +template +struct TVoxelChunkDiff; + +DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num Voxel Asset Items"), STAT_NumVoxelAssetItems, STATGROUP_VoxelCounters, VOXEL_API); +DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num Voxel Disable Edits Items"), STAT_NumVoxelDisableEditsItems, STATGROUP_VoxelCounters, VOXEL_API); +DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num Voxel Data Items"), STAT_NumVoxelDataItems, STATGROUP_VoxelCounters, VOXEL_API); + +extern VOXEL_API TAutoConsoleVariable CVarMaxPlaceableItemsPerOctree; +extern VOXEL_API TAutoConsoleVariable CVarStoreSpecialValueForGeneratorValuesInSaves; + +// Turns off some expensive compression settings that aren't needed if you just want to save, recreate world, load +// TODO REMOVE AND MAKE Save/Load param +struct FVoxelScopedFastSaveLoad +{ + // No need to diff against generator as it's very slow + const int32 DiffGenerator = CVarStoreSpecialValueForGeneratorValuesInSaves.GetValueOnGameThread(); + + FVoxelScopedFastSaveLoad() + { + CVarStoreSpecialValueForGeneratorValuesInSaves->Set(0); + } + ~FVoxelScopedFastSaveLoad() + { + CVarStoreSpecialValueForGeneratorValuesInSaves->Set(DiffGenerator); + } +}; + +template +class TVoxelDataItemWrapper +{ +public: + T Item; + +private: + mutable int32 Index = -1; + TVoxelWeakPtr Data; + + friend class FVoxelData; +}; + +struct VOXEL_API FVoxelDataSettings +{ + const int32 Depth; + const FVoxelIntBox WorldBounds; + const TVoxelSharedRef Generator; + const bool bEnableMultiplayer; + const bool bEnableUndoRedo; + + FVoxelDataSettings(const AVoxelWorld* World, EVoxelPlayType PlayType); + FVoxelDataSettings( + int32 Depth, + const TVoxelSharedRef& Generator, + bool bEnableMultiplayer, + bool bEnableUndoRedo); + FVoxelDataSettings( + const FVoxelIntBox& WorldBounds, + const TVoxelSharedRef& Generator, + bool bEnableMultiplayer, + bool bEnableUndoRedo); +}; + +/** + * Class that handle voxel data + */ +class VOXEL_API FVoxelData : public IVoxelData, public TVoxelSharedFromThis +{ +private: + explicit FVoxelData(const FVoxelDataSettings& Settings); + +public: + static TVoxelSharedRef Create(const FVoxelDataSettings& Settings, int32 DataOctreeInitialSubdivisionDepth = 0); + // Clone without keeping the voxel data + TVoxelSharedRef Clone() const; + ~FVoxelData(); + +private: + TUniquePtr Octree; + // Is locked as read when a lock is done + // Lock as write to clear the octree, making sure no octrees are locked + mutable FVoxelSharedMutex MainLock; + +public: + FORCEINLINE int32 Size() const + { + return DATA_CHUNK_SIZE << Depth; + } + FVoxelDataOctreeBase& GetOctree() const; + + // NOTE: what if we query between WorldBounds.Max - 1 and WorldBounds.Max? + template + FORCEINLINE bool IsInWorld(T X, T Y, T Z) const + { + return WorldBounds.ContainsTemplate(X, Y, Z); + } + template + FORCEINLINE bool IsInWorld(const T& P) const + { + return WorldBounds.ContainsTemplate(P); + } + + template + FORCEINLINE void ClampToWorld(T& X, T& Y, T& Z) const + { + WorldBounds.Clamp(X, Y, Z); + ensureVoxelSlow(IsInWorld(X, Y, Z)); + } + template + FORCEINLINE T ClampToWorld(const T& P) const + { + return WorldBounds.Clamp(P); + } + +public: + /** + * Lock the bounds + * @param LockType Read or write lock + * @param Bounds Bounds to lock + * @param Name The name of the task locking these bounds, for debug + */ + TUniquePtr Lock(EVoxelLockType LockType, const FVoxelIntBox& Bounds, FName Name) const; + + /** + * Unlock previously locked bounds + */ + void Unlock(TUniquePtr LockInfo) const; + +public: + // Must NOT be locked. Will delete the entire octree & recreate one + // Destroys all items + void ClearData(); + + // Will clear all the edited data. Keeps items + // Requires write lock + void ClearOctreeData(TArray& OutBoundsToUpdate); + + // Requires write lock + template + void CacheBounds(const FVoxelIntBox& Bounds, bool bMultiThreaded); + + // Requires write lock + template + void ClearCacheInBounds(const FVoxelIntBox& Bounds); + + // Requires write lock + template + void CheckIsSingle(const FVoxelIntBox& Bounds); + + // Get the data in zone. Requires read lock + template + void Get(TVoxelQueryZone& QueryZone, int32 LOD) const; + + template + TArray Get(const FVoxelIntBox& Bounds) const; + + // Will always use 8 threads + template + TArray ParallelGet(const FVoxelIntBox& Bounds, bool bForceSingleThread = false) const; + + TArray GetValues(const FVoxelIntBox& Bounds) const + { + return Get(Bounds); + } + TArray GetMaterials(const FVoxelIntBox& Bounds) const + { + return Get(Bounds); + } + + // Requires read lock + TVoxelRange GetValueRange(const FVoxelIntBox& Bounds, int32 LOD) const; + + bool IsEmpty(const FVoxelIntBox& Bounds, int32 LOD) const; + + template + T GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const; + + template + FORCEINLINE T GetCustomOutput(T DefaultValue, FName Name, const U& P, int32 LOD) const + { + return GetCustomOutput(DefaultValue, Name, P.X, P.Y, P.Z, LOD); + } + + TVoxelRange GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD) const; + +public: + template + void Set(const FVoxelIntBox& Bounds, F Apply); + + template + void ParallelSet(const FVoxelIntBox& Bounds, F Apply, bool bForceSingleThread = false); + +public: + /** + * Getters/Setters + */ + // Set value or material at position depending on template argument (FVoxelValue or FVoxelMaterial) + template + void Set(int32 X, int32 Y, int32 Z, const T& Value); + + template + FORCEINLINE void Set(const FIntVector& P, const T& Value) + { + Set(P.X, P.Y, P.Z, Value); + } + + template + T Get(int32 X, int32 Y, int32 Z, int32 LOD) const; + + template + FORCEINLINE T Get(const FIntVector& P, int32 LOD) const + { + return Get(P.X, P.Y, P.Z, LOD); + } + + // Get the value at position. Requires read lock + FORCEINLINE FVoxelValue GetValue(int32 X, int32 Y, int32 Z, int32 LOD) const { return Get(X, Y, Z, LOD); } + FORCEINLINE FVoxelValue GetValue(const FIntVector& P , int32 LOD) const { return Get(P, LOD); } + // Get the material at position. Requires read lock + FORCEINLINE FVoxelMaterial GetMaterial(int32 X, int32 Y, int32 Z, int32 LOD) const { return Get(X, Y, Z, LOD); } + FORCEINLINE FVoxelMaterial GetMaterial(const FIntVector& P , int32 LOD) const { return Get(P, LOD); } + + // Set value at position. Requires write lock + FORCEINLINE void SetValue(int32 X, int32 Y, int32 Z, FVoxelValue Value) { Set(X, Y, Z, Value); } + FORCEINLINE void SetValue(const FIntVector& P , FVoxelValue Value) { Set(P, Value); } + // Set material at position. Requires write lock + FORCEINLINE void SetMaterial(int32 X, int32 Y, int32 Z, FVoxelMaterial Material) { Set(X, Y, Z, Material); } + FORCEINLINE void SetMaterial(const FIntVector& P , FVoxelMaterial Material) { Set(P, Material); } + +public: + /** + * Load/Save + */ + + // Get a save of this world. No lock required + void GetSave(FVoxelUncompressedWorldSaveImpl& OutSave, TArray& OutObjects); + + /** + * Load this world from save. No lock required + * @param Save Save to load from + * @param LoadInfo Used to load placeable items. Can use {} + * @param OutBoundsToUpdate The modified bounds + * @return true if loaded successfully, false if the world is corrupted and must not be saved again + */ + bool LoadFromSave(const FVoxelUncompressedWorldSaveImpl& Save, const FVoxelPlaceableItemLoadInfo& LoadInfo, TArray* OutBoundsToUpdate = nullptr); + +public: + /** + * Networking + */ + + /** + * Get diff arrays to allow network transmission. No lock required + * @param OutValueDiffQueue Values diff array; sorted by increasing Id + * @param OutMaterialDiffQueue Materials diff array; sorted by increasing Id + */ + void GetDiffs(TArray>& OutValueDiffQueue, TArray>& OutMaterialDiffQueue); + + /** + * Load values and materials from diffs, and add modified positions. No lock required + * @param ValueDiffQueue Values diff array; sorted by increasing Id + * @param MaterialDiffQueue Materials diff array; sorted by increasing Id + */ + void LoadFromDiffs(TArray>& ValueDiffQueue, TArray>& MaterialDiffQueue, TArray& OutModifiedBounds); + +public: + /** + * Undo/Redo + */ + + // Undo one frame and add it to the redo stack. Current frame must be empty. No lock required + bool Undo(TArray& OutBoundsToUpdate); + // Redo one frame and add it to the undo stack. Current frame must be empty. No lock required + bool Redo(TArray& OutBoundsToUpdate); + // Clear all the frames. No lock required + void ClearFrames(); + // Add the current frame to the undo stack. Clear the redo stack. No lock required. Bounds: must contain all the edits since last SaveFrame + void SaveFrame(const FVoxelIntBox& Bounds); + // Check that the current frame is empty (safe to call Undo/Redo). No lock required + bool IsCurrentFrameEmpty(); + // Get the history position. No lock required + inline int32 GetHistoryPosition() const { return UndoRedo.HistoryPosition; } + // Get the max history position, ie HistoryPosition + redo frames. No lock required + inline int32 GetMaxHistoryPosition() const { return UndoRedo.MaxHistoryPosition; } + + // Dirty state: can use that to track if the data is dirty + // MarkAsDirty is called on Undo, Redo, SaveFrame and ClearData + FORCEINLINE void MarkAsDirty() { bIsDirty = true; } + FORCEINLINE void ClearDirtyFlag() { bIsDirty = false; } + FORCEINLINE bool IsDirty() const { return bIsDirty; } + + // Each save frame call gets assigned a unique ID, can be used to track the state of the world + // Will always be != 0 + FORCEINLINE uint64 GetCurrentFrameUniqueId() const { return UndoRedo.CurrentFrameUniqueId; } + +private: + struct FUndoRedo + { + int32 HistoryPosition = 0; + int32 MaxHistoryPosition = 0; + + TArray UndoFramesBounds; + TArray RedoFramesBounds; + + // Used to clear redo stacks on SaveFrame without iterating the entire octree + // Stack: added when undoing, poping when redoing + TArray> LeavesWithRedoStackStack; + + // Each save frame is assigned a unique ID + uint64 FrameUniqueIdCounter = 2; + uint64 CurrentFrameUniqueId = 1; + TArray UndoUniqueIds; + TArray RedoUniqueIds; + }; + FUndoRedo UndoRedo; + bool bIsDirty = false; + +public: + /** + * Placeable items + */ + + /** Add a FVoxelPlaceableItem to the world. Requires write lock on the item bounds + * @param Args Passed to the constructor of T + * @param bDoNotModifyExistingDataChunks Used when loading from a save + */ + template + TVoxelWeakPtr> AddItem(TArgs&&... Args); + + // Requires write lock on item bounds + template + bool RemoveItem(TVoxelWeakPtr>& Item, FString& OutError); + +private: + template + struct TItemData + { + FCriticalSection Section; + TArray>> Items; + }; + + TItemData AssetItemsData; + TItemData DisableEditsItemsData; + TItemData DataItemsData; + // When adding a new item type also add it to ClearData, AddItem & RemoveItem, ApplyToAllItems, NumItems, NeedToSubdivide + + template + TItemData& GetItemsData(); +}; + +namespace FVoxelDataUtilities +{ + // This will NOT add the item to the item holder + VOXEL_API void AddAssetItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelTransformableGeneratorInstance& Generator, + const FVoxelIntBox& Bounds, + const FTransform& LocalToWorld, + bool bModifyValues, + bool bModifyMaterials); + + // Will update all the values that were the same as the old generator to the new generator values + template + void MigrateLeafDataToNewGenerator( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelIntBox& BoundsToMigrate, + TApplyOld ApplyOldGenerator, + TApplyNew ApplyNewGenerator); + + // This will NOT add the item to the item holder, but will assume it has already been added + template + void AddItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const TItem& Item); + + // This will NOT remove the item from the item holder, but will assume it has already been removed + template + void RemoveItemFromLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const TItem& Item); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.inl new file mode 100644 index 00000000..1500a026 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelData.inl @@ -0,0 +1,532 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelDataOctree.h" +#include "VoxelUtilities/VoxelOctreeUtilities.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" + +#include "Async/ParallelFor.h" + +FORCEINLINE FVoxelDataOctreeBase& FVoxelData::GetOctree() const +{ + return *Octree; +} + +template +void FVoxelData::CacheBounds(const FVoxelIntBox& Bounds, bool bMultiThreaded) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray Leaves; + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Chunk) + { + if (Chunk.IsLeaf()) + { + ensureThreadSafe(Chunk.IsLockedForWrite()); + + auto& Leaf = Chunk.AsLeaf(); + auto& DataHolder = Leaf.GetData(); + if (!DataHolder.HasData()) + { + Leaves.Add(&Leaf); + } + } + else + { + auto& Parent = Chunk.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Chunk.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); + + ParallelFor(Leaves.Num(), [&](int32 Index) + { + FVoxelDataOctreeLeaf& Leaf = *Leaves[Index]; + + auto& DataHolder = Leaf.GetData(); + DataHolder.CreateData(*this, [&](T* RESTRICT DataPtr) + { + TVoxelQueryZone QueryZone(Leaf.GetBounds(), DataPtr); + Leaf.GetFromGeneratorAndAssets(*Generator, QueryZone, 0); + }); + + }, !bMultiThreaded); +} + +template +void FVoxelData::ClearCacheInBounds(const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + FVoxelOctreeUtilities::IterateLeavesInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForWrite()); + + auto& DataHolder = Leaf.GetData(); + if (DataHolder.HasAllocation() && !DataHolder.IsDirty()) + { + DataHolder.ClearData(*this); + } + }); +} + +template +TArray FVoxelData::Get(const FVoxelIntBox& Bounds) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray Result; + Result.Empty(Bounds.Count()); + Result.SetNumUninitialized(Bounds.Count()); + TVoxelQueryZone QueryZone(Bounds, Result); + Get(QueryZone, 0); + return Result; +} + +template +TArray FVoxelData::ParallelGet(const FVoxelIntBox& Bounds, bool bForceSingleThread) const +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + TArray Result; + Result.SetNumUninitialized(Bounds.Count()); + TVoxelQueryZone QueryZone(Bounds, Result); + + Bounds.ParallelSplit([&](const FVoxelIntBox& LocalBounds) + { + auto LocalQueryZone = QueryZone.ShrinkTo(LocalBounds); + Get(LocalQueryZone, 0); + }, bForceSingleThread); + + return Result; +} + +FORCEINLINE bool FVoxelData::IsEmpty(const FVoxelIntBox& Bounds, int32 LOD) const +{ + const auto Range = GetValueRange(Bounds, LOD); + return Range.Min.IsEmpty() == Range.Max.IsEmpty(); +} + +template +FORCEINLINE T FVoxelData::GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const +{ + // Clamp to world, to avoid un-editable border + ClampToWorld(X, Y, Z); + + auto& Node = FVoxelOctreeUtilities::GetBottomNode(GetOctree(), int32(X), int32(Y), int32(Z)); + return Node.GetCustomOutput(*Generator, DefaultValue, Name, X, Y, Z, LOD); +} + +template +void FVoxelData::Set(const FVoxelIntBox& Bounds, F Apply) +{ + if (!ensure(Bounds.IsValid())) return; + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + auto& Leaf = Tree.AsLeaf(); + ensureThreadSafe(Leaf.IsLockedForWrite()); + FVoxelDataOctreeSetter::Set(*this, Leaf, [&](auto Lambda) + { + Leaf.GetBounds().Overlap(Bounds).Iterate(Lambda); + }, Apply); + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); +} + +template +void FVoxelData::ParallelSet(const FVoxelIntBox& Bounds, F Apply, bool bForceSingleThread) +{ + if (!ensure(Bounds.IsValid())) return; + + TArray Leaves; + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + auto& Leaf = Tree.AsLeaf(); + ensureThreadSafe(Leaf.IsLockedForWrite()); + Leaves.Add(&Leaf); + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + Parent.CreateChildren(); + } + } + }); + + ParallelFor(Leaves.Num(), [&](int32 Index) + { + auto& Leaf = *Leaves[Index]; + FVoxelDataOctreeSetter::Set(*this, Leaf, [&](auto Lambda) + { + Leaf.GetBounds().Overlap(Bounds).Iterate(Lambda); + }, Apply); + }, bForceSingleThread); +} + +template +FORCEINLINE void FVoxelData::Set(int32 X, int32 Y, int32 Z, const T& Value) +{ + if (IsInWorld(X, Y, Z)) + { + auto Iterate = [&](auto Lambda) { Lambda(X, Y, Z); }; + auto Apply = [&](int32, int32, int32, T& InValue) { InValue = Value; }; + auto& Leaf = *FVoxelOctreeUtilities::GetLeaf(GetOctree(), X, Y, Z); + FVoxelDataOctreeSetter::Set(*this, Leaf, Iterate, Apply); + } +} + +template +FORCEINLINE T FVoxelData::Get(int32 X, int32 Y, int32 Z, int32 LOD) const +{ + // Clamp to world, to avoid un-editable border + ClampToWorld(X, Y, Z); + + auto& Node = FVoxelOctreeUtilities::GetBottomNode(GetOctree(), int32(X), int32(Y), int32(Z)); + return Node.Get(*Generator, X, Y, Z, LOD); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +namespace FVoxelDataItemsUtilities +{ + // This will NOT add the item to the item holder + template + void AddItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const T& Item) + { + } + // This will NOT remove the item from the item holder, but will assume it has already been removed + template + void RemoveItemFromLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const T& Item) + { + if (!TIsSame::Value && !TIsSame::Value) + { + return; + } + + // Flush cache if possible + if (!Leaf.Values.IsDirty()) + { + Leaf.Values.ClearData(Data); + } + if (!Leaf.Materials.IsDirty()) + { + Leaf.Materials.ClearData(Data); + } + + // If something is still dirty, remove manually + if (Leaf.Values.IsDirty()) + { + FVoxelDataUtilities::RemoveItemFromLeafData(Data, Leaf, Item); + } + if (Leaf.Materials.IsDirty()) + { + FVoxelDataUtilities::RemoveItemFromLeafData(Data, Leaf, Item); + } + } +} + +template<> +inline void FVoxelDataItemsUtilities::AddItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelAssetItem& Item) +{ + // Flush cache if possible + if (!Leaf.Values.IsDirty()) + { + Leaf.Values.ClearData(Data); + } + if (!Leaf.Materials.IsDirty()) + { + Leaf.Materials.ClearData(Data); + } + + // If something is still dirty, merge manually + if (Leaf.Values.IsDirty() || Leaf.Materials.IsDirty()) + { + FVoxelDataUtilities::AddAssetItemToLeafData(Data, Leaf, *Item.Generator, Item.Bounds, Item.LocalToWorld, Leaf.Values.IsDirty(), Leaf.Materials.IsDirty()); + } +} + +template<> +inline void FVoxelDataItemsUtilities::AddItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelDataItem& Item) +{ + // Flush cache if possible + if (!Leaf.Values.IsDirty()) + { + Leaf.Values.ClearData(Data); + } + if (!Leaf.Materials.IsDirty()) + { + Leaf.Materials.ClearData(Data); + } + + // If something is still dirty, merge manually + if (Leaf.Values.IsDirty()) + { + FVoxelDataUtilities::AddItemToLeafData(Data, Leaf, Item); + } + if (Leaf.Materials.IsDirty()) + { + FVoxelDataUtilities::AddItemToLeafData(Data, Leaf, Item); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +TVoxelWeakPtr> FVoxelData::AddItem(TArgs&&... Args) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const auto ItemWrapper = MakeVoxelShared>(); + ItemWrapper->Item = T{ Forward(Args)... }; + ItemWrapper->Data = AsShared(); + + const int32 MaxPlaceableItemsPerOctree = CVarMaxPlaceableItemsPerOctree.GetValueOnAnyThread(); + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), ItemWrapper->Item.Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + ensureThreadSafe(Tree.IsLockedForWrite()); + + Tree.GetItemHolder().AddItem(ItemWrapper->Item); + + if (!bDoNotModifyExistingDataChunks) + { + FVoxelDataItemsUtilities::AddItemToLeafData(*this, Tree.AsLeaf(), ItemWrapper->Item); + } + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + ensureThreadSafe(Parent.IsLockedForWrite()); + // -1: since we're adding a new one + if (Tree.GetItemHolder().NeedToSubdivide(MaxPlaceableItemsPerOctree - 1)) + { + Parent.CreateChildren(); + } + else + { + Tree.GetItemHolder().AddItem(ItemWrapper->Item); + } + } + } + }); + + if (TIsSame::Value) { INC_DWORD_STAT(STAT_NumVoxelAssetItems); } + if (TIsSame::Value) { INC_DWORD_STAT(STAT_NumVoxelDisableEditsItems); } + if (TIsSame::Value) { INC_DWORD_STAT(STAT_NumVoxelDataItems); } + + TItemData& ItemsData = GetItemsData(); + + FScopeLock Lock(&ItemsData.Section); + ItemWrapper->Index = ItemsData.Items.Add(ItemWrapper); + return ItemWrapper; +} + +template +bool FVoxelData::RemoveItem(TVoxelWeakPtr>& InItem, FString& OutError) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const auto Item = InItem.Pin(); + if (!Item.IsValid() || Item->Index == -1) + { + OutError = TEXT("Invalid item, or the item was already deleted"); + return false; + } + if (Item->Data != AsShared()) + { + OutError = TEXT("Item doesn't belong to this data!"); + return false; + } + + TItemData& ItemsData = GetItemsData(); + + { + FScopeLock Lock(&ItemsData.Section); + if (!ensure(ItemsData.Items.IsValidIndex(Item->Index))) + { + return false; + } + if (!ensure(ItemsData.Items[Item->Index] == Item)) + { + return false; + } + } + + FVoxelOctreeUtilities::IterateTreeInBounds(GetOctree(), Item->Item.Bounds, [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeafOrHasNoChildren()) + { + ensureThreadSafe(Tree.IsLockedForWrite()); + + Tree.GetItemHolder().RemoveItem(Item->Item); + + if (Tree.IsLeaf()) + { + FVoxelDataItemsUtilities::RemoveItemFromLeafData(*this, Tree.AsLeaf(), Item->Item); + } + } + }); + + if (TIsSame::Value) { DEC_DWORD_STAT(STAT_NumVoxelAssetItems); } + if (TIsSame::Value) { DEC_DWORD_STAT(STAT_NumVoxelDisableEditsItems); } + if (TIsSame::Value) { DEC_DWORD_STAT(STAT_NumVoxelDataItems); } + + FScopeLock Lock(&ItemsData.Section); + // Make sure our item is the last one + ItemsData.Items.Swap(Item->Index, ItemsData.Items.Num() - 1); + // Fixup the one we swapped (could be us, but that's fine) + ItemsData.Items[Item->Index]->Index = Item->Index; + // Pop the item + ensure(ItemsData.Items.Pop(false) == Item); + // Clear the index, in case we try to remove the item twice + Item->Index = -1; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template<> +inline FVoxelData::TItemData& FVoxelData::GetItemsData() +{ + return AssetItemsData; +} + +template<> +inline FVoxelData::TItemData& FVoxelData::GetItemsData() +{ + return DisableEditsItemsData; +} + +template<> +inline FVoxelData::TItemData& FVoxelData::GetItemsData() +{ + return DataItemsData; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelDataUtilities::MigrateLeafDataToNewGenerator( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const FVoxelIntBox& BoundsToMigrate, + TApplyOld ApplyOldGenerator, + TApplyNew ApplyNewGenerator) +{ + TVoxelDataOctreeLeafData& DataHolder = Leaf.GetData(); + if (!ensure(DataHolder.IsDirty())) + { + return; + } + + const FVoxelIntBox Bounds = Leaf.GetBounds().Overlap(BoundsToMigrate); + const FIntVector Size = Bounds.Size(); + + // Revert to the old generator to query the old data + ApplyOldGenerator(); + + TArray OldGeneratorData; + OldGeneratorData.SetNumUninitialized(Bounds.Count()); + { + TVoxelQueryZone QueryZone(Bounds, OldGeneratorData.GetData()); + Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + } + + // Switch back to the new generator, and query the new data + ApplyNewGenerator(); + + TArray NewGeneratorData; + NewGeneratorData.SetNumUninitialized(Bounds.Count()); + { + TVoxelQueryZone QueryZone(Bounds, NewGeneratorData.GetData()); + Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + } + + // Update all the data that was the same as the old generator to the new generator + FVoxelDataOctreeSetter::Set(Data, Leaf, [&](auto Lambda) { Bounds.Iterate(Lambda); }, + [&](int32 X, int32 Y, int32 Z, T& Value) + { + const int32 Index = FVoxelUtilities::Get3DIndex(Size, X, Y, Z, Bounds.Min); + + if (Value == FVoxelUtilities::Get(OldGeneratorData, Index)) + { + // Switch the edited value to the new value if it wasn't edited + Value = FVoxelUtilities::Get(NewGeneratorData, Index); + } + }); +} + +template +void FVoxelDataUtilities::AddItemToLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const TItem& Item) +{ + MigrateLeafDataToNewGenerator( + Data, + Leaf, + Item.Bounds, + [&]() { ensure(Leaf.GetItemHolder().RemoveItem(Item)); }, + [&]() { Leaf.GetItemHolder().AddItem(Item); }); +} + +template +void FVoxelDataUtilities::RemoveItemFromLeafData( + const FVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + const TItem& Item) +{ + ensureVoxelSlowNoSideEffects(!Leaf.GetItemHolder().RemoveItem(Item)); + MigrateLeafDataToNewGenerator( + Data, + Leaf, + Item.Bounds, + [&]() { Leaf.GetItemHolder().AddItem(Item); }, + [&]() { ensure(Leaf.GetItemHolder().RemoveItem(Item)); }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.h new file mode 100644 index 00000000..8545802a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.h @@ -0,0 +1,172 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" + +class FVoxelData; +class FVoxelDataOctreeLeaf; +class FVoxelDataOctreeBase; + +namespace FVoxelDataAcceleratorParameters +{ + VOXEL_API int32 GetDefaultCacheSize(); + VOXEL_API bool GetUseAcceleratorMap(); + VOXEL_API bool GetShowStats(); +} + +template +class TVoxelDataAccelerator +{ +public: + TData& Data; + const FVoxelIntBox Bounds; + const int32 CacheSize; + const bool bUseAcceleratorMap; + + static constexpr bool bIsConst = TIsConst::Value; + + // Will not build an accelerator map. Access can be done anywhere + explicit TVoxelDataAccelerator(TData& Data, int32 CacheSize = FVoxelDataAcceleratorParameters::GetDefaultCacheSize()); + // Will build an accelerator map. Access can only be done in Bounds + // Map source can't be used when TData is not const + TVoxelDataAccelerator(TData& Data, const FVoxelIntBox& Bounds, const TVoxelDataAccelerator* MapSource = nullptr, int32 CacheSize = FVoxelDataAcceleratorParameters::GetDefaultCacheSize()); + ~TVoxelDataAccelerator(); + + // Copying can mess up the cache + UE_NONCOPYABLE(TVoxelDataAccelerator); + +public: + template + T GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const; + +public: + v_flt GetFloatValue(v_flt X, v_flt Y, v_flt Z, int32 LOD, bool* bIsGeneratorValue = nullptr) const; + + FORCEINLINE v_flt GetFloatValue(const FVoxelVector& P, int32 LOD, bool* bIsGeneratorValue = nullptr) const + { + return GetFloatValue(P.X, P.Y, P.Z, LOD, bIsGeneratorValue); + } + +public: + template + T Get(int32 X, int32 Y, int32 Z, int32 LOD) const; + + template + FORCEINLINE T Get(const FIntVector& P, int32 LOD) const + { + return Get(P.X, P.Y, P.Z, LOD); + } + + FORCEINLINE FVoxelValue GetValue(int32 X, int32 Y, int32 Z, int32 LOD) const { return Get(X, Y, Z, LOD); } + FORCEINLINE FVoxelValue GetValue(const FIntVector& P, int32 LOD) const { return Get(P, LOD); } + + FORCEINLINE FVoxelMaterial GetMaterial(int32 X, int32 Y, int32 Z, int32 LOD) const { return Get(X, Y, Z, LOD); } + FORCEINLINE FVoxelMaterial GetMaterial(const FIntVector& P, int32 LOD) const { return Get(P, LOD); } + +public: + // Returns if value was set, or if it was out of the world + + template + FORCEINLINE bool Set(int32 X, int32 Y, int32 Z, const T& Value) + { + return SetImpl(X, Y, Z, [&](auto& InValue) { InValue = Value; }); + } + template + FORCEINLINE bool Set(const FIntVector& P, const T& Value) + { + return Set(P.X, P.Y, P.Z, Value); + } + + template + FORCEINLINE bool SetValue(int32 X, int32 Y, int32 Z, FVoxelValue Value) { return Set(X, Y, Z, Value); } + template + FORCEINLINE bool SetValue(const FIntVector& P, FVoxelValue Value) { return Set(P, Value); } + + template + FORCEINLINE bool SetMaterial(int32 X, int32 Y, int32 Z, FVoxelMaterial Material) { return Set(X, Y, Z, Material); } + template + FORCEINLINE bool SetMaterial(const FIntVector& P, FVoxelMaterial Material) { return Set(P, Material); } + +public: + template + FORCEINLINE bool Edit(int32 X, int32 Y, int32 Z, TLambda Lambda) + { + return SetImpl(X, Y, Z, Lambda); + } + template + FORCEINLINE bool Edit(const FIntVector& P, TLambda Lambda) + { + return Edit(P.X, P.Y, P.Z, Lambda); + } + + template + FORCEINLINE bool EditValue(int32 X, int32 Y, int32 Z, TLambda Lambda) { return Edit(X, Y, Z, Lambda); } + template + FORCEINLINE bool EditValue(const FIntVector& P, TLambda Lambda) { return Edit(P, Lambda); } + + template + FORCEINLINE bool EditMaterial(int32 X, int32 Y, int32 Z, TLambda Lambda) { return Edit(X, Y, Z, Lambda); } + template + FORCEINLINE bool EditMaterial(const FIntVector& P, TLambda Lambda) { return Edit(P, Lambda); } + +private: + struct FCacheEntry + { + FVoxelDataOctreeBase* Octree; + uint64 LastAccessTime; + }; + mutable TNoGrowArray CacheEntries; + mutable uint64 GlobalTime = 0; + +#if VOXEL_DATA_ACCELERATOR_STATS + mutable uint32 NumGet = 0; + mutable uint32 NumSet = 0; + + mutable uint32 NumCacheTopAccess = 0; + mutable uint32 NumCacheTopMiss = 0; + + mutable uint32 NumCacheAllAccess = 0; + mutable uint32 NumCacheAllMiss = 0; + + mutable uint32 NumMapAccess = 0; + mutable uint32 NumMapMiss = 0; + + mutable uint32 NumOutOfWorld = 0; +#endif + + using FAcceleratorMap = TMap; + using FConstAcceleratorMap = typename TChooseClass::Result; + + // Map from Leaf.GetMin() to &Leaf + const TVoxelSharedPtr AcceleratorMap; + + template + auto GetImpl(int32 X, int32 Y, int32 Z, T UseOctree) const; + + template + bool SetImpl(int32 X, int32 Y, int32 Z, TLambda EditValue) const; + + FVoxelDataOctreeBase* GetOctreeFromCache_CheckTopOnly(int32 X, int32 Y, int32 Z) const; + FVoxelDataOctreeBase* GetOctreeFromCache_CheckAll(int32 X, int32 Y, int32 Z) const; + FVoxelDataOctreeBase* GetOctreeFromMap(int32 X, int32 Y, int32 Z) const; + + void StoreOctreeInCache(FVoxelDataOctreeBase& Octree) const; + + static TVoxelSharedRef GetAcceleratorMap(const FVoxelData& Data, const FVoxelIntBox& Bounds); +}; + +class FVoxelMutableDataAccelerator : public TVoxelDataAccelerator +{ +public: + using TVoxelDataAccelerator::TVoxelDataAccelerator; +}; + +class FVoxelConstDataAccelerator : public TVoxelDataAccelerator +{ +public: + using TVoxelDataAccelerator::TVoxelDataAccelerator; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.inl new file mode 100644 index 00000000..3ed9d12f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.inl @@ -0,0 +1,346 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelData.inl" +#include "VoxelData/VoxelDataUtilities.h" + +#if VOXEL_DATA_ACCELERATOR_STATS +#define ACCELERATOR_STAT(X) X +#else +#define ACCELERATOR_STAT(X) +#endif + +template +TVoxelDataAccelerator::TVoxelDataAccelerator(TData& Data, int32 CacheSize) + : Data(Data) + , Bounds(FVoxelIntBox::Infinite) + , CacheSize(CacheSize) + , bUseAcceleratorMap(false) +{ + CacheEntries.Reserve(CacheSize); +} + +template +TVoxelDataAccelerator::TVoxelDataAccelerator(TData& Data, const FVoxelIntBox& Bounds, const TVoxelDataAccelerator* MapSource, int32 CacheSize) + : Data(Data) + , Bounds(Bounds) + , CacheSize(CacheSize) + , bUseAcceleratorMap(FVoxelDataAcceleratorParameters::GetUseAcceleratorMap()) + , AcceleratorMap(MapSource ? MapSource->AcceleratorMap : GetAcceleratorMap(Data, Bounds)) +{ + check(!MapSource || bIsConst); + ensure(!MapSource || bUseAcceleratorMap == MapSource->bUseAcceleratorMap); + CacheEntries.Reserve(CacheSize); +} + +template +TVoxelDataAccelerator::~TVoxelDataAccelerator() +{ +#if VOXEL_DATA_ACCELERATOR_STATS + if (FVoxelDataAcceleratorParameters::GetShowStats() && (NumGet > 0 || NumSet > 0)) + { + LOG_VOXEL( + Log, + TEXT("DataAccelerator: %6u reads; %6u writes; %6u/%6u top cache miss (%3.2f%% hits); %6u/%6u other cache miss (%3.2f%% hits); %6u/%6u map miss (%3.2f%% hits); %6u out of world"), + NumGet, + NumSet, + NumCacheTopMiss, + NumCacheTopAccess, + NumCacheTopAccess > 0 ? 100 * double(NumCacheTopAccess - NumCacheTopMiss) / NumCacheTopAccess : 0, + NumCacheAllMiss, + NumCacheAllAccess, + NumCacheAllAccess > 0 ? 100 * double(NumCacheAllAccess - NumCacheAllMiss) / NumCacheAllAccess : 0, + NumMapMiss, + NumMapAccess, + NumMapAccess > 0 ? 100 * double(NumMapAccess - NumMapMiss) / NumMapAccess : 0, + NumOutOfWorld); + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +template +FORCEINLINE T TVoxelDataAccelerator::GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const +{ + // Clamp to world, to avoid un-editable border + Data.ClampToWorld(X, Y, Z); + + return GetImpl(X, Y, Z, + [&](const FVoxelDataOctreeBase& Octree) + { + return Octree.GetCustomOutput(*Data.Generator, DefaultValue, Name, X, Y, Z, LOD); + }); +} + +template +FORCEINLINE v_flt TVoxelDataAccelerator::GetFloatValue(v_flt X, v_flt Y, v_flt Z, int32 LOD, bool* bIsGeneratorValue) const +{ + // Clamp to world, to avoid un-editable border + Data.ClampToWorld(X, Y, Z); + + return GetImpl(int32(X), int32(Y), int32(Z), + [&](const FVoxelDataOctreeBase& Octree) + { + if (Octree.IsLeaf() && Octree.AsLeaf().GetData().HasData()) + { + if (bIsGeneratorValue) *bIsGeneratorValue = false; + return FVoxelDataUtilities::MakeBilinearInterpolatedData(*this).GetValue(X, Y, Z, LOD); + } + else + { + if (bIsGeneratorValue) *bIsGeneratorValue = true; + return Octree.GetFromGeneratorAndAssets(*Data.Generator, X, Y, Z, LOD); + } + }); +} + +template +template +FORCEINLINE T TVoxelDataAccelerator::Get(int32 X, int32 Y, int32 Z, int32 LOD) const +{ + // Clamp to world, to avoid un-editable border + Data.ClampToWorld(X, Y, Z); + + return GetImpl(X, Y, Z, + [&](const FVoxelDataOctreeBase& Octree) + { + return Octree.Get(*Data.Generator, X, Y, Z, LOD); + }); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +template +auto TVoxelDataAccelerator::GetImpl(int32 X, int32 Y, int32 Z, T UseOctree) const +{ + ACCELERATOR_STAT(NumGet++); + + // Each caller should clamp the coordinates + ensureVoxelSlow(Data.IsInWorld(X, Y, Z)); + + ensureMsgfVoxelSlowNoSideEffects(!bUseAcceleratorMap || Bounds.Contains(X, Y, Z), TEXT("(%d, %d, %d) is not in %s!"), X, Y, Z, *Bounds.ToString()); + + FVoxelDataOctreeBase* Octree = GetOctreeFromCache_CheckTopOnly(X, Y, Z); + + if (Octree) + { + // Fast path: top cache is a hit + return UseOctree(*Octree); + } + + Octree = GetOctreeFromCache_CheckAll(X, Y, Z); + checkVoxelSlow(!Octree || Octree->IsInOctree(X, Y, Z)); + + if (Octree) + { + // Cache hit + return UseOctree(*Octree); + } + + if (bUseAcceleratorMap) + { + Octree = GetOctreeFromMap(X, Y, Z); + } + + if (!Octree) + { + // Need to get the octree for ItemHolders, even if there is no leaf + Octree = &FVoxelOctreeUtilities::GetBottomNode(Data.GetOctree(), X, Y, Z); + } + + checkVoxelSlow(Octree); + StoreOctreeInCache(*Octree); + + return UseOctree(*Octree); +} + +template +template +bool TVoxelDataAccelerator::SetImpl(int32 X, int32 Y, int32 Z, TLambda EditValue) const +{ + static_assert(!bIsConst, "Calling Set on a const data accelerator!"); + + ACCELERATOR_STAT(NumSet++); + + ensureVoxelSlowNoSideEffects(!bUseAcceleratorMap || Bounds.Contains(X, Y, Z)); + + FVoxelDataOctreeBase* Octree; + + const auto DoSet = [&]() + { + checkVoxelSlow(Octree); + checkVoxelSlow(Octree->IsLeaf()); + checkVoxelSlow(Octree->IsInOctree(X, Y, Z)); + auto Iterate = [&](auto Lambda) { Lambda(X, Y, Z); }; + auto Apply = [&](int32, int32, int32, T& Value) { EditValue(Value); }; + FVoxelDataOctreeSetter::Set(Data, Octree->AsLeaf(), Iterate, Apply); + }; + + Octree = GetOctreeFromCache_CheckTopOnly(X, Y, Z); + + // Set must be applied on leaves + if (Octree && Octree->IsLeaf()) + { + // Fast path: top cache is a hit + DoSet(); + return true; + } + + Octree = GetOctreeFromCache_CheckAll(X, Y, Z); + + // Set must be applied on leaves + if (!Octree || !Octree->IsLeaf()) + { + // No need to check IsInWorld if we get a hit, so check now instead + if (!Data.IsInWorld(X, Y, Z)) + { + ACCELERATOR_STAT(NumOutOfWorld++); + return false; + } + + if (bUseAcceleratorMap) + { + Octree = GetOctreeFromMap(X, Y, Z); + } + + if (!Octree) + { + auto& Node = FVoxelOctreeUtilities::GetBottomNode(Data.GetOctree(), X, Y, Z); + // Not true if it was edited outside of this accelerator // ensureVoxelSlowNoSideEffects(!bUseAcceleratorMap || !Node.IsLeaf()); + Octree = FVoxelOctreeUtilities::GetLeaf(Node, X, Y, Z); + + if (bUseAcceleratorMap) + { + const FIntVector HashPosition = FVoxelUtilities::DivideFloor(FIntVector(X, Y, Z), DATA_CHUNK_SIZE); + ensureVoxelSlowNoSideEffects(AcceleratorMap.IsUnique()); + AcceleratorMap->Add(HashPosition, &Octree->AsLeaf()); + } + } + + StoreOctreeInCache(*Octree); + } + checkVoxelSlow(Octree); + checkVoxelSlow(Octree->IsLeaf()); + + DoSet(); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE FVoxelDataOctreeBase* TVoxelDataAccelerator::GetOctreeFromCache_CheckTopOnly(int32 X, int32 Y, int32 Z) const +{ + ACCELERATOR_STAT(NumCacheTopAccess++); + if (CacheEntries.Num() == 0) + { + ACCELERATOR_STAT(NumCacheTopMiss++); + return nullptr; + } + + auto* Octree = CacheEntries.GetData()[0].Octree; + // If we are const, octrees are not allowed to change and have children + ensureVoxelSlowNoSideEffects(!bIsConst || Octree->IsLeafOrHasNoChildren()); + if (Octree->IsInOctree(X, Y, Z) && (bIsConst || Octree->IsLeafOrHasNoChildren())) + { + return Octree; + } + else + { + ACCELERATOR_STAT(NumCacheTopMiss++); + return nullptr; + } +} + +template +FVoxelDataOctreeBase* TVoxelDataAccelerator::GetOctreeFromCache_CheckAll(int32 X, int32 Y, int32 Z) const +{ + checkVoxelSlow(!GetOctreeFromCache_CheckTopOnly(X, Y, Z) || !GetOctreeFromCache_CheckTopOnly(X, Y, Z)->IsLeaf()); + ACCELERATOR_STAT(NumCacheAllAccess++); + for (int32 Index = 1; Index < CacheEntries.Num(); Index++) + { + ensureVoxelSlowNoSideEffects(CacheEntries[Index - 1].LastAccessTime > CacheEntries[Index].LastAccessTime); + } + for (int32 Index = 1; Index < CacheEntries.Num(); Index++) + { + auto& CacheEntry = CacheEntries.GetData()[Index]; + checkVoxelSlow(CacheEntry.Octree); + ensureVoxelSlowNoSideEffects(!bIsConst || CacheEntry.Octree->IsLeafOrHasNoChildren()); + if (CacheEntry.Octree->IsInOctree(X, Y, Z) && (bIsConst || CacheEntry.Octree->IsLeafOrHasNoChildren())) + { + CacheEntry.LastAccessTime = ++GlobalTime; + auto* Octree = CacheEntry.Octree; // Need to cache the ptr, as CacheEntry is going to be modified during the sorting + + // Sort the cache entries + for (int32 SortIndex = Index; SortIndex > 0; SortIndex--) + { + CacheEntries.SwapMemory(SortIndex, SortIndex - 1); + } + + return Octree; + } + } + ACCELERATOR_STAT(NumCacheAllMiss++); + return nullptr; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +FVoxelDataOctreeBase* TVoxelDataAccelerator::GetOctreeFromMap(int32 X, int32 Y, int32 Z) const +{ + checkVoxelSlow(bUseAcceleratorMap); + + ACCELERATOR_STAT(NumMapAccess++); + const FIntVector HashPosition = FVoxelUtilities::DivideFloor(FIntVector(X, Y, Z), DATA_CHUNK_SIZE); + auto* Result = AcceleratorMap->FindRef(HashPosition); + ACCELERATOR_STAT(if (!Result) NumMapMiss++); + return Result; +} + +template +void TVoxelDataAccelerator::StoreOctreeInCache(FVoxelDataOctreeBase& Octree) const +{ + if (CacheSize <= 0) return; + FCacheEntry CacheEntry; + CacheEntry.Octree = &Octree; + CacheEntry.LastAccessTime = ++GlobalTime; + if (CacheEntries.Num() == CacheSize) + { + // Limit cache size + CacheEntries.Pop(false); + } + CacheEntries.Insert(CacheEntry, 0); +} + +template +TVoxelSharedRef> TVoxelDataAccelerator::GetAcceleratorMap(const FVoxelData& Data, const FVoxelIntBox& Bounds) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const auto AcceleratorMap = MakeVoxelShared(); + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](auto& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForRead()); + AcceleratorMap->Add(Leaf.GetMin() / DATA_CHUNK_SIZE, &Leaf); + }); + AcceleratorMap->Compact(); + return AcceleratorMap; +} + +#undef ACCELERATOR_STAT \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.h new file mode 100644 index 00000000..ff2fb94f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.h @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class FVoxelData; +struct FVoxelIntBox; + +struct FModifiedValueDummy +{ + template + FModifiedValueDummy(TArgs...) {} +}; + +template +class TVoxelDataImpl +{ +public: + FVoxelData& Data; + const bool bMultiThreadedEdits; + const bool bRecordModifiedValues; + TArray ModifiedValues; + TArray OtherModifiedValues; + + explicit TVoxelDataImpl(FVoxelData& Data, bool bMultiThreadedEdits, bool bRecordModifiedValues) + : Data(Data) + , bMultiThreadedEdits(bMultiThreadedEdits) + , bRecordModifiedValues(bRecordModifiedValues) + { + } + + template + void Set(const FVoxelIntBox& Bounds, TLambda Lambda); + + template + void Set(const FVoxelIntBox& Bounds, TLambda Lambda); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.inl new file mode 100644 index 00000000..180082c8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataImpl.inl @@ -0,0 +1,122 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelDataImpl.h" +#include "VoxelData/VoxelData.inl" + +template +template +void TVoxelDataImpl::Set(const FVoxelIntBox& Bounds, TLambda Lambda) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (bRecordModifiedValues) + { + FThreadSafeCounter Counter = ModifiedValues.AddUninitialized(Bounds.Count()); + + const auto RecordingLambda = [&](int32 X, int32 Y, int32 Z, T& Value) + { + const T OldValue = Value; + Lambda(X, Y, Z, Value); + const T& NewValue = Value; + + if (OldValue != NewValue) + { + const int32 Index = Counter.Increment() - 1; + if (ensureVoxelSlow(Index < ModifiedValues.Num())) + { + checkVoxelSlow(0 <= Index); + ModifiedValues.GetData()[Index] = TModifiedValue{ FIntVector(X, Y, Z), OldValue, NewValue }; + } + } + }; + + if (bMultiThreadedEdits) + { + Data.ParallelSet(Bounds, RecordingLambda); + } + else + { + Data.Set(Bounds, RecordingLambda); + } + + ModifiedValues.SetNum(Counter.GetValue()); + } + else + { + if (bMultiThreadedEdits) + { + Data.ParallelSet(Bounds, Lambda); + } + else + { + Data.Set(Bounds, Lambda); + } + } +} + +template +template +void TVoxelDataImpl::Set(const FVoxelIntBox& Bounds, TLambda Lambda) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + if (bRecordModifiedValues) + { + FThreadSafeCounter CounterA = ModifiedValues.AddUninitialized(Bounds.Count()); + FThreadSafeCounter CounterB = OtherModifiedValues.AddUninitialized(Bounds.Count()); + + const auto RecordingLambda = [&](int32 X, int32 Y, int32 Z, TA& ValueA, TB& ValueB) + { + const TA OldValueA = ValueA; + const TB OldValueB = ValueB; + Lambda(X, Y, Z, ValueA, ValueB); + const TA& NewValueA = ValueA; + const TB& NewValueB = ValueB; + + if (OldValueA != NewValueA) + { + const int32 Index = CounterA.Increment() - 1; + if (ensureVoxelSlow(Index < ModifiedValues.Num())) + { + checkVoxelSlow(0 <= Index); + ModifiedValues.GetData()[Index] = TModifiedValue{ FIntVector(X, Y, Z), OldValueA, NewValueA }; + } + } + if (OldValueB != NewValueB) + { + const int32 Index = CounterB.Increment() - 1; + if (ensureVoxelSlow(Index < OtherModifiedValues.Num())) + { + checkVoxelSlow(0 <= Index); + OtherModifiedValues.GetData()[Index] = TOtherModifiedValue{ FIntVector(X, Y, Z), OldValueB, NewValueB }; + } + } + }; + + if (bMultiThreadedEdits) + { + Data.ParallelSet(Bounds, RecordingLambda); + } + else + { + Data.Set(Bounds, RecordingLambda); + } + + ModifiedValues.SetNum(CounterA.GetValue()); + OtherModifiedValues.SetNum(CounterB.GetValue()); + } + else + { + if (bMultiThreadedEdits) + { + Data.ParallelSet(Bounds, Lambda); + } + else + { + Data.Set(Bounds, Lambda); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataIncludes.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataIncludes.h new file mode 100644 index 00000000..98937af0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataIncludes.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelData.inl" +#include "VoxelData/VoxelDataLock.h" +#include "VoxelData/VoxelDataImpl.inl" +#include "VoxelData/VoxelDataUtilities.inl" +#include "VoxelData/VoxelDataAccelerator.inl" \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataLock.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataLock.h new file mode 100644 index 00000000..dc0aabb2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataLock.h @@ -0,0 +1,108 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelData.h" +#include "VoxelOctreeId.h" + +class FVoxelDataLockInfo +{ +public: + ~FVoxelDataLockInfo() + { + checkf(LockedOctrees.Num() == 0, TEXT("Data not unlocked by %s!"), *Name.ToString()); + } + + FVoxelDataLockInfo(const FVoxelDataLockInfo&) = delete; + FVoxelDataLockInfo& operator=(const FVoxelDataLockInfo&) = delete; + +private: + FVoxelDataLockInfo() = default; + + FName Name; + EVoxelLockType LockType = EVoxelLockType::Read; + TArray LockedOctrees; // In depth first order + + friend class FVoxelData; +}; + +template +class TVoxelScopeLock +{ +public: + using TData = typename TChooseClass::Result; + + TVoxelScopeLock(TData& InData, const FVoxelIntBox& Bounds, const FName& Name, bool bCondition = true) + : Data(InData) + { + if (bCondition) + { + LockInfo = Data.Lock(LockType, Bounds, Name); + } + } + ~TVoxelScopeLock() + { + if (LockInfo.IsValid()) + { + Unlock(); + } + } + + void Unlock() + { + check(LockInfo.IsValid()); + Data.Unlock(MoveTemp(LockInfo)); + } + +private: + const FVoxelData& Data; + TUniquePtr LockInfo; +}; + +class FVoxelReadScopeLock : public TVoxelScopeLock +{ + using TVoxelScopeLock::TVoxelScopeLock; +}; +class FVoxelWriteScopeLock : public TVoxelScopeLock +{ + using TVoxelScopeLock::TVoxelScopeLock; +}; + +// Read lock that can be promoted to a write lock +class FVoxelPromotableReadScopeLock +{ +public: + FVoxelPromotableReadScopeLock(FVoxelData& Data, const FVoxelIntBox& Bounds, const FName& Name) + : Data(Data) + , Bounds(Bounds) + , Name(Name) + { + LockInfo = Data.Lock(EVoxelLockType::Read, Bounds, Name); + } + ~FVoxelPromotableReadScopeLock() + { + Data.Unlock(MoveTemp(LockInfo)); + } + + FORCEINLINE bool IsPromoted() const + { + return bPromoted; + } + void Promote() + { + check(!bPromoted); + bPromoted = true; + + Data.Unlock(MoveTemp(LockInfo)); + LockInfo = Data.Lock(EVoxelLockType::Write, Bounds, Name); + } + +private: + const FVoxelData& Data; + const FVoxelIntBox Bounds; + const FName Name; + + bool bPromoted = false; + TUniquePtr LockInfo; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctree.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctree.h new file mode 100644 index 00000000..deaa1bbc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctree.h @@ -0,0 +1,353 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelOctree.h" +#include "VoxelQueryZone.h" +#include "VoxelSharedMutex.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" +#include "VoxelData/VoxelDataOctreeLeafData.h" +#include "VoxelData/VoxelDataOctreeLeafUndoRedo.h" +#include "VoxelData/VoxelDataOctreeLeafMultiplayer.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Data Octrees Memory"), STAT_VoxelDataOctreesMemory, STATGROUP_VoxelMemory, VOXEL_API); +DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Voxel Data Octrees Count"), STAT_VoxelDataOctreesCount, STATGROUP_VoxelCounters, VOXEL_API); + +namespace FVoxelDataOctreeUtilities +{ + FORCEINLINE FVoxelCellIndex IndexFromCoordinates(int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(0 <= X && X < DATA_CHUNK_SIZE && 0 <= Y && Y < DATA_CHUNK_SIZE && 0 <= Z && Z < DATA_CHUNK_SIZE); + return X + DATA_CHUNK_SIZE * Y + DATA_CHUNK_SIZE * DATA_CHUNK_SIZE * Z; + } + FORCEINLINE FIntVector CoordinatesFromIndex(FVoxelCellIndex Index) + { + return + { + Index % DATA_CHUNK_SIZE, + (Index / DATA_CHUNK_SIZE) % DATA_CHUNK_SIZE, + (Index / (DATA_CHUNK_SIZE * DATA_CHUNK_SIZE)) + }; + } + FORCEINLINE FVoxelCellIndex IndexFromGlobalCoordinates(const FIntVector& Min, int32 X, int32 Y, int32 Z) + { + X -= Min.X; + Y -= Min.Y; + Z -= Min.Z; + return IndexFromCoordinates(X, Y, Z); + } +} + +class VOXEL_API FVoxelDataOctreeBase : public TVoxelOctreeBase +{ +public: + FVoxelDataOctreeBase(uint8 Height, const FIntVector& Position) + : TVoxelOctreeBase(Height, Position) + { + INC_DWORD_STAT_BY(STAT_VoxelDataOctreesCount, 1); + } + ~FVoxelDataOctreeBase() + { + DEC_DWORD_STAT_BY(STAT_VoxelDataOctreesCount, 1); + } + +public: + class FVoxelDataOctreeParent& AsParent(); + const FVoxelDataOctreeParent& AsParent() const; + + class FVoxelDataOctreeLeaf& AsLeaf(); + const FVoxelDataOctreeLeaf& AsLeaf() const; + + bool IsLeafOrHasNoChildren() const; + +public: + template + T Get(const FVoxelGeneratorInstance& Generator, int32 X, int32 Y, int32 Z, int32 LOD) const; + template + T GetCustomOutput(const FVoxelGeneratorInstance& Generator, T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const; + template + T GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, U X, U Y, U Z, int32 LOD) const; + template + void GetFromGeneratorAndAssets(const FVoxelGeneratorInstance& Generator, TVoxelQueryZone& QueryZone, int32 LOD) const; + +public: +#if DO_THREADSAFE_CHECKS + bool IsLockedForRead() const { return Mutex.IsLockedForRead() || (Parent && Parent->IsLockedForRead()); } + bool IsLockedForWrite() const { return Mutex.IsLockedForWrite() || (Parent && Parent->IsLockedForWrite()); } +#endif + +public: + FVoxelPlaceableItemHolder& GetItemHolder() { return *ItemHolder; } + const FVoxelPlaceableItemHolder& GetItemHolder() const { return *ItemHolder; } + +private: + // Always valid on a node with no children + TUniquePtr ItemHolder = MakeUnique(); + FVoxelSharedMutex Mutex; +#if DO_THREADSAFE_CHECKS + FVoxelDataOctreeBase* Parent = nullptr; +#endif + + friend class FVoxelDataOctreeLocker; + friend class FVoxelDataOctreeUnlocker; + friend class FVoxelDataOctreeParent; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelDataOctreeLeaf : public TVoxelOctreeLeaf +{ +public: + FVoxelDataOctreeLeaf(const FVoxelDataOctreeBase& Parent, uint8 ChildIndex) + : TVoxelOctreeLeaf(Parent, ChildIndex) + { + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreesMemory, sizeof(FVoxelDataOctreeLeaf)); + } + ~FVoxelDataOctreeLeaf() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreesMemory, sizeof(FVoxelDataOctreeLeaf)); + } + + TVoxelDataOctreeLeafData Values; + TVoxelDataOctreeLeafData Materials; + + TUniquePtr UndoRedo; + TUniquePtr Multiplayer; + +public: + template + FORCEINLINE void InitForEdit(const IVoxelData& Data) + { + using T = typename TRemoveConst::Type; + + TVoxelDataOctreeLeafData& DataHolder = GetData(); + if (!DataHolder.HasData()) + { + DataHolder.CreateData(Data, [&](T* RESTRICT DataPtr) + { + TVoxelQueryZone QueryZone(GetBounds(), DataPtr); + GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + }); + } + DataHolder.PrepareForWrite(Data); + + if (!TIsConst::Value) + { + if (Data.bEnableMultiplayer && !Multiplayer.IsValid()) + { + Multiplayer = MakeUnique(); + } + if (Data.bEnableUndoRedo && !UndoRedo.IsValid()) + { + UndoRedo = MakeUnique(*this); + } + } + } + +public: + template FORCEINLINE TVoxelDataOctreeLeafData::Type>& GetData() { return FVoxelUtilities::TValuesMaterialsSelector::Get(*this); } + template FORCEINLINE const TVoxelDataOctreeLeafData::Type>& GetData() const { return FVoxelUtilities::TValuesMaterialsSelector::Get(*this); } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelDataOctreeParent : public TVoxelOctreeParent +{ +public: + explicit FVoxelDataOctreeParent(uint8 Height) + : TVoxelOctreeParent(Height) + { + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreesMemory, sizeof(FVoxelDataOctreeParent)); + } + ~FVoxelDataOctreeParent() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreesMemory, sizeof(FVoxelDataOctreeParent)); + } + FVoxelDataOctreeParent(const FVoxelDataOctreeParent& Parent, uint8 ChildIndex) + : TVoxelOctreeParent(Parent, ChildIndex) + { + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreesMemory, sizeof(FVoxelDataOctreeParent)); + } + + void CreateChildren(); + void DestroyChildren(); +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelDataOctreeSetter +{ + template + static void Set( + const IVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + T1 Iterate, T2 Apply) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + ensureThreadSafe(Leaf.IsLockedForWrite()); + + const auto& DisableEditsBoxes = Leaf.GetItemHolder().GetDisableEditsBoxItems(); + + const auto DoWork = [&](auto NeedToCheckCanEdit, auto EnableMultiplayer, auto EnableUndoRedo) + { + Leaf.InitForEdit(Data); + + const FIntVector Min = Leaf.GetMin(); + auto& DataHolder = Leaf.GetData(); + + Iterate([&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(Leaf.IsInOctree(X, Y, Z)); + if (NeedToCheckCanEdit) + { + for (auto* Item : DisableEditsBoxes) + { + if (Item->Bounds.Contains(X, Y, Z)) + { + return; + } + } + } + + const uint32 Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(Min, X, Y, Z); + T& Ref = DataHolder.GetRef(Index); + T OldValue = Ref; + + Apply(X, Y, Z, Ref); + + if (OldValue != Ref) + { + DataHolder.SetIsDirty(true, Data); + if (EnableMultiplayer) Leaf.Multiplayer->MarkIndexDirty(Index); + if (EnableUndoRedo) Leaf.UndoRedo->SavePreviousValue(Index, OldValue); + } + }); + }; + + FVoxelUtilities::StaticBranch(DisableEditsBoxes.Num() > 0, Data.bEnableMultiplayer, Data.bEnableUndoRedo, DoWork); + } + template + static void Set( + const IVoxelData& Data, + FVoxelDataOctreeLeaf& Leaf, + T1 Iterate, T2 Apply) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + ensureThreadSafe(Leaf.IsLockedForWrite()); + + const auto& DisableEditsBoxes = Leaf.GetItemHolder().GetDisableEditsBoxItems(); + + const auto DoWork = [&](auto NeedToCheckCanEdit, auto EnableMultiplayer, auto EnableUndoRedo) + { + Leaf.InitForEdit(Data); + Leaf.InitForEdit(Data); + + const FIntVector Min = Leaf.GetMin(); + auto& DataHolderA = Leaf.GetData(); + auto& DataHolderB = Leaf.GetData(); + + Iterate([&](int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(Leaf.IsInOctree(X, Y, Z)); + if (NeedToCheckCanEdit) + { + for (auto* Item : DisableEditsBoxes) + { + if (Item->Bounds.Contains(X, Y, Z)) + { + return; + } + } + } + + const uint32 Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(Min, X, Y, Z); + + TA& RefA = DataHolderA.GetRef(Index); + TB& RefB = DataHolderB.GetRef(Index); + TA OldValueA = RefA; + TB OldValueB = RefB; + + Apply(X, Y, Z, RefA, RefB); + + if (OldValueA != RefA) + { + DataHolderA.SetIsDirty(true, Data); + if (EnableMultiplayer) Leaf.Multiplayer->MarkIndexDirty(Index); + if (EnableUndoRedo) Leaf.UndoRedo->SavePreviousValue(Index, OldValueA); + } + if (OldValueB != RefB) + { + DataHolderB.SetIsDirty(true, Data); + if (EnableMultiplayer) Leaf.Multiplayer->MarkIndexDirty(Index); + if (EnableUndoRedo) Leaf.UndoRedo->SavePreviousValue(Index, OldValueB); + } + }); + }; + + FVoxelUtilities::StaticBranch(DisableEditsBoxes.Num() > 0, Data.bEnableMultiplayer, Data.bEnableUndoRedo, DoWork); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE FVoxelDataOctreeParent& FVoxelDataOctreeBase::AsParent() +{ + checkVoxelSlow(!IsLeaf()); + return static_cast(*this); +} + +FORCEINLINE const FVoxelDataOctreeParent& FVoxelDataOctreeBase::AsParent() const +{ + checkVoxelSlow(!IsLeaf()); + return static_cast(*this); +} + +FORCEINLINE FVoxelDataOctreeLeaf& FVoxelDataOctreeBase::AsLeaf() +{ + checkVoxelSlow(IsLeaf()); + return static_cast(*this); +} + +FORCEINLINE const FVoxelDataOctreeLeaf& FVoxelDataOctreeBase::AsLeaf() const +{ + checkVoxelSlow(IsLeaf()); + return static_cast(*this); +} + +FORCEINLINE bool FVoxelDataOctreeBase::IsLeafOrHasNoChildren() const +{ + return IsLeaf() || !AsParent().HasChildren(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +T FVoxelDataOctreeBase::Get(const FVoxelGeneratorInstance& Generator, int32 X, int32 Y, int32 Z, int32 LOD) const +{ + checkVoxelSlow(IsLeafOrHasNoChildren()); + ensureThreadSafe(IsLockedForRead()); + if (IsLeaf()) + { + auto& Data = AsLeaf().GetData(); + if (Data.HasData()) + { + return Data.Get(FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(GetMin(), X, Y, Z)); + } + } + return GetFromGeneratorAndAssets(Generator, X, Y, Z, LOD); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafData.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafData.h new file mode 100644 index 00000000..33608b16 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafData.h @@ -0,0 +1,727 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelData/IVoxelData.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Dirty Values Memory"), STAT_VoxelDataOctreeDirtyValuesMemory, STATGROUP_VoxelMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Dirty Materials Memory"), STAT_VoxelDataOctreeDirtyMaterialsMemory, STATGROUP_VoxelMemory, VOXEL_API); + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Cached Values Memory"), STAT_VoxelDataOctreeCachedValuesMemory, STATGROUP_VoxelMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Cached Materials Memory"), STAT_VoxelDataOctreeCachedMaterialsMemory, STATGROUP_VoxelMemory, VOXEL_API); + +template +struct TVoxelDataOctreeLeafMemoryUsage +{ + static_assert(TIsSame::Value || TIsSame::Value, ""); + + static void Increase(int32 MemorySize, bool bDirty, const IVoxelDataOctreeMemory& Memory) + { + if (bDirty) + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.DirtyMemory).Add(MemorySize); + + if (TIsSame::Value) { INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeDirtyValuesMemory , MemorySize); } + if (TIsSame::Value) { INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeDirtyMaterialsMemory, MemorySize); } + } + else + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.CachedMemory).Add(MemorySize); + + if (TIsSame::Value) { INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeCachedValuesMemory , MemorySize); } + if (TIsSame::Value) { INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeCachedMaterialsMemory, MemorySize); } + } + } + static void Decrease(int32 MemorySize, bool bDirty, const IVoxelDataOctreeMemory& Memory) + { + if (bDirty) + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.DirtyMemory).Subtract(MemorySize); + ensureVoxelSlowNoSideEffects(FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.DirtyMemory).GetValue() >= 0); + + if (TIsSame::Value) { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeDirtyValuesMemory , MemorySize); } + if (TIsSame::Value) { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeDirtyMaterialsMemory, MemorySize); } + } + else + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.CachedMemory).Subtract(MemorySize); + ensureVoxelSlowNoSideEffects(FVoxelUtilities::TValuesMaterialsSelector::Get(Memory.CachedMemory).GetValue() >= 0); + + if (TIsSame::Value) { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeCachedValuesMemory , MemorySize); } + if (TIsSame::Value) { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelDataOctreeCachedMaterialsMemory, MemorySize); } + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +class TVoxelDataOctreeLeafData; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template<> +class TVoxelDataOctreeLeafData +{ + FVoxelValue* RESTRICT DataPtr = nullptr; + FVoxelValue SingleValue; + bool bIsSingleValue = false; + bool bDirty = false; + + static constexpr int32 MemorySize = VOXELS_PER_DATA_CHUNK * sizeof(FVoxelValue); + + friend class FVoxelSaveBuilder; + friend class FVoxelSaveLoader; + +public: + TVoxelDataOctreeLeafData() = default; + ~TVoxelDataOctreeLeafData() + { + if (!ensureVoxelSlow(!DataPtr)) + { + ClearData(IVoxelDataOctreeMemory()); + } + } + + UE_NONCOPYABLE(TVoxelDataOctreeLeafData); + +public: + FORCEINLINE bool IsDirty() const + { + return bDirty; + } + + void SetIsDirty(bool bNewDirty, const IVoxelDataOctreeMemory& Memory) + { + if (bDirty == bNewDirty) + { + return; + } + + const bool bOldDirty = bDirty; + bDirty = bNewDirty; + + // Update the memory usages according to the new dirty flags + // This is so that the memory usage reported by the voxel world (eg to get the size of the map voxel data) is accurate + // without including cached memory usage into dirty memory usage + if (DataPtr) + { + TVoxelDataOctreeLeafMemoryUsage::Decrease(MemorySize, bOldDirty, Memory); + TVoxelDataOctreeLeafMemoryUsage::Increase(MemorySize, bNewDirty, Memory); + } + } + +public: + void CreateData(const IVoxelDataOctreeMemory& Memory) + { + check(!HasData()); + CheckState(); + Allocate(Memory); + CheckState(); + } + template + void CreateData(const IVoxelDataOctreeMemory& Memory, TLambda Init) + { + CreateData(Memory); + check(DataPtr); + Init(static_cast(DataPtr)); + } + void CreateData(const IVoxelDataOctreeMemory& Memory, const TVoxelDataOctreeLeafData& Source) + { + check(!HasData()); + CheckState(); + + bIsSingleValue = Source.bIsSingleValue; + if (Source.bIsSingleValue) + { + SingleValue = Source.SingleValue; + } + else + { + if (Source.DataPtr) + { + Allocate(Memory); + FMemory::Memcpy(DataPtr, Source.DataPtr, MemorySize); + } + } + CheckState(); + } + + void ClearData(const IVoxelDataOctreeMemory& Memory) + { + CheckState(); + SetIsDirty(false, Memory); + if (DataPtr) + { + Deallocate(Memory); + } + bIsSingleValue = false; + checkVoxelSlow(!HasData()); + CheckState(); + } + +public: + // Used to determine if it's worth compressing or clearing the cache + FORCEINLINE bool HasAllocation() const + { + return DataPtr != nullptr; + } + FORCEINLINE bool HasData() const + { + return DataPtr || bIsSingleValue; + } + +public: + void Compress(const IVoxelDataOctreeMemory& Memory) + { + if (!bIsSingleValue) + { + TryCompressToSingleValue(Memory); + } + } + +public: + FORCEINLINE FVoxelValue Get(int32 Index) const + { + checkVoxelSlow(HasData()); + CheckBounds(Index); + if (bIsSingleValue) + { + return SingleValue; + } + else + { + checkVoxelSlow(DataPtr); + return DataPtr[Index]; + } + } + +public: + FORCEINLINE void PrepareForWrite(const IVoxelDataOctreeMemory& Memory) + { + checkVoxelSlow(HasData()); + CheckState(); + if (bIsSingleValue) + { + ExpandSingleValue(Memory); + } + CheckState(); + } + FORCEINLINE FVoxelValue& GetRef(int32 Index) + { + checkVoxelSlow(DataPtr); + checkVoxelSlow(HasData()); + CheckBounds(Index); + return DataPtr[Index]; + } + +public: + FORCEINLINE void CopyTo(FVoxelValue* RESTRICT DestPtr) const + { + checkVoxelSlow(HasData()); + if (bIsSingleValue) + { + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + DestPtr[Index] = SingleValue; + } + } + else + { + FMemory::Memcpy(DestPtr, DataPtr, MemorySize); + } + } + +public: + FORCEINLINE bool IsSingleValue() const + { + return bIsSingleValue; + } + FORCEINLINE FVoxelValue GetSingleValue() const + { + checkVoxelSlow(IsSingleValue()); + return SingleValue; + } + + void SetSingleValue(FVoxelValue InSingleValue) + { + CheckState(); + check(!DataPtr && !bIsSingleValue); + bIsSingleValue = true; + SingleValue = InSingleValue; + CheckState(); + } + void ExpandSingleValue(const IVoxelDataOctreeMemory& Memory) + { + CheckState(); + check(bIsSingleValue); + + bIsSingleValue = false; + Allocate(Memory); + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + DataPtr[Index] = SingleValue; + } + CheckState(); + } + void TryCompressToSingleValue(const IVoxelDataOctreeMemory& Memory) + { + CheckState(); + check(!bIsSingleValue); + + if (!DataPtr) + { + return; + } + + const FVoxelValue NewSingleValue = DataPtr[0]; + for (int32 Index = 1; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + if (DataPtr[Index] != NewSingleValue) return; + } + + Deallocate(Memory); + SingleValue = NewSingleValue; + bIsSingleValue = true; + + CheckState(); + } + +private: + FORCEINLINE void CheckState() const + { + checkVoxelSlow(!(DataPtr && bIsSingleValue)); + checkVoxelSlow(!bDirty || HasData()); + } + FORCEINLINE static void CheckBounds(int32 Index) + { + checkVoxelSlow(0 <= Index && Index < VOXELS_PER_DATA_CHUNK); + } + +private: + void Allocate(const IVoxelDataOctreeMemory& Memory) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(!DataPtr && !bIsSingleValue); + DataPtr = static_cast(FMemory::Malloc(MemorySize)); + + TVoxelDataOctreeLeafMemoryUsage::Increase(MemorySize, bDirty, Memory); + } + void Deallocate(const IVoxelDataOctreeMemory& Memory) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(DataPtr); + FMemory::Free(DataPtr); + DataPtr = nullptr; + + TVoxelDataOctreeLeafMemoryUsage::Decrease(MemorySize, bDirty, Memory); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template<> +class TVoxelDataOctreeLeafData +{ + static constexpr int32 NumChannels = FVoxelMaterial::NumChannels; + + TVoxelStaticArray Channels_DataPtr{ ForceInit }; + TVoxelStaticArray Channels_SingleValue{ ForceInit }; + + FVoxelMaterial* RESTRICT Main_DataPtr = nullptr; + // If set, implies the data stored in Channels is valid + // If Channels_DataPtr[I] is null, then Channels_SingleValue[I] is valid + // Data in Channels is assumed constant: compression won't try to compress it again + bool bUseChannels = false; + bool bDirty = false; + + friend class FVoxelSaveBuilder; + friend class FVoxelSaveLoader; + +public: + TVoxelDataOctreeLeafData() = default; + ~TVoxelDataOctreeLeafData() + { + bool bClear = !ensureVoxelSlow(!Main_DataPtr); + for (auto& DataPtr : Channels_DataPtr) + { + bClear |= !ensureVoxelSlow(!DataPtr); + } + + if (bClear) + { + ClearData(IVoxelDataOctreeMemory()); + } + } + + UE_NONCOPYABLE(TVoxelDataOctreeLeafData); + +public: + FORCEINLINE bool IsDirty() const + { + return bDirty; + } + + void SetIsDirty(const bool bNewDirty, const IVoxelDataOctreeMemory& Memory) + { + if (bDirty == bNewDirty) + { + return; + } + + const bool bOldDirty = bDirty; + bDirty = bNewDirty; + + // Update the memory usages according to the new dirty flags + // This is so that the memory usage reported by the voxel world (eg to get the size of the map voxel data) is accurate + // without including cached memory usage into dirty memory usage + if (bUseChannels) + { + for (auto& DataPtr : Channels_DataPtr) + { + if (DataPtr) + { + TVoxelDataOctreeLeafMemoryUsage::Decrease(Channels_MemorySize, bOldDirty, Memory); + TVoxelDataOctreeLeafMemoryUsage::Increase(Channels_MemorySize, bNewDirty, Memory); + } + } + } + else + { + if (Main_DataPtr) + { + TVoxelDataOctreeLeafMemoryUsage::Decrease(Main_MemorySize, bOldDirty, Memory); + TVoxelDataOctreeLeafMemoryUsage::Increase(Main_MemorySize, bNewDirty, Memory); + } + } + } + +public: + void CreateData(const IVoxelDataOctreeMemory& Memory) + { + check(!HasData()); + CheckState(); + Main_Allocate(Memory); + CheckState(); + } + template + void CreateData(const IVoxelDataOctreeMemory& Memory, TLambda Init) + { + CreateData(Memory); + check(Main_DataPtr); + Init(static_cast(Main_DataPtr)); + } + void CreateData(const IVoxelDataOctreeMemory& Memory, const TVoxelDataOctreeLeafData& Source) + { + check(!HasData()); + CheckState(); + bUseChannels = Source.bUseChannels; + if (Source.bUseChannels) + { + for (int32 Channel = 0; Channel < NumChannels; Channel++) + { + auto* SourceDataPtr = Source.Channels_DataPtr[Channel]; + if (SourceDataPtr) + { + auto*& DataPtr = Channels_DataPtr[Channel]; + Channels_Allocate(DataPtr, Memory); + + FMemory::Memcpy(DataPtr, SourceDataPtr, Channels_MemorySize); + } + } + } + else + { + if (Source.Main_DataPtr) + { + FMemory::Memcpy(Main_DataPtr, Source.Main_DataPtr, Main_MemorySize); + } + } + CheckState(); + } + + void ClearData(const IVoxelDataOctreeMemory& Memory) + { + CheckState(); + SetIsDirty(false, Memory); + if (bUseChannels) + { + for (auto& DataPtr : Channels_DataPtr) + { + if (DataPtr) + { + Channels_Deallocate(DataPtr, Memory); + } + } + } + else + { + if (Main_DataPtr) + { + Main_Deallocate(Memory); + } + } + bUseChannels = false; + checkVoxelSlow(!HasData()); + CheckState(); + } + +public: + // Used to determine if it's worth compressing or clearing the cache + FORCEINLINE bool HasAllocation() const + { + if (bUseChannels) + { + for (auto& DataPtr : Channels_DataPtr) + { + if (DataPtr) + { + return true; + } + } + return false; + } + else + { + return Main_DataPtr != nullptr; + } + } + FORCEINLINE bool HasData() const + { + return bUseChannels || Main_DataPtr; + } + +public: + void Compress(const IVoxelDataOctreeMemory& Memory) + { + CheckState(); + + if (bUseChannels || !Main_DataPtr) + { + return; + } + + const FVoxelMaterial SingleMaterial = Main_DataPtr[0]; + + static_assert(NumChannels < 31, ""); + constexpr uint32 DoNotCompressAnyChannel = (1 << NumChannels) - 1; + + uint32 DoNotCompressChannel = 0; + + // Iterate all the data, checking for constant channels + for (int32 Index = 1; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + for (int32 Channel = 0; Channel < NumChannels; Channel++) + { + const bool bDifferent = SingleMaterial.GetRaw(Channel) != Main_DataPtr[Index].GetRaw(Channel); + DoNotCompressChannel |= (1 << Channel) * bDifferent; + } + + if (DoNotCompressChannel == DoNotCompressAnyChannel) + { + // Fast path if all channels are different + return; + } + } + checkVoxelSlow(DoNotCompressChannel != DoNotCompressAnyChannel); + + // Create channels + for (int32 Channel = 0; Channel < NumChannels; Channel++) + { + if (DoNotCompressChannel & (1 << Channel)) + { + uint8* RESTRICT& DataPtr = Channels_DataPtr[Channel]; + Channels_Allocate(DataPtr, Memory); + + // Copy data from main + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + DataPtr[Index] = Main_DataPtr[Index].GetRaw(Channel); + } + } + else + { + // Use the constant value we found + Channels_SingleValue[Channel] = SingleMaterial.GetRaw(Channel); + } + } + + // Then delete main + Main_Deallocate(Memory); + bUseChannels = true; + + CheckState(); + } + +public: + FORCEINLINE FVoxelMaterial Get(int32 Index) const + { + checkVoxelSlow(HasData()); + CheckBounds(Index); + + if (bUseChannels) + { + return GetFromChannels(Index); + } + else + { + checkVoxelSlow(Main_DataPtr); + return Main_DataPtr[Index]; + } + } + +public: + FORCEINLINE void PrepareForWrite(const IVoxelDataOctreeMemory& Memory) + { + checkVoxelSlow(HasData()); + CheckState(); + if (bUseChannels) + { + // Allocate main + Main_Allocate(Memory); + checkVoxelSlow(Main_DataPtr); + + // Copy data over + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + Main_DataPtr[Index] = GetFromChannels(Index); + } + + // Free channels + for (auto& DataPtr : Channels_DataPtr) + { + if (DataPtr) + { + Channels_Deallocate(DataPtr, Memory); + } + } + bUseChannels = false; + } + checkVoxelSlow(!bUseChannels); + checkVoxelSlow(HasData()); + CheckState(); + } + FORCEINLINE FVoxelMaterial& GetRef(int32 Index) + { + CheckBounds(Index); + checkVoxelSlow(Main_DataPtr); + return Main_DataPtr[Index]; + } + FORCEINLINE void SetSingleValue(FVoxelMaterial SingleValue) + { + CheckState(); + checkVoxelSlow(!HasData()); + bUseChannels = true; + for (int32 Channel = 0; Channel < NumChannels; Channel++) + { + Channels_SingleValue[Channel] = SingleValue.GetRaw(Channel); + } + CheckState(); + } + +public: + FORCEINLINE void CopyTo(FVoxelMaterial* RESTRICT DestPtr) const + { + checkVoxelSlow(DestPtr); + checkVoxelSlow(HasData()); + if (bUseChannels) + { + for (int32 Index = 0; Index < VOXELS_PER_DATA_CHUNK; Index++) + { + DestPtr[Index] = GetFromChannels(Index); + } + } + else + { + checkVoxelSlow(Main_DataPtr); + FMemory::Memcpy(DestPtr, Main_DataPtr, Main_MemorySize); + } + } + +private: + FORCEINLINE void CheckState() const + { + checkVoxelSlow(!(Main_DataPtr && bUseChannels)); + checkVoxelSlow(!bDirty || HasData()); + } + FORCEINLINE static void CheckBounds(int32 Index) + { + checkVoxelSlow(0 <= Index && Index < VOXELS_PER_DATA_CHUNK); + } + + FORCEINLINE FVoxelMaterial GetFromChannels(int32 Index) const + { + CheckBounds(Index); + checkVoxelSlow(bUseChannels); + + FVoxelMaterial Material; + for (int32 Channel = 0; Channel < NumChannels; Channel++) + { + if (const uint8* RESTRICT const DataPtr = Channels_DataPtr[Channel]) + { + Material.GetRaw(Channel) = DataPtr[Index]; + } + else + { + Material.GetRaw(Channel) = Channels_SingleValue[Channel]; + } + } + return Material; + } + +private: + static constexpr int32 Main_MemorySize = VOXELS_PER_DATA_CHUNK * sizeof(FVoxelMaterial); + static constexpr int32 Channels_MemorySize = VOXELS_PER_DATA_CHUNK * sizeof(uint8); + + void Main_Allocate(const IVoxelDataOctreeMemory& Memory) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(!Main_DataPtr); + Main_DataPtr = static_cast(FMemory::Malloc(Main_MemorySize)); + + TVoxelDataOctreeLeafMemoryUsage::Increase(Main_MemorySize, bDirty, Memory); + } + void Main_Deallocate(const IVoxelDataOctreeMemory& Memory) + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(Main_DataPtr); + FMemory::Free(Main_DataPtr); + Main_DataPtr = nullptr; + + TVoxelDataOctreeLeafMemoryUsage::Decrease(Main_MemorySize, bDirty, Memory); + } + + void Channels_Allocate(uint8* RESTRICT& DataPtr, const IVoxelDataOctreeMemory& Memory) const + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(!DataPtr); + DataPtr = static_cast(FMemory::Malloc(Channels_MemorySize)); + + TVoxelDataOctreeLeafMemoryUsage::Increase(Channels_MemorySize, bDirty, Memory); + } + void Channels_Deallocate(uint8* RESTRICT& DataPtr, const IVoxelDataOctreeMemory& Memory) const + { + VOXEL_SLOW_FUNCTION_COUNTER(); + + check(DataPtr); + FMemory::Free(DataPtr); + DataPtr = nullptr; + + TVoxelDataOctreeLeafMemoryUsage::Decrease(Channels_MemorySize, bDirty, Memory); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafMultiplayer.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafMultiplayer.h new file mode 100644 index 00000000..27bc4b4f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafMultiplayer.h @@ -0,0 +1,68 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelDiff.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Multiplayer Memory"), STAT_VoxelMultiplayerMemory, STATGROUP_VoxelMemory, VOXEL_API); + +template +struct TEmptyArray +{ + inline int32 Add(T Value) + { + return -1; + } + inline int32 Num() const + { + return 0; + } + inline T* begin() const + { + return nullptr; + } + inline T* end() const + { + return nullptr; + } +}; + +class FVoxelDataOctreeLeafMultiplayer +{ +public: + struct FDirty + { + // TODO bit arrays would be better + TSet Values; + TSet Materials; + }; + FDirty Dirty; + +public: + template + FORCEINLINE void MarkIndexDirty(FVoxelCellIndex Index) + { + FVoxelUtilities::TValuesMaterialsSelector::Get(Dirty).Add(Index); + } + + template + void AddToDiffQueueAndReset(const TData& Data, TArray>& OutDiffQueue) + { + auto& DirtyT = FVoxelUtilities::TValuesMaterialsSelector::Get(Dirty); + for (int32 Index : DirtyT) + { + OutDiffQueue.Emplace(Index, Data.Get(Index)); + } + DirtyT.Empty(); + } + + template + bool IsNetworkDirty() + { + return FVoxelUtilities::TValuesMaterialsSelector::Get(Dirty).Num() > 0; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafUndoRedo.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafUndoRedo.h new file mode 100644 index 00000000..0babfaa8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafUndoRedo.h @@ -0,0 +1,126 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" + +class IVoxelData; +class FVoxelDataOctreeLeaf; +class FVoxelGeneratorInstance; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel UndoRedo Memory"), STAT_VoxelUndoRedoMemory, STATGROUP_VoxelMemory, VOXEL_API); + +enum class EVoxelUndoRedo +{ + Undo, + Redo +}; + +class VOXEL_API FVoxelDataOctreeLeafUndoRedo +{ +public: + explicit FVoxelDataOctreeLeafUndoRedo(const FVoxelDataOctreeLeaf& Leaf); + ~FVoxelDataOctreeLeafUndoRedo(); + + void ClearFrames(const FVoxelDataOctreeLeaf& Leaf); + void SaveFrame(const FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition); + + template + void ClearFramesOfType(); + + template + void UndoRedo(const IVoxelData& Data, FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition); + +public: + template + inline bool CanUndoRedo(int32 HistoryPosition) const + { + return GetFramesStack().Num() > 0 && GetFramesStack().Last()->HistoryPosition == HistoryPosition; + } + inline bool IsCurrentFrameEmpty() const + { + return CurrentFrame->IsEmpty(); + } + + template + inline auto& GetFramesStack() + { + return Type == EVoxelUndoRedo::Undo ? UndoFramesStack : RedoFramesStack; + } + template + inline const auto& GetFramesStack() const + { + return Type == EVoxelUndoRedo::Undo ? UndoFramesStack : RedoFramesStack; + } + +public: + template + FORCEINLINE void SavePreviousValue(FVoxelCellIndex Index, T Value) + { + auto& AlreadyModifiedT = FVoxelUtilities::TValuesMaterialsSelector::Get(AlreadyModified); + if (!AlreadyModifiedT.Test(Index)) + { + AlreadyModifiedT.Set(Index); + FVoxelUtilities::TValuesMaterialsSelector::Get(*CurrentFrame).Emplace(Index, Value); + } + } + +private: + template + struct TModifiedValue + { + FVoxelCellIndex Index; + T Value; + + TModifiedValue(FVoxelCellIndex Index, T Value) : Index(Index), Value(Value) {} + }; + struct FFrame + { + template + explicit FFrame(const TLeaf& Leaf) + : bValuesDirty(Leaf.Values.IsDirty()) + , bMaterialsDirty(Leaf.Materials.IsDirty()) + { + } + ~FFrame() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); + } + + int32 HistoryPosition = -1; + + const bool bValuesDirty; + const bool bMaterialsDirty; + + TArray> Values; + TArray> Materials; + + mutable uint32 AllocatedSize = 0; + + void UpdateStats() const; + + inline bool IsEmpty() const + { + return Values.Num() == 0 && Materials.Num() == 0; + } + }; + struct FAlreadyModified + { + TVoxelStaticBitArray Values = ForceInit; + TVoxelStaticBitArray Materials = ForceInit; + }; + + FAlreadyModified AlreadyModified; + + TUniquePtr CurrentFrame; + + TArray> UndoFramesStack; + TArray> RedoFramesStack; + + template + void AddFrameToStack(TUniquePtr& Frame); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.h new file mode 100644 index 00000000..f19bb3fd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.h @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelData.h" + +namespace FVoxelDataUtilities +{ + template + struct TBilinearInterpolatedData + { + const TData& Data; + + explicit TBilinearInterpolatedData(const TData& Data) : Data(Data) {} + + v_flt GetValue(v_flt X, v_flt Y, v_flt Z, int32 LOD) const + { + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + const int32 MinZ = FMath::FloorToInt(Z); + + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + const int32 MaxZ = FMath::CeilToInt(Z); + + const v_flt AlphaX = X - MinX; + const v_flt AlphaY = Y - MinY; + const v_flt AlphaZ = Z - MinZ; + + return FVoxelUtilities::TrilinearInterpolation( + Data.GetValue(MinX, MinY, MinZ, LOD).ToFloat(), + Data.GetValue(MaxX, MinY, MinZ, LOD).ToFloat(), + Data.GetValue(MinX, MaxY, MinZ, LOD).ToFloat(), + Data.GetValue(MaxX, MaxY, MinZ, LOD).ToFloat(), + Data.GetValue(MinX, MinY, MaxZ, LOD).ToFloat(), + Data.GetValue(MaxX, MinY, MaxZ, LOD).ToFloat(), + Data.GetValue(MinX, MaxY, MaxZ, LOD).ToFloat(), + Data.GetValue(MaxX, MaxY, MaxZ, LOD).ToFloat(), + AlphaX, + AlphaY, + AlphaZ); + } + template + v_flt GetValue(const T& P, int32 LOD) const + { + return GetValue(P.X, P.Y, P.Z, LOD); + } + }; + + template + TBilinearInterpolatedData MakeBilinearInterpolatedData(const TData& Data) + { + return TBilinearInterpolatedData(Data); + } + + template + struct TFloatData + { + const TData& Data; + + explicit TFloatData(const TData& Data) : Data(Data) {} + + float GetValue(int32 X, int32 Y, int32 Z, int32 LOD) const + { + return Data.GetValue(X, Y, Z, LOD).ToFloat(); + } + float GetValue(const FIntVector& P, int32 LOD) const + { + return GetValue(P.X, P.Y, P.Z, LOD); + } + }; + + template + TFloatData MakeFloatData(const TData& Data) + { + return TFloatData(Data); + } + + /** + * Requires read lock in FVoxelIntBox(Position - Offset, Position + Offset + 1) + */ + template + FVector GetGradientFromGetValue(const TData& Data, T X, T Y, T Z, int32 LOD, T Offset = 1); + + template + FVector GetGradientFromGetFloatValue(TData& Data, T X, T Y, T Z, int32 LOD, T Offset = 1); + + template + void IterateDirtyDataInBounds(const FVoxelData& Data, const FVoxelIntBox& Bounds, F Lambda); + + template + void ClearData(FVoxelData& Data); + + template + bool HasData(FVoxelData& Data); + + template + bool CheckIfSameAsGenerator(const FVoxelData& Data, FVoxelDataOctreeLeaf& Leaf); + + template + void SetEntireDataAsDirtyAndCopyFrom(const FVoxelData& SourceData, FVoxelData& DestData); + + template + void CopyDirtyChunksFrom(const FVoxelData& SourceData, FVoxelData& DestData); + + // Note: will set the entire Data as dirty! + template + void OverrideGeneratorValue(FVoxelData& Data, T Value); + + template + void ScaleWorldData(const FVoxelData& SourceData, FVoxelData& DestData, const FVoxelVector& Scale); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.inl new file mode 100644 index 00000000..125baf6f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelDataUtilities.inl @@ -0,0 +1,260 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelDataUtilities.h" +#include "VoxelData/VoxelDataAccelerator.h" +#include "VoxelFeedbackContext.h" + +template +FVector FVoxelDataUtilities::GetGradientFromGetValue(const TData& Data, T X, T Y, T Z, int32 LOD, T Offset) +{ + const double MinX = Data.GetValue(X - Offset, Y, Z, LOD); + const double MaxX = Data.GetValue(X + Offset, Y, Z, LOD); + const double MinY = Data.GetValue(X, Y - Offset, Z, LOD); + const double MaxY = Data.GetValue(X, Y + Offset, Z, LOD); + const double MinZ = Data.GetValue(X, Y, Z - Offset, LOD); + const double MaxZ = Data.GetValue(X, Y, Z + Offset, LOD); + return FVector(MaxX - MinX, MaxY - MinY, MaxZ - MinZ).GetSafeNormal(); +} + +template +FVector FVoxelDataUtilities::GetGradientFromGetFloatValue(TData& Data, T X, T Y, T Z, int32 LOD, T Offset) +{ + // Force offset to 1 as we don't have data any further outside of the generator + const auto Fallback = [&]() { return GetGradientFromGetValue(MakeBilinearInterpolatedData(Data), X, Y, Z, LOD, 1); }; + bool bIsGeneratorValue = true; + + const double MinX = Data.GetFloatValue(X - Offset, Y, Z, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + const double MaxX = Data.GetFloatValue(X + Offset, Y, Z, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + const double MinY = Data.GetFloatValue(X, Y - Offset, Z, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + const double MaxY = Data.GetFloatValue(X, Y + Offset, Z, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + const double MinZ = Data.GetFloatValue(X, Y, Z - Offset, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + const double MaxZ = Data.GetFloatValue(X, Y, Z + Offset, LOD, &bIsGeneratorValue); + if (!bIsGeneratorValue) return Fallback(); + + return FVector(MaxX - MinX, MaxY - MinY, MaxZ - MinZ).GetSafeNormal(); +} + +template +void FVoxelDataUtilities::IterateDirtyDataInBounds(const FVoxelData& Data, const FVoxelIntBox& Bounds, F Lambda) +{ + FVoxelOctreeUtilities::IterateLeavesInBounds(Data.GetOctree(), Bounds, [&](const FVoxelDataOctreeLeaf& Leaf) + { + auto& DataHolder = Leaf.GetData(); + if (DataHolder.IsDirty()) + { + const FVoxelIntBox LeafBounds = Leaf.GetBounds(); + LeafBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelCellIndex Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(LeafBounds.Min, X, Y, Z); + const T& Value = DataHolder.Get(Index); + Lambda(X, Y, Z, Value); + }); + } + }); +} + +template +void FVoxelDataUtilities::ClearData(FVoxelData& Data) +{ + FVoxelOctreeUtilities::IterateAllLeaves(Data.GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForWrite()); + Leaf.GetData().ClearData(Data); + }); +} + +template +bool FVoxelDataUtilities::HasData(FVoxelData& Data) +{ + return !FVoxelOctreeUtilities::IterateLeavesInBoundsEarlyExit(Data.GetOctree(), FVoxelIntBox::Infinite, [](FVoxelDataOctreeLeaf& Leaf) + { + ensureThreadSafe(Leaf.IsLockedForRead()); + return !Leaf.GetData().HasData(); + }); +} + +template +bool FVoxelDataUtilities::CheckIfSameAsGenerator(const FVoxelData& Data, FVoxelDataOctreeLeaf& Leaf) +{ + VOXEL_SLOW_FUNCTION_COUNTER(); + + auto& DataHolder = Leaf.GetData(); + + if (!ensure(DataHolder.IsDirty())) return false; + + const FIntVector Min = Leaf.GetMin(); + + // Note: check if single value too, as else we end up with the save file being big because of single values! + // 1024 x 1024 x 1024 world -> 32k possible single values! Ends up being a lot as you store 14 bytes per value + for (int32 X = 0; X < DATA_CHUNK_SIZE; X++) + { + for (int32 Y = 0; Y < DATA_CHUNK_SIZE; Y++) + { + for (int32 Z = 0; Z < DATA_CHUNK_SIZE; Z++) + { + const T GeneratorValue = Leaf.GetFromGeneratorAndAssets(*Data.Generator, Min.X + X, Min.Y + Y, Min.Z + Z, 0); + const T Value = DataHolder.Get(FVoxelDataOctreeUtilities::IndexFromCoordinates(X, Y, Z)); + if (Value != GeneratorValue) + { + return false; + } + } + } + } + + DataHolder.SetIsDirty(false, Data); + return true; +} + +template +void FVoxelDataUtilities::SetEntireDataAsDirtyAndCopyFrom(const FVoxelData& SourceData, FVoxelData& DestData) +{ + FVoxelScopedSlowTask SlowTask(1 << (3 * DestData.Depth), VOXEL_LOCTEXT("Copying data to new generator")); + FVoxelOctreeUtilities::IterateEntireTree(DestData.GetOctree(), [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + SlowTask.EnterProgressFrame(); + + FVoxelDataOctreeLeaf& Leaf = Tree.AsLeaf(); + const FVoxelDataOctreeBase& SourceBottomNode = FVoxelOctreeUtilities::GetBottomNode(SourceData.GetOctree(), Leaf.Position.X, Leaf.Position.Y, Leaf.Position.Z); + const FVoxelDataOctreeLeaf* SourceLeaf = SourceBottomNode.IsLeaf() ? &SourceBottomNode.AsLeaf() : nullptr; + + TVoxelDataOctreeLeafData& DataHolder = Leaf.GetData(); + + const auto CopyFromGenerator = [&]() + { + DataHolder.CreateData(DestData, [&](T* RESTRICT DataPtr) + { + TVoxelQueryZone QueryZone(Leaf.GetBounds(), DataPtr); + SourceBottomNode.GetFromGeneratorAndAssets(*SourceData.Generator, QueryZone, 0); // Note: make sure to use the source generator! + }); + DataHolder.Compress(DestData); // To save memory + }; + + if (SourceLeaf) + { + const TVoxelDataOctreeLeafData& SourceDataHolder = SourceLeaf->GetData(); + if (SourceDataHolder.HasData()) + { + DataHolder.CreateData(DestData, SourceDataHolder); + } + else + { + CopyFromGenerator(); + } + } + else + { + CopyFromGenerator(); + } + + DataHolder.SetIsDirty(true, DestData); + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + Parent.CreateChildren(); + } + } + }); +} + +template +void FVoxelDataUtilities::CopyDirtyChunksFrom(const FVoxelData& SourceData, FVoxelData& DestData) +{ + FVoxelOctreeUtilities::IterateAllLeaves(SourceData.GetOctree(), [&](FVoxelDataOctreeLeaf& SourceLeaf) + { + const TVoxelDataOctreeLeafData& SourceDataHolder = SourceLeaf.GetData(); + + if (!SourceDataHolder.IsDirty()) + { + return; + } + + FVoxelDataOctreeLeaf& DestLeaf = *FVoxelOctreeUtilities::GetLeaf(DestData.GetOctree(), SourceLeaf.Position.X, SourceLeaf.Position.Y, SourceLeaf.Position.Z); + TVoxelDataOctreeLeafData& DestDataHolder = DestLeaf.GetData(); + + if (SourceDataHolder.IsSingleValue()) + { + DestDataHolder.SetSingleValue(SourceDataHolder.GetSingleValue()); + } + else if (auto* SrcDataPtr = SourceDataHolder.GetDataPtr()) + { + DestDataHolder.CreateData(DestData, [&](T* RESTRICT DataPtr) + { + FMemory::Memcpy(DataPtr, SrcDataPtr, VOXELS_PER_DATA_CHUNK * sizeof(T)); + }); + } + }); +} + +template +void FVoxelDataUtilities::OverrideGeneratorValue(FVoxelData& Data, T Value) +{ + FVoxelOctreeUtilities::IterateEntireTree(Data.GetOctree(), [&](FVoxelDataOctreeBase& Tree) + { + if (Tree.IsLeaf()) + { + ensureThreadSafe(Tree.IsLockedForWrite()); + + FVoxelDataOctreeLeaf& Leaf = Tree.AsLeaf(); + TVoxelDataOctreeLeafData& DataHolder = Leaf.GetData(); + + if (!DataHolder.IsDirty()) + { + DataHolder.ClearData(Data); + DataHolder.SetSingleValue(Value); + DataHolder.SetIsDirty(true, Data); + } + } + else + { + auto& Parent = Tree.AsParent(); + if (!Parent.HasChildren()) + { + Parent.CreateChildren(); + } + } + }); +} + +template +void FVoxelDataUtilities::ScaleWorldData(const FVoxelData& SourceData, FVoxelData& DestData, const FVoxelVector& Scale) +{ + TArray DirtyBounds; + FVoxelOctreeUtilities::IterateAllLeaves(SourceData.GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + if (Leaf.GetData().IsDirty()) + { + const auto ScaledBounds = Leaf.GetBounds().Scale(Scale); + DirtyBounds.Add(ScaledBounds); + } + }); + + const FVoxelConstDataAccelerator SourceAccelerator(SourceData, FVoxelIntBox::Infinite); + FVoxelMutableDataAccelerator DestAccelerator(DestData, FVoxelIntBox::Infinite); + + const auto CopyData = [&](int32 X, int32 Y, int32 Z) + { + const auto Position = FVoxelVector(X, Y, Z) / Scale; + DestAccelerator.Set(X, Y, Z, T(SourceAccelerator.GetFloatValue(Position, 0))); + }; + + FVoxelScopedSlowTask SlowTask(DirtyBounds.Num(), VOXEL_LOCTEXT("Scaling data")); + for (const FVoxelIntBox& Bounds : DirtyBounds) + { + SlowTask.EnterProgressFrame(); + Bounds.Iterate(CopyData); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSave.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSave.h new file mode 100644 index 00000000..91e463b1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSave.h @@ -0,0 +1,254 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelSaveStruct.h" +#include "VoxelObjectArchive.h" +#include "VoxelSave.generated.h" + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Uncompressed Saves Memory"), STAT_VoxelUncompressedSavesMemory, STATGROUP_VoxelMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Compressed Saves Memory"), STAT_VoxelCompressedSavesMemory, STATGROUP_VoxelMemory, VOXEL_API); + +namespace FVoxelSaveVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + FoliagePaint, + ValueConfigFlagAndSaveGUIDs, + SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + StoreMaterialChannelsIndividuallyAndRemoveFoliage, + ProperlySerializePlaceableItemsObjects, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +} + +struct VOXEL_API FVoxelUncompressedWorldSaveImpl +{ +public: + FVoxelUncompressedWorldSaveImpl() + { + } + ~FVoxelUncompressedWorldSaveImpl() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUncompressedSavesMemory, AllocatedSize); + } + +public: + int32 GetDepth() const + { + return Depth; + } + FGuid GetGuid() const + { + return Guid; + } + + bool HasValues() const + { + return ValueBuffers.Num() > 0; + } + bool HasMaterials() const + { + return MaterialBuffers.Num() > 0; + } + + /** + * Use in combination with SetUserFlags to do custom fixes (note that the plugin handles loading values/materials saved with different defines on its own) + * + * For example: + * When loading: + * Save.ApplyCustomFixes([&](uint64 Flags, TArray& Values, TArray& Materials) + * { + * if (Flags & EMyFlags::InvertR) // Or Flags < EMyVersions::InvertR + * { + * for (auto& Material : Materials) + * { + * Material.SetR(255 - Material.GetR()); + * } + * } + * }); + * + * When saving: + * Save.SetUserFlags(EMyFlags::Current); + */ + template + void ApplyCustomFixes(T Lambda) + { + Lambda(UserFlags, ValueBuffers, MaterialBuffers); + } + void SetUserFlags(uint64 InUserFlags) + { + UserFlags = InUserFlags; + } + uint64 GetUserFlags() const + { + return UserFlags; + } + + int64 GetAllocatedSize() const + { + return AllocatedSize; + } + +public: + void UpdateAllocatedSize() const; + bool Serialize(FArchive& Ar); + + bool operator==(const FVoxelUncompressedWorldSaveImpl& Other) const + { + return Guid == Other.Guid; + } + +private: + struct FVoxelChunkSave + { + FIntVector Position; + + int32 ValuesIndex = -1; + // Index into MaterialsIndices. MaterialsIndices are indices to single materials if they have MaterialIndexSingleValueFlag + int32 MaterialsIndex = -1; + + bool bSingleValue = false; + + friend FArchive& operator<<(FArchive& Ar, FVoxelChunkSave& Save) + { + Ar << Save.Position; + + Ar << Save.ValuesIndex; + Ar << Save.MaterialsIndex; + + Ar << Save.bSingleValue; + + return Ar; + } + }; + + // In theory shouldn't overlap with actual data, as array nums are int32 + static constexpr uint32 MaterialIndexSingleValueFlag = 1u << 31; + + int32 Version = -1; + FGuid Guid; + int32 Depth = -1; + uint64 UserFlags = 0; + + TNoGrowArray ValueBuffers; + TNoGrowArray SingleValues; + + TNoGrowArray> MaterialsIndices; + TNoGrowArray MaterialBuffers; + TNoGrowArray SingleMaterials; + + TNoGrowArray Chunks; + + TArray PlaceableItems; + + mutable int64 AllocatedSize = 0; + + friend class FVoxelSaveBuilder; + friend class FVoxelSaveLoader; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct VOXEL_API FVoxelCompressedWorldSaveImpl +{ + FVoxelCompressedWorldSaveImpl() = default; + ~FVoxelCompressedWorldSaveImpl(); + + int32 GetDepth() const + { + return Depth; + } + + bool operator==(const FVoxelCompressedWorldSaveImpl& Other) const + { + return Guid == Other.Guid; + } + + bool Serialize(FArchive& Ar); + void UpdateAllocatedSize() const; + +private: + int32 Version; + FGuid Guid; + int32 Depth = -1; + TArray CompressedData; + + mutable int64 AllocatedSize = 0; + + friend class UVoxelSaveUtilities; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Blueprint wrapper that's cheap to copy around +USTRUCT(BlueprintType, Category = Voxel) +struct VOXEL_API FVoxelUncompressedWorldSave +#if CPP + : public TVoxelSaveStruct +#endif +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Category = "Objects") + TArray Objects; +}; + +// Blueprint wrapper that's cheap to copy around +USTRUCT(BlueprintType, Category = Voxel) +struct VOXEL_API FVoxelCompressedWorldSave +#if CPP + : public TVoxelSaveStruct +#endif +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Category = "Objects") + TArray Objects; +}; + +DEFINE_VOXEL_SAVE_STRUCT(FVoxelUncompressedWorldSave); +DEFINE_VOXEL_SAVE_STRUCT(FVoxelCompressedWorldSave); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(Blueprintable, Category = Voxel) +class VOXEL_API UVoxelWorldSaveObject : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelCompressedWorldSave Save; + + // Depth of the world + UPROPERTY(VisibleAnywhere, Category = "Voxel") + int32 Depth = 0; + + virtual void PostLoad() override; + + void CopyDepthFromSave(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSaveUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSaveUtilities.h new file mode 100644 index 00000000..ebe3f79e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelData/VoxelSaveUtilities.h @@ -0,0 +1,95 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSave.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelSaveUtilities.generated.h" + +struct FVoxelAssetItem; +struct FVoxelPlaceableItemLoadInfo; + +class AVoxelWorld; +class IVoxelDataOctreeMemory; +template +class TVoxelDataOctreeLeafData; + +class FVoxelSaveBuilder +{ +public: + explicit FVoxelSaveBuilder(int32 Depth); + + void AddChunk( + const FIntVector& Position, + const TVoxelDataOctreeLeafData& Values, + const TVoxelDataOctreeLeafData& Materials) + { + ChunksToSave.Add({ Position, &Values, &Materials }); + } + + void AddAssetItem(const FVoxelAssetItem& AssetItem); + + void Save(FVoxelUncompressedWorldSaveImpl& OutSave, TArray& OutObjects); + +private: + struct FChunkToSave + { + FIntVector Position; + const TVoxelDataOctreeLeafData* Values = nullptr; + const TVoxelDataOctreeLeafData* Materials = nullptr; + }; + const int32 Depth; + TArray ChunksToSave; + TArray AssetItems; +}; + +class FVoxelSaveLoader +{ +public: + explicit FVoxelSaveLoader(const FVoxelUncompressedWorldSaveImpl& Save) + : Save(Save) + { + } + + void ExtractChunk( + int32 ChunkIndex, + const IVoxelDataOctreeMemory& Memory, + TVoxelDataOctreeLeafData& OutValues, + TVoxelDataOctreeLeafData& OutMaterials) const; + + void GetPlaceableItems(const FVoxelPlaceableItemLoadInfo& LoadInfo, TArray& OutAssetItems); + +public: + int32 NumChunks() const + { + return Save.Chunks.Num(); + } + FIntVector GetChunkPosition(int32 ChunkIndex) const + { + return Save.Chunks[ChunkIndex].Position; + } + bool GetError() const + { + return bError; + } + +private: + const FVoxelUncompressedWorldSaveImpl& Save; + bool bError = false; +}; + +UCLASS() +class VOXEL_API UVoxelSaveUtilities : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Save") + static void CompressVoxelSave(const FVoxelUncompressedWorldSave& UncompressedSave, FVoxelCompressedWorldSave& OutCompressedSave); + static void CompressVoxelSave(const FVoxelUncompressedWorldSaveImpl& UncompressedSave, FVoxelCompressedWorldSaveImpl& OutCompressedSave); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Save") + static bool DecompressVoxelSave(const FVoxelCompressedWorldSave& CompressedSave, FVoxelUncompressedWorldSave& OutUncompressedSave); + static bool DecompressVoxelSave(const FVoxelCompressedWorldSaveImpl& CompressedSave, FVoxelUncompressedWorldSaveImpl& OutUncompressedSave); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug.h new file mode 100644 index 00000000..50242e6f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEngineVersionHelpers.h" + +struct VOXEL_API FVoxelDebug +{ + template +#if ENGINE_MINOR_VERSION < 26 + using TDelegate = TMulticastDelegate>; +#else + using TDelegate = TMulticastDelegate)>; +#endif + + template + static TDelegate& GetDelegate(); + + template + static void Broadcast(FName Name, const FIntVector& Size, TArrayView Data) + { + GetDelegate::Type>().Broadcast(Name, Size, Data); + } + template + static void Broadcast(FName Name, const FIntVector& Size, const TArray& Data) + { + Broadcast(Name, Size, TArrayView(Data)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugManager.h new file mode 100644 index 00000000..9bb4fcf9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugManager.h @@ -0,0 +1,80 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelTickable.h" + +enum class EVoxelPlayType; +class IVoxelPool; +class FVoxelData; +class AVoxelWorld; +class UVoxelProceduralMeshComponent; + +struct FVoxelDebugManagerSettings +{ + const TWeakObjectPtr VoxelWorld; + const TVoxelSharedRef Pool; + const TVoxelSharedRef Data; + const bool bDisabled; + + FVoxelDebugManagerSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& Data, + bool bDisabled = false); +}; + +class VOXEL_API FVoxelDebugManager : public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + const FVoxelDebugManagerSettings Settings; + + static TVoxelSharedRef Create(const FVoxelDebugManagerSettings& Settings); + void Destroy(); + +public: + void ReportUpdatedChunks(TFunction()> InUpdatedChunks); + void ReportRenderChunks(TFunction()> InRenderChunks); + void ReportMultiplayerSyncedChunks(TFunction()> InMultiplayerSyncedChunks); + + void ReportMeshTaskCount(int32 TaskCount); + void ReportMeshTasksCallbacksQueueNum(int32 Num); + void ReportMeshActionQueueNum(int32 Num); + void ReportFoliageTaskCount(int32 TaskCount); + + void ReportChunkEmptyState(const FVoxelIntBox& Bounds, bool bIsEmpty); + void ClearChunksEmptyStates(); + +public: + static bool ShowCollisionAndNavmeshDebug(); + static FColor GetCollisionAndNavmeshDebugColor(bool bEnableCollisions, bool bEnableNavmesh); + +protected: + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +private: + explicit FVoxelDebugManager(const FVoxelDebugManagerSettings& Settings); + + TArray UpdatedChunks; + TArray RenderChunks; + TArray MultiplayerSyncedChunks; + + int32 MeshTaskCount = 0; + int32 MeshTasksCallbacksQueueNum = 0; + int32 MeshActionQueueNum = 0; + FThreadSafeCounter FoliageTaskCount; + + struct FChunkEmptyState + { + FVoxelIntBox Bounds; + bool bIsEmpty; + }; + TArray ChunksEmptyStates; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugUtilities.h new file mode 100644 index 00000000..25408ef2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelDebugUtilities.h @@ -0,0 +1,84 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelDebugUtilities.generated.h" + +class FVoxelData; +class AVoxelWorld; +class AVoxelWorldInterface; +class IVoxelWorldInterface; +class UVoxelLineBatchComponent; + +UCLASS() +class VOXEL_API UVoxelDebugUtilities : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Debug", meta = (DefaultToSelf = "World", AdvancedDisplay = "Transform")) + static void DrawDebugIntBox( + AVoxelWorld* World, + FVoxelIntBox Bounds, + FTransform Transform, + float Lifetime = 1, + float Thickness = 0, + FLinearColor Color = FLinearColor::Red); + + static void DrawDebugIntBox( + const AVoxelWorldInterface* World, + FVoxelIntBox Box, + float Lifetime = 1, + float Thickness = 0, + FLinearColor Color = FLinearColor::Red); + + static void DrawDebugIntBox( + const IVoxelWorldInterface& World, + UVoxelLineBatchComponent& LineBatchComponent, + FTransform Transform, + FVoxelIntBox Box, + float Lifetime = 1, + float Thickness = 0, + FLinearColor Color = FLinearColor::Red); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Debug", meta = (DefaultToSelf = "World")) + static void DebugVoxelsInsideBounds( + AVoxelWorld* World, + FVoxelIntBox Bounds, + FLinearColor Color = FLinearColor::Red, + float Lifetime = 1, + float Thickness = 1, + bool bDebugDensities = true, + FLinearColor TextColor = FLinearColor::Black); + + struct FDrawDataOctreeSettings + { + AVoxelWorld* World = nullptr; + float Lifetime = 0; + bool bShowSingle = false; + bool bShowCached = false; + FColor SingleColor = FColor::Red; + FColor SingleDirtyColor = FColor::Red; + FColor CachedColor = FColor::Red; + FColor DirtyColor = FColor::Red; + + }; + template + static void DrawDataOctreeImpl(const FVoxelData& Data, const FDrawDataOctreeSettings& Settings); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Debug", meta = (DefaultToSelf = "World")) + static void DrawDataOctree( + AVoxelWorld* World, + EVoxelDataType DataType, + float Lifetime = 0, + bool bShowSingle = false, + bool bShowCached = false, + FColor SingleColor = FColor::Red, + FColor SingleDirtyColor = FColor::Red, + FColor CachedColor = FColor::Red, + FColor DirtyColor = FColor::Red); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelLineBatchComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelLineBatchComponent.h new file mode 100644 index 00000000..8c9d1466 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDebug/VoxelLineBatchComponent.h @@ -0,0 +1,64 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Components/LineBatchComponent.h" +#include "VoxelLineBatchComponent.generated.h" + +UCLASS() +class VOXEL_API UVoxelLineBatchComponent : public UPrimitiveComponent +{ + GENERATED_BODY() + +public: + // Buffer of lines to draw. No support for depth priority + TArray BatchedLines; + // Buffer or points to draw + TArray BatchedPoints; + // Buffer of simple meshes to draw + TArray BatchedMeshes; + + // Default time that lines/points will draw for + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float DefaultLifeTime = 1.0f; + + // Whether to calculate a tight accurate bounds (encompassing all points), or use a giant bounds that is fast to compute + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bCalculateAccurateBounds = false; + + UVoxelLineBatchComponent(); + + //~ Begin UPrimitiveComponent Interface. + virtual FPrimitiveSceneProxy* CreateSceneProxy() override; + virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; + //~ End UPrimitiveComponent Interface. + + //~ Begin UActorComponent Interface. + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + virtual void ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) override; + //~ End UActorComponent Interface. + + // Clear all batched lines, points and meshes + void Flush(); +}; + +class VOXEL_API FVoxelLineBatcherSceneProxy : public FPrimitiveSceneProxy +{ +public: + explicit FVoxelLineBatcherSceneProxy(const UVoxelLineBatchComponent* InComponent); + + //~ Begin FPrimitiveSceneProxy Interface + virtual SIZE_T GetTypeHash() const override; + virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override; + virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override; + virtual uint32 GetMemoryFootprint() const override; + //~ End FPrimitiveSceneProxy Interface + + uint32 GetAllocatedSize() const; + +private: + TArray Lines; + TArray Points; + TArray Meshes; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefaultPool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefaultPool.h new file mode 100644 index 00000000..4ed664fe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefaultPool.h @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/StaticArray.h" +#include "IVoxelPool.h" + +class VOXEL_API FVoxelDefaultPool : public IVoxelPool +{ +public: + static TVoxelSharedRef Create( + int32 ThreadCount, + bool bConstantPriorities, + const TMap& PriorityCategories, + const TMap& PriorityOffsets); + virtual ~FVoxelDefaultPool(); + +public: + //~ Begin IVoxelPool Interface + virtual void QueueTask(EVoxelTaskType Type, IVoxelQueuedWork* Task) override; + virtual void QueueTasks(EVoxelTaskType Type, const TArray& Tasks) override; + + virtual int32 GetNumTasks() const override; + //~ End IVoxelPool Interface + +private: + const TVoxelSharedRef Pool; + const TStaticArray PriorityCategories; + const TStaticArray PriorityOffsets; + + explicit FVoxelDefaultPool( + int32 ThreadCount, + bool bConstantPriorities, + const TMap& PriorityCategories, + const TMap& PriorityOffsets); + +public: + static void FixPriorityCategories(TMap& PriorityCategories); + static void FixPriorityOffsets(TMap& PriorityOffsets); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefinitions.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefinitions.h new file mode 100644 index 00000000..1d30b1c4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDefinitions.h @@ -0,0 +1,138 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUserDefinitions.h" + +/** + * To change a definition, add it to VoxelUserDefinitions.h + */ + +// Enable this to enable double precision in generators and voxel graphs +#ifndef VOXEL_DOUBLE_PRECISION +#define VOXEL_DOUBLE_PRECISION 0 +#endif + +// Enables slow voxel checks +#ifndef VOXEL_DEBUG +#define VOXEL_DEBUG 0 +#endif + +// True when compilation speed does not matter +#ifndef VOXEL_PLUGIN_PACKAGED +#define VOXEL_PLUGIN_PACKAGED 1 +#endif + +// Disable if the stats file is too big +// Expensive +#ifndef VOXEL_SLOW_STATS +#define VOXEL_SLOW_STATS 0 +#endif + +// Will take longer to compile, but will be faster at runtime +#ifndef VOXEL_ENABLE_SLOW_OPTIMIZATIONS +#define VOXEL_ENABLE_SLOW_OPTIMIZATIONS (UE_BUILD_SHIPPING || VOXEL_PLUGIN_PACKAGED) +#endif + +// Will check that the data octree is locked for read/write +// Expensive +#ifndef DO_THREADSAFE_CHECKS +#define DO_THREADSAFE_CHECKS VOXEL_DEBUG +#endif + +// Size of a render chunk +// Bigger = less draw calls +// Smaller = faster edits +// Must be a power of 2 +#ifndef RENDER_CHUNK_SIZE +#define RENDER_CHUNK_SIZE 32 +#endif + +// Size of a data chunk +// Should leave it to default +#ifndef DATA_CHUNK_SIZE +#define DATA_CHUNK_SIZE 16 +#endif + +// No tessellation support on some platforms +#ifndef ENABLE_TESSELLATION +#define ENABLE_TESSELLATION (!PLATFORM_ANDROID && !PLATFORM_SWITCH) +#endif + +// Make UVoxelProceduralMeshComponent inherit from UModelComponent instead of UPrimitiveComponent +// to make unreal foliage painting in editor work. +// Huge hack, should disable if you don't use unreal in-editor foliage painting +#ifndef VOXEL_ENABLE_FOLIAGE_PAINT_HACK +#define VOXEL_ENABLE_FOLIAGE_PAINT_HACK 1 +#endif + +// Enables recording detailed mesher stats (eg profiles every GetValue call) +// In my tests, adds a cost < 5% of the total generation time with a flat generator, +// which is the worst cast for this as no time is spent generating values with it. +// Should be cheap enough to leave on +// NOTE: stats are recorded all the time and can only ever be cleared by the user! They are relatively small so should have no impact on memory, +// but might be an issue at some point! +#ifndef ENABLE_MESHER_STATS +#define ENABLE_MESHER_STATS (!UE_BUILD_SHIPPING) +#endif + +// Records memory stats about voxels in addition to UE's stat system +// Unlike UE's stat system, it can be used in shipping builds +// Use UVoxelBlueprintLibrary::GetMemoryUsageInMB to get the info +#ifndef ENABLE_VOXEL_MEMORY_STATS +#define ENABLE_VOXEL_MEMORY_STATS 1 +#endif + +// Record stats about voxel data accelerators +// Minimal impact on performance +#ifndef VOXEL_DATA_ACCELERATOR_STATS +#define VOXEL_DATA_ACCELERATOR_STATS VOXEL_DEBUG +#endif + +// No support for indices optimizations on some platforms +// Note: I have yet to find any performance improvements due to this +#ifndef ENABLE_OPTIMIZE_INDICES +#define ENABLE_OPTIMIZE_INDICES PLATFORM_WINDOWS +#endif + +#ifndef EIGHT_BITS_VOXEL_VALUE +#define EIGHT_BITS_VOXEL_VALUE 0 +#endif + +// If true, Voxel Materials will default to R = G = B = A = 255 +// else to R = G = B = A = 0 +#ifndef VOXEL_MATERIAL_DEFAULT_IS_WHITE +#define VOXEL_MATERIAL_DEFAULT_IS_WHITE 0 +#endif + +/** + * Voxel material config: use those to reduce the size of a FVoxelMaterial + */ + +#ifndef VOXEL_MATERIAL_ENABLE_R +#define VOXEL_MATERIAL_ENABLE_R 1 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_G +#define VOXEL_MATERIAL_ENABLE_G 1 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_B +#define VOXEL_MATERIAL_ENABLE_B 1 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_A +#define VOXEL_MATERIAL_ENABLE_A 1 +#endif + +// Each additional UV channel uses 2 bytes +#ifndef VOXEL_MATERIAL_ENABLE_UV0 +#define VOXEL_MATERIAL_ENABLE_UV0 1 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_UV1 +#define VOXEL_MATERIAL_ENABLE_UV1 1 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_UV2 +#define VOXEL_MATERIAL_ENABLE_UV2 0 +#endif +#ifndef VOXEL_MATERIAL_ENABLE_UV3 +#define VOXEL_MATERIAL_ENABLE_UV3 0 +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDelegateHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDelegateHelpers.h new file mode 100644 index 00000000..5fb3bb3b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDelegateHelpers.h @@ -0,0 +1,410 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSharedPtr.h" +#include "VoxelEngineVersionHelpers.h" + +/** + * Delegate impl for weak lambda on shared pointers + */ + +#if ENGINE_MINOR_VERSION < 26 +template +class TBaseSPFunctorDelegateInstance; + +template +class TBaseSPFunctorDelegateInstance : public IBaseDelegateInstance::Type (ParamTypes...)> +{ +public: + typedef typename TUnwrapType::Type RetValType; + +private: + typedef IBaseDelegateInstance Super; + typedef TBaseSPFunctorDelegateInstance UnwrappedThisType; + +public: + TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, FunctorType&& InFunctor) + : UserObject(InUserObject) + , Functor(MoveTemp(InFunctor)) + , Handle(FDelegateHandle::GenerateNewHandle) + { + // NOTE: Shared pointer delegates are allowed to have a null incoming object pointer. Weak pointers can expire, + // an it is possible for a copy of a delegate instance to end up with a null pointer. + } + + // IDelegateInstance interface + +#if USE_DELEGATE_TRYGETBOUNDFUNCTIONNAME + virtual FName TryGetBoundFunctionName() const override final + { + return {}; + } +#endif + virtual UObject* GetUObject() const override final + { + return nullptr; + } + + virtual const void* GetObjectForTimerManager() const override final + { + return UserObject.Pin().Get(); + } + + virtual uint64 GetBoundProgramCounterForTimerManager() const override final + { + return 0; + } + + // Deprecated + virtual bool HasSameObject(const void* InUserObject) const override final + { + return UserObject.HasSameObject(InUserObject); + } + + virtual bool IsSafeToExecute() const override final + { + return UserObject.IsValid(); + } + +public: + + // IBaseDelegateInstance interface + + virtual void CreateCopy(FDelegateBase& Base) override final + { + new (Base) UnwrappedThisType(*(UnwrappedThisType*)this); + } + + virtual RetValType Execute(ParamTypes... Params) const override final + { + typedef typename TRemoveConst::Type MutableUserClass; + + // Verify that the user object is still valid. We only have a weak reference to it. + auto SharedUserObject = UserObject.Pin(); + check(SharedUserObject.IsValid()); + + return Functor(Forward(Params)...); + } + + virtual FDelegateHandle GetHandle() const override final + { + return Handle; + } + +public: + + /** + * Creates a new shared pointer delegate binding for the given user object and lambda. + * + * @param InUserObjectRef Shared reference to the user's object that contains the class method. + * @param InFunc Lambda + * @return The new delegate. + */ + FORCEINLINE static void Create(FDelegateBase& Base, const TSharedPtr& InUserObjectRef, FunctorType&& InFunc) + { + new (Base) UnwrappedThisType(InUserObjectRef, MoveTemp(InFunc)); + } + + /** + * Creates a new shared pointer delegate binding for the given user object and lambda. + * + * This overload requires that the supplied object derives from TSharedFromThis. + * + * @param InUserObject The user's object that contains the class method. Must derive from TSharedFromThis. + * @param InFunc Lambda + * @return The new delegate. + */ + FORCEINLINE static void Create(FDelegateBase& Base, UserClass* InUserObject, FunctorType&& InFunc) + { + // We expect the incoming InUserObject to derived from TSharedFromThis. + auto UserObjectRef = StaticCastSharedRef(InUserObject->AsShared()); + Create(Base, UserObjectRef, MoveTemp(InFunc)); + } + +protected: + + // Weak reference to an instance of the user's class which contains a method we would like to call. + TWeakPtr UserObject; + + // Functor + FunctorType Functor; + + // The handle of this delegate + FDelegateHandle Handle; +}; + +template +class TBaseSPFunctorDelegateInstance : public TBaseSPFunctorDelegateInstance (ParamTypes...), FunctorType, VarTypes...> +{ + typedef TBaseSPFunctorDelegateInstance (ParamTypes...), FunctorType, VarTypes...> Super; + +public: + TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, FunctorType&& InFunctor) + : Super(InUserObject, MoveTemp(InFunctor)) + { + } + + virtual bool ExecuteIfSafe(ParamTypes... Params) const override final + { + // Verify that the user object is still valid. We only have a weak reference to it. + auto SharedUserObject = Super::UserObject.Pin(); + if (SharedUserObject.IsValid()) + { + Super::Execute(Params...); + + return true; + } + + return false; + } +}; +#else + +template +class TBaseSPFunctorDelegateInstance; + +template +class TBaseSPFunctorDelegateInstance : public TCommonDelegateInstanceState +{ +private: + static_assert(TAreTypesEqual::Type>::Value, "FunctorType cannot be a reference"); + + using Super = TCommonDelegateInstanceState; + using RetValType = typename Super::RetValType; + using UnwrappedThisType = TBaseSPFunctorDelegateInstance; + +public: + TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, const FunctorType& InFunctor) + : Super () + , UserObject(InUserObject) + , Functor (InFunctor) + { + } + + TBaseSPFunctorDelegateInstance(const TSharedPtr& InUserObject, FunctorType&& InFunctor) + : Super () + , UserObject(InUserObject) + , Functor (MoveTemp(InFunctor)) + { + } + + // IDelegateInstance interface + +#if USE_DELEGATE_TRYGETBOUNDFUNCTIONNAME + + FName TryGetBoundFunctionName() const final + { + return NAME_None; + } + +#endif + + UObject* GetUObject() const final + { + return nullptr; + } + + const void* GetObjectForTimerManager() const final + { + return UserObject.Pin().Get(); + } + + uint64 GetBoundProgramCounterForTimerManager() const final + { + return 0; + } + + // Deprecated + bool HasSameObject(const void* InUserObject) const final + { + return UserObject.HasSameObject(InUserObject); + } + + bool IsSafeToExecute() const final + { + return UserObject.IsValid(); + } + +public: + // IBaseDelegateInstance interface + void CreateCopy(FDelegateBase& Base) final + { + new (Base) UnwrappedThisType(*(UnwrappedThisType*)this); + } + + virtual RetValType Execute(ParamTypes... Params) const override final + { + typedef typename TRemoveConst::Type MutableUserClass; + + // Verify that the user object is still valid. We only have a weak reference to it. + auto SharedUserObject = UserObject.Pin(); + check(SharedUserObject.IsValid()); + + return Functor(Forward(Params)...); + } + + virtual bool ExecuteIfSafe(ParamTypes... Params) const override final + { + // Verify that the user object is still valid. We only have a weak reference to it. + if (TSharedPtr SharedUserObject = this->UserObject.Pin()) + { + (void)Functor(Forward(Params)...); + + return true; + } + + return false; + } + +public: + /** + * Creates a new shared pointer delegate binding for the given user object and lambda. + * + * @param InUserObjectRef Shared reference to the user's object that contains the class method. + * @param InFunc Lambda + * @return The new delegate. + */ + FORCEINLINE static void Create(FDelegateBase& Base, const TSharedPtr& InUserObjectRef, FunctorType&& InFunc) + { + new (Base) UnwrappedThisType(InUserObjectRef, MoveTemp(InFunc)); + } + + /** + * Creates a new shared pointer delegate binding for the given user object and lambda. + * + * This overload requires that the supplied object derives from TSharedFromThis. + * + * @param InUserObject The user's object that contains the class method. Must derive from TSharedFromThis. + * @param InFunc Lambda + * @return The new delegate. + */ + FORCEINLINE static void Create(FDelegateBase& Base, UserClass* InUserObject, FunctorType&& InFunc) + { + // We expect the incoming InUserObject to derived from TSharedFromThis. + auto UserObjectRef = StaticCastSharedRef(InUserObject->AsShared()); + Create(Base, UserObjectRef, MoveTemp(InFunc)); + } + +private: + // Context object - the validity of this object controls the validity of the lambda + TWeakPtr UserObject; + + // C++ functor + // We make this mutable to allow mutable lambdas to be bound and executed. We don't really want to + // model the Functor as being a direct subobject of the delegate (which would maintain transivity of + // const - because the binding doesn't affect the substitutability of a copied delegate. + mutable typename TRemoveConst::Type Functor; +}; + +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +struct TDelegateFromLambda : TDelegateFromLambda +{ +}; + +template +struct TDelegateFromLambda : TDelegateFromLambda +{ + +}; + +template +struct TDelegateFromLambda +{ +#if ENGINE_MINOR_VERSION < 26 + using Type = TBaseDelegate; +#else + using Type = TDelegate; +#endif + + template + using TDelegateImpl = TBaseSPFunctorDelegateInstance; +}; + +template +inline auto MakeLambdaDelegate(TLambda Lambda) +{ + return TDelegateFromLambda::Type::CreateLambda(MoveTemp(Lambda)); +} + +template +inline auto MakeWeakObjectPtrDelegate(T* Ptr, TLambda Lambda) +{ + return TDelegateFromLambda::Type::CreateWeakLambda(const_cast::Type*>(Ptr), MoveTemp(Lambda)); +} + +template +inline auto MakeWeakPtrDelegate(const TSharedRef& Object, TLambda Lambda) +{ + typename TDelegateFromLambda::Type Delegate; + TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); + return Delegate; +} + +template +inline auto MakeWeakPtrDelegate(TClass* Object, TLambda Lambda) +{ + typename TDelegateFromLambda::Type Delegate; + TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); + return Delegate; +} + +template +inline auto MakeVoxelWeakPtrDelegate(TClass* Object, TLambda Lambda) +{ + typename TDelegateFromLambda::Type Delegate; + TDelegateFromLambda::template TDelegateImpl::Create(Delegate, Object, MoveTemp(Lambda)); + return Delegate; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +struct TLambdaConditionalForward : TLambdaConditionalForward +{ +}; + +template +struct TLambdaConditionalForward : TLambdaConditionalForward +{ + +}; + +template +struct TLambdaConditionalForward +{ + template + static auto Create(TLambda Lambda, TGetCondition GetCondition, TCheckCondition CheckCondition) + { + return [=](TArgs... Args) + { + // Could be a shared pointer, or a bool + auto&& Condition = GetCondition(); + if (CheckCondition(Condition)) + { + Lambda(Forward(Args)...); + } + }; + } +}; + +template +inline auto MakeWeakPtrLambda(const T& Ptr, TLambda Lambda) +{ + return TLambdaConditionalForward::Create(Lambda, [WeakPtr = MakeWeakPtr(Ptr)]() { return WeakPtr.Pin(); }, [](const auto& InPtr) { return InPtr.IsValid(); }); +} + +template +inline auto MakeVoxelWeakPtrLambda(const T& Ptr, TLambda Lambda) +{ + return TLambdaConditionalForward::Create(Lambda, [WeakPtr = MakeVoxelWeakPtr(Ptr)]() { return WeakPtr.Pin(); }, [](const auto& InPtr) { return InPtr.IsValid(); }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDiff.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDiff.h new file mode 100644 index 00000000..2b8f5934 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDiff.h @@ -0,0 +1,53 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" + +template +struct TVoxelDiff +{ + FVoxelCellIndex Index; + T Value; + + TVoxelDiff() = default; + TVoxelDiff(FVoxelCellIndex Index, const T& Value) : Index(Index), Value(Value) {} +}; + +template +FORCEINLINE FArchive& operator<<(FArchive &Ar, TVoxelDiff& ValueDiff) +{ + Ar << ValueDiff.Index; + Ar << ValueDiff.Value; + + return Ar; +} + +template<> +FORCEINLINE FArchive& operator<<(FArchive &Ar, TVoxelDiff& ValueDiff) +{ + Ar << ValueDiff.Index; + Ar << ValueDiff.Value.GetStorage(); + + return Ar; +} + +template +struct TVoxelChunkDiff +{ + FIntVector Position; + TArray> Diffs; + + TVoxelChunkDiff() = default; + TVoxelChunkDiff(const FIntVector& Position) : Position(Position) {} +}; + +template +FORCEINLINE FArchive& operator<<(FArchive &Ar, TVoxelChunkDiff& ChunkDiff) +{ + Ar << ChunkDiff.Position; + Ar << ChunkDiff.Diffs; + + return Ar; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDirection.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDirection.h new file mode 100644 index 00000000..46210b9d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelDirection.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +namespace EVoxelDirectionFlag +{ + enum Type : uint8 + { + XMin = 0x01, + XMax = 0x02, + YMin = 0x04, + YMax = 0x08, + ZMin = 0x10, + ZMax = 0x20 + }; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegates.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegates.h new file mode 100644 index 00000000..2df4460a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegates.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UMaterial; + +struct VOXEL_API FVoxelEditorDelegates +{ + DECLARE_MULTICAST_DELEGATE_OneParam(FFixVoxelLandscapeMaterial, UMaterial*); + static FFixVoxelLandscapeMaterial FixVoxelLandscapeMaterial; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegatesInterface.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegatesInterface.h new file mode 100644 index 00000000..61fdff9c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEditorDelegatesInterface.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "VoxelEditorDelegatesInterface.generated.h" + +UINTERFACE(BlueprintType) +class VOXEL_API UVoxelEditorDelegatesInterface : public UInterface +{ + GENERATED_BODY() +}; + +class VOXEL_API IVoxelEditorDelegatesInterface : public IInterface +{ + GENERATED_BODY() + +public: +#if WITH_EDITOR + DECLARE_MULTICAST_DELEGATE_TwoParams(FBindEditorDelegates, IVoxelEditorDelegatesInterface*, UObject*); + static FBindEditorDelegates BindEditorDelegatesDelegate; + + void BindEditorDelegates(UObject* Self) + { + BindEditorDelegatesDelegate.Broadcast(this, Self); + } + + virtual void OnPreSaveWorld(uint32 SaveFlags, UWorld* World) {} + virtual void OnPreBeginPIE(bool bIsSimulating) {} + virtual void OnEndPIE(bool bIsSimulating) {} + virtual void OnPrepareToCleanseEditorObject(UObject* Object) {} + virtual void OnPreExit() {} + virtual void OnApplyObjectToActor(UObject* Object, AActor* Actor) {} +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEngineVersionHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEngineVersionHelpers.h new file mode 100644 index 00000000..2597348d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEngineVersionHelpers.h @@ -0,0 +1,80 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Launch/Resources/Version.h" + +#if ENGINE_MINOR_VERSION < 24 +#define ONLY_UE_24_AND_HIGHER(...) +#else +#define ONLY_UE_24_AND_HIGHER(...) __VA_ARGS__ +#endif + +#if ENGINE_MINOR_VERSION > 24 +#define ONLY_UE_24_AND_LOWER(...) +#else +#define ONLY_UE_24_AND_LOWER(...) __VA_ARGS__ +#endif + +#if ENGINE_MINOR_VERSION < 25 +#define ONLY_UE_25_AND_HIGHER(...) +#else +#define ONLY_UE_25_AND_HIGHER(...) __VA_ARGS__ +#endif + +#if ENGINE_MINOR_VERSION > 25 +#define ONLY_UE_25_AND_LOWER(X) +#else +#define ONLY_UE_25_AND_LOWER(...) __VA_ARGS__ +#endif + +#if ENGINE_MINOR_VERSION >= 24 +#define UE_24_SWITCH(Before, AfterOrEqual) AfterOrEqual +#define UE_24_ONLY(...) __VA_ARGS__ +#else +#define UE_24_SWITCH(Before, AfterOrEqual) Before +#define UE_24_ONLY(...) +#endif + +#if ENGINE_MINOR_VERSION >= 25 +#define UE_25_SWITCH(Before, AfterOrEqual) AfterOrEqual +#define UE_25_ONLY(...) __VA_ARGS__ +#else +#define UE_25_SWITCH(Before, AfterOrEqual) Before +#define UE_25_ONLY(...) +#endif + +#if ENGINE_MINOR_VERSION >= 26 +#define UE_26_SWITCH(Before, AfterOrEqual) AfterOrEqual +#define UE_26_ONLY(...) __VA_ARGS__ +#else +#define UE_26_SWITCH(Before, AfterOrEqual) Before +#define UE_26_ONLY(...) +#endif + +#if ENGINE_MINOR_VERSION >= 27 +#define UE_27_SWITCH(Before, AfterOrEqual) AfterOrEqual +#define UE_27_ONLY(...) __VA_ARGS__ +#else +#define UE_27_SWITCH(Before, AfterOrEqual) Before +#define UE_27_ONLY(...) +#endif + +#if ENGINE_MINOR_VERSION < 25 +using FProperty = UProperty; +using FStructProperty = UStructProperty; +using FSoftObjectProperty = USoftObjectProperty; +using FIntProperty = UIntProperty; +using FFloatProperty = UFloatProperty; +using FBoolProperty = UBoolProperty; +using FObjectProperty = UObjectProperty; +using FStructProperty = UStructProperty; +using FArrayProperty = UArrayProperty; +using FMapProperty = UMapProperty; +using FSetProperty = USetProperty; + +#define LAYOUT_FIELD(Type, Name) Type Name +#define DECLARE_TYPE_LAYOUT(...) +#define IMPLEMENT_TYPE_LAYOUT(...) +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEnums.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEnums.h new file mode 100644 index 00000000..f21ba2c1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEnums.h @@ -0,0 +1,182 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.generated.h" + +UENUM(BlueprintType) +enum class EVoxelRenderType : uint8 +{ + MarchingCubes, + Cubic, + // Surface nets only work well at LOD 0. They will have holes between higher LODs, and the material won't be picked correctly. + SurfaceNets +}; + +UENUM(BlueprintType) +enum class EVoxelNormalConfig : uint8 +{ + NoNormal, + // Use the density field gradient as normal. Might have glitches on hard corners which can be quite visible when using triplanar projection + GradientNormal, + // Each vertex will be duplicated & its normal set to the face normal + // This will disable vertex translating on transitions between LODs as the normals are not the same anymore + // This will not create any holes, but the transitions might look slightly worse (tiny vertical faces) + FlatNormal, + // Compute the normal from the mesh faces. This will have glitches on chunks borders, Gradient Normal are preferred + MeshNormal +}; + +UENUM(BlueprintType) +enum class EVoxelMaterialConfig : uint8 +{ + RGB, + SingleIndex, + DoubleIndex_DEPRECATED UMETA(Hidden), + MultiIndex +}; + +UENUM(BlueprintType) +enum class EVoxelUVConfig : uint8 +{ + GlobalUVs UMETA(DisplayName = "Global UVs"), + PackWorldUpInUVs UMETA(DisplayName = "Pack WorldUp in UVs"), + // In Cubic, per Voxel. In others, per chunk + PerVoxelUVs UMETA(DisplayName = "Per Voxel/Chunk UVs"), + Max UMETA(Hidden) +}; + +UENUM(BlueprintType) +enum class EVoxelRGBA : uint8 +{ + R, + G, + B, + A +}; + +UENUM(BlueprintType) +enum class EVoxelSpawnerActorSpawnType : uint8 +{ + // Spawn all spawner actors + All, + // Spawn only floating spawner actors + OnlyFloating +}; + +UENUM(BlueprintType) +enum class EVoxelSamplerMode : uint8 +{ + // Clamp the coordinates + Clamp, + // Tile the coordinates + Tile +}; + +enum class EVoxelPlayType +{ + Game, + Preview +}; + +UENUM(BlueprintType) +enum class EVoxelDataType : uint8 +{ + Values, + Materials +}; + +UENUM(BlueprintType) +enum class EVoxelRGBHardness : uint8 +{ + // Interpret the material as 4 way blend, and use MaterialsHardness + FourWayBlend, + // Interpret the material as 5 way blend, and use MaterialsHardness + FiveWayBlend, + // Use the Red channel as hardness + R, + // Use the Green channel as hardness + G, + // Use the Blue channel as hardness + B, + // Use the Alpha channel as hardness + A, + // Use the U0 channel as hardness + U0, + // Use the U1 channel as hardness + U1, + // Use the V0 channel as hardness + V0, + // Use the V1 channel as hardness + V1 +}; + +UENUM(BlueprintType) +enum class EVoxelFalloff : uint8 +{ + Linear, + Smooth, + Spherical, + Tip +}; + +UENUM(BlueprintType) +enum class EVoxelComputeDevice : uint8 +{ + CPU, + GPU +}; + +UENUM(BlueprintType) +enum class EVoxelAxis : uint8 +{ + X, + Y, + Z +}; + +UENUM(BlueprintType, DisplayName = "Voxel 32 bit Mask", meta = (Bitflags)) +enum class EVoxel32BitMask : uint8 +{ + Channel0, + Channel1, + Channel2, + Channel3, + Channel4, + Channel5, + Channel6, + Channel7, + Channel8, + Channel9, + Channel10, + Channel11, + Channel12, + Channel13, + Channel14, + Channel15, + Channel16, + Channel17, + Channel18, + Channel19, + Channel20, + Channel21, + Channel22, + Channel23, + Channel24, + Channel25, + Channel26, + Channel27, + Channel28, + Channel29, + Channel30, + Channel31 +}; + +UENUM() +enum class EVoxelDataItemCombineMode +{ + Min, + Max, + Sum +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEvents/VoxelEventManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEvents/VoxelEventManager.h new file mode 100644 index 00000000..d6d4f38a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelEvents/VoxelEventManager.h @@ -0,0 +1,185 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelTickable.h" + +struct FVoxelChunkMesh; +class AVoxelWorld; +class AVoxelWorldInterface; +class UVoxelInvokerComponentBase; + +DECLARE_DELEGATE_OneParam(FChunkDelegate, FVoxelIntBox); +DECLARE_MULTICAST_DELEGATE_OneParam(FChunkMulticastDelegate, FVoxelIntBox); +DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnMeshCreatedDelegate, int32, const FVoxelIntBox&, const FVoxelChunkMesh&); + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Events Memory"), STAT_VoxelEventsMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct VOXEL_API FVoxelEventManagerSettings +{ + const float UpdateRate; + const TWeakObjectPtr VoxelWorldInterface; + const TWeakObjectPtr World; + const FVoxelIntBox WorldBounds; + + FVoxelEventManagerSettings(const AVoxelWorld* World, EVoxelPlayType PlayType); +}; + +struct FVoxelEventHandle +{ + FDelegateHandle OnActivateHandle; + FDelegateHandle OnDeactivateHandle; + + int32 ChunkSize = -1; + int32 DistanceInChunks = -1; + uint32 Flags; + + inline bool IsValid() const + { + return OnActivateHandle.IsValid() || OnDeactivateHandle.IsValid(); + } +}; + +namespace EVoxelEventFlags +{ + enum Type : uint32 + { + None = 0, + GenerationEvent = 0x1, + LocalInvokerOnly = 0x2 + }; +} + +class VOXEL_API FVoxelEventManager : public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + FVoxelEventManagerSettings Settings; + + static TVoxelSharedRef Create(const FVoxelEventManagerSettings& Settings); + void Destroy(); + ~FVoxelEventManager(); + +private: + explicit FVoxelEventManager(const FVoxelEventManagerSettings& Settings); + UE_NONCOPYABLE(FVoxelEventManager); + +public: + FVoxelEventHandle BindEvent( + bool bFireExistingOnes, + int32 ChunkSize, + int32 DistanceInChunks, + const FChunkDelegate& OnActivate, + const FChunkDelegate& OnDeactivate, + EVoxelEventFlags::Type Flags = EVoxelEventFlags::None); + FVoxelEventHandle BindGenerationEvent( + bool bFireExistingOnes, + int32 ChunkSize, + int32 DistanceInChunks, + const FChunkDelegate& OnGenerate, + EVoxelEventFlags::Type Flags = EVoxelEventFlags::None); + + void UnbindEvent(FVoxelEventHandle Handle); + +public: + template + void IterateActiveChunks(int32 ChunkSize, int32 DistanceInChunks, uint32 Flags, T Lambda) const + { + const FEventKey Key{ ChunkSize, DistanceInChunks, Flags }; + if (auto* EventPtr = Events.Find(Key)) + { + auto& Event = **EventPtr; + for (auto& P : Event.ActiveOrGeneratedChunks) + { + Lambda(FVoxelIntBox(P * Event.ChunkSize, (P + 1) * Event.ChunkSize)); + } + } + } + +protected: + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +private: + struct FEventKey + { + int32 ChunkSize = -1; + int32 Distance = -1; + uint32 Flags = 0; + + FEventKey() = default; + FEventKey(int32 ChunkSize, int32 Distance, uint32 Flags) + : ChunkSize(ChunkSize) + , Distance(Distance) + , Flags(Flags) + { + } + + inline friend uint32 GetTypeHash(FEventKey Key) + { + return uint32(Key.ChunkSize) + uint32(Key.Distance * 23) + 93 * Key.Flags; + } + inline bool operator==(const FEventKey& Other) const + { + return + ChunkSize == Other.ChunkSize && + Distance == Other.Distance && + Flags == Other.Flags; + } + }; + struct FEventInvoker + { + uint32 ChunkSize = -1; + FIntVector Position; + TWeakObjectPtr InvokerComponent; + + FEventInvoker() = default; + FEventInvoker(uint32 ChunkSize, const FIntVector& Position, TWeakObjectPtr InvokerComponent) + : ChunkSize(ChunkSize) + , Position(Position) + , InvokerComponent(InvokerComponent) + { + } + }; + struct FEventInfo + { + const int32 ChunkSize; + const int32 DistanceInChunks; + const uint32 Flags; + FChunkMulticastDelegate OnActivate; + FChunkMulticastDelegate OnDeactivate; + TSet ActiveOrGeneratedChunks; // If generation event this is the list of already generated chunks + + FEventInfo(int32 ChunkSize, int32 Distance, uint32 Flags) + : ChunkSize(ChunkSize) + , DistanceInChunks(Distance) + , Flags(Flags) + { + } + + inline bool IsBound() const { return OnActivate.IsBound() || OnDeactivate.IsBound(); } + inline uint32 GetAllocatedSize() const { return sizeof(*this) + ActiveOrGeneratedChunks.GetAllocatedSize(); } + }; + + double LastUpdateTime = 0; + + TArray> OldInvokerComponents; + TMap> EventsInvokers; + TMap> Events; + + void Update(); + void UpdateInvokers(const TArray>>& InvokersToUpdate); + void ClearOldInvokerComponents(); + +private: + uint32 EventsAllocatedSize = 0; + uint32 NumEvents = 0; + uint32 NumActiveOrGeneratedChunks = 0; + + void UpdateEventsAllocatedSize(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelFeedbackContext.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelFeedbackContext.h new file mode 100644 index 00000000..248d09da --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelFeedbackContext.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Misc/ScopedSlowTask.h" + +// Use this to set the plugin to use a custom feedback context for progress bars/slow tasks +VOXEL_API void SetVoxelFeedbackContext(FFeedbackContext& FeedbackContext); + +struct VOXEL_API FVoxelScopedSlowTask : FScopedSlowTask +{ + explicit FVoxelScopedSlowTask(float InAmountOfWork, const FText& InDefaultMessage = FText(), bool bInEnabled = true); +}; + +struct FVoxelScopedFeedbackContext +{ + explicit FVoxelScopedFeedbackContext(FFeedbackContext& FeedbackContext) + { + SetVoxelFeedbackContext(FeedbackContext); + } + ~FVoxelScopedFeedbackContext() + { + SetVoxelFeedbackContext(*GWarn); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelEmptyGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelEmptyGenerator.h new file mode 100644 index 00000000..c7858a8e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelEmptyGenerator.h @@ -0,0 +1,77 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "VoxelGenerators/VoxelTransformableGeneratorHelper.h" +#include "VoxelEmptyGenerator.generated.h" + +class UVoxelEmptyGenerator; + +class FVoxelEmptyGeneratorInstance : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + // WorldUpGenerator is used by VoxelPhysics for new parts + explicit FVoxelEmptyGeneratorInstance(v_flt Value = 1, TVoxelSharedPtr WorldUpGenerator = nullptr) + : Super(nullptr) + , Value(Value) + , WorldUpGenerator(WorldUpGenerator) + { + } + + //~ Begin FVoxelGeneratorInstance Interface + v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return Value; + } + FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return FVoxelMaterial::Default(); + } + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + return Value; + } + FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return WorldUpGenerator.IsValid() ? WorldUpGenerator->GetUpVector(X, Y, Z) : FVector::UpVector; + } + //~ End FVoxelGeneratorInstance Interface + +private: + const v_flt Value; + const TVoxelSharedPtr WorldUpGenerator = nullptr; +}; + +class FVoxelTransformableEmptyGeneratorInstance : public TVoxelTransformableGeneratorHelper +{ +public: + explicit FVoxelTransformableEmptyGeneratorInstance(v_flt Value = 1) + : TVoxelTransformableGeneratorHelper(MakeVoxelShared(Value), false) + { + } +}; + +VOXEL_DEPRECATED(1.2, "Use FVoxelTransformableEmptyGeneratorInstance instead of FVoxelTransformableEmptyWorldGeneratorInstance") +typedef FVoxelTransformableEmptyGeneratorInstance FVoxelTransformableEmptyWorldGeneratorInstance; + +/** + * Empty world, can be used to remove voxels + */ +UCLASS(Blueprintable) +class VOXEL_API UVoxelEmptyGenerator : public UVoxelTransformableGenerator +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelGenerator Interface + TVoxelSharedRef GetTransformableInstance() override + { + return MakeVoxelShared(); + } + //~ End UVoxelGenerator Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelFlatGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelFlatGenerator.h new file mode 100644 index 00000000..4c997ae6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelFlatGenerator.h @@ -0,0 +1,139 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelUtilities/VoxelDataItemUtilities.h" +#include "VoxelFlatGenerator.generated.h" + +class UVoxelFlatGenerator; + +USTRUCT(BlueprintType) +struct FVoxelFlatGeneratorDataItemConfig +{ + GENERATED_BODY() + + // In voxels, how smooth the intersection with the existing terrain and these items should be + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + float Smoothness = 10; + + // Only items matching this mask will be added + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", meta = (Bitmask, BitmaskEnum = EVoxel32BitMask)) + int32 Mask = 0; + + // If true, will subtract the items from the world and will invert their values + // If false, will add the items to the world and will not invert their values + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + bool bSubtractItems = false; +}; + +/** + * Flat world + */ +UCLASS(Blueprintable) +class VOXEL_API UVoxelFlatGenerator : public UVoxelGenerator +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FLinearColor Color = FLinearColor::Transparent; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray DataItemConfigs = + { + { + 2, + 1 << 0, + true + }, + { + 2, + 1 << 1, + false + }, + }; + + //~ Begin UVoxelGenerator Interface + TVoxelSharedRef GetInstance() override; + //~ End UVoxelGenerator Interface +}; + +class FVoxelFlatGeneratorInstance : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + const FVoxelMaterial Material; + const TArray DataItemConfigs; + + explicit FVoxelFlatGeneratorInstance(UVoxelFlatGenerator& Object) + : Super(&Object) + , Material(FVoxelMaterial::CreateFromColor(Object.Color)) + , DataItemConfigs(Object.DataItemConfigs) + { + } + + //~ Begin FVoxelGeneratorInstance Interface + inline v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + v_flt Density = Z + 0.001f; // Try to avoid having 0 as density, as it behaves weirdly + + if (Items.ItemHolder.GetDataItems().Num() > 0) + { + for (auto& DataItemConfig : DataItemConfigs) + { + if (DataItemConfig.bSubtractItems) + { + Density = FVoxelUtilities::CombineDataItemDistance(Density, Items.ItemHolder, X, Y, Z, DataItemConfig.Smoothness, DataItemConfig.Mask, EVoxelDataItemCombineMode::Max); + } + else + { + Density = FVoxelUtilities::CombineDataItemDistance(Density, Items.ItemHolder, X, Y, Z, DataItemConfig.Smoothness, DataItemConfig.Mask, EVoxelDataItemCombineMode::Min); + } + } + } + + return Density; + } + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + const auto X = TVoxelRange(Bounds.Min.X, Bounds.Max.X); + const auto Y = TVoxelRange(Bounds.Min.Y, Bounds.Max.Y); + const auto Z = TVoxelRange(Bounds.Min.Z, Bounds.Max.Z); + + auto Density = Z + 0.001f; + + if (Items.ItemHolder.GetDataItems().Num() > 0) + { + for (auto& DataItemConfig : DataItemConfigs) + { + if (DataItemConfig.bSubtractItems) + { + Density = FVoxelUtilities::CombineDataItemDistanceRange(Density, Items.ItemHolder, X, Y, Z, DataItemConfig.Smoothness, DataItemConfig.Mask, EVoxelDataItemCombineMode::Max); + } + else + { + Density = FVoxelUtilities::CombineDataItemDistanceRange(Density, Items.ItemHolder, X, Y, Z, DataItemConfig.Smoothness, DataItemConfig.Mask, EVoxelDataItemCombineMode::Min); + } + } + } + + return Density; + } + inline FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return Material; + } + FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return FVector::UpVector; + } + //~ End FVoxelGeneratorInstance Interface +}; + +inline TVoxelSharedRef UVoxelFlatGenerator::GetInstance() +{ + return MakeVoxelShared(*this); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGenerator.h new file mode 100644 index 00000000..ce5393bf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGenerator.h @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelGenerator.generated.h" + +class FVoxelGeneratorInstance; +class FVoxelTransformableGeneratorInstance; +struct FVoxelGeneratorParameter; + +/** + * A UVoxelGenerator is used to create a FVoxelGeneratorInstance + */ +UCLASS(BlueprintType, Abstract) +class VOXEL_API UVoxelGenerator : public UObject +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelGenerator Interface + virtual void ApplyParameters(const TMap& Parameters); + virtual void GetParameters(TArray& OutParameters) const; + + virtual TVoxelSharedRef GetInstance(const TMap& Parameters); + virtual TVoxelSharedRef GetInstance(); + //~ End UVoxelGenerator Interface + +protected: + TMap ApplyParametersInternal(const TMap& Parameters); +}; + +// Generator that can be moved around +UCLASS(Abstract) +class VOXEL_API UVoxelTransformableGenerator : public UVoxelGenerator +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelTransformableGenerator Interface + virtual TVoxelSharedRef GetTransformableInstance(const TMap& Parameters); + virtual TVoxelSharedRef GetTransformableInstance(); + //~ End UVoxelTransformableGenerator Interface + + //~ Begin UVoxelGenerator Interface + virtual TVoxelSharedRef GetInstance(const TMap& Parameters) override; + virtual TVoxelSharedRef GetInstance() override; + //~ End UVoxelGenerator Interface +}; + +UCLASS(Abstract) +class VOXEL_API UVoxelTransformableGeneratorWithBounds : public UVoxelTransformableGenerator +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelTransformableGeneratorWithBounds Interface + virtual FVoxelIntBox GetBounds() const; + //~ End UVoxelTransformableGeneratorWithBounds Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorCache.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorCache.h new file mode 100644 index 00000000..ba61336c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorCache.h @@ -0,0 +1,57 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Templates/SubclassOf.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGeneratorCache.generated.h" + +class UVoxelGeneratorInstanceWrapper; +class UVoxelTransformableGeneratorInstanceWrapper; + +UCLASS(BlueprintType) +class VOXEL_API UVoxelGeneratorCache : public UObject +{ +public: + GENERATED_BODY() + +public: + /** + * Creates (or reuse if possible) a new generator instance + * + * Among other things, this is required for DataItemActors to reuse generators, which allows for smaller update when moving them + */ + UFUNCTION(BlueprintCallable, Category = "Voxel") + UVoxelGeneratorInstanceWrapper* MakeGeneratorInstance(FVoxelGeneratorPicker Picker) const; + + /** + * Creates (or reuse if possible) a new generator instance + * + * Among other things, this is required for DataItemActors to reuse generators, which allows for smaller update when moving them + */ + UFUNCTION(BlueprintCallable, Category = "Voxel") + UVoxelTransformableGeneratorInstanceWrapper* MakeTransformableGeneratorInstance(FVoxelTransformableGeneratorPicker Picker) const; + +public: + void SetGeneratorInit(const FVoxelGeneratorInit& NewInit) + { + GeneratorInit = NewInit; + } + void ClearCache() + { + Cache.Reset(); + } + +private: + UPROPERTY() + FVoxelGeneratorInit GeneratorInit; + + UPROPERTY() + mutable TMap Cache; + + UPROPERTY() + mutable TMap TransformableCache; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorHelpers.h new file mode 100644 index 00000000..7ab18404 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorHelpers.h @@ -0,0 +1,247 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" + +/** + * Inherit from TVoxelGeneratorInstanceHelper, and implement: + * + * For the values: + * inline v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + * + * For the materials: + * inline FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + * + * And: + * TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + * { + * return { Min, Max }; // Replace this by the possible values in Bounds + * } + */ +template< + typename TWorldInstance, + typename UWorldObject, + typename TParent = FVoxelGeneratorInstance> +class TVoxelGeneratorInstanceHelper : public TParent +{ +public: + GENERATE_MEMBER_FUNCTION_CHECK(GetValueImpl, v_flt, const, v_flt, v_flt, v_flt, int32, const FVoxelItemStack&); + GENERATE_MEMBER_FUNCTION_CHECK(GetMaterialImpl, FVoxelMaterial, const, v_flt, v_flt, v_flt, int32, const FVoxelItemStack&); + GENERATE_MEMBER_FUNCTION_CHECK(GetValueRangeImpl, TVoxelRange, const, const FVoxelIntBox&, int32, const FVoxelItemStack&); + + using FVoxelGeneratorInstance::TOutputFunctionPtr; + using FVoxelGeneratorInstance::TRangeOutputFunctionPtr; + + using FVoxelGeneratorInstance::FBaseFunctionPtrs; + using FVoxelGeneratorInstance::FCustomFunctionPtrs; + + using UStaticClass = UWorldObject; + + explicit TVoxelGeneratorInstanceHelper(const UWorldObject* Object, const FCustomFunctionPtrs& CustomFunctionPtrs = {}) + : TParent( + UWorldObject::StaticClass(), + Object, + FBaseFunctionPtrs + { + static_cast>(&TWorldInstance::GetValueImpl), + static_cast>(&TWorldInstance::GetMaterialImpl), + static_cast>(&TWorldInstance::GetValueRangeImpl), + }, + CustomFunctionPtrs) + { + // doesn't work with fwd decl static_assert(TIsDerivedFrom::IsDerived, "UWorldObject needs to inherit from UVoxelGenerator"); + static_assert(THasMemberFunction_GetValueImpl ::Value, "Missing 'v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const'"); + static_assert(THasMemberFunction_GetMaterialImpl::Value, "Missing 'FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const'"); + static_assert(THasMemberFunction_GetValueRangeImpl::Value, "Missing 'TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const'"); + } + + virtual void GetValues(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, FVoxelValue(This().GetValueImpl(X, Y, Z, LOD, Items))); + } + } + } + } + virtual void GetMaterials(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, This().GetMaterialImpl(X, Y, Z, LOD, Items)); + } + } + } + } + +private: + inline const TWorldInstance& This() const + { + return static_cast(*this); + } +}; + +/** + * If you want your generator to be placeable as an asset, + * inherit from TVoxelTransformableGeneratorInstanceHelper, and implement: + * + * For the values: + * template // bCustomTransform is false if LocalToWorld is identity + * inline v_flt GetValueImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + * + * For the materials: + * template + * inline FVoxelMaterial GetMaterialImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + * + * And: + * template + * TVoxelRange GetValueRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + * { + * return { Min, Max }; // Replace this by the possible values in Bounds + * } + */ +template< + typename TWorldInstance, + typename UWorldObject, + typename TParent = FVoxelTransformableGeneratorInstance> +class TVoxelTransformableGeneratorInstanceHelper : public TParent +{ +public: + // doesn't work with fwd decl static_assert(TIsDerivedFrom::IsDerived, "UWorldObject needs to inherit from UVoxelTransformableGenerator"); + + using FVoxelGeneratorInstance::TOutputFunctionPtr; + using FVoxelGeneratorInstance::TRangeOutputFunctionPtr; + + using FVoxelTransformableGeneratorInstance::TOutputFunctionPtr_Transform; + using FVoxelTransformableGeneratorInstance::TRangeOutputFunctionPtr_Transform; + + using FVoxelGeneratorInstance::FBaseFunctionPtrs; + using FVoxelGeneratorInstance::FCustomFunctionPtrs; + + using FVoxelTransformableGeneratorInstance::FBaseFunctionPtrs_Transform; + using FVoxelTransformableGeneratorInstance::FCustomFunctionPtrs_Transform; + + using UStaticClass = UWorldObject; + + explicit TVoxelTransformableGeneratorInstanceHelper( + const UWorldObject* Object, + const FCustomFunctionPtrs& CustomFunctionPtrs = {}, + const FCustomFunctionPtrs_Transform& CustomFunctionPtrs_Transform = {}) + : TParent( + UWorldObject::StaticClass(), + Object, + FBaseFunctionPtrs + { + static_cast>(&TWorldInstance::GetValueNoTransformImpl), + static_cast>(&TWorldInstance::GetMaterialNoTransformImpl), + static_cast>(&TWorldInstance::GetValueRangeNoTransformImpl), + }, + CustomFunctionPtrs, + FBaseFunctionPtrs_Transform + { + static_cast>(&TWorldInstance::GetValueWithTransformImpl), + static_cast>(&TWorldInstance::GetMaterialWithTransformImpl), + static_cast>(&TWorldInstance::GetValueRangeWithTransformImpl), + }, + CustomFunctionPtrs_Transform) + { + } + + virtual void GetValues(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, FVoxelValue(This().GetValueNoTransformImpl(X, Y, Z, LOD, Items))); + } + } + } + } + virtual void GetMaterials(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, This().GetMaterialNoTransformImpl(X, Y, Z, LOD, Items)); + } + } + } + } + + virtual void GetValues_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, FVoxelValue(This().GetValueWithTransformImpl(LocalToWorld, X, Y, Z, LOD, Items))); + } + } + } + } + virtual void GetMaterials_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + QueryZone.Set(X, Y, Z, This().GetMaterialWithTransformImpl(LocalToWorld, X, Y, Z, LOD, Items)); + } + } + } + } + + v_flt GetValueNoTransformImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetValueImpl(FTransform(), X, Y, Z, LOD, Items); + } + v_flt GetValueWithTransformImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetValueImpl(LocalToWorld, X, Y, Z, LOD, Items); + } + + FVoxelMaterial GetMaterialNoTransformImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetMaterialImpl(FTransform(), X, Y, Z, LOD, Items); + } + FVoxelMaterial GetMaterialWithTransformImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetMaterialImpl(LocalToWorld, X, Y, Z, LOD, Items); + } + + TVoxelRange GetValueRangeNoTransformImpl(const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetValueRangeImpl(FTransform(), WorldBounds, LOD, Items); + } + TVoxelRange GetValueRangeWithTransformImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + return This().template GetValueRangeImpl(LocalToWorld, WorldBounds, LOD, Items); + } + +private: + inline const TWorldInstance& This() const + { + return static_cast(*this); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInit.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInit.h new file mode 100644 index 00000000..84856224 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInit.h @@ -0,0 +1,58 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelMinimal.h" +#include "VoxelGeneratorInit.generated.h" + +class AVoxelWorld; +class UVoxelMaterialCollectionBase; + +USTRUCT(BlueprintType) +struct FVoxelGeneratorInit +{ + GENERATED_BODY() + + VOXEL_DEPRECATED(1.2, "Seeds are now regular generator parameters") + TMap Seeds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + float VoxelSize = 100; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + int32 WorldSize = 1 << 12; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + EVoxelRenderType RenderType = EVoxelRenderType::MarchingCubes; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig::RGB; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + const UVoxelMaterialCollectionBase* MaterialCollection = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + const AVoxelWorld* World = nullptr; // Can be null + + FVoxelGeneratorInit() = default; + FVoxelGeneratorInit( + float VoxelSize, + uint32 WorldSize, + EVoxelRenderType RenderType, + EVoxelMaterialConfig MaterialConfig, + const UVoxelMaterialCollectionBase* MaterialCollection, + const AVoxelWorld* World) + : VoxelSize(VoxelSize) + , WorldSize(WorldSize) + , RenderType(RenderType) + , MaterialConfig(MaterialConfig) + , MaterialCollection(MaterialCollection) + , World(World) + { + } +}; + +VOXEL_DEPRECATED(1.2, "Use FVoxelGeneratorInit instead of FVoxelWorldGeneratorInit.") +typedef FVoxelGeneratorInit FVoxelWorldGeneratorInit; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.h new file mode 100644 index 00000000..1688767c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.h @@ -0,0 +1,214 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelIntBox.h" +#include "Templates/SubclassOf.h" +#include "VoxelGenerators/VoxelGenerator.h" + +struct FVoxelItemStack; +struct FVoxelGeneratorInit; +class UMaterialInstanceDynamic; + +template +struct TVoxelRange; +template +class TVoxelQueryZone; + +/** + * A FVoxelGeneratorInstance is a constant object created by a UVoxelGenerator + */ +class VOXEL_API FVoxelGeneratorInstance : public TVoxelSharedFromThis +{ +public: + template + using TOutputFunctionPtr = T(FVoxelGeneratorInstance::*)(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + + template + using TRangeOutputFunctionPtr = TVoxelRange(FVoxelGeneratorInstance::*)(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const; + + struct FBaseFunctionPtrs + { + TOutputFunctionPtr Value; + TOutputFunctionPtr Material; + TRangeOutputFunctionPtr ValueRange; + }; + struct FCustomFunctionPtrs + { + TMap> Float; + TMap> Int; + TMap> Color; + + TMap> FloatRange; + }; + +public: + FVoxelGeneratorInstance( + TSubclassOf Class, + const UVoxelGenerator* Object, + const FBaseFunctionPtrs& BasePtrs, + const FCustomFunctionPtrs& CustomPtrs) + : Class(Class) + , Object(Object) + , BasePtrs(BasePtrs) + , CustomPtrs(CustomPtrs) + { + check(Class); + check(BasePtrs.Value); + check(BasePtrs.Material); + check(BasePtrs.ValueRange); + } + virtual ~FVoxelGeneratorInstance() = default; + +public: + // Used for serialization + const TSubclassOf Class; + const TSoftObjectPtr Object; + + const FBaseFunctionPtrs BasePtrs; + const FCustomFunctionPtrs CustomPtrs; + + template + const TMap>& GetOutputsPtrMap() const; + template + const TMap>& GetOutputsRangesPtrMap() const; + +public: + //~ Begin FVoxelGeneratorInstance Interface + // Initialization + virtual void Init(const FVoxelGeneratorInit& InitStruct) {} + + // Called before a chunk is computed + // Needs to be thread safe! + // EXPERIMENTAL + virtual void InitArea(const FVoxelIntBox& Bounds, int32 LOD) {} + + // Will be called on every chunk material instance + // Can be used eg to pass a texture per chunk + // EXPERIMENTAL + virtual void SetupMaterialInstance(int32 ChunkLOD, const FVoxelIntBox& ChunkBounds, UMaterialInstanceDynamic* Instance) {} + + // You should implement a fast version of that function, called every time a chunk is updated + virtual void GetValues (TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const = 0; + // This function is only called when a chunk material is edited for the first time. Fine to leave as default + virtual void GetMaterials(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const = 0; + + // World up vector at position (must be normalized). Used for spawners + virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const = 0; + //~ End FVoxelGeneratorInstance Interface + +public: + v_flt GetValue(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + FVoxelMaterial GetMaterial(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + TVoxelRange GetValueRange(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const; + + template + T Get(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + template + void Get(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const; + template + T Get(const FIntVector& P, int32 LOD, const FVoxelItemStack& Items) const; + + template + FVector GetUpVector(const TVector& P) const; + + template + T GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + template + T GetCustomOutput(T DefaultValue, FName Name, const U& P, int32 LOD, const FVoxelItemStack& Items) const; + template + TVoxelRange GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const; +}; + +class VOXEL_API FVoxelTransformableGeneratorInstance : public FVoxelGeneratorInstance +{ +public: + template + using TOutputFunctionPtr_Transform = T(FVoxelTransformableGeneratorInstance::*)(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + + template + using TRangeOutputFunctionPtr_Transform = TVoxelRange(FVoxelTransformableGeneratorInstance::*)(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const; + + struct FBaseFunctionPtrs_Transform + { + TOutputFunctionPtr_Transform Value; + TOutputFunctionPtr_Transform Material; + TRangeOutputFunctionPtr_Transform ValueRange; + }; + struct FCustomFunctionPtrs_Transform + { + TMap> Float; + TMap> Int; + TMap> Color; + + TMap> FloatRange; + }; + +public: + FVoxelTransformableGeneratorInstance( + TSubclassOf Class, + const UVoxelTransformableGenerator* Object, + const FBaseFunctionPtrs& BasePtrs, + const FCustomFunctionPtrs& CustomPtrs, + const FBaseFunctionPtrs_Transform& BasePtrs_Transform, + const FCustomFunctionPtrs_Transform& CustomPtrs_Transform) + : FVoxelGeneratorInstance(Class, Object, BasePtrs, CustomPtrs) + , BasePtrs_Transform(BasePtrs_Transform) + , CustomPtrs_Transform(CustomPtrs_Transform) + { + check(BasePtrs_Transform.Value); + check(BasePtrs_Transform.Material); + check(BasePtrs_Transform.ValueRange); + + if (auto* GeneratorWithBounds = Cast(Object)) + { + GeneratorBounds = GeneratorWithBounds->GetBounds(); + } + } + + bool HasBounds() const { return GeneratorBounds.IsSet(); } + FVoxelIntBox GetBounds() const { return GeneratorBounds.GetValue(); } + +private: + TOptional GeneratorBounds; + +public: + const FBaseFunctionPtrs_Transform BasePtrs_Transform; + const FCustomFunctionPtrs_Transform CustomPtrs_Transform; + + template + const TMap>& GetOutputsPtrMap_Transform() const; + template + const TMap>& GetOutputsRangesPtrMap_Transform() const; + + //~ Begin FVoxelTransformableGeneratorInstance Interface + virtual void GetValues_Transform (const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const = 0; + virtual void GetMaterials_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const = 0; + //~ End FVoxelTransformableGeneratorInstance Interface + +public: + v_flt GetValue_Transform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + FVoxelMaterial GetMaterial_Transform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + TVoxelRange GetValueRange_Transform(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const; + + template + T Get_Transform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + template + void Get_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const; + template + T Get_Transform(const FTransform& LocalToWorld, const FIntVector& P, int32 LOD, const FVoxelItemStack& Items) const; + + template + T GetCustomOutput_Transform(const FTransform& LocalToWorld, T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + template + TVoxelRange GetCustomOutputRange_Transform(const FTransform& LocalToWorld, TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const; +}; + +VOXEL_DEPRECATED(1.2, "Please use FVoxelGeneratorInstance instead of FVoxelWorldGeneratorInstance.") +typedef FVoxelGeneratorInstance FVoxelWorldGeneratorInstance; + +VOXEL_DEPRECATED(1.2, "Please use FVoxelTransformableGeneratorInstance instead of FVoxelTransformableWorldGeneratorInstance.") +typedef FVoxelTransformableGeneratorInstance FVoxelTransformableWorldGeneratorInstance; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.inl new file mode 100644 index 00000000..3063870f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.inl @@ -0,0 +1,360 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRange.h" +#include "VoxelQueryZone.h" +#include "VoxelItemStack.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" + +template +FORCEINLINE FVector FVoxelGeneratorInstance::GetUpVector(const TVector& P) const +{ + return GetUpVector(P.X, P.Y, P.Z); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE T FVoxelGeneratorInstance::GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + if (const auto Ptr = GetOutputsPtrMap().FindRef(Name)) + { + return (this->*Ptr)(X, Y, Z, LOD, Items); + } + else + { + return DefaultValue; + } +} +template +FORCEINLINE T FVoxelGeneratorInstance::GetCustomOutput(T DefaultValue, FName Name, const TVector& P, int32 LOD, const FVoxelItemStack& Items) const +{ + return GetCustomOutput(DefaultValue, Name, P.X, P.Y, P.Z, LOD, Items); +} + +template +FORCEINLINE TVoxelRange FVoxelGeneratorInstance::GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const +{ + if (const auto Ptr = GetOutputsRangesPtrMap().FindRef(Name)) + { + return (this->*Ptr)(Bounds, LOD, Items); + } + else + { + return DefaultValue; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE T FVoxelTransformableGeneratorInstance::GetCustomOutput_Transform(const FTransform& LocalToWorld, T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + if (const auto Ptr = GetOutputsPtrMap_Transform().FindRef(Name)) + { + return (this->*Ptr)(LocalToWorld, X, Y, Z, LOD, Items); + } + else + { + return DefaultValue; + } +} + +template +FORCEINLINE TVoxelRange FVoxelTransformableGeneratorInstance::GetCustomOutputRange_Transform(const FTransform& LocalToWorld, TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const +{ + if (const auto Ptr = GetOutputsRangesPtrMap_Transform().FindRef(Name)) + { + return (this->*Ptr)(LocalToWorld, Bounds, LOD, Items); + } + else + { + return DefaultValue; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE v_flt FVoxelGeneratorInstance::GetValue(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs.Value)(X, Y, Z, LOD, Items); +} + +FORCEINLINE FVoxelMaterial FVoxelGeneratorInstance::GetMaterial(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs.Material)(X, Y, Z, LOD, Items); +} + +FORCEINLINE TVoxelRange FVoxelGeneratorInstance::GetValueRange(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs.ValueRange)(Bounds, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE v_flt FVoxelTransformableGeneratorInstance::GetValue_Transform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs_Transform.Value)(LocalToWorld, X, Y, Z, LOD, Items); +} + +FORCEINLINE FVoxelMaterial FVoxelTransformableGeneratorInstance::GetMaterial_Transform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs_Transform.Material)(LocalToWorld, X, Y, Z, LOD, Items); +} + +FORCEINLINE TVoxelRange FVoxelTransformableGeneratorInstance::GetValueRange_Transform(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const +{ + return (this->*BasePtrs_Transform.ValueRange)(LocalToWorld, WorldBounds, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE T FVoxelGeneratorInstance::Get(const FIntVector& P, int32 LOD, const FVoxelItemStack& Items) const +{ + return Get(P.X, P.Y, P.Z, LOD, Items); +} + +template<> +FORCEINLINE FVoxelValue FVoxelGeneratorInstance::Get( + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return FVoxelValue(GetValue(X, Y, Z, LOD, Items)); +} +template<> +FORCEINLINE v_flt FVoxelGeneratorInstance::Get( + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return GetValue(X, Y, Z, LOD, Items); +} +template<> +FORCEINLINE FVoxelMaterial FVoxelGeneratorInstance::Get( + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return GetMaterial(X, Y, Z, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +template<> +FORCEINLINE void FVoxelGeneratorInstance::Get(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const +{ + GetValues(QueryZone, LOD, Items); +} +template<> +FORCEINLINE void FVoxelGeneratorInstance::Get(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const +{ + GetMaterials(QueryZone, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE T FVoxelTransformableGeneratorInstance::Get_Transform(const FTransform& LocalToWorld, const FIntVector& P, int32 LOD, const FVoxelItemStack& Items) const +{ + return Get_Transform(LocalToWorld, P.X, P.Y, P.Z, LOD, Items); +} + +template<> +FORCEINLINE FVoxelValue FVoxelTransformableGeneratorInstance::Get_Transform( + const FTransform& LocalToWorld, + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return FVoxelValue(GetValue_Transform(LocalToWorld, X, Y, Z, LOD, Items)); +} +template<> +FORCEINLINE v_flt FVoxelTransformableGeneratorInstance::Get_Transform( + const FTransform& LocalToWorld, + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return GetValue_Transform(LocalToWorld, X, Y, Z, LOD, Items); +} +template<> +FORCEINLINE FVoxelMaterial FVoxelTransformableGeneratorInstance::Get_Transform( + const FTransform& LocalToWorld, + v_flt X, v_flt Y, v_flt Z, + int32 LOD, const FVoxelItemStack& Items) const +{ + return GetMaterial_Transform(LocalToWorld, X, Y, Z, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +template<> +FORCEINLINE void FVoxelTransformableGeneratorInstance::Get_Transform( + const FTransform& LocalToWorld, + TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const +{ + GetValues_Transform(LocalToWorld, QueryZone, LOD, Items); +} +template<> +FORCEINLINE void FVoxelTransformableGeneratorInstance::Get_Transform( + const FTransform& LocalToWorld, + TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const +{ + GetMaterials_Transform(LocalToWorld, QueryZone, LOD, Items); +} + +/////////////////////////////////////////////////////////////////////////////// + +template<> +FORCEINLINE const TMap>& FVoxelGeneratorInstance::GetOutputsPtrMap() const +{ + return CustomPtrs.Float; +} + +template<> +FORCEINLINE const TMap>& FVoxelGeneratorInstance::GetOutputsPtrMap() const +{ + return CustomPtrs.Int; +} + +template<> +FORCEINLINE const TMap>& FVoxelGeneratorInstance::GetOutputsPtrMap() const +{ + return CustomPtrs.Color; +} + +template<> +FORCEINLINE const TMap>& FVoxelGeneratorInstance::GetOutputsRangesPtrMap() const +{ + return CustomPtrs.FloatRange; +} + +/////////////////////////////////////////////////////////////////////////////// + +template<> +FORCEINLINE const TMap>& FVoxelTransformableGeneratorInstance::GetOutputsPtrMap_Transform() const +{ + return CustomPtrs_Transform.Float; +} + +template<> +FORCEINLINE const TMap>& FVoxelTransformableGeneratorInstance::GetOutputsPtrMap_Transform() const +{ + return CustomPtrs_Transform.Int; +} + +template<> +FORCEINLINE const TMap>& FVoxelTransformableGeneratorInstance::GetOutputsPtrMap_Transform() const +{ + return CustomPtrs_Transform.Color; +} + +template<> +FORCEINLINE const TMap>& FVoxelTransformableGeneratorInstance::GetOutputsRangesPtrMap_Transform() const +{ + return CustomPtrs_Transform.FloatRange; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FORCEINLINE T FVoxelItemStack::Get(v_flt X, v_flt Y, v_flt Z, int32 LOD) const +{ + check(IsValid()); + if (Depth == -1) + { + return Generator->Get(X, Y, Z, LOD, *this); + } + else + { + auto& Asset = *ItemHolder.GetAssetItems()[Depth]; + return Asset.Generator->Get_Transform(Asset.LocalToWorld, X, Y, Z, LOD, *this); + } +} + +FORCEINLINE TVoxelRange FVoxelItemStack::GetValueRange(const FVoxelIntBox& Bounds, int32 LOD) const +{ + check(IsValid()); + if (Depth == -1) + { + return Generator->GetValueRange(Bounds, LOD, *this); + } + else + { + TOptional> Range; + + auto& Asset = *ItemHolder.GetAssetItems()[Depth]; + if (Asset.Bounds.Intersect(Bounds)) + { + Range = Asset.Generator->GetValueRange_Transform( + Asset.LocalToWorld, + Asset.Bounds.Overlap(Bounds), + LOD, + *this); + } + + const auto NextStack = FVoxelItemStack(ItemHolder, *Generator, Depth - 1, CustomData); + for (auto& SubBounds : Bounds.Difference(Asset.Bounds)) + { + const auto NextRange = NextStack.GetValueRange(SubBounds, LOD); + Range = Range.IsSet() ? TVoxelRange::Union(Range.GetValue(), NextRange) : NextRange; + } + + if (!ensure(Range.IsSet())) + { + Range = TVoxelRange::Infinite(); + } + + return Range.GetValue(); + } +} + +template +FORCEINLINE T FVoxelItemStack::GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const +{ + check(IsValid()); + if (Depth == -1) + { + return Generator->GetCustomOutput(DefaultValue, Name, X, Y, Z, LOD, *this); + } + else + { + auto& Asset = *ItemHolder.GetAssetItems()[Depth]; + return Asset.Generator->GetCustomOutput_Transform(Asset.LocalToWorld, DefaultValue, Name, X, Y, Z, LOD, *this); + } +} + +template +FORCEINLINE TVoxelRange FVoxelItemStack::GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD) const +{ + check(IsValid()); + if (Depth == -1) + { + return Generator->GetCustomOutputRange(DefaultValue, Name, Bounds, LOD, *this); + } + else + { + auto& Asset = *ItemHolder.GetAssetItems()[Depth]; + return Asset.Generator->GetCustomOutputRange_Transform(Asset.LocalToWorld, DefaultValue, Name, Bounds, LOD, *this); + } +} + +template +int32 FVoxelItemStack::GetNextDepth(TArgs... Args) const +{ + for (int32 Index = Depth - 1; Index >= 0; Index--) + { + auto& Item = *ItemHolder.GetAssetItems()[Index]; + if (Item.Bounds.ContainsTemplate(Args...)) + { + return Index; + } + else if (Item.Bounds.Intersect(Args...)) + { + // Invalid, must abort as multiple assets are possible + return -2; + } + } + return -1; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstanceWrapper.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstanceWrapper.h new file mode 100644 index 00000000..9ebf4a21 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstanceWrapper.h @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelGeneratorInstanceWrapper.generated.h" + +class UVoxelGenerator; +class FVoxelGeneratorInstance; +class FVoxelTransformableGeneratorInstance; + +UCLASS(BlueprintType) +class VOXEL_API UVoxelGeneratorInstanceWrapper : public UObject +{ + GENERATED_BODY() + +public: + TVoxelSharedPtr Instance; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators") + bool IsValid() const { return Instance.IsValid(); } +}; + +UCLASS(BlueprintType) +class VOXEL_API UVoxelTransformableGeneratorInstanceWrapper : public UObject +{ + GENERATED_BODY() + +public: + TVoxelSharedPtr Instance; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators") + bool IsValid() const { return Instance.IsValid(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorParameters.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorParameters.h new file mode 100644 index 00000000..b3748b37 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorParameters.h @@ -0,0 +1,141 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelGeneratorParameters.generated.h" + +UENUM() +enum class EVoxelGeneratorParameterContainerType : uint8 +{ + None, + Array, + Set, + Map +}; + +UENUM() +enum class EVoxelGeneratorParameterPropertyType : uint8 +{ + Float, + Int, + Bool, + Object, + Struct, +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelGeneratorParameterTerminalType +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + EVoxelGeneratorParameterPropertyType PropertyType = EVoxelGeneratorParameterPropertyType::Float; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FName PropertyClass; + + FString ToString_Terminal() const; + bool CanBeAssignedFrom_Terminal(const FVoxelGeneratorParameterTerminalType& Other) const; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelGeneratorParameterType : public FVoxelGeneratorParameterTerminalType +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + EVoxelGeneratorParameterContainerType ContainerType = EVoxelGeneratorParameterContainerType::None; + + // For maps + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FVoxelGeneratorParameterTerminalType ValueType; + + FVoxelGeneratorParameterType() = default; + explicit FVoxelGeneratorParameterType(FProperty& Property); + +public: + bool operator==(const FVoxelGeneratorParameterType& Other) const + { + return + PropertyType == Other.PropertyType && + PropertyClass == Other.PropertyClass; + } + bool operator!=(const FVoxelGeneratorParameterType& Other) const + { + return !(*this == Other); + } + + FString ToString() const; + bool CanBeAssignedFrom(const FVoxelGeneratorParameterType& Other) const; +}; + +USTRUCT(BlueprintType) +struct FVoxelGeneratorParameter +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FName Id; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FVoxelGeneratorParameterType Type; + + // Not consistent with vs without editor + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FString Name; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FString Category; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FString ToolTip; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + int32 Priority = 0; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + TMap MetaData; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parameter") + FString DefaultValue; + + FVoxelGeneratorParameter() = default; + FVoxelGeneratorParameter( + const FName& Id, + const FVoxelGeneratorParameterType& Type, + const FString& Name, + const FString& Category, + const FString& ToolTip, + int32 Priority, + const TMap& MetaData, + const FString& DefaultValue) + : Id(Id) + , Type(Type) + , Name(Name) + , Category(Category) + , ToolTip(ToolTip) + , Priority(Priority) + , MetaData(MetaData) + , DefaultValue(DefaultValue) + { + } + + bool operator==(const FVoxelGeneratorParameter& Other) const + { + return + Id == Other.Id && + Type == Other.Type && + Name == Other.Name && + Category == Other.Category && + ToolTip == Other.ToolTip && + Priority == Other.Priority && + MetaData.OrderIndependentCompareEqual(Other.MetaData) && + DefaultValue == Other.DefaultValue; + } + bool operator!=(const FVoxelGeneratorParameter& Other) const + { + return !(*this == Other); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorPicker.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorPicker.h new file mode 100644 index 00000000..25fd9890 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorPicker.h @@ -0,0 +1,181 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Templates/SubclassOf.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelGeneratorPicker.generated.h" + +UENUM(BlueprintType) +enum class EVoxelGeneratorPickerType : uint8 +{ + Class, + Object +}; + +template +struct TVoxelGeneratorPicker +{ +public: + using UGenerator = TGenerator; + using FGeneratorInstance = typename TChooseClass::Value, FVoxelGeneratorInstance, FVoxelTransformableGeneratorInstance>::Result; + + TVoxelGeneratorPicker() = default; + TVoxelGeneratorPicker(TYPE_OF_NULLPTR) {} + TVoxelGeneratorPicker(UClass* InClass) + { + Type = EVoxelGeneratorPickerType::Class; + Class = InClass; + } + TVoxelGeneratorPicker(TSubclassOf InClass) + : TVoxelGeneratorPicker(InClass.Get()) + { + } + TVoxelGeneratorPicker(UGenerator* InObject) + { + Type = EVoxelGeneratorPickerType::Object; + Object = InObject; + } + TVoxelGeneratorPicker(TSoftClassPtr InClass) + : TVoxelGeneratorPicker(InClass.LoadSynchronous()) + { + } + TVoxelGeneratorPicker(TSoftObjectPtr InObject) + : TVoxelGeneratorPicker(InObject.LoadSynchronous()) + { + } + + template + TVoxelGeneratorPicker(TVoxelGeneratorPicker Picker) + { + Type = Picker.Type; + Class = Picker.Class.Get(); + Object = Cast(Picker.Object); + } + +public: + EVoxelGeneratorPickerType Type = EVoxelGeneratorPickerType::Class; + TSubclassOf Class; + UGenerator* Object = nullptr; + TMap Parameters; +#if WITH_EDITORONLY_DATA + UObject* EditorData = nullptr; +#endif + + // Might return nullptr! + UGenerator* GetGenerator() const + { + if (Type == EVoxelGeneratorPickerType::Class) + { + return Class ? Class->template GetDefaultObject() : nullptr; + } + else + { + return Object; + } + } + UObject* GetObject() const + { + if (Type == EVoxelGeneratorPickerType::Class) + { + return Class; + } + else + { + return Object; + } + } + + bool IsValid() const { return GetObject() != nullptr; } + bool IsClass() const { return Type == EVoxelGeneratorPickerType::Class; } + bool IsObject() const { return Type == EVoxelGeneratorPickerType::Object; } + + bool operator==(const TVoxelGeneratorPicker& Other) const + { + // We ignore editor data here + return + Type == Other.Type && + (Type == EVoxelGeneratorPickerType::Class ? (Class == Other.Class) : (Object == Other.Object)) && + Parameters.OrderIndependentCompareEqual(Other.Parameters); + } +}; + +template +uint32 GetTypeHash(const TVoxelGeneratorPicker& Key) +{ + return HashCombine(GetTypeHash(Key.GetObject()), GetTypeHash(Key.Parameters.Num())); +} + +USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelGeneratorTools.MakeGeneratorPickerFromObject")) +struct VOXEL_API FVoxelGeneratorPicker +#if CPP + : TVoxelGeneratorPicker +#endif +{ + GENERATED_BODY() + + using TVoxelGeneratorPicker::TVoxelGeneratorPicker; + + // Will default to EmptyGenerator if null + TVoxelSharedRef GetInstance(bool bSilent) const; + +#if !CPP + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + EVoxelGeneratorPickerType Type = EVoxelGeneratorPickerType::Class; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TSubclassOf Class; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (DisallowedClasses = "VoxelGraphMacro")) + UVoxelGenerator* Object = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TMap Parameters; + +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient) + UObject* EditorData = nullptr; +#endif +#endif +}; + +USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelGeneratorTools.MakeTransformableGeneratorPickerFromObject")) +struct FVoxelTransformableGeneratorPicker +#if CPP + : TVoxelGeneratorPicker +#endif +{ + GENERATED_BODY() + + using TVoxelGeneratorPicker::TVoxelGeneratorPicker; + + // Will default to EmptyGenerator if null + TVoxelSharedRef GetInstance(bool bSilent) const; + +#if !CPP + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + EVoxelGeneratorPickerType Type = EVoxelGeneratorPickerType::Class; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TSubclassOf Class; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (DisallowedClasses = "VoxelGraphMacro")) + UVoxelTransformableGenerator* Object = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TMap Parameters; + +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient) + UObject* EditorData = nullptr; +#endif +#endif +}; + +VOXEL_DEPRECATED(1.2, "Use FVoxelGeneratorPicker instead of FVoxelWorldGeneratorPicker") +typedef FVoxelGeneratorPicker FVoxelWorldGeneratorPicker; + +VOXEL_DEPRECATED(1.2, "Use FVoxelTransformableGeneratorPicker instead of FVoxelTransformableWorldGeneratorPicker") +typedef FVoxelTransformableGeneratorPicker FVoxelTransformableWorldGeneratorPicker; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorTools.h new file mode 100644 index 00000000..fd5994c5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorTools.h @@ -0,0 +1,247 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SubclassOf.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelTexture.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGeneratorTools.generated.h" + +class AVoxelWorld; +class UTexture2D; +class UVoxelGeneratorInstanceWrapper; +class UVoxelTransformableGeneratorInstanceWrapper; + +UCLASS() +class VOXEL_API UVoxelGeneratorTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** + * Creates a new generator instance. Consider using VoxelWorld->GetGeneratorCache()->MakeGeneratorInstance instead + * @param GeneratorPicker The picker + * @param GeneratorInit The generator init. Use VoxelWorld->GetGeneratorInit to get it + * @return The generator instance + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta=(Keywords="construct build")) + static UVoxelGeneratorInstanceWrapper* MakeGeneratorInstance(FVoxelGeneratorPicker GeneratorPicker, FVoxelGeneratorInit GeneratorInit); + + /** + * Creates a new transformable generator instance. Consider using VoxelWorld->GetGeneratorCache()->MakeTransformableGeneratorInstance instead + * @param GeneratorPicker The picker + * @param GeneratorInit The generator init. Use VoxelWorld->GetGeneratorInit to get it + * @return The generator instance + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta=(Keywords="construct build")) + static UVoxelTransformableGeneratorInstanceWrapper* MakeTransformableGeneratorInstance(FVoxelTransformableGeneratorPicker GeneratorPicker, FVoxelGeneratorInit GeneratorInit); + +public: + /** + * @see MakeGeneratorPickerFromClass, MakeTransformableGeneratorPickerFromObject + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelGeneratorPicker MakeGeneratorPickerFromObject(UVoxelGenerator* Generator) { return Generator; } + /** + * @see MakeTransformableGeneratorPickerFromClass, MakeGeneratorPickerFromObject + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelTransformableGeneratorPicker MakeTransformableGeneratorPickerFromObject(UVoxelTransformableGenerator* Generator) { return Generator; } + + /** + * @see MakeGeneratorPickerFromObject + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelGeneratorPicker MakeGeneratorPickerFromClass(TSubclassOf GeneratorClass) { return GeneratorClass; } + /** + * @see MakeTransformableGeneratorPickerFromObject, MakeGeneratorPickerFromClass + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelTransformableGeneratorPicker MakeTransformableGeneratorPickerFromClass(TSubclassOf GeneratorClass) { return GeneratorClass; } + + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", DisplayName = "Is Valid") + static bool IsValid_GeneratorPicker(FVoxelGeneratorPicker GeneratorPicker) { return GeneratorPicker.IsValid(); } + UFUNCTION(BlueprintPure, Category = "Voxel|Generators", DisplayName = "Is Valid") + static bool IsValid_TransformableGeneratorPicker(FVoxelTransformableGeneratorPicker GeneratorPicker) { return GeneratorPicker.IsValid(); } + +public: + static bool SetGeneratorParameterImpl( + TVoxelGeneratorPicker& Picker, + FName Name, + FProperty& Property, + void* Data, + const FString& FunctionName); + static bool CheckIsValidParameterName( + TVoxelGeneratorPicker GeneratorPicker, + FName Name, + FProperty& Property, + const FString& FunctionName); + + /** + * Set a voxel generator parameter + * @param Picker The generator picker, by ref + * @param UniqueName The name of the parameter. Note that this is not the display name, but the parameter unique name. + * You can get that unique name by checking the tooltip of the parameter in the picker details + * @param Value The value + * @return Success + */ + UFUNCTION(BlueprintCallable, CustomThunk, Category = "Voxel|Generators", meta = (CustomStructureParam = "Value")) + static bool SetGeneratorParameter(const FVoxelGeneratorPicker& Picker, FName UniqueName, int32 Value); + + /** + * Set a voxel generator parameter + * @param Picker The generator picker, by ref + * @param UniqueName The name of the parameter. Note that this is not the display name, but the parameter unique name. + * You can get that unique name by checking the tooltip of the parameter in the picker details + * @param Value The value + * @return Success + */ + UFUNCTION(BlueprintCallable, CustomThunk, Category = "Voxel|Generators", meta = (CustomStructureParam = "Value")) + static bool SetTransformableGeneratorParameter(const FVoxelTransformableGeneratorPicker& Picker, FName UniqueName, int32 Value); + +private: + DECLARE_FUNCTION(execSetGeneratorParameter) + { + execSetGeneratorParameterImpl(Context, Stack, RESULT_PARAM); + } + + DECLARE_FUNCTION(execSetTransformableGeneratorParameter) + { + execSetGeneratorParameterImpl(Context, Stack, RESULT_PARAM); + } + + DECLARE_FUNCTION(execSetGeneratorParameterImpl) + { + P_GET_STRUCT_REF(TVoxelGeneratorPicker, Picker); + P_GET_STRUCT(FName, Name); + + Stack.StepCompiledIn(nullptr); + + P_FINISH; + bool bSuccess = false; + + if (Stack.MostRecentProperty) + { + bSuccess = SetGeneratorParameterImpl(Picker, Name, *Stack.MostRecentProperty, Stack.MostRecentPropertyAddress, "SetGeneratorParameter"); + } + else + { + const FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::AccessViolation, VOXEL_LOCTEXT("Failed to resolve the Value parameter for SetGeneratorParameter.")); + FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo); + } + *static_cast(RESULT_PARAM) = bSuccess; + } + +public: + // Scale is applied to (Start + Position) + template + static TVoxelTexture CreateTextureFromGeneratorImpl( + const FVoxelGeneratorInstance& Generator, + FName OutputName, + const FIntPoint& Start, + const FIntPoint& Size, + float Scale); + + /** + * Creates a float texture by reading a float output from a generator + * + * @param OutTexture The result + * @param Generator The generator to use + * @param OutputName The output name to query. Must be a float output. + * @param SizeX The Size of the resulting texture on the X axis + * @param SizeY The Size of the resulting texture on the Y axis + * @param Scale Scale that can be used to scale the inputs: the generator will be queried as (Start + Position) * Scale + * @param StartX Where the texture starts + * @param StartY Where the texture starts + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta = (AdvancedDisplay = "StartX, StartY, VoxelSize")) + static void CreateFloatTextureFromGenerator( + FVoxelFloatTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName = "Value", + int32 SizeX = 512, + int32 SizeY = 512, + float Scale = 1, + int32 StartX = 0, + int32 StartY = 0); + + /** + * Creates a float texture by reading a float output from a generator, asynchronously + * + * @param OutTexture The result + * @param Generator The generator to use + * @param OutputName The output name to query. Must be a float output. + * @param SizeX The Size of the resulting texture on the X axis + * @param SizeY The Size of the resulting texture on the Y axis + * @param Scale Scale that can be used to scale the inputs: the generator will be queried as (Start + Position) * Scale + * @param StartX Where the texture starts + * @param StartY Where the texture starts + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta = (Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "StartX, StartY, VoxelSize, bHideLatentWarnings")) + static void CreateFloatTextureFromGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelFloatTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName = "Value", + int32 SizeX = 512, + int32 SizeY = 512, + float Scale = 1, + int32 StartX = 0, + int32 StartY = 0, + bool bHideLatentWarnings = false); + +public: + /** + * Creates a color texture by reading a color output from a generator + * + * @param OutTexture The result + * @param Generator The generator to use + * @param OutputName The output name to query. Must be a color output. + * @param SizeX The Size of the resulting texture on the X axis + * @param SizeY The Size of the resulting texture on the Y axis + * @param Scale Scale that can be used to scale the inputs: the generator will be queried as (Start + Position) * Scale + * @param StartX Where the texture starts + * @param StartY Where the texture starts + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta = (AdvancedDisplay = "StartX, StartY, VoxelSize")) + static void CreateColorTextureFromGenerator( + FVoxelColorTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName = "MyColor", + int32 SizeX = 512, + int32 SizeY = 512, + float Scale = 1, + int32 StartX = 0, + int32 StartY = 0); + + /** + * Creates a color texture by reading a color output from a generator, asynchronously + * + * @param OutTexture The result + * @param Generator The generator to use + * @param OutputName The output name to query. Must be a color output. + * @param SizeX The Size of the resulting texture on the X axis + * @param SizeY The Size of the resulting texture on the Y axis + * @param Scale Scale that can be used to scale the inputs: the generator will be queried as (Start + Position) * Scale + * @param StartX Where the texture starts + * @param StartY Where the texture starts + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Generators", meta = (Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "StartX, StartY, VoxelSize, bHideLatentWarnings")) + static void CreateColorTextureFromGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelColorTexture& OutTexture, + UVoxelGeneratorInstanceWrapper* Generator, + FName OutputName = "MyColor", + int32 SizeX = 512, + int32 SizeY = 512, + float Scale = 1, + int32 StartX = 0, + int32 StartY = 0, + bool bHideLatentWarnings = false); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelTransformableGeneratorHelper.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelTransformableGeneratorHelper.h new file mode 100644 index 00000000..86e744e9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelGenerators/VoxelTransformableGeneratorHelper.h @@ -0,0 +1,138 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUtilities/VoxelRangeUtilities.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" + +template +class TVoxelTransformableGeneratorHelper : public TVoxelTransformableGeneratorInstanceHelper, TObject> +{ +public: + using Super = TVoxelTransformableGeneratorInstanceHelper, TObject>; + + const TVoxelSharedRef Generator; + const bool bSubtractiveAsset; + + explicit TVoxelTransformableGeneratorHelper( + const TVoxelSharedRef& Generator, + bool bSubtractiveAsset) + : Super(Cast(Generator->Object.Get())) + , Generator(Generator) + , bSubtractiveAsset(bSubtractiveAsset) + { + ensure(!Generator->Object.IsValid() || this->Object.IsValid()); + } + + //~ Begin FVoxelGeneratorInstance Interface + virtual void Init(const FVoxelGeneratorInit& InitStruct) + { + Generator->Init(InitStruct); + } + + template + inline float GetValueImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + const FVector P = GetLocalPosition(LocalToWorld, X, Y, Z); + const v_flt Value = Generator->GetValueImpl(P.X, P.Y, P.Z, LOD, Items); + if (Items.IsEmpty() || FVoxelValue(Value) == (bSubtractiveAsset ? FVoxelValue::Empty() : FVoxelValue::Full())) + { + // No need to merge as we are the best value possible + return Value; + } + else + { + const auto NextStack = Items.GetNextStack(X, Y, Z); + const auto NextValue = NextStack.Get(X, Y, Z, LOD); + return FVoxelUtilities::MergeAsset(Value, NextValue, bSubtractiveAsset); + } + } + + template + inline FVoxelMaterial GetMaterialImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + const FVector P = GetLocalPosition(LocalToWorld, X, Y, Z); + + const auto GetGeneratorMaterial = [&]() + { + return Generator->GetMaterialImpl(P.X, P.Y, P.Z, LOD, Items); + }; + + if (Items.IsEmpty()) + { + return GetGeneratorMaterial(); + } + + const FVoxelValue Value = FVoxelValue(Generator->GetValueImpl(P.X, P.Y, P.Z, LOD, Items)); + if ((bSubtractiveAsset && Value == FVoxelValue::Empty()) || + (!bSubtractiveAsset && Value == FVoxelValue::Full())) + { + // No need to check further down + return GetGeneratorMaterial(); + } + + const auto NextStack = Items.GetNextStack(X, Y, Z); + const auto NextValue = NextStack.Get(X, Y, Z, LOD); + if (bSubtractiveAsset ? Value >= NextValue : Value <= NextValue) + { + // We have a better value + return GetGeneratorMaterial(); + } + else + { + return NextStack.Get(X, Y, Z, LOD); + } + } + + template + TVoxelRange GetValueRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + const FVoxelIntBox LocalBounds = bCustomTransform ? WorldBounds.ApplyTransform(LocalToWorld) : WorldBounds; + const TVoxelRange GeneratorRange = Generator->GetValueRangeImpl(LocalBounds, LOD, Items); + + const auto GetNextRange = [&]() -> TVoxelRange + { + if (Items.IsEmpty()) + { + return bSubtractiveAsset ? -1 : 1; + } + else + { + const auto NextStack = Items.GetNextStack(WorldBounds); + if (NextStack.IsValid()) + { + return NextStack.GetValueRange(WorldBounds, LOD); + } + else + { + return TVoxelRange::Infinite(); + } + } + }; + const TVoxelRange NextRange = GetNextRange(); + + return bSubtractiveAsset + ? FVoxelRangeUtilities::Max(GeneratorRange, NextRange) + : FVoxelRangeUtilities::Min(GeneratorRange, NextRange); + } + FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return Generator->GetUpVector(X, Y, Z); + } + //~ End FVoxelGeneratorInstance Interface + +private: + template + FORCEINLINE FVector GetLocalPosition(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z) const + { + if (bCustomTransform) + { + const auto LocalPosition = LocalToWorld.InverseTransformPosition(FVector(X, Y, Z)); + X = LocalPosition.X; + Y = LocalPosition.Y; + Z = LocalPosition.Z; + } + return FVector(X, Y, Z); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelLandscapeImporter.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelLandscapeImporter.h new file mode 100644 index 00000000..3613f27f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelLandscapeImporter.h @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "GameFramework/Actor.h" +#include "VoxelLandscapeImporter.generated.h" + +class ULandscapeLayerInfoObject; +class ALandscape; + +USTRUCT() +struct VOXEL_API FVoxelLandscapeImporterLayerInfo +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + ULandscapeLayerInfoObject* LayerInfo = nullptr; + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelRGBA Layer = EVoxelRGBA::R; + + UPROPERTY(EditAnywhere, Category = "Voxel") + uint8 Index = 0; +}; + +UCLASS(BlueprintType, HideCategories = ("Tick", "Replication", "Input", "Actor", "Rendering", "HOLD", "LOD", "Cooking")) +class VOXEL_API AVoxelLandscapeImporter : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Import configuration") + ALandscape* Landscape = nullptr; + + UPROPERTY(EditAnywhere, Category = "Import configuration") + EVoxelHeightmapImporterMaterialConfig MaterialConfig = EVoxelHeightmapImporterMaterialConfig::RGB; + + UPROPERTY(EditAnywhere, Category = "Import configuration") + TArray LayerInfos; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelMeshImporter.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelMeshImporter.h new file mode 100644 index 00000000..9c6756f4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelImporters/VoxelMeshImporter.h @@ -0,0 +1,279 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "GameFramework/Actor.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelMeshImporter.generated.h" + +class UTexture; +class UTextureRenderTarget2D; +class UVoxelDataAsset; +class UStaticMesh; +class UMaterialInstanceDynamic; +class UStaticMeshComponent; +struct FVoxelDataAssetData; + +struct FVoxelMeshImporterInputData +{ + TArray Vertices; + TArray Triangles; + TArray UVs; +}; + +// We don't want to copy the arrays in the BP, so use an object for that +UCLASS(BlueprintType) +class VOXEL_API UVoxelMeshImporterInputData : public UObject +{ + GENERATED_BODY() + +public: + FVoxelMeshImporterInputData Data; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelMeshImporterRenderTargetCache +{ + GENERATED_BODY() + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + UTextureRenderTarget2D* ColorsRenderTarget = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + UTextureRenderTarget2D* UVsRenderTarget = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + UMaterialInterface* LastRenderedColorsMaterial = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + UMaterialInterface* LastRenderedUVsMaterial = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + int32 LastRenderedRenderTargetSize = 0; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelMeshImporterSettingsBase +{ + GENERATED_BODY() + + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, meta = (ClampMin = "0")) + float VoxelSize = 100; + + // Sweep direction to determine the voxel signs. If you have a plane, use Z + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere) + EVoxelAxis SweepDirection = EVoxelAxis::X; + + // Will do the sweep the other way around: eg, if SweepDirection = Z, the sweep will be done top to bottom if true + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere) + bool bReverseSweep = true; + + // If true, will assume every line of voxels starts outside the mesh, then goes inside, then goes outside it + // Set to false if you have a shell and not a true volume + // For example: + // - sphere: set to true + // - half sphere with no bottom geometry: set to false + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere) + bool bWatertight = true; + + // If true, will hide leaks by having holes instead + // If false, leaks will be long tubes going through the entire asset + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, AdvancedDisplay) + bool bHideLeaks = true; + + // Distance will be exact for voxels under this distance from a triangle + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, AdvancedDisplay, meta = (ClampMin = "1")) + int32 ExactBand = 1; + + // Increase this if the shadows/normals quality is bad. Might require to increase MaxVoxelDistanceFromTriangle + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, AdvancedDisplay) + float DistanceDivisor = 1; + +public: + friend bool operator==(const FVoxelMeshImporterSettingsBase& Lhs, const FVoxelMeshImporterSettingsBase& RHS) + { + return Lhs.VoxelSize == RHS.VoxelSize + && Lhs.SweepDirection == RHS.SweepDirection + && Lhs.bWatertight == RHS.bWatertight + && Lhs.bReverseSweep == RHS.bReverseSweep + && Lhs.bHideLeaks == RHS.bHideLeaks + && Lhs.ExactBand == RHS.ExactBand + && Lhs.DistanceDivisor == RHS.DistanceDivisor; + } + friend bool operator!=(const FVoxelMeshImporterSettingsBase& Lhs, const FVoxelMeshImporterSettingsBase& RHS) + { + return !(Lhs == RHS); + } +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelMeshImporterSettings : public FVoxelMeshImporterSettingsBase +{ + GENERATED_BODY() + + FVoxelMeshImporterSettings(); + explicit FVoxelMeshImporterSettings(const FVoxelMeshImporterSettingsBase& Base); + + // Will sample ColorsMaterial at the mesh UVs to get the voxel colors + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere) + bool bImportColors = true; + + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, meta = (EditCondition = "bPaintColors")) + UMaterialInterface* ColorsMaterial = nullptr; + + // Will sample UVChannelsMaterial at the mesh UVs to get the voxel UVs + // RG will go in first UV channel, BA in second + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere) + bool bImportUVs = true; + + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, meta = (EditCondition = bPaintUVs)) + UMaterialInterface* UVsMaterial = nullptr; + + UPROPERTY(Category = "Import Configuration", BlueprintReadWrite, EditAnywhere, AdvancedDisplay) + int32 RenderTargetSize = 4096; + +public: + friend bool operator==(const FVoxelMeshImporterSettings& Lhs, const FVoxelMeshImporterSettings& RHS) + { + return static_cast(Lhs) == static_cast(RHS) + && Lhs.bImportColors == RHS.bImportColors + && Lhs.ColorsMaterial == RHS.ColorsMaterial + && Lhs.bImportUVs == RHS.bImportUVs + && Lhs.UVsMaterial == RHS.UVsMaterial + && Lhs.RenderTargetSize == RHS.RenderTargetSize; + } + friend bool operator!=(const FVoxelMeshImporterSettings& Lhs, const FVoxelMeshImporterSettings& RHS) + { + return !(Lhs == RHS); + } +}; + +UCLASS() +class VOXEL_API UVoxelMeshImporterLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + static void CreateMeshDataFromStaticMesh(UStaticMesh* StaticMesh, FVoxelMeshImporterInputData& Data); + + static bool ConvertMeshToVoxels( + UObject* WorldContextObject, + const FVoxelMeshImporterInputData& Mesh, + const FTransform& Transform, + const FVoxelMeshImporterSettings& Settings, + FVoxelMeshImporterRenderTargetCache& RenderTargetCache, + FVoxelDataAssetData& OutAsset, + FIntVector& OutOffset, + int32& OutNumLeaks); + + static void ConvertMeshToDistanceField( + const FVoxelMeshImporterInputData& Mesh, + const FTransform& Transform, + const FVoxelMeshImporterSettingsBase& Settings, + // Needed if we want a smooth import, in voxels + float BoxExtension, + TArray& OutDistanceField, + TArray& OutSurfacePositions, + FIntVector& OutSize, + FIntVector& OutOffset, + int32& OutNumLeaks, + EVoxelComputeDevice Device = EVoxelComputeDevice::GPU, + bool bMultiThreaded = true, + int32 MaxPasses_Debug = -1); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Mesh Importer") + static UVoxelMeshImporterInputData* CreateMeshDataFromStaticMesh(UStaticMesh* StaticMesh); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Mesh Importer", meta = (WorldContext = "WorldContextObject")) + static UTextureRenderTarget2D* CreateTextureFromMaterial( + UObject* WorldContextObject, + UMaterialInterface* Material, + int32 Width = 1024, + int32 Height = 1024); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Mesh Importer", meta = (WorldContext = "WorldContextObject")) + static void ConvertMeshToVoxels( + UObject* WorldContextObject, + UVoxelMeshImporterInputData* Mesh, + FTransform Transform, + bool bSubtractive, + FVoxelMeshImporterSettings Settings, + UPARAM(ref) FVoxelMeshImporterRenderTargetCache& RenderTargetCache, + UVoxelDataAsset*& Asset, + int32& NumLeaks); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Mesh Importer", meta = (WorldContext = "WorldContextObject")) + static void ConvertMeshToVoxels_NoMaterials( + UObject* WorldContextObject, + UVoxelMeshImporterInputData* Mesh, + FTransform Transform, + bool bSubtractive, + FVoxelMeshImporterSettingsBase Settings, + UVoxelDataAsset*& Asset, + int32& NumLeaks); +}; + +/** + * Actor that creates a VoxelDataAsset from a static mesh + */ +UCLASS(NotBlueprintType, NotBlueprintable, HideCategories = ("Tick", "Replication", "Input", "Actor", "Rendering", "HOLD", "LOD", "Cooking")) +class VOXEL_API AVoxelMeshImporter : public AActor +{ + GENERATED_BODY() + +public: + // The static mesh to import from + UPROPERTY(EditAnywhere, Category = "Import Configuration") + UStaticMesh* StaticMesh; + + UPROPERTY(EditAnywhere, Category = "Import Configuration", meta = (ShowOnlyInnerProperties)) + FVoxelMeshImporterSettings Settings; + + UPROPERTY(VisibleAnywhere, Category = "Expected Size") + uint32 SizeX; + + UPROPERTY(VisibleAnywhere, Category = "Expected Size") + uint32 SizeY; + + UPROPERTY(VisibleAnywhere, Category = "Expected Size") + uint32 SizeZ; + + UPROPERTY(VisibleAnywhere, Category = "Expected Size") + uint64 NumberOfVoxels; + + UPROPERTY(VisibleAnywhere, Category = "Expected Size") + float SizeInMB; + + AVoxelMeshImporter(); + +protected: + virtual void Tick(float DeltaSeconds) override; +#if WITH_EDITOR + void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + virtual bool ShouldTickIfViewportsOnly() const override { return true; } +#endif + +private: + UPROPERTY() + UStaticMeshComponent* MeshComponent; + + UPROPERTY(Transient) + UMaterialInstanceDynamic* MaterialInstance; + + UPROPERTY(Transient) + FBox CachedBox; + + UPROPERTY(Transient) + UStaticMesh* CachedStaticMesh; + + UPROPERTY(Transient) + TArray CachedVertices; + + UPROPERTY(Transient) + FTransform CachedTransform; + + void InitMaterialInstance(); + void UpdateSizes(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBox.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBox.h new file mode 100644 index 00000000..ea12924f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBox.h @@ -0,0 +1,783 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUtilities/VoxelVectorUtilities.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" +#include "Async/ParallelFor.h" +#include "VoxelIntBox.generated.h" + +enum class EInverseTransform : uint8 +{ + True, + False +}; + +/** + * A Box with int32 coordinates + */ +USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelIntBoxLibrary.MakeIntBox", HasNativeBreak="Voxel.VoxelIntBoxLibrary.BreakIntBox")) +struct VOXEL_API FVoxelIntBox +{ + GENERATED_BODY() + + // Min of the box. Inclusive + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Min; + + // Max of the box. Exclusive + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Max; + + const static FVoxelIntBox Infinite; + + FVoxelIntBox() + : Min(FIntVector::ZeroValue) + , Max(FIntVector::ZeroValue) + { + } + + FVoxelIntBox(const FIntVector& InMin, const FIntVector& InMax) + : Min(InMin) + , Max(InMax) + { + if (!ensureMsgf(Min.X <= Max.X, TEXT("%d <= %d"), Min.X, Max.X)) Max.X = Min.X; + if (!ensureMsgf(Min.Y <= Max.Y, TEXT("%d <= %d"), Min.Y, Max.Y)) Max.Y = Min.Y; + if (!ensureMsgf(Min.Z <= Max.Z, TEXT("%d <= %d"), Min.Z, Max.Z)) Max.Z = Min.Z; + } + explicit FVoxelIntBox(int32 Min, const FIntVector& Max) + : FVoxelIntBox(FIntVector(Min), Max) + { + } + explicit FVoxelIntBox(const FIntVector& Min, int32 Max) + : FVoxelIntBox(Min, FIntVector(Max)) + { + } + explicit FVoxelIntBox(int32 Min, int32 Max) + : FVoxelIntBox(FIntVector(Min), FIntVector(Max)) + { + } + explicit FVoxelIntBox(const FVector& Min, const FVector& Max) + : FVoxelIntBox(FVoxelUtilities::FloorToInt(Min), FVoxelUtilities::CeilToInt(Max) + 1) + { + } + explicit FVoxelIntBox(const FVoxelVector& Min, const FVoxelVector& Max) + : FVoxelIntBox(FVoxelUtilities::FloorToInt(Min), FVoxelUtilities::CeilToInt(Max) + 1) + { + } + + explicit FVoxelIntBox(const FVector& Position) + : FVoxelIntBox(Position, Position) + { + } + explicit FVoxelIntBox(const FVoxelVector& Position) + : FVoxelIntBox(Position, Position) + { + } + + explicit FVoxelIntBox(const FIntVector& Position) + : FVoxelIntBox(Position, Position + 1) + { + } + explicit FVoxelIntBox(const FBox& Box) + : FVoxelIntBox(Box.Min, Box.Max) + { + } + + explicit FVoxelIntBox(int32 X, int32 Y, int32 Z) + : FVoxelIntBox(FIntVector(X, Y, Z), FIntVector(X + 1, Y + 1, Z + 1)) + { + } + + template + explicit FVoxelIntBox(const TArray& Data) + { + if (!ensure(Data.Num() > 0)) + { + Min = Max = FIntVector::ZeroValue; + return; + } + + *this = FVoxelIntBox(Data[0]); + for (int32 Index = 1; Index < Data.Num(); Index++) + { + *this = *this + Data[Index]; + } + } + + FORCEINLINE static FVoxelIntBox SafeConstruct(const FIntVector& A, const FIntVector& B) + { + FVoxelIntBox Box; + Box.Min = FVoxelUtilities::ComponentMin(A, B); + Box.Max = FVoxelUtilities::ComponentMax3(A, B, Box.Min + FIntVector(1, 1, 1)); + return Box; + } + FORCEINLINE static FVoxelIntBox SafeConstruct(const FVoxelVector& A, const FVoxelVector& B) + { + FVoxelIntBox Box; + Box.Min = FVoxelUtilities::FloorToInt(FVoxelUtilities::ComponentMin(A, B)); + Box.Max = FVoxelUtilities::CeilToInt(FVoxelUtilities::ComponentMax3(A, B, Box.Min + FIntVector(1, 1, 1))); + return Box; + } + + FORCEINLINE FIntVector Size() const + { + ensure(SizeIs32Bit()); + return Max - Min; + } + FORCEINLINE FVoxelVector GetCenter() const + { + return FVoxelVector(Min + Max) / 2.f; + } + FORCEINLINE uint64 Count() const + { + return + uint64(Max.X - Min.X) * + uint64(Max.Y - Min.Y) * + uint64(Max.Z - Min.Z); + } + + FORCEINLINE bool SizeIs32Bit() const + { + return + int64(Max.X) - int64(Min.X) < MAX_int32 && + int64(Max.Y) - int64(Min.Y) < MAX_int32 && + int64(Max.Z) - int64(Min.Z) < MAX_int32; + } + + /** + * Get the corners that are inside the box (max - 1) + */ + TVoxelStaticArray GetCorners(int32 MaxBorderSize) const + { + return { + FIntVector(Min.X, Min.Y, Min.Z), + FIntVector(Max.X - MaxBorderSize, Min.Y, Min.Z), + FIntVector(Min.X, Max.Y - MaxBorderSize, Min.Z), + FIntVector(Max.X - MaxBorderSize, Max.Y - MaxBorderSize, Min.Z), + FIntVector(Min.X, Min.Y, Max.Z - MaxBorderSize), + FIntVector(Max.X - MaxBorderSize, Min.Y, Max.Z - MaxBorderSize), + FIntVector(Min.X, Max.Y - MaxBorderSize, Max.Z - MaxBorderSize), + FIntVector(Max.X - MaxBorderSize, Max.Y - MaxBorderSize, Max.Z - MaxBorderSize) + }; + } + FString ToString() const + { + return FString::Printf(TEXT("(%d/%d, %d/%d, %d/%d)"), Min.X, Max.X, Min.Y, Max.Y, Min.Z, Max.Z); + } + + FORCEINLINE bool IsValid() const + { + return Min.X < Max.X && Min.Y < Max.Y && Min.Z < Max.Z; + } + + template + FORCEINLINE bool ContainsTemplate(T X, T Y, T Z) const + { + return ((X >= Min.X) && (X < Max.X) && (Y >= Min.Y) && (Y < Max.Y) && (Z >= Min.Z) && (Z < Max.Z)); + } + template + FORCEINLINE typename TEnableIf, TIsSame, TIsSame>::Value, bool>::Type ContainsTemplate(const T& V) const + { + return ContainsTemplate(V.X, V.Y, V.Z); + } + template + FORCEINLINE typename TEnableIf, TIsSame>::Value, bool>::Type ContainsTemplate(const T& Other) const + { + return Min.X <= Other.Min.X && Min.Y <= Other.Min.Y && Min.Z <= Other.Min.Z && + Max.X >= Other.Max.X && Max.Y >= Other.Max.Y && Max.Z >= Other.Max.Z; + } + + FORCEINLINE bool Contains(int32 X, int32 Y, int32 Z) const + { + return ContainsTemplate(X, Y, Z); + } + FORCEINLINE bool Contains(const FIntVector& V) const + { + return ContainsTemplate(V); + } + FORCEINLINE bool Contains(const FVoxelIntBox& Other) const + { + return ContainsTemplate(Other); + } + + // Not an overload as the float behavior can be a bit tricky. Use ContainsTemplate if the input type is unknown + FORCEINLINE bool ContainsFloat(float X, float Y, float Z) const + { + return ContainsTemplate(X, Y, Z); + } + FORCEINLINE bool ContainsFloat(const FVector& V) const + { + return ContainsTemplate(V); + } + FORCEINLINE bool ContainsFloat(const FVoxelVector& V) const + { + return ContainsTemplate(V); + } + FORCEINLINE bool ContainsFloat(const FBox& Other) const + { + return ContainsTemplate(Other); + } + + template + bool Contains(T X, T Y, T Z) const = delete; + + template + FORCEINLINE FIntVector Clamp(T P) const + { + Clamp(P.X, P.Y, P.Z); + return P; + } + FORCEINLINE void Clamp(int32& X, int32& Y, int32& Z) const + { + X = FMath::Clamp(X, Min.X, Max.X - 1); + Y = FMath::Clamp(Y, Min.Y, Max.Y - 1); + Z = FMath::Clamp(Z, Min.Z, Max.Z - 1); + ensureVoxelSlowNoSideEffects(Contains(X, Y, Z)); + } + template + FORCEINLINE void Clamp(T& X, T& Y, T& Z) const + { + // Note: use - 1 even if that's not the closest value for which Contains would return true + // because it's really hard to figure out that value (largest float f such that f < i) + X = FMath::Clamp(X, Min.X, Max.X - 1); + Y = FMath::Clamp(Y, Min.Y, Max.Y - 1); + Z = FMath::Clamp(Z, Min.Z, Max.Z - 1); + ensureVoxelSlowNoSideEffects(ContainsTemplate(X, Y, Z)); + } + FORCEINLINE FVoxelIntBox Clamp(const FVoxelIntBox& Other) const + { + // It's not valid to call Clamp if we're not intersecting Other + ensureVoxelSlowNoSideEffects(Intersect(Other)); + + FVoxelIntBox Result; + + Result.Min.X = FMath::Clamp(Other.Min.X, Min.X, Max.X - 1); + Result.Min.Y = FMath::Clamp(Other.Min.Y, Min.Y, Max.Y - 1); + Result.Min.Z = FMath::Clamp(Other.Min.Z, Min.Z, Max.Z - 1); + + Result.Max.X = FMath::Clamp(Other.Max.X, Min.X + 1, Max.X); + Result.Max.Y = FMath::Clamp(Other.Max.Y, Min.Y + 1, Max.Y); + Result.Max.Z = FMath::Clamp(Other.Max.Z, Min.Z + 1, Max.Z); + + ensureVoxelSlowNoSideEffects(Other.Contains(Result)); + return Result; + } + + /** + * Checks whether the given bounding box intersects this bounding box. + * + * @param Other The bounding box to intersect with. + * @return true if the boxes intersect, false otherwise. + */ + template + FORCEINLINE bool Intersect(const TBox& Other) const + { + if ((Min.X >= Other.Max.X) || (Other.Min.X >= Max.X)) + { + return false; + } + + if ((Min.Y >= Other.Max.Y) || (Other.Min.Y >= Max.Y)) + { + return false; + } + + if ((Min.Z >= Other.Max.Z) || (Other.Min.Z >= Max.Z)) + { + return false; + } + + return true; + } + /** + * Useful for templates taking a box or coordinates + */ + template + FORCEINLINE bool Intersect(TNumeric X, TNumeric Y, TNumeric Z) const + { + return ContainsTemplate(X, Y, Z); + } + FVoxelIntBox Overlap(const FVoxelIntBox& Other) const + { + if (!Intersect(Other)) + { + return FVoxelIntBox(); + } + + // otherwise they overlap + // so find overlapping box + FIntVector MinVector, MaxVector; + + MinVector.X = FMath::Max(Min.X, Other.Min.X); + MaxVector.X = FMath::Min(Max.X, Other.Max.X); + + MinVector.Y = FMath::Max(Min.Y, Other.Min.Y); + MaxVector.Y = FMath::Min(Max.Y, Other.Max.Y); + + MinVector.Z = FMath::Max(Min.Z, Other.Min.Z); + MaxVector.Z = FMath::Min(Max.Z, Other.Max.Z); + + return FVoxelIntBox(MinVector, MaxVector); + } + FVoxelIntBox Union(const FVoxelIntBox& Other) const + { + FIntVector MinVector, MaxVector; + + MinVector.X = FMath::Min(Min.X, Other.Min.X); + MaxVector.X = FMath::Max(Max.X, Other.Max.X); + + MinVector.Y = FMath::Min(Min.Y, Other.Min.Y); + MaxVector.Y = FMath::Max(Max.Y, Other.Max.Y); + + MinVector.Z = FMath::Min(Min.Z, Other.Min.Z); + MaxVector.Z = FMath::Max(Max.Z, Other.Max.Z); + + return FVoxelIntBox(MinVector, MaxVector); + } + + // union(return value, Other) = this + TArray> Difference(const FVoxelIntBox& Other) const + { + if (!Intersect(Other)) + { + return { *this }; + } + + TArray> OutBoxes; + + if (Min.Z < Other.Min.Z) + { + // Add bottom + OutBoxes.Emplace(Min, FIntVector(Max.X, Max.Y, Other.Min.Z)); + } + if (Other.Max.Z < Max.Z) + { + // Add top + OutBoxes.Emplace(FIntVector(Min.X, Min.Y, Other.Max.Z), Max); + } + + const int32 MinZ = FMath::Max(Min.Z, Other.Min.Z); + const int32 MaxZ = FMath::Min(Max.Z, Other.Max.Z); + + if (Min.X < Other.Min.X) + { + // Add X min + OutBoxes.Emplace(FIntVector(Min.X, Min.Y, MinZ), FIntVector(Other.Min.X, Max.Y, MaxZ)); + } + if (Other.Max.X < Max.X) + { + // Add X max + OutBoxes.Emplace(FIntVector(Other.Max.X, Min.Y, MinZ), FIntVector(Max.X, Max.Y, MaxZ)); + } + + const int32 MinX = FMath::Max(Min.X, Other.Min.X); + const int32 MaxX = FMath::Min(Max.X, Other.Max.X); + + if (Min.Y < Other.Min.Y) + { + // Add Y min + OutBoxes.Emplace(FIntVector(MinX, Min.Y, MinZ), FIntVector(MaxX, Other.Min.Y, MaxZ)); + } + if (Other.Max.Y < Max.Y) + { + // Add Y max + OutBoxes.Emplace(FIntVector(MinX, Other.Max.Y, MinZ), FIntVector(MaxX, Max.Y, MaxZ)); + } + + return OutBoxes; + } + + FORCEINLINE uint64 ComputeSquaredDistanceFromBoxToPoint(const FIntVector& Point) const + { + // Accumulates the distance as we iterate axis + uint64 DistSquared = 0; + + // Check each axis for min/max and add the distance accordingly + if (Point.X < Min.X) + { + DistSquared += FMath::Square(Min.X - Point.X); + } + else if (Point.X > Max.X) + { + DistSquared += FMath::Square(Point.X - Max.X); + } + + if (Point.Y < Min.Y) + { + DistSquared += FMath::Square(Min.Y - Point.Y); + } + else if (Point.Y > Max.Y) + { + DistSquared += FMath::Square(Point.Y - Max.Y); + } + + if (Point.Z < Min.Z) + { + DistSquared += FMath::Square(Min.Z - Point.Z); + } + else if (Point.Z > Max.Z) + { + DistSquared += FMath::Square(Point.Z - Max.Z); + } + + return DistSquared; + } + + FORCEINLINE bool IsMultipleOf(int32 Step) const + { + return Min.X % Step == 0 && Min.Y % Step == 0 && Min.Z % Step == 0 && + Max.X % Step == 0 && Max.Y % Step == 0 && Max.Z % Step == 0; + } + + // OldBox included in NewBox, but NewBox not included in OldBox + FORCEINLINE FVoxelIntBox MakeMultipleOfBigger(int32 Step) const + { + FVoxelIntBox NewBox; + NewBox.Min = FVoxelUtilities::DivideFloor(Min, Step) * Step; + NewBox.Max = FVoxelUtilities::DivideCeil(Max, Step) * Step; + return NewBox; + } + // NewBox included in OldBox, but OldBox not included in NewBox + FORCEINLINE FVoxelIntBox MakeMultipleOfSmaller(int32 Step) const + { + FVoxelIntBox NewBox; + NewBox.Min = FVoxelUtilities::DivideCeil(Min, Step) * Step; + NewBox.Max = FVoxelUtilities::DivideFloor(Max, Step) * Step; + return NewBox; + } + FORCEINLINE FVoxelIntBox MakeMultipleOfRoundUp(int32 Step) const + { + FVoxelIntBox NewBox; + NewBox.Min = FVoxelUtilities::DivideCeil(Min, Step) * Step; + NewBox.Max = FVoxelUtilities::DivideCeil(Max, Step) * Step; + return NewBox; + } + + // Guarantee: union(OutChilds).Contains(this) + template + bool Subdivide(int32 ChildrenSize, TArray& OutChildren, int32 MaxChildren = -1) const + { + const FIntVector LowerBound = FVoxelUtilities::DivideFloor(Min, ChildrenSize) * ChildrenSize; + const FIntVector UpperBound = FVoxelUtilities::DivideCeil(Max, ChildrenSize) * ChildrenSize; + for (int32 X = LowerBound.X; X < UpperBound.X; X += ChildrenSize) + { + for (int32 Y = LowerBound.Y; Y < UpperBound.Y; Y += ChildrenSize) + { + for (int32 Z = LowerBound.Z; Z < UpperBound.Z; Z += ChildrenSize) + { + OutChildren.Emplace(FIntVector(X, Y, Z), FIntVector(X + ChildrenSize, Y + ChildrenSize, Z + ChildrenSize)); + if (MaxChildren != -1 && OutChildren.Num() > MaxChildren) + { + return false; + } + } + } + } + return true; + } + + FORCEINLINE FVoxelIntBox Scale(v_flt S) const + { + return { FVoxelUtilities::FloorToInt(FVoxelVector(Min) * S), FVoxelUtilities::CeilToInt(FVoxelVector(Max) * S) }; + } + FORCEINLINE FVoxelIntBox Scale(const FVoxelVector& S) const + { + return SafeConstruct(FVoxelVector(Min) * S, FVoxelVector(Max) * S); + } + + FORCEINLINE FVoxelIntBox Extend(const FIntVector& Amount) const + { + return { Min - Amount, Max + Amount }; + } + FORCEINLINE FVoxelIntBox Extend(int32 Amount) const + { + return Extend(FIntVector(Amount)); + } + FORCEINLINE FVoxelIntBox Translate(const FIntVector& Position) const + { + return FVoxelIntBox(Min + Position, Max + Position); + } + + FORCEINLINE FVoxelIntBox RemoveTranslation() const + { + return FVoxelIntBox(0, Max - Min); + } + // Will move the box so that GetCenter = 0,0,0. Will extend it if its size is odd + FVoxelIntBox Center() const + { + FIntVector NewMin = Min; + FIntVector NewMax = Max; + if (FVoxelVector(GetCenter().ToInt()) != GetCenter()) + { + NewMax = NewMax + 1; + } + ensure(FVoxelVector(GetCenter().ToInt()) == GetCenter()); + const FIntVector Offset = GetCenter().ToInt(); + NewMin -= Offset; + NewMax -= Offset; + ensure(NewMin + NewMax == FIntVector(0)); + return FVoxelIntBox(NewMin, NewMax); + } + + FORCEINLINE FVoxelIntBox& operator*=(int32 Scale) + { + Min *= Scale; + Max *= Scale; + return *this; + } + + FORCEINLINE bool operator==(const FVoxelIntBox& Other) const + { + return Min == Other.Min && Max == Other.Max; + } + FORCEINLINE bool operator!=(const FVoxelIntBox& Other) const + { + return Min != Other.Min || Max != Other.Max; + } + + // More expensive, but should be more random + FORCEINLINE uint32 GetMurmurHash() const + { + return FVoxelUtilities::MurmurHash(Min) ^ FVoxelUtilities::MurmurHash(Max); + } + + template + FORCEINLINE void Iterate(T Lambda) const + { + for (int32 X = Min.X; X < Max.X; X++) + { + for (int32 Y = Min.Y; Y < Max.Y; Y++) + { + for (int32 Z = Min.Z; Z < Max.Z; Z++) + { + Lambda(X, Y, Z); + } + } + } + } + template + FORCEINLINE void Iterate(int32 Step, T Lambda) const + { + for (int32 X = Min.X; X < Max.X; X += Step) + { + for (int32 Y = Min.Y; Y < Max.Y; Y += Step) + { + for (int32 Z = Min.Z; Z < Max.Z; Z += Step) + { + Lambda(X, Y, Z); + } + } + } + } + + template + FORCEINLINE void ParallelSplit(T Lambda, bool bForceSingleThread = false) const + { + const FIntVector Half = (Min + Max) / 2; + ParallelFor(8, [&](int32 Index) + { + const FVoxelIntBox LocalBounds( + FIntVector( + (Index & 0x1) ? Half.X : Min.X, + (Index & 0x2) ? Half.Y : Min.Y, + (Index & 0x4) ? Half.Z : Min.Z), + FIntVector( + (Index & 0x1) ? Max.X : Half.X, + (Index & 0x2) ? Max.Y : Half.Y, + (Index & 0x4) ? Max.Z : Half.Z)); + Lambda(LocalBounds); + }, bForceSingleThread); + } + template + FORCEINLINE void ParallelIterate(T Lambda, bool bForceSingleThread = false) const + { + ParallelFor(Size().X, [&](int32 Index) + { + const int32 X = Min.X + Index; + checkVoxelSlow(X < Max.X); + for (int32 Y = Min.Y; Y < Max.Y; Y++) + { + for (int32 Z = Min.Z; Z < Max.Z; Z++) + { + Lambda(X, Y, Z); + } + } + }, bForceSingleThread); + } + + // MaxBorderSize: if we do a 180 rotation for example, min and max are inverted + // If we don't work on values that are actually inside the box, the resulting box will be wrong + template + FVoxelIntBox ApplyTransform(const FTransform& Transform, int32 MaxBorderSize = 1) const + { + const auto Corners = GetCorners(MaxBorderSize); + + FIntVector NewMin(MAX_int32); + FIntVector NewMax(MIN_int32); + for (int32 Index = 0; Index < 8; Index++) + { + const FVector P = + Inverse == EInverseTransform::True + ? Transform.InverseTransformPosition(FVector(Corners[Index])) + : Transform.TransformPosition(FVector(Corners[Index])); + NewMin = FVoxelUtilities::ComponentMin(NewMin, FVoxelUtilities::FloorToInt(P)); + NewMax = FVoxelUtilities::ComponentMax(NewMax, FVoxelUtilities::CeilToInt(P)); + } + return FVoxelIntBox(NewMin, NewMax + MaxBorderSize); + } +}; + +FORCEINLINE uint32 GetTypeHash(const FVoxelIntBox& Box) +{ + static_assert(sizeof(FVoxelIntBox) == 6 * sizeof(int32), "Alignement error"); + return FCrc::MemCrc32(&Box, sizeof(FVoxelIntBox)); +} + +FORCEINLINE FVoxelIntBox operator*(const FVoxelIntBox& Box, int32 Scale) +{ + FVoxelIntBox Copy = Box; + return Copy *= Scale; +} + +FORCEINLINE FVoxelIntBox operator*(int32 Scale, const FVoxelIntBox& Box) +{ + FVoxelIntBox Copy = Box; + return Copy *= Scale; +} + +FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FVoxelIntBox& Other) +{ + FVoxelIntBox Copy = Box; + Copy.Min = FVoxelUtilities::ComponentMin(Copy.Min, Other.Min); + Copy.Max = FVoxelUtilities::ComponentMax(Copy.Max, Other.Max); + return Copy; +} + +FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FIntVector& Point) +{ + return Box + FVoxelIntBox(Point); +} + +FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FVector& Point) +{ + return Box + FVoxelIntBox(Point); +} + +FORCEINLINE FArchive& operator<<(FArchive& Ar, FVoxelIntBox& Box) +{ + Ar << Box.Min; + Ar << Box.Max; + return Ar; +} + +// Voxel Int Box with a IsValid flag +USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelIntBoxLibrary.MakeIntBoxWithValidity", HasNativeBreak="Voxel.VoxelIntBoxLibrary.BreakIntBoxWithValidity")) +struct FVoxelIntBoxWithValidity +{ + GENERATED_BODY() + + FVoxelIntBoxWithValidity() = default; + FVoxelIntBoxWithValidity(const FVoxelIntBox& Box) + : Box(Box) + , bValid(true) + { + } + + FORCEINLINE const FVoxelIntBox& GetBox() const + { + check(IsValid()); + return Box; + } + + FORCEINLINE bool IsValid() const + { + return bValid; + } + FORCEINLINE void Reset() + { + bValid = false; + } + + FORCEINLINE FVoxelIntBoxWithValidity& operator=(const FVoxelIntBox& Other) + { + Box = Other; + bValid = true; + return *this; + } + + FORCEINLINE bool operator==(const FVoxelIntBoxWithValidity& Other) const + { + if (bValid != Other.bValid) + { + return false; + } + if (!bValid && !Other.bValid) + { + return true; + } + return Box == Other.Box; + } + FORCEINLINE bool operator!=(const FVoxelIntBoxWithValidity& Other) const + { + return !(*this == Other); + } + + FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FVoxelIntBox& Other) + { + if (bValid) + { + Box = Box + Other; + } + else + { + Box = Other; + bValid = true; + } + return *this; + } + FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FVoxelIntBoxWithValidity& Other) + { + if (Other.bValid) + { + *this += Other.GetBox(); + } + return *this; + } + + FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FIntVector& Point) + { + return *this += FVoxelIntBox(Point); + } + + template + FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const TArray& Other) + { + for (auto& It : Other) + { + *this += It; + } + return *this; + } + +private: + UPROPERTY() + FVoxelIntBox Box; + + UPROPERTY() + bool bValid = false; +}; + +template +FORCEINLINE FVoxelIntBoxWithValidity operator+(const FVoxelIntBoxWithValidity& Box, const T& Other) +{ + FVoxelIntBoxWithValidity Copy = Box; + return Copy += Other; +} + +UE_DEPRECATED(4.24, "Please use FVoxelIntBox instead of FIntBox.") +typedef FVoxelIntBox FIntBox; + +UE_DEPRECATED(4.24, "Please use FVoxelIntBoxWithValidity instead of FIntBoxWithValidity.") +typedef FVoxelIntBoxWithValidity FIntBoxWithValidity; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBoxLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBoxLibrary.h new file mode 100644 index 00000000..ecd4ba5f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelIntBoxLibrary.h @@ -0,0 +1,230 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelIntBox.h" +#include "VoxelMessages.h" +#include "VoxelIntBoxLibrary.generated.h" + +UCLASS() +class UVoxelIntBoxLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** Makes an Int Box. Min must be <= to Max */ + UFUNCTION(BlueprintPure, Category="Math|VoxelIntBox", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelIntBox MakeIntBox(FIntVector Min, FIntVector Max) + { + if (Min.X >= Max.X || + Min.Y >= Max.Y || + Min.Z >= Max.Z) + { + FVoxelMessages::Error(FString::Printf(TEXT("MakeIntBox: Min should be < to Max! Min: %s; Max: %s"), *Min.ToString(), *Max.ToString())); + } + return FVoxelIntBox::SafeConstruct(Min, Max); + } + + /** Breaks an Int Box */ + UFUNCTION(BlueprintPure, Category="Math|VoxelIntBox", meta=(NativeBreakFunc)) + static void BreakIntBox(FVoxelIntBox Box, FIntVector& Min, FIntVector& Max) + { + Min = Box.Min; + Max = Box.Max; + } + +public: + UFUNCTION(BlueprintPure, Category="Math|VoxelIntBox", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelIntBoxWithValidity MakeIntBoxWithValidity(FVoxelIntBox Box, bool bIsValid = true) + { + if (bIsValid) + { + return Box; + } + else + { + return {}; + } + } + UFUNCTION(BlueprintPure, Category="Math|VoxelIntBox", meta=(NativeBreakFunc)) + static void BreakIntBoxWithValidity(FVoxelIntBoxWithValidity BoxWithValidity, FVoxelIntBox& Box, bool& bIsValid) + { + if (BoxWithValidity.IsValid()) + { + bIsValid = true; + Box = BoxWithValidity.GetBox(); + } + else + { + bIsValid = false; + Box = {}; + } + } + +public: + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox InfiniteBox() + { + return FVoxelIntBox::Infinite; + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox TranslateBox(FVoxelIntBox Box, FIntVector Position) + { + return Box.Translate(Position); + } + // Will move the box so that GetCenter = 0,0,0. Will extend it if its size is odd + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox Center(FVoxelIntBox Box) + { + return Box.Center(); + } + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox RemoveTranslation(FVoxelIntBox Box) + { + return Box.RemoveTranslation(); + } + + UFUNCTION(BlueprintPure, meta=(DisplayName = "ToString (VoxelIntBox)", CompactNodeTitle = "->", BlueprintAutocast), Category="Utilities|String") + static FString Conv_IntBoxToString(FVoxelIntBox IntBox) + { + return IntBox.ToString(); + } + + // From -Radius(included) to Radius(excluded) + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox MakeBoxFromLocalPositionAndRadius(FIntVector Position, int32 Radius) + { + return FVoxelIntBox(FIntVector(-Radius), FIntVector(Radius)).Translate(Position); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox MakeBoxFromPositionAndRadius(FVector Position, float Radius) + { + Radius = FMath::Max(0.f, Radius); + return FVoxelIntBox(FVoxelUtilities::FloorToInt(Position - Radius), FVoxelUtilities::CeilToInt(Position + Radius)); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static bool IsIntVectorInsideBox(FVoxelIntBox Box, FIntVector Position) + { + return Box.Contains(Position); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static bool IsVectorInsideBox(FVoxelIntBox Box, FVector Position) + { + return Box.ContainsFloat(Position); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FIntVector GetSize(FVoxelIntBox Box) + { + return Box.Size(); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVector GetCenter(FVoxelIntBox Box) + { + return Box.GetCenter().ToFloat(); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static TArray GetCorners(FVoxelIntBox Box) + { + return TArray(Box.GetCorners(0)); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static bool Intersect(FVoxelIntBox Box, FVoxelIntBox Other) + { + return Box.Intersect(Other); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static bool Contains(FVoxelIntBox Box, FVoxelIntBox Other) + { + return Box.Contains(Other); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static bool IsValid(FVoxelIntBox Box) + { + return Box.IsValid(); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox Overlap(FVoxelIntBox A, FVoxelIntBox B) + { + return A.Overlap(B); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox Extend(FVoxelIntBox Box, int32 Extent) + { + return Box.Extend(Extent); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox Extend_IntVector(FVoxelIntBox Box, FIntVector Extent) + { + return Box.Extend(Extent); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox") + static FVoxelIntBox ApplyTransform(FVoxelIntBox Box, FTransform Transform) + { + return Box.ApplyTransform(Transform); + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox", meta = (CompactNodeTitle = "+")) + static FVoxelIntBox AddPoint(FVoxelIntBox Box, FIntVector Point) + { + return Box + Point; + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox", meta = (CompactNodeTitle = "+")) + static FVoxelIntBox AddBox(FVoxelIntBox Box, FVoxelIntBox BoxToAdd) + { + return Box + BoxToAdd; + } + + UFUNCTION(BlueprintPure, Category = "Math|VoxelIntBox", meta = (CompactNodeTitle = "x")) + static FVoxelIntBox Scale(FVoxelIntBox Box, int32 Scale) + { + return Box * Scale; + } + + UFUNCTION(BlueprintPure, meta=(DisplayName = "Equal (VoxelIntBox)", CompactNodeTitle = "==", Keywords = "== equal"), Category="Math|VoxelIntBox") + static bool EqualEqual_IntBoxIntBox(FVoxelIntBox A, FVoxelIntBox B) + { + return A == B; + } + + UFUNCTION(BlueprintPure, meta=(DisplayName = "NotEqual (VoxelIntBox)", CompactNodeTitle = "!=", Keywords = "!= not equal"), Category="Math|VoxelIntBox") + static bool NotEqual_IntBoxIntBox(FVoxelIntBox A, FVoxelIntBox B) + { + return A != B; + } + + UFUNCTION(BlueprintPure, Category="Math|VoxelIntBox", meta=(Keywords="construct build", NativeMakeFunc)) + static FVoxelIntBox MakeIntBoxFromPoints(TArray Points) + { + if (Points.Num() == 0) + { + FVoxelMessages::Error(FUNCTION_ERROR("No points!")); + Points.Add(FVector::ZeroVector); + } + + if (Points.Num() == 2) + { + return FVoxelIntBox::SafeConstruct(Points[0], Points[1]); + } + else + { + return FVoxelIntBox(Points); + } + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInterpolator.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInterpolator.h new file mode 100644 index 00000000..987fdd79 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInterpolator.h @@ -0,0 +1,52 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" + +template +struct TVoxelInterpolator +{ + TVoxelStaticArray Data; + +#define OP(bop, op) \ + FORCEINLINE TVoxelInterpolator& operator op(const TVoxelInterpolator& Other) \ + { \ + for (int32 Index = 0; Index < NumChannels; Index++) \ + { \ + Data[Index] op Other.Data[Index]; \ + } \ + return *this; \ + } \ + FORCEINLINE TVoxelInterpolator operator bop(const TVoxelInterpolator& Other) const \ + { \ + auto Copy = *this; \ + return Copy op Other; \ + } + + OP(+, +=) + OP(-, -=) + OP(*, *=) + OP(/, /=) + +#undef OP + + FORCEINLINE TVoxelInterpolator& operator*=(float Other) + { + for (int32 Index = 0; Index < NumChannels; Index++) + { + Data[Index] *= Other; + } + return *this; + } + FORCEINLINE TVoxelInterpolator operator*(float Other) const + { + auto Copy = *this; + return Copy *= Other; + } + FORCEINLINE friend TVoxelInterpolator operator*(float Other, const TVoxelInterpolator& This) + { + return This * Other; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInvokerSettings.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInvokerSettings.h new file mode 100644 index 00000000..357efb7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelInvokerSettings.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelInvokerSettings.generated.h" + +USTRUCT(BlueprintType) +struct FVoxelInvokerSettings +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + bool bUseForLOD = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + int32 LODToSet = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + FVoxelIntBox LODBounds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + bool bUseForCollisions = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + FVoxelIntBox CollisionsBounds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + bool bUseForNavmesh = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + FVoxelIntBox NavmeshBounds; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelItemStack.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelItemStack.h new file mode 100644 index 00000000..47caabd3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelItemStack.h @@ -0,0 +1,66 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRange.h" + +class FVoxelPlaceableItemHolder; +class FVoxelGeneratorInstance; + +struct VOXEL_API FVoxelItemStack +{ +public: + const FVoxelPlaceableItemHolder& ItemHolder; + const FVoxelGeneratorInstance* const Generator; + const int32 Depth; // Index in VoxelAssetItem array, -1 if generator + const TArray* const CustomData; // Use this to send custom data to a generator + + explicit FVoxelItemStack(const FVoxelPlaceableItemHolder& ItemHolder, const TArray* CustomData = nullptr) + : ItemHolder(ItemHolder) + , Generator(nullptr) + , Depth(-1) + , CustomData(CustomData) + { + } + FVoxelItemStack(const FVoxelPlaceableItemHolder& ItemHolder, const FVoxelGeneratorInstance& Generator, int32 Depth, const TArray* CustomData = nullptr) + : ItemHolder(ItemHolder) + , Generator(&Generator) + , Depth(Depth) + , CustomData(CustomData) + { + } + + static FVoxelItemStack Empty; + + template + T Get(v_flt X, v_flt Y, v_flt Z, int32 LOD) const; + TVoxelRange GetValueRange(const FVoxelIntBox& Bounds, int32 LOD) const; + + template + T GetCustomOutput(T DefaultValue, FName Name, v_flt X, v_flt Y, v_flt Z, int32 LOD) const; + template + TVoxelRange GetCustomOutputRange(TVoxelRange DefaultValue, FName Name, const FVoxelIntBox& Bounds, int32 LOD) const; + + template + FORCEINLINE FVoxelItemStack GetNextStack(TArgs... Args) const + { + return { ItemHolder, *Generator, GetNextDepth(Args...), CustomData }; + } + FORCEINLINE FVoxelItemStack WithCustomData(const TArray* InCustomData) const + { + return { ItemHolder, *Generator, Depth, InCustomData }; + } + FORCEINLINE bool IsEmpty() const + { + return Depth == -1; + } + FORCEINLINE bool IsValid() const + { + return Depth >= -1; + } + +private: + template + int32 GetNextDepth(TArgs... Args) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelLog.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelLog.h new file mode 100644 index 00000000..0840507f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelLog.h @@ -0,0 +1,9 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +VOXEL_API DECLARE_LOG_CATEGORY_EXTERN(LogVoxel, Log, All); + +#define LOG_VOXEL(Verbosity, Format, ...) UE_LOG(LogVoxel, Verbosity, Format, ##__VA_ARGS__) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMacros.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMacros.h new file mode 100644 index 00000000..cd1a10bf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMacros.h @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelDefinitions.h" + +#if VOXEL_DOUBLE_PRECISION +using v_flt = double; + +#define MIN_vflt MIN_dbl +#define MAX_vflt MAX_dbl +#else +using v_flt = float; + +#define MIN_vflt MIN_flt +#define MAX_vflt MAX_flt +#endif + +#define VOXELS_PER_DATA_CHUNK (DATA_CHUNK_SIZE * DATA_CHUNK_SIZE * DATA_CHUNK_SIZE) + +using FVoxelCellIndex = uint16; +static_assert(VOXELS_PER_DATA_CHUNK < TNumericLimits::Max(), "CellIndex type is too small"); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(__INTELLISENSE__) || defined(__RSCPP_VERSION) +#define INTELLISENSE_PARSER 1 +#else +#define INTELLISENSE_PARSER 0 +#endif + +#if INTELLISENSE_PARSER +#define CORE_API +#define ENGINE_API +#undef VOXEL_DEBUG +#define VOXEL_DEBUG 1 +#error "Compiler defined as parser" +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if VOXEL_DEBUG +#define checkVoxelSlow(x) check(x) +#define checkfVoxelSlow(x, ...) checkf(x, ##__VA_ARGS__) +#define ensureVoxelSlow(x) ensure(x) +#define ensureVoxelSlowNoSideEffects(x) ensure(x) +#define ensureMsgfVoxelSlowNoSideEffects(x, ...) ensureMsgf(x, ##__VA_ARGS__) +#undef FORCEINLINE +#define FORCEINLINE FORCEINLINE_DEBUGGABLE_ACTUAL +#else +#define checkVoxelSlow(x) +#define checkfVoxelSlow(x, ...) +#define ensureVoxelSlow(x) (!!(x)) +#define ensureVoxelSlowNoSideEffects(x) +#define ensureMsgfVoxelSlowNoSideEffects(...) +#endif + +#if DO_THREADSAFE_CHECKS +#define ensureThreadSafe(...) ensure(__VA_ARGS__) +#else +#define ensureThreadSafe(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define VOXEL_LOCTEXT(Text) INVTEXT(Text) + +// Inline static helper to avoid rehashing FNames +#ifndef STATIC_FNAME +#define STATIC_FNAME(Name) ([]() -> const FName& { static const FName StaticName = Name; return StaticName; }()) +#endif + +// Static string helper +#ifndef STATIC_FSTRING +#define STATIC_FSTRING(String) ([]() -> const FString& { static const FString StaticString = String; return StaticString; }()) +#endif + +#ifndef UNIQUE_ID +#define UNIQUE_ID() []() { ensureVoxelSlowNoSideEffects(IsInGameThread()); static uint64 Id = 0; return ++Id; }() +#endif + +#ifndef OBJECT_LINE_ID +#define OBJECT_LINE_ID() ((uint64)this + __LINE__) +#endif + +#ifndef GET_MEMBER_NAME_STATIC +#define GET_MEMBER_NAME_STATIC(ClassName, MemberName) STATIC_FNAME(GET_MEMBER_NAME_STRING_CHECKED(ClassName, MemberName)) +#endif + +#ifndef GET_OWN_MEMBER_NAME +#define GET_OWN_MEMBER_NAME(MemberName) GET_MEMBER_NAME_CHECKED(TDecay::Type, MemberName) +#endif + +#ifndef FUNCTION_FNAME +#define FUNCTION_FNAME FName(__FUNCTION__) +#endif + +#ifndef FUNCTION_ERROR_IMPL +#define FUNCTION_ERROR_IMPL(FunctionName, Error) (FString(FunctionName) + TEXT(": ") + Error) +#endif + +#ifndef FUNCTION_ERROR +#define FUNCTION_ERROR(Error) FUNCTION_ERROR_IMPL(__FUNCTION__, Error) +#endif + +#define VOXEL_DEPRECATED(Version, Message) UE_DEPRECATED(0, Message " If this is a C++ voxel graph, you should compile it to C++ again.") \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterial.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterial.h new file mode 100644 index 00000000..2b8bba2a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterial.h @@ -0,0 +1,773 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelMaterial.generated.h" + +namespace EVoxelMaterialConfigFlag +{ + enum Type : uint32 + { + EnableA = 0x01, + EnableR = 0x02, + EnableG = 0x04, + // = 0x08, Forgot to use that one :( + EnableB = 0x10, + EnableUV0 = 0x20, + EnableUV1 = 0x40, + EnableUV2 = 0x80, + EnableUV3 = 0x100 + }; +} +constexpr uint32 GVoxelMaterialConfigFlag = + EVoxelMaterialConfigFlag::EnableA * VOXEL_MATERIAL_ENABLE_A + + EVoxelMaterialConfigFlag::EnableR * VOXEL_MATERIAL_ENABLE_R + + EVoxelMaterialConfigFlag::EnableG * VOXEL_MATERIAL_ENABLE_G + + EVoxelMaterialConfigFlag::EnableB * VOXEL_MATERIAL_ENABLE_B + + EVoxelMaterialConfigFlag::EnableUV0 * VOXEL_MATERIAL_ENABLE_UV0 + + EVoxelMaterialConfigFlag::EnableUV1 * VOXEL_MATERIAL_ENABLE_UV1 + + EVoxelMaterialConfigFlag::EnableUV2 * VOXEL_MATERIAL_ENABLE_UV2 + + EVoxelMaterialConfigFlag::EnableUV3 * VOXEL_MATERIAL_ENABLE_UV3; + +UENUM(BlueprintType, DisplayName = "Voxel Material Mask", meta = (Bitflags)) +enum class EVoxelMaterialMask_BP : uint8 +{ + R, + G, + B, + A, + U0, + V0, + U1, + V1, + U2, + V2, + U3, + V3, +}; + +namespace EVoxelMaterialMask +{ + enum Type : uint32 + { + R = 1 << 0, + G = 1 << 1, + B = 1 << 2, + A = 1 << 3, + U0 = 1 << 4, + V0 = 1 << 5, + U1 = 1 << 6, + V1 = 1 << 7, + U2 = 1 << 8, + V2 = 1 << 9, + U3 = 1 << 10, + V3 = 1 << 11, + + None = 0, + All = R | G | B| A | U0 | U1 | U2 | U3 | V0 | V1 | V2 | V3, + + RGB = R | G | B, + RGBA = R | G | B | A, + + SingleIndex = A, + + MultiIndex_Blend0 = R, + MultiIndex_Blend1 = G, + MultiIndex_Blend2 = B, + MultiIndex_Wetness = A, + MultiIndex_Index0 = U0, + MultiIndex_Index1 = V0, + MultiIndex_Index2 = U1, + MultiIndex_Index3 = V1, + MultiIndex_NoWetness = MultiIndex_Blend0 | + MultiIndex_Blend1 | + MultiIndex_Blend2 | + MultiIndex_Index0 | + MultiIndex_Index1 | + MultiIndex_Index2 | + MultiIndex_Index3, + MultiIndex = MultiIndex_NoWetness | MultiIndex_Wetness, + + UV0 = U0 | V0, + UV1 = U1 | V1, + UV2 = U2 | V2, + UV3 = U3 | V3, + + UV = UV0 | UV1 | UV2 | UV3 + }; +} + +// Base class without all the #if ENABLE_SOMETHING +template +struct TVoxelMaterialImpl +{ + FORCEINLINE TVoxelMaterialImpl() + { + } + + FORCEINLINE explicit TVoxelMaterialImpl(EForceInit) + { +#if VOXEL_MATERIAL_DEFAULT_IS_WHITE + SetR(255); + SetG(255); + SetB(255); + SetA(255); +#else + SetR(0); + SetG(0); + SetB(0); + SetA(0); +#endif + + SetU0(0); + SetU1(0); + SetU2(0); + SetU3(0); + + SetV0(0); + SetV1(0); + SetV2(0); + SetV3(0); + } + + FORCEINLINE static T Default() + { + return T(ForceInit); + } + +public: +#define DEFINE_GETTER_SETTER(Name) \ + FORCEINLINE void Set##Name(uint8 New##Name) { static_cast(*this).Impl_Set##Name(New##Name); } \ + FORCEINLINE void Set##Name(int32 New##Name) { static_cast(*this).Impl_Set##Name(FVoxelUtilities::CastToUINT8(New##Name)); } \ + FORCEINLINE void Set##Name##_AsFloat(float New##Name) { static_cast(*this).Impl_Set##Name(FVoxelUtilities::FloatToUINT8(New##Name)); } \ + template \ + FORCEINLINE void Set##Name(X New##Name) \ + { \ + static_assert(!TIsSame::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to use " PREPROCESSOR_TO_STRING(Set##Name##_AsFloat)); \ + static_assert(TIsSame::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to cast to float, uint8 or int32"); \ + } \ + FORCEINLINE uint8 Get##Name() const { return static_cast(*this).Impl_Get##Name(); } \ + FORCEINLINE float Get##Name##_AsFloat() const { return FVoxelUtilities::UINT8ToFloat(static_cast(*this).Impl_Get##Name()); } + + DEFINE_GETTER_SETTER(R) + DEFINE_GETTER_SETTER(G) + DEFINE_GETTER_SETTER(B) + DEFINE_GETTER_SETTER(A) + DEFINE_GETTER_SETTER(U0) + DEFINE_GETTER_SETTER(U1) + DEFINE_GETTER_SETTER(U2) + DEFINE_GETTER_SETTER(U3) + DEFINE_GETTER_SETTER(V0) + DEFINE_GETTER_SETTER(V1) + DEFINE_GETTER_SETTER(V2) + DEFINE_GETTER_SETTER(V3) +#undef DEFINE_GETTER_SETTER + +public: +#define DEFINE_UV_GETTER_SETTER(Name, Type, Suffix) \ + FORCEINLINE Type Get##Name##Suffix(int32 Tex) const \ + { \ + switch (Tex) \ + { \ + case 0: return Get##Name##0##Suffix(); \ + case 1: return Get##Name##1##Suffix(); \ + case 2: return Get##Name##2##Suffix(); \ + case 3: return Get##Name##3##Suffix(); \ + default: return 0; \ + } \ + } \ + template \ + FORCEINLINE void Set##Name##Suffix(int32 Tex, X Value) \ + { \ + switch (Tex) \ + { \ + case 0: Set##Name##0##Suffix(Value); break; \ + case 1: Set##Name##1##Suffix(Value); break; \ + case 2: Set##Name##2##Suffix(Value); break; \ + case 3: Set##Name##3##Suffix(Value); break; \ + default: break; \ + } \ + } + + DEFINE_UV_GETTER_SETTER(U, uint8,) + DEFINE_UV_GETTER_SETTER(V, uint8,) + DEFINE_UV_GETTER_SETTER(U, float, _AsFloat) + DEFINE_UV_GETTER_SETTER(V, float, _AsFloat) + +#undef DEFINE_UV_GETTER_SETTER + +public: +#define DEFINE_FORWARD(Name, Forward) \ + FORCEINLINE void Set##Name(uint8 New##Name) { Set##Forward(New##Name); } \ + FORCEINLINE void Set##Name(int32 New##Name) { Set##Forward(New##Name); } \ + FORCEINLINE void Set##Name##_AsFloat(float New##Name) { Set##Forward##_AsFloat(New##Name); } \ + template \ + FORCEINLINE void Set##Name(X New##Name) \ + { \ + static_assert(!TIsSame::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to use " PREPROCESSOR_TO_STRING(Set##Name##_AsFloat)); \ + static_assert(TIsSame::Value, PREPROCESSOR_TO_STRING(Set##Name) ": need to cast to float, uint8 or int32"); \ + } \ + FORCEINLINE uint8 Get##Name() const { return Get##Forward(); } \ + FORCEINLINE float Get##Name##_AsFloat() const { return Get##Forward##_AsFloat(); } + + DEFINE_FORWARD(SingleIndex, A) + + DEFINE_FORWARD(MultiIndex_Blend0, R) + DEFINE_FORWARD(MultiIndex_Blend1, G) + DEFINE_FORWARD(MultiIndex_Blend2, B) + DEFINE_FORWARD(MultiIndex_Wetness, A) + DEFINE_FORWARD(MultiIndex_Index0, U0) + DEFINE_FORWARD(MultiIndex_Index1, V0) + DEFINE_FORWARD(MultiIndex_Index2, U1) + DEFINE_FORWARD(MultiIndex_Index3, V1) +#undef DEFINE_FORWARD + +public: + FORCEINLINE uint32 GetPackedColor() const + { + return + (uint32(GetR()) << 0) | + (uint32(GetG()) << 8) | + (uint32(GetB()) << 16) | + (uint32(GetA()) << 24); + } + +public: + FORCEINLINE void SetColor(const FColor& Color) + { + SetR(Color.R); + SetG(Color.G); + SetB(Color.B); + SetA(Color.A); + } + FORCEINLINE FColor GetColor() const + { + return FColor( + GetR(), + GetG(), + GetB(), + GetA()); + } + + FORCEINLINE FVector4 GetColorVector() const + { + return FVector4( + GetR_AsFloat(), + GetG_AsFloat(), + GetB_AsFloat(), + GetA_AsFloat()); + } + + FORCEINLINE void SetColor(const FLinearColor& Color) + { + SetR_AsFloat(Color.R); + SetG_AsFloat(Color.G); + SetB_AsFloat(Color.B); + SetA_AsFloat(Color.A); + } + FORCEINLINE FLinearColor GetLinearColor() const + { + return FLinearColor( + GetR_AsFloat(), + GetG_AsFloat(), + GetB_AsFloat(), + GetA_AsFloat()); + } + +public: + FORCEINLINE void SetUV_AsFloat(int32 Tex, const FVector2D& UV) + { + SetU_AsFloat(Tex, UV.X); + SetV_AsFloat(Tex, UV.Y); + } + FORCEINLINE FVector2D GetUV_AsFloat(int32 Tex) const + { + return FVector2D(GetU_AsFloat(Tex), GetV_AsFloat(Tex)); + } + +public: + FORCEINLINE static T CreateFromColor(const FLinearColor& Color) + { + T Material(ForceInit); + Material.SetColor(Color); + return Material; + } + FORCEINLINE static T CreateFromColor(const FColor& Color) + { + T Material(ForceInit); + Material.SetColor(Color); + return Material; + } + +public: + FORCEINLINE void CopyFrom(const T& Other, uint32 Mask) + { + if (Mask & EVoxelMaterialMask::R) SetR(Other.GetR()); + if (Mask & EVoxelMaterialMask::G) SetG(Other.GetG()); + if (Mask & EVoxelMaterialMask::B) SetB(Other.GetB()); + if (Mask & EVoxelMaterialMask::A) SetA(Other.GetA()); + + if (Mask & EVoxelMaterialMask::U0) SetU0(Other.GetU0()); + if (Mask & EVoxelMaterialMask::U1) SetU1(Other.GetU1()); + if (Mask & EVoxelMaterialMask::U2) SetU2(Other.GetU2()); + if (Mask & EVoxelMaterialMask::U3) SetU3(Other.GetU3()); + + if (Mask & EVoxelMaterialMask::V0) SetV0(Other.GetV0()); + if (Mask & EVoxelMaterialMask::V1) SetV1(Other.GetV1()); + if (Mask & EVoxelMaterialMask::V2) SetV2(Other.GetV2()); + if (Mask & EVoxelMaterialMask::V3) SetV3(Other.GetV3()); + } + +public: + FORCEINLINE bool operator==(const T& Other) const + { + return + GetR() == Other.GetR() && + GetG() == Other.GetG() && + GetB() == Other.GetB() && + GetA() == Other.GetA() && + GetU0() == Other.GetU0() && + GetU1() == Other.GetU1() && + GetU2() == Other.GetU2() && + GetV3() == Other.GetU3() && + GetV0() == Other.GetV0() && + GetV1() == Other.GetV1() && + GetV2() == Other.GetV2() && + GetV3() == Other.GetV3(); + } + FORCEINLINE bool operator!=(const T& Other) const + { + return + GetR() != Other.GetR() || + GetG() != Other.GetG() || + GetB() != Other.GetB() || + GetA() != Other.GetA() || + GetU0() != Other.GetU0() || + GetU1() != Other.GetU1() || + GetU2() != Other.GetU2() || + GetV3() != Other.GetU3() || + GetV0() != Other.GetV0() || + GetV1() != Other.GetV1() || + GetV2() != Other.GetV2() || + GetV3() != Other.GetV3(); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +struct TVoxelMaterialStorage +{ + FORCEINLINE static TVoxelMaterialStorage SerializeWithCustomConfig(FArchive& Ar, uint32 ConfigFlags) + { + check(Ar.IsLoading()); + + T R = 0; + T G = 0; + T B = 0; + T A = 0; + + T U0 = 0; + T V0 = 0; + T U1 = 0; + T V1 = 0; + T U2 = 0; + T V2 = 0; + T U3 = 0; + T V3 = 0; + + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableR) + { + Ar << R; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableG) + { + Ar << G; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableB) + { + Ar << B; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableA) + { + Ar << A; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV0) + { + Ar << U0; + Ar << V0; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV1) + { + Ar << U1; + Ar << V1; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV2) + { + Ar << U2; + Ar << V2; + } + if (ConfigFlags & EVoxelMaterialConfigFlag::EnableUV3) + { + Ar << U3; + Ar << V3; + } + + TVoxelMaterialStorage Storage; + Storage.Impl_SetR(R); + Storage.Impl_SetG(G); + Storage.Impl_SetB(B); + Storage.Impl_SetA(A); + Storage.Impl_SetU0(U0); + Storage.Impl_SetV0(V0); + Storage.Impl_SetU1(U1); + Storage.Impl_SetV1(V1); + Storage.Impl_SetU2(U2); + Storage.Impl_SetV2(V2); + Storage.Impl_SetU3(U3); + Storage.Impl_SetV3(V3); + return Storage; + } + friend FORCEINLINE FArchive& operator<<(FArchive& Ar, TVoxelMaterialStorage& Storage) + { +#if VOXEL_MATERIAL_ENABLE_R + Ar << Storage.R; +#endif +#if VOXEL_MATERIAL_ENABLE_G + Ar << Storage.G; +#endif +#if VOXEL_MATERIAL_ENABLE_B + Ar << Storage.B; +#endif +#if VOXEL_MATERIAL_ENABLE_A + Ar << Storage.A; +#endif +#if VOXEL_MATERIAL_ENABLE_UV0 + Ar << Storage.U0; + Ar << Storage.V0; +#endif +#if VOXEL_MATERIAL_ENABLE_UV1 + Ar << Storage.U1; + Ar << Storage.V1; +#endif +#if VOXEL_MATERIAL_ENABLE_UV2 + Ar << Storage.U2; + Ar << Storage.V2; +#endif +#if VOXEL_MATERIAL_ENABLE_UV3 + Ar << Storage.U3; + Ar << Storage.V3; +#endif + return Ar; + } + +public: + static constexpr int32 NumChannels = + VOXEL_MATERIAL_ENABLE_R + + VOXEL_MATERIAL_ENABLE_G + + VOXEL_MATERIAL_ENABLE_B + + VOXEL_MATERIAL_ENABLE_A + + 2 * VOXEL_MATERIAL_ENABLE_UV0 + + 2 * VOXEL_MATERIAL_ENABLE_UV1 + + 2 * VOXEL_MATERIAL_ENABLE_UV2 + + 2 * VOXEL_MATERIAL_ENABLE_UV3; + + static constexpr uint32 ChannelsMask = + EVoxelMaterialMask::R * VOXEL_MATERIAL_ENABLE_R + + EVoxelMaterialMask::G * VOXEL_MATERIAL_ENABLE_G + + EVoxelMaterialMask::B * VOXEL_MATERIAL_ENABLE_B + + EVoxelMaterialMask::A * VOXEL_MATERIAL_ENABLE_A + + EVoxelMaterialMask::UV0 * VOXEL_MATERIAL_ENABLE_UV0 + + EVoxelMaterialMask::UV1 * VOXEL_MATERIAL_ENABLE_UV1 + + EVoxelMaterialMask::UV2 * VOXEL_MATERIAL_ENABLE_UV2 + + EVoxelMaterialMask::UV3 * VOXEL_MATERIAL_ENABLE_UV3; + + FORCEINLINE T& GetRaw(int32 Channel) + { + checkVoxelSlow(0 <= Channel && Channel < NumChannels); + return *(reinterpret_cast(this) + Channel); + } + FORCEINLINE T GetRaw(int32 Channel) const + { + checkVoxelSlow(0 <= Channel && Channel < NumChannels); + return *(reinterpret_cast(this) + Channel); + } + + FORCEINLINE T& operator[](int32 Channel) + { + return GetRaw(Channel); + } + FORCEINLINE T operator[](int32 Channel) const + { + return GetRaw(Channel); + } + +public: + FORCEINLINE T Impl_GetR() const + { +#if VOXEL_MATERIAL_ENABLE_R + return R; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetG() const + { +#if VOXEL_MATERIAL_ENABLE_G + return G; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetB() const + { +#if VOXEL_MATERIAL_ENABLE_B + return B; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetA() const + { +#if VOXEL_MATERIAL_ENABLE_A + return A; +#else + return 0; +#endif + } + + FORCEINLINE T Impl_GetU0() const + { +#if VOXEL_MATERIAL_ENABLE_UV0 + return U0; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetU1() const + { +#if VOXEL_MATERIAL_ENABLE_UV1 + return U1; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetU2() const + { +#if VOXEL_MATERIAL_ENABLE_UV2 + return U2; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetU3() const + { +#if VOXEL_MATERIAL_ENABLE_UV3 + return U3; +#else + return 0; +#endif + } + + FORCEINLINE T Impl_GetV0() const + { +#if VOXEL_MATERIAL_ENABLE_UV0 + return V0; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetV1() const + { +#if VOXEL_MATERIAL_ENABLE_UV1 + return V1; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetV2() const + { +#if VOXEL_MATERIAL_ENABLE_UV2 + return V2; +#else + return 0; +#endif + } + FORCEINLINE T Impl_GetV3() const + { +#if VOXEL_MATERIAL_ENABLE_UV3 + return V3; +#else + return 0; +#endif + } + +public: + FORCEINLINE void Impl_SetR(T Value) + { +#if VOXEL_MATERIAL_ENABLE_R + R = Value; +#endif + } + FORCEINLINE void Impl_SetG(T Value) + { +#if VOXEL_MATERIAL_ENABLE_G + G = Value; +#endif + } + FORCEINLINE void Impl_SetB(T Value) + { +#if VOXEL_MATERIAL_ENABLE_B + B = Value; +#endif + } + FORCEINLINE void Impl_SetA(T Value) + { +#if VOXEL_MATERIAL_ENABLE_A + A = Value; +#endif + } + + FORCEINLINE void Impl_SetU0(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV0 + U0 = Value; +#endif + } + FORCEINLINE void Impl_SetU1(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV1 + U1 = Value; +#endif + } + FORCEINLINE void Impl_SetU2(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV2 + U2 = Value; +#endif + } + FORCEINLINE void Impl_SetU3(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV3 + U3 = Value; +#endif + } + + FORCEINLINE void Impl_SetV0(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV0 + V0 = Value; +#endif + } + FORCEINLINE void Impl_SetV1(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV1 + V1 = Value; +#endif + } + FORCEINLINE void Impl_SetV2(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV2 + V2 = Value; +#endif + } + FORCEINLINE void Impl_SetV3(T Value) + { +#if VOXEL_MATERIAL_ENABLE_UV3 + V3 = Value; +#endif + } + +private: +#if VOXEL_MATERIAL_ENABLE_R + T R; +#endif +#if VOXEL_MATERIAL_ENABLE_G + T G; +#endif +#if VOXEL_MATERIAL_ENABLE_B + T B; +#endif +#if VOXEL_MATERIAL_ENABLE_A + T A; +#endif +#if VOXEL_MATERIAL_ENABLE_UV0 + T U0; + T V0; +#endif +#if VOXEL_MATERIAL_ENABLE_UV1 + T U1; + T V1; +#endif +#if VOXEL_MATERIAL_ENABLE_UV2 + T U2; + T V2; +#endif +#if VOXEL_MATERIAL_ENABLE_UV3 + T U3; + T V3; +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// TODO Make not compatible with BP and have a serialization-safe BP version of it +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelMaterial +#if CPP // Hide the template from UHT + : public TVoxelMaterialImpl + , public TVoxelMaterialStorage +#endif +{ + GENERATED_BODY() + +public: + FORCEINLINE FVoxelMaterial() + { + } + FORCEINLINE FVoxelMaterial(EForceInit) + : TVoxelMaterialImpl(ForceInit) + { + } + FORCEINLINE FVoxelMaterial(const TVoxelMaterialStorage& Storage) + : TVoxelMaterialStorage(Storage) + { + } + + FORCEINLINE bool Serialize(FArchive& Ar) + { + Ar << *this; + return true; + } +}; + +static_assert(FVoxelMaterial::NumChannels <= sizeof(FVoxelMaterial), ""); + +template <> +struct TTypeTraits : TTypeTraitsBase +{ + enum + { + IsBytewiseComparable = true + }; +}; + +template<> +struct TStructOpsTypeTraits : TStructOpsTypeTraitsBase2 +{ + enum + { + WithZeroConstructor = true, + WithNoInitConstructor = true, + WithNoDestructor = true, + WithSerializer = true, + WithIdenticalViaEquality = true + }; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterialBuilder.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterialBuilder.h new file mode 100644 index 00000000..11269656 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMaterialBuilder.h @@ -0,0 +1,144 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelMaterial.h" +#include "VoxelContainers/VoxelStaticArray.h" + +class VOXEL_API FVoxelMaterialBuilder +{ +public: + FVoxelMaterialBuilder() = default; + +public: + void SetMaterialConfig(EVoxelMaterialConfig InMaterialConfig) + { + MaterialConfig = InMaterialConfig; + } + + void Clear(); + FVoxelMaterial Build() const; + +public: + void AddMultiIndex(uint8 Index, float Strength, bool bLockStrength = false) + { + if (MaterialConfig != EVoxelMaterialConfig::MultiIndex || Strength <= 0) + { + return; + } + + for (auto& It : IndicesStrengths) + { + if (It.Index == Index) + { + It.bLocked |= bLockStrength; + It.Strength += Strength; + return; + } + } + + auto& NewIndex = IndicesStrengths.Emplace_GetRef(); + NewIndex.Index = Index; + NewIndex.bLocked = bLockStrength; + NewIndex.Strength = Strength; + } + void AddMultiIndex(int32 Index, float Strength, bool bLockStrength = false) + { + AddMultiIndex(FVoxelUtilities::ClampToUINT8(Index), Strength, bLockStrength); + } + + void SetColor(FColor InColor) + { + Color = InColor; + } + void SetColor(FLinearColor InColor) + { + SetColor(FVoxelUtilities::FloatToUINT8(InColor)); + } + + void SetWetness(uint8 InWetness) + { + Wetness = InWetness; + } + void SetWetness(float InWetness) + { + SetWetness(FVoxelUtilities::FloatToUINT8(InWetness)); + } + void SetWetness(double InWetness) + { + SetWetness(float(InWetness)); + } + + void SetSingleIndex(uint8 Index) + { + SingleIndex = Index; + } + void SetSingleIndex(int32 Index) + { + SetSingleIndex(FVoxelUtilities::ClampToUINT8(Index)); + } + + void SetU(int32 Channel, uint8 Value) + { + if (0 <= Channel && Channel < 4) + { + Us[Channel] = Value; + } + } + void SetU(int32 Channel, float Value) + { + SetU(Channel, FVoxelUtilities::FloatToUINT8(Value)); + } + void SetU(int32 Channel, double Value) + { + SetU(Channel, float(Value)); + } + + void SetV(int32 Channel, uint8 Value) + { + if (0 <= Channel && Channel < 4) + { + Vs[Channel] = Value; + } + } + void SetV(int32 Channel, float Value) + { + SetV(Channel, FVoxelUtilities::FloatToUINT8(Value)); + } + void SetV(int32 Channel, double Value) + { + SetV(Channel, float(Value)); + } + + FVoxelMaterialBuilder& operator=(const FVoxelMaterial& Other) + { + // Override this material by another + // Mainly here to support SetMaterial in graphs + MaterialOverride = Other; + return *this; + } + +private: + EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig(-1); + + TOptional MaterialOverride; + + FColor Color{ ForceInit }; + uint8 SingleIndex = 0; + uint8 Wetness = 0; + + TVoxelStaticArray Us{ ForceInit }; + TVoxelStaticArray Vs{ ForceInit }; + + struct FIndexStrength + { + uint8 Index = 0; + bool bLocked = false; + float Strength = 0; + }; + // Since we usually have very few of them, it's faster to have a TArray than a TMap + // or a static array of size 256, which is expensive to initialize + TArray> IndicesStrengths; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMessages.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMessages.h new file mode 100644 index 00000000..597a98fc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMessages.h @@ -0,0 +1,119 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Logging/TokenizedMessage.h" + +enum class EVoxelShowNotification : uint8 +{ + Show, + Hide +}; + +struct VOXEL_API FVoxelMessages +{ + DECLARE_MULTICAST_DELEGATE_TwoParams(FLogMessageDelegate, const TSharedRef&, EVoxelShowNotification); + static FLogMessageDelegate LogMessageDelegate; + + struct FButton + { + FString Text; + FString Tooltip; + FSimpleDelegate OnClick; + bool bCloseOnClick = true; + }; + struct FNotification + { + uint64 UniqueId = 0; + FString Message; + FSimpleDelegate OnClose; + float Duration = 10.f; + + TArray Buttons; + }; + DECLARE_MULTICAST_DELEGATE_OneParam(FShowNotificationDelegate, const FNotification&); + static FShowNotificationDelegate ShowNotificationDelegate; + +public: + static void LogMessage(const TSharedRef& Message, EVoxelShowNotification ShouldShow); + static void LogMessage(const FText& Message, EMessageSeverity::Type Severity, EVoxelShowNotification ShouldShow, const UObject* Object = nullptr); + static void ShowNotification(const FNotification& Notification); + +public: + template + static void Error(const FString& Message, const UObject* Object = nullptr) + { + Error(FText::FromString(Message), Object); + } + template + static void Error(const FText& Message, const UObject* Object = nullptr) + { + LogMessage(Message, EMessageSeverity::Error, ShouldShow, Object); + } + + template + static void Warning(const FString& Message, const UObject* Object = nullptr) + { + Warning(FText::FromString(Message), Object); + } + template + static void Warning(const FText& Message, const UObject* Object = nullptr) + { + LogMessage(Message, EMessageSeverity::Warning, ShouldShow, Object); + } + + template + static void Info(const FString& Message, const UObject* Object = nullptr) + { + Info(FText::FromString(Message), Object); + } + template + static void Info(const FText& Message, const UObject* Object = nullptr) + { + LogMessage(Message, EMessageSeverity::Info, ShouldShow, Object); + } + +public: + template + static void CondError(bool bCond, const FString& Message, const UObject* Object = nullptr) + { + CondError(bCond, FText::FromString(Message), Object); + } + template + static void CondError(bool bCond, const FText& Message, const UObject* Object = nullptr) + { + if (bCond) + { + LogMessage(Message, EMessageSeverity::Error, ShouldShow, Object); + } + } + + template + static void CondWarning(bool bCond, const FString& Message, const UObject* Object = nullptr) + { + CondWarning(bCond, FText::FromString(Message), Object); + } + template + static void CondWarning(bool bCond, const FText& Message, const UObject* Object = nullptr) + { + if (bCond) + { + LogMessage(Message, EMessageSeverity::Warning, ShouldShow, Object); + } + } + + template + static void CondInfo(bool bCond, const FString& Message, const UObject* Object = nullptr) + { + CondInfo(bCond, FText::FromString(Message), Object); + } + template + static void CondInfo(bool bCond, const FText& Message, const UObject* Object = nullptr) + { + if (bCond) + { + LogMessage(Message, EMessageSeverity::Info, ShouldShow, Object); + } + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMinimal.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMinimal.h new file mode 100644 index 00000000..b3108042 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMinimal.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelLog.h" +#include "VoxelStats.h" +#include "VoxelDebug.h" +#include "VoxelMacros.h" +#include "VoxelSharedPtr.h" +#include "VoxelDefinitions.h" +#include "VoxelDelegateHelpers.h" +#include "VoxelEngineVersionHelpers.h" +#include "VoxelContainers/NoGrowArray.h" \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelModule.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelModule.h new file mode 100644 index 00000000..92877014 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelModule.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class VOXEL_API FVoxelModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h new file mode 100644 index 00000000..d13b13c0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h @@ -0,0 +1,71 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMinimal.h" +#include "VoxelDiff.h" +#include "UObject/Object.h" +#include "VoxelMultiplayerInterface.generated.h" + +struct FVoxelMaterial; +struct FVoxelCompressedWorldSaveImpl; +class IVoxelMultiplayerClient; +class IVoxelMultiplayerServer; + +UCLASS(Abstract, BlueprintType) +class VOXEL_API UVoxelMultiplayerInterface : public UObject +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelMultiplayerInterface Interface + virtual bool IsServer() const { unimplemented(); return false; } + virtual TVoxelSharedPtr CreateClient() const { unimplemented(); return nullptr; } + virtual TVoxelSharedPtr CreateServer() const { unimplemented(); return nullptr; } + //~ End UVoxelMultiplayerInterface Interface +}; + +enum class EVoxelMultiplayerNextLoadType : uint8 +{ + Save = 0, + Diffs = 1, + Unknown = 3 +}; + +class IVoxelMultiplayerClient : public TVoxelSharedFromThis +{ +public: + IVoxelMultiplayerClient() = default; + virtual ~IVoxelMultiplayerClient() = default; + + //~ Begin IVoxelMultiplayerClient Interface + virtual bool IsValid() const = 0; + virtual void Destroy() = 0; + + virtual bool ReceiveDiffs(TArray>& OutValueDiffs, TArray>& OutMaterialDiffs) = 0; + virtual bool ReceiveSave(FVoxelCompressedWorldSaveImpl& OutSave) = 0; + + virtual EVoxelMultiplayerNextLoadType GetNextLoadType() = 0; + //~ End IVoxelMultiplayerClient Interface +}; + +class IVoxelMultiplayerServer : public TVoxelSharedFromThis +{ +public: + // Trigger this when a new player connects + // Must be run on the game thread + FSimpleDelegate OnConnection; + + IVoxelMultiplayerServer() = default; + virtual ~IVoxelMultiplayerServer() = default; + + //~ Begin IVoxelMultiplayerServer Interface + virtual bool IsValid() const = 0; + virtual void Destroy() = 0; + + virtual void SendDiffs(const TArray>& ValueDiffs, const TArray>& MaterialDiffs) = 0; + virtual void SendSave(FVoxelCompressedWorldSaveImpl& Save, bool bForceLoad) = 0; + //~ End IVoxelMultiplayerServer Interface +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h new file mode 100644 index 00000000..c17bfffe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h @@ -0,0 +1,56 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMultiplayer/VoxelMultiplayerInterface.h" + +class VOXEL_API FVoxelMultiplayerClientWithSocket : public IVoxelMultiplayerClient +{ +public: + using IVoxelMultiplayerClient::IVoxelMultiplayerClient; + + //~ Begin IVoxelMultiplayerClient Interface + virtual bool ReceiveDiffs(TArray>& OutValueDiffs, TArray>& OutMaterialDiffs) override final; + virtual bool ReceiveSave(FVoxelCompressedWorldSaveImpl& OutSave) override final; + virtual EVoxelMultiplayerNextLoadType GetNextLoadType() override final; + //~ End IVoxelMultiplayerClient Interface + +protected: + TArray PendingData; + + //~ Begin FVoxelMultiplayerClientWithSocket Interface + virtual void FetchPendingData() = 0; + //~ End FVoxelMultiplayerClientWithSocket Interface + +private: + uint32 ExpectedSize = 0; + EVoxelMultiplayerNextLoadType NextLoadType = EVoxelMultiplayerNextLoadType::Unknown; + + bool TryToReceiveData(uint32 Size, TArray& OutData); + void ResetHeaders(); +}; + +class VOXEL_API FVoxelMultiplayerServerWithSocket : public IVoxelMultiplayerServer +{ +public: + using IVoxelMultiplayerServer::IVoxelMultiplayerServer; + + //~ Begin IVoxelMultiplayerServer Interface + virtual void SendDiffs(const TArray>& ValueDiffs, const TArray>& MaterialDiffs) override final; + virtual void SendSave(FVoxelCompressedWorldSaveImpl& Save, bool bForceLoad) override final; + //~ End IVoxelMultiplayerServer Interface + +protected: + enum class ETarget : uint8 + { + NewSockets, + ExistingSockets + }; + + //~ Begin FVoxelMultiplayerServerWithSocket Interface + virtual void SendData(const TArray& Data, ETarget Target) = 0; + virtual void ClearNewSockets() = 0; + //~ End FVoxelMultiplayerServerWithSocket Interface + +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerManager.h new file mode 100644 index 00000000..6e66af12 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerManager.h @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelTickable.h" +#include "UObject/WeakObjectPtr.h" + +class AVoxelWorld; +class FVoxelData; +class IVoxelLODManager; +class FVoxelDebugManager; +class IVoxelMultiplayerClient; +class IVoxelMultiplayerServer; + +DECLARE_MULTICAST_DELEGATE(FVoxelMultiplayerManagerOnClientConnection); + +struct FVoxelMultiplayerSettings +{ + const TVoxelSharedRef Data; + const TVoxelSharedRef DebugManager; + const TVoxelSharedRef LODManager; + const TWeakObjectPtr VoxelWorld; + const float MultiplayerSyncRate; + + FVoxelMultiplayerSettings( + const AVoxelWorld* World, + const TVoxelSharedRef& Data, + const TVoxelSharedRef& DebugManager, + const TVoxelSharedRef& LODManager); +}; + +class VOXEL_API FVoxelMultiplayerManager : public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + const FVoxelMultiplayerSettings Settings; + + FVoxelMultiplayerManagerOnClientConnection OnClientConnection; + + static TVoxelSharedRef Create(const FVoxelMultiplayerSettings& Settings); + void Destroy(); + + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + //~ End FVoxelTickable Interface + +private: + explicit FVoxelMultiplayerManager( + const FVoxelMultiplayerSettings& Settings, + TVoxelSharedPtr Server, + TVoxelSharedPtr Client); + + double LastSyncTime = 0; + + const TVoxelSharedPtr Server; + const TVoxelSharedPtr Client; + + void ReceiveData() const; + void SendData() const; + void OnConnection(); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h new file mode 100644 index 00000000..df05bc0c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h @@ -0,0 +1,101 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h" +#include "VoxelMultiplayerTcp.generated.h" + +class FSocket; +class FTcpListener; +struct FIPv4Endpoint; +class FVoxelMultiplayerTcpServer; +class FVoxelMultiplayerTcpClient; + +// TCP interface, only accepts IPv4 +UCLASS() +class VOXEL_API UVoxelMultiplayerTcpInterface : public UVoxelMultiplayerInterface +{ + GENERATED_BODY() + +public: + /** + * Connect to a TCP server + * @param Ip The IPv4 of the server + * @param Port The port of the server + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Multiplayer|Tcp") + bool ConnectToServer(FString& OutError, const FString& Ip = TEXT("127.0.0.1"), int32 Port = 10000); + + /** + * Start a TCP server + * @param Ip The IPv4 to accept connection on. 0.0.0.0 to accept all + * @param Port The port of the server + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Multiplayer|Tcp") + bool StartServer(FString& OutError, const FString& Ip = TEXT("0.0.0.0"), int32 Port = 10000); + +public: + //~ Begin UVoxelMultiplayerInterface Interface + virtual bool IsServer() const override; + virtual TVoxelSharedPtr CreateClient() const override; + virtual TVoxelSharedPtr CreateServer() const override; + + //~ End UVoxelMultiplayerInterface Interface + +private: + TVoxelSharedPtr Client; + TVoxelSharedPtr Server; +}; + +class FVoxelMultiplayerTcpClient : public FVoxelMultiplayerClientWithSocket +{ +public: + using FVoxelMultiplayerClientWithSocket::FVoxelMultiplayerClientWithSocket; + + bool Connect(const FString& Ip, int32 Port, FString& OutError); + + //~ Begin IVoxelMultiplayerClient Interface + virtual bool IsValid() const override final; + virtual void Destroy() override final; + //~ End IVoxelMultiplayerClient Interface + +protected: + //~ Begin FVoxelMultiplayerClientWithSocket Interface + virtual void FetchPendingData() override final; + //~ End FVoxelMultiplayerClientWithSocket Interface + +private: + FSocket* Socket = nullptr; +}; + +class FVoxelMultiplayerTcpServer : public FVoxelMultiplayerServerWithSocket +{ +public: + FVoxelMultiplayerTcpServer(); + ~FVoxelMultiplayerTcpServer(); + + bool Start(const FString& Ip, int32 Port, FString& OutError); + + //~ Begin IVoxelMultiplayerServer Interface + virtual bool IsValid() const override final; + virtual void Destroy() override final; + //~ End IVoxelMultiplayerServer Interface + +protected: + //~ Begin FVoxelMultiplayerServerWithSocket Interface + virtual void SendData(const TArray& Data, ETarget Target) override; + virtual void ClearNewSockets() override; + //~ Begin FVoxelMultiplayerServerWithSocket Interface + +private: + TUniquePtr TcpListener; + + TArray Sockets; + + // Sockets that haven't received a save yet + FCriticalSection NewSocketsSection; + TArray NewSockets; + + bool Accept(FSocket* NewSocket, const FIPv4Endpoint& Endpoint); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerUtilities.h new file mode 100644 index 00000000..d47af057 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerUtilities.h @@ -0,0 +1,74 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMultiplayer/VoxelMultiplayerInterface.h" + +struct FVoxelCompressedWorldSaveImpl; + +namespace FVoxelMultiplayerUtilities +{ + constexpr uint8 SizeBytes = 4; + constexpr uint8 NextLoadTypeByes = 1; + constexpr uint8 MagicBytes = 8; + constexpr uint8 HeaderBytes = SizeBytes + NextLoadTypeByes + MagicBytes; + + FORCEINLINE void CreateHeader(TArray& Data, uint32 SizeToSend, EVoxelMultiplayerNextLoadType NextLoadType) + { + check(NextLoadType != EVoxelMultiplayerNextLoadType::Unknown); + + Data.SetNum(HeaderBytes); + + Data[0] = SizeToSend % 256; + SizeToSend /= 256; + Data[1] = SizeToSend % 256; + SizeToSend /= 256; + Data[2] = SizeToSend % 256; + SizeToSend /= 256; + Data[3] = SizeToSend % 256; + + Data[4] = uint8(NextLoadType); + + Data[5] = 0xD; + Data[6] = 0xE; + Data[7] = 0xA; + Data[8] = 0xD; + Data[9] = 0xB; + Data[10] = 0xE; + Data[11] = 0xE; + Data[12] = 0xF; + + static_assert(HeaderBytes == 13, ""); + } + + FORCEINLINE bool LoadHeader(const TArray& Data, uint32& ExpectedSize, EVoxelMultiplayerNextLoadType& NextLoadType) + { + check(Data.Num() == HeaderBytes); + + ExpectedSize = Data[0] + 256 * (Data[1] + 256 * (Data[2] + 256 * Data[3])); + + NextLoadType = EVoxelMultiplayerNextLoadType(Data[4]); + + bool bValid = true; + bValid &= ensureAlways(Data[5] == 0xD); + bValid &= ensureAlways(Data[6] == 0xE); + bValid &= ensureAlways(Data[7] == 0xA); + bValid &= ensureAlways(Data[8] == 0xD); + bValid &= ensureAlways(Data[9] == 0xB); + bValid &= ensureAlways(Data[10] == 0xE); + bValid &= ensureAlways(Data[11] == 0xE); + bValid &= ensureAlways(Data[12] == 0xF); + static_assert(HeaderBytes == 13, ""); + + return bValid; + } + + VOXEL_API void ReadDiffs(const TArray& Data, TArray>& OutValueDiffs, TArray>& OutMaterialDiffs); + // Will append to Data + VOXEL_API void WriteDiffs(TArray& Data, const TArray>& ValueDiffs, const TArray>& MaterialDiffs); + + VOXEL_API void ReadSave(const TArray& Data, FVoxelCompressedWorldSaveImpl& OutSave); + // Will append to Data + VOXEL_API void WriteSave(TArray& Data, const FVoxelCompressedWorldSaveImpl& Save); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelObjectArchive.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelObjectArchive.h new file mode 100644 index 00000000..0b43a287 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelObjectArchive.h @@ -0,0 +1,122 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelObjectArchive.generated.h" + +USTRUCT() +struct FVoxelObjectArchiveEntry +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Category = "Entry") + TSoftObjectPtr Object; + + // Zero is reserved for nullptr + UPROPERTY(VisibleAnywhere, Category = "Entry") + int32 Index = -1; +}; + +// Reader must be called from the game thread +// Writer can be called async, since it's working with soft object pointers +class VOXEL_API FVoxelObjectArchive +{ +public: + static FVoxelObjectArchive MakeReader(FArchive& Ar, const TArray& Objects); + static FVoxelObjectArchive MakeWriter(FArchive& Ar); + + TArray GetWriterObjects() const; + + bool IsSaving() const { return bIsSaving; } + +private: + FVoxelObjectArchive(FArchive& Ar, bool bIsSaving) + : Ar(Ar) + , bIsSaving(bIsSaving) + { + check(bIsSaving ? Ar.IsSaving() : Ar.IsLoading()); + } + +public: + template + FVoxelObjectArchive& operator<<(T& Object) + { + static_assert(!TIsConst::Value, ""); + Ar << Object; + return *this; + } + + template + FVoxelObjectArchive& operator<<(T*& Object) = delete; + + template + FVoxelObjectArchive& operator<<(TSoftObjectPtr& Object) + { + static_assert(TIsDerivedFrom::IsDerived, ""); + + if (IsSaving()) + { + int32 ObjectIndex; + if (Object.IsNull()) + { + ObjectIndex = 0; + } + else + { + if (auto* ObjectIndexPtr = WriterObjects.Find(Object)) + { + ObjectIndex = *ObjectIndexPtr; + } + else + { + ObjectIndex = ObjectCounter++; + WriterObjects.Add(Object, ObjectIndex); + } + } + + Ar << ObjectIndex; + } + else + { + int32 ObjectIndex = -1; + Ar << ObjectIndex; + + if (ObjectIndex == 0) + { + Object = nullptr; + return *this; + } + else + { + TSoftObjectPtr* FoundObjectPtr = ReaderObjects.Find(ObjectIndex); + if (FoundObjectPtr) + { + UObject* FoundObject = FoundObjectPtr->LoadSynchronous(); + if (ensure(FoundObject) && ensure(FoundObject->IsA())) + { + Object = CastChecked(FoundObject); + return *this; + } + } + } + + ensure(false); + + Object = nullptr; + Ar.SetError(); + } + + return *this; + } + +private: + FArchive& Ar; + + const bool bIsSaving; + TMap, int32> WriterObjects; + TMap> ReaderObjects; + + // Zero is reserved for nullptr + int32 ObjectCounter = 1; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctree.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctree.h new file mode 100644 index 00000000..8b9d9f93 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctree.h @@ -0,0 +1,281 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelOctreeId.h" + +template +class TVoxelOctreeBase +{ +public: + // Height of the octree (distance to smallest possible leaf) + const uint8 Height; + + // Center of the octree + const FIntVector Position; + + TVoxelOctreeBase(uint8 Height, const FIntVector& Position) + : Height(Height) + , Position(Position) + { + check(Height < 32); + } + ~TVoxelOctreeBase() = default; + +public: + FORCEINLINE uint32 GetSize() const + { + return ChunkSize << Height; + } + FORCEINLINE uint32 GetHalfSize() const + { + return (ChunkSize / 2) << Height; + } + FORCEINLINE FIntVector GetMin() const + { + return Position - GetHalfSize(); + } + FORCEINLINE FIntVector GetMax() const + { + return Position + GetHalfSize(); + } + FORCEINLINE FVoxelIntBox GetBounds() const + { + return FVoxelIntBox(GetMin(), GetMax()); + } + FORCEINLINE bool IsInOctree(int32 X, int32 Y, int32 Z) const + { + const int32 HalfSize = GetHalfSize(); + return + Position.X - HalfSize <= X && X < Position.X + HalfSize && + Position.Y - HalfSize <= Y && Y < Position.Y + HalfSize && + Position.Z - HalfSize <= Z && Z < Position.Z + HalfSize; + } + FORCEINLINE bool IsInOctree(const FIntVector& P) const + { + return IsInOctree(P.X, P.Y, P.Z); + } + FORCEINLINE bool IsLeaf() const + { + return Height == 0; + } + FORCEINLINE FVoxelOctreeId GetId() const + { + return { Position, Height }; + } + +protected: + FORCEINLINE static FIntVector GetChildPosition(const FIntVector& ParentPosition, uint32 ParentSize, uint8 ChildIndex) + { + return ParentPosition + + FIntVector( + ParentSize / 4 * ((ChildIndex & 0x1) ? 1 : -1), + ParentSize / 4 * ((ChildIndex & 0x2) ? 1 : -1), + ParentSize / 4 * ((ChildIndex & 0x4) ? 1 : -1)); + } +}; + +template +class TVoxelOctreeLeaf : public BaseType +{ +protected: + TVoxelOctreeLeaf(const BaseType& Parent, uint8 ChildIndex) + : BaseType(Parent.Height - 1, this->GetChildPosition(Parent.Position, Parent.GetSize(), ChildIndex)) + { + check(0 <= ChildIndex && ChildIndex < 8); + } +}; + +template +class TVoxelOctreeParent : public BaseType +{ +public: + template + struct FChildrenIterator + { + void* const Children; + bool const bIsLeaf; + + FChildrenIterator(void* Children, bool bIsLeaf) + : Children(Children) + , bIsLeaf(bIsLeaf) + { + } + + struct FIterator + { + void* const VoidPtr; + bool const bIsLeaf; + uint32 Index = 0; + + FIterator(void* Ptr, bool bIsLeaf) + : VoidPtr(Ptr) + , bIsLeaf(bIsLeaf) + { + } + + inline T& operator*() const + { + if (bIsLeaf) + { + LeafType* Ptr = reinterpret_cast(VoidPtr); + return static_cast(Ptr[Index]); + } + else + { + ParentType* Ptr = reinterpret_cast(VoidPtr); + return static_cast(Ptr[Index]); + } + } + inline void operator++() + { + Index++; + } + inline bool operator!=(const FIterator&) const + { + return Index != 8; + } + }; + + FIterator begin() { return FIterator(Children, bIsLeaf); } + FIterator end() { return FIterator(nullptr, false); } + }; + +public: + TVoxelOctreeParent(uint8 Height) + : BaseType(Height, FIntVector(0, 0, 0)) + { + } + ~TVoxelOctreeParent() + { + if (HasChildren()) + { + DestroyChildren(); + } + } + +public: + inline bool HasChildren() const + { + return Children != nullptr; + } + +public: + inline const BaseType& GetChild(int32 X, int32 Y, int32 Z) const + { + return GetChild(GetChildIndex(X, Y, Z)); + } + inline BaseType& GetChild(int32 X, int32 Y, int32 Z) + { + return GetChild(GetChildIndex(X, Y, Z)); + } + + inline const BaseType& GetChild(const FIntVector& P) const + { + return GetChild(P.X, P.Y, P.Z); + } + inline BaseType& GetChild(const FIntVector& P) + { + return GetChild(P.X, P.Y, P.Z); + } + + inline const BaseType& GetChild(int32 Index) const + { + check((Children != nullptr) & (0 <= Index) & (Index < 8)); + if (this->Height == 1) + { + const LeafType* Ptr = reinterpret_cast(Children); + return static_cast(Ptr[Index]); + } + else + { + const ParentType* Ptr = reinterpret_cast(Children); + return static_cast(Ptr[Index]); + } + } + inline BaseType& GetChild(int32 Index) + { + check((Children != nullptr) & (0 <= Index) & (Index < 8)); + if (this->Height == 1) + { + LeafType* Ptr = reinterpret_cast(Children); + return static_cast(Ptr[Index]); + } + else + { + ParentType* Ptr = reinterpret_cast(Children); + return static_cast(Ptr[Index]); + } + } + + inline FChildrenIterator GetChildren() const + { + check(Children); + return { Children, this->Height == 1 }; + } + inline FChildrenIterator GetChildren() + { + check(Children); + return { Children, this->Height == 1 }; + } + +protected: + TVoxelOctreeParent(const TVoxelOctreeParent& Parent, uint8 ChildIndex) + : BaseType(Parent.Height - 1, this->GetChildPosition(Parent.Position, Parent.GetSize(), ChildIndex)) + { + check(0 <= ChildIndex && ChildIndex < 8); + } + + template + inline void CreateChildren(TArgs&&... Args) + { + check(!HasChildren() && this->Height > 0); + + // TODO PERF: Specialized malloc call + Children = FMemory::Malloc(8 * (this->Height == 1 ? sizeof(LeafType) : sizeof(ParentType))); + + for (int32 Index = 0; Index < 8 ; Index++) + { + if (this->Height == 1) + { + LeafType* Ptr = static_cast(Children); + new (&Ptr[Index]) LeafType (static_cast(*this), Index, Forward(Args)...); + } + else + { + ParentType* Ptr = static_cast(Children); + new (&Ptr[Index]) ParentType(static_cast(*this), Index, Forward(Args)...); + } + } + } + inline void DestroyChildren() + { + check(HasChildren()); + + for (auto& Child : GetChildren()) + { + if (this->Height == 1) + { + static_cast(Child).~LeafType(); + } + else + { + static_cast(Child).~ParentType(); + } + } + + FMemory::Free(Children); + Children = nullptr; + } + +private: + void* Children = nullptr; + + inline uint32 GetChildIndex(int32 X, int32 Y, int32 Z) const + { + return (X >= this->Position.X) + 2 * (Y >= this->Position.Y) + 4 * (Z >= this->Position.Z); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctreeId.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctreeId.h new file mode 100644 index 00000000..127f2d71 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelOctreeId.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelOctreeId +{ + FIntVector Position; + uint8 Height; + + FORCEINLINE bool operator==(const FVoxelOctreeId& Other) const + { + return Position == Other.Position && Height == Other.Height; + } + FORCEINLINE bool operator!=(const FVoxelOctreeId& Other) const + { + return Position != Other.Position || Height != Other.Height; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelAssetActor.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelAssetActor.h new file mode 100644 index 00000000..5e1c1b92 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelAssetActor.h @@ -0,0 +1,159 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h" +#include "VoxelTools/VoxelAssetTools.h" +#include "Components/PrimitiveComponent.h" +#include "VoxelEditorDelegatesInterface.h" +#include "VoxelAssetActor.generated.h" + +class UBoxComponent; +class FVoxelData; +class FVoxelDebugManager; +class IVoxelRenderer; +class FVoxelFixedResolutionLODManager; +class IVoxelPool; + +UENUM() +enum class EVoxelAssetActorPreviewUpdateType +{ + // Will only update when Update is clicked, or when a property is changed + Manually, + // Will update after each move + EndOfMove, + // Will update while moving + RealTime +}; + +UCLASS() +class VOXEL_API UAssetActorPrimitiveComponent : public UPrimitiveComponent +{ + GENERATED_BODY() +}; + +UCLASS(HideCategories = ("Tick", "Replication", "Input", "Actor", "Rendering", "HOLD", "LOD", "Cooking", "Collision")) +class VOXEL_API AVoxelAssetActor : public AVoxelPlaceableItemActor, public IVoxelEditorDelegatesInterface +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings") + FVoxelTransformableGeneratorPicker Generator; + + // Higher priority assets will be on top + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings") + int32 Priority = 0; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings", meta = (InlineEditConditionToggle)) + bool bOverrideAssetBounds = false; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings", meta = (EditCondition = "bOverrideAssetBounds")) + FVoxelIntBox AssetBounds = FVoxelIntBox(-25, 25); + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings") + bool bImportAsReference = true; + + UPROPERTY(EditAnywhere, Category = "Asset Actor Settings", meta = (EditCondition = "!bImportAsReference")) + bool bSubtractiveAsset = false; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Asset Actor Settings", meta = (EditCondition = "!bImportAsReference")) + EVoxelAssetMergeMode MergeMode = EVoxelAssetMergeMode::InnerValuesAndInnerMaterials; + +public: +#if WITH_EDITORONLY_DATA + // The lower, the better looking but the slower + UPROPERTY(EditAnywhere, Category = "Preview Settings", meta = (ClampMin = "0", ClampMax = "24", UIMin = "0", UIMax = "10")) + int32 PreviewLOD = 0; + + UPROPERTY(EditAnywhere, Category = "Preview Settings") + EVoxelAssetActorPreviewUpdateType UpdateType = EVoxelAssetActorPreviewUpdateType::EndOfMove; + + // If true, the voxel asset actor position will be rounded to the nearest voxel position when moved + // Always on in cubic mode + UPROPERTY(EditAnywhere, Category = "Preview Settings") + bool bRoundAssetPosition = false; + + // If true, the voxel asset actor rotation will be rounded to the nearest valid rotation (90/180/-90) + // Always on in cubic mode + UPROPERTY(EditAnywhere, Category = "Preview Settings") + bool bRoundAssetRotation = false; + + // Increase this if you want a higher quality preview + // Be careful: might freeze Unreal if too high! + UPROPERTY(EditAnywhere, Category = "Preview Settings", AdvancedDisplay) + uint32 MaxPreviewChunks = 1024; +#endif + +public: + AVoxelAssetActor(); + + //~ Begin AVoxelPlaceableItemActor Interface + virtual void AddItemToWorld(AVoxelWorld* World) override; + virtual int32 GetPriority() const override; + //~ End AVoxelPlaceableItemActor Interface + + // If VoxelWorldData is null, will only return the bounds + FVoxelIntBox AddItemToData( + AVoxelWorld* VoxelWorld, + FVoxelData* VoxelWorldData) const; + +#if WITH_EDITOR + void UpdatePreview(); +#endif + +private: + UPROPERTY() + USceneComponent* Root; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + UAssetActorPrimitiveComponent* PrimitiveComponent; + + UPROPERTY() + UBoxComponent* Box; +#endif + +protected: +#if WITH_EDITOR + //~ Begin AActor Interface + virtual void BeginPlay() override; + virtual void BeginDestroy() override; + virtual void Tick(float DeltaTime) override; + virtual bool ShouldTickIfViewportsOnly() const override { return true; } + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void PostEditMove(bool bFinished) override; + virtual bool CanEditChange(const FProperty* InProperty) const override; + //~ End AActor Interface +#endif + +private: +#if WITH_EDITOR + TVoxelSharedPtr Data; + TVoxelSharedPtr Renderer; + TVoxelSharedPtr LODManager; + TVoxelSharedPtr DebugManager; + + bool IsPreviewCreated() const; + void CreatePreview(); + void DestroyPreview(); + void UpdateBox(); + void ClampTransform(); +#endif + +public: +#if WITH_EDITOR + //~ Begin IVoxelEditorDelegatesInterface Interface + virtual void OnPrepareToCleanseEditorObject(UObject* Object) override; + //~ End IVoxelEditorDelegatesInterface Interface +#endif + +public: +#if WITH_EDITOR + static TVoxelSharedPtr StaticPool; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDataItemActor.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDataItemActor.h new file mode 100644 index 00000000..bf3fe550 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDataItemActor.h @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "VoxelDataItemActor.generated.h" + +class AVoxelWorld; + +UCLASS(Abstract) +class VOXEL_API AVoxelDataItemActor : public AActor +{ + GENERATED_BODY() + +public: + AVoxelDataItemActor(); + +public: + UFUNCTION(BlueprintNativeEvent, DisplayName = "AddItemToWorld") + void K2_AddItemToWorld(AVoxelWorld* World); + + void K2_AddItemToWorld_Implementation(AVoxelWorld* World) + { + AddItemToWorld(World); + } + + virtual void AddItemToWorld(AVoxelWorld* World) {} + + void CallAddItemToWorld(AVoxelWorld* World) + { + TGuardValue AllowScriptsInEditor(GAllowActorScriptExecutionInEditor, true); + K2_AddItemToWorld(World); + } + +public: + // If true, will automatically remove & add back the item to the voxel world when edited + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + bool bAutomaticUpdates = true; + + // Delay in second to queue the refresh, to merge eventual duplicate queries together + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", AdvancedDisplay, meta = (ClampMin = 0)) + float RefreshDelay = 0.1f; + + DECLARE_EVENT(AVoxelDataAssetActor, FOnRefresh); + FOnRefresh OnRefresh; + + UFUNCTION(BlueprintCallable, Category = "Voxel Data Item Actor") + void ScheduleRefresh(); + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void PostEditMove(bool bFinished) override; +#endif + virtual void Destroyed() override; + //~ End UObject Interface + +private: + FTimerHandle RefreshTimerHandle; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h new file mode 100644 index 00000000..4d38bfe6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h" +#include "VoxelDisableEditsBox.generated.h" + +class UBoxComponent; +class AVoxelWorld; + +UCLASS() +class VOXEL_API AVoxelDisableEditsBox : public AVoxelPlaceableItemActor +{ + GENERATED_BODY() + +public: + AVoxelDisableEditsBox(); + + //~ Begin AVoxelPlaceableItemActor Interface + void AddItemToWorld(AVoxelWorld* World) override; + //~ End AVoxelPlaceableItemActor Interface + +private: + FVoxelIntBox GetBox(AVoxelWorld* World) const; + + UPROPERTY() + UBoxComponent* Box; + +#if WITH_EDITOR +protected: + void BeginPlay() override; + void Tick(float DeltaTime) override; + bool ShouldTickIfViewportsOnly() const override { return true; } + void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + void PostEditMove(bool bFinished) override; + +private: + float VoxelSize = 0; + FVector WorldLocation; + + void ClampTransform(); +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h new file mode 100644 index 00000000..faa16f67 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "VoxelPlaceableItemActor.generated.h" + +class AVoxelWorld; + +UCLASS(Abstract) +class VOXEL_API AVoxelPlaceableItemActor : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere, CallInEditor, Category = "Placeable Item Actor Settings") + AVoxelWorld* PreviewWorld; + + // If true, will only affect PreviewWorld. If false, will affect all the voxel worlds spawned into the scene + UPROPERTY(BlueprintReadWrite, EditAnywhere, CallInEditor, Category = "Placeable Item Actor Settings") + bool bOnlyImportIntoPreviewWorld = true; + +public: + UFUNCTION(BlueprintNativeEvent, DisplayName = "AddItemToWorld") + void K2_AddItemToWorld(AVoxelWorld* World); + + UFUNCTION(BlueprintNativeEvent, DisplayName = "GetPriority") + int32 K2_GetPriority() const; + + void K2_AddItemToWorld_Implementation(AVoxelWorld* World) + { + AddItemToWorld(World); + } + int32 K2_GetPriority_Implementation() const + { + return GetPriority(); + } + +public: + //~ Begin AVoxelPlaceableItemActor Interface + virtual void AddItemToWorld(AVoxelWorld* World) {} + virtual int32 GetPriority() const { return 0; } + //~ End AVoxelPlaceableItemActor Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h new file mode 100644 index 00000000..5f16400d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" +#include "VoxelPlaceableItemActorHelper.generated.h" + +class AVoxelWorld; +class AVoxelDataItemActor; + +UCLASS(Within=VoxelWorld) +class VOXEL_API UVoxelPlaceableItemActorHelper : public UObject +{ + GENERATED_BODY() + +public: + void Initialize(); + + AVoxelWorld& GetVoxelWorld() const; + +private: + using FItemInfo = FVoxelDataItemConstructionInfo; + using FItemPtr = TVoxelWeakPtr>; + + struct FActorData + { + TMap Items; + }; + TMap, FActorData> ActorsData; + + void AddActor(AVoxelDataItemActor& Actor); + + void OnActorSpawned(AActor* Actor); + void OnActorUpdated(TWeakObjectPtr Actor); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItem.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItem.h new file mode 100644 index 00000000..7ee13be4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItem.h @@ -0,0 +1,176 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" + +class AVoxelWorld; +class FVoxelObjectArchive; +class FVoxelGeneratorInstance; +class FVoxelTransformableGeneratorInstance; + +struct FVoxelGeneratorInit; +struct FVoxelObjectArchiveEntry; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelAssetItem +{ + TVoxelSharedPtr Generator; + FVoxelIntBox Bounds; + FTransform LocalToWorld; + // Assets are sorted by priority + int32 Priority; + + static void Sort(TArray& Array) + { + Array.Sort([](const FVoxelAssetItem& A, const FVoxelAssetItem& B) { return A.Priority < B.Priority; }); + } +}; + +struct FVoxelDisableEditsBoxItem +{ + FVoxelIntBox Bounds; + + static void Sort(TArray& Array) {} +}; + +struct FVoxelDataItem +{ + TVoxelSharedPtr Generator; + FVoxelIntBox Bounds; + TArray Data; + uint32 Mask = 0; + + static void Sort(TArray& Array) {} +}; + +#define FOREACH_VOXEL_ASSET_ITEM(Macro) \ + Macro(AssetItem) \ + Macro(DisableEditsBoxItem) \ + Macro(DataItem) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +namespace FVoxelPlaceableItemVersion +{ + enum Type : int32 + { + FirstVersion, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +} + +struct FVoxelPlaceableItemLoadInfo +{ + const FVoxelGeneratorInit* GeneratorInit = nullptr; + const TArray* Objects = nullptr; +}; + +namespace FVoxelPlaceableItemsUtilities +{ + VOXEL_API void SerializeItems( + FVoxelObjectArchive& Ar, + const FVoxelPlaceableItemLoadInfo& LoadInfo, + TArray& AssetItems); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Placeable Items Pointers Memory"), STAT_VoxelPlaceableItemsPointers, STATGROUP_VoxelMemory, VOXEL_API); + +#define Macro(X) DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num " #X " Pointers"), STAT_Num## X ## Pointers, STATGROUP_VoxelCounters, VOXEL_API); +FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + +class FVoxelPlaceableItemHolder +{ +public: + FVoxelPlaceableItemHolder() = default; + ~FVoxelPlaceableItemHolder() + { +#define Macro(X) DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPlaceableItemsPointers, X.GetAllocatedSize()); DEC_DWORD_STAT_BY(STAT_Num ## X ## Pointers, X.Num()); X.Reset(); + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + } + +private: +#define Macro(X) TArray X; + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + +public: + template + void ApplyToAllItems(T Lambda) + { +#define Macro(X) for (auto* Item : X) { Lambda(*Item); } + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + } + +public: + int32 NumItems() const + { + int32 Num = 0; +#define Macro(X) Num += X.Num(); + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + return Num; + } + bool NeedToSubdivide(int32 Threshold) const + { + bool bValue = false; +#define Macro(X) bValue |= X.Num() > Threshold; + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + return bValue; + } + +public: +#define Macro(X) const TArray& Get ## X ## s() const { return X; } + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + +#define Macro(X) \ + void AddItem(const FVoxel ## X & Item) \ + { \ + INC_DWORD_STAT(STAT_Num ## X ## Pointers); \ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPlaceableItemsPointers, X.GetAllocatedSize()); \ + \ + ensureVoxelSlowNoSideEffects(!X.Contains(&Item)); \ + X.Add(&Item); \ + FVoxel ## X :: Sort(X); \ + \ + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPlaceableItemsPointers, X.GetAllocatedSize()); \ + } + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro + +#define Macro(X) \ + bool RemoveItem(const FVoxel ## X& Item) \ + { \ + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPlaceableItemsPointers, X.GetAllocatedSize()); \ + \ + const int32 NumRemoved = X.Remove(&Item); \ + ensureVoxelSlow(NumRemoved <= 1); \ + if (NumRemoved) \ + { \ + DEC_DWORD_STAT(STAT_Num ## X ## Pointers); \ + } \ + \ + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelPlaceableItemsPointers, X.GetAllocatedSize()); \ + return NumRemoved != 0; \ + } + FOREACH_VOXEL_ASSET_ITEM(Macro); +#undef Macro +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemManager.h new file mode 100644 index 00000000..bdec75de --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemManager.h @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelPlaceableItemManager.generated.h" + +struct FVoxelDataItem; + +template +class TVoxelDataItemWrapper; + +class IVoxelWorldInterface; + +class UVoxelGeneratorCache; +class UVoxelLineBatchComponent; +class UVoxelGeneratorInstanceWrapper; + +class FVoxelData; + +USTRUCT(BlueprintType) +struct FVoxelDataItemConstructionInfo +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UVoxelGeneratorInstanceWrapper* Generator = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelIntBox Bounds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray Parameters; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (Bitmask, BitmaskEnum = EVoxel32BitMask)) + int32 Mask = uint32(-1); + + bool operator==(const FVoxelDataItemConstructionInfo& Other) const + { + return + Generator == Other.Generator && + Bounds == Other.Bounds && + Parameters == Other.Parameters && + Mask == Other.Mask; + } + friend uint32 GetTypeHash(const FVoxelDataItemConstructionInfo& Info) + { + return + HashCombine( + HashCombine( + HashCombine( + GetTypeHash(Info.Generator), + GetTypeHash(Info.Bounds)), + GetTypeHash(Info.Parameters.Num())), + GetTypeHash(Info.Mask)); + } +}; + +UCLASS(EditInlineNew, Blueprintable, BlueprintType) +class VOXEL_API UVoxelPlaceableItemManager : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + bool bEnableDebug = true; + + // If true, will show all the data items bounds + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", meta = (EditCondition = bEnableDebug)) + bool bDebugBounds = false; + +public: + // Do not call this directly: call the respective Add Data Item instead! + UFUNCTION(BlueprintCallable, Category = "Voxel Placeable Item Manager") + void AddDataItem(FVoxelDataItemConstructionInfo Info); // Important: parameter must not change names! See UK2Node_AddDataItem + + /** + * Draws a line in the world & in the voxel graph preview + * @param Start The start position in voxels + * @param End The end position in voxels + * @param Color The color + */ + UFUNCTION(BlueprintCallable, Category = "Voxel Placeable Item Manager") + void DrawDebugLine( + FVector Start, + FVector End, + FLinearColor Color = FLinearColor::Red); + + /** + * Draws a point in the world & in the voxel graph preview + * @param Position The position in voxels + * @param Color The color + */ + UFUNCTION(BlueprintCallable, Category = "Voxel Placeable Item Manager") + void DrawDebugPoint( + FVector Position, + FLinearColor Color = FLinearColor::Green); + + UFUNCTION(BlueprintCallable, Category = "Voxel Placeable Item Manager") + UVoxelGeneratorCache* GetGeneratorCache() const; + +public: + UFUNCTION(BlueprintNativeEvent, Category = "Voxel Placeable Item Manager") + void OnGenerate(); + + UFUNCTION(BlueprintNativeEvent, Category = "Voxel Placeable Item Manager") + void OnClear(); + + virtual void OnGenerate_Implementation() {} + virtual void OnClear_Implementation() {} + +public: + using FVoxelDataItemPtr = TVoxelWeakPtr>; + + void Generate(); + void Clear(); + void ApplyToData( + FVoxelData& Data, + TMap* OutItems = nullptr); + + const TArray& GetDataItemInfos() const { return DataItemInfos; } + + void SetExternalGeneratorCache(UVoxelGeneratorCache* NewCache) + { + GeneratorCache = NewCache; + } + +private: + // Transient as otherwise it's serialized in the graph preview settings + UPROPERTY(Transient) + TArray DataItemInfos; + + UPROPERTY(Transient) + UVoxelGeneratorCache* GeneratorCache = nullptr; + +public: + struct FDebugLine + { + FVector Start; + FVector End; + FLinearColor Color; + }; + struct FDebugPoint + { + FVector Position; + FLinearColor Color; + }; + + const TArray& GetDebugLines() const { return DebugLines; } + const TArray& GetDebugPoints() const { return DebugPoints; } + + void DrawDebug( + const IVoxelWorldInterface& VoxelWorldInterface, + UVoxelLineBatchComponent& LineBatchComponent); + +private: + TArray DebugLines; + TArray DebugPoints; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h new file mode 100644 index 00000000..462d8b46 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h @@ -0,0 +1,87 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelPlaceableItemsUtilities.generated.h" + +class AVoxelWorld; +class UVoxelGenerator; + +USTRUCT(BlueprintType) +struct FVoxelPerlinWormsSettings +{ + GENERATED_BODY() + + // The random seed to use + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + int32 Seed = 2727; + + // The radius of the worms in voxel + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + float Radius = 25; + + // Start of the worms, in voxel space + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + FVector Start = FVector(0, 0, 0); + + // Direction of the first worm + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + FVector Direction = FVector(1, 1, -1); + + // The amplitude of the random rotation on each worm + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + FVector RotationAmplitude = FVector(10, 10, 90); + + // Max depth of the worms tree + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + int32 NumSegments = 100; + + // Length of the worms in voxel + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + float SegmentLength = 50; + + // Probability of a worm to create 2 worms + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (UIMin = 0, UIMax = 1)) + float SplitProbability = 0.1f; + + // How much SplitProbability is reduced when worms go deeper in the tree + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (UIMin = 0, UIMax = 1)) + float SplitProbabilityGain = 0.1f; + + // Controls the size of the branches after a split + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm") + int32 BranchMeanSize = 25; + + // Controls the size of the branches after a split, relative to BranchMeanSize + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (UIMin = 0, UIMax = 1)) + float BranchSizeVariation = 0.1f; + +public: + // Perlin noise traversal direction + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (AdvancedDisplay)) + FVector NoiseDirection = FVector(1, 1, 1); + + // Segment lengths to use for perlin noise + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (AdvancedDisplay)) + float NoiseSegmentLength = 10; + +public: + // To avoid infinite loops + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Perlin Worm", meta = (AdvancedDisplay)) + int32 MaxWorms = 1000; +}; + +UCLASS() +class VOXEL_API UVoxelPlaceableItemsUtilities : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + DECLARE_DYNAMIC_DELEGATE_ThreeParams(FAddWorm, FVector, Start, FVector, End, float, Radius); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Placeable Items", meta = (DefaultToSelf = World, AdvancedDisplay = "NoiseDir, NoiseSegmentLength")) + static void AddWorms(FAddWorm AddWorm, FVoxelPerlinWormsSettings Settings); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPriorityHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPriorityHandler.h new file mode 100644 index 00000000..28dfa2d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelPriorityHandler.h @@ -0,0 +1,78 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" + +// Somewhat thread safe array +class FInvokerPositionsArray +{ +public: + FInvokerPositionsArray() = default; + explicit FInvokerPositionsArray(int32 NewMax) + : Max(NewMax) + , Data(reinterpret_cast(FMemory::Malloc(sizeof(FIntVector) * NewMax, alignof(FIntVector)))) + { + } + ~FInvokerPositionsArray() + { + FMemory::Free(Data); + } + + void Set(const TArray& Array) + { + check(Array.Num() <= Max); + for (int32 Index = 0; Index < Array.Num(); Index++) + { + Data[Index] = Array[Index]; + } + // Make sure all the data is written before updating Num + FPlatformMisc::MemoryBarrier(); + Num = Array.Num(); + // Force Num update + FPlatformMisc::MemoryBarrier(); + } + FORCEINLINE int32 GetMax() const + { + return Max; + } + FORCEINLINE int32 GetNum() const + { + return Num; + } + FORCEINLINE FIntVector Get(int32 Index) const + { + checkVoxelSlow(Index < Num); + return Data[Index]; + } + +private: + int32 Num = 0; + const int32 Max = 0; + FIntVector* RESTRICT const Data = nullptr; +}; + +struct FVoxelPriorityHandler +{ + FVoxelIntBox Bounds; + TVoxelSharedPtr InvokersPositions; + + FVoxelPriorityHandler() = default; + FVoxelPriorityHandler(const FVoxelIntBox& Bounds, const TVoxelSharedRef& InvokersPositions) + : Bounds(Bounds) + , InvokersPositions(InvokersPositions) + { + } + + inline uint32 GetPriority() const + { + uint64 Distance = MAX_uint64; + for (int32 Index = 0; Index < InvokersPositions->GetNum(); Index++) + { + const FIntVector Position = InvokersPositions->Get(Index); + Distance = FMath::Min(Distance, Bounds.ComputeSquaredDistanceFromBoxToPoint(Position)); + } + return MAX_uint32 - uint32(FMath::Sqrt(Distance)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueryZone.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueryZone.h new file mode 100644 index 00000000..51a68b3c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueryZone.h @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelContainers/VoxelStaticArray.h" + +template +class TVoxelQueryZone +{ +public: + const uint32 Step; + const FVoxelIntBox Bounds; + + template + TVoxelQueryZone(const FVoxelIntBox& Bounds, TArray& Data) + : TVoxelQueryZone(Bounds, Bounds.Size(), 0, Data) + { + } + template + TVoxelQueryZone(const FVoxelIntBox& Bounds, TVoxelStaticArray& Data) + : TVoxelQueryZone(Bounds, Bounds.Size(), 0, Data) + { + } + TVoxelQueryZone(const FVoxelIntBox& Bounds, T* Data) + : TVoxelQueryZone(Bounds, Bounds.Size(), 0, Data) + { + } + template + TVoxelQueryZone(const FVoxelIntBox& Bounds, const FIntVector& ArraySize, int32 LOD, TArray& Data) + : TVoxelQueryZone(Bounds, Bounds.Min, ArraySize, LOD, Data.GetData()) + { + check(Bounds.IsValid()); + check(Bounds.Size() / Step == ArraySize); + check(Data.Num() == ArraySize.X * ArraySize.Y * ArraySize.Z); + } + template + TVoxelQueryZone(const FVoxelIntBox& Bounds, const FIntVector& ArraySize, int32 LOD, TVoxelStaticArray& Data) + : TVoxelQueryZone(Bounds, Bounds.Min, ArraySize, LOD, Data.GetData()) + { + check(Bounds.IsValid()); + check(Bounds.Size() / Step == ArraySize); + check(Data.Num() == ArraySize.X * ArraySize.Y * ArraySize.Z); + } + TVoxelQueryZone(const FVoxelIntBox& Bounds, const FIntVector& ArraySize, int32 LOD, T* Data) + : TVoxelQueryZone(Bounds, Bounds.Min, ArraySize, LOD, Data) + { + check(Bounds.IsValid()); + check(Bounds.Size() / Step == ArraySize); + check(Data); + } + + FORCEINLINE void Set(int32 X, int32 Y, int32 Z, T Value) + { + checkVoxelSlow(Bounds.Contains(X, Y, Z)); + + checkVoxelSlow(X % Step == 0); + checkVoxelSlow(Y % Step == 0); + checkVoxelSlow(Z % Step == 0); + + checkVoxelSlow(Offset.X <= X); + checkVoxelSlow(Offset.Y <= Y); + checkVoxelSlow(Offset.Z <= Z); + + const int32 LocalX = uint32(X - Offset.X) >> LOD; + const int32 LocalY = uint32(Y - Offset.Y) >> LOD; + const int32 LocalZ = uint32(Z - Offset.Z) >> LOD; + + checkVoxelSlow(0 <= LocalX && LocalX < ArraySize.X); + checkVoxelSlow(0 <= LocalY && LocalY < ArraySize.Y); + checkVoxelSlow(0 <= LocalZ && LocalZ < ArraySize.Z); + + const int32 Index = LocalX + ArraySize.X * LocalY + ArraySize.X * ArraySize.Y * LocalZ; + Data[Index] = Value; + } + + TVoxelQueryZone ShrinkTo(const FVoxelIntBox& InBounds) const + { + FVoxelIntBox LocalBounds = Bounds.Overlap(InBounds); + LocalBounds = LocalBounds.MakeMultipleOfRoundUp(Step); + return TVoxelQueryZone(LocalBounds, Offset, ArraySize, LOD, Data); + } + +private: + T* RESTRICT Data; + const FIntVector Offset; + const FIntVector ArraySize; + const uint32 LOD; + + TVoxelQueryZone(const FVoxelIntBox& Bounds, const FIntVector& Offset, const FIntVector& ArraySize, int32 LOD, T* Data) + : Step(1 << LOD) + , Bounds(Bounds) + , Data(Data) + , Offset(Offset) + , ArraySize(ArraySize) + , LOD(LOD) + { + check(Bounds.IsMultipleOf(Step)); + check(FVoxelUtilities::CountIs32Bits(ArraySize)); + } +}; + +#define VOXEL_QUERY_ZONE_ITERATE(QueryZone, X) int32 X = QueryZone.Bounds.Min.X; X < QueryZone.Bounds.Max.X; X += QueryZone.Step \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueueWithNum.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueueWithNum.h new file mode 100644 index 00000000..4eb157c0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueueWithNum.h @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/Queue.h" + +template +struct TVoxelQueueWithNum +{ + inline bool Dequeue(ItemType& OutItem) + { + const bool bSuccess = Queue.Dequeue(OutItem); + if (bSuccess) + { + ensure(QueueNum.Decrement() >= 0); + } + return bSuccess; + } + inline void Empty() + { + Queue.Empty(); + } + inline void Enqueue(const ItemType& Item) + { + QueueNum.Increment(); + Queue.Enqueue(Item); + } + inline void Enqueue(ItemType&& Item) + { + QueueNum.Increment(); + Queue.Enqueue(MoveTemp(Item)); + } + inline bool IsEmpty() const + { + return Queue.IsEmpty(); + } + inline bool Peek(ItemType& OutItem) const + { + return Queue.Peek(OutItem); + } + inline bool Pop() + { + const bool bSuccess = Queue.Pop(); + if (bSuccess) + { + ensure(QueueNum.Decrement() >= 0); + } + return bSuccess; + } + inline int32 Num() const + { + return QueueNum.GetValue(); + } + +private: + FThreadSafeCounter QueueNum; + TQueue Queue; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueuedWork.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueuedWork.h new file mode 100644 index 00000000..048b2b73 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelQueuedWork.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Misc/IQueuedWork.h" + +class IVoxelQueuedWork : public IQueuedWork +{ +public: + // Used for performance reporting and debugging + const FName Name; + // Number of seconds a priority can be cached before being recomputed + const double PriorityDuration; + + FORCEINLINE IVoxelQueuedWork(FName Name, double PriorityDuration) + : Name(Name) + , PriorityDuration(PriorityDuration) + { + } + + IVoxelQueuedWork(const IVoxelQueuedWork&) = delete; + IVoxelQueuedWork& operator=(const IVoxelQueuedWork&) = delete; + + // Called to determine which thread to process next + // Voxel works are usually quite long, so it's worth it to compute all the priorities + // Must be thread safe + virtual uint32 GetPriority() const = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRange.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRange.h new file mode 100644 index 00000000..fee57c12 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRange.h @@ -0,0 +1,698 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" +#include "VoxelRange.generated.h" + +class FVoxelRangeFailStatus : public TThreadSingleton +{ +public: + bool HasFailed() const { return bHasFailed; } + bool HasWarning() const { return bHasWarning; } + const TCHAR* GetMessage() const { return Message; } + +public: + void Fail(const TCHAR* InError) + { + // Note: bHasFailed might be true already if the generated graph has scoped ifs that failed + if (!HasFailed()) + { + bHasFailed = true; + Message = InError; + } + } + void Warning(const TCHAR* InError) + { + if (!HasFailed() && !HasWarning()) + { + bHasWarning = true; + Message = InError; + } + } + void Reset() + { + bHasFailed = false; + bHasWarning = false; + Message = nullptr; + } + +private: + bool bHasFailed = false; + bool bHasWarning = false; + + const TCHAR* Message = nullptr; + + // Not inline, else it's messed up across modules + static VOXEL_API uint32& GetTlsSlot(); + + friend TThreadSingleton; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelBoolRange +{ + bool bCanBeTrue = true; + bool bCanBeFalse = true; + + FVoxelBoolRange() = default; + FVoxelBoolRange(bool bValue) + { + if (bValue) + { + bCanBeTrue = true; + bCanBeFalse = false; + } + else + { + bCanBeTrue = false; + bCanBeFalse = true; + } + } + FVoxelBoolRange(bool bCanBeTrue, bool bCanBeFalse) + : bCanBeTrue(bCanBeTrue) + , bCanBeFalse(bCanBeFalse) + { + check(bCanBeTrue || bCanBeFalse); + } + + FString ToString() const + { + return bCanBeTrue && bCanBeFalse ? "true, false" : bCanBeTrue ? "true" : "false"; + } + + FVoxelBoolRange operator!() const + { + return { bCanBeFalse, bCanBeTrue }; + } + FVoxelBoolRange operator&&(const FVoxelBoolRange& Other) const + { + if (!bCanBeFalse && !Other.bCanBeFalse) + { + return FVoxelBoolRange::True(); + } + else if (!bCanBeTrue || !Other.bCanBeTrue) + { + return FVoxelBoolRange::False(); + } + else + { + return FVoxelBoolRange::TrueOrFalse(); + } + } + FVoxelBoolRange operator||(const FVoxelBoolRange& Other) const + { + if (!bCanBeFalse || !Other.bCanBeFalse) + { + return FVoxelBoolRange::True(); + } + else if (!bCanBeTrue && !Other.bCanBeTrue) + { + return FVoxelBoolRange::False(); + } + else + { + return FVoxelBoolRange::TrueOrFalse(); + } + } + + operator bool() const + { + if (bCanBeTrue && !bCanBeFalse) + { + return true; + } + else if (!bCanBeTrue && bCanBeFalse) + { + return false; + } + else + { + checkVoxelSlow(bCanBeTrue && bCanBeFalse); + FVoxelRangeFailStatus::Get().Fail(TEXT("condition can be true or false")); + return false; + } + } + + static FVoxelBoolRange True() { return { true, false }; } + static FVoxelBoolRange False() { return { false, true }; } + static FVoxelBoolRange TrueOrFalse() { return { true, true }; } + + static bool If(const FVoxelBoolRange& Condition, bool bDefaultValue) + { + auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); + if (RangeFailStatus.HasFailed()) + { + return true; // If already failed do nothing + } + const bool bCondition = Condition; + if (RangeFailStatus.HasFailed()) + { + RangeFailStatus.Reset(); + return bDefaultValue; + } + else + { + return bCondition; + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +T NegativeInfinity(); +template +T PositiveInfinity(); + +template<> +inline constexpr float NegativeInfinity() +{ + return -std::numeric_limits::infinity(); +} +template<> +inline constexpr float PositiveInfinity() +{ + return std::numeric_limits::infinity(); +} + +template<> +inline constexpr double NegativeInfinity() +{ + return -std::numeric_limits::infinity(); +} +template<> +inline constexpr double PositiveInfinity() +{ + return std::numeric_limits::infinity(); +} + +template<> +inline constexpr int32 NegativeInfinity() +{ + return MIN_int32; +} +template<> +inline constexpr int32 PositiveInfinity() +{ + return MAX_int32; +} + +template<> +inline constexpr uint16 NegativeInfinity() +{ + return MIN_uint16; +} +template<> +inline constexpr uint16 PositiveInfinity() +{ + return MAX_uint16; +} + +template<> +inline constexpr FVoxelValue NegativeInfinity() +{ + return FVoxelValue::Full(); +} +template<> +inline constexpr FVoxelValue PositiveInfinity() +{ + return FVoxelValue::Empty(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FString PrettyPrint(const T& Value) +{ + return LexToString(Value); +} + +template<> +inline FString PrettyPrint(const float& Value) +{ + return FString::SanitizeFloat(Value); +} + +template<> +inline FString PrettyPrint(const double& Value) +{ + return FString::SanitizeFloat(Value); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +struct TVoxelRange +{ + T Min; + T Max; + + TVoxelRange() = default; + TVoxelRange(T Value) + : Min(Value) + , Max(Value) + { + } + TVoxelRange(T Min, T Max) + : Min(Min) + , Max(Max) + { + ensure(Min <= Max); + } + template + explicit TVoxelRange(const TVoxelRange& Range) + : Min(Range.Min) + , Max(Range.Max) + { + } + +public: + static constexpr bool bIsInteger = TIsIntegral::Value; + +public: + template + static TVoxelRange FromList(TArgs... Values) + { + return { FVoxelUtilities::VariadicMin(Values...), FVoxelUtilities::VariadicMax(Values...) }; + } + static TVoxelRange Union(const TVoxelRange& A, const TVoxelRange& B) + { + return { FMath::Min(A.Min, B.Min), FMath::Max(A.Max, B.Max) }; + } + static TVoxelRange Intersection(const TVoxelRange& A, const TVoxelRange& B) + { + const T NewMin = FMath::Max(A.Min, B.Min); + const T NewMax = FMath::Min(A.Max, B.Max); + if (ensure(NewMin <= NewMax)) + { + return { NewMin, NewMax }; + } + else + { + return Union(A, B); + } + } + template + static TVoxelRange Union(const TVoxelRange& A, const TVoxelRange& B, TArgs... Args) + { + return Union(Union(A, B), Args...); + } + template + static TVoxelRange Intersection(const TVoxelRange& A, const TVoxelRange& B, TArgs... Args) + { + return Intersection(Intersection(A, B), Args...); + } + static TVoxelRange Infinite() + { + return { NegativeInfinity(), PositiveInfinity() }; + } + static TVoxelRange PositiveInfinite() + { + return { 0, PositiveInfinity() }; + } + static TVoxelRange NegativeInfinite() + { + return { NegativeInfinity(), 0 }; + } + + FString ToString() const + { + return IsSingleValue() ? PrettyPrint(Min) : FString::Printf(TEXT("%s, %s"), *PrettyPrint(Min), *PrettyPrint(Max)); + } + + template + bool Contains(const TOther& Other) const + { + return Min <= Other && Other <= Max; + } + template + bool Contains(const TVoxelRange& Other) const + { + return Min <= Other.Min && Other.Max <= Max; + } + template + bool Intersects(const TVoxelRange& Other) const + { + return Contains(Other.Min) || Contains(Other.Max) || Other.Contains(Min) || Other.Contains(Max); + } + + bool IsSingleValue() const + { + return Min == Max; + } + T GetSingleValue() const + { + ensure(IsSingleValue()); + return Min; + } + + bool IsSingleSign() const + { + return Min == 0 || Max == 0 || (Min < 0) == (Max < 0); + } + T GetSign() const + { + ensure(IsSingleSign()); + return Min == 0 ? FMath::Sign(Max) : FMath::Sign(Min); + } + TVoxelRange ExtendToInfinity() const + { + TVoxelRange New = *this; + if (Min < 0) + { + New.Min = NegativeInfinity(); + New.Max = Max > 0 ? PositiveInfinity() : 0; + } + else + { + New.Min = 0; + New.Max = PositiveInfinity(); + } + return New; + } + + bool IsNegativeInfinity() const + { + return Min == NegativeInfinity(); + } + bool IsPositiveInfinity() const + { + return Max == PositiveInfinity(); + } + bool IsInfinity() const + { + return IsNegativeInfinity() || IsPositiveInfinity(); + } + + template + TVoxelRange& operator=(const TVoxelRange& Other) + { + Min = Other.Min; + Max = Other.Max; + return *this; + } + + template + auto Apply(F Op) const + { + return TVoxelRange{ Op(Min), Op(Max) }; + } + +public: + template + FVoxelBoolRange operator==(const TVoxelRange& Other) const + { + if (IsSingleValue() && Other.IsSingleValue() && Min == Other.Min) + { + checkVoxelSlow(Max == Other.Max); + return FVoxelBoolRange::True(); + } + else if (!Intersects(Other)) + { + return FVoxelBoolRange::False(); + } + else + { + return FVoxelBoolRange::TrueOrFalse(); + } + } + template + FVoxelBoolRange operator!=(const TVoxelRange& Other) const + { + return !(*this == Other); + } + template + FVoxelBoolRange operator<(const TVoxelRange& Other) const + { + if (Max < Other.Min) + { + return FVoxelBoolRange::True(); + } + else if (Other.Max <= Min) + { + return FVoxelBoolRange::False(); + } + else + { + return FVoxelBoolRange::TrueOrFalse(); + } + } + template + FVoxelBoolRange operator>(const TVoxelRange& Other) const + { + if (Min > Other.Max) + { + return FVoxelBoolRange::True(); + } + else if (Other.Min >= Max) + { + return FVoxelBoolRange::False(); + } + else + { + return FVoxelBoolRange::TrueOrFalse(); + } + } + template + FVoxelBoolRange operator<=(const TVoxelRange& Other) const + { + return !(*this > Other); + } + template + FVoxelBoolRange operator>=(const TVoxelRange& Other) const + { + return !(*this < Other); + } + +public: + template + TVoxelRange operator+(const TVoxelRange& Other) const + { + return { Min + Other.Min, Max + Other.Max }; + } + template + TVoxelRange operator-(const TVoxelRange& Other) const + { + return { Min - Other.Max, Max - Other.Min }; + } + TVoxelRange operator-() const + { + return { -Max, -Min }; + } + template + TVoxelRange operator*(const TVoxelRange& Other) const + { + return TVoxelRange::FromList(Min * Other.Min, Min * Other.Max, Max * Other.Min, Max * Other.Max); + } + template + TVoxelRange operator/(const TVoxelRange& Other) const + { + if (Other.IsSingleValue() && Other.GetSingleValue() == 0) + { + if (bIsInteger) + { + // That's how integer / 0 is handled in voxel graphs + return 0; + } + if (IsSingleValue() && GetSingleValue() == 0) + { + FVoxelRangeFailStatus::Get().Warning(TEXT("0 / 0 encountered, will result in a nan")); + return Infinite(); + } + if (0 < Min) + { + // Single value: +inf + return PositiveInfinity(); + } + if (Max < 0) + { + // Single value: -inf + return NegativeInfinity(); + } + return Infinite(); + } + + if (!Other.Contains(0)) // Will also handle single value cases + { + if (Other.IsInfinity()) + { + ensureVoxelSlowNoSideEffects(Other.IsSingleSign()); // Does not contain 0 + ensureVoxelSlowNoSideEffects(Other.GetSign() != 0); // Else wouldn't be infinity, and does not contain 0 + const auto Inf = ExtendToInfinity(); + return TVoxelRange::FromList(Inf.Min / Other.GetSign(), Inf.Max / Other.GetSign()); + } + return TVoxelRange::FromList(Min / Other.Min, Min / Other.Max, Max / Other.Min, Max / Other.Max); + } + else + { + if (Other.IsSingleSign()) + { + ensureVoxelSlowNoSideEffects(Other.GetSign() != 0); // Else would be a single value + const auto Inf = ExtendToInfinity(); + return TVoxelRange::FromList(Inf.Min / Other.GetSign(), Inf.Max / Other.GetSign()); + } + return Infinite(); + } + } + +public: + TVoxelRange operator+(T Other) const + { + return { Min + Other, Max + Other }; + } + TVoxelRange operator-(T Other) const + { + return { Min - Other, Max - Other}; + } + TVoxelRange operator*(T Other) const + { + return { FMath::Min(Min * Other, Max * Other), FMath::Max(Min * Other, Max * Other) }; + } + TVoxelRange operator/(T Other) const + { + return { FMath::Min(Min / Other, Max / Other), FMath::Max(Min / Other, Max / Other) }; + } + + friend FVoxelBoolRange operator==(const TVoxelRange& Range, T Other) + { + return Range == TVoxelRange(Other); + } + friend FVoxelBoolRange operator<(const TVoxelRange& Range, T Other) + { + return Range < TVoxelRange(Other); + } + friend FVoxelBoolRange operator>(const TVoxelRange& Range, T Other) + { + return Range > TVoxelRange(Other); + } + friend FVoxelBoolRange operator<=(const TVoxelRange& Range, T Other) + { + return Range <= TVoxelRange(Other); + } + friend FVoxelBoolRange operator>=(const TVoxelRange& Range, T Other) + { + return Range >= TVoxelRange(Other); + } + friend TVoxelRange operator-(T Other, const TVoxelRange& Range) + { + return TVoxelRange(Other) - Range; + } + friend TVoxelRange operator+(T Other, const TVoxelRange& Range) + { + return TVoxelRange(Other) + Range; + } + friend TVoxelRange operator*(T Other, const TVoxelRange& Range) + { + return TVoxelRange(Other) * Range; + } + friend TVoxelRange operator/(T Other, const TVoxelRange& Range) + { + return TVoxelRange(Other) / Range; + } + + template + TVoxelRange& operator-=(const U& Other) + { + *this = *this - Other; + return *this; + } + template + TVoxelRange& operator+=(const U& Other) + { + *this = *this + Other; + return *this; + } + template + TVoxelRange& operator*=(const U& Other) + { + *this = *this * Other; + return *this; + } + template + TVoxelRange& operator/=(const U& Other) + { + *this = *this / Other; + return *this; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +FArchive& operator<<(FArchive& Ar, TVoxelRange& Range) +{ + Ar << Range.Min; + Ar << Range.Max; + return Ar; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelMaterialRange +{ + FVoxelMaterialRange() = default; + FVoxelMaterialRange(const struct FVoxelMaterial&) {} +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelColorRange +{ + FVoxelColorRange() = default; + FVoxelColorRange(const struct FColor&) {} +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// For display and serialization +USTRUCT() +struct FVoxelRange +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + double Min = 0; + + UPROPERTY(EditAnywhere, Category = "Voxel") + double Max = 0; + + FVoxelRange() = default; + FVoxelRange(float Min, float Max) + : Min(Min) + , Max(Max) + { + } + FVoxelRange(const TVoxelRange& Range) + : Min(Range.Min) + , Max(Range.Max) + { + } + + operator TVoxelRange() const + { + return { v_flt(Min), v_flt(Max) }; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelLODManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelLODManager.h new file mode 100644 index 00000000..ee7c1723 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelLODManager.h @@ -0,0 +1,108 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" + +class IVoxelRenderer; +class IVoxelPool; +class FVoxelDebugManager; +class AVoxelWorld; +class FVoxelData; + +// Fired once per chunk +DECLARE_MULTICAST_DELEGATE_OneParam(FVoxelOnChunkUpdateFinished, FVoxelIntBox); +DECLARE_MULTICAST_DELEGATE_OneParam(FVoxelOnChunkUpdate, FVoxelIntBox); + +struct FVoxelLODSettings +{ + const TVoxelSharedRef Renderer; + const TVoxelSharedRef Pool; + + const float VoxelSize; + const int32 OctreeDepth; + const FVoxelIntBox WorldBounds; + const bool bConstantLOD; + const bool bStaticWorld; + const float MinDelayBetweenLODUpdates; + // Update invokers positions 10 times per seconds: used for LOD updates, but also for chunk updates priority + const float MinDelayBetweenInvokersUpdates = 0.1; + const bool bEnableTransitions; + const bool bInvertTransitions; + + const TWeakObjectPtr World; + + // If Data isn't null, it's Depth and WorldBounds will be used + FVoxelLODSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Renderer, + const TVoxelSharedRef& Pool, + const FVoxelData* Data = nullptr); +}; + +class VOXEL_API IVoxelLODManager +{ +public: + FVoxelOnChunkUpdate OnChunkUpdate; + const FVoxelLODSettings Settings; + + explicit IVoxelLODManager(const FVoxelLODSettings& Settings) + : Settings(Settings) + { + } + virtual ~IVoxelLODManager() = default; + + //~ Begin IVoxelLODManager Interface + // Both specializations are used, and we don't want to allocate single element arrays or to do lots of virtual calls + // Returns the number of chunks to update = number of times FinishDelegate is going to be fired + virtual int32 UpdateBounds(const FVoxelIntBox& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate = FVoxelOnChunkUpdateFinished()) = 0; + virtual int32 UpdateBounds(const TArray& Bounds, const FVoxelOnChunkUpdateFinished& FinishDelegate = FVoxelOnChunkUpdateFinished()) = 0; + + virtual void ForceLODsUpdate() = 0; + virtual bool AreCollisionsEnabled(const FIntVector& Position, uint8& OutLOD) const = 0; + + virtual void Destroy() = 0; + //~ End IVoxelLODManager Interface + +public: + inline int32 UpdateBounds(const FVoxelIntBox& Bounds, const FVoxelOnChunkUpdateFinished::FDelegate& FinishDelegate) + { + FVoxelOnChunkUpdateFinished MulticastDelegate; + MulticastDelegate.Add(FinishDelegate); + return UpdateBounds(Bounds, MulticastDelegate); + } + inline int32 UpdateBounds(const TArray& Bounds, const FVoxelOnChunkUpdateFinished::FDelegate& FinishDelegate) + { + FVoxelOnChunkUpdateFinished MulticastDelegate; + MulticastDelegate.Add(FinishDelegate); + return UpdateBounds(Bounds, MulticastDelegate); + } + + template + inline int32 UpdateBounds_OnAllFinished(const T& Bounds, const FSimpleDelegate& AllFinishedDelegate, const FVoxelOnChunkUpdateFinished::FDelegate& FinishDelegate = {}) + { + TVoxelSharedRef Count = MakeVoxelShared(0); + *Count = UpdateBounds( + Bounds, + FVoxelOnChunkUpdateFinished::FDelegate::CreateLambda([=](FVoxelIntBox ChunkBounds) + { + (*Count)--; + ensure(*Count >= 0); + FinishDelegate.ExecuteIfBound(ChunkBounds); + if (*Count == 0) + { + AllFinishedDelegate.ExecuteIfBound(); + } + })); + if (*Count == 0) + { + // No chunk to update + AllFinishedDelegate.ExecuteIfBound(); + } + return *Count; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h new file mode 100644 index 00000000..c86d10d3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Containers/Queue.h" + +class UVoxelProceduralMeshComponent; + +// We don't want to have every component ticking +// Will be deleted on the game thread when pinned +class IVoxelProceduralMeshComponent_PhysicsCallbackHandler : public TVoxelSharedFromThis +{ +public: + void TickHandler(); + +private: + struct FCallback + { + uint64 CookerId; + TWeakObjectPtr Component; + }; + TQueue Queue; + +public: + void CookerCallback(uint64 CookerId, TWeakObjectPtr Component); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h new file mode 100644 index 00000000..32898580 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h @@ -0,0 +1,192 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#if ENGINE_MINOR_VERSION < 26 +#include "PhysicsEngine/BodySetupEnums.h" +#else +#include "BodySetupEnums.h" +#endif +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelRender/VoxelMeshConfig.h" +#include "VoxelContainers/VoxelStaticArray.h" + +struct FVoxelMaterialIndices; +class FInvokerPositionsArray; +class IVoxelPool; +class FVoxelData; +class FVoxelDebugManager; +class FVoxelToolRenderingManager; +class UMaterialInterface; +class UMaterialInstanceDynamic; +class UVoxelMaterialCollectionBase; +class UVoxelProceduralMeshComponent; +class AVoxelWorld; +class AActor; +struct FVoxelChunkUpdate; + +DECLARE_MULTICAST_DELEGATE(FVoxelRendererOnWorldLoaded); +// Fired once per chunk +DECLARE_MULTICAST_DELEGATE_OneParam(FVoxelOnChunkUpdateFinished, FVoxelIntBox); +DECLARE_MULTICAST_DELEGATE_ThreeParams(FVoxelOnMaterialInstanceCreated, int32 /*ChunkLOD*/, const FVoxelIntBox& /*ChunkBounds*/, UMaterialInstanceDynamic* /*Instance*/); + +struct FVoxelRendererDynamicSettings +{ + struct FLODData + { + TWeakObjectPtr Material; + TWeakObjectPtr MaterialCollection; + FThreadSafeCounter MaxMaterialIndices = 1; + }; + + TVoxelStaticArray LODData{ ForceInit }; +}; + +struct FVoxelRendererSettingsBase +{ + const float VoxelSize; + const TVoxelSharedRef WorldOffset; + + // Always valid + UClass* const ProcMeshClass; + const bool bCastFarShadow; + + const EVoxelPlayType PlayType; + + const TWeakObjectPtr World; + const TWeakObjectPtr RootComponent; + + const EVoxelUVConfig UVConfig; + const float UVScale; + const EVoxelNormalConfig NormalConfig; + const EVoxelMaterialConfig MaterialConfig; + const bool bHardColorTransitions; + + const float BoundsExtension; + + const ECollisionTraceFlag CollisionTraceFlag; + const int32 NumConvexHullsPerAxis; + const bool bCleanCollisionMeshes; + + const EVoxelRenderType RenderType; + const uint32 RenderSharpness; + const bool bCreateMaterialInstances; + const bool bDitherChunks; + const float ChunksDitheringDuration; + const bool bOptimizeIndices; + + const int32 MaxDistanceFieldLOD; + const int32 DistanceFieldBoundsExtension; + const int32 DistanceFieldResolutionDivisor; + const float DistanceFieldSelfShadowBias; + + const bool bOneMaterialPerCubeSide; + const bool bHalfPrecisionCoordinates; + const bool bInterpolateColors; + const bool bInterpolateUVs; + const bool bSRGBColors; + const bool bRenderWorld; + + const float MeshUpdatesBudget; + + const TArray HolesMaterials; + const TMap MaterialsMeshConfigs; + + const bool bMergeChunks; + const int32 ChunksClustersSize; + const bool bDoNotMergeCollisionsAndNavmesh; + + const bool bStaticWorld; + + const float PriorityDuration; + + const TVoxelSharedRef DynamicSettings; + + // If Data isn't null, it's Depth and WorldBounds will be used, and WorldOffset will be set to 0 + FVoxelRendererSettingsBase( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + UPrimitiveComponent* RootComponent, + const FVoxelData* Data = nullptr); + +public: + inline FVector GetChunkRelativePosition(const FIntVector& Position) const + { + return FVector(Position + *WorldOffset) * VoxelSize; + } + + UMaterialInterface* GetVoxelMaterial(int32 LOD, const FVoxelMaterialIndices& MaterialIndices) const; + UMaterialInterface* GetVoxelMaterial(int32 LOD) const; + + inline void OnMaterialsChanged() const + { + // Needed to have errors display again + UniqueId = UniqueIdCounter++; + } + +private: + mutable uint64 UniqueId = UniqueIdCounter++; + static uint64 UniqueIdCounter; +}; + +struct FVoxelRendererSettings : FVoxelRendererSettingsBase +{ + const TVoxelSharedRef Data; + const TVoxelSharedRef Pool; + const TVoxelSharedPtr ToolRenderingManager; // No tools in asset actors + const TVoxelSharedRef DebugManager; + + FVoxelRendererSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + UPrimitiveComponent* RootComponent, + const TVoxelSharedRef& Data, + const TVoxelSharedRef& Pool, + const TVoxelSharedPtr& ToolRenderingManager, + const TVoxelSharedRef& DebugManager, + bool bUseDataSettings); +}; + +class VOXEL_API IVoxelRenderer +{ +public: + const FVoxelRendererSettings Settings; + FVoxelRendererOnWorldLoaded OnWorldLoaded; + FVoxelOnMaterialInstanceCreated OnMaterialInstanceCreated; + + explicit IVoxelRenderer(const FVoxelRendererSettings& Settings); + virtual ~IVoxelRenderer() = default; + + //~ Begin IVoxelRenderer Interface + virtual void Destroy() = 0; + + virtual int32 UpdateChunks( + const FVoxelIntBox& Bounds, + const TArray& ChunksToUpdate, + const FVoxelOnChunkUpdateFinished& FinishDelegate) = 0; + virtual void UpdateLODs(uint64 InUpdateIndex, const TArray& ChunkUpdates) = 0; + + virtual int32 GetTaskCount() const = 0; + + virtual void RecomputeMeshPositions() = 0; + virtual void ApplyNewMaterials() = 0; + virtual void ApplyToAllMeshes(TFunctionRef Lambda) = 0; + + virtual void CreateGeometry_AnyThread(int32 LOD, const FIntVector& ChunkPosition, TArray& OutIndices, TArray& OutVertices) const = 0; + //~ End IVoxelRenderer Interface + + // Called by LOD manager + void SetInvokersPositionsForPriorities(const TArray& NewInvokersPositionsForPriorities); + + // Used by render chunks to compute the priorities + inline const TVoxelSharedRef& GetInvokersPositionsForPriorities() const + { + return InvokersPositionsForPriorities; + } + +private: + TVoxelSharedRef InvokersPositionsForPriorities; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h new file mode 100644 index 00000000..9349d028 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterialCollectionBase.h" +#include "VoxelBasicMaterialCollection.generated.h" + +USTRUCT(BlueprintType) +struct FVoxelBasicMaterialCollectionLayer +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + uint8 LayerIndex = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + UMaterialInterface* LayerMaterial = nullptr; + + inline bool operator==(int32 Other) const + { + return LayerIndex == Other; + } +}; + +// Material collection that does not generate any blending and is just a list of materials +UCLASS(BlueprintType) +class VOXEL_API UVoxelBasicMaterialCollection : public UVoxelMaterialCollectionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Layers") + TArray Layers; + + //~ Begin UVoxelMaterialCollectionBase Interface + virtual int32 GetMaxMaterialIndices() const override; + virtual UMaterialInterface* GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const override; + virtual UMaterialInterface* GetIndexMaterial(uint8 Index) const override; + virtual TArray GetMaterials() const override; + virtual int32 GetMaterialIndex(FName Name) const override; + //~ End UVoxelMaterialCollectionBase Interface + +#if WITH_EDITOR + // Begin UObject Interface + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + // End UObject Interface +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h new file mode 100644 index 00000000..87050558 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRender/VoxelMaterialIndices.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelCachedMaterialCollection.generated.h" + +UCLASS(Abstract) +class VOXEL_API UVoxelCachedMaterialCollection : public UVoxelMaterialCollectionBase +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelMaterialCollectionBase Interface + virtual UMaterialInterface* GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const override final; + virtual void InitializeCollection() override; + //~ End UVoxelMaterialCollectionBase Interface + + //~ Begin UVoxelCachedMaterialCollection Interface + virtual UMaterialInterface* GetVoxelMaterial_NotCached(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const + { + unimplemented(); + return nullptr; + } + //~ End UVoxelCachedMaterialCollection Interface + +private: + UPROPERTY(Transient) + mutable TMap CachedMaterials; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h new file mode 100644 index 00000000..8f1c211e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h @@ -0,0 +1,132 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCachedMaterialCollection.h" +#include "VoxelInstancedMaterialCollection.generated.h" + +class UMaterialInstance; + +UCLASS(Blueprintable, BlueprintType) +class VOXEL_API UVoxelInstancedMaterialCollectionTemplates : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Template") + UMaterialInterface* Template = nullptr; + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template1x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template2x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template3x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template4x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template5x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + UMaterialInterface* Template6x = nullptr; + +public: +#if WITH_EDITOR + // Begin UObject Interface + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + // End UObject Interface +#endif +}; + +USTRUCT(BlueprintType) +struct FVoxelInstancedMaterialCollectionLayer +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + uint8 LayerIndex = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UMaterialInstance* LayerMaterialInstance = nullptr; + + inline bool operator==(int32 Other) const + { + return LayerIndex == Other; + } +}; + +UCLASS(BlueprintType) +class VOXEL_API UVoxelInstancedMaterialCollection : public UVoxelCachedMaterialCollection +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", meta = (ClampMin = 1, ClampMax = 6, UIMin = 1, UIMax = 6)) + int32 MaxMaterialsToBlendAtOnce = 6; + + // For example: If " Sides" is a redirect and there's a "Normal Sides" parameter not overriden, the value of the "Normal" parameter will be used instead + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + TArray Redirects; + + // Parameters prefix in the template: eg, if VOXELPARAM_, VOXELPARAM_0:SomeParameterName will be set + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", AdvancedDisplay) + FString ParametersPrefix = "VOXELPARAM_"; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Templates") + UVoxelInstancedMaterialCollectionTemplates* Templates = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Layers") + TArray Layers; + +public: + UVoxelInstancedMaterialCollection(); + + //~ Begin UVoxelMaterialCollectionBase Interface + virtual int32 GetMaxMaterialIndices() const override; + virtual int32 GetMaterialIndex(FName Name) const override; + virtual TArray GetMaterials() const override; + virtual UMaterialInterface* GetIndexMaterial(uint8 Index) const override; + //~ End UVoxelMaterialCollectionBase Interface + + //~ Begin UVoxelCachedMaterialCollection Interface + virtual UMaterialInterface* GetVoxelMaterial_NotCached(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const override; + //~ End UVoxelCachedMaterialCollection Interface + +#if WITH_EDITOR + // Begin UObject Interface + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + // End UObject Interface +#endif +}; + +// Use to share layers across multiple collections - eg to enable/disable tessellation +UCLASS(BlueprintType) +class VOXEL_API UVoxelInstancedMaterialCollectionInstance : public UVoxelInstancedMaterialCollection +{ + GENERATED_BODY() + +public: + // The layers will be copied from this collection + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Instance") + UVoxelInstancedMaterialCollection* LayersSource; + +public: + //~ Begin UVoxelMaterialCollectionBase Interface + virtual void InitializeCollection() override; + //~ End UVoxelMaterialCollectionBase Interface + + // Begin UObject Interface + virtual void PostLoad() override; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual bool CanEditChange(const FProperty* InProperty) const override; +#endif + // End UObject Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h new file mode 100644 index 00000000..25c93f51 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterialCollectionBase.h" +#include "VoxelLandscapeMaterialCollection.generated.h" + +struct FMaterialParameterInfo; +class UMaterialInstanceConstant; + +USTRUCT(BlueprintType) +struct FVoxelLandscapeMaterialCollectionLayer +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Config") + FName Name; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + uint8 Index = 0; +}; + +USTRUCT(BlueprintType) +struct FVoxelLandscapeMaterialCollectionPermutation +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Config") + FName Names[6]; + + bool operator==(const FVoxelLandscapeMaterialCollectionPermutation& Other) const + { + return + Names[0] == Other.Names[0] && + Names[1] == Other.Names[1] && + Names[2] == Other.Names[2] && + Names[3] == Other.Names[3] && + Names[4] == Other.Names[4] && + Names[5] == Other.Names[5]; + } + + FString ToString() const + { + return FString::Printf(TEXT("%s,%s,%s,%s,%s,%s"), + *Names[0].ToString(), + *Names[1].ToString(), + *Names[2].ToString(), + *Names[3].ToString(), + *Names[4].ToString(), + *Names[5].ToString()); + } +}; + +inline uint32 GetTypeHash(const FVoxelLandscapeMaterialCollectionPermutation& Key) +{ + return + HashCombine(GetTypeHash(Key.Names[0]), + HashCombine(GetTypeHash(Key.Names[1]), + HashCombine(GetTypeHash(Key.Names[2]), + HashCombine(GetTypeHash(Key.Names[3]), + HashCombine(GetTypeHash(Key.Names[4]), GetTypeHash(Key.Names[5])))))); +} + +// Material collection that does not generate any blending and is just a list of materials +UCLASS(BlueprintType) +class VOXEL_API UVoxelLandscapeMaterialCollection : public UVoxelMaterialCollectionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", meta = (ClampMin = 1, ClampMax = 6, UIMin = 1, UIMax = 6)) + int32 MaxMaterialsToBlendAtOnce = 6; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + UMaterialInterface* Material = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Layers") + TArray Layers; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Cache") + mutable TMap MaterialCache; + +public: + //~ Begin UVoxelMaterialCollectionBase Interface + virtual int32 GetMaxMaterialIndices() const override { return FMath::ClampAngle(MaxMaterialsToBlendAtOnce, 1, 6); } + virtual UMaterialInterface* GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const override; + virtual UMaterialInterface* GetIndexMaterial(uint8 Index) const override; + virtual TArray GetMaterials() const override; + virtual int32 GetMaterialIndex(FName Name) const override; + virtual void InitializeCollection() override; + //~ End UVoxelMaterialCollectionBase Interface + + // Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + // End UObject Interface + +private: + UMaterialInstanceConstant* FindOrAddPermutation(const FVoxelLandscapeMaterialCollectionPermutation& Permutation) const; + +#if WITH_EDITOR + UMaterialInstanceConstant* CreateInstanceForPermutation(const FVoxelLandscapeMaterialCollectionPermutation& Permutation) const; + void ForeachMaterialParameter(TFunctionRef Lambda) const; + bool NeedsToBeConvertedToVoxel() const; + void FixupLayers(); + void CleanupCache() const; +#endif + + UPROPERTY(Transient) + TMap IndicesToLayers; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h new file mode 100644 index 00000000..fdb77141 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h @@ -0,0 +1,51 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterialCollectionBase.generated.h" + +struct FVoxelMaterialIndices; +class UMaterialInterface; + +UCLASS(Abstract) +class VOXEL_API UVoxelMaterialCollectionBase : public UObject +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelMaterialCollectionBase Interface + // Max number of material indices this collection can handle + // eg if = 2, this collection can only blend between 2 indices at a time + virtual int32 GetMaxMaterialIndices() const + { + unimplemented(); + return 0; + } + virtual UMaterialInterface* GetVoxelMaterial(const FVoxelMaterialIndices& Indices, uint64 UniqueIdForErrors) const + { + unimplemented(); + return nullptr; + } + + UFUNCTION(BlueprintPure, Category = "Voxel|Material Collection") + virtual UMaterialInterface* GetIndexMaterial(uint8 Index) const + { + return nullptr; + } + + struct FMaterialInfo + { + uint8 Index = 0; + FName Name; + TWeakObjectPtr Material; + }; + // Used by paint material customization. Some materials might be null. + virtual TArray GetMaterials() const { return {}; } + + // Get the material index from a material name + virtual int32 GetMaterialIndex(FName Name) const { return -1; } + // Called before the material collection is used (can be at runtime when dynamic renderer settings change) + virtual void InitializeCollection() {} + //~ End UVoxelMaterialCollectionBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMaterials.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMaterials.h new file mode 100644 index 00000000..8f372b2c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMaterials.h @@ -0,0 +1,46 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRender/VoxelMaterialIndices.h" + +class FVoxelMaterialInterface; + +struct FVoxelChunkMaterials +{ +public: + FVoxelChunkMaterials() = default; + + template + TVoxelSharedRef FindOrAddSingle(T Create) + { + if (!SingleMaterial.IsValid()) + { + const TVoxelSharedRef NewMaterial = Create(); + SingleMaterial = NewMaterial; + } + return SingleMaterial.ToSharedRef(); + } + template + TVoxelSharedRef FindOrAddMultiple(const FVoxelMaterialIndices& Key, T Create) + { + auto* Result = Materials.Find(Key); + if (!Result) + { + const TVoxelSharedRef NewMaterial = Create(); + Result = &Materials.Add(Key, NewMaterial); + } + return Result->ToSharedRef(); + } + + void Reset() + { + SingleMaterial.Reset(); + Materials.Empty(); + } + +private: + TVoxelSharedPtr SingleMaterial; + TMap> Materials; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMesh.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMesh.h new file mode 100644 index 00000000..1e1502ad --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkMesh.h @@ -0,0 +1,154 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelRender/VoxelProcMeshTangent.h" +#include "VoxelRender/VoxelMaterialIndices.h" + +class FVoxelData; +class FDistanceFieldVolumeData; +struct FVoxelRendererSettingsBase; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Chunk Mesh Memory"), STAT_VoxelChunkMeshMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct VOXEL_API FVoxelChunkMeshBuffers +{ + TArray Indices; + TArray Positions; + + // Will not be set if bRenderWorld is false + TArray Normals; + TArray Tangents; + TArray Colors; + TArray> TextureCoordinates; + + FBox Bounds; + FGuid Guid; // Use to avoid rebuilding collisions when the mesh didn't change + + FVoxelChunkMeshBuffers() = default; + ~FVoxelChunkMeshBuffers() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelChunkMeshMemory, LastAllocatedSize); + } + + inline int32 GetNumVertices() const + { + return Positions.Num(); + } + + void BuildAdjacency(TArray& OutAdjacencyIndices) const; + void OptimizeIndices(); + void Shrink(); + void ComputeBounds(); + +private: + int32 LastAllocatedSize = 0; + + void UpdateStats(); +}; + +struct FVoxelChunkMesh +{ +public: + inline bool IsSingle() const + { + return bSingleBuffers; + } + inline bool IsEmpty() const + { + return bSingleBuffers ? SingleBuffers->Indices.Num() == 0 : Map.Num() == 0; + } + + inline TVoxelSharedPtr GetSingleBuffers() const + { + ensure(IsSingle()); + ensure(SingleBuffers.IsValid()); + return SingleBuffers; + } + inline TVoxelSharedPtr GetDistanceFieldVolumeData() const + { + return DistanceFieldVolumeData; + } + inline TVoxelSharedPtr FindBuffer(const FVoxelMaterialIndices& MaterialIndices) const + { + ensure(!IsSingle()); + return Map.FindRef(MaterialIndices); + } + +public: + inline void SetIsSingle(bool bIsSingle) + { + bSingleBuffers = bIsSingle; + } + inline FVoxelChunkMeshBuffers& CreateSingleBuffers() + { + ensure(IsSingle()); + ensure(!SingleBuffers.IsValid()); + SingleBuffers = MakeVoxelShared(); + return *SingleBuffers; + } + inline FVoxelChunkMeshBuffers& FindOrAddBuffer(FVoxelMaterialIndices MaterialIndices, bool& bOutAdded) + { + ensure(!IsSingle()); + auto* BufferPtr = Map.Find(MaterialIndices); + bOutAdded = BufferPtr == nullptr; + if (!BufferPtr) + { + BufferPtr = &Map.Add(MaterialIndices, MakeVoxelShared()); + } + return **BufferPtr; + } + +public: + void BuildDistanceField(int32 LOD, const FIntVector& Position, const FVoxelData& Data, const FVoxelRendererSettingsBase& Settings); + + template + inline void IterateBuffers(T Lambda) + { + if (bSingleBuffers) + { + Lambda(*SingleBuffers); + } + else + { + for (auto& It : Map) + { + Lambda(*It.Value); + } + } + } + template + inline void IterateBuffers(T Lambda) const + { + if (bSingleBuffers) + { + Lambda(static_cast(*SingleBuffers)); + } + else + { + for (auto& It : Map) + { + Lambda(static_cast(*It.Value)); + } + } + } + + template + inline void IterateMaterials(T Lambda) const + { + ensure(!IsSingle()); + for (auto& It : Map) + { + Lambda(It.Key); + } + } + +private: + bool bSingleBuffers = false; + TVoxelSharedPtr SingleBuffers; + TMap> Map; + + TVoxelSharedPtr DistanceFieldVolumeData; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkToUpdate.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkToUpdate.h new file mode 100644 index 00000000..891a4b04 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelChunkToUpdate.h @@ -0,0 +1,53 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" + +struct FVoxelChunkSettings +{ + bool bVisible : 1; + bool bEnableCollisions : 1; + bool bEnableNavmesh : 1; + uint8 TransitionsMask; + + inline bool HasRenderChunk() const { return bVisible || bEnableCollisions || bEnableNavmesh; } + + inline bool operator!=(const FVoxelChunkSettings& Other) const + { + return + bVisible != Other.bVisible || + bEnableCollisions != Other.bEnableCollisions || + bEnableNavmesh != Other.bEnableNavmesh || + TransitionsMask != Other.TransitionsMask; + } + inline bool operator==(const FVoxelChunkSettings& Other) const + { + return + bVisible == Other.bVisible && + bEnableCollisions == Other.bEnableCollisions && + bEnableNavmesh == Other.bEnableNavmesh && + TransitionsMask == Other.TransitionsMask; + } + + inline static FVoxelChunkSettings Visible() + { + return { true, false, false, 0 }; + } + inline static FVoxelChunkSettings VisibleWithCollisions() + { + return { true, true, false, 0 }; + } + +}; + +struct FVoxelChunkUpdate +{ + uint64 Id = -1; + int32 LOD = -1; + FVoxelIntBox Bounds; + FVoxelChunkSettings OldSettings; + FVoxelChunkSettings NewSettings; + TArray> PreviousChunks; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelLODMaterials.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelLODMaterials.h new file mode 100644 index 00000000..3a7c0317 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelLODMaterials.h @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelLODMaterials.generated.h" + +class UMaterialInterface; +class UVoxelMaterialCollectionBase; + +USTRUCT(BlueprintType) +struct FVoxelLODMaterialsBase +{ + GENERATED_BODY() + + // Inclusive + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel", meta = (ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26)) + int32 StartLOD = 0; + + // Inclusive + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel", meta = (ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26)) + int32 EndLOD = 0; +}; + +USTRUCT(BlueprintType) +struct FVoxelLODMaterials : public FVoxelLODMaterialsBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UMaterialInterface* Material = nullptr; +}; + +USTRUCT(BlueprintType) +struct FVoxelLODMaterialCollections : public FVoxelLODMaterialsBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UVoxelMaterialCollectionBase* MaterialCollection = nullptr; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialExpressions.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialExpressions.h new file mode 100644 index 00000000..46c7bf84 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialExpressions.h @@ -0,0 +1,83 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Materials/MaterialExpressionLandscapeLayerBlend.h" +#include "Materials/MaterialExpressionLandscapeLayerSample.h" +#include "Materials/MaterialExpressionLandscapeLayerSwitch.h" +#include "Materials/MaterialExpressionLandscapeLayerWeight.h" +#include "Materials/MaterialExpressionLandscapeVisibilityMask.h" +#include "VoxelMaterialExpressions.generated.h" + +#if WITH_EDITOR +#define FORWARD_CLASS() \ + public: \ + virtual void GetCaption(TArray& OutCaptions) const override; \ + virtual int32 Compile(FMaterialCompiler* Compiler, int32 OutputIndex) override; +#else +#define FORWARD_CLASS() +#endif + +UCLASS() +class VOXEL_API UMaterialExpressionVoxelLandscapeLayerBlend : public UMaterialExpressionLandscapeLayerBlend +{ + GENERATED_BODY() + FORWARD_CLASS() +}; + +UCLASS() +class VOXEL_API UMaterialExpressionVoxelLandscapeLayerSwitch : public UMaterialExpressionLandscapeLayerSwitch +{ + GENERATED_BODY() + FORWARD_CLASS() +}; + +UCLASS() +class VOXEL_API UMaterialExpressionVoxelLandscapeLayerWeight : public UMaterialExpressionLandscapeLayerWeight +{ + GENERATED_BODY() + FORWARD_CLASS() +}; + +UCLASS() +class VOXEL_API UMaterialExpressionVoxelLandscapeLayerSample : public UMaterialExpressionLandscapeLayerSample +{ + GENERATED_BODY() + FORWARD_CLASS() +}; + +UCLASS() +class VOXEL_API UMaterialExpressionVoxelLandscapeVisibilityMask : public UMaterialExpressionLandscapeVisibilityMask +{ + GENERATED_BODY() + FORWARD_CLASS() +}; + +#undef FORWARD_CLASS + +#if WITH_EDITOR +struct VOXEL_API FVoxelMaterialExpressionUtilities +{ + static UClass* GetVoxelExpression(UClass* LandscapeExpression) + { + static TMap Map = + { + #define MAPPING(Name) \ + { \ + UMaterialExpression ## Name::StaticClass(), \ + UMaterialExpressionVoxel ## Name::StaticClass(), \ + } + MAPPING(LandscapeLayerBlend), + MAPPING(LandscapeLayerSwitch), + MAPPING(LandscapeLayerWeight), + MAPPING(LandscapeLayerSample), + MAPPING(LandscapeVisibilityMask) + #undef MAPPING + }; + + return Map.FindRef(LandscapeExpression); + } + static bool NeedsToBeConvertedToVoxel(const TArray& Expressions); +}; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialIndices.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialIndices.h new file mode 100644 index 00000000..956fa249 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialIndices.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelMaterialIndices.generated.h" + +USTRUCT() // UStruct to use it in TMap for GC +struct FVoxelMaterialIndices +{ + GENERATED_BODY() + + uint8 NumIndices = 0; + TVoxelStaticArray SortedIndices; + + FVoxelMaterialIndices() = default; + + inline FString ToString() const + { + FString Result; + for (int32 Index = 0; Index < NumIndices; Index++) + { + if (!Result.IsEmpty()) Result += ", "; + Result += FString::FromInt(SortedIndices[Index]); + } + return Result; + } + + inline bool operator==(const FVoxelMaterialIndices& Other) const + { + if (NumIndices != Other.NumIndices) return false; + for (int32 Index = 0; Index < NumIndices; Index++) + { + if (SortedIndices[Index] != Other.SortedIndices[Index]) return false; + } + return true; + } +}; + +inline uint32 GetTypeHash(const FVoxelMaterialIndices& Indices) +{ + uint32 Hash = GetTypeHash(Indices.NumIndices); + for (int32 Index = 0; Index < Indices.NumIndices; Index++) + { + Hash = HashCombine(Hash, GetTypeHash(Indices.SortedIndices[Index])); + } + return Hash; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h new file mode 100644 index 00000000..224324b7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h @@ -0,0 +1,109 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Containers/Queue.h" +#include "UObject/GCObject.h" +#include "UObject/WeakObjectPtr.h" + +class UMaterialInterface; +class UMaterialInstanceDynamic; +class FVoxelMaterialInterface; + +class VOXEL_API FVoxelMaterialInterfaceManager : public FGCObject +{ +public: + static FVoxelMaterialInterfaceManager& Get() + { + if (!Singleton) + { + Singleton = new FVoxelMaterialInterfaceManager(); + } + return *Singleton; + } + +private: + static FVoxelMaterialInterfaceManager* Singleton; + +public: + FVoxelMaterialInterfaceManager(); + + TVoxelSharedRef DefaultMaterial() const; + TVoxelSharedRef CreateMaterial(UMaterialInterface* MaterialInterface); + TVoxelSharedRef CreateMaterialInstance(UMaterialInterface* Parent); + +protected: + //~ Begin FGCObject Interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + //~ End FGCObject Interface + +private: + TVoxelSharedPtr DefaultMaterialPtr; + +private: + // Need to preallocate chunks for thread safety without locks + static constexpr int32 ChunkSize = 1024; + static constexpr int32 MaxNumChunks = 1024; + + struct FMaterialReference + { + int32 Index = -1; + int32 ChunkIndex = -1; + }; + struct FMaterialInfo + { + UMaterialInterface* Material = nullptr; + bool bIsInstance = false; + int32 ReferenceCount = 0; + }; + struct FChunk + { + TArray> MaterialsInfos_AnyThread; + TArray FreeIndices_GameThread; + }; + TArray, TFixedAllocator> Chunks; + + TQueue ReferencesToDecrement; + + TMap MaterialsMap; + + FMaterialInfo* GetMaterialInfo_AnyThread(FMaterialReference Reference); + FMaterialReference AllocateMaterialInfo_GameThread(); + void DestroyMaterialInfo_GameThread(FMaterialInfo& MaterialInfo); + + void ProcessReferencesToDecrement_GameThread(); + + UMaterialInterface* GetMaterial_AnyThread(FMaterialReference Reference); + void RemoveReference_AnyThread(FMaterialReference Reference); + + TVoxelSharedRef CreateMaterialImpl(UMaterialInterface* MaterialInterface, bool bIsInstance); + +private: + TArray InstancePool; + + UMaterialInstanceDynamic* GetInstanceFromPool(); + void ReturnInstanceToPool(UMaterialInstanceDynamic* Instance); + +public: + void ClearInstancePool(); + + friend class FVoxelMaterialInterface; +}; + +class VOXEL_API FVoxelMaterialInterface +{ +public: + ~FVoxelMaterialInterface(); + + // Will be null if the asset is force deleted + UMaterialInterface* GetMaterial() const; + +private: + const FVoxelMaterialInterfaceManager::FMaterialReference Reference; + + explicit FVoxelMaterialInterface(FVoxelMaterialInterfaceManager::FMaterialReference Reference); + + friend class FVoxelMaterialInterfaceManager; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMeshConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMeshConfig.h new file mode 100644 index 00000000..47a9d7a7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMeshConfig.h @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMeshConfig.generated.h" + +enum class ERendererStencilMask : uint8; + +USTRUCT(BlueprintType) +struct FVoxelMeshConfig +{ + GENERATED_BODY() + + FVoxelMeshConfig() = default; + + /** Whether the primitive receives decals. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel") + bool bReceivesDecals = true; + + /** If true, this component will be rendered in the CustomDepth pass (usually used for outlines) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel", meta=(DisplayName = "Render CustomDepth Pass")) + bool bRenderCustomDepth = false; + + /** Mask used for stencil buffer writes. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel", meta=(EditCondition="bRenderCustomDepth")) + ERendererStencilMask CustomDepthStencilWriteMask = ERendererStencilMask(0); + + /** Optionally write this 0-255 value to the stencil buffer in CustomDepth pass (Requires project setting or r.CustomDepth == 3) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Voxel", meta=(UIMin = "0", UIMax = "255", EditCondition="bRenderCustomDepth", DisplayName = "CustomDepth Stencil Value")) + int32 CustomDepthStencilValue = 0; + + template + inline auto& ApplyTo(T& Mesh) const + { + Mesh.bReceivesDecals = bReceivesDecals; + Mesh.bRenderCustomDepth = bRenderCustomDepth; + Mesh.CustomDepthStencilWriteMask = CustomDepthStencilWriteMask; + Mesh.CustomDepthStencilValue = CustomDepthStencilValue; + return *this; + } + template + inline auto& CopyFrom(T& Mesh) + { + bReceivesDecals = Mesh.bReceivesDecals; + bRenderCustomDepth = Mesh.bRenderCustomDepth; + CustomDepthStencilWriteMask = Mesh.CustomDepthStencilWriteMask; + CustomDepthStencilValue = Mesh.CustomDepthStencilValue; + return *this; + } +}; + +inline uint32 GetTypeHash(const FVoxelMeshConfig& Config) +{ + return + Config.bReceivesDecals * 131 + + Config.bRenderCustomDepth * 5413 + + uint8(Config.CustomDepthStencilWriteMask) * 56453 + + Config.CustomDepthStencilValue * 26737; +} + +inline bool operator==(const FVoxelMeshConfig& A, const FVoxelMeshConfig& B) +{ + return + A.bReceivesDecals == B.bReceivesDecals && + A.bRenderCustomDepth == B.bRenderCustomDepth && + A.CustomDepthStencilWriteMask == B.CustomDepthStencilWriteMask && + A.CustomDepthStencilValue == B.CustomDepthStencilValue; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMesherAsyncWork.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMesherAsyncWork.h new file mode 100644 index 00000000..f2c6f63b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelMesherAsyncWork.h @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelPriorityHandler.h" +#include "VoxelAsyncWork.h" + +struct FVoxelRendererSettings; +struct FVoxelChunkMesh; +class FVoxelDefaultRenderer; +class FVoxelMesherBase; + +class VOXEL_API FVoxelMesherAsyncWork : public FVoxelAsyncWork +{ +public: + const uint64 TaskId = UNIQUE_ID(); + + const uint64 ChunkId; + const int32 LOD; + const FIntVector ChunkPosition; + const bool bIsTransitionTask; + const uint8 TransitionsMask; // If bIsTransitionTask is true + + // Output + TVoxelSharedPtr Chunk; + double CreationTime = 0; + + FVoxelMesherAsyncWork( + FVoxelDefaultRenderer& Renderer, + uint64 ChunkId, + int32 LOD, + const FVoxelIntBox& Bounds, + bool bIsTransitionTask, + uint8 TransitionsMask); + + static void CreateGeometry_AnyThread( + const FVoxelDefaultRenderer& Renderer, + int32 LOD, + const FIntVector& ChunkPosition, + TArray& OutIndices, + TArray& OutVertices); + +private: + // Important: do not allow public delete + virtual ~FVoxelMesherAsyncWork() override; + + //~ Begin FVoxelAsyncWork Interface + virtual void DoWork() override final; + virtual void PostDoWork() override final; + virtual uint32 GetPriority() const override final; + //~ End FVoxelAsyncWork Interface + + static TUniquePtr GetMesher( + const FVoxelRendererSettings& Settings, + int32 LOD, + const FIntVector& ChunkPosition, + bool bIsTransitionTask, + uint8 TransitionsMask); + + + const TVoxelWeakPtr Renderer; + const FVoxelPriorityHandler PriorityHandler; + + template + friend struct TVoxelAsyncWorkDelete; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshBuffers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshBuffers.h new file mode 100644 index 00000000..1c54a33f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshBuffers.h @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "StaticMeshResources.h" +#include "VoxelRawStaticIndexBuffer.h" + +class FVoxelProcMeshBuffersRenderData; + +DECLARE_STATS_GROUP(TEXT("Voxel Proc Mesh Memory"), STATGROUP_VoxelProcMeshMemory, STATCAT_Advanced); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Proc Mesh Memory"), STAT_VoxelProcMeshMemory, STATGROUP_VoxelMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Indices"), STAT_VoxelProcMeshMemory_Indices, STATGROUP_VoxelProcMeshMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Positions"), STAT_VoxelProcMeshMemory_Positions, STATGROUP_VoxelProcMeshMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Colors"), STAT_VoxelProcMeshMemory_Colors, STATGROUP_VoxelProcMeshMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("Adjacency"), STAT_VoxelProcMeshMemory_Adjacency, STATGROUP_VoxelProcMeshMemory, VOXEL_API); +DECLARE_VOXEL_MEMORY_STAT(TEXT("UVs & Tangents"), STAT_VoxelProcMeshMemory_UVs_Tangents, STATGROUP_VoxelProcMeshMemory, VOXEL_API); + +struct VOXEL_API FVoxelProcMeshBuffers +{ + // We'll be initializing/releasing a single buffer multiple times, so need to keep the data on the CPU! + static constexpr bool bNeedsCPUAccess = true; + + // GUIDs of the meshes merged into these buffers, used to avoid rebuilding collisions & navmesh + TArray Guids; + + /** Vertex buffer for this section */ + FStaticMeshVertexBuffers VertexBuffers; + /** Index buffer for this section */ + FVoxelRawStaticIndexBuffer IndexBuffer{ bNeedsCPUAccess }; + /** Index buffer containing adjacency information required by tessellation. */ + FVoxelRawStaticIndexBuffer AdjacencyIndexBuffer{ bNeedsCPUAccess }; + /** Local bounds of this section */ + FBox LocalBounds = FBox(ForceInit); + + inline int32 GetNumVertices() const + { + return VertexBuffers.PositionVertexBuffer.GetNumVertices(); + } + inline int32 GetNumIndices() const + { + return IndexBuffer.GetNumIndices(); + } + + FVoxelProcMeshBuffers(); + ~FVoxelProcMeshBuffers(); + + uint32 GetAllocatedSize() const; + void UpdateStats(); + +private: + int32 LastAllocatedSize = 0; + int32 LastAllocatedSize_Indices = 0; + int32 LastAllocatedSize_Positions = 0; + int32 LastAllocatedSize_Colors = 0; + int32 LastAllocatedSize_Adjacency = 0; + int32 LastAllocatedSize_UVs_Tangents = 0; + mutable TVoxelWeakPtr RenderData; + + friend class FVoxelProcMeshBuffersRenderData; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshSectionSettings.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshSectionSettings.h new file mode 100644 index 00000000..6af72f50 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshSectionSettings.h @@ -0,0 +1,50 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class FVoxelMaterialInterface; + +struct FVoxelProcMeshSectionSettings +{ + TVoxelSharedPtr Material; + bool bEnableCollisions = false; + bool bEnableNavmesh = false; + bool bEnableTessellation = false; + bool bSectionVisible = true; + + FVoxelProcMeshSectionSettings() = default; + FVoxelProcMeshSectionSettings( + const TVoxelSharedPtr& MaterialInterface, + bool bEnableCollisions, + bool bEnableNavmesh, + bool bEnableTessellation, + bool bSectionVisible) + : Material(MaterialInterface) + , bEnableCollisions(bEnableCollisions) + , bEnableNavmesh(bEnableNavmesh) + , bEnableTessellation(bEnableTessellation) + , bSectionVisible(bSectionVisible) + { + } + + bool operator==(const FVoxelProcMeshSectionSettings& Other) const + { + return + Material == Other.Material && + bEnableCollisions == Other.bEnableCollisions && + bEnableNavmesh == Other.bEnableNavmesh && + bEnableTessellation == Other.bEnableTessellation && + bSectionVisible == Other.bSectionVisible; + } + friend uint32 GetTypeHash(const FVoxelProcMeshSectionSettings& Settings) + { + return + GetTypeHash(Settings.Material) + + 131 * Settings.bEnableCollisions + + 9109 * Settings.bEnableNavmesh + + 50551 * Settings.bEnableTessellation + + 100019 * Settings.bSectionVisible; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshTangent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshTangent.h new file mode 100644 index 00000000..e268ec48 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProcMeshTangent.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelProcMeshTangent +{ + FVector TangentX = FVector::RightVector; + bool bFlipTangentY = false; + + FVoxelProcMeshTangent() = default; + FVoxelProcMeshTangent(float X, float Y, float Z) + : TangentX(X, Y, Z) + , bFlipTangentY(false) + { + } + FVoxelProcMeshTangent(FVector InTangentX, bool bInFlipTangentY) + : TangentX(InTangentX) + , bFlipTangentY(bInFlipTangentY) + { + } + + FVector GetY(const FVector& Normal) const + { + return (Normal ^ TangentX) * (bFlipTangentY ? -1 : 1); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h new file mode 100644 index 00000000..587a1061 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h @@ -0,0 +1,341 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelPriorityHandler.h" +#include "VoxelRender/VoxelProcMeshSectionSettings.h" +#include "Components/PrimitiveComponent.h" +#include "Components/ModelComponent.h" +#include "VoxelProceduralMeshComponent.generated.h" + +struct FKConvexElem; +struct FVoxelProcMeshBuffers; +struct FVoxelRendererSettings; +struct FMaterialRelevance; +class FVoxelToolRenderingManager; +class FDistanceFieldVolumeData; +class IVoxelAsyncPhysicsCooker; +class UBodySetup; +class UMaterialInterface; +class UVoxelProceduralMeshComponent; +class AVoxelWorld; +class IVoxelPool; +class IVoxelProceduralMeshComponent_PhysicsCallbackHandler; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Physics Triangle Meshes Memory"), STAT_VoxelPhysicsTriangleMeshesMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct FVoxelProceduralMeshComponentMemoryUsage +{ + uint32 TriangleMeshes = 0; +}; + +enum class EVoxelProcMeshSectionUpdate : uint8 +{ + UpdateNow, + DelayUpdate +}; + +DECLARE_MULTICAST_DELEGATE_OneParam(FOnFreezeVoxelCollisionChanged, bool); + +UCLASS(BlueprintType, Blueprintable, ClassGroup = (Voxel), meta = (BlueprintSpawnableComponent)) +class VOXEL_API UVoxelProceduralMeshComponent +#if 0 // <-- change here +#if VOXEL_ENABLE_FOLIAGE_PAINT_HACK +#error "Please replace the #if 1 above by a #if 0" +#endif + : public UPrimitiveComponent +#else +#if !VOXEL_ENABLE_FOLIAGE_PAINT_HACK +#error "Please replace the #if 0 above by a #if 1" +#endif + // Inheriting from UModelComponent allows to use foliage paint tools in editor. Should have no side effects. + : public UModelComponent +#endif +{ + GENERATED_BODY() + +public: + void Init( + int32 InDebugLOD, + uint32 InDebugChunkId, + const FVoxelPriorityHandler& InPriorityHandler, + const TVoxelWeakPtr& InPhysicsCallbackHandler, + const FVoxelRendererSettings& RendererSettings); + void ClearInit(); + +private: + bool bInit = false; + // Used for convex collisions + uint64 UniqueId = 0; + // Used to show LOD color in the mesh LOD visualization & for convex collision cooking + int32 LOD = 0; + // For debug + uint32 DebugChunkId = 0; + // Priority for physics cooking tasks + FVoxelPriorityHandler PriorityHandler; + // Will be triggered by the async cooker on an async thread, and then will trigger us on game thread + TVoxelWeakPtr PhysicsCallbackHandler; + // Weak ptr else the pool stays created until GC + TVoxelWeakPtr Pool; + // Used to show tools overlays + TVoxelWeakPtr ToolRenderingManager; + // For cooking tasks + float PriorityDuration = 0; + // Collisions settings + ECollisionTraceFlag CollisionTraceFlag = ECollisionTraceFlag::CTF_UseDefault; + // For convex collisions + int32 NumConvexHullsPerAxis = 2; + // Cooks slower, but won't crash in case of weird complex geometry + bool bCleanCollisionMesh = false; + // Will clear the proc mesh buffers once navmesh + collisions have been built + bool bClearProcMeshBuffersOnFinishUpdate = false; + // Distance field bias + float DistanceFieldSelfShadowBias = 0.f; + +public: + UVoxelProceduralMeshComponent(); + ~UVoxelProceduralMeshComponent(); + + UFUNCTION(BlueprintImplementableEvent, Category = "Voxel") + void InitChunk(uint8 ChunkLOD, FVoxelIntBox ChunkBounds); + +public: + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Voxel|Collisions") + static bool AreVoxelCollisionsFrozen(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Collisions") + static void SetVoxelCollisionsFrozen(bool bFrozen); + + static void AddOnFreezeVoxelCollisionChanged(const FOnFreezeVoxelCollisionChanged::FDelegate& NewDelegate); + +private: + static bool bAreCollisionsFrozen; + static TSet> PendingCollisions; + static FOnFreezeVoxelCollisionChanged OnFreezeVoxelCollisionChanged; + +public: + void SetDistanceFieldData(const TVoxelSharedPtr& InDistanceFieldData); + void SetProcMeshSection(int32 Index, FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update); + int32 AddProcMeshSection(FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update); + void ReplaceProcMeshSection(FVoxelProcMeshSectionSettings Settings, TUniquePtr Buffers, EVoxelProcMeshSectionUpdate Update); + void ClearSections(EVoxelProcMeshSectionUpdate Update); + void FinishSectionsUpdates(); + + template + inline void IterateSectionsSettings(F Lambda) + { + for (auto& Section : ProcMeshSections) + { + Lambda(Section.Settings); + } + } + template + inline void IterateSections(F Lambda) const + { + for (auto& Section : ProcMeshSections) + { + Lambda(Section.Settings, static_cast(*Section.Buffers)); + } + } + +public: + //~ Begin UPrimitiveComponent Interface. + virtual FPrimitiveSceneProxy* CreateSceneProxy() override final; + virtual UBodySetup* GetBodySetup() override final; + virtual UMaterialInterface* GetMaterialFromCollisionFaceIndex(int32 FaceIndex, int32& SectionIndex) const override final; + virtual int32 GetNumMaterials() const override final; + virtual UMaterialInterface* GetMaterial(int32 ElementIndex) const override final; + virtual void GetUsedMaterials(TArray& OutMaterials, bool bGetDebugMaterials) const override; + virtual bool DoCustomNavigableGeometryExport(FNavigableGeometryExport& GeomExport) const override final; + virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override final; + virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override; + //~ End UPrimitiveComponent Interface. + + FMaterialRelevance GetMaterialRelevance(ERHIFeatureLevel::Type InFeatureLevel) const; + +private: + void UpdatePhysicalMaterials(); + void UpdateLocalBounds(); + void UpdateNavigation(); + void UpdateCollision(); + void FinishCollisionUpdate(); + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + void UpdateConvexMeshes( + const FBox& ConvexBounds, + TArray&& ConvexElements, + TArray&& ConvexMeshes, + bool bCanFail = false); +#endif + +private: + void PhysicsCookerCallback(uint64 CookerId); + + friend class IVoxelAsyncPhysicsCooker; + friend class FVoxelAsyncPhysicsCooker_PhysX; + friend class FVoxelAsyncPhysicsCooker_Chaos; + friend class IVoxelProceduralMeshComponent_PhysicsCallbackHandler; + +private: + UPROPERTY(Transient) + UBodySetup* BodySetup; + UPROPERTY(Transient) + UBodySetup* BodySetupBeingCooked; + + IVoxelAsyncPhysicsCooker* AsyncCooker = nullptr; + FVoxelProceduralMeshComponentMemoryUsage MemoryUsage; + + struct FVoxelProcMeshSection + { + FVoxelProcMeshSectionSettings Settings; + TVoxelSharedPtr Buffers; + }; + TArray ProcMeshSections; + TVoxelSharedPtr DistanceFieldData; + + // Used to skip rebuilding collisions & navmesh + // GUID to detect geometry change + // Map to detect settings changes + TArray ProcMeshSectionsSortedGuids; + TMap ProcMeshSectionsGuidToSettings; + + FBoxSphereBounds LocalBounds; + + double LastFinishSectionsUpdatesTime = 0; + + friend class FVoxelProceduralMeshSceneProxy; + +#if VOXEL_ENABLE_FOLIAGE_PAINT_HACK +public: + // Skip UBrushComponent overrides and forward everything to UPrimitiveComponent + + //~ Begin UPrimitiveComponent Interface. + virtual void CreateRenderState_Concurrent(ONLY_UE_25_AND_HIGHER(FRegisterComponentContext* Context)) override + { + UPrimitiveComponent::CreateRenderState_Concurrent(ONLY_UE_25_AND_HIGHER(Context)); + } + virtual void DestroyRenderState_Concurrent() override + { + UPrimitiveComponent::DestroyRenderState_Concurrent(); + } + virtual bool GetLightMapResolution(int32& Width, int32& Height) const override + { + return UPrimitiveComponent::GetLightMapResolution(Width, Height); + } + virtual int32 GetStaticLightMapResolution() const override + { + return UPrimitiveComponent::GetStaticLightMapResolution(); + } + virtual void GetLightAndShadowMapMemoryUsage(int32& LightMapMemoryUsage, int32& ShadowMapMemoryUsage) const override + { + UPrimitiveComponent::GetLightAndShadowMapMemoryUsage(LightMapMemoryUsage, ShadowMapMemoryUsage); + } + virtual bool ShouldRecreateProxyOnUpdateTransform() const override + { + return UPrimitiveComponent::ShouldRecreateProxyOnUpdateTransform(); + } +#if WITH_EDITOR + virtual void GetStaticLightingInfo(FStaticLightingPrimitiveInfo& OutPrimitiveInfo, const TArray& InRelevantLights, const FLightingBuildOptions& Options) override + { + UPrimitiveComponent::GetStaticLightingInfo(OutPrimitiveInfo, InRelevantLights, Options); + } + virtual void AddMapBuildDataGUIDs(TSet& InGUIDs) const override + { + UPrimitiveComponent::AddMapBuildDataGUIDs(InGUIDs); + } +#endif + virtual ELightMapInteractionType GetStaticLightingType() const override + { + return UPrimitiveComponent::GetStaticLightingType(); + } +#if ENGINE_MINOR_VERSION > 22 + virtual void GetStreamingRenderAssetInfo(FStreamingTextureLevelContext& LevelContext, TArray& OutStreamingRenderAssets) const override + { + UPrimitiveComponent::GetStreamingRenderAssetInfo(LevelContext, OutStreamingRenderAssets); + } +#else + virtual void GetStreamingTextureInfo(FStreamingTextureLevelContext& LevelContext, TArray& OutStreamingTextures) const override + { + UPrimitiveComponent::GetStreamingTextureInfo(LevelContext, OutStreamingTextures); + } +#endif + virtual bool IsPrecomputedLightingValid() const override + { + return UPrimitiveComponent::IsPrecomputedLightingValid(); + } + virtual bool SupportsStaticLighting() const override + { + return UPrimitiveComponent::SupportsStaticLighting(); + } + //~ End UPrimitiveComponent Interface. + + //~ Begin UActorComponent Interface. + virtual void InvalidateLightingCacheDetailed(bool bInvalidateBuildEnqueuedLighting, bool bTranslationOnly) override + { + UPrimitiveComponent::InvalidateLightingCacheDetailed(bInvalidateBuildEnqueuedLighting, bTranslationOnly); + } + virtual void PropagateLightingScenarioChange() override + { + UPrimitiveComponent::PropagateLightingScenarioChange(); + } + //~ End UActorComponent Interface. + + //~ Begin UObject Interface. +#define SerializeFake Serialize // Else UHT complains about Serialize function in #if + virtual void SerializeFake(FArchive& Ar) override +#undef SerializeFake + { + UPrimitiveComponent::Serialize(Ar); + } + virtual void PostLoad() override + { + UPrimitiveComponent::PostLoad(); + } +#if WITH_EDITOR + virtual void PostEditUndo() override + { + UPrimitiveComponent::PostEditUndo(); + } + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override + { + UPrimitiveComponent::PostEditChangeProperty(PropertyChangedEvent); + } +#endif // WITH_EDITOR + virtual bool IsNameStableForNetworking() const override + { + return UPrimitiveComponent::IsNameStableForNetworking(); + } + static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) + { + UPrimitiveComponent::AddReferencedObjects(InThis, Collector); + } + //~ End UObject Interface. + + // Need to implement overrides as well to avoid linking errors + + //~ Begin Interface_CollisionDataProvider Interface + virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override + { + return false; + } + virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override + { + return false; + } + virtual bool WantsNegXTriMesh() override + { + return false; + } + //~ End Interface_CollisionDataProvider Interface + +#if WITH_EDITOR + virtual bool GenerateElements(bool bBuildRenderData) override + { + return false; + } +#endif +#endif +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelRawStaticIndexBuffer.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelRawStaticIndexBuffer.h new file mode 100644 index 00000000..6bae3468 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelRawStaticIndexBuffer.h @@ -0,0 +1,175 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "RenderResource.h" +#include "RawIndexBuffer.h" +#include "VoxelMinimal.h" + +/** + * Constructor isn't exported in the engine definition + */ +class VOXEL_API FVoxelRawStaticIndexBuffer : public FIndexBuffer +{ +public: + /** + * Initialization constructor. + * @param InNeedsCPUAccess True if resource array data should be accessible by the CPU. + */ + FVoxelRawStaticIndexBuffer(bool InNeedsCPUAccess); + + /** + * Sets a single index value. Consider using SetIndices() instead if you're setting a lot of indices. + * @param At The index of the index to set + * @param NewIndexValue The index value + */ + inline void SetIndex( const uint32 At, const uint32 NewIndexValue ) + { + check( At >= 0 && At < uint32(IndexStorage.Num()) ); + + if( b32Bit ) + { + uint32* Indices32Bit = reinterpret_cast(IndexStorage.GetData()); + Indices32Bit[ At ] = NewIndexValue; + } + else + { + uint16* Indices16Bit = reinterpret_cast(IndexStorage.GetData()); + Indices16Bit[ At ] = uint16(NewIndexValue); + } + } + + /** + * Set the indices stored within this buffer. + * @param InIndices The new indices to copy in to the buffer. + * @param DesiredStride The desired stride (16 or 32 bits). + */ + void SetIndices(const TArray& InIndices, EIndexBufferStride::Type DesiredStride); + + // Stride set from the num + void AllocateData(int32 NumInIndices); + + /** + * Insert indices at the given position in the buffer + * @param At Index to insert at + * @param IndicesToAppend Pointer to the array of indices to insert + * @param NumIndicesToAppend How many indices are in the IndicesToAppend array + */ + void InsertIndices(uint32 At, const uint32* IndicesToAppend, uint32 NumIndicesToAppend ); + + /** + * Append indices to the end of the buffer + * @param IndicesToAppend Pointer to the array of indices to add to the end + * @param NumIndicesToAppend How many indices are in the IndicesToAppend array + */ + void AppendIndices( const uint32* IndicesToAppend, uint32 NumIndicesToAppend ); + + /** @return Gets a specific index value */ + inline uint32 GetIndex(const uint32 At) const + { + checkVoxelSlow(At >= 0 && At < uint32(IndexStorage.Num())); + uint32 IndexValue; + if (b32Bit) + { + const uint32* SrcIndices32Bit = reinterpret_cast(IndexStorage.GetData()); + IndexValue = SrcIndices32Bit[At]; + } + else + { + const uint16* SrcIndices16Bit = reinterpret_cast(IndexStorage.GetData()); + IndexValue = SrcIndices16Bit[At]; + } + + return IndexValue; + } + + inline const uint32* RESTRICT GetData_32() const + { + check(b32Bit); + return reinterpret_cast(IndexStorage.GetData()); + } + inline const uint16* RESTRICT GetData_16() const + { + check(!b32Bit); + return reinterpret_cast(IndexStorage.GetData()); + } + + /** + * Removes indices from the buffer + * + * @param At The index of the first index to remove + * @param NumIndicesToRemove How many indices to remove + */ + void RemoveIndicesAt(uint32 At, uint32 NumIndicesToRemove ); + + /** + * Retrieve a copy of the indices in this buffer. Only valid if created with + * NeedsCPUAccess set to true or the resource has not yet been initialized. + * @param OutIndices Array in which to store the copy of the indices. + */ + void GetCopy(TArray& OutIndices) const; + + /** + * Get the direct read access to index data + * Only valid if NeedsCPUAccess = true and indices are 16 bit + */ + const uint16* AccessStream16() const; + + /** + * Retrieves an array view in to the index buffer. The array view allows code + * to retrieve indices as 32-bit regardless of how they are stored internally + * without a copy. The array view is valid only if: + * The buffer was created with NeedsCPUAccess = true + * OR the resource has not yet been initialized + * AND SetIndices has not been called since. + */ + FIndexArrayView GetArrayView() const; + + /** + * Computes the number of indices stored in this buffer. + */ + FORCEINLINE int32 GetNumIndices() const + { + return NumIndices; + } + + /** + * Computes the amount of memory allocated to store the indices. + */ + FORCEINLINE uint32 GetAllocatedSize() const + { + return IndexStorage.GetAllocatedSize(); + } + + /** + * Serialization. + * @param Ar Archive to serialize with + * @param bNeedsCPUAccess Whether the elements need to be accessed by the CPU + */ + void Serialize(FArchive& Ar, bool bNeedsCPUAccess); + + /** + * Discard + * discards the serialized data when it is not needed + */ + void Discard(); + + // FRenderResource interface. + virtual void InitRHI() override; + + inline bool Is32Bit() const { return b32Bit; } + +private: + /** Storage for indices. */ + TResourceArray IndexStorage; + /** 32bit or 16bit? */ + bool b32Bit; + /** The cached number of indices. */ + uint32 NumIndices = 0; + + void UpdateCachedNumIndices() + { + NumIndices = b32Bit ? (IndexStorage.Num() / 4) : (IndexStorage.Num() / 2); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelToolRendering.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelToolRendering.h new file mode 100644 index 00000000..4230f431 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelRender/VoxelToolRendering.h @@ -0,0 +1,84 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelContainers/VoxelSparseArray.h" +#include "Misc/ScopeLock.h" + +class FVoxelMaterialInterface; + +struct FVoxelToolRendering +{ + bool bEnabled = false; + FBox WorldBounds; + TVoxelSharedPtr Material; +}; + +DEFINE_TYPED_VOXEL_SPARSE_ARRAY_ID(FVoxelToolRenderingId); + +class FVoxelToolRenderingManager : public TVoxelSharedFromThis +{ +public: + FVoxelToolRenderingManager() = default; + ~FVoxelToolRenderingManager() = default; + + inline FVoxelToolRenderingId CreateTool(bool bEnabled = false) + { + FScopeLock Lock(&Section); + return Tools.Add({ bEnabled }); + } + inline void RemoveTool(FVoxelToolRenderingId Id) + { + FScopeLock Lock(&Section); + if (!ensure(Tools.IsValidIndex(Id))) return; + Tools.RemoveAt(Id); + RecomputeToolsMaterials(); + } + template + inline void EditTool(FVoxelToolRenderingId Id, T Lambda) + { + FScopeLock Lock(&Section); + if (!ensure(Tools.IsValidIndex(Id))) return; + Lambda(Tools[Id]); + RecomputeToolsMaterials(); + } + inline bool IsValidTool(FVoxelToolRenderingId Id) const + { + FScopeLock Lock(&Section); + return Tools.IsValidIndex(Id); + } + template + inline void IterateTools(T Lambda) const + { + FScopeLock Lock(&Section); + for (auto& Tool : Tools) + { + Lambda(Tool); + } + } + inline const TArray>& GetToolsMaterials() const + { + check(IsInGameThread()); + return ToolsMaterials; + } + +private: + mutable FCriticalSection Section; + TVoxelTypedSparseArray Tools; + TArray> ToolsMaterials; + + inline void RecomputeToolsMaterials() + { + check(IsInGameThread()); + ToolsMaterials.Reset(); + for (auto& Tool : Tools) + { + if (Tool.Material.IsValid()) + { + ToolsMaterials.Add(Tool.Material); + } + } + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSaveStruct.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSaveStruct.h new file mode 100644 index 00000000..92d63b76 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSaveStruct.h @@ -0,0 +1,67 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +// Base class for blueprint structs that hold a pointer to a big implementation struct that's expensive to copy around +template +struct TVoxelSaveStruct +{ +public: + bool operator==(const TVoxelSaveStruct& Other) const + { + return Impl == Other.Impl || *Impl == *Other.Impl; + } + bool Serialize(FArchive& Ar) + { + if (Ar.IsLoading()) + { + // If we're going to write, create a new impl + Impl = MakeShared(); + } + return Impl->Serialize(Ar); + } + const T& Const() const + { + return *Impl; + } + // Copies the data over from old impl + T& Mutable() + { + if (Impl.IsUnique()) + { + return *Impl; + } + else + { + Impl = MakeShared(*Impl); + return *Impl; + } + } + // Does not copy over the old impl data + T& NewMutable() + { + Impl = MakeShared(); + return *Impl; + } + +private: + TSharedRef Impl = MakeShared(); +}; + +#define DEFINE_VOXEL_SAVE_STRUCT(Name) \ + inline FArchive& operator<<(FArchive& Ar, Name& Save) \ + { \ + Save.Serialize(Ar); \ + return Ar; \ + } \ + template<> \ + struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 \ + { \ + enum \ + { \ + WithSerializer = true, \ + WithIdenticalViaEquality = true \ + }; \ + }; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSettings.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSettings.h new file mode 100644 index 00000000..635836e4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSettings.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DeveloperSettings.h" +#include "VoxelSettings.generated.h" + +/** + * Usage example: In DefaultEngine.ini + * [/Script/Voxel.VoxelSettings] + * bDisableAutoPreview=True + */ + +UCLASS(config=Engine, defaultconfig, meta=(DisplayName="Voxel Plugin")) +class VOXEL_API UVoxelSettings : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + UVoxelSettings(); + + UPROPERTY(Config, EditAnywhere, Category="Config") + bool bShowNotifications = true; + + UPROPERTY(Config, EditAnywhere, Category="Config") + bool bDisableAutoPreview = false; + + // Round voxels that do not affect surface nor normals to improve compression + // Takes a while when saving + UPROPERTY(Config, EditAnywhere, Category="Config") + bool bRoundBeforeSaving = false; + + // -1 = ZLib default compression + // 0 = No compression + // 1 = Best speed + // 9 = Best compression + // Used when compressing voxel save, heightmaps, data assets... + // Compression speed is written to the log + // In my tests a compression level of 1 was very fast without compromising too much compression + UPROPERTY(Config, EditAnywhere, Category="Compression", meta = (ClampMin = -1, ClampMax = 9, UIMin = -1, UIMax = 9)) + int32 DefaultCompressionLevel = 1; + + virtual FName GetContainerName() const override; + virtual void PostInitProperties() override; +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h new file mode 100644 index 00000000..4ddfb52a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h @@ -0,0 +1,107 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "ShaderParameterMacros.h" +#include "RenderCommandFence.h" +#include "GlobalShader.h" + +BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FVoxelDistanceFieldParameters,) + + SHADER_PARAMETER(uint32, SizeX) + SHADER_PARAMETER(uint32, SizeY) + SHADER_PARAMETER(uint32, SizeZ) + SHADER_PARAMETER(uint32, Step) + +END_GLOBAL_SHADER_PARAMETER_STRUCT() + +#define VOXEL_DISTANCE_FIELD_NUM_THREADS_CS 8 + +typedef TUniformBufferRef FVoxelDistanceFieldParametersRef; + +class FVoxelDistanceFieldBaseCS : public FGlobalShader +{ + DECLARE_TYPE_LAYOUT(FVoxelDistanceFieldBaseCS, NonVirtual); +public: + FVoxelDistanceFieldBaseCS() = default; + FVoxelDistanceFieldBaseCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer); + + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); + } + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment); + +#if ENGINE_MINOR_VERSION < 25 + virtual bool Serialize(FArchive& Ar) override; +#endif + + void SetBuffers( + FRHICommandList& RHICmdList, + const FRWBuffer& SrcBuffer, + const FRWBuffer& DstBuffer) const; + + void SetUniformBuffers( + FRHICommandList& RHICmdList, + const FVoxelDistanceFieldParameters& Parameters) const; + +private: + LAYOUT_FIELD(FRWShaderParameter, Src); + LAYOUT_FIELD(FRWShaderParameter, Dst); +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelJumpFloodCS : public FVoxelDistanceFieldBaseCS +{ +public: + DECLARE_SHADER_TYPE(FVoxelJumpFloodCS, Global); + + using FVoxelDistanceFieldBaseCS::FVoxelDistanceFieldBaseCS; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelDistanceFieldShaderHelper : public TVoxelSharedFromThis +{ +public: + FVoxelDistanceFieldShaderHelper() = default; + + void WaitForCompletion() const; + + void StartCompute( + const FIntVector& Size, + const TVoxelSharedRef>& InOutData, + int32 MaxPasses_Debug = -1); + + void Compute_RenderThread( + FRHICommandListImmediate& RHICmdList, + const FIntVector& Size, + FVector* RESTRICT Data, + int32 Num, + int32 MaxPasses_Debug = -1); + +private: + FIntVector AllocatedSize = FIntVector::ZeroValue; + + FRWBuffer SrcBuffer; + FRWBuffer DstBuffer; + + FRenderCommandFence Fence; + + template + void ApplyComputeShader( + FRHICommandListImmediate& RHICmdList, + const FIntVector& Size, + int32 Step); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelErosion.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelErosion.h new file mode 100644 index 00000000..eae4361c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelShaders/VoxelErosion.h @@ -0,0 +1,121 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "RHI.h" +#include "RHIResources.h" +#include "VoxelTexture.h" +#include "VoxelErosion.generated.h" + +class FVoxelErosionParameters; +class UTexture2D; + +UCLASS(Blueprintable, BlueprintType) +class VOXEL_API UVoxelErosion : public UObject +{ + GENERATED_BODY() + +public: + // Must be a multiple of 32 + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + int32 Size = 1024; + + // Time elapsed between each simulation step. Smaller = more stable, but slower + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float DeltaTime = 0.005; + + // The scale of the simulation. Should leave to default + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float Scale = 1; + + // Gravity, use to compute the speed of the water + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float Gravity = 10; + + // How much sediment a volume of water can carry + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float SedimentCapacity = 0.05; + + // How much sediment is removed from height by the water in each step + // This controls the "strength" of the erosion + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float SedimentDissolving = 0.001; + + // How much sediment can go from the water to the height + // This controls how far the sediments are carried + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float SedimentDeposition = 0.0001; + + // Amount of water added per step + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float RainStrength = 2; + + // Controls the evaporation of the water + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Erosion Parameters") + float Evaporation = 1; + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init Parameters") + FVoxelFloatTexture RainMapInit; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init Parameters") + FVoxelFloatTexture HeightmapInit; + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + void Initialize(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + bool IsInitialized() const; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + void Step(int32 Count = 10); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + FVoxelFloatTexture GetTerrainHeightTexture(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + FVoxelFloatTexture GetWaterHeightTexture(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Erosion") + FVoxelFloatTexture GetSedimentTexture(); + +private: + int32 RealSize = 0; // Can't be changed through BP after init + bool bIsInit = false; + + FUnorderedAccessViewRHIRef RainMapUAV; + FUnorderedAccessViewRHIRef TerrainHeightUAV; + FUnorderedAccessViewRHIRef TerrainHeight1UAV; + FUnorderedAccessViewRHIRef WaterHeightUAV; + FUnorderedAccessViewRHIRef WaterHeight1UAV; + FUnorderedAccessViewRHIRef WaterHeight2UAV; + FUnorderedAccessViewRHIRef SedimentUAV; + FUnorderedAccessViewRHIRef Sediment1UAV; + FUnorderedAccessViewRHIRef OutflowUAV; + FUnorderedAccessViewRHIRef VelocityUAV; + + FTexture2DRHIRef RainMap; + FTexture2DRHIRef TerrainHeight; + FTexture2DRHIRef TerrainHeight1; + FTexture2DRHIRef WaterHeight; + FTexture2DRHIRef WaterHeight1; + FTexture2DRHIRef WaterHeight2; + FTexture2DRHIRef Sediment; + FTexture2DRHIRef Sediment1; + FTexture2DRHIRef Outflow; + FTexture2DRHIRef Velocity; + + template + void RunShader(const FVoxelErosionParameters& Parameters); + + void CopyTextureToRHI(const TVoxelTexture& Texture, const FTexture2DRHIRef& RHITexture); + void CopyRHIToTexture(const FTexture2DRHIRef& RHITexture, TVoxelSharedRef::FTextureData>& Texture); + + static void CopyTextureToRHI_RenderThread(const TVoxelTexture& Texture, const FTexture2DRHIRef& RHITexture); + static void CopyRHIToTexture_RenderThread(const FTexture2DRHIRef& RHITexture, TVoxelTexture::FTextureData& Texture); + + void Init_RenderThread(); + void Step_RenderThread(const FVoxelErosionParameters& Parameters, int32 Count); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedMutex.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedMutex.h new file mode 100644 index 00000000..265ebfd5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedMutex.h @@ -0,0 +1,121 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Misc/ScopeLock.h" +#include +#include + +enum class EVoxelLockType +{ + Read, + Write +}; + +class FVoxelSharedMutex +{ +public: + void Lock(EVoxelLockType LockType) + { +#if DO_THREADSAFE_CHECKS + AddThreadId(); +#endif + if (LockType == EVoxelLockType::Read) + { + std::unique_lock Lock(Mutex); + while (bWriting) + { + WriteQueue.wait(Lock); + } + NumReaders++; + } + else + { + std::unique_lock Lock(Mutex); + while (bWriting) + { + WriteQueue.wait(Lock); + } + + bWriting = true; + + while (0 < NumReaders) + { + ReadQueue.wait(Lock); + } + } + } + void Unlock(EVoxelLockType LockType) + { +#if DO_THREADSAFE_CHECKS + RemoveThreadId(); +#endif + if (LockType == EVoxelLockType::Read) + { + uint32 NumReaders_Local; + bool bWriting_Local; + { + std::lock_guard Lock(Mutex); + NumReaders--; + checkf(NumReaders >= 0, TEXT("Unlock Read called, but not locked for read!")); + NumReaders_Local = NumReaders; + bWriting_Local = bWriting; + } + + if (bWriting_Local && NumReaders_Local == 0) + { + ReadQueue.notify_one(); + } + } + else + { + { + std::lock_guard Lock(Mutex); + checkf(bWriting, TEXT("Unlock Write called, but not locked for write!")); + bWriting = false; + } + + WriteQueue.notify_all(); + } + } + + FORCEINLINE bool IsLockedForRead() const + { + std::lock_guard Lock(Mutex); + return bWriting || NumReaders > 0; + } + FORCEINLINE bool IsLockedForWrite() const + { + std::lock_guard Lock(Mutex); + return bWriting; + } + +private: + mutable std::mutex Mutex; + std::condition_variable ReadQueue; + std::condition_variable WriteQueue; + int32 NumReaders = 0; + bool bWriting = false; + +#if DO_THREADSAFE_CHECKS + FCriticalSection ThreadIdsSection; + TArray> ThreadIds; + + void AddThreadId() + { + const uint32 ThreadId = FPlatformTLS::GetCurrentThreadId(); + FScopeLock ScopeLock(&ThreadIdsSection); + checkf(!ThreadIds.Contains(ThreadId), TEXT("Mutex already locked by this thread!")); + ThreadIds.Add(ThreadId); + } + void RemoveThreadId() + { + const uint32 ThreadId = FPlatformTLS::GetCurrentThreadId(); + FScopeLock ScopeLock(&ThreadIdsSection); + checkf(ThreadIds.Contains(ThreadId), TEXT("Mutex not locked by this thread!")); + verify(ThreadIds.RemoveSwap(ThreadId) == 1); + } +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedPtr.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedPtr.h new file mode 100644 index 00000000..d2c9825e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSharedPtr.h @@ -0,0 +1,77 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +template +using TVoxelSharedRef = TSharedRef; +template +using TVoxelSharedPtr = TSharedPtr; +template +using TVoxelWeakPtr = TWeakPtr; +template +using TVoxelSharedFromThis = TSharedFromThis; + +template +inline TVoxelSharedRef StaticCastVoxelSharedRef(const TVoxelSharedRef& InSharedRef) +{ + return StaticCastSharedRef(InSharedRef); +} + +template +inline TVoxelSharedPtr StaticCastVoxelSharedPtr(const TVoxelSharedPtr& InSharedPtr) +{ + return StaticCastSharedPtr(InSharedPtr); +} + +template +inline TVoxelSharedRef MakeVoxelShared(InArgTypes&&... Args) +{ + return MakeShared(Forward(Args)...); +} + +template class TPtr> +inline TVoxelWeakPtr MakeVoxelWeakPtr(const TPtr& Ptr) +{ + return TVoxelWeakPtr(Ptr); +} +template +inline TVoxelWeakPtr MakeVoxelWeakPtr(T* Ptr) +{ + return TVoxelWeakPtr(StaticCastVoxelSharedRef(Ptr->AsShared())); +} + +template class TPtr> +inline TWeakPtr MakeWeakPtr(const TPtr& Ptr) +{ + return TWeakPtr(Ptr); +} +template +inline TWeakPtr MakeWeakPtr(T* Ptr) +{ + return TWeakPtr(StaticCastSharedRef(Ptr->AsShared())); +} + +// Need TEnableIf as &&& is equivalent to &, so T could get matched with Smthg& +template +inline typename TEnableIf::Value, TSharedRef>::Type MakeSharedCopy(T&& Data) +{ + return MakeShared(MoveTemp(Data)); +} +template +inline typename TEnableIf::Value, TVoxelSharedRef>::Type MakeVoxelSharedCopy(T&& Data) +{ + return MakeVoxelShared(MoveTemp(Data)); +} + +template +inline TSharedRef MakeSharedCopy(const T& Data) +{ + return MakeShared(Data); +} +template +inline TVoxelSharedRef MakeVoxelSharedCopy(const T& Data) +{ + return MakeVoxelShared(Data); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSimpleOctree.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSimpleOctree.h new file mode 100644 index 00000000..9b957a80 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSimpleOctree.h @@ -0,0 +1,152 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelOctree.h" + +template +class TSimpleVoxelOctree +{ +public: + using ChildrenArray = ElementType[8]; + + // Center of the octree + const FIntVector Position; + + // Height of the octree (distance to smallest possible leaf) + const uint8 Height; + + TSimpleVoxelOctree(uint8 Height) + : Position(FIntVector::ZeroValue) + , Height(Height) + { + check(Height < 32); + } + ~TSimpleVoxelOctree() + { + if (HasChildren()) + { + DestroyChildren(); + } + } + +public: + inline uint32 Size() const + { + return ChunkSize << Height; + } + inline FVoxelIntBox GetBounds() const + { + return FVoxelIntBox(Position - Size() / 2, Position + Size() / 2); + } + inline bool HasChildren() const + { + return Children != nullptr; + } + inline FVoxelOctreeId GetId() const + { + return { Position, Height }; + } + +public: + inline const ElementType& GetChild(int32 X, int32 Y, int32 Z) const + { + return GetChild(GetChildIndex(X, Y, Z)); + } + inline ElementType& GetChild(int32 X, int32 Y, int32 Z) + { + return GetChild(GetChildIndex(X, Y, Z)); + } + + inline const ElementType& GetChild(const FIntVector& P) const + { + return GetChild(P.X, P.Y, P.Z); + } + inline ElementType& GetChild(const FIntVector& P) + { + return GetChild(P.X, P.Y, P.Z); + } + + inline const ElementType& GetChild(int32 Index) const + { + checkVoxelSlow((Children != nullptr) & (0 <= Index) & (Index < 8)); + return Children[Index]; + } + inline ElementType& GetChild(int32 Index) + { + checkVoxelSlow((Children != nullptr) & (0 <= Index) & (Index < 8)); + return Children[Index]; + } + + inline const ChildrenArray& GetChildren() const + { + checkVoxelSlow(Children); + return reinterpret_cast(*Children); + } + inline ChildrenArray& GetChildren() + { + checkVoxelSlow(Children); + return reinterpret_cast(*Children); + } + +protected: + TSimpleVoxelOctree(const ElementType& Parent, uint8 ChildIndex) + : Position(GetChildPosition(Parent.Position, Parent.Size(), ChildIndex)) + , Height(Parent.Height - 1) + { + checkVoxelSlow(0 <= ChildIndex && ChildIndex < 8); + } + + template + inline void CreateChildren(TArgs&&... Args) + { + check(!HasChildren() && Height > 0); + + Children = static_cast(Allocator::Malloc(8 * sizeof(ElementType))); + + for (int32 Index = 0; Index < 8 ; Index++) + { + new (&Children[Index]) ElementType(This(), Index, Forward(Args)...); + } + } + + inline void DestroyChildren() + { + check(HasChildren()); + + for (auto& Child : GetChildren()) + { + Child.~ElementType(); + } + + Allocator::Free(Children); + Children = nullptr; + } + + +private: + ElementType* Children = nullptr; + + inline int32 GetChildIndex(int32 X, int32 Y, int32 Z) const + { + return (X >= Position.X) + 2 * (Y >= Position.Y) + 4 * (Z >= Position.Z); + } + inline static FIntVector GetChildPosition(const FIntVector& ParentPosition, uint32 ParentSize, uint8 ChildIndex) + { + return ParentPosition + + FIntVector( + ParentSize / 4 * ((ChildIndex & 0x1) ? 1 : -1), + ParentSize / 4 * ((ChildIndex & 0x2) ? 1 : -1), + ParentSize / 4 * ((ChildIndex & 0x4) ? 1 : -1)); + } + + inline ElementType& This() + { + return static_cast(*this); + } + inline const ElementType& This() const + { + return static_cast(*this); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/IVoxelSpawnerManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/IVoxelSpawnerManager.h new file mode 100644 index 00000000..f8b4cc71 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/IVoxelSpawnerManager.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelIntBox; +struct FVoxelSpawnersSaveImpl; +struct FVoxelSpawnerTransforms; + +class IVoxelSpawnerManager +{ +public: + virtual ~IVoxelSpawnerManager() = default; + + virtual void Destroy() = 0; + + virtual void Regenerate(const FVoxelIntBox& Bounds) = 0; + virtual void MarkDirty(const FVoxelIntBox& Bounds) = 0; + + virtual void SaveTo(FVoxelSpawnersSaveImpl& Save) = 0; + virtual void LoadFrom(const FVoxelSpawnersSaveImpl& Save) = 0; + + virtual bool GetMeshSpawnerTransforms(const FGuid& SpawnerGuid, TArray& OutTransforms) const = 0; + + virtual int32 GetTaskCount() const = 0; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h new file mode 100644 index 00000000..2c0d95dc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h @@ -0,0 +1,101 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelSpawners/VoxelBasicSpawner.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelAssetSpawner.generated.h" + +class UVoxelAssetSpawner; +class UVoxelTransformableGenerator; +class FVoxelTransformableGeneratorInstance; +class FVoxelAssetSpawnerProxy; + +template +class TVoxelDataItemWrapper; + +struct FVoxelAssetItem; + +class VOXEL_API FVoxelAssetSpawnerProxyResult : public FVoxelSpawnerProxyResult +{ +public: + explicit FVoxelAssetSpawnerProxyResult(const FVoxelAssetSpawnerProxy& Proxy); + + void Init(TArray&& InMatrices, TArray&& InGeneratorsIndices); + + //~ Begin FVoxelSpawnerProxyResult override + virtual void CreateImpl() override; + virtual void DestroyImpl() override; + + virtual void SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) override; + + virtual uint32 GetAllocatedSize() override; + //~ End FVoxelSpawnerProxyResult override + +private: + FVoxelIntBox Bounds; + TArray Matrices; + TArray GeneratorsIndices; + + TArray>> Items; +}; + +class VOXEL_API FVoxelAssetSpawnerProxy : public FVoxelBasicSpawnerProxy +{ +public: + const TArray> Generators; + const FVoxelIntBox GeneratorLocalBounds; + const int32 Priority; + const bool bRoundAssetPosition; + + FVoxelAssetSpawnerProxy(UVoxelAssetSpawner* Spawner, FVoxelSpawnerManager& Manager); + virtual ~FVoxelAssetSpawnerProxy(); + + //~ Begin FVoxelSpawnerProxy Interface + virtual TUniquePtr ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const override; + virtual void PostSpawn() override {} + //~ End FVoxelSpawnerProxy Interface +}; + +UCLASS() +class VOXEL_API UVoxelAssetSpawner : public UVoxelBasicSpawner +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + FVoxelTransformableGeneratorPicker Generator; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + FVoxelIntBox GeneratorLocalBounds = FVoxelIntBox(-25, 25); + + // The voxel world seeds will be sent to the generator. + // Add the names of the seeds you want to be randomized here + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + TArray Seeds; + + // All generators are created at begin play + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings", meta = (ClampMin = 1)) + int32 NumberOfDifferentSeedsToUse = 1; + + // Priority of the spawned assets + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + int32 Priority = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + bool bRoundAssetPosition = false; + +public: + //~ Begin UVoxelSpawner Interface +#if WITH_EDITOR + virtual bool NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) override { return Object == Generator.GetObject(); } +#endif + virtual TVoxelSharedRef GetSpawnerProxy(FVoxelSpawnerManager& Manager) override; + virtual FString GetDebugInfo() const override; + //~ End UVoxelSpawner Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h new file mode 100644 index 00000000..26b9f18f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h @@ -0,0 +1,154 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelBasicSpawner.generated.h" + +UENUM() +enum class EVoxelBasicSpawnerScaling : uint8 +{ + /** Instances will have uniform X, Y and Z scales */ + Uniform, + /** Instances will have random X, Y and Z scales */ + Free, + /** X and Y will be the same random scale, Z will be another */ + LockXY +}; + +USTRUCT() +struct FVoxelBasicSpawnerScaleSettings +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Config") + EVoxelBasicSpawnerScaling Scaling = EVoxelBasicSpawnerScaling::Uniform; + + /** Specifies the range of scale, from minimum to maximum, to apply to an actor instance's X Scale property */ + UPROPERTY(EditAnywhere, Category = "Config") + FFloatInterval ScaleX = FFloatInterval(1.0f, 1.0f); + + /** Specifies the range of scale, from minimum to maximum, to apply to an actor instance's Y Scale property */ + UPROPERTY(EditAnywhere, Category = "Config") + FFloatInterval ScaleY = FFloatInterval(1.0f, 1.0f); + + /** Specifies the range of scale, from minimum to maximum, to apply to an actor instance's Z Scale property */ + UPROPERTY(EditAnywhere, Category = "Config") + FFloatInterval ScaleZ = FFloatInterval(1.0f, 1.0f); + + inline FVector GetScale(const FRandomStream& Stream) const + { + FVector Scale; + switch (Scaling) + { + case EVoxelBasicSpawnerScaling::Uniform: + Scale.X = ScaleX.Interpolate(Stream.GetFraction()); + Scale.Y = Scale.X; + Scale.Z = Scale.X; + break; + case EVoxelBasicSpawnerScaling::Free: + Scale.X = ScaleX.Interpolate(Stream.GetFraction()); + Scale.Y = ScaleY.Interpolate(Stream.GetFraction()); + Scale.Z = ScaleZ.Interpolate(Stream.GetFraction()); + break; + case EVoxelBasicSpawnerScaling::LockXY: + Scale.X = ScaleX.Interpolate(Stream.GetFraction()); + Scale.Y = Scale.X; + Scale.Z = ScaleZ.Interpolate(Stream.GetFraction()); + break; + default: + check(false); + } + return Scale; + } +}; + +UENUM() +enum class EVoxelBasicSpawnerRotation : uint8 +{ + AlignToSurface, + AlignToWorldUp, + RandomAlign +}; + +UCLASS(Abstract) +class VOXEL_API UVoxelBasicSpawner : public UVoxelSpawner +{ + GENERATED_BODY() + +public: + // Min/max angle between object up vector and generator up vector in degrees + UPROPERTY(EditAnywhere, Category = "Placement", meta = (UIMin = 0, ClampMin = 0, UIMax = 180, ClampMax = 180)) + FFloatInterval GroundSlopeAngle = { 0, 90 }; + + UPROPERTY(EditAnywhere, Category = "Placement", meta = (InlineEditConditionToggle)) + bool bEnableHeightRestriction = false; + + // In voxels. Only spawn instances if the instance voxel Z position is in this interval. + // TODO: optimize to not generate chunks that do not match this restriction + UPROPERTY(EditAnywhere, Category = "Placement", meta = (EditCondition = "bEnableHeightRestriction")) + FFloatInterval HeightRestriction = { -100.f, 100.f }; + + // In voxels, the size of the fade on the edges of HeightRestriction + UPROPERTY(EditAnywhere, Category = "Placement", meta = (EditCondition = "bEnableHeightRestriction", UIMin = 0, ClampMin = 0)) + float HeightRestrictionFalloff = 0.f; + + // Specifies instance scaling type + UPROPERTY(EditAnywhere, Category = "Placement - Scale") + FVoxelBasicSpawnerScaleSettings Scaling; + + // Vertical to use for the instances + UPROPERTY(EditAnywhere, Category = "Placement - Rotation") + EVoxelBasicSpawnerRotation RotationAlignment = EVoxelBasicSpawnerRotation::AlignToWorldUp; + + // If selected, foliage instances will have a random yaw rotation around their vertical axis applied + UPROPERTY(EditAnywhere, Category = "Placement - Rotation") + bool bRandomYaw = true; + + // A random pitch adjustment can be applied to each instance, up to the specified angle in degrees, from the original vertical + UPROPERTY(EditAnywhere, Category = "Placement - Rotation", meta = (UIMin = 0, ClampMin = 0, UIMax = 180, ClampMax = 180)) + float RandomPitchAngle = 6; + + // Apply an offset to the instance position. Applied before the rotation. In cm + UPROPERTY(EditAnywhere, Category = "Placement - Offset") + FVector LocalPositionOffset = FVector::ZeroVector; + + // Apply an offset to the instance rotation. Applied after the local position offset, and before the rotation + UPROPERTY(EditAnywhere, Category = "Placement - Offset") + FRotator LocalRotationOffset = FRotator::ZeroRotator; + + // Apply an offset to the instance position. Applied after the rotation. In cm + UPROPERTY(EditAnywhere, Category = "Placement - Offset") + FVector GlobalPositionOffset = FVector::ZeroVector; +}; + +class VOXEL_API FVoxelBasicSpawnerProxy : public FVoxelSpawnerProxy +{ +public: + const FFloatInterval GroundSlopeAngle; + const bool bEnableHeightRestriction; + const FFloatInterval HeightRestriction; + const float HeightRestrictionFalloff; + const FVoxelBasicSpawnerScaleSettings Scaling; + const EVoxelBasicSpawnerRotation RotationAlignment; + const bool bRandomYaw; + const float RandomPitchAngle; + const FVector LocalPositionOffset; + const FRotator LocalRotationOffset; + const FVector GlobalPositionOffset; + + FVoxelBasicSpawnerProxy(UVoxelBasicSpawner* Spawner, FVoxelSpawnerManager& Manager, EVoxelSpawnerProxyType Type, uint32 Seed); + + bool CanSpawn( + const FRandomStream& RandomStream, + const FVoxelVector& Position, + const FVector& Normal, + const FVector& WorldUp) const; + + FMatrix GetTransform( + const FRandomStream& Stream, + const FVector& Normal, + const FVector& WorldUp, + const FVector& Position) const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelEmptySpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelEmptySpawner.h new file mode 100644 index 00000000..709e33ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelEmptySpawner.h @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawner.h" + +class FVoxelEmptySpawnerProxyResult : public FVoxelSpawnerProxyResult +{ +public: + explicit FVoxelEmptySpawnerProxyResult(const FVoxelSpawnerProxy& Proxy) + : FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType::EmptySpawner, Proxy) + { + } + + //~ Begin FVoxelSpawnerProxyResult Interface + virtual void CreateImpl() override {} + virtual void DestroyImpl() override {} + + virtual void SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) override {} + + virtual uint32 GetAllocatedSize() override { return sizeof(*this); } + //~ End FVoxelSpawnerProxyResult Interface +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h new file mode 100644 index 00000000..912ca199 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h @@ -0,0 +1,218 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelSpawners/VoxelSpawnerMatrix.h" +#include "VoxelSpawners/VoxelInstancedMeshSettings.h" +#include "Components/HierarchicalInstancedStaticMeshComponent.h" +#include "VoxelHierarchicalInstancedStaticMeshComponent.generated.h" + +struct FVoxelSpawnerTransform; +struct FVoxelSpawnerTransforms; +struct FVoxelHISMBuiltData; +struct FVoxelInstancedMeshAndActorSettings; +class IVoxelPool; +class FVoxelConstDataAccelerator; +class FVoxelInstancedMeshManager; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel HISM Memory"), STAT_VoxelHISMMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct FVoxelInstancesSection +{ + int32 StartIndex = -1; + int32 Num = -1; + + // Between 0 and Num - 1 + TArray RemovedIndices; +}; + +// Need to prefix names with Voxel to avoid collisions with normal HISM +UCLASS() +class VOXEL_API UVoxelHierarchicalInstancedStaticMeshComponent : public UHierarchicalInstancedStaticMeshComponent +{ + GENERATED_BODY() + +public: + // How long to wait for new instances before triggering a new cull tree/render update + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Voxel_BuildDelay = 0.5f; + +private: + UPROPERTY() + UMaterialInterface* Voxel_DebugMaterial; + +public: + UVoxelHierarchicalInstancedStaticMeshComponent(const FObjectInitializer& ObjectInitializer); + + ~UVoxelHierarchicalInstancedStaticMeshComponent(); + +public: + void Voxel_Init( + TVoxelWeakPtr Pool, + TVoxelWeakPtr InstancedMeshManager, + float VoxelSize, + const FIntVector& VoxelPosition, + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings); + + void Voxel_SetRelativeLocation(); + + FIntVector Voxel_GetVoxelPosition() const + { + return Voxel_VoxelPosition; + } + const FVoxelInstancedMeshAndActorSettings& Voxel_GetMeshAndActorSettings() const + { + return Voxel_MeshAndActorSettings; + } + +private: + TVoxelWeakPtr Voxel_Pool; + TVoxelWeakPtr Voxel_InstancedMeshManager; + float Voxel_VoxelSize = 0; + FIntVector Voxel_VoxelPosition; + FVoxelInstancedMeshAndActorSettings Voxel_MeshAndActorSettings; + + friend class FVoxelHISMBuildTask; + +public: + TVoxelWeakPtr Voxel_AppendTransforms(const TArray& InTransforms, const FVoxelIntBox& Bounds); + void Voxel_RemoveSection(const TVoxelWeakPtr& Section); + + bool Voxel_IsEmpty() const; + + void Voxel_StartBuildTree(); + void Voxel_FinishBuilding(FVoxelHISMBuiltData& BuiltData); + + void Voxel_EnablePhysics(FVoxelIntBox Chunk); + void Voxel_DisablePhysics(FVoxelIntBox Chunk); + void Voxel_RefreshPhysics(const FVoxelIntBox& BoundsToUpdate); + + FVoxelSpawnerTransforms Voxel_RemoveInstancesInArea( + const FVoxelIntBox& VoxelBounds, + const FVoxelConstDataAccelerator* Accelerator, + EVoxelSpawnerActorSpawnType SpawnType); + bool Voxel_RemoveInstanceByIndex(int32 InstanceIndex, FVoxelSpawnerTransform& OutTransform); + + int32 Voxel_GetNumInstances() const + { + return Voxel_UnbuiltMatrices.Num(); + } + +public: + template + inline void Voxel_IterateInstancesInBounds(const TArray& ClusterTree, const T1& Bounds, T2 Lambda) const + { + VOXEL_FUNCTION_COUNTER(); + Voxel_IterateInstancesInBoundsImpl(ClusterTree, Bounds, Lambda, 0); + } + template + void Voxel_IterateInstancesInBoundsImpl(const TArray& ClusterTree, const T1& Bounds, T2 Lambda, const int32 Index) const + { + struct FHelper + { + inline static bool ContainsBounds(const FVoxelIntBox& A, const FBox& B) { return A.ContainsTemplate(B); } + inline static bool ContainsBounds(const FBox& A, const FBox& B) { return A.IsInside(B); } + }; + + if (ClusterTree.Num() == 0) + { + return; + } + auto& Tree = ClusterTree[Index]; + const auto TreeBounds = FBox(Tree.BoundMin, Tree.BoundMax); + if (Bounds.Intersect(TreeBounds)) + { + if (FHelper::ContainsBounds(Bounds, TreeBounds) || Tree.FirstChild < 0) + { + for (int32 Instance = Tree.FirstInstance; Instance <= Tree.LastInstance; Instance++) + { + Lambda(Instance); + } + } + else + { + for (int32 Child = Tree.FirstChild; Child <= Tree.LastChild; Child++) + { + Voxel_IterateInstancesInBoundsImpl(ClusterTree, Bounds, Lambda, Child); + } + } + } + } + +public: + //~ Begin UActorComponent Interface + virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override; + virtual bool ShouldCreatePhysicsState() const override; + virtual void OnDestroyPhysicsState() override; + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + //~ End UActorComponent Interface + +private: + // Stored relative to Voxel_VoxelPosition + TArray Voxel_UnbuiltMatrices; + TArray Voxel_BuiltMatrices; + + TArray> Voxel_Sections; + + struct FVoxelMappings + { + TArray InstancesToBuiltInstances; + TArray BuiltInstancesToInstances; + + // We need to store a stack of the deletions that happened since the last tree build + struct FDeletion + { + uint64 TaskUniqueId = 0; // Id of the task started right after that deletion + int32 StartIndex = -1; + int32 Num = -1; + }; + TArray DeletionsStack; + + int32 GetBuiltIndex(int32 UnbuiltIndex) const; + int32 GetUnbuiltIndex(int32 BuiltIndex) const; + }; + FVoxelMappings Voxel_Mappings; + + // Unbuilt instances indices to clear when task finishes + TArray Voxel_UnbuiltInstancesToClear; + + uint64 Voxel_TaskUniqueId = 0; + TVoxelSharedPtr Voxel_TaskCancelCounterPtr; + + FTimerHandle Voxel_TimerHandle; + + // Instance bounds to rebuild physics on when the respective tasks will be done + TMap> Voxel_TaskIdToNewInstancesBounds; + // Instances bounds to rebuild physics on when the next task will be done + TArray Voxel_PendingNewInstancesBounds; + + TMap> Voxel_InstanceBodies; + + uint32 Voxel_AllocatedMemory = 0; + + void Voxel_UpdateAllocatedMemory(); + + void Voxel_ScheduleBuildTree(); + void Voxel_RemoveInstancesFromSections(const TArray& BuiltIndices); + void Voxel_SetInstancesScaleToZero(const TArray& BuiltIndices); + + // These 2 do not modify Voxel_InstanceBodies. Used by RefreshPhysics. + void Voxel_EnablePhysicsImpl(const FVoxelIntBox& Chunk, TArray& OutBodies) const; + void Voxel_DisablePhysicsImpl(TArray& Bodies) const; + + FORCEINLINE FVoxelIntBox Voxel_VoxelBoundsToLocal(const FVoxelIntBox& VoxelBounds) const + { + return VoxelBounds.Translate(-Voxel_VoxelPosition).Scale(Voxel_VoxelSize); + } + FORCEINLINE FVoxelVector Voxel_GetGlobalVoxelPosition(const FVoxelSpawnerMatrix& Matrix) const + { + const FVector LocalVoxelPosition = (FTransform(Matrix.GetCleanMatrix()).GetTranslation() + Matrix.GetPositionOffset()) / Voxel_VoxelSize; + return FVoxelVector(Voxel_VoxelPosition) + FVoxelVector(LocalVoxelPosition); + } + +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshManager.h new file mode 100644 index 00000000..02cbe69a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshManager.h @@ -0,0 +1,148 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "Templates/SubclassOf.h" +#include "VoxelSpawners/VoxelSpawnerMatrix.h" +#include "VoxelInstancedMeshSettings.h" +#include "VoxelTickable.h" + +struct FVoxelInstancesSection; +struct FVoxelHISMBuiltData; +class AActor; +class AVoxelWorld; +class AVoxelSpawnerActor; +class UStaticMesh; +class UVoxelHierarchicalInstancedStaticMeshComponent; +class IVoxelPool; +class FVoxelData; +class FVoxelEventManager; +struct FVoxelIntBox; + +struct VOXEL_API FVoxelInstancedMeshManagerSettings +{ + const TWeakObjectPtr ComponentsOwner; + const TVoxelSharedRef WorldOffset; + const TVoxelSharedRef Pool; + const TVoxelSharedRef EventManager; + const uint32 HISMChunkSize; + const uint32 CollisionChunkSize = 32; // Also change in AVoxelWorld::PostEditChangeProperty + const uint32 CollisionDistanceInChunks; + const float VoxelSize; + const int64 MaxNumberOfInstances; + const FSimpleDelegate OnMaxInstanceReached; + + FVoxelInstancedMeshManagerSettings( + const AVoxelWorld* World, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& EventManager); +}; + +struct FVoxelInstancedMeshInstancesRef +{ +public: + FVoxelInstancedMeshInstancesRef() = default; + + bool IsValid() const + { + return Section.IsValid(); + } + +private: + TWeakObjectPtr HISM; + TVoxelWeakPtr Section; + int32 NumInstances = 0; + + friend class FVoxelInstancedMeshManager; +}; + +class VOXEL_API FVoxelInstancedMeshManager : public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + const FVoxelInstancedMeshManagerSettings Settings; + + static TVoxelSharedRef Create(const FVoxelInstancedMeshManagerSettings& Settings); + void Destroy(); + ~FVoxelInstancedMeshManager(); + +public: + // TransformsOffset is used to reduce precision errors + FIntVector ComputeTransformsOffset(const FVoxelIntBox& Bounds) const + { + return FVoxelUtilities::DivideFloor(Bounds.Min, Settings.HISMChunkSize) * Settings.HISMChunkSize; + } + + FVoxelInstancedMeshInstancesRef AddInstances( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransforms& Transforms, + const FVoxelIntBox& Bounds); + void RemoveInstances(FVoxelInstancedMeshInstancesRef Ref); + + static const TArray& GetRemovedIndices(FVoxelInstancedMeshInstancesRef Ref); + + AVoxelSpawnerActor* SpawnActor( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransform& Transform) const; + void SpawnActors( + const FVoxelInstancedMeshAndActorSettings& MeshAndActorSettings, + const FVoxelSpawnerTransforms& Transforms, + TArray& OutActors) const; + + void SpawnActorsInArea( + const FVoxelIntBox& Bounds, + const FVoxelData& Data, + EVoxelSpawnerActorSpawnType SpawnType, + TArray& OutActors) const; + + TMap> RemoveInstancesInArea( + const FVoxelIntBox& Bounds, + const FVoxelData& Data, + EVoxelSpawnerActorSpawnType SpawnType) const; + + AVoxelSpawnerActor* SpawnActorByIndex(UVoxelHierarchicalInstancedStaticMeshComponent* Component, int32 InstanceIndex); + + void RecomputeMeshPositions(); + +protected: + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +public: + void HISMBuildTaskCallback(TWeakObjectPtr Component, const TVoxelSharedRef& BuiltData); + +private: + struct FQueuedBuildCallback + { + TWeakObjectPtr Component; + TVoxelSharedPtr Data; + }; + TQueue HISMBuiltDataQueue; + +private: + explicit FVoxelInstancedMeshManager(const FVoxelInstancedMeshManagerSettings& Settings); + + UVoxelHierarchicalInstancedStaticMeshComponent* CreateHISM(const FVoxelInstancedMeshAndActorSettings& MeshSettings, const FIntVector& Position) const; + +private: + struct FHISMChunk + { + FVoxelIntBoxWithValidity Bounds; + TWeakObjectPtr HISM; + }; + struct FHISMChunks + { + TMap Chunks; + }; + TMap MeshSettingsToChunks; + + TSet> HISMs; + + int64 NumInstances = 0; + bool bMaxNumInstancesReachedFired = false; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshSettings.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshSettings.h new file mode 100644 index 00000000..9ad07dcb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshSettings.h @@ -0,0 +1,145 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Engine/EngineTypes.h" +#include "UObject/WeakObjectPtr.h" +#include "Templates/SubclassOf.h" +#include "PhysicsEngine/BodyInstance.h" +#include "Components/PrimitiveComponent.h" +#include "VoxelInstancedMeshSettings.generated.h" + +class UVoxelHierarchicalInstancedStaticMeshComponent; +class UStaticMesh; +class AVoxelSpawnerActor; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelInt32Interval +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + int32 Min = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + int32 Max = 0; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelInstancedMeshSettings +{ + GENERATED_BODY() + + FVoxelInstancedMeshSettings(); + +public: + // Distance from camera at which each instance begins/completely to fade out + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Instance Settings") + FVoxelInt32Interval CullDistance = { 100000, 200000 }; + + /** Controls whether the foliage should cast a shadow or not. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Instance Settings") + bool bCastShadow = true; + + /** Controls whether the foliage should inject light into the Light Propagation Volume. This flag is only used if CastShadow is true. */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta=(EditCondition="bCastShadow")) + bool bAffectDynamicIndirectLighting = false; + + /** Controls whether the primitive should affect dynamic distance field lighting methods. This flag is only used if CastShadow is true. */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta=(EditCondition="bCastShadow")) + bool bAffectDistanceFieldLighting = false; + + /** Whether this foliage should cast dynamic shadows as if it were a two sided material. */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta=(EditCondition="bCastShadow")) + bool bCastShadowAsTwoSided = false; + + /** Whether the foliage receives decals. */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings") + bool bReceivesDecals = true; + + /** + * If enabled, foliage will render a pre-pass which allows it to occlude other primitives, and also allows + * it to correctly receive DBuffer decals. Enabling this setting may have a negative performance impact. + */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings") + bool bUseAsOccluder = false; + + /** Custom collision for foliage */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Instance Settings", meta = (ShowOnlyInnerProperties)) + FBodyInstance BodyInstance; + + /** Force navmesh */ + UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Instance Settings") + TEnumAsByte CustomNavigableGeometry = {}; + + /** + * Lighting channels that placed foliage will be assigned. Lights with matching channels will affect the foliage. + * These channels only apply to opaque materials, direct lighting, and dynamic lighting and shadowing. + */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings") + FLightingChannels LightingChannels{}; + + /** If true, the foliage will be rendered in the CustomDepth pass (usually used for outlines) */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta=(DisplayName = "Render CustomDepth Pass")) + bool bRenderCustomDepth = false; + + /** Optionally write this 0-255 value to the stencil buffer in CustomDepth pass (Requires project setting or r.CustomDepth == 3) */ + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta=(UIMin = "0", UIMax = "255", editcondition = "bRenderCustomDepth", DisplayName = "CustomDepth Stencil Value")) + int32 CustomDepthStencilValue = 0; + + // If more instances are added before BuildDelay seconds elapsed, the tree build is queued + // This is useful to avoid spending lots of time building the tree for nothing. + // However, it can lead to delays in foliage spawning. + // To disable this feature entirely, set it to 0 + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", meta = (ClampMin = 0, DisplayName = "Culling Tree Build Delay")) + float BuildDelay = 0.1; + + // If you want to edit the HISM properties create a BP inheriting from HierarchicalInstancedStaticMeshComponent and set it here + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Instance Settings", AdvancedDisplay, meta = (DisplayName = "HISM Template")) + TSubclassOf HISMTemplate; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelSpawnerActorSettings +{ + GENERATED_BODY() + + FVoxelSpawnerActorSettings(); + +public: + // Actor to spawn to replace the instanced mesh. After spawn, the SetStaticMesh event will be called on the actor with Mesh as argument + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Actor Settings") + TSubclassOf ActorClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Actor Settings", meta = (ShowOnlyInnerProperties)) + FBodyInstance BodyInstance; + + // Set the lifespan of this actor. When it expires the object will be destroyed. + // Set to 0 to disable + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Actor Settings", meta = (ClampMin = 0)) + float Lifespan = 5.f; +}; + +struct FVoxelInstancedMeshAndActorSettings +{ + FVoxelInstancedMeshAndActorSettings() = default; + FVoxelInstancedMeshAndActorSettings( + TWeakObjectPtr Mesh, + const TMap& SectionMaterials, + FVoxelInstancedMeshSettings MeshSettings, + FVoxelSpawnerActorSettings ActorSettings); + + TWeakObjectPtr Mesh; + // Index in the array = mesh section index + TArray> MaterialsOverrides; + FVoxelInstancedMeshSettings MeshSettings; + FVoxelSpawnerActorSettings ActorSettings; + + TMap GetSectionsMaterials() const; + void SetSectionsMaterials(const TMap& SectionMaterials); +}; + +VOXEL_API bool operator==(const FVoxelInstancedMeshAndActorSettings& A, const FVoxelInstancedMeshAndActorSettings& B); +VOXEL_API uint32 GetTypeHash(const FVoxelInstancedMeshAndActorSettings& Settings); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h new file mode 100644 index 00000000..2e0a384f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h @@ -0,0 +1,187 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawners/VoxelBasicSpawner.h" +#include "VoxelSpawners/VoxelInstancedMeshSettings.h" +#include "VoxelSpawners/VoxelSpawnerMatrix.h" +#include "Templates/SubclassOf.h" +#include "Engine/EngineTypes.h" +#include "VoxelMeshSpawner.generated.h" + +class FVoxelConstDataAccelerator; +class FVoxelMeshSpawnerProxy; +class FVoxelMeshSpawnerGroupProxy; +struct FVoxelInstancedMeshInstancesRef; +class UStaticMesh; +class UVoxelMeshSpawnerBase; +class UVoxelMeshSpawnerGroup; +class UVoxelHierarchicalInstancedStaticMeshComponent; +enum class EVoxelMeshSpawnerInstanceRandom : uint8; + +class VOXEL_API FVoxelMeshSpawnerProxyResult : public FVoxelSpawnerProxyResult +{ +public: + explicit FVoxelMeshSpawnerProxyResult(const FVoxelMeshSpawnerProxy& Proxy); + ~FVoxelMeshSpawnerProxyResult(); + + void Init(const FVoxelIntBox& InBounds, FVoxelSpawnerTransforms&& InTransforms); + + const FVoxelSpawnerTransforms& GetTransforms() const { return Transforms; } + + //~ Begin FVoxelSpawnerProxyResult override + virtual void CreateImpl() override; + virtual void DestroyImpl() override; + + virtual void SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) override; + + virtual uint32 GetAllocatedSize() override; + //~ End FVoxelSpawnerProxyResult override + +private: + FVoxelIntBox Bounds; + FVoxelSpawnerTransforms Transforms; + + TUniquePtr InstancesRef; + + void ApplyRemovedIndices(); +}; + +class VOXEL_API FVoxelMeshSpawnerProxy : public FVoxelBasicSpawnerProxy +{ +public: + const FVoxelInstancedMeshAndActorSettings InstanceSettings; + const EVoxelMeshSpawnerInstanceRandom InstanceRandom; + const FName ColorOutputName; + const bool bAlwaysSpawnActor; + const FVector FloatingDetectionOffset; + + FVoxelMeshSpawnerProxy( + UVoxelMeshSpawnerBase* Spawner, + TWeakObjectPtr Mesh, + const TMap& SectionsMaterials, + FVoxelSpawnerManager& Manager, + uint32 Seed); + + //~ Begin FVoxelSpawnerProxy Interface + virtual TUniquePtr ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const override; + virtual void PostSpawn() override {} + //~ End FVoxelSpawnerProxy Interface +}; + +class VOXEL_API FVoxelMeshSpawnerGroupProxy : public FVoxelSpawnerProxy +{ +public: + const TArray> Proxies; + + FVoxelMeshSpawnerGroupProxy(UVoxelMeshSpawnerGroup* Spawner, FVoxelSpawnerManager& Manager); + + //~ Begin FVoxelSpawnerProxy Interface + virtual TUniquePtr ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const override; + virtual void PostSpawn() override {} + //~ End FVoxelSpawnerProxy Interface +}; + +UENUM() +enum class EVoxelMeshSpawnerInstanceRandom : uint8 +{ + // Random number + // Use GetVoxelSpawnerActorInstanceRandom to get it + // Will have the same value in the spawned actor as in the instance + Random, + // Get the voxel material in the shader + // Use GetVoxelMaterialFromInstanceRandom + VoxelMaterial, + // Get a voxel graph output color in the shader + // Use GetColorFromInstanceRandom + ColorOutput +}; + +UCLASS(Abstract) +class VOXEL_API UVoxelMeshSpawnerBase : public UVoxelBasicSpawner +{ + GENERATED_BODY() + +public: + // What to send through InstanceRandom + // Check enum values tooltips + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + EVoxelMeshSpawnerInstanceRandom InstanceRandom = EVoxelMeshSpawnerInstanceRandom::Random; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + FName ColorOutputName; + + // Actor to spawn to replace the instanced mesh. After spawn, the SetStaticMesh event will be called on the actor with Mesh as argument + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Actor Settings", meta = (ShowOnlyInnerProperties)) + FVoxelSpawnerActorSettings ActorSettings; + + // Will always spawn an actor instead of an instanced mesh + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Actor Settings") + bool bAlwaysSpawnActor = false; + +public: + UPROPERTY(EditAnywhere, Category = "Instance Settings", meta = (ShowOnlyInnerProperties)) + FVoxelInstancedMeshSettings InstancedMeshSettings; + +public: + // In local space. Increase this if your foliage is enabling physics too soon. In cm + UPROPERTY(EditAnywhere, Category = "Placement - Offset") + FVector FloatingDetectionOffset = FVector(0, 0, -10); + +protected: + //~ Begin UObject Interface + virtual void Serialize(FArchive& Ar) override; + //~ End UObject Interface +}; + +UCLASS() +class VOXEL_API UVoxelMeshSpawner : public UVoxelMeshSpawnerBase +{ + GENERATED_BODY() + +public: + // Mesh to spawn. Can be left to null if AlwaysSpawnActor is true + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + UStaticMesh* Mesh = nullptr; + + // Per mesh section + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "General Settings") + TMap MaterialsOverrides; + +public: + //~ Begin UVoxelSpawner Interface +#if WITH_EDITOR + virtual bool NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual TVoxelSharedRef GetSpawnerProxy(FVoxelSpawnerManager& Manager) override; + virtual FString GetDebugInfo() const override; + //~ End UVoxelSpawner Interface +}; + +UCLASS() +class VOXEL_API UVoxelMeshSpawnerGroup : public UVoxelMeshSpawnerBase +{ + GENERATED_BODY() + +public: + // Meshes to spawn. Can be left to null if AlwaysSpawnActor is true + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General Settings") + TArray Meshes; + +public: +#if WITH_EDITOR + virtual bool NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + //~ Begin UVoxelSpawner Interface + virtual TVoxelSharedRef GetSpawnerProxy(FVoxelSpawnerManager& Manager) override; + virtual float GetDistanceBetweenInstancesInVoxel() const override; + virtual FString GetDebugInfo() const override; + //~ End UVoxelSpawner Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h new file mode 100644 index 00000000..5e108a76 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h @@ -0,0 +1,260 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelSaveStruct.h" +#include "VoxelSpawner.generated.h" + +class FVoxelConstDataAccelerator; +class FVoxelSpawnerManager; +class FVoxelSpawnerProxy; +class FVoxelData; +class AVoxelSpawnerActor; +class UVoxelSpawner; + +namespace FVoxelSpawnersSaveVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + SHARED_RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + SHARED_ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +} + +struct VOXEL_API FVoxelSpawnersSaveImpl +{ + FVoxelSpawnersSaveImpl() = default; + + bool Serialize(FArchive& Ar); + + bool operator==(const FVoxelSpawnersSaveImpl& Other) const + { + return Guid == Other.Guid; + } + +private: + // Version of FVoxelSpawnerSave, not of the compressed data! + int32 Version; + FGuid Guid; + TArray CompressedData; + + friend class FVoxelSpawnerManager; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelSpawnerHit +{ + // Relative to chunk bounds.min + FVector LocalPosition; + FVector Normal; + + FVoxelSpawnerHit() = default; + FVoxelSpawnerHit(const FVector& LocalPosition, const FVector& Normal) + : LocalPosition(LocalPosition) + , Normal(Normal) + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Spawner Results Memory"), STAT_VoxelSpawnerResults, STATGROUP_VoxelMemory, VOXEL_API); + +enum class EVoxelSpawnerProxyType : uint8 +{ + Invalid = 0, + // Special spawner: any proxy can return an empty spawner + EmptySpawner = 1, + AssetSpawner = 2, + MeshSpawner = 3, + SpawnerGroup = 4, +}; + +inline const TCHAR* ToString(EVoxelSpawnerProxyType ProxyType) +{ + switch (ProxyType) + { + case EVoxelSpawnerProxyType::Invalid: return TEXT("Invalid"); + case EVoxelSpawnerProxyType::EmptySpawner: return TEXT("EmptySpawner"); + case EVoxelSpawnerProxyType::AssetSpawner: return TEXT("AssetSpawner"); + case EVoxelSpawnerProxyType::MeshSpawner: return TEXT("MeshSpawner"); + case EVoxelSpawnerProxyType::SpawnerGroup: return TEXT("SpawnerGroup"); + default: check(false); return TEXT("");; + } +} + +class VOXEL_API FVoxelSpawnerProxyResult : public TVoxelSharedFromThis +{ +private: + uint32 AllocatedSize = 0; + bool bCreated = false; + bool bDirty = false; + bool bCanBeSaved = true; + bool bCanBeDespawned = true; + +public: + const EVoxelSpawnerProxyType Type; + const FVoxelSpawnerProxy& Proxy; + +public: + explicit FVoxelSpawnerProxyResult(EVoxelSpawnerProxyType Type, const FVoxelSpawnerProxy& Proxy); + virtual ~FVoxelSpawnerProxyResult(); + + FVoxelSpawnerProxyResult(const FVoxelSpawnerProxyResult&) = delete; + FVoxelSpawnerProxyResult& operator=(const FVoxelSpawnerProxyResult&) = delete; + +public: + void Create(); + void Destroy(); + void SerializeProxy(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version); + + static TVoxelSharedRef CreateFromType(EVoxelSpawnerProxyType Type, FVoxelSpawnerProxy& Proxy); + + inline bool IsCreated() const + { + return bCreated; + } + + inline bool IsDirty() const + { + return bDirty; + } + inline void MarkDirty() + { + bDirty = true; + } + + inline bool CanBeSaved() const + { + return bCanBeSaved; + } + inline void SetCanBeSaved(bool bNewCanBeSaved) + { + bCanBeSaved = bNewCanBeSaved; + } + + inline bool CanBeDespawned() const + { + return bCanBeDespawned; + } + inline void SetCanBeDespawned(bool bNewCanBeDespawned) + { + bCanBeDespawned = bNewCanBeDespawned; + } + + inline bool NeedsToBeSaved() const + { + return IsDirty() && CanBeSaved(); + } + +protected: + //~ Begin FVoxelSpawnerProxyResult Interface + // Creates rendering. Called on the game thread. + virtual void CreateImpl() = 0; + // Destroys rendering. Called on the game thread. + virtual void DestroyImpl() = 0; + + virtual void SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) = 0; + + virtual uint32 GetAllocatedSize() = 0; + //~ End FVoxelSpawnerProxyResult Interface + +private: + void UpdateStats(); +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelSpawnerProxy : public TVoxelSharedFromThis +{ +public: + FVoxelSpawnerManager& Manager; + + const EVoxelSpawnerProxyType Type; + const uint32 SpawnerSeed; + + + // Seed can be 0 + FVoxelSpawnerProxy(UVoxelSpawner* Spawner, FVoxelSpawnerManager& Manager, EVoxelSpawnerProxyType Type, uint32 Seed); + virtual ~FVoxelSpawnerProxy() = default; + + // Both of these functions must be called only from the Manager or recursively! + + //~ Begin FVoxelSpawnerProxy Interface + virtual TUniquePtr ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const = 0; + virtual void PostSpawn() = 0; // Called right after every spawner is created + //~ End FVoxelSpawnerProxy Interface +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract) +class VOXEL_API UVoxelSpawner : public UObject +{ + GENERATED_BODY() + +public: + // Average distance between the instances, in voxels + // Num Instances = Area in voxels / Square(DistanceBetweenInstancesInVoxel) + // Not a density because the values would be too small to store in a float + UPROPERTY(EditAnywhere, Category = "General Settings", meta = (ClampMin = 0)) + float DistanceBetweenInstancesInVoxel = 10; + + // Use this if you create the spawner at runtime + UPROPERTY(Transient) + uint32 SeedOverride = 0; + +public: +#if WITH_EDITOR + virtual bool NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent) { return false; } +#endif + + virtual TVoxelSharedRef GetSpawnerProxy(FVoxelSpawnerManager& Manager); + // All added spawners MUST be valid. Returns success + virtual bool GetSpawners(TSet& OutSpawners); + virtual float GetDistanceBetweenInstancesInVoxel() const { return DistanceBetweenInstancesInVoxel; } + virtual FString GetDebugInfo() const { return {}; } +}; + +USTRUCT(BlueprintType, Category = Voxel) +struct VOXEL_API FVoxelSpawnersSave +#if CPP + : public TVoxelSaveStruct +#endif +{ + GENERATED_BODY() +}; + +DEFINE_VOXEL_SAVE_STRUCT(FVoxelSpawnersSave) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerActor.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerActor.h new file mode 100644 index 00000000..d30ed4c1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerActor.h @@ -0,0 +1,58 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "PhysicsEngine/BodyInstance.h" +#include "VoxelSpawnerActor.generated.h" + +class UVoxelPhysicsRelevancyComponent; +class UStaticMeshComponent; + +// Actor that can be spawned by voxel spawners +// Base class: does nothing +UCLASS() +class VOXEL_API AVoxelSpawnerActor : public AActor +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintNativeEvent, Category = "Voxel") + void SetStaticMesh(UStaticMesh* Mesh, const TMap& SectionsMaterials, const FBodyInstance& CollisionPresets); + + UFUNCTION(BlueprintNativeEvent, Category = "Voxel") + void SetInstanceRandom(float Value); + + virtual void SetStaticMesh_Implementation(UStaticMesh* Mesh, const TMap& SectionsMaterials, const FBodyInstance& CollisionPresets) {} + virtual void SetInstanceRandom_Implementation(float Value) {} +}; + +// Basic voxel actor with a static mesh component +UCLASS() +class VOXEL_API AVoxelMeshSpawnerActor : public AVoxelSpawnerActor +{ + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel") + UStaticMeshComponent* StaticMeshComponent; + + AVoxelMeshSpawnerActor(); + + virtual void SetStaticMesh_Implementation(UStaticMesh* Mesh, const TMap& SectionsMaterials, const FBodyInstance& CollisionPresets) override; + virtual void SetInstanceRandom_Implementation(float Value) override; +}; + +// Basic voxel actor with a static mesh component and a voxel physics relevancy component: physics will be frozen when outside the voxel world collision range +UCLASS() +class VOXEL_API AVoxelMeshWithPhysicsRelevancySpawnerActor : public AVoxelMeshSpawnerActor +{ + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel") + UVoxelPhysicsRelevancyComponent* PhysicsRelevancyComponent; + + AVoxelMeshWithPhysicsRelevancySpawnerActor(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerConfig.h new file mode 100644 index 00000000..51a8ee5d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerConfig.h @@ -0,0 +1,414 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelSpawnerConfig.generated.h" + +class UVoxelSpawner; +class FVoxelGeneratorInstance; +class UVoxelSpawnerConfig; +class UVoxelSpawnerOutputsConfig; + +USTRUCT() +struct FVoxelSpawnerOutputName +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + FName Name; + + FVoxelSpawnerOutputName() = default; + + template + FVoxelSpawnerOutputName(TArgs... Args) + : Name(Forward(Args)...) + { + } + + inline operator FName() const + { + return Name; + } + inline bool IsNone() const + { + return Name.IsNone(); + } +}; + +UENUM() +enum class EVoxelSpawnerDensityType : uint8 +{ + // Use a constant as density + Constant, + // Use a generator output + GeneratorOutput, + // Use one of the material RGBA channels. Only for Ray Spawners. + MaterialRGBA, + // Use the material UV channels. Only for Ray Spawners. + MaterialUVs, + // Use a five way blend strength. Only for Ray Spawners. + MaterialFiveWayBlend, + // Use a single index channel. Only for Ray Spawners. + SingleIndex, + // Use a multi index channel. Only for Ray Spawners. + MultiIndex +}; + +UENUM() +enum class EVoxelSpawnerUVAxis +{ + U, + V +}; + +UENUM() +enum class EVoxelSpawnerDensityTransform +{ + Identity UMETA(DisplayName = "None"), + OneMinus UMETA(DisplayName = "1 - X"), +}; + +USTRUCT() +struct FVoxelSpawnerDensity +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelSpawnerDensityType Type = EVoxelSpawnerDensityType::Constant; + + UPROPERTY(EditAnywhere, Category = "Voxel") + float Constant = 1.f; + + // Your generator needs to have a float output named like this + UPROPERTY(EditAnywhere, Category = "Voxel") + FVoxelSpawnerOutputName GeneratorOutputName; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "RGBA Channel")) + EVoxelRGBA RGBAChannel; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "UV Channel", ClampMin = 0, ClampMax = 3)) + int32 UVChannel; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "UV Axis")) + EVoxelSpawnerUVAxis UVAxis; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ClampMin = 0, ClampMax = 4)) + int32 FiveWayBlendChannel = 0; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ClampMin = 0, ClampMax = 255)) + TArray SingleIndexChannels = { 0 }; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ClampMin = 0, ClampMax = 255)) + TArray MultiIndexChannels = { 0 }; + + // Transform to apply to the density + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelSpawnerDensityTransform Transform = EVoxelSpawnerDensityTransform::Identity; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UENUM() +enum class EVoxelSpawnerConfigElementRandomGenerator : uint8 +{ + // Evenly distributed points + Sobol, + // More uneven points than Sobol. Unreal uses Halton to spawn grass in the default Landscape system + Halton +}; + +UENUM() +enum class EVoxelSpawnerType +{ + // Will line trace the voxel geometry to find spawning locations. Works with any kind of world/shapes + Ray, + // These spawners uses a height output from the generator to spawn, allowing for large spawn distance. + Height +}; + +USTRUCT() +struct FVoxelSpawnerConfigSpawner +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + UVoxelSpawner* Spawner = nullptr; + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelSpawnerType SpawnerType = EVoxelSpawnerType::Ray; + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + FVoxelSpawnerDensity Density; + + // Final Density = Density * DensityMultiplier. Use this to eg paint an Erase Foliage channel. + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Density Multiplier")) + FVoxelSpawnerDensity DensityMultiplier_RayOnly; + + // The name of the custom graph output used to determine the height + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Height Graph Output Name")) + FVoxelSpawnerOutputName HeightGraphOutputName_HeightOnly = "Height"; + +public: + // Chunk size, affects the LOD if ray spawner + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Chunk Size")) + uint32 ChunkSize_EditorOnly = 32; + + // The LOD of the mesh to trace rays against + // High LOD = faster but less precise + UPROPERTY(VisibleAnywhere, Category = "Voxel") + int32 LOD = 0; + + // Generation distance in voxels + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Generation Distance")) + uint32 GenerationDistanceInVoxels_EditorOnly = 0; + + UPROPERTY(VisibleAnywhere, Category = "Voxel") + int32 GenerationDistanceInChunks = 2; + + UPROPERTY() + bool bInfiniteGenerationDistance = false; + +public: + // Whether to save the instances that are removed + // If false will also respawn instances if they are out of range + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bSave = true; + + // If false, instances that are out of range will be despawned. If true, they will stay forever. + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bDoNotDespawn = false; + + // Seed for this spawner. Note that changing this is not required to get unique results per spawner. + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 Seed = 1337; + +public: + // Controls the spawning pattern + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelSpawnerConfigElementRandomGenerator RandomGenerator = EVoxelSpawnerConfigElementRandomGenerator::Halton; + + // Unique ID used when saving spawners to disk + UPROPERTY(VisibleAnywhere, Category = "Voxel") + FGuid Guid; + + // Controls whether to compute the density or the height first. Try both and see which is faster + // If false, the following are true when querying the density: + // - for flat worlds: Z = Height + // - for sphere worlds: Length(X, Y, Z) = Height + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Compute Density First")) + bool bComputeDensityFirst_HeightOnly = false; + + // If true, will not spawn height instances if they are now floating due to user edits or additional 3D noise in the generator + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Check If Floating")) + bool bCheckIfFloating_HeightOnly = true; + + // If true, will not spawn height instances if they are now covered due to user edits or additional 3D noise in the generator + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (DisplayName = "Check If Covered")) + bool bCheckIfCovered_HeightOnly = true; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +USTRUCT() +struct FVoxelSpawnerConfigElementAdvanced_Height +{ + GENERATED_BODY() + + UPROPERTY() + bool bSave = true; + + UPROPERTY() + bool bDoNotDespawn = false; + + UPROPERTY() + FName SeedName = "FoliageSeed"; + + UPROPERTY() + uint32 DefaultSeed = 1337; + + UPROPERTY() + EVoxelSpawnerConfigElementRandomGenerator RandomGenerator = EVoxelSpawnerConfigElementRandomGenerator::Halton; + + UPROPERTY() + bool bComputeDensityFirst = false; + + UPROPERTY() + FGuid Guid; +}; + +USTRUCT() +struct FVoxelSpawnerConfigElementAdvanced_Ray +{ + GENERATED_BODY() + + UPROPERTY() + bool bSave = true; + + UPROPERTY() + bool bDoNotDespawn = false; + + UPROPERTY() + FName SeedName = "FoliageSeed"; + + UPROPERTY() + uint32 DefaultSeed = 1337; + + UPROPERTY() + EVoxelSpawnerConfigElementRandomGenerator RandomGenerator = EVoxelSpawnerConfigElementRandomGenerator::Halton; + + UPROPERTY() + FGuid Guid; +}; + +USTRUCT() +struct FVoxelSpawnerConfigElement_Height +{ + GENERATED_BODY() + + UPROPERTY() + UVoxelSpawner* Spawner = nullptr; + + UPROPERTY() + FVoxelSpawnerDensity Density; + + UPROPERTY() + FVoxelSpawnerOutputName DensityGraphOutputName_DEPRECATED; + + UPROPERTY() + FVoxelSpawnerConfigElementAdvanced_Height Advanced; +}; + +USTRUCT() +struct FVoxelSpawnerConfigElement_Ray +{ + GENERATED_BODY() + + UPROPERTY() + UVoxelSpawner* Spawner = nullptr; + + UPROPERTY() + FVoxelSpawnerDensity Density; + + UPROPERTY() + FVoxelSpawnerDensity DensityMultiplier; + + UPROPERTY() + FVoxelSpawnerOutputName DensityGraphOutputName_DEPRECATED; + + UPROPERTY() + FVoxelSpawnerConfigElementAdvanced_Ray Advanced; +}; + +USTRUCT() +struct FVoxelSpawnerConfigHeightGroup +{ + GENERATED_BODY() + + UPROPERTY() + FVoxelSpawnerOutputName HeightGraphOutputName = "Height"; + + UPROPERTY() + uint32 ChunkSize = 32; + + UPROPERTY() + uint32 GenerationDistanceInChunks = 2; + + UPROPERTY() + uint32 GenerationDistanceInVoxels_EditorOnly = 0; + + UPROPERTY() + TArray Spawners; +}; + +USTRUCT() +struct FVoxelSpawnerConfigRayGroup +{ + GENERATED_BODY() + + UPROPERTY() + uint32 LOD = 0; + + UPROPERTY() + uint32 ChunkSize_EditorOnly = 32; + + UPROPERTY() + uint32 GenerationDistanceInChunks = 2; + + UPROPERTY() + uint32 GenerationDistanceInVoxels_EditorOnly = 0; + + UPROPERTY() + TArray Spawners; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UENUM() +enum class EVoxelSpawnerConfigRayWorldType : uint8 +{ + Flat, + Sphere +}; + +USTRUCT(BlueprintType) +struct FVoxelSpawnerConfigFiveWayBlendSetup +{ + GENERATED_BODY() + + // If true, will ignore Alpha + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Voxel") + bool bFourWayBlend = false; +}; + +UCLASS() +class VOXEL_API UVoxelSpawnerConfig : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + EVoxelSpawnerConfigRayWorldType WorldType; + + UPROPERTY(EditAnywhere, Category = "Config") + UVoxelSpawnerOutputsConfig* GeneratorOutputs; + + UPROPERTY(EditAnywhere, Category = "Config", AdvancedDisplay) + FVoxelSpawnerConfigFiveWayBlendSetup FiveWayBlendSetup; + +public: + UPROPERTY(EditAnywhere, Category = "Spawners") + TArray Spawners; + +public: + UPROPERTY() + TArray RaySpawners_DEPRECATED; + + UPROPERTY() + TArray HeightSpawners_DEPRECATED; + +public: +#if WITH_EDITOR + bool NeedsToRebuild(UObject* Object, const FPropertyChangedEvent& PropertyChangedEvent); +#endif + +protected: +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual void PostLoad() override; + + void SetReadOnlyPropertiesFromEditorOnly(); + void SetEditorOnlyPropertiesFromReadOnly(); + void FixGuids(); + void FixSpawnerDensityTypes(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h new file mode 100644 index 00000000..008b63cc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h @@ -0,0 +1,94 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawner.h" +#include "VoxelSpawnerGroup.generated.h" + +class UVoxelSpawnerGroup; + +class VOXEL_API FVoxelSpawnerGroupProxyResult : public FVoxelSpawnerProxyResult +{ +public: + explicit FVoxelSpawnerGroupProxyResult(const FVoxelSpawnerProxy& Proxy); + + void Init(TArray>&& InResults); + + //~ Begin FVoxelSpawnerProxyResult override + virtual void CreateImpl() override; + virtual void DestroyImpl() override; + + virtual void SerializeImpl(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version) override; + + virtual uint32 GetAllocatedSize() override; + //~ End FVoxelSpawnerProxyResult override + +private: + TArray> Results; +}; + +class VOXEL_API FVoxelSpawnerGroupProxy : public FVoxelSpawnerProxy +{ +public: + const TWeakObjectPtr SpawnerGroup; + + FVoxelSpawnerGroupProxy(UVoxelSpawnerGroup* Spawner, FVoxelSpawnerManager& Manager); + + //~ Begin FVoxelSpawnerProxy Interface + virtual TUniquePtr ProcessHits( + const FVoxelIntBox& Bounds, + const TArray& Hits, + const FVoxelConstDataAccelerator& Accelerator) const override; + virtual void PostSpawn() override; + //~ End FVoxelSpawnerProxy Interface + +private: + struct FChild + { + TVoxelSharedPtr Spawner; + float ProbabilitySum; + }; + TArray Children; + + int32 GetChild(float RandomNumber) const; +}; + +USTRUCT() +struct FVoxelSpawnerGroupChild +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Config") + UVoxelSpawner* Spawner = nullptr; + + UPROPERTY(EditAnywhere, Category = "Config", meta = (ClampMin = 0, ClampMax = 1, UIMin = 0, UIMax = 1)) + float Probability = 0; +}; + +UCLASS() +class VOXEL_API UVoxelSpawnerGroup : public UVoxelSpawner +{ + GENERATED_BODY() + +public: + // Probabilities do not need to be normalized, although it might be harder to understand what's happening if they're not + UPROPERTY(EditAnywhere, Category = "Config") + bool bNormalizeProbabilitiesOnEdit = true; + + UPROPERTY(EditAnywhere, Category = "Config") + TArray Children; + + //~ Begin UVoxelSpawner Interface + virtual TVoxelSharedRef GetSpawnerProxy(FVoxelSpawnerManager& Manager) override; + virtual bool GetSpawners(TSet& OutSpawners) override; + virtual FString GetDebugInfo() const override; + //~ End UVoxelSpawner Interface + +protected: + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; +#endif + //~ End UObject Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerManager.h new file mode 100644 index 00000000..5d8db3f8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerManager.h @@ -0,0 +1,224 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelSpawnerConfig.h" +#include "Containers/Queue.h" +#include "VoxelIntBox.h" +#include "VoxelTickable.h" +#include "IVoxelSpawnerManager.h" + +namespace FVoxelSpawnersSaveVersion +{ + enum Type : int32; +} + +class FVoxelReadScopeLock; +class FVoxelSpawnerTask; +class AVoxelWorld; +class AVoxelWorldInterface; +class FVoxelSpawnerProxy; +class FVoxelSpawnerProxyResult; +class IVoxelRenderer; +class IVoxelLODManager; +class FVoxelInstancedMeshManager; +class FVoxelEventManager; +class FVoxelDebugManager; +class FVoxelData; +class IVoxelPool; +class FVoxelConstDataAccelerator; +struct FVoxelSpawnerHit; +struct FVoxelSpawnersSaveImpl; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Spawner Manager Memory"), STAT_VoxelSpawnerManagerMemory, STATGROUP_VoxelMemory, VOXEL_API); + +struct FVoxelSpawnerSettings +{ + // Used for debug + const TWeakObjectPtr VoxelWorldInterface; + + const TVoxelSharedRef Pool; + const TVoxelSharedRef DebugManager; + const TVoxelSharedRef Data; + const TVoxelSharedRef MeshManager; + const TVoxelSharedRef EventManager; + const TVoxelSharedRef LODManager; + const TVoxelSharedRef Renderer; + const TWeakObjectPtr Config; + const float VoxelSize; + const float PriorityDuration; + + FVoxelSpawnerSettings( + const AVoxelWorld* World, + EVoxelPlayType PlayType, + const TVoxelSharedRef& Pool, + const TVoxelSharedRef& DebugManager, + const TVoxelSharedRef& Data, + const TVoxelSharedRef& LODManager, + const TVoxelSharedRef& Renderer, + const TVoxelSharedRef& MeshManager, + const TVoxelSharedRef& EventManager); +}; + +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelSpawnerConfigSpawnerWithRuntimeData : FVoxelSpawnerConfigSpawner +{ + float DistanceBetweenInstancesInVoxel = 0; + FString DebugName; + + FVoxelSpawnerConfigSpawnerWithRuntimeData() = default; + explicit FVoxelSpawnerConfigSpawnerWithRuntimeData(const FVoxelSpawnerConfigSpawner& Config) + : FVoxelSpawnerConfigSpawner(Config) + { + } +}; +struct FVoxelSpawnerConfigGroupBase +{ + int32 LOD = 0; + int32 GenerationDistanceInChunks = 0; + bool bInfiniteGenerationDistance = false; + EVoxelSpawnerType SpawnerType = {}; +}; + +struct FVoxelSpawnerConfigGroup : FVoxelSpawnerConfigGroupBase +{ + TArray Spawners; + + FVoxelSpawnerConfigGroup() = default; + explicit FVoxelSpawnerConfigGroup(const FVoxelSpawnerConfigGroupBase& Base) : FVoxelSpawnerConfigGroupBase(Base) {} +}; + +struct FVoxelSpawnerThreadSafeConfig +{ + EVoxelSpawnerConfigRayWorldType WorldType = EVoxelSpawnerConfigRayWorldType::Flat; + FVoxelSpawnerConfigFiveWayBlendSetup FiveWayBlendSetup; + TArray Groups; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class VOXEL_API FVoxelSpawnerManager : public IVoxelSpawnerManager, public FVoxelTickable, public TVoxelSharedFromThis +{ +public: + const FVoxelSpawnerSettings Settings; + const FVoxelSpawnerThreadSafeConfig ThreadSafeConfig; + + static TVoxelSharedRef Create(const FVoxelSpawnerSettings& Settings); + ~FVoxelSpawnerManager(); + + TVoxelSharedPtr GetSpawner(UVoxelSpawner* Spawner) const; + + void Serialize(FArchive& Ar, FVoxelSpawnersSaveVersion::Type Version); + + //~ Begin IVoxelSpawnerManager Interface + virtual void Destroy() override; + + virtual void Regenerate(const FVoxelIntBox& Bounds) override; + virtual void MarkDirty(const FVoxelIntBox& Bounds) override; + + virtual void SaveTo(FVoxelSpawnersSaveImpl& Save) override; + virtual void LoadFrom(const FVoxelSpawnersSaveImpl& Save) override; + + virtual bool GetMeshSpawnerTransforms(const FGuid& SpawnerGuid, TArray& OutTransforms) const override; + + virtual int32 GetTaskCount() const override; + //~ End IVoxelSpawnerManager Interface + +protected: + //~ Begin FVoxelTickable Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickableInEditor() const override { return true; } + //~ End FVoxelTickable Interface + +private: + explicit FVoxelSpawnerManager(const FVoxelSpawnerSettings& Settings, const FVoxelSpawnerThreadSafeConfig& ThreadSafeConfig); + + void SpawnGroup_GameThread(FVoxelIntBox Bounds, int32 GroupIndex); + void DestroyGroup_GameThread(FVoxelIntBox Bounds, int32 GroupIndex); + + void SpawnGroup_AnyThread(const FVoxelSpawnerTask& Task); + + void ProcessHits( + const FVoxelSpawnerTask& Task, + const FVoxelConstDataAccelerator& Accelerator, + FVoxelReadScopeLock& Lock, + const TMap>& HitsMap); + + void UpdateTaskCount() const; + + void FlushCreateQueue_GameThread(); + + template + void IterateResultsInBounds(const FVoxelIntBox& Bounds, T ApplyToResult_ReturnsShouldRebuild); + + bool FindSpawnerByGuid(const FGuid& Guid, int32& GroupIndex, int32& SpawnerIndex) const; + + FVoxelIntBoxWithValidity SpawnedSpawnersBounds; + + TMap> SpawnersMap; + + // Tricky thread safety case: Removing a chunk whose task has been started, but not finished yet + // To handle that, we make queuing a result into the apply queues and storing a result into the chunk data atomic + // by locking Group.Section. Using UpdateIndex we can make sure a task isn't storing old data. + + struct FSpawnerGroupChunkData + { + // Update index, used to cancel old tasks + TVoxelSharedRef UpdateIndex = MakeVoxelShared(); + // The results. Same order as Group.Proxies. + TArray> SpawnerProxiesResults; + + void CancelTasks() const + { + UpdateIndex->Increment(); + } + }; + struct FSpawnerGroupData + { + const uint32 ChunkSize = 0; + // The group proxies, in the same order as the group spawners in ThreadSafeConfig + const TArray SpawnerProxies; + + mutable FCriticalSection Section; + TMap ChunksData; + mutable int64 AllocatedSize = 0; + + FSpawnerGroupData(uint32 ChunkSize, const TArray& SpawnerProxies) + : ChunkSize(ChunkSize) + , SpawnerProxies(SpawnerProxies) + { + } + ~FSpawnerGroupData() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerManagerMemory, AllocatedSize); + } + + FIntVector GetChunkKey(const FVoxelIntBox& Bounds) const + { + ensure(Bounds.Size() == FIntVector(ChunkSize)); + return Bounds.Min; + } + void UpdateStats() const + { + // Does not account for the Results array, but that should roughly be Number of Results * sizeof(ptr) + // Would be too costly to compute exact size + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerManagerMemory, AllocatedSize); + AllocatedSize = sizeof(*this) + SpawnerProxies.GetAllocatedSize() + ChunksData.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelSpawnerManagerMemory, AllocatedSize); + } + }; + + TArray GroupsData; + + // Can't use a TQueue as we need to remove elements + FCriticalSection CreateQueueSection; + TArray> CreateQueue; + + // For debug + mutable FThreadSafeCounter TaskCounter; + + friend FVoxelSpawnerTask; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerMatrix.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerMatrix.h new file mode 100644 index 00000000..81971b96 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerMatrix.h @@ -0,0 +1,106 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +// Matrix with special meaning for the last column +// Matrix[0][3] : random instance id, // Matrix[1 2 3][3]: position offset (used for voxel lookup) +struct FVoxelSpawnerMatrix +{ + FVoxelSpawnerMatrix() = default; + explicit FVoxelSpawnerMatrix(const FMatrix & Matrix) + : Matrix(Matrix) + { + } + + FORCEINLINE float GetRandomInstanceId() const + { + return Matrix.M[0][3]; + } + FORCEINLINE void SetRandomInstanceId(float RandomInstanceId) + { + Matrix.M[0][3] = RandomInstanceId; + } + FORCEINLINE void SetRandomInstanceId(uint32 PackedInt) + { + SetRandomInstanceId(*reinterpret_cast(&PackedInt)); + } + + // Used for floating detection: the voxel position is GetMatrixTranslation() + GetPositionOffset() + FORCEINLINE FVector GetPositionOffset() const + { + return FVector(Matrix.M[1][3], Matrix.M[2][3], Matrix.M[3][3]); + } + FORCEINLINE void SetPositionOffset(const FVector& PositionOffset) + { + Matrix.M[1][3] = PositionOffset.X; + Matrix.M[2][3] = PositionOffset.Y; + Matrix.M[3][3] = PositionOffset.Z; + } + + FORCEINLINE FMatrix GetCleanMatrix() const + { + auto Copy = Matrix; + Copy.M[0][3] = 0; + Copy.M[1][3] = 0; + Copy.M[2][3] = 0; + Copy.M[3][3] = 1; + return Copy; + } + + FORCEINLINE bool operator==(const FVoxelSpawnerMatrix& Other) const + { + return Matrix == Other.Matrix; + } + FORCEINLINE bool operator!=(const FVoxelSpawnerMatrix& Other) const + { + return Matrix != Other.Matrix; + } + + FORCEINLINE friend FArchive& operator<<(FArchive& Ar, FVoxelSpawnerMatrix& SpawnerMatrix) + { + Ar << SpawnerMatrix.Matrix; + return Ar; + } + +private: + FMatrix Matrix; +}; + +struct FVoxelSpawnerTransform +{ + // Used to reduce precision errors + FIntVector TransformOffset; + // Relative to TransformOffset + FVoxelSpawnerMatrix Matrix; + + FORCEINLINE bool operator==(const FVoxelSpawnerTransform& Other) const + { + return TransformOffset == Other.TransformOffset && Matrix == Other.Matrix; + } + FORCEINLINE bool operator!=(const FVoxelSpawnerTransform& Other) const + { + return TransformOffset != Other.TransformOffset || Matrix != Other.Matrix; + } + FORCEINLINE friend FArchive& operator<<(FArchive& Ar, FVoxelSpawnerTransform& Transform) + { + Ar << Transform.TransformOffset; + Ar << Transform.Matrix; + return Ar; + } +}; +struct FVoxelSpawnerTransforms +{ + // Used to reduce precision errors. In voxels + FIntVector TransformsOffset; + // Relative to TransformsOffset. Not in voxels, but multiplied by Voxel Size! + TArray Matrices; + + FORCEINLINE friend FArchive& operator<<(FArchive& Ar, FVoxelSpawnerTransforms& Transforms) + { + Ar << Transforms.TransformsOffset; + Ar << Transforms.Matrices; + return Ar; + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerOutputsConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerOutputsConfig.h new file mode 100644 index 00000000..f2e3f5b0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerOutputsConfig.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelSpawnerOutputsConfig.generated.h" + +UCLASS() +class VOXEL_API UVoxelSpawnerOutputsConfig : public UObject +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelSpawnerOutputConfig Interface + virtual TArray GetFloatOutputs() const { unimplemented(); return {}; } + //~ End UVoxelSpawnerOutputConfig Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStaticWorld.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStaticWorld.h new file mode 100644 index 00000000..cef7a989 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStaticWorld.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "VoxelStaticWorld.generated.h" + +class UStaticMeshComponent; +class USceneComponent; + +UCLASS() +class VOXEL_API AVoxelStaticWorld : public AActor +{ + GENERATED_BODY() + +public: + AVoxelStaticWorld(); + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Voxel") + UStaticMeshComponent* BaseMesh; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray Meshes; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStats.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStats.h new file mode 100644 index 00000000..30efc16e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelStats.h @@ -0,0 +1,171 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Stats/Stats.h" +#include "VoxelDefinitions.h" + +DECLARE_STATS_GROUP(TEXT("Voxel"), STATGROUP_Voxel, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Slow"), STATGROUP_VoxelSlow, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Render"), STATGROUP_VoxelRender, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Async"), STATGROUP_VoxelAsync, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Async Verbose"), STATGROUP_VoxelAsyncVerbose, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Counters"), STATGROUP_VoxelCounters, STATCAT_Advanced); +DECLARE_STATS_GROUP(TEXT("Voxel Memory"), STATGROUP_VoxelMemory, STATCAT_Advanced); + +#if STATS +struct FStat_Voxel_Base +{ + static FORCEINLINE EStatDataType::Type GetStatType() + { + return EStatDataType::ST_int64; + } + static FORCEINLINE bool IsClearEveryFrame() + { + return true; + } + static FORCEINLINE bool IsCycleStat() + { + return true; + } + static FORCEINLINE FPlatformMemory::EMemoryCounterRegion GetMemoryRegion() + { + return FPlatformMemory::MCR_Invalid; + } +}; +template +struct TStat_Voxel_Initializer +{ + TStat_Voxel_Initializer(const FString& Description) + { + T::GetDescriptionRef() = Description; + } +}; + +#define VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(Suffix) PREPROCESSOR_JOIN(PREPROCESSOR_JOIN(FStat_Voxel_, __LINE__), Suffix) + +// We want to be able to use __FUNCTION__ as description, so it's a bit tricky +#define VOXEL_SCOPE_COUNTER_IMPL_IMPL(StatGroup, Description) \ + struct VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(PREPROCESSOR_NOTHING) : FStat_Voxel_Base \ + { \ + using TGroup = FStatGroup_##StatGroup; \ + \ + static FORCEINLINE FString& GetDescriptionRef() \ + { \ + static FString StaticDescription; \ + return StaticDescription; \ + } \ + static FORCEINLINE const char* GetStatName() \ + { \ + return PREPROCESSOR_TO_STRING(VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(_Name)); \ + } \ + static FORCEINLINE const TCHAR* GetDescription() \ + { \ + return *GetDescriptionRef(); \ + } \ + }; \ + static FThreadSafeStaticStat VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(_Ptr); \ + static TStat_Voxel_Initializer VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(_Initializer){ Description }; \ + FScopeCycleCounter VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(_CycleCount)(VOXEL_SCOPE_COUNTER_STAT_CLASS_NAME(_Ptr.GetStatId())); + +#else +#define VOXEL_SCOPE_COUNTER_IMPL_IMPL(StatGroup, Description) +#endif + +VOXEL_API FString VoxelStats_RemoveLambdaFromFunctionName(const FString& FunctionName); + +#define VOXEL_INLINE_COUNTER_IMPL(Macro, Name, ...) ([&]() -> decltype(auto) { Macro(VoxelStats_RemoveLambdaFromFunctionName(__FUNCTION__) + TEXT(".") + Name); return __VA_ARGS__; }()) + +#define VOXEL_SCOPE_COUNTER_NAME(Description) __FUNCTION__ + FString(TEXT(".")) + Description + +#define VOXEL_SCOPE_COUNTER_IMPL(Description) ensureVoxelSlowNoSideEffects(IsInGameThread()); VOXEL_SCOPE_COUNTER_IMPL_IMPL(STATGROUP_Voxel, Description) +#define VOXEL_SCOPE_COUNTER(Description) VOXEL_SCOPE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_NAME(Description)) +#define VOXEL_FUNCTION_COUNTER() VOXEL_SCOPE_COUNTER_IMPL(__FUNCTION__) +#define VOXEL_INLINE_COUNTER(Name, ...) VOXEL_INLINE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_IMPL, Name, __VA_ARGS__) + +#define VOXEL_RENDER_SCOPE_COUNTER_IMPL(Description) ensureVoxelSlowNoSideEffects(IsInRenderingThread()); VOXEL_SCOPE_COUNTER_IMPL_IMPL(STATGROUP_VoxelRender, Description) +#define VOXEL_RENDER_SCOPE_COUNTER(Description) VOXEL_RENDER_SCOPE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_NAME(Description)) +#define VOXEL_RENDER_FUNCTION_COUNTER() VOXEL_RENDER_SCOPE_COUNTER_IMPL(__FUNCTION__) +#define VOXEL_RENDER_INLINE_COUNTER(Name, ...) VOXEL_INLINE_COUNTER_IMPL(VOXEL_RENDER_SCOPE_COUNTER_IMPL, Name, __VA_ARGS__) + +#define VOXEL_ASYNC_SCOPE_COUNTER_IMPL(Description) VOXEL_SCOPE_COUNTER_IMPL_IMPL(STATGROUP_VoxelAsync, Description) +#define VOXEL_ASYNC_SCOPE_COUNTER(Description) VOXEL_ASYNC_SCOPE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_NAME(Description)) +#define VOXEL_ASYNC_FUNCTION_COUNTER() VOXEL_ASYNC_SCOPE_COUNTER_IMPL(__FUNCTION__) +#define VOXEL_ASYNC_INLINE_COUNTER(Name, ...) VOXEL_INLINE_COUNTER_IMPL(VOXEL_ASYNC_SCOPE_COUNTER_IMPL, Name, __VA_ARGS__) + +#define VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER_IMPL(Description) VOXEL_SCOPE_COUNTER_IMPL_IMPL(STATGROUP_VoxelAsyncVerbose, Description) +#define VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER(Description) VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_NAME(Description)) +#define VOXEL_ASYNC_VERBOSE_FUNCTION_COUNTER() VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER_IMPL(__FUNCTION__) +#define VOXEL_ASYNC_VERBOSE_INLINE_COUNTER(Name, ...) VOXEL_INLINE_COUNTER_IMPL(VOXEL_ASYNC_VERBOSE_SCOPE_COUNTER_IMPL, Name, __VA_ARGS__) + +#if VOXEL_SLOW_STATS +#define VOXEL_SLOW_SCOPE_COUNTER_IMPL(Description) VOXEL_SCOPE_COUNTER_IMPL_IMPL(STATGROUP_VoxelSlow, Description) +#else +#define VOXEL_SLOW_SCOPE_COUNTER_IMPL(Description) +#endif +#define VOXEL_SLOW_SCOPE_COUNTER(Description) VOXEL_SLOW_SCOPE_COUNTER_IMPL(VOXEL_SCOPE_COUNTER_NAME(Description)) +#define VOXEL_SLOW_FUNCTION_COUNTER() VOXEL_SLOW_SCOPE_COUNTER_IMPL(__FUNCTION__) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if ENABLE_VOXEL_MEMORY_STATS +struct FVoxelMemoryCounterRef +{ + FThreadSafeCounter64* UsageCounterPtr = nullptr; + FThreadSafeCounter64* PeakCounterPtr = nullptr; +}; +VOXEL_API TMap& GetVoxelMemoryCounters(); + +struct FVoxelMemoryCounterStaticRef +{ + VOXEL_API FVoxelMemoryCounterStaticRef(const TCHAR* Name, const FVoxelMemoryCounterRef& Ref); +}; + +#define VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName) PREPROCESSOR_JOIN(StatName, _MemoryUsage) +#define VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName) PREPROCESSOR_JOIN(StatName, _MemoryPeak) + +#define DECLARE_VOXEL_MEMORY_STAT(Name, StatName, Group, API) \ + extern API FThreadSafeCounter64 VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName); \ + extern API FThreadSafeCounter64 VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName); \ + inline const TCHAR* Get ## StatName ## StaticName() { return Name; } \ + DECLARE_MEMORY_STAT_EXTERN(Name, StatName ## _Stat, Group, API) + +#define DEFINE_VOXEL_MEMORY_STAT(StatName) \ + FThreadSafeCounter64 VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName); \ + FThreadSafeCounter64 VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName); \ + static FVoxelMemoryCounterStaticRef StaticRef ## StatName(Get ## StatName ## StaticName(), FVoxelMemoryCounterRef{ &VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName), &VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName) }); \ + DEFINE_STAT(StatName ## _Stat) + +#define INC_VOXEL_MEMORY_STAT_BY_IMPL(StatName, Amount) \ + INC_MEMORY_STAT_BY(StatName ## _Stat, Amount) \ + VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName).Set(FMath::Max( \ + VOXEL_MEMORY_PEAK_COUNTER_NAME(StatName).GetValue(), \ + Amount + VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName).Add(Amount))); // Max is not atomic, but w/e should be fine anyways + +#define DEC_VOXEL_MEMORY_STAT_BY_IMPL(StatName, Amount) \ + DEC_MEMORY_STAT_BY(StatName ## _Stat, Amount); \ + VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName).Subtract(Amount); \ + ensureVoxelSlowNoSideEffects(VOXEL_MEMORY_USAGE_COUNTER_NAME(StatName).GetValue() >= 0); + +/////////////////////////////////////////////////////////////////////////////// + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Total Voxel Memory"), STAT_TotalVoxelMemory, STATGROUP_VoxelMemory, VOXEL_API); + +#define INC_VOXEL_MEMORY_STAT_BY(StatName, Amount) \ + INC_VOXEL_MEMORY_STAT_BY_IMPL(StatName, Amount) \ + INC_VOXEL_MEMORY_STAT_BY_IMPL(STAT_TotalVoxelMemory, Amount) + +#define DEC_VOXEL_MEMORY_STAT_BY(StatName, Amount) \ + DEC_VOXEL_MEMORY_STAT_BY_IMPL(StatName, Amount) \ + DEC_VOXEL_MEMORY_STAT_BY_IMPL(STAT_TotalVoxelMemory, Amount) + +#else +#define DECLARE_VOXEL_MEMORY_STAT(Name, StatName, Group, API) DECLARE_MEMORY_STAT_EXTERN(Name, StatName ## _Stat, Group, API) +#define DEFINE_VOXEL_MEMORY_STAT(StatName) DEFINE_STAT(StatName ## _Stat) + +#define INC_VOXEL_MEMORY_STAT_BY(StatName, Amount) INC_MEMORY_STAT_BY(StatName ## _Stat, Amount); +#define DEC_VOXEL_MEMORY_STAT_BY(StatName, Amount) DEC_MEMORY_STAT_BY(StatName ## _Stat, Amount); +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTexture.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTexture.h new file mode 100644 index 00000000..4061e7b5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTexture.h @@ -0,0 +1,247 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" +#include "VoxelTexture.generated.h" + +class UTexture; +class UTexture2D; + +DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel Texture Memory"), STAT_VoxelTextureMemory, STATGROUP_VoxelMemory, VOXEL_API); + +template<> +struct TNumericLimits +{ + typedef FColor NumericType; + + static constexpr NumericType Min() + { + return FColor(MIN_uint8, MIN_uint8, MIN_uint8, MIN_uint8); + } + + static constexpr NumericType Max() + { + return FColor(MAX_uint8, MAX_uint8, MAX_uint8, MAX_uint8); + } + + static constexpr NumericType Lowest() + { + return Min(); + } +}; + +namespace FVoxelUtilities +{ + inline float ComponentMin(float A, float B) + { + return FMath::Min(A, B); + } + inline float ComponentMax(float A, float B) + { + return FMath::Max(A, B); + } + + inline FColor ComponentMin(FColor A, FColor B) + { + return FColor(FMath::Min(A.R, B.R), FMath::Min(A.G, B.G), FMath::Min(A.B, B.B), FMath::Min(A.A, B.A)); + } + inline FColor ComponentMax(FColor A, FColor B) + { + return FColor(FMath::Max(A.R, B.R), FMath::Max(A.G, B.G), FMath::Max(A.B, B.B), FMath::Max(A.A, B.A)); + } +} + +template +struct TVoxelTexture +{ + inline int32 GetSizeX() const + { + return DataPtr->SizeX; + } + inline int32 GetSizeY() const + { + return DataPtr->SizeY; + } + inline const TArray& GetTextureData() const + { + return DataPtr->TextureData; + } + inline T GetMin() const + { + return DataPtr->Min; + } + inline T GetMax() const + { + return DataPtr->Max; + } + + inline T SampleRaw(int32 X, int32 Y, EVoxelSamplerMode Mode) const + { + if (Mode == EVoxelSamplerMode::Clamp) + { + X = FMath::Clamp(X, 0, GetSizeX() - 1); + Y = FMath::Clamp(Y, 0, GetSizeY() - 1); + } + else + { + X = FVoxelUtilities::PositiveMod(X, GetSizeX()); + Y = FVoxelUtilities::PositiveMod(Y, GetSizeY()); + } + return GetTextureData()[X + GetSizeX() * Y]; + } + template + inline U Sample(int32 X, int32 Y, EVoxelSamplerMode Mode) const + { + return U(SampleRaw(X, Y, Mode)); + } + template + inline U Sample(v_flt X, v_flt Y, EVoxelSamplerMode Mode) const + { + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + + const v_flt AlphaX = X - MinX; + const v_flt AlphaY = Y - MinY; + + return FVoxelUtilities::BilinearInterpolation( + Sample(MinX, MinY, Mode), + Sample(MaxX, MinY, Mode), + Sample(MinX, MaxY, Mode), + Sample(MaxX, MaxY, Mode), + AlphaX, + AlphaY); + } + +public: + struct FTextureData + { + FTextureData() = default; + ~FTextureData() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelTextureMemory, AllocatedSize); + } + + void SetSize(int32 NewSizeX, int32 NewSizeY) + { + check(NewSizeX >= 0 && NewSizeY >= 0); + SizeX = NewSizeX; + SizeY = NewSizeY; + TextureData.Empty(SizeX * SizeY); + TextureData.SetNumUninitialized(SizeX * SizeY); + UpdateAllocatedSize(); + + Min = TNumericLimits::Max(); + Max = TNumericLimits::Lowest(); + } + void SetBounds(T NewMin, T NewMax) + { + Min = NewMin; + Max = NewMax; + } + + FORCEINLINE void SetValue(int32 X, int32 Y, T Value) + { + checkVoxelSlow(0 <= X && X < SizeX); + checkVoxelSlow(0 <= Y && Y < SizeY); + SetValue(X + SizeX * Y, Value); + } + FORCEINLINE void SetValue(int32 Index, T Value) + { + SetValue_NoBounds(Index, Value); + Min = FVoxelUtilities::ComponentMin(Min, Value); + Max = FVoxelUtilities::ComponentMax(Max, Value); + } + FORCEINLINE void SetValue_NoBounds(int32 Index, T Value) + { + checkVoxelSlow(TextureData.IsValidIndex(Index)); + TextureData.GetData()[Index] = Value; + } + + private: + int32 SizeX = 1; + int32 SizeY = 1; + TArray TextureData = { T{} }; + T Min{}; + T Max{}; + + int64 AllocatedSize = 0; + + void UpdateAllocatedSize() + { + DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelTextureMemory, AllocatedSize); + AllocatedSize = TextureData.GetAllocatedSize(); + INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelTextureMemory, AllocatedSize); + } + + friend TVoxelTexture; + }; + + TVoxelTexture() + : TVoxelTexture(MakeShareable(new FTextureData())) + { + } + explicit TVoxelTexture(const TVoxelSharedRef& InDataPtr) + : DataPtr(InDataPtr) + { + check(GetTextureData().Num() == GetSizeX() * GetSizeY()); + } + +private: + TVoxelSharedRef DataPtr; +}; + +// TODO: function to clear render target cache + +namespace FVoxelTextureUtilities +{ + VOXEL_API TVoxelTexture CreateFromTexture_Color(UTexture* Texture); + VOXEL_API TVoxelTexture CreateFromTexture_Float(UTexture* Texture, EVoxelRGBA Channel); + + VOXEL_API bool CanCreateFromTexture(UTexture* Texture, FString& OutError); + VOXEL_API void FixTexture(UTexture* Texture); + + VOXEL_API void ClearCache(); + VOXEL_API void ClearCache(UTexture* Texture); + + VOXEL_API void CreateOrUpdateUTexture2D(const TVoxelTexture& Texture, UTexture2D*& InOutTexture); + VOXEL_API void CreateOrUpdateUTexture2D(const TVoxelTexture& Texture, UTexture2D*& InOutTexture); + + VOXEL_API TVoxelTexture CreateColorTextureFromFloatTexture(const TVoxelTexture& Texture, EVoxelRGBA Channel, bool bNormalize); + + VOXEL_API TVoxelTexture Normalize(const TVoxelTexture& Texture); +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelFloatTexture +{ + GENERATED_BODY() + + FVoxelFloatTexture() = default; + FVoxelFloatTexture(const TVoxelTexture& Texture) + : Texture(Texture) + { + } + + TVoxelTexture Texture; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelColorTexture +{ + GENERATED_BODY() + + FVoxelColorTexture() = default; + FVoxelColorTexture(const TVoxelTexture& Texture) + : Texture(Texture) + { + } + + TVoxelTexture Texture; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelThreadPool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelThreadPool.h new file mode 100644 index 00000000..a3921a6e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelThreadPool.h @@ -0,0 +1,117 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "HAL/PlatformAffinity.h" +#include "HAL/ThreadSafeBool.h" +#include "VoxelMinimal.h" +#include + +class IVoxelQueuedWork; +class FVoxelQueuedThread; + +class VOXEL_API FVoxelQueuedThreadPoolStats +{ +public: + static FVoxelQueuedThreadPoolStats& Get(); + + void Report(FName Name, double Time); + void LogTimes() const; + +private: + FVoxelQueuedThreadPoolStats() = default; + + mutable FCriticalSection Section; + TMap Times; +}; + +struct VOXEL_API FVoxelQueuedThreadPoolSettings +{ + const FString PoolName; + const uint32 NumThreads; + const uint32 StackSize; + const EThreadPriority ThreadPriority; + const bool bConstantPriorities; + + FVoxelQueuedThreadPoolSettings( + const FString& PoolName, + uint32 NumThreads, + uint32 StackSize, + EThreadPriority ThreadPriority, + bool bConstantPriorities); +}; + +class VOXEL_API FVoxelQueuedThreadPool : public TVoxelSharedFromThis +{ +public: + const FVoxelQueuedThreadPoolSettings Settings; + + static TVoxelSharedRef Create(const FVoxelQueuedThreadPoolSettings& Settings); + ~FVoxelQueuedThreadPool(); + + int32 GetNumPendingWorks() const + { + // Not really thread safe, only use this for debug + // Also count active threads + return (Settings.bConstantPriorities ? StaticQueuedWorks.size() : QueuedWorks.Num()) + GetNumThreads() - QueuedThreads.Num(); + } + int32 GetNumThreads() const + { + return AllThreads.Num(); + } + + // Final priority is 64 bits: PriorityCategory in upper bits, and GetPriority in lower bits + // Use PriorityCategory to make some type of tasks have a higher priority than other + void AddQueuedWork(IVoxelQueuedWork* InQueuedWork, uint32 PriorityCategory, int32 PriorityOffset); + void AddQueuedWorks(const TArray& InQueuedWorks, uint32 PriorityCategory, int32 PriorityOffset); + + IVoxelQueuedWork* ReturnToPoolOrGetNextJob(FVoxelQueuedThread* InQueuedThread); + + void AbandonAllTasks(); + +private: + explicit FVoxelQueuedThreadPool(const FVoxelQueuedThreadPoolSettings& Settings); + + const TArray> AllThreads; + + FCriticalSection Section; + TArray QueuedThreads; + + struct FQueuedWorkInfo + { + IVoxelQueuedWork* Work; + double NextPriorityUpdateTime; + uint32 PriorityCategory; + uint32 Priority; + int32 PriorityOffset; + + FQueuedWorkInfo() = default; + FQueuedWorkInfo( + IVoxelQueuedWork* Work, + uint32 PriorityCategory, + int32 PriorityOffset) + : Work(Work) + , NextPriorityUpdateTime(0) + , PriorityCategory(PriorityCategory) + , Priority(0) + , PriorityOffset(PriorityOffset) + { + } + + void RecomputePriority(double Time); + + FORCEINLINE uint64 GetPriority() const + { + return (uint64(PriorityCategory) << 32) | uint64(Priority); + } + FORCEINLINE bool operator<(const FQueuedWorkInfo& Other) const + { + return GetPriority() < Other.GetPriority(); + } + }; + TArray QueuedWorks; + std::priority_queue StaticQueuedWorks; + + FThreadSafeBool TimeToDie = false; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTickable.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTickable.h new file mode 100644 index 00000000..70cedccb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTickable.h @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Tickable.h" + +class FVoxelTickable : public FTickableGameObject +{ +public: + virtual ~FVoxelTickable() + { + ensure(!bShouldTick); + } + + virtual bool IsTickable() const final override + { + return bShouldTick; + } + virtual TStatId GetStatId() const final override + { + RETURN_QUICK_DECLARE_CYCLE_STAT(FVoxelTickable, STATGROUP_Tickables); + } + + void StopTicking() + { + ensure(IsInGameThread()); + ensure(bShouldTick); + bShouldTick = false; + } + void ResumeTicking() + { + ensure(IsInGameThread()); + ensure(!bShouldTick); + bShouldTick = true; + } + bool IsTicking() const + { + ensure(IsInGameThread()); + return bShouldTick; + } + +private: + bool bShouldTick = true; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelBoxTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelBoxTools.h new file mode 100644 index 00000000..f94ea651 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelBoxTools.h @@ -0,0 +1,377 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Gen/VoxelToolsBase.h" +#include "VoxelBoxTools.generated.h" + +UCLASS() +class VOXEL_API UVoxelBoxTools : public UVoxelToolsBase +{ + GENERATED_BODY() + +public: + /** + * Set the density in a box + * @see SetValueBox, SetValueBoxAsync and FVoxelBoxToolsImpl::SetValueBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Value The density to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender")) + static void SetValueBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + + /** + * Set the density in a box + * Runs asynchronously in a background thread + * @see SetValueBox, SetValueBoxAsync and FVoxelBoxToolsImpl::SetValueBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Value The density to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SetValueBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Set the density in a box + * @see SetValueBox, SetValueBoxAsync and FVoxelBoxToolsImpl::SetValueBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Value The density to set + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetValueBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Set the density in a box + * Runs asynchronously in a background thread + * @see SetValueBox, SetValueBoxAsync and FVoxelBoxToolsImpl::SetValueBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Value The density to set + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetValueBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + float Value, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + +public: + /** + * Add a box shape + * @see AddBox, AddBoxAsync and FVoxelBoxToolsImpl::AddBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender")) + static void AddBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + + /** + * Add a box shape + * Runs asynchronously in a background thread + * @see AddBox, AddBoxAsync and FVoxelBoxToolsImpl::AddBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void AddBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Add a box shape + * @see AddBox, AddBoxAsync and FVoxelBoxToolsImpl::AddBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void AddBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Add a box shape + * Runs asynchronously in a background thread + * @see AddBox, AddBoxAsync and FVoxelBoxToolsImpl::AddBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void AddBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + +public: + /** + * Remove a box shape + * @see RemoveBox, RemoveBoxAsync and FVoxelBoxToolsImpl::RemoveBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender")) + static void RemoveBox( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + + /** + * Remove a box shape + * Runs asynchronously in a background thread + * @see RemoveBox, RemoveBoxAsync and FVoxelBoxToolsImpl::RemoveBox + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void RemoveBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Remove a box shape + * @see RemoveBox, RemoveBoxAsync and FVoxelBoxToolsImpl::RemoveBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RemoveBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Remove a box shape + * Runs asynchronously in a background thread + * @see RemoveBox, RemoveBoxAsync and FVoxelBoxToolsImpl::RemoveBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RemoveBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + +public: + /** + * Paint a box shape + * @see SetMaterialBox, SetMaterialBoxAsync and FVoxelBoxToolsImpl::SetMaterialBox + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param PaintMaterial The material to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender")) + static void SetMaterialBox( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); + + /** + * Paint a box shape + * Runs asynchronously in a background thread + * @see SetMaterialBox, SetMaterialBoxAsync and FVoxelBoxToolsImpl::SetMaterialBox + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param PaintMaterial The material to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Box Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SetMaterialBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Paint a box shape + * @see SetMaterialBox, SetMaterialBoxAsync and FVoxelBoxToolsImpl::SetMaterialBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param PaintMaterial The material to set + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetMaterialBox( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Paint a box shape + * Runs asynchronously in a background thread + * @see SetMaterialBox, SetMaterialBoxAsync and FVoxelBoxToolsImpl::SetMaterialBox + * @param VoxelWorld The voxel world to do the edit to + * @param Bounds The bounds of the box + * @param PaintMaterial The material to set + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetMaterialBoxAsync( + AVoxelWorld* VoxelWorld, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelLevelTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelLevelTools.h new file mode 100644 index 00000000..30c97f58 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelLevelTools.h @@ -0,0 +1,144 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Gen/VoxelToolsBase.h" +#include "VoxelLevelTools.generated.h" + +UCLASS() +class VOXEL_API UVoxelLevelTools : public UVoxelToolsBase +{ + GENERATED_BODY() + +public: + /** + * Stamps a cylinder, to quickly level parts of the world + * If additive, will stamp a smooth cylinder above Position. Else will remove one below Position + * @see Level, LevelAsync and FVoxelLevelToolsImpl::Level + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the top (or bottom if subtractive) of the cylinder. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff between 0 and 1. The higher the smoother the cylinder edge. + * @param Height The height of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bAdditive Additive or subtractive edit, see node comment + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position, Radius and Height will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Level Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void Level( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Stamps a cylinder, to quickly level parts of the world + * If additive, will stamp a smooth cylinder above Position. Else will remove one below Position + * Runs asynchronously in a background thread + * @see Level, LevelAsync and FVoxelLevelToolsImpl::Level + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the top (or bottom if subtractive) of the cylinder. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff between 0 and 1. The higher the smoother the cylinder edge. + * @param Height The height of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bAdditive Additive or subtractive edit, see node comment + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position, Radius and Height will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Level Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void LevelAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Stamps a cylinder, to quickly level parts of the world + * If additive, will stamp a smooth cylinder above Position. Else will remove one below Position + * @see Level, LevelAsync and FVoxelLevelToolsImpl::Level + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the top (or bottom if subtractive) of the cylinder. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff between 0 and 1. The higher the smoother the cylinder edge. + * @param Height The height of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bAdditive Additive or subtractive edit, see node comment + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position, Radius and Height will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void Level( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Stamps a cylinder, to quickly level parts of the world + * If additive, will stamp a smooth cylinder above Position. Else will remove one below Position + * Runs asynchronously in a background thread + * @see Level, LevelAsync and FVoxelLevelToolsImpl::Level + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the top (or bottom if subtractive) of the cylinder. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff between 0 and 1. The higher the smoother the cylinder edge. + * @param Height The height of the cylinder. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bAdditive Additive or subtractive edit, see node comment + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position, Radius and Height will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void LevelAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSphereTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSphereTools.h new file mode 100644 index 00000000..1141ce1c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSphereTools.h @@ -0,0 +1,1438 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Gen/VoxelToolsBase.h" +#include "VoxelSphereTools.generated.h" + +UCLASS() +class VOXEL_API UVoxelSphereTools : public UVoxelToolsBase +{ + GENERATED_BODY() + +public: + /** + * Set the density in a sphere + * @see SetValueSphere, SetValueSphereAsync and FVoxelSphereToolsImpl::SetValueSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Value The density to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void SetValueSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Set the density in a sphere + * Runs asynchronously in a background thread + * @see SetValueSphere, SetValueSphereAsync and FVoxelSphereToolsImpl::SetValueSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Value The density to set + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SetValueSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Set the density in a sphere + * @see SetValueSphere, SetValueSphereAsync and FVoxelSphereToolsImpl::SetValueSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Value The density to set + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetValueSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Set the density in a sphere + * Runs asynchronously in a background thread + * @see SetValueSphere, SetValueSphereAsync and FVoxelSphereToolsImpl::SetValueSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Value The density to set + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetValueSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Value, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Remove a sphere + * @see RemoveSphere, RemoveSphereAsync and FVoxelSphereToolsImpl::RemoveSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void RemoveSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Remove a sphere + * Runs asynchronously in a background thread + * @see RemoveSphere, RemoveSphereAsync and FVoxelSphereToolsImpl::RemoveSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void RemoveSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Remove a sphere + * @see RemoveSphere, RemoveSphereAsync and FVoxelSphereToolsImpl::RemoveSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RemoveSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Remove a sphere + * Runs asynchronously in a background thread + * @see RemoveSphere, RemoveSphereAsync and FVoxelSphereToolsImpl::RemoveSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RemoveSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Add a sphere + * @see AddSphere, AddSphereAsync and FVoxelSphereToolsImpl::AddSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void AddSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Add a sphere + * Runs asynchronously in a background thread + * @see AddSphere, AddSphereAsync and FVoxelSphereToolsImpl::AddSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void AddSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Add a sphere + * @see AddSphere, AddSphereAsync and FVoxelSphereToolsImpl::AddSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void AddSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Add a sphere + * Runs asynchronously in a background thread + * @see AddSphere, AddSphereAsync and FVoxelSphereToolsImpl::AddSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void AddSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Paint material in a sphere shape + * @see SetMaterialSphere, SetMaterialSphereAsync and FVoxelSphereToolsImpl::SetMaterialSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param PaintMaterial The material to paint + * @param Strength The strength of the painting (preferably between 0 and 1) + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender")) + static void SetMaterialSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength = 1.f, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Paint material in a sphere shape + * Runs asynchronously in a background thread + * @see SetMaterialSphere, SetMaterialSphereAsync and FVoxelSphereToolsImpl::SetMaterialSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param PaintMaterial The material to paint + * @param Strength The strength of the painting (preferably between 0 and 1) + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SetMaterialSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength = 1.f, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Paint material in a sphere shape + * @see SetMaterialSphere, SetMaterialSphereAsync and FVoxelSphereToolsImpl::SetMaterialSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param PaintMaterial The material to paint + * @param Strength The strength of the painting (preferably between 0 and 1) + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetMaterialSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength = 1.f, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Paint material in a sphere shape + * Runs asynchronously in a background thread + * @see SetMaterialSphere, SetMaterialSphereAsync and FVoxelSphereToolsImpl::SetMaterialSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param PaintMaterial The material to paint + * @param Strength The strength of the painting (preferably between 0 and 1) + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SetMaterialSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength = 1.f, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Apply a 3x3x3 kernel + * @see ApplyKernelSphere, ApplyKernelSphereAsync and FVoxelSphereToolsImpl::ApplyKernelSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void ApplyKernelSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Apply a 3x3x3 kernel + * Runs asynchronously in a background thread + * @see ApplyKernelSphere, ApplyKernelSphereAsync and FVoxelSphereToolsImpl::ApplyKernelSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void ApplyKernelSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Apply a 3x3x3 kernel + * @see ApplyKernelSphere, ApplyKernelSphereAsync and FVoxelSphereToolsImpl::ApplyKernelSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void ApplyKernelSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Apply a 3x3x3 kernel + * Runs asynchronously in a background thread + * @see ApplyKernelSphere, ApplyKernelSphereAsync and FVoxelSphereToolsImpl::ApplyKernelSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void ApplyKernelSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Apply a 3x3x3 kernel to the materials + * @see ApplyMaterialKernelSphere, ApplyMaterialKernelSphereAsync and FVoxelSphereToolsImpl::ApplyMaterialKernelSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Mask The material channels to affect + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender")) + static void ApplyMaterialKernelSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + UPARAM(meta = (Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) int32 Mask = 4095, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Apply a 3x3x3 kernel to the materials + * Runs asynchronously in a background thread + * @see ApplyMaterialKernelSphere, ApplyMaterialKernelSphereAsync and FVoxelSphereToolsImpl::ApplyMaterialKernelSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Mask The material channels to affect + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void ApplyMaterialKernelSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + UPARAM(meta = (Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) int32 Mask = 4095, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Apply a 3x3x3 kernel to the materials + * @see ApplyMaterialKernelSphere, ApplyMaterialKernelSphereAsync and FVoxelSphereToolsImpl::ApplyMaterialKernelSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Mask The material channels to affect + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void ApplyMaterialKernelSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Apply a 3x3x3 kernel to the materials + * Runs asynchronously in a background thread + * @see ApplyMaterialKernelSphere, ApplyMaterialKernelSphereAsync and FVoxelSphereToolsImpl::ApplyMaterialKernelSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Mask The material channels to affect + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void ApplyMaterialKernelSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Smooth a sphere + * @see SmoothSphere, SmoothSphereAsync and FVoxelSphereToolsImpl::SmoothSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void SmoothSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Smooth a sphere + * Runs asynchronously in a background thread + * @see SmoothSphere, SmoothSphereAsync and FVoxelSphereToolsImpl::SmoothSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SmoothSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Smooth a sphere + * @see SmoothSphere, SmoothSphereAsync and FVoxelSphereToolsImpl::SmoothSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SmoothSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Smooth a sphere + * Runs asynchronously in a background thread + * @see SmoothSphere, SmoothSphereAsync and FVoxelSphereToolsImpl::SmoothSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SmoothSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Smooth materials in a sphere + * @see SmoothMaterialSphere, SmoothMaterialSphereAsync and FVoxelSphereToolsImpl::SmoothMaterialSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param Mask The material channels to affect + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender")) + static void SmoothMaterialSphere( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + UPARAM(meta = (Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) int32 Mask = 4095, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Smooth materials in a sphere + * Runs asynchronously in a background thread + * @see SmoothMaterialSphere, SmoothMaterialSphereAsync and FVoxelSphereToolsImpl::SmoothMaterialSphere + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param Mask The material channels to affect + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void SmoothMaterialSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + UPARAM(meta = (Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) int32 Mask = 4095, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Smooth materials in a sphere + * @see SmoothMaterialSphere, SmoothMaterialSphereAsync and FVoxelSphereToolsImpl::SmoothMaterialSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param Mask The material channels to affect + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SmoothMaterialSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Smooth materials in a sphere + * Runs asynchronously in a background thread + * @see SmoothMaterialSphere, SmoothMaterialSphereAsync and FVoxelSphereToolsImpl::SmoothMaterialSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param Mask The material channels to affect + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void SmoothMaterialSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Trim tool: used to quickly flatten large portions of the world + * Best results are obtained when Position and Normal are averages: use FindProjectionVoxels to do some linetraces, and then GetAveragePosition/Normal on the result + * This ensures the tool usage is relatively smooth. + * Works by stamping a shape into the world (if bAdditive = false, the stamp is destructive: voxels are removed instead) + * The shape is the smooth union of a sphere SDF and a plane SDF. The smoothness of the union is controlled by the Falloff parameter. + * @see TrimSphere, TrimSphereAsync and FVoxelSphereToolsImpl::TrimSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Normal The normal + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff, between 0 and 1 + * @param bAdditive Whether to add or remove voxels + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void TrimSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Trim tool: used to quickly flatten large portions of the world + * Best results are obtained when Position and Normal are averages: use FindProjectionVoxels to do some linetraces, and then GetAveragePosition/Normal on the result + * This ensures the tool usage is relatively smooth. + * Works by stamping a shape into the world (if bAdditive = false, the stamp is destructive: voxels are removed instead) + * The shape is the smooth union of a sphere SDF and a plane SDF. The smoothness of the union is controlled by the Falloff parameter. + * Runs asynchronously in a background thread + * @see TrimSphere, TrimSphereAsync and FVoxelSphereToolsImpl::TrimSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Normal The normal + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff, between 0 and 1 + * @param bAdditive Whether to add or remove voxels + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void TrimSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Trim tool: used to quickly flatten large portions of the world + * Best results are obtained when Position and Normal are averages: use FindProjectionVoxels to do some linetraces, and then GetAveragePosition/Normal on the result + * This ensures the tool usage is relatively smooth. + * Works by stamping a shape into the world (if bAdditive = false, the stamp is destructive: voxels are removed instead) + * The shape is the smooth union of a sphere SDF and a plane SDF. The smoothness of the union is controlled by the Falloff parameter. + * @see TrimSphere, TrimSphereAsync and FVoxelSphereToolsImpl::TrimSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Normal The normal + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff, between 0 and 1 + * @param bAdditive Whether to add or remove voxels + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void TrimSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Trim tool: used to quickly flatten large portions of the world + * Best results are obtained when Position and Normal are averages: use FindProjectionVoxels to do some linetraces, and then GetAveragePosition/Normal on the result + * This ensures the tool usage is relatively smooth. + * Works by stamping a shape into the world (if bAdditive = false, the stamp is destructive: voxels are removed instead) + * The shape is the smooth union of a sphere SDF and a plane SDF. The smoothness of the union is controlled by the Falloff parameter. + * Runs asynchronously in a background thread + * @see TrimSphere, TrimSphereAsync and FVoxelSphereToolsImpl::TrimSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Normal The normal + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param Falloff The falloff, between 0 and 1 + * @param bAdditive Whether to add or remove voxels + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void TrimSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Reverts the voxels inside a sphere shape to a previous frame in the undo history. + * Can be used to "paint" the undo history + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @see RevertSphere, RevertSphereAsync and FVoxelSphereToolsImpl::RevertSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param HistoryPosition The history position to go back to. You can use GetHistoryPosition to get it. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void RevertSphere( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Reverts the voxels inside a sphere shape to a previous frame in the undo history. + * Can be used to "paint" the undo history + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * Runs asynchronously in a background thread + * @see RevertSphere, RevertSphereAsync and FVoxelSphereToolsImpl::RevertSphere + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param HistoryPosition The history position to go back to. You can use GetHistoryPosition to get it. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void RevertSphereAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Reverts the voxels inside a sphere shape to a previous frame in the undo history. + * Can be used to "paint" the undo history + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @see RevertSphere, RevertSphereAsync and FVoxelSphereToolsImpl::RevertSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param HistoryPosition The history position to go back to. You can use GetHistoryPosition to get it. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RevertSphere( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Reverts the voxels inside a sphere shape to a previous frame in the undo history. + * Can be used to "paint" the undo history + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * Runs asynchronously in a background thread + * @see RevertSphere, RevertSphereAsync and FVoxelSphereToolsImpl::RevertSphere + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param HistoryPosition The history position to go back to. You can use GetHistoryPosition to get it. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RevertSphereAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + +public: + /** + * Reverts the voxels inside a sphere shape to their generator value + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @see RevertSphereToGenerator, RevertSphereToGeneratorAsync and FVoxelSphereToolsImpl::RevertSphereToGenerator + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender")) + static void RevertSphereToGenerator( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Reverts the voxels inside a sphere shape to their generator value + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * Runs asynchronously in a background thread + * @see RevertSphereToGenerator, RevertSphereToGeneratorAsync and FVoxelSphereToolsImpl::RevertSphereToGenerator + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Sphere Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedValues, bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void RevertSphereToGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Reverts the voxels inside a sphere shape to their generator value + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @see RevertSphereToGenerator, RevertSphereToGeneratorAsync and FVoxelSphereToolsImpl::RevertSphereToGenerator + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RevertSphereToGenerator( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Reverts the voxels inside a sphere shape to their generator value + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * Runs asynchronously in a background thread + * @see RevertSphereToGenerator, RevertSphereToGeneratorAsync and FVoxelSphereToolsImpl::RevertSphereToGenerator + * @param VoxelWorld The voxel world to do the edit to + * @param Position The position of the center. In world space (unreal units) if bConvertToVoxelSpace is true. In voxel space if false. + * @param Radius The radius. In unreal units if bConvertToVoxelSpace is true. In voxels if false. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bConvertToVoxelSpace If true, Position and Radius will be converted to voxel space. Else they will be used directly. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void RevertSphereToGeneratorAsync( + AVoxelWorld* VoxelWorld, + const FVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSurfaceEditTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSurfaceEditTools.h new file mode 100644 index 00000000..2f26ba0b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelSurfaceEditTools.h @@ -0,0 +1,307 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/VoxelSurfaceEdits.h" +#include "VoxelTools/Gen/VoxelToolsBase.h" +#include "VoxelSurfaceEditTools.generated.h" + +UCLASS() +class VOXEL_API UVoxelSurfaceEditTools : public UVoxelToolsBase +{ + GENERATED_BODY() + +public: + /** + * Apply processed voxels strengths to the voxel values + * @see ApplyStack, AddToStack + * @see EditVoxelValues, EditVoxelValuesAsync and FVoxelSurfaceEditToolsImpl::EditVoxelValues + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param DistanceDivisor Distance divisor: the new value will be divided by this. Useful if normals are bad because of clamped values + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "DistanceDivisor, bMultiThreaded, bRecordModifiedValues, bUpdateRender")) + static void EditVoxelValues( + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor = 1.f, + bool bMultiThreaded = true, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + + /** + * Apply processed voxels strengths to the voxel values + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see EditVoxelValues, EditVoxelValuesAsync and FVoxelSurfaceEditToolsImpl::EditVoxelValues + * @param ModifiedValues Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param DistanceDivisor Distance divisor: the new value will be divided by this. Useful if normals are bad because of clamped values + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "DistanceDivisor, bMultiThreaded, bRecordModifiedValues, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void EditVoxelValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedValues, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor = 1.f, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Apply processed voxels strengths to the voxel values + * @see ApplyStack, AddToStack + * @see EditVoxelValues, EditVoxelValuesAsync and FVoxelSurfaceEditToolsImpl::EditVoxelValues + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param DistanceDivisor Distance divisor: the new value will be divided by this. Useful if normals are bad because of clamped values + * @param OutModifiedValues Optional. Record the Values modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void EditVoxelValues( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor = 1.f, + TArray* OutModifiedValues = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Apply processed voxels strengths to the voxel values + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see EditVoxelValues, EditVoxelValuesAsync and FVoxelSurfaceEditToolsImpl::EditVoxelValues + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param DistanceDivisor Distance divisor: the new value will be divided by this. Useful if normals are bad because of clamped values + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedValues If false, will not fill ModifiedValues, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void EditVoxelValuesAsync( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor = 1.f, + const FOnVoxelToolComplete_WithModifiedValues& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedValues = true, + bool bUpdateRender = true); + +public: + /** + * Apply paint material to the processed voxels, using the processed strengths + * @see ApplyStack, AddToStack + * @see EditVoxelMaterials, EditVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::EditVoxelMaterials + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param PaintMaterial The paint material to apply + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender")) + static void EditVoxelMaterials( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); + + /** + * Apply paint material to the processed voxels, using the processed strengths + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see EditVoxelMaterials, EditVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::EditVoxelMaterials + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param PaintMaterial The paint material to apply + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void EditVoxelMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Apply paint material to the processed voxels, using the processed strengths + * @see ApplyStack, AddToStack + * @see EditVoxelMaterials, EditVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::EditVoxelMaterials + * @param VoxelWorld The voxel world to do the edit to + * @param PaintMaterial The paint material to apply + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void EditVoxelMaterials( + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Apply paint material to the processed voxels, using the processed strengths + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see EditVoxelMaterials, EditVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::EditVoxelMaterials + * @param VoxelWorld The voxel world to do the edit to + * @param PaintMaterial The paint material to apply + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void EditVoxelMaterialsAsync( + AVoxelWorld* VoxelWorld, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); + +public: + /** + * Propagate the materials of the voxels, so that the new surface is painted correctly + * Must be called BEFORE EditVoxelValues! + * @see ApplyStack, AddToStack + * @see PropagateVoxelMaterials, PropagateVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender")) + static void PropagateVoxelMaterials( + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded = true, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); + + /** + * Propagate the materials of the voxels, so that the new surface is painted correctly + * Must be called BEFORE EditVoxelValues! + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see PropagateVoxelMaterials, PropagateVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials + * @param ModifiedMaterials Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging + * @param EditedBounds Returns the bounds edited by this function + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + * @param bHideLatentWarnings Hide latent warnings caused by calling a node before its previous call completion. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Edit Tools", meta = (DefaultToSelf = "VoxelWorld", AdvancedDisplay = "bMultiThreaded, bRecordModifiedMaterials, bUpdateRender, bHideLatentWarnings", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject")) + static void PropagateVoxelMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& ModifiedMaterials, + FVoxelIntBox& EditedBounds, + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + + /** + * Propagate the materials of the voxels, so that the new surface is painted correctly + * Must be called BEFORE EditVoxelValues! + * @see ApplyStack, AddToStack + * @see PropagateVoxelMaterials, PropagateVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param OutModifiedMaterials Optional. Record the Materials modified by this function. Useful to track the amount of edit done, for instance to give resources when digging. Will append to existing values. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void PropagateVoxelMaterials( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + TArray* OutModifiedMaterials = nullptr, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = true, + bool bUpdateRender = true); + + /** + * Propagate the materials of the voxels, so that the new surface is painted correctly + * Must be called BEFORE EditVoxelValues! + * Runs asynchronously in a background thread + * @see ApplyStack, AddToStack + * @see PropagateVoxelMaterials, PropagateVoxelMaterialsAsync and FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials + * @param VoxelWorld The voxel world to do the edit to + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack + * @param Callback Called on the game thread when the function is completed. Will not be called if the async function completes after the voxel world is destroyed. + * @param OutEditedBounds Optional. Returns the bounds edited by this function + * @param bMultiThreaded If true, multiple threads will be used to make the edit faster. Not recommended on async functions, as it might cause lag. + * @param bRecordModifiedMaterials If false, will not fill ModifiedMaterials, making the edit faster. + * @param bUpdateRender If false, will only edit the data and not update the render. Rarely needed. + */ + static void PropagateVoxelMaterialsAsync( + AVoxelWorld* VoxelWorld, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + const FOnVoxelToolComplete_WithModifiedMaterials& Callback = {}, + FVoxelIntBox* OutEditedBounds = nullptr, + bool bMultiThreaded = false, + bool bRecordModifiedMaterials = true, + bool bUpdateRender = true); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelToolsBase.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelToolsBase.h new file mode 100644 index 00000000..c0cb0213 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Gen/VoxelToolsBase.h @@ -0,0 +1,76 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelTools/VoxelPaintMaterial.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelToolsBase.generated.h" + +class AVoxelWorld; + +USTRUCT(BlueprintType) +struct FModifiedVoxelValue +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float OldValue = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float NewValue = 0; + + FModifiedVoxelValue() = default; + + FModifiedVoxelValue(const FIntVector& Position, float OldValue, float NewValue) + : Position(Position) + , OldValue(OldValue) + , NewValue(NewValue) + { + } + + FModifiedVoxelValue(const FIntVector& Position, FVoxelValue OldValue, FVoxelValue NewValue) + : Position(Position) + , OldValue(OldValue.ToFloat()) + , NewValue(NewValue.ToFloat()) + { + } +}; + +USTRUCT(BlueprintType) +struct FModifiedVoxelMaterial +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelMaterial OldMaterial = FVoxelMaterial(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelMaterial NewMaterial = FVoxelMaterial(ForceInit); +}; + +DECLARE_DELEGATE(FOnVoxelToolComplete); +DECLARE_DELEGATE_OneParam(FOnVoxelToolComplete_WithModifiedValues, const TArray&); +DECLARE_DELEGATE_OneParam(FOnVoxelToolComplete_WithModifiedMaterials, const TArray&); + +UCLASS() +class VOXEL_API UVoxelToolsBase : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Base") + static FVoxelIntBox GetModifiedVoxelValuesBounds(const TArray& ModifiedVoxels); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Base") + static FVoxelIntBox GetModifiedVoxelMaterialsBounds(const TArray& ModifiedVoxels); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.h new file mode 100644 index 00000000..765b81f6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.h @@ -0,0 +1,66 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" + +class VOXEL_API FVoxelBoxToolsImpl : public FVoxelToolsBaseImpl +{ +public: + template + static void BoxEdit( + TData& Data, + const FVoxelIntBox& Bounds); + +public: + /** + * Set the density in a box + * @param Bounds The bounds of the box + * @param Value The density to set @VoxelValue + * @ExportSetValue + */ + template + static void SetValueBox( + TData& Data, + const FVoxelIntBox& Bounds, + FVoxelValue Value); + + /** + * Add a box shape + * @param Bounds The bounds of the box + * @ExportSetValue + */ + template + static void AddBox( + TData& Data, + const FVoxelIntBox& Bounds) + { + BoxEdit(Data, Bounds); + } + + /** + * Remove a box shape + * @param Bounds The bounds of the box + * @ExportSetValue + */ + template + static void RemoveBox( + TData& Data, + const FVoxelIntBox& Bounds) + { + BoxEdit(Data, Bounds); + } + + /** + * Paint a box shape + * @param Bounds The bounds of the box + * @param PaintMaterial The material to set + * @ExportSetMaterial + */ + template + static void SetMaterialBox( + TData& Data, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.inl new file mode 100644 index 00000000..1467633e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.inl @@ -0,0 +1,51 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelTools/Impl/VoxelBoxToolsImpl.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" + +#define VOXEL_BOX_TOOL_IMPL() VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + +template +void FVoxelBoxToolsImpl::BoxEdit(TData& Data, const FVoxelIntBox& Bounds) +{ + VOXEL_BOX_TOOL_IMPL(); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + if (X == Bounds.Min.X || X == Bounds.Max.X - 1 || Y == Bounds.Min.Y || Y == Bounds.Max.Y - 1 || Z == Bounds.Min.Z || Z == Bounds.Max.Z - 1) + { + if ((bAdd && Value.IsEmpty()) || (!bAdd && !Value.IsEmpty())) + { + Value = FVoxelValue(0.f); + } + } + else + { + Value = bAdd ? FVoxelValue::Full() : FVoxelValue::Empty(); + } + }); +} + +template +void FVoxelBoxToolsImpl::SetValueBox(TData& Data, const FVoxelIntBox& Bounds, FVoxelValue Value) +{ + VOXEL_BOX_TOOL_IMPL(); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& OldValue) + { + OldValue = Value; + }); +} + +template +void FVoxelBoxToolsImpl::SetMaterialBox(TData& Data, const FVoxelIntBox& Bounds, const FVoxelPaintMaterial& PaintMaterial) +{ + VOXEL_BOX_TOOL_IMPL(); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelMaterial& Material) + { + PaintMaterial.ApplyToMaterial(Material, 1.f); + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.h new file mode 100644 index 00000000..fab9d88f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.h @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" + +class VOXEL_API FVoxelLevelToolsImpl : public FVoxelToolsBaseImpl +{ +public: + static FVoxelIntBox GetBounds(const FVoxelVector& Position, float Radius, float Height, bool bAdditive); + +public: + /** + * Stamps a cylinder, to quickly level parts of the world + * If additive, will stamp a smooth cylinder above Position. Else will remove one below Position + * @param Position The position of the top (or bottom if subtractive) of the cylinder @VoxelPosition @GetBounds + * @param Radius The radius of the cylinder @VoxelDistance @GetBounds + * @param Falloff The falloff between 0 and 1. The higher the smoother the cylinder edge. + * @param Height The height of the cylinder @VoxelDistance @GetBounds + * @param bAdditive Additive or subtractive edit, see node comment @GetBounds + * @ExportSetValue + */ + template + static void Level( + TData& Data, + const FVoxelVector& Position, + float Radius, + float Falloff, + float Height, + bool bAdditive); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.inl new file mode 100644 index 00000000..cee1d3d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.inl @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelTools/Impl/VoxelLevelToolsImpl.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" + +inline FVoxelIntBox FVoxelLevelToolsImpl::GetBounds(const FVoxelVector& Position, float Radius, float Height, bool bAdditive) +{ + const float R = Radius + 2; + if (bAdditive) + { + // Below + return FVoxelIntBox(Position - FVector(R, R, Height + 1.f), Position + FVector(R, R, 1.f)); + } + else + { + // Above + return FVoxelIntBox(Position - FVector(R, R, 1.f), Position + FVector(R, R, Height + 1.f)); + } +} + +template +void FVoxelLevelToolsImpl::Level(TData& Data, const FVoxelVector& Position, float Radius, float Falloff, float Height, bool bAdditive) +{ + const FVoxelIntBox Bounds = GetBounds(Position, Radius, Height, bAdditive); + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + // Center the height + const FVoxelVector CylinderPosition = Position + FVoxelVector(0, 0, (bAdditive ? -Height : Height) / 2); + + const float SquaredRadius = FMath::Square(Radius + 2); + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float SquaredDistance = FVector2D(X - Position.X, Y - Position.Y).SizeSquared(); + if (SquaredDistance <= SquaredRadius) + { + const float SDF = FVoxelUtilities::RoundCylinder(FVoxelVector(X, Y, Z) - CylinderPosition, Radius, Height, Falloff); + if (bAdditive) + { + Value = FMath::Min(Value, FVoxelValue(SDF)); + } + else + { + Value = FMath::Max(Value, FVoxelValue(-SDF)); + } + } + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.h new file mode 100644 index 00000000..d0be9574 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.h @@ -0,0 +1,306 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUtilities/VoxelLambdaUtilities.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" + +class VOXEL_API FVoxelSphereToolsImpl : public FVoxelToolsBaseImpl +{ +public: + static FVoxelIntBox GetBounds(const FVoxelVector& Position, float Radius); + +public: + template + static void SphereEdit( + TData& Data, + const FVoxelVector& Position, + float Radius); + + template + static void SetMaterialSphereImpl( + TData& Data, + const FVoxelVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + T GetStrength = FVoxelLambdaUtilities::ConstantStrength); + +public: + template + static void ApplyKernelSphereImpl_GetData( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + TInterpolator* RESTRICT & SrcBuffer, + bool bForceSingleThread, + TGetInterpolator GetInterpolator); + + template + static TInterpolator ApplyKernelSphereImpl_GetNeighborsValue( + TInterpolator* RESTRICT Buffer, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + const FIntVector& Size, + int32 X, int32 Y, int32 Z); + + template + static void ApplyKernelSphereImpl_Iterate( + TInterpolator* RESTRICT & SrcBuffer, + TInterpolator* RESTRICT & DstBuffer, + bool bForceSingleThread, + const FVoxelVector& LocalPosition, + const FIntVector& Size, + float SquaredRadius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + TGetStrength GetStrength); + + template + static void ApplyKernelSphereImpl( + TData& Data, + TGetInterpolator GetInterpolator, + TSetInterpolator SetInterpolator, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + TGetStrength GetStrength); + + template + static void ApplyMaterialKernelSphereImpl( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + uint32 Mask, + T GetStrength); + +public: + /** + * Set the density in a sphere + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param Value The density to set @VoxelValue + * @ExportSetValue + */ + template + static void SetValueSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + FVoxelValue Value); + + /** + * Remove a sphere + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @ExportSetValue + */ + template + static void RemoveSphere( + TData& Data, + const FVoxelVector& Position, + float Radius) + { + SphereEdit(Data, Position, Radius); + } + + /** + * Add a sphere + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @ExportSetValue + */ + template + static void AddSphere( + TData& Data, + const FVoxelVector& Position, + float Radius) + { + SphereEdit(Data, Position, Radius); + } + + /** + * Paint material in a sphere shape + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param PaintMaterial The material to paint + * @param Strength The strength of the painting (preferably between 0 and 1) + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @ExportSetMaterial + */ + template + static void SetMaterialSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength = 1.f, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f); + + /** + * Apply a 3x3x3 kernel + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param GetStrength Use this to apply a falloff. @UseDefault @Internal + * @ExportSetValue + */ + template + static void ApplyKernelSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + T GetStrength = FVoxelLambdaUtilities::ConstantStrength); + + /** + * Apply a 3x3x3 kernel to the materials + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param CenterMultiplier Multiplier of the center value + * @param FirstDegreeNeighborMultiplier Multiplier of the immediate neighbors, which share 2 coordinates with the center + * @param SecondDegreeNeighborMultiplier Multiplier of the near corners neighbors, which share 1 coordinates with the center + * @param ThirdDegreeNeighborMultiplier Multiplier of the far corners neighbors, which share 0 coordinates with the center + * @param NumIterations The number of times the kernel is going to be applied. Increase to make the effect faster, but more expensive. + * @param Mask The material channels to affect @BitMask=EVoxelMaterialMask_BP @UFunctionDefault=4095 + * @param GetStrength Use this to apply a falloff. @UseDefault @Internal + * @ExportSetMaterial + */ + template + static void ApplyMaterialKernelSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier = 0.037f, + float FirstDegreeNeighborMultiplier = 0.037f, + float SecondDegreeNeighborMultiplier = 0.037f, + float ThirdDegreeNeighborMultiplier = 0.037f, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + T GetStrength = FVoxelLambdaUtilities::ConstantStrength); + + /** + * Smooth a sphere + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @ExportSetValue + */ + template + static void SmoothSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f); + + /** + * Smooth materials in a sphere + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param Strength The strength of the smoothing (preferably between 0 and 1) + * @param NumIterations The number of times the smooth is going to be applied. Increase to make smoothing faster, but more expensive. + * @param Mask The material channels to affect @BitMask=EVoxelMaterialMask_BP @UFunctionDefault=4095 + * @param FalloffType The type of falloff + * @param Falloff The falloff, between 0 and 1. Set to 0 to disable. + * @ExportSetMaterial + */ + template + static void SmoothMaterialSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float Strength, + int32 NumIterations = 1, + uint32 Mask = EVoxelMaterialMask::All, + EVoxelFalloff FalloffType = EVoxelFalloff::Linear, + float Falloff = 0.5f); + + /** + * Trim tool: used to quickly flatten large portions of the world + * Best results are obtained when Position and Normal are averages: use FindProjectionVoxels to do some linetraces, and then GetAveragePosition/Normal on the result + * This ensures the tool usage is relatively smooth. + * + * Works by stamping a shape into the world (if bAdditive = false, the stamp is destructive: voxels are removed instead) + * The shape is the smooth union of a sphere SDF and a plane SDF. The smoothness of the union is controlled by the Falloff parameter. + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Normal The normal + * @param Radius The radius @VoxelDistance @GetBounds + * @param Falloff The falloff, between 0 and 1 + * @param bAdditive Whether to add or remove voxels + * @ImplNote Radius + Falloff must be locked + * @ExportSetValue + */ + template + static void TrimSphere( + TData& Data, + const FVoxelVector& Position, + const FVector& Normal, + float Radius, + float Falloff, + bool bAdditive); + + /** + * Reverts the voxels inside a sphere shape to a previous frame in the undo history. + * Can be used to "paint" the undo history + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param HistoryPosition The history position to go back to. You can use GetHistoryPosition to get it. + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @ExportSetValue + */ + template + static void RevertSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + int32 HistoryPosition, + bool bRevertValues, + bool bRevertMaterials); + + /** + * Reverts the voxels inside a sphere shape to their generator value + * NOTE: Does not work with bMultiThreaded and does not fill ModifiedValues! + * @param Position The position of the center @VoxelPosition @GetBounds + * @param Radius The radius @VoxelDistance @GetBounds + * @param bRevertValues Whether to revert values + * @param bRevertMaterials Whether to revert materials + * @ExportSetValue + */ + template + static void RevertSphereToGenerator( + TData& Data, + const FVoxelVector& Position, + float Radius, + bool bRevertValues, + bool bRevertMaterials); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.inl new file mode 100644 index 00000000..92fa7e39 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.inl @@ -0,0 +1,810 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelTools/Impl/VoxelSphereToolsImpl.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" +#include "VoxelUtilities/VoxelSDFUtilities.h" +#include "VoxelInterpolator.h" + +#define VOXEL_SPHERE_TOOL_IMPL() const FVoxelIntBox Bounds = FVoxelSphereToolsImpl::GetBounds(Position, Radius); VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + +inline FVoxelIntBox FVoxelSphereToolsImpl::GetBounds(const FVoxelVector& Position, float Radius) +{ + return FVoxelIntBox(Position - Radius - 3, Position + Radius + 3); +} + + +template +void FVoxelSphereToolsImpl::SphereEdit(TData& Data, const FVoxelVector& Position, float Radius) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + const float SquaredRadiusPlus2 = FMath::Square(Radius + 2); + const float SquaredRadiusMinus2 = FMath::Square(FMath::Max(Radius - 2, 0.f)); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance > SquaredRadiusPlus2) return; + + if (SquaredDistance <= SquaredRadiusMinus2) + { + Value = bAdd ? FVoxelValue::Full() : FVoxelValue::Empty(); + } + else + { + const float Distance = FMath::Sqrt(SquaredDistance); + const FVoxelValue NewValue{ FMath::Clamp(Radius - Distance, -2.f, 2.f) / 2 * (bAdd ? -1 : 1) }; + + // We want to cover as many surface as possible, so we take the biggest value + Value = FVoxelUtilities::MergeAsset(Value, NewValue, !bAdd); + } + }); +} + +template +void FVoxelSphereToolsImpl::SetMaterialSphereImpl( + TData& Data, + const FVoxelVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + T GetStrength) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + const float SquaredRadius = FMath::Square(Radius); + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelMaterial& Material) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadius) + { + PaintMaterial.ApplyToMaterial(Material, GetStrength(FMath::Sqrt(SquaredDistance))); + } + }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSphereToolsImpl::ApplyKernelSphereImpl_GetData( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + TInterpolator* RESTRICT & SrcBuffer, + bool bForceSingleThread, + TGetInterpolator GetInterpolator) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const FIntVector Size = Bounds.Size(); + + const TArray Values = GetActualData(Data).template Get(Bounds); + FVoxelIntBox(0, Size).ParallelIterate([&](int32 X, int32 Y, int32 Z) + { + FVoxelUtilities::Get3D(SrcBuffer, Size, X, Y, Z) = GetInterpolator(FVoxelUtilities::Get3D(Values, Size, X, Y, Z)); + }, bForceSingleThread); +} + +template +TInterpolator FVoxelSphereToolsImpl::ApplyKernelSphereImpl_GetNeighborsValue( + TInterpolator* Buffer, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + const FIntVector& Size, + int32 X, int32 Y, int32 Z) +{ + const auto GetNeighbor = [&](int32 DX, int32 DY, int32 DZ) + { + const int32 Degree = FMath::Abs(DX) + FMath::Abs(DY) + FMath::Abs(DZ); + const float Multiplier = + Degree == 1 ? FirstDegreeNeighborMultiplier + : Degree == 2 ? SecondDegreeNeighborMultiplier + : ThirdDegreeNeighborMultiplier; + + // We can safely query neighbors thanks to the sphere distance check + return FVoxelUtilities::Get3D(Buffer, Size, X + DX, Y + DY, Z + DZ) * Multiplier; + }; + + return + GetNeighbor(-1, -1, -1) + + GetNeighbor(+0, -1, -1) + + GetNeighbor(+1, -1, -1) + + GetNeighbor(-1, +0, -1) + + GetNeighbor(+0, +0, -1) + + GetNeighbor(+1, +0, -1) + + GetNeighbor(-1, +1, -1) + + GetNeighbor(+0, +1, -1) + + GetNeighbor(+1, +1, -1) + + GetNeighbor(-1, -1, +0) + + GetNeighbor(+0, -1, +0) + + GetNeighbor(+1, -1, +0) + + GetNeighbor(-1, +0, +0) + + GetNeighbor(+1, +0, +0) + + GetNeighbor(-1, +1, +0) + + GetNeighbor(+0, +1, +0) + + GetNeighbor(+1, +1, +0) + + GetNeighbor(-1, -1, +1) + + GetNeighbor(+0, -1, +1) + + GetNeighbor(+1, -1, +1) + + GetNeighbor(-1, +0, +1) + + GetNeighbor(+0, +0, +1) + + GetNeighbor(+1, +0, +1) + + GetNeighbor(-1, +1, +1) + + GetNeighbor(+0, +1, +1) + + GetNeighbor(+1, +1, +1); +} + +template +void FVoxelSphereToolsImpl::ApplyKernelSphereImpl_Iterate( + TInterpolator* RESTRICT & SrcBuffer, + TInterpolator* RESTRICT & DstBuffer, + bool bForceSingleThread, + const FVoxelVector& LocalPosition, + const FIntVector& Size, + float SquaredRadius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + TGetStrength GetStrength) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + for (int32 Iteration = 0; Iteration < NumIterations; Iteration++) + { + VOXEL_ASYNC_SCOPE_COUNTER("Step"); + FVoxelIntBox(0, Size).ParallelIterate([&](int32 X, int32 Y, int32 Z) + { + const float SquaredDistance = FVector(X - LocalPosition.X, Y - LocalPosition.Y, Z - LocalPosition.Z).SizeSquared(); + + const int32 Index = FVoxelUtilities::Get3DIndex(Size, X, Y, Z); + if (SquaredDistance <= SquaredRadius) // Kinda hacky: assume this is false for at least a 1-voxel thick border, making it safe to query neighbors + { + const TInterpolator NeighborsValue = ApplyKernelSphereImpl_GetNeighborsValue( + SrcBuffer, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + Size, + X, Y, Z); + const TInterpolator OldValue = SrcBuffer[Index]; + const TInterpolator NewValue = NeighborsValue + OldValue * CenterMultiplier; + DstBuffer[Index] = FMath::Lerp(OldValue, NewValue, GetStrength(FMath::Sqrt(SquaredDistance))); + } + else + { + DstBuffer[Index] = SrcBuffer[Index]; + } + }, bForceSingleThread); + + // Swap of RESTRICT is confusing clang + auto Copy = SrcBuffer; + SrcBuffer = DstBuffer; + DstBuffer = Copy; + } +} + +template +void FVoxelSphereToolsImpl::ApplyKernelSphereImpl( + TData& Data, + TGetInterpolator GetInterpolator, + TSetInterpolator SetInterpolator, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + TGetStrength GetStrength) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + if (NumIterations <= 0) + { + return; + } + + const bool bForceSingleThread = !IsDataMultiThreaded(Data); + const float SquaredRadius = FMath::Square(Radius); + const FIntVector Size = Bounds.Size(); + const FVoxelVector LocalPosition = Position - Bounds.Min; + + TArray Buffer; + Buffer.SetNumUninitialized(Bounds.Count() * 2); + + // Only make one allocation + TInterpolator* RESTRICT SrcBuffer = Buffer.GetData(); + TInterpolator* RESTRICT DstBuffer = Buffer.GetData() + Bounds.Count(); + + // Copy data to buffer + // We could do it inline in Step, but it would do the conversions many more times than needed when querying the neighbors + ApplyKernelSphereImpl_GetData( + GetActualData(Data), + Bounds, + SrcBuffer, + bForceSingleThread, + GetInterpolator); + + ApplyKernelSphereImpl_Iterate( + SrcBuffer, + DstBuffer, + bForceSingleThread, + LocalPosition, + Size, + SquaredRadius, + CenterMultiplier, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + NumIterations, + GetStrength); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, T& Value) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadius) + { + TInterpolator NewValue = FVoxelUtilities::Get3D(SrcBuffer, Size, X, Y, Z, Bounds.Min); + SetInterpolator(NewValue, Value); + } + }); +} + +template +void FVoxelSphereToolsImpl::ApplyMaterialKernelSphereImpl( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + uint32 Mask, + T GetStrength) +{ + ApplyKernelSphereImpl>( + Data, + [&](const FVoxelMaterial& Material) + { + TVoxelInterpolator Interpolator; + + int32 Index = 0; + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + if (Mask & (1 << Channel)) + { + Interpolator.Data[Index++] = Material[Channel]; + } + } + ensureVoxelSlow(Index == NumChannels); + + return Interpolator; + }, + [&](const TVoxelInterpolator& Interpolator, FVoxelMaterial& Material) + { + int32 Index = 0; + for (int32 Channel = 0; Channel < FVoxelMaterial::NumChannels; Channel++) + { + if (Mask & (1 << Channel)) + { + Material[Channel] = Interpolator.Data[Index++]; + } + } + ensureVoxelSlow(Index == NumChannels); + }, + Position, + Radius, + CenterMultiplier, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + NumIterations, + GetStrength); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSphereToolsImpl::SetValueSphere(TData& Data, const FVoxelVector& Position, float Radius, FVoxelValue Value) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + const float SquaredRadius = FMath::Square(Radius); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& OldValue) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadius) + { + OldValue = Value; + } + }); +} + +template +void FVoxelSphereToolsImpl::SetMaterialSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + const FVoxelPaintMaterial& PaintMaterial, + float Strength, + EVoxelFalloff FalloffType, + float Falloff) +{ + FVoxelUtilities::DispatchFalloff(FalloffType, Radius, Falloff, [&](auto GetFalloff) + { + SetMaterialSphereImpl(Data, Position, Radius, PaintMaterial, [&](float Distance) { return Strength * GetFalloff(Distance); }); + }); +} + +template +void FVoxelSphereToolsImpl::ApplyKernelSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + T GetStrength) +{ + // ApplyKernelSphereImpl compiles 2x faster, but is 2x slower at runtime for values +#if 0 + ApplyKernelSphereImpl>( + Data, + [](FVoxelValue Value) + { + TVoxelInterpolator<1> Interpolator; + Interpolator.Data[0] = Value.ToFloat(); + return Interpolator; + }, + [](TVoxelInterpolator<1> Interpolator, FVoxelValue& Value) + { + Value = FVoxelValue(Interpolator.Data[0]); + }, + Position, + Radius, + CenterMultiplier, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + NumIterations, + GetStrength); +#else + VOXEL_SPHERE_TOOL_IMPL(); + + if (NumIterations <= 0) + { + return; + } + + const float SquaredRadius = FMath::Square(Radius); + const FIntVector Size = Bounds.Size(); + + const auto GetNewValue = [&](auto& GetValue, float Distance, int32 X, int32 Y, int32 Z) + { + const auto GetNeighbor = [&](int32 DX, int32 DY, int32 DZ) + { + const int32 Degree = FMath::Abs(DX) + FMath::Abs(DY) + FMath::Abs(DZ); + const float Multiplier = + Degree == 1 ? FirstDegreeNeighborMultiplier + : Degree == 2 ? SecondDegreeNeighborMultiplier + : ThirdDegreeNeighborMultiplier; + + return GetValue(X + DX, Y + DY, Z + DZ) * Multiplier; + }; + + const float NeighborSum = + GetNeighbor(-1, -1, -1) + + GetNeighbor(+0, -1, -1) + + GetNeighbor(+1, -1, -1) + + GetNeighbor(-1, +0, -1) + + GetNeighbor(+0, +0, -1) + + GetNeighbor(+1, +0, -1) + + GetNeighbor(-1, +1, -1) + + GetNeighbor(+0, +1, -1) + + GetNeighbor(+1, +1, -1) + + GetNeighbor(-1, -1, +0) + + GetNeighbor(+0, -1, +0) + + GetNeighbor(+1, -1, +0) + + GetNeighbor(-1, +0, +0) + + GetNeighbor(+1, +0, +0) + + GetNeighbor(-1, +1, +0) + + GetNeighbor(+0, +1, +0) + + GetNeighbor(+1, +1, +0) + + GetNeighbor(-1, -1, +1) + + GetNeighbor(+0, -1, +1) + + GetNeighbor(+1, -1, +1) + + GetNeighbor(-1, +0, +1) + + GetNeighbor(+0, +0, +1) + + GetNeighbor(+1, +0, +1) + + GetNeighbor(-1, +1, +1) + + GetNeighbor(+0, +1, +1) + + GetNeighbor(+1, +1, +1); + + const float OldValue = GetValue(X, Y, Z); + + return FMath::Lerp(OldValue, NeighborSum + OldValue * CenterMultiplier, GetStrength(Distance)); + }; + + const TArray Values = GetActualData(Data).GetValues(Bounds); + const auto GetValue = [&](int32 X, int32 Y, int32 Z) { return FVoxelUtilities::Get3D(Values, Size, X, Y, Z, Bounds.Min).ToFloat(); }; + + if (NumIterations > 1) + { + TArray Buffer; + Buffer.SetNumUninitialized(Values.Num() * 2); + + // Only make one allocation + float* RESTRICT const BufferA = Buffer.GetData(); + float* RESTRICT const BufferB = Buffer.GetData() + Values.Num(); + + const auto Step = [&](auto Src, auto Dst) + { + Bounds.ParallelIterate([&](int32 X, int32 Y, int32 Z) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + + if (SquaredDistance <= SquaredRadius) + { + Dst(X, Y, Z) = GetNewValue(Src, FMath::Sqrt(SquaredDistance), X, Y, Z); + } + else + { + Dst(X, Y, Z) = Src(X, Y, Z); + } + }, !IsDataMultiThreaded(Data)); + }; + + const auto GetA = FVoxelUtilities::Create3DGetter(BufferA, Size, Bounds.Min); + const auto GetB = FVoxelUtilities::Create3DGetter(BufferB, Size, Bounds.Min); + + Step(GetValue, GetA); + + bool bSrcIsA = true; + // -1: last one is done in the SetValue + for (int32 Iteration = 1; Iteration < NumIterations - 1; Iteration++) + { + if (bSrcIsA) + { + Step(GetA, GetB); + } + else + { + Step(GetB, GetA); + } + bSrcIsA = !bSrcIsA; + } + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadius) + { + if (bSrcIsA) + { + Value = FVoxelValue(GetNewValue(GetA, FMath::Sqrt(SquaredDistance), X, Y, Z)); + } + else + { + Value = FVoxelValue(GetNewValue(GetB, FMath::Sqrt(SquaredDistance), X, Y, Z)); + } + } + }); + } + else + { + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadius) // Kinda hacky: assume this is false for at least a 1-voxel thick border, making it safe to query neighbors + { + Value = FVoxelValue(GetNewValue(GetValue, FMath::Sqrt(SquaredDistance), X, Y, Z)); + } + }); + } +#endif +} + +template +void FVoxelSphereToolsImpl::ApplyMaterialKernelSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float CenterMultiplier, + float FirstDegreeNeighborMultiplier, + float SecondDegreeNeighborMultiplier, + float ThirdDegreeNeighborMultiplier, + int32 NumIterations, + uint32 Mask, + T GetStrength) +{ + Mask &= FVoxelMaterial::ChannelsMask; + +#if VOXEL_ENABLE_SLOW_OPTIMIZATIONS + const int32 NumChannels = FMath::CountBits(Mask); + if (NumChannels == 0) return; + ensure(NumChannels <= FVoxelMaterial::NumChannels); + + FVoxelUtilities::TStaticSwitch::Switch(NumChannels - 1, [&](auto Num) + { + ApplyMaterialKernelSphereImpl( + Data, + Position, + Radius, + CenterMultiplier, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + NumIterations, + Mask, + GetStrength); + }); +#else + ApplyMaterialKernelSphereImpl( + Data, + Position, + Radius, + CenterMultiplier, + FirstDegreeNeighborMultiplier, + SecondDegreeNeighborMultiplier, + ThirdDegreeNeighborMultiplier, + NumIterations, + Mask, + GetStrength); +#endif +} + +template +void FVoxelSphereToolsImpl::SmoothSphere(TData& Data, const FVoxelVector& Position, float Radius, float Strength, int32 NumIterations, EVoxelFalloff FalloffType, float Falloff) +{ + float CenterStrength = 1; + float NeighborsStrength = Strength; + const float Sum = 26 * NeighborsStrength + CenterStrength; + CenterStrength /= Sum; + NeighborsStrength /= Sum; + + FVoxelUtilities::DispatchFalloff(FalloffType, Radius, Falloff, [&](auto GetFalloff) + { + ApplyKernelSphere( + Data, + Position, + Radius, + CenterStrength, + NeighborsStrength, + NeighborsStrength, + NeighborsStrength, + NumIterations, + GetFalloff); + }); +} + +template +void FVoxelSphereToolsImpl::SmoothMaterialSphere( + TData& Data, + const FVoxelVector& Position, + float Radius, + float Strength, + int32 NumIterations, + uint32 Mask, + EVoxelFalloff FalloffType, + float Falloff) +{ + float CenterStrength = 1; + float NeighborsStrength = Strength; + const float Sum = 26 * NeighborsStrength + CenterStrength; + CenterStrength /= Sum; + NeighborsStrength /= Sum; + + FVoxelUtilities::DispatchFalloff(FalloffType, Radius, Falloff, [&](auto GetFalloff) + { + ApplyMaterialKernelSphere( + Data, + Position, + Radius, + CenterStrength, + NeighborsStrength, + NeighborsStrength, + NeighborsStrength, + NumIterations, + Mask, + GetFalloff); + }); +} + +template +void FVoxelSphereToolsImpl::TrimSphere(TData& Data, const FVoxelVector& Position, const FVector& Normal, float Radius, float Falloff, bool bAdditive) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + const float RelativeRadius = Radius * (1.f - Falloff); + const float RelativeFalloff = Radius * Falloff; + + const FPlane Plane(Position.ToFloat(), Normal); + const float SquaredRadiusFalloff = FMath::Square(RelativeRadius + RelativeFalloff + 2); + + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float SquaredDistance = FVector(X - Position.X, Y - Position.Y, Z - Position.Z).SizeSquared(); + if (SquaredDistance <= SquaredRadiusFalloff) + { + const float Distance = FMath::Sqrt(SquaredDistance); + const float PlaneSDF = Plane.PlaneDot(FVector(X, Y, Z)); + const float SphereSDF = Distance - RelativeRadius - RelativeFalloff; + if (bAdditive) + { + const float SDF = FVoxelSDFUtilities::opSmoothIntersection(PlaneSDF, SphereSDF, RelativeFalloff); + Value = FMath::Min(Value, FVoxelValue(SDF)); + } + else + { + const float SDF = -FVoxelSDFUtilities::opSmoothIntersection(-PlaneSDF, SphereSDF, RelativeFalloff); + Value = FMath::Max(Value, FVoxelValue(SDF)); + } + } + }); +} + +template +void FVoxelSphereToolsImpl::RevertSphere(TData& InData, const FVoxelVector& Position, float Radius, int32 HistoryPosition, bool bRevertValues, bool bRevertMaterials) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + auto& Data = GetActualData(InData); + const float RadiusSquared = FMath::Square(Radius); + + FVoxelOctreeUtilities::IterateTreeInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Chunk) + { + if (!Chunk.IsLeaf()) + { + return; + } + + ensureThreadSafe(Chunk.IsLockedForWrite()); + auto& Leaf = Chunk.AsLeaf(); + + const auto ApplyForType = [&](auto TypeInst, auto SetValue) + { + using Type = decltype(TypeInst); + + if (!Leaf.GetData().IsDirty() || !Leaf.UndoRedo.IsValid()) + { + return; + } + + TVoxelStaticArray IsValueSet; + IsValueSet.Memzero(); + + TVoxelStaticArray Values; + Leaf.GetData().CopyTo(Values.GetData()); + + const auto& Stack = Leaf.UndoRedo->GetFramesStack(); + for (int32 Index = Stack.Num() - 1; Index >= 0; --Index) + { + if (Stack[Index]->HistoryPosition < HistoryPosition) break; + + for (auto& Value : FVoxelUtilities::TValuesMaterialsSelector::Get(*Stack[Index])) + { + IsValueSet[Value.Index] = true; + Values[Value.Index] = Value.Value; + } + } + + const FIntVector Min = Leaf.GetMin(); + + FVoxelDataOctreeSetter::Set(Data, Leaf, [&](auto Lambda) + { + Leaf.GetBounds().Iterate(Lambda); + }, + [&](int32 X, int32 Y, int32 Z, Type& Value) + { + const float DistanceSquared = (FVector(X, Y, Z) - Position).SizeSquared(); + if (DistanceSquared < RadiusSquared) + { + const uint32 Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(Min, X, Y, Z); + if (IsValueSet[Index]) + { + SetValue(DistanceSquared, Value, Values[Index]); + } + } + }); + }; + + if (bRevertValues) + { + ApplyForType(FVoxelValue(), [&](float DistanceSquared, FVoxelValue& Value, const FVoxelValue& NewValue) + { + //const float Alpha = FMath::Sqrt(DistanceSquared) / Radius; + const float Strength = 1; // TODO use Alpha + Value = FVoxelValue(FMath::Lerp(Value.ToFloat(), NewValue.ToFloat(), Strength)); + }); + } + if (bRevertMaterials) + { + ApplyForType(FVoxelMaterial(), [&](float DistanceSquared, FVoxelMaterial& Value, const FVoxelMaterial& NewValue) + { + Value = NewValue; + }); + } + }); +} + +template +void FVoxelSphereToolsImpl::RevertSphereToGenerator(TData& InData, const FVoxelVector& Position, float Radius, bool bRevertValues, bool bRevertMaterials) +{ + VOXEL_SPHERE_TOOL_IMPL(); + + auto& Data = GetActualData(InData); + const float RadiusSquared = FMath::Square(Radius); + + FVoxelOctreeUtilities::IterateTreeInBounds(Data.GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Chunk) + { + if (!Chunk.IsLeaf()) + { + return; + } + + ensureThreadSafe(Chunk.IsLockedForWrite()); + auto& Leaf = Chunk.AsLeaf(); + + const auto ApplyForType = [&](auto TypeInst) + { + using Type = decltype(TypeInst); + + auto& DataHolder = Leaf.GetData(); + if (!DataHolder.IsDirty()) + { + return; + } + + TVoxelStaticArray Values; + TVoxelQueryZone QueryZone(Leaf.GetBounds(), Values); + Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); + + const FIntVector Min = Leaf.GetMin(); + + bool bAllGeneratorValues = true; + + FVoxelDataOctreeSetter::Set(Data, Leaf, [&](auto Lambda) + { + Leaf.GetBounds().Iterate(Lambda); + }, + [&](int32 X, int32 Y, int32 Z, Type& Value) + { + const float DistanceSquared = (FVector(X, Y, Z) - Position).SizeSquared(); + const int32 Index = FVoxelDataOctreeUtilities::IndexFromGlobalCoordinates(Min, X, Y, Z); + if (DistanceSquared < RadiusSquared) + { + Value = Values[Index]; + } + else + { + bAllGeneratorValues &= (Value == Values[Index]); + } + }); + + if (bAllGeneratorValues) + { + DataHolder.ClearData(Data); + } + }; + + if (bRevertValues) + { + ApplyForType(FVoxelValue()); + } + if (bRevertMaterials) + { + ApplyForType(FVoxelMaterial()); + } + }); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h new file mode 100644 index 00000000..f0aba3fc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" + +struct FVoxelHardnessHandler; +struct FVoxelSurfaceEditsVoxel; +struct FVoxelSurfaceEditsProcessedVoxels; + +/** + * @dependency VoxelTools/VoxelSurfaceEdits.h + */ +class VOXEL_API FVoxelSurfaceEditToolsImpl : public FVoxelToolsBaseImpl +{ +public: + static FVoxelIntBox GetBounds(const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels); + static bool ShouldCompute(const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels); + +public: + // Bounds need to encompass Bounds(Voxels).Extend(0, 0, MaxStrength + DistanceDivisor + 2)! + template + static void EditVoxelValues2D( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const TArray& Voxels, + float DistanceDivisor); + +public: + template + static void Edit( + TData& Data, + const FVoxelIntBox& Bounds, + const TArray& Voxels, + TLambda Lambda); + + template + static void EditVoxelValues( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const TArray& Voxels, + float DistanceDivisor, + bool bHasValues); + + template + static void EditVoxelMaterials( + TData& Data, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const TArray& Voxels); + + template + static void PropagateVoxelMaterials( + TData& Data, + const TArray& Voxels); + +public: + /** + * Apply processed voxels strengths to the voxel values + * @param HardnessHandler Hardness handler @Internal + * @param Bounds Bounds. See EditVoxelValues2D comment. @Internal + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack @GetBounds @ShouldCompute + * @param DistanceDivisor Distance divisor: the new value will be divided by this. Useful if normals are bad because of clamped values @Advanced + * @see ApplyStack, AddToStack + * @ExportSetValue + */ + template + static void EditVoxelValues( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor = 1.f); + + /** + * Apply paint material to the processed voxels, using the processed strengths + * @param Bounds Bounds. See EditVoxelValues2D comment. @Internal + * @param PaintMaterial The paint material to apply + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack @GetBounds @ShouldCompute + * @see ApplyStack, AddToStack + * @ExportSetMaterial + */ + template + static void EditVoxelMaterials( + TData& Data, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels); + + /** + * Propagate the materials of the voxels, so that the new surface is painted correctly + * Must be called BEFORE EditVoxelValues! + * @param ProcessedVoxels The processed voxels, usually obtained by applying a surface edit stack @GetBounds @ShouldCompute + * @see ApplyStack, AddToStack + * @check ProcessedVoxels.Info.bHasSurfacePositions PropagateVoxelMaterials needs surface positions! Use FindSurfaceVoxelsFromDistanceField + * @ExportSetMaterial + */ + template + static void PropagateVoxelMaterials( + TData& Data, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl new file mode 100644 index 00000000..86bd1a9f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl @@ -0,0 +1,251 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h" +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" +#include "VoxelTools/VoxelSurfaceEdits.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" + +#define VOXEL_SURFACE_TOOL_IMPL() VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + +inline FVoxelIntBox FVoxelSurfaceEditToolsImpl::GetBounds(const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels) +{ + return ProcessedVoxels.Bounds; +} + +inline bool FVoxelSurfaceEditToolsImpl::ShouldCompute(const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels) +{ + return ProcessedVoxels.Voxels->Num() > 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSurfaceEditToolsImpl::EditVoxelValues2D( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const TArray& Voxels, + float DistanceDivisor) +{ + VOXEL_SURFACE_TOOL_IMPL(); + + auto* ModifiedValues = GetModifiedValues(Data); + + FVoxelMutableDataAccelerator Accelerator(GetActualData(Data), Bounds); + + for (auto& Voxel : Voxels) + { + const FVoxelValue Value = Accelerator.GetValue(Voxel.Position, 0); + if (!ensureVoxelSlow(!Value.IsEmpty())) continue; // Shouldn't be empty if it's a 2D edit position + const FVoxelValue ValueAbove = Accelerator.GetValue(Voxel.Position + FIntVector(0, 0, 1), 0); + if (!ensureVoxelSlow(ValueAbove.IsEmpty())) continue; + + const float VoxelHeight = FVoxelUtilities::GetAbsDistanceFromDensities(Value.ToFloat(), ValueAbove.ToFloat()); + + float Strength = Voxel.Strength; + if (HardnessHandler.NeedsToCompute()) + { + Strength /= HardnessHandler.GetHardness(Accelerator.GetMaterial(Voxel.Position, 0)); + } + + // -: we want a negative strength to add + const float Height = Voxel.Position.Z + VoxelHeight - Strength; + + const bool bIsAdding = Strength < 0; + + const int32 A = FMath::FloorToInt(Voxel.Position.Z - DistanceDivisor); + const int32 B = FMath::CeilToInt(Voxel.Position.Z + DistanceDivisor); + const int32 C = FMath::FloorToInt(Height - DistanceDivisor); + const int32 D = FMath::CeilToInt(Height + DistanceDivisor); + const int32 Start = FMath::Min(FMath::Min(A, B), FMath::Min(C, D)); + const int32 End = FMath::Max(FMath::Max(A, B), FMath::Max(C, D)); + + for (int32 Z = Start; Z <= End; Z++) + { + Accelerator.EditValue(Voxel.Position.X, Voxel.Position.Y, Z, [&](FVoxelValue& CurrentValue) + { + const float FloatValue = CurrentValue.ToFloat(); + const float WantedValue = (Z - Height) / DistanceDivisor; + + if ((bIsAdding && WantedValue < FloatValue) || + (!bIsAdding && WantedValue > FloatValue)) + { + const FVoxelValue OldValue = CurrentValue; + CurrentValue = FVoxelValue(WantedValue); + + if (ModifiedValues) + { + ModifiedValues->Add({ Voxel.Position, OldValue, CurrentValue }); + } + } + }); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSurfaceEditToolsImpl::Edit(TData& Data, const FVoxelIntBox& Bounds, const TArray& Voxels, TLambda Lambda) +{ + auto* ModifiedValues = GetModifiedValues(Data); + if (ModifiedValues) + { + ModifiedValues->SetNumUninitialized(Voxels.Num()); + } + + TArray ElementsToRemove; + + FVoxelUtilities::ParallelFor_PerThreadData(Voxels.Num(), [&]() + { + return MakeUnique(GetActualData(Data), Bounds); + }, + [&](const TUniquePtr& Accelerator, int32 Index) + { + const FVoxelSurfaceEditsVoxel& Voxel = Voxels[Index]; + const bool bSet = Accelerator->Edit(Voxel.Position, [&](T& Value) + { + const auto OldValue = Value; + Lambda(*Accelerator, Voxel, Value); + + if (ModifiedValues) + { + (*ModifiedValues)[Index] = { Voxel.Position, OldValue, Value }; + } + }); + + if (!bSet && ModifiedValues) + { + ElementsToRemove.Add(Index); + } + }, !IsDataMultiThreaded(Data) || true); // TODO multithreading doesn't work because a single data chunk might be accessed by different threads + + for (int32 Index = ElementsToRemove.Num() - 1; Index >= 0; --Index) + { + ModifiedValues->RemoveAtSwap(ElementsToRemove[Index]); + } +} + +template +void FVoxelSurfaceEditToolsImpl::EditVoxelValues( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const TArray& Voxels, + float DistanceDivisor, + bool bHasValues) +{ + VOXEL_SURFACE_TOOL_IMPL(); + + Edit(Data, Bounds, Voxels, [&](FVoxelMutableDataAccelerator& Accelerator, const FVoxelSurfaceEditsVoxel& Voxel, FVoxelValue& Value) + { + const float OldValue = bHasValues ? Voxel.Value : Value.ToFloat(); + float Strength = Voxel.Strength; + if (HardnessHandler.NeedsToCompute()) + { + Strength /= HardnessHandler.GetHardness(Accelerator.GetMaterial(Voxel.Position, 0)); + } + const float NewValue = OldValue + Strength; + Value = FVoxelValue(NewValue / DistanceDivisor); + }); +} + +template +void FVoxelSurfaceEditToolsImpl::EditVoxelMaterials( + TData& Data, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const TArray& Voxels) +{ + VOXEL_SURFACE_TOOL_IMPL(); + + Edit(Data, Bounds, Voxels, [&](FVoxelMutableDataAccelerator& Accelerator, const FVoxelSurfaceEditsVoxel& Voxel, FVoxelMaterial& Material) + { + PaintMaterial.ApplyToMaterial(Material, Voxel.Strength); + }); +} + +template +void FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials( + TData& Data, + const TArray& Voxels) +{ + VOXEL_SURFACE_TOOL_IMPL(); + + // Note: the reads are only safe because surface positions are all in the bounds (since they are computed by flood jump) + + FVoxelMutableDataAccelerator Accelerator(GetActualData(Data)); + for (auto& Voxel : Voxels) + { + FIntVector ClosestPosition; + { + v_flt Distance = MAX_vflt; + for (auto& Neighbor : FVoxelUtilities::GetNeighbors(Voxel.SurfacePosition)) + { + const FVoxelValue Value = Accelerator.GetValue(Neighbor, 0); + if (!Value.IsEmpty()) + { + const v_flt PointDistance = (FVoxelVector(Neighbor) - Voxel.SurfacePosition).SizeSquared(); + if (PointDistance < Distance) + { + Distance = PointDistance; + ClosestPosition = Neighbor; + } + } + } + if (!ensureVoxelSlow(Distance < MAX_vflt)) + { + continue; + } + } + + Accelerator.SetMaterial(Voxel.Position, Accelerator.GetMaterial(ClosestPosition, 0)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +void FVoxelSurfaceEditToolsImpl::EditVoxelValues( + TData& Data, + const FVoxelHardnessHandler& HardnessHandler, + const FVoxelIntBox& Bounds, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float DistanceDivisor) +{ + if (ProcessedVoxels.Info.bIs2D) + { + EditVoxelValues2D(Data, HardnessHandler, Bounds, *ProcessedVoxels.Voxels, DistanceDivisor); + } + else + { + EditVoxelValues(Data, HardnessHandler, Bounds, *ProcessedVoxels.Voxels, DistanceDivisor, ProcessedVoxels.Info.bHasValues); + } +} + +template +void FVoxelSurfaceEditToolsImpl::EditVoxelMaterials( + TData& Data, + const FVoxelIntBox& Bounds, + const FVoxelPaintMaterial& PaintMaterial, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels) +{ + EditVoxelMaterials(Data, Bounds, PaintMaterial, *ProcessedVoxels.Voxels); +} + +template +void FVoxelSurfaceEditToolsImpl::PropagateVoxelMaterials( + TData& Data, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels) +{ + ensure(ProcessedVoxels.Info.bHasSurfacePositions); + PropagateVoxelMaterials(Data, *ProcessedVoxels.Voxels); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.h new file mode 100644 index 00000000..84fc7a0c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.h @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelValue.h" +#include "VoxelIntBox.h" +#include "VoxelData/VoxelDataImpl.h" + +class AVoxelWorld; +class FVoxelData; + +struct FVoxelIntBox; +struct FVoxelVector; +struct FModifiedVoxelValue; +struct FVoxelPaintMaterial; + +class VOXEL_API FVoxelToolsBaseImpl +{ +public: + template + static bool IsDataMultiThreaded(const TData& Data); + + template + static FVoxelData& GetActualData(TData& Data); + + template + static auto* GetModifiedValues(TData& Data); + + template + static auto* GetModifiedValues(TVoxelDataImpl& Data); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.inl new file mode 100644 index 00000000..6f74ebcd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.inl @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelTools/Impl/VoxelToolsBaseImpl.h" +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelTools/VoxelHardnessHandler.h" +#include "VoxelData/VoxelDataIncludes.h" + +struct VOXEL_API FScopeToolsTimeLogger +{ + const char* Name; + const int64 NumVoxels; + const double StartTime = FPlatformTime::Seconds(); + + explicit FScopeToolsTimeLogger(const char* Name, int64 NumVoxels) + : Name(Name) + , NumVoxels(NumVoxels) + { + } + ~FScopeToolsTimeLogger(); +}; + +#define VOXEL_TOOL_FUNCTION_COUNTER(Num) FScopeToolsTimeLogger PREPROCESSOR_JOIN(EditCounter, __LINE__)(__FUNCTION__, Num); VOXEL_ASYNC_FUNCTION_COUNTER() +#define VOXEL_TOOL_SCOPE_COUNTER(Name, Num) FScopeToolsTimeLogger PREPROCESSOR_JOIN(EditCounter, __LINE__)(Name, Num); VOXEL_ASYNC_SCOPE_COUNTER(Name) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +bool FVoxelToolsBaseImpl::IsDataMultiThreaded(const TData& Data) +{ + return Data.bMultiThreadedEdits; +} + +template<> +inline bool FVoxelToolsBaseImpl::IsDataMultiThreaded(const FVoxelData& Data) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +FVoxelData& FVoxelToolsBaseImpl::GetActualData(TData& Data) +{ + return Data.Data; +} + +template<> +inline FVoxelData& FVoxelToolsBaseImpl::GetActualData(FVoxelData& Data) +{ + return Data; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +auto* FVoxelToolsBaseImpl::GetModifiedValues(TData& Data) +{ + return static_cast*>(nullptr); +} + +template +auto* FVoxelToolsBaseImpl::GetModifiedValues(TVoxelDataImpl& Data) +{ + return Data.bRecordModifiedValues ? &Data.ModifiedValues : nullptr; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelFlattenTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelFlattenTool.h new file mode 100644 index 00000000..9af3155e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelFlattenTool.h @@ -0,0 +1,67 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelFlattenTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelFlattenTool : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "1")) + float Strength = 0.1; + + // If true, the plane used for flatten will be the same while clicking + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bFreezeOnClick = false; + + // Use Average Position & Normal + // If true, use linetraces to find average position/normal under the cursor + // If false, use a single linetrace from the cursor + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bUseAverage = true; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (InlineEditConditionToggle)) + bool bUseFixedRotation = false; + + // Override the normal by a fixed rotation + // The rotation is apply to Up Vector to find the plane normal + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bUseFixedRotation")) + FRotator FixedRotation { ForceInit }; + + // If true, will propagate materials so that the surface stays correctly painted. Expensive. + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bPropagateMaterials = true; + +public: + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite) + bool bEnableFalloff = true; + + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bEnableFalloff")) + EVoxelFalloff FalloffType = EVoxelFalloff::Smooth; + + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bEnableFalloff", UIMin = "0", UIMax = "1")) + float Falloff = 0.5; + +public: + UVoxelFlattenTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void Tick() override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface + +private: + FVector LastClickFlattenPosition = FVector::ZeroVector; + FVector LastClickFlattenNormal = FVector::UpVector; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelLevelTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelLevelTool.h new file mode 100644 index 00000000..2989862b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelLevelTool.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelLevelTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelLevelTool : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UStaticMesh* CylinderMesh = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "1")) + float Falloff = 0.1; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "10000")) + float Height = 1000.f; + + // Offset, relative to the height + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "1")) + float Offset = 1.f; + + // Relative to the radius + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin=0, UIMax=1)) + float Stride = 0.f; + +public: + UVoxelLevelTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void Tick() override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface + +private: + FVector GetToolOffset() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelMeshTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelMeshTool.h new file mode 100644 index 00000000..82611b88 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelMeshTool.h @@ -0,0 +1,191 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssets/VoxelDataAssetData.h" +#include "VoxelImporters/VoxelMeshImporter.h" +#include "VoxelTools/Tools/VoxelToolWithAlignment.h" +#include "VoxelMeshTool.generated.h" + +class UTextureRenderTarget2D; + +UCLASS() +class VOXEL_API UVoxelMeshTool : public UVoxelToolWithAlignment +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + UStaticMesh* Mesh = nullptr; + + // Relative to the radius + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin=0, UIMax=1)) + float Stride = 0.f; + + // Do a smooth import by converting the voxel densities & the mesh to true distance fields, and doing a smooth union/subtraction on these + // NOTE: Disabled if bProgressiveStamp = true + // NOTE: Will disable bImportColorsFromMesh/bImportUVsFromMesh + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "!bProgressiveStamp")) + bool bSmoothImport = false; + + // Relative to radius + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bSmoothImport && !bProgressiveStamp", UIMin = 0, UIMax = 1)) + float Smoothness = 0.5f; + + // Will slowly grow/shrink the surface towards the mesh + // NOTE: Will disable SmoothImport + // NOTE: Will disable bImportColorsFromMesh/bImportUVsFromMesh + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bProgressiveStamp = false; + + // Speed of the progressive stamp + // Make sure your mesh is intersecting the voxel world! + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bProgressiveStamp", UIMin = 0, UIMax = 1)) + float Speed = 0.1f; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + FVoxelMeshImporterSettingsBase MeshImporterSettings; + +public: + UPROPERTY(Category = "Sculpt Settings", EditAnywhere, BlueprintReadWrite) + bool bSculpt = true; + +public: + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite) + bool bPaint = true; + + // Use to restrict editing on some channels + UPROPERTY(Category = "Paint Settings", AdvancedDisplay, EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) + int32 PaintMask = EVoxelMaterialMask::All; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", ShowForMaterialConfigs = "RGB, SingleIndex")) + bool bPaintColors = true; + + // Import the colors directly from the mesh by sampling ColorsMaterial at the mesh UVs + // NOTE: Will be disabled if bSmoothImport = true or bProgressiveStamp = true + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSmoothImport && !bProgressiveStamp && bPaintColors", ShowForMaterialConfigs = "RGB, SingleIndex")) + bool bImportColorsFromMesh = true; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSmoothImport && !bProgressiveStamp && bPaintColors && bImportColorsFromMesh", ShowForMaterialConfigs = "RGB, SingleIndex")) + UMaterialInterface* ColorsMaterial = nullptr; + + // Used if bImportColorsFromMesh = false + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && bPaintColors", ShowForMaterialConfigs = "RGB, SingleIndex")) + FColor ColorToPaint = FColor::White; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", ShowForMaterialConfigs = "RGB, SingleIndex")) + bool bPaintUVs = true; + + // Import the uvs directly from the mesh by sampling UVsMaterial at the mesh UVs + // NOTE: Will be disabled if bSmoothImport = true or bProgressiveStamp = true + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSmoothImport && !bProgressiveStamp && bPaintUVs", ShowForMaterialConfigs = "RGB, SingleIndex")) + bool bImportUVsFromMesh = true; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSmoothImport && !bProgressiveStamp && bPaintUVs", ShowForMaterialConfigs = "RGB, SingleIndex")) + UMaterialInterface* UVsMaterial = nullptr; + + // Used if bImportUVsFromMesh = false + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && bPaintUVs", ShowForMaterialConfigs = "RGB, SingleIndex")) + FVector2D UV0ToPaint = FVector2D::ZeroVector; + + // Used if bImportUVsFromMesh = false + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && bPaintUVs", ShowForMaterialConfigs = "RGB, SingleIndex")) + FVector2D UV1ToPaint = FVector2D::ZeroVector; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", ShowForMaterialConfigs = "SingleIndex, MultiIndex")) + bool bPaintIndex = false; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && bPaintIndex", ShowForMaterialConfigs = "SingleIndex, MultiIndex")) + uint8 IndexToPaint = 0; + + // For debug + UPROPERTY(Category = "Paint Settings", AdvancedDisplay, VisibleAnywhere, BlueprintReadOnly, Transient, meta = (ShowForMaterialConfigs = "RGB, SingleIndex")) + UTextureRenderTarget2D* UVsRenderTarget = nullptr; + + // For debug + UPROPERTY(Category = "Paint Settings", AdvancedDisplay, VisibleAnywhere, BlueprintReadOnly, Transient, meta = (ShowForMaterialConfigs = "RGB, SingleIndex")) + UTextureRenderTarget2D* ColorsRenderTarget = nullptr; + + UPROPERTY(Category = "Paint Settings", AdvancedDisplay, EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSmoothImport && !bProgressiveStamp"), meta = (ShowForMaterialConfigs = "RGB, SingleIndex")) + int32 RenderTargetSize = 4096; + +public: + // Relative to the size of the mesh + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite, meta = (UIMin = -1, UIMax = 1)) + FVector PositionOffset = FVector::ZeroVector; + + // If false the mesh scale will be set to match the radius + // If true the mesh scale will ignore the radius, and only use the scale below + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite) + bool bAbsoluteScale = false; + + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite) + FVector Scale = FVector::OneVector; + + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite) + bool bAlignToNormal = true; + + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite) + bool bAlignToMovement = true; + + // Applied after position and scale offset + UPROPERTY(Category = "Transform", EditAnywhere, BlueprintReadWrite) + FRotator RotationOffset = FRotator::ZeroRotator; + +public: + UVoxelMeshTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface + +private: + struct FMeshData + { + TWeakObjectPtr StaticMesh; + FVoxelMeshImporterInputData Data; + FBox Bounds; + }; + TUniquePtr CachedMeshData; + + const FMeshData* GetMeshData(); + void GetTransform( + const FMeshData& MeshData, + FVector& OutMeshScale, + FTransform& OutTransformNoTranslation, + FTransform& OutTransformWithTranslation) const; + +private: + struct FAssetData + { + FTransform Transform; + FVoxelDataAssetData Data; + FIntVector PositionOffset = FIntVector::ZeroValue; + }; + struct FDistanceFieldData + { + FTransform Transform; + float BoxExtension = 0; + FVoxelMeshImporterSettingsBase ImporterSettings; + + TArray Data; + TArray SurfacePositions; + FIntVector Size; + FIntVector PositionOffset = FIntVector::ZeroValue; + }; + TUniquePtr AssetData; + TUniquePtr DistanceFieldData; + + UPROPERTY(Transient) + FVoxelMeshImporterRenderTargetCache RenderTargetCache; + + UPROPERTY(Transient) + FVoxelMeshImporterSettings AssetData_ImporterSettings; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelRevertTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelRevertTool.h new file mode 100644 index 00000000..7c1e10c7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelRevertTool.h @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelSphereToolBase.h" +#include "VoxelRevertTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelRevertTool : public UVoxelSphereToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bRevertValues = true; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bRevertMaterials = false; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + int32 HistoryPosition = 0; + + UPROPERTY(Category = "Tool Settings", VisibleAnywhere, BlueprintReadOnly) + int32 CurrentHistoryPosition = 0; + +public: + UVoxelRevertTool(); + + //~ Begin UVoxelToolBase Interface + virtual void Tick() override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSmoothTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSmoothTool.h new file mode 100644 index 00000000..bee64823 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSmoothTool.h @@ -0,0 +1,56 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterial.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelSmoothTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelSmoothTool : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bSculpt = true; + + // Doesn't work with multi index yet + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = 0, UIMax = 1)) + bool bPaint = false; + + // Which channels to smooth + // In Single Index Alpha will be automatically disabled + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", Bitmask, BitmaskEnum = EVoxelMaterialMask_BP)) + int32 PaintMask = EVoxelMaterialMask::All; + + // NumIterations also affects strength + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = 0, UIMax = 1)) + float Strength = 1.f; + + // Number of times to apply the smooth in a single frame + // Will be ignored if Shift is pressed + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (ClampMin = 1)) + int32 NumIterations = 10; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + EVoxelFalloff FalloffType = EVoxelFalloff::Smooth; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = 0, UIMax = 1)) + float Falloff = 0.5f; + +public: + UVoxelSmoothTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void Tick() override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereTool.h new file mode 100644 index 00000000..ae77bfb1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereTool.h @@ -0,0 +1,44 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelSphereToolBase.h" +#include "VoxelSphereTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelSphereTool : public UVoxelSphereToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* OverlayMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bSculpt = true; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite) + bool bPaint = false; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", UIMin = "0", UIMax = "1")) + float PaintStrength = 0.5f; + + // Paint falloff type + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSculpt")) + EVoxelFalloff FalloffType = EVoxelFalloff::Smooth; + + // Paint falloff + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint && !bSculpt", UIMin = 0, UIMax = 1)) + float Falloff = 0.5f; + +public: + UVoxelSphereTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereToolBase.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereToolBase.h new file mode 100644 index 00000000..1a29184c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereToolBase.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelToolWithAlignment.h" +#include "VoxelSphereToolBase.generated.h" + +UCLASS(Abstract) +class VOXEL_API UVoxelSphereToolBase : public UVoxelToolWithAlignment +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UStaticMesh* SphereMesh = nullptr; + +public: + UVoxelSphereToolBase(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + //~ End UVoxelToolBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h new file mode 100644 index 00000000..3ad51433 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h @@ -0,0 +1,168 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTexture.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelSurfaceTool.generated.h" + +class UTexture2D; + +UENUM(BlueprintType) +enum class EVoxelSurfaceToolMaskType : uint8 +{ + Texture, + Generator +}; + +USTRUCT(BlueprintType) +struct FVoxelSurfaceToolMask +{ + GENERATED_BODY() + + UPROPERTY(Category = "Mask", EditAnywhere, BlueprintReadWrite) + EVoxelSurfaceToolMaskType Type = EVoxelSurfaceToolMaskType::Texture; + +public: + UPROPERTY(Category = "Mask|Texture", EditAnywhere, BlueprintReadWrite) + UTexture2D* Texture = nullptr; + + UPROPERTY(Category = "Mask|Texture", EditAnywhere, BlueprintReadWrite) + EVoxelRGBA Channel = EVoxelRGBA::R; + +public: + UPROPERTY(Category = "Mask|Generator", EditAnywhere, BlueprintReadWrite) + FVoxelGeneratorPicker Generator; + + UPROPERTY(Category = "Mask|Generator", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + TArray SeedsToRandomize = { "Seed" }; + + UPROPERTY(Category = "Mask|Generator", EditAnywhere, BlueprintReadWrite) + bool bScaleWithBrushSize = true; + + UPROPERTY(Category = "Mask|Generator", VisibleAnywhere, BlueprintReadOnly, AdvancedDisplay, Transient) + UTexture2D* GeneratorDebugTexture = nullptr; + +public: + UPROPERTY(Category = "Mask", EditAnywhere, BlueprintReadWrite, meta = (UIMin = 0.01, UIMax = 10)) + float Scale = 1; + + // ScaleY/ScaleX. MaskScale = ScaleX + UPROPERTY(Category = "Mask", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta = (UIMin = 0, UIMax = 10)) + float Ratio = 1; + +public: + bool HasSameGeneratorSettings(const FVoxelSurfaceToolMask& Other) const + { + return + Generator.GetObject() == Other.Generator.GetObject() && + SeedsToRandomize == Other.SeedsToRandomize && + bScaleWithBrushSize == Other.bScaleWithBrushSize && + Scale == Other.Scale && + Ratio == Other.Ratio; + } +}; + +UCLASS() +class VOXEL_API UVoxelSurfaceTool : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Sculpt Settings", EditAnywhere, BlueprintReadWrite) + bool bSculpt = true; + + // Relative to brush size + UPROPERTY(Category = "Sculpt Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bSculpt", UIMin = "0", UIMax = "1")) + float SculptStrength = 0.5f; + + // If true, will propagate materials so that the surface stays correctly painted. + // Disabled in 2D mode or if Paint is enabled + UPROPERTY(Category = "Sculpt Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "!bPaint && !b2DBrush")) + bool bPropagateMaterials = true; + +public: + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite) + bool bPaint = false; + + UPROPERTY(Category = "Paint Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bPaint", UIMin = "0", UIMax = "1")) + float PaintStrength = 0.5f; + +public: + // Will only affect the topmost voxels + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (DisplayName = "2D Brush")) + bool b2DBrush = false; + + // If true, sculpt/paint strength will be modulated by the distance the mouse travels + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + bool bMovementAffectsStrength = false; + + // Relative to the radius + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin=0, UIMax=1)) + float Stride = 0.f; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (InlineEditConditionToggle)) + bool bAlignToMovement = true; + + // If false, align the tool to the mouse movement + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "!bAlignToMovement")) + FRotator FixedDirection = FRotator::ZeroRotator; + + // If true, strength will be modulated by the time since the last edit so that the results don't depend on the framerate + // Automatically turned off if Stride > 0, or if paint strength = 1 + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bModulateStrengthByDeltaTime = true; + +public: + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite) + bool bEnableFalloff = true; + + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bEnableFalloff")) + EVoxelFalloff FalloffType = EVoxelFalloff::Smooth; + + UPROPERTY(Category = "Falloff", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bEnableFalloff", UIMin = "0", UIMax = "1")) + float Falloff = 0.5; + +public: + UPROPERTY(Category = "Mask", EditAnywhere, BlueprintReadWrite) + bool bUseMask = false; + + UPROPERTY(Category = "Mask", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bUseMask")) + FVoxelSurfaceToolMask Mask; + +public: + UVoxelSurfaceTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void Tick() override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface + +private: + struct FMaskGeneratorCache + { + FVoxelSurfaceToolMask CachedConfig; + float BrushSize = 0.f; // Needed for bScaleWithBrushSize + + TMap Seeds; + + TVoxelTexture VoxelTexture; + }; + FMaskGeneratorCache MaskGeneratorCache; + + UPROPERTY(Transient) + UTexture2D* MaskGeneratorCache_RenderTexture = nullptr; + + bool GetMaskData(bool bShowNotification, TVoxelTexture& OutTexture, float& OutScaleX, float& OutScaleY); + bool ShouldUseMask() const; + + void GetStrengths(float& OutSignedSculptStrength, float& OutSignedPaintStrength) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTool.h new file mode 100644 index 00000000..cef39d58 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTool.h @@ -0,0 +1,329 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "Engine/EngineTypes.h" +#include "Templates/SubclassOf.h" +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelTool.generated.h" + +class AVoxelWorld; +class UStaticMesh; +struct FHitResult; + +struct FVoxelToolKeys +{ + static constexpr const TCHAR* AlternativeMode = TEXT("AlternativeMode"); +}; + +struct FVoxelToolAxes +{ + static constexpr const TCHAR* BrushSize = TEXT("BrushSize"); + static constexpr const TCHAR* Falloff = TEXT("Falloff"); + static constexpr const TCHAR* Strength = TEXT("Strength"); +}; + +USTRUCT(BlueprintType) +struct FVoxelToolTickData +{ + GENERATED_BODY() + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + FVector2D MousePosition = FVector2D(-1, -1); + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + FVector CameraViewDirection = FVector::ForwardVector; + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + bool bEdit = false; + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + TMap Keys; + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + TMap Axes; + + UPROPERTY(Category = "Voxel", EditAnywhere, BlueprintReadWrite) + TEnumAsByte CollisionChannel = ECC_Visibility; + +public: + bool IsKeyDown(FName Key) const + { + return Keys.FindRef(Key); + } + float GetAxis(FName Axis) const + { + return Axes.FindRef(Axis); + } + + bool IsAlternativeMode() const + { + return IsKeyDown(FVoxelToolKeys::AlternativeMode); + } + +public: + bool Deproject(const FVector2D& InScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection) const + { + return ensure(DeprojectLambda) && DeprojectLambda(InScreenPosition, OutWorldPosition, OutWorldDirection); + } + const FVector& GetRayOrigin() const { return RayOrigin; } + const FVector& GetRayDirection() const { return RayDirection; } + +public: + using FDeproject = TFunction; + + void Init(const FDeproject& InDeprojectLambda) + { + //ensure(MousePosition.X >= 0 && MousePosition.Y >= 0); + DeprojectLambda = InDeprojectLambda; + Deproject(MousePosition, RayOrigin, RayDirection); + } + +private: + FDeproject DeprojectLambda; + FVector RayOrigin = FVector::ZeroVector; + FVector RayDirection = FVector::ForwardVector; +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FVoxelTool_OnBoundsUpdated, AVoxelWorld*, World, FVoxelIntBox, Bounds); + +UCLASS(BlueprintType) +class VOXEL_API UVoxelToolSharedConfig : public UObject +{ + GENERATED_BODY() + +public: + UVoxelToolSharedConfig(); + +public: + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, meta = (ClampMin = 0, UIMin = 0, UIMax = 20000)) + float BrushSize = 1000; + + UPROPERTY(Category = "Shared Config - Paint", EditAnywhere, BlueprintReadWrite, meta = (ShowOnlyInnerProperties, PaintMaterial)) + FVoxelPaintMaterial PaintMaterial; + +public: + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta = (UIMin = 0, UIMax = 1)) + float ToolOpacity = 0.5f; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta = (UIMin = 0, UIMax = 1)) + float AlignToMovementSmoothness = 0.75; + + // Input speed: 0.05 increase radius by 5% every time you press ] + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta = (UIMin = 0, UIMax = 1)) + float ControlSpeed = 0.05f; + + // If empty, allow editing all worlds + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Transient, meta = (HideInPanel)) + TArray WorldsToEdit; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bCacheData = true; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bMultiThreaded = true; + + // Which compute device to use when there's a choice + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + EVoxelComputeDevice ComputeDevice = EVoxelComputeDevice::GPU; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bRegenerateSpawners = true; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bCheckForSingleValues = true; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bWaitForUpdates = true; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + bool bDebug = false; + + // This is used when calling ApplyTool + // We cannot use the app delta time as it's not deterministic + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta = (HideInPanel)) + float FixedDeltaTime = 1.f / 60.f; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UStaticMesh* PlaneMesh = nullptr; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* PlaneMaterial = nullptr; + +public: + EVoxelComputeDevice GetComputeDevice() const + { + // Dedicated servers can't use the GPU + return IsRunningDedicatedServer() ? EVoxelComputeDevice::CPU : ComputeDevice; + } + +public: + UPROPERTY(BlueprintAssignable) + FVoxelTool_OnBoundsUpdated OnBoundsUpdated; + +public: + DECLARE_MULTICAST_DELEGATE_TwoParams(FRegisterTransactionDelegate, FName, AVoxelWorld*); + FRegisterTransactionDelegate RegisterTransaction; + +#if WITH_EDITOR + FSimpleMulticastDelegate RefreshDetails; +#endif +}; + +UCLASS(BlueprintType, Abstract) +class VOXEL_API UVoxelTool : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditDefaultsOnly, Category = "Config") + FName ToolName; + + UPROPERTY(EditDefaultsOnly, Category = "Config") + FText ToolTip; + + UPROPERTY(EditDefaultsOnly, Category = "Config") + bool bShowInDropdown = true; + + UPROPERTY(EditDefaultsOnly, Category = "Config") + bool bShowPaintMaterial = false; + +public: + // Shared config allows to share some values across several tools, like the brush size or the paint material + // If not set, it will be created in EnableTool + UPROPERTY(BlueprintReadOnly, Category = "Voxel") + UVoxelToolSharedConfig* SharedConfig = nullptr; + +public: + // Call this to do some initial setup + // Will be called in the first tool tick if you don't + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + virtual void EnableTool(); + + // Call this to delete any tool preview actors + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + virtual void DisableTool(); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + virtual AVoxelWorld* GetVoxelWorld() const { ensure(false); return nullptr; } + +public: + DECLARE_DYNAMIC_DELEGATE_TwoParams(FDoEditDynamicOverride, FVector, Position, FVector, Normal); + DECLARE_DELEGATE_TwoParams(FDoEditOverride, FVector /* Position */, FVector /* Normal */); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools", DisplayName = "AdvancedTick", meta = (AdvancedDisplay = "DoEditOverride", AutoCreateRefTerm = "DoEditOverride")) + void K2_AdvancedTick(UWorld* World, const FVoxelToolTickData& TickData, const FDoEditDynamicOverride& DoEditOverride); + void AdvancedTick(UWorld* World, const FVoxelToolTickData& TickData, const FDoEditOverride& DoEditOverride = {}); + + /** + * Tick the tool + * @param PlayerController The player controller - use GetPlayerController to get it + * @param bEdit Whether the user is pressing the edit button this frame + * @param Keys The keys pressed this frame. Use MakeToolKeys. You can add additional values to the map if you have custom tools using them. + * @param Axes The axes values in this frame, to control brush size/strength etc. Use MakeToolAxes. You can add additional values to the map if you have custom tools using them. + * @param DoEditOverride If provided, the edit will not be done but this function will be called instead. + * Useful for multiplayer, as you can broadcast the parameters to the other players & call ApplyTool + * @param CollisionChannel The collision channel to do traces against + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools", DisplayName = "SimpleTick", meta = (AdvancedDisplay = "DoEditOverride", AutoCreateRefTerm = "Keys, Axes, DoEditOverride, CollisionChannel")) + void K2_SimpleTick( + APlayerController* PlayerController, + bool bEdit, + const TMap& Keys, + const TMap& Axes, + const FDoEditDynamicOverride& DoEditOverride, + ECollisionChannel CollisionChannel = ECC_Visibility); + void SimpleTick( + APlayerController* PlayerController, + bool bEdit, + const TMap& Keys, + const TMap& Axes, + const FDoEditOverride& DoEditOverride = {}, + ECollisionChannel CollisionChannel = ECC_Visibility); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools", meta = (AutoCreateRefTerm = "Keys, Axes", DefaultToSelf = "World")) + void Apply( + AVoxelWorld* World, + FVector Position, + FVector Normal, + const TMap& Keys, + const TMap& Axes); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + FName GetToolName() const; + +public: + UFUNCTION(BlueprintPure, Category = "Voxel|Tools") + static TMap MakeToolKeys(bool bAlternativeMode); + + UFUNCTION(BlueprintPure, Category = "Voxel|Tools") + static TMap MakeToolAxes(float BrushSizeDelta, float FalloffDelta, float StrengthDelta); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools", meta = (DeterminesOutputType = "ToolClass")) + static UVoxelTool* MakeVoxelTool(TSubclassOf ToolClass); + +public: + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static bool IsKeyDown(FVoxelToolTickData TickData, FName Key) + { + return TickData.IsKeyDown(Key); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static float GetAxis(FVoxelToolTickData TickData, FName Axis) + { + return TickData.GetAxis(Axis); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static bool IsAlternativeMode(FVoxelToolTickData TickData) + { + return TickData.IsAlternativeMode(); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static bool Deproject(FVoxelToolTickData TickData, FVector2D ScreenPosition, FVector& WorldPosition, FVector& WorldDirection) + { + return TickData.Deproject(ScreenPosition, WorldPosition, WorldDirection); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static FVector GetRayOrigin(FVoxelToolTickData TickData) + { + return TickData.GetRayOrigin(); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Tick Data") + static FVector GetRayDirection(FVoxelToolTickData TickData) + { + return TickData.GetRayDirection(); + } + +protected: + enum class ECallToolMode + { + Tick, + Apply + }; + struct FCallToolParameters + { + ECallToolMode Mode; + FVector Position; + FVector Normal; + bool bBlockingHit = false; + + TFunction DoEditOverride; + }; + virtual void CallTool(AVoxelWorld* VoxelWorld, const FVoxelToolTickData& TickData, const FCallToolParameters& Parameters) {} + +public: + //~ Begin UObject Interface + virtual void BeginDestroy() override; + //~ End UObject Interface + +private: + UPROPERTY(Transient) + bool bEnabled = false; + + // For debug + UPROPERTY(Transient) + FVoxelToolTickData FrozenTickData; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolBase.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolBase.h new file mode 100644 index 00000000..38f7d486 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolBase.h @@ -0,0 +1,266 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelDataImpl.h" +#include "VoxelTools/Tools/VoxelTool.h" +#include "VoxelContainers/VoxelSparseArray.h" +#include "VoxelToolBase.generated.h" + +class AStaticMeshActor; +class UStaticMesh; +class UMaterialInterface; +class UMaterialInstanceDynamic; + +DEFINE_TYPED_VOXEL_SPARSE_ARRAY_ID(FVoxelToolRenderingId); + +UENUM(BlueprintType) +enum class EVoxelToolAlignment : uint8 +{ + // The tool follow the surface. The surface is frozen until the next click. + Surface, + // Align with the camera view plane + View, + // Align with XY plane + Ground, + // Align with the camera view plane, with the Z component zeroed out + Up +}; + +USTRUCT(BlueprintType) +struct FVoxelToolBaseConfig +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Materials", EditAnywhere, BlueprintReadWrite) + UMaterialInterface* OverlayMaterial = nullptr; + + UPROPERTY(Category = "Materials", EditAnywhere, BlueprintReadWrite) + UMaterialInterface* MeshMaterial = nullptr; + +public: + // Set to 0 to disable + UPROPERTY(Category = "General", EditAnywhere, BlueprintReadWrite) + float Stride = 0.f; + +public: + // If false will align to movement + UPROPERTY(Category = "Direction", EditAnywhere, BlueprintReadWrite) + bool bUseFixedDirection = false; + + // If bUseFixedDirection = true + UPROPERTY(Category = "Direction", EditAnywhere, BlueprintReadWrite) + FRotator FixedDirection = FRotator::ZeroRotator; + +public: + UPROPERTY(Category = "Normal", EditAnywhere, BlueprintReadWrite) + bool bUseFixedNormal = false; + + // If UseFixedNormal = true + UPROPERTY(Category = "Normal", EditAnywhere, BlueprintReadWrite) + FVector FixedNormal = FVector::ZeroVector; + +public: + // Whether this tool has an alignment setting + UPROPERTY(Category = "Aligment", EditAnywhere, BlueprintReadWrite) + bool bHasAlignment = false; + + UPROPERTY(Category = "Aligment", EditAnywhere, BlueprintReadWrite) + EVoxelToolAlignment Alignment = EVoxelToolAlignment::Surface; + + // If Alignment != Surface + UPROPERTY(Category = "Aligment", EditAnywhere, BlueprintReadWrite) + bool bAirMode = false; + + // If Alignment != Surface and AirMode = true + UPROPERTY(Category = "Aligment", EditAnywhere, BlueprintReadWrite) + float DistanceToCamera = 0.f; + + // If Alignment != Surface + UPROPERTY(Category = "Aligment", EditAnywhere, BlueprintReadWrite) + bool bShowPlanePreview = false; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract, Blueprintable) +class VOXEL_API UVoxelToolBase : public UVoxelTool +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const {} + virtual void Tick(); + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) {} + virtual FVoxelIntBoxWithValidity DoEdit() { return {}; } + //~ End UVoxelToolBase Interface + +public: + // Called on tick + UFUNCTION(BlueprintNativeEvent, DisplayName = "GetToolConfig") + void K2_GetToolConfig(FVoxelToolBaseConfig InConfig, FVoxelToolBaseConfig& OutConfig) const; + // Called first, before DoEdit and UpdateRender + // Note: Tick is a BlueprintImplementableEvent. The native Tick will always be called before. + UFUNCTION(BlueprintImplementableEvent, DisplayName = "Tick") + void K2_Tick(); + // Might not always be called - if you want to compute things for the frame, use Tick + UFUNCTION(BlueprintNativeEvent, DisplayName = "UpdateRender") + void K2_UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance); + // Returned bounds will be updated & SaveFrame called on them, as well as RegenerateSpawners if enabled + // Tick will always be called before + UFUNCTION(BlueprintNativeEvent, DisplayName = "DoEdit") + FVoxelIntBoxWithValidity K2_DoEdit(); + +public: + void K2_GetToolConfig_Implementation(FVoxelToolBaseConfig InConfig, FVoxelToolBaseConfig& OutConfig) const + { + GetToolConfig(InConfig); + OutConfig = InConfig; + } + void K2_UpdateRender_Implementation(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) + { + UpdateRender(OverlayMaterialInstance, MeshMaterialInstance); + } + FVoxelIntBoxWithValidity K2_DoEdit_Implementation() + { + return DoEdit(); + } + +public: + virtual void EnableTool() override; + virtual void DisableTool() override; + + virtual void CallTool(AVoxelWorld* VoxelWorld, const FVoxelToolTickData& TickData, const FCallToolParameters& Parameters) final override; + + virtual AVoxelWorld* GetVoxelWorld() const override final { return VoxelWorld; } + +public: + UFUNCTION(BlueprintCallable, Category = "Tool|Transform") + FVector GetToolPosition() const; + + UFUNCTION(BlueprintCallable, Category = "Tool|Transform") + FVector GetToolPreviewPosition() const; + + UFUNCTION(BlueprintCallable, Category = "Tool|Transform") + FVector GetToolNormal() const; + + UFUNCTION(BlueprintCallable, Category = "Tool|Transform") + FVector GetToolDirection() const; + +public: + UFUNCTION(BlueprintCallable, Category = "Tool") + bool CanEdit() const { return bCanEdit; } + + UFUNCTION(BlueprintCallable, Category = "Tool") + bool LastFrameCanEdit() const { return bLastFrameCanEdit; } + + UFUNCTION(BlueprintCallable, Category = "Tool") + const FVoxelToolTickData& GetTickData() const { return TickData; } + + UFUNCTION(BlueprintCallable, Category = "Tool") + const FVoxelToolTickData& GetLastFrameTickData() const { return LastFrameTickData; } + +public: + UFUNCTION(BlueprintCallable, Category = "Tool") + float GetMouseMovementSize() const { return MouseMovementSize; } + + // Delta time accounting for the skipped frame waiting for updates + UFUNCTION(BlueprintCallable, Category = "Tool") + float GetDeltaTime() const { return DeltaTime; } + + // Will also debug them + UFUNCTION(BlueprintCallable, Category = "Tool") + FVoxelIntBox GetBoundsToCache(const FVoxelIntBox& Bounds) const; + + UFUNCTION(BlueprintCallable, Category = "Tool") + float GetValueAfterAxisInput(FName AxisName, float CurrentValue, float Min = 0.f, float Max = 1.f) const; + +public: + UFUNCTION(BlueprintCallable, Category = "Tool|Render") + void SetToolOverlayBounds(const FBox& Bounds); + + // Note: Material will not be updated if the mesh did not change + UFUNCTION(BlueprintCallable, Category = "Tool|Render") + void UpdateToolMesh(UStaticMesh* Mesh, + UMaterialInterface* Material, + const FTransform& Transform, + FName Id = NAME_None); + +protected: + TVoxelDataImpl<> GetDataImpl(FVoxelData& Data) const + { + return TVoxelDataImpl<>(Data, SharedConfig->bMultiThreaded, false); + } + template + TVoxelDataImpl GetDataImpl(FVoxelData& Data) const + { + return TVoxelDataImpl(Data, SharedConfig->bMultiThreaded, true); + } + template + void CacheData(TData& Data, const FVoxelIntBox& Bounds) + { + if (SharedConfig->bCacheData) + { + Data.template CacheBounds(Bounds, SharedConfig->bMultiThreaded); + } + } + +private: + UPROPERTY(Transient) + AVoxelWorld* VoxelWorld = nullptr; + + FVoxelToolBaseConfig ToolBaseConfig; + + int32 NumPendingUpdates = 0; + + FVoxelToolRenderingId ToolRenderingId; + + bool bCanEdit = true; + bool bLastFrameCanEdit = true; + + FVoxelToolTickData TickData; + FVoxelToolTickData LastFrameTickData; + + double LastTickTime = FPlatformTime::Seconds(); + float DeltaTime = 0.f; + + FVector CurrentPosition = FVector::ZeroVector; + FVector CurrentNormal = FVector::UpVector; + + FVector MovementTangent = FVector::RightVector; + FVector LastPositionUsedForTangent = FVector::ZeroVector; + + FVector StridePosition = FVector::ZeroVector; + FVector StrideNormal = FVector::UpVector; + FVector StrideDirection = FVector::ForwardVector; + + float MouseMovementSize = 0; + + struct FViewportSpaceMovement + { + FPlane LastClickPlane = FPlane(FVector::ZeroVector, FVector::UpVector); + FVector LastClickPoint = FVector::ZeroVector; + FVector LastClickNormal = FVector::UpVector; + }; + FViewportSpaceMovement ViewportSpaceMovement; + + FVoxelIntBoxWithValidity PendingFrameBounds; + + // Map from id to mesh actor, to allow having multiple meshes + TMap> StaticMeshActors; + + UPROPERTY(Transient) + UMaterialInstanceDynamic* ToolOverlayMaterialInstance = nullptr; + UPROPERTY(Transient) + UMaterialInstanceDynamic* ToolMeshMaterialInstance = nullptr; + UPROPERTY(Transient) + UMaterialInstanceDynamic* PlaneMeshMaterialInstance = nullptr; + + void ClearVoxelWorld(); + void ApplyPendingFrameBounds(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolLibary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolLibary.h new file mode 100644 index 00000000..d72124f7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolLibary.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelToolLibary.generated.h" + +class UVoxelToolBase; +class UMaterialInstanceDynamic; + +UCLASS() +class VOXEL_API UVoxelToolLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel Tool Library") + static void UpdateSphereOverlayMaterial(UVoxelToolBase* Tool, UMaterialInstanceDynamic* OverlayMaterialInstance, EVoxelFalloff FalloffType, float Falloff); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolWithAlignment.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolWithAlignment.h new file mode 100644 index 00000000..c330cf0d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelToolWithAlignment.h @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelToolWithAlignment.generated.h" + +// Voxel tool with basic alignment settings +UCLASS(Abstract) +class VOXEL_API UVoxelToolWithAlignment : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + // The plane your sculpting is restricted to when holding mouse button down + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + EVoxelToolAlignment Alignment = EVoxelToolAlignment::View; + + // Position is based on the distance from the camera instead of the hit point + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "Alignment != EVoxelToolAlignment::Surface")) + bool bAirMode = false; + + // Distance to the camera when no voxel world under the cursor, or Air Mode = true + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "Alignment != EVoxelToolAlignment::Surface")) + float DistanceToCamera = 10000; + + UPROPERTY(Category = "Tool Settings", AdvancedDisplay, EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "Alignment != EVoxelToolAlignment::Surface")) + bool bShowPlanePreview = true; + +public: + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override + { + OutConfig.bHasAlignment = true; + OutConfig.Alignment = Alignment; + OutConfig.bAirMode = bAirMode; + OutConfig.DistanceToCamera = DistanceToCamera; + OutConfig.bShowPlanePreview = bShowPlanePreview; + } + //~ End UVoxelToolBase Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTrimTool.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTrimTool.h new file mode 100644 index 00000000..d541ac7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/Tools/VoxelTrimTool.h @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/Tools/VoxelToolBase.h" +#include "VoxelTrimTool.generated.h" + +UCLASS() +class VOXEL_API UVoxelTrimTool : public UVoxelToolBase +{ + GENERATED_BODY() + +public: + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + UMaterialInterface* ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "1")) + float Falloff = 0.5; + + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0", UIMax = "1")) + float Roughness = 0; + +public: + UVoxelTrimTool(); + + //~ Begin UVoxelToolBase Interface + virtual void GetToolConfig(FVoxelToolBaseConfig& OutConfig) const override; + virtual void Tick() override; + virtual void UpdateRender(UMaterialInstanceDynamic* OverlayMaterialInstance, UMaterialInstanceDynamic* MeshMaterialInstance) override; + virtual FVoxelIntBoxWithValidity DoEdit() override; + //~ End UVoxelToolBase Interface + +private: + FVector Position; + FVector Normal; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.h new file mode 100644 index 00000000..cd3e2fb9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.h @@ -0,0 +1,334 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterial.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssetTools.generated.h" + +class AVoxelWorld; +class FVoxelData; +class UVoxelTransformableGeneratorInstanceWrapper; + +template +class TVoxelDataItemWrapper; + +struct FVoxelAssetItem; +struct FVoxelDisableEditsBoxItem; + +UENUM(BlueprintType) +enum class EVoxelAssetMergeMode : uint8 +{ + // Import all values. Import no materials + AllValues, + // Import all materials. Import no values + AllMaterials, + // Import all values and all materials + AllValuesAndAllMaterials, + // Import values that are "inside" the asset. Import no materials. + InnerValues, + // Import materials that are "inside" the asset. Import no values. + InnerMaterials, + // Import values and materials that are "inside" the asset. + InnerValuesAndInnerMaterials, +}; + +USTRUCT(BlueprintType) +struct FVoxelAssetItemReference +{ + GENERATED_BODY() + + FVoxelIntBox Bounds; + TVoxelWeakPtr> Item; +}; + +USTRUCT(BlueprintType) +struct FVoxelDisableEditsBoxItemReference +{ + GENERATED_BODY() + + FVoxelIntBox Bounds; + TVoxelWeakPtr> Item; +}; + +UCLASS() +class VOXEL_API UVoxelAssetTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** + * Add a voxel asset to the world: can be a data asset (eg imported from a mesh), or a graph asset + * Cheap, unless there are edited voxels in the zone the asset is imported in + * Will provide the PreviousGenerator to voxel graphs + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param Priority Priority of the asset: the higher priority ones will be on top of lower priority ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + * @param bUpdateRender If the render should be updated. Not needed if done right after creating the world + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", AdvancedDisplay = "bConvertToVoxelSpace, bUpdateRender")) + static void ImportAssetAsReference( + FVoxelAssetItemReference& Reference, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + int32 Priority, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true); + + /** + * Add a voxel asset to the world: can be a data asset (eg imported from a mesh), or a graph asset + * Cheap, unless there are edited voxels in the zone the asset is imported in + * Will provide the PreviousGenerator to voxel graphs + * Runs asynchronously + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param Priority Priority of the asset: the higher priority ones will be on top of lower priority ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + * @param bUpdateRender If the render should be updated. Not needed if done right after creating the world + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bConvertToVoxelSpace, bUpdateRender, bHideLatentWarnings")) + static void ImportAssetAsReferenceAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelAssetItemReference& Reference, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + int32 Priority, + bool bConvertToVoxelSpace = true, + bool bUpdateRender = true, + bool bHideLatentWarnings = false); + +public: + static void ImportModifierAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FTransform& Transform, + const FVoxelTransformableGeneratorInstance& Instance, + bool bModifyValues, + bool bModifyMaterials); + + /** + * Add a voxel modifier asset to the world. Should be a graph asset. + * Unlike ImportAsset, this WILL provide the Previous Generator to the graph, so you can access the existing voxel value + * Unlike ImportAssetAsReference, this one copies the asset data into the world. Can be expensive for large assets. Use this if your asset is relatively small + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", AdvancedDisplay = "bLockEntireWorld, bConvertToVoxelSpace")) + static void ImportModifierAsset( + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bModifyValues = true, + bool bModifyMaterials = true, + bool bLockEntireWorld = true, + bool bConvertToVoxelSpace = true); + + /** + * Add a voxel asset to the world: can be a data asset (eg imported from a mesh), or a graph asset + * Will not provide the Previous Generator, so won't work with graphs that use it! + * Unlike ImportAssetAsReference, this one copies the asset data into the world. Can be expensive for large assets. Use this if your asset is relatively small + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bLockEntireWorld, bConvertToVoxelSpace, bHideLatentWarnings")) + static void ImportModifierAssetAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bModifyValues = true, + bool bModifyMaterials = true, + bool bLockEntireWorld = true, + bool bConvertToVoxelSpace = true, + bool bHideLatentWarnings = false); + +public: + static void ImportAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FTransform& Transform, + const FVoxelTransformableGeneratorInstance& Instance, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + uint32 MaterialMask); + static void ImportDataAssetImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + const FVoxelVector& Position, + const FVoxelDataAssetData& AssetData, + bool bSubtractive, + EVoxelAssetMergeMode MergeMode, + uint32 MaterialMask); + + template + static void ImportDataAssetImpl( + TData& Data, + const FVoxelVector& Position, + const FVoxelDataAssetData& AssetData, + // To set the default value when querying the asset + bool bSubtractive, + T1 GetValue = [] (float OldValue, float InstanceValue) { return OldValue; }, + bool bSetMaterials = false, + T2 GetMaterial = [](float OldValue, float NewValue, FVoxelMaterial OldMaterial, float InstanceValue, FVoxelMaterial InstanceMaterial) { return OldMaterial; }); + + /** + * Add a voxel asset to the world: can be a data asset (eg imported from a mesh), or a graph asset + * Will not provide the Previous Generator, so won't work with graphs that use it! + * Unlike ImportAssetAsReference, this one copies the asset data into the world. Can be expensive for large assets. Use this if your asset is relatively small + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param bSubtractive If false, the inner values are the full ones. If true, the inner values are the empty ones. + * @param MergeMode How the values and materials of the asset should be merged with existing ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", AdvancedDisplay = "bSetAllMaterials, bConvertToVoxelSpace")) + static void ImportAsset( + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bSubtractive = false, + EVoxelAssetMergeMode MergeMode = EVoxelAssetMergeMode::InnerValuesAndInnerMaterials, + bool bConvertToVoxelSpace = true); + + /** + * Add a voxel asset to the world: can be a data asset (eg imported from a mesh), or a graph asset + * Will not provide the Previous Generator, so won't work with graphs that use it! + * Unlike ImportAssetAsReference, this one copies the asset data into the world. Can be expensive for large assets. Use this if your asset is relatively small + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Transform The transform of the asset: in world space, unless ConvertToVoxelSpace is false + * @param Bounds The bounds of the asset in voxel space. If the asset is a VoxelTransformableGeneratorWithBounds, they will be set automatically if not provided + * @param bSubtractive If false, the inner values are the full ones. If true, the inner values are the empty ones. + * @param MergeMode How the values and materials of the asset should be merged with existing ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bSetAllMaterials, bConvertToVoxelSpace, bHideLatentWarnings")) + static void ImportAssetAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelTransformableGeneratorInstanceWrapper* Asset, + FTransform Transform, + FVoxelIntBox Bounds, + bool bSubtractive = false, + EVoxelAssetMergeMode MergeMode = EVoxelAssetMergeMode::InnerValuesAndInnerMaterials, + bool bConvertToVoxelSpace = true, + bool bHideLatentWarnings = false); + +public: + /** + * Specialization of ImportAsset for data assets with no scale nor rotation + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Position The position of the asset: in world space, unless ConvertToVoxelSpace is false + * @param MergeMode How the values and materials of the asset should be merged with existing ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", AdvancedDisplay = "bSetAllMaterials, bConvertToVoxelSpace")) + static void ImportDataAssetFast( + AVoxelWorld* World, + UVoxelDataAsset* Asset, + FVector Position, + EVoxelAssetMergeMode MergeMode = EVoxelAssetMergeMode::InnerValuesAndInnerMaterials, + bool bConvertToVoxelSpace = true); + + /** + * Specialization of ImportAsset for data assets with no scale nor rotation + * @param World The voxel world to edit + * @param Asset The asset to import + * @param Position The position of the asset: in world space, unless ConvertToVoxelSpace is false + * @param MergeMode How the values and materials of the asset should be merged with existing ones + * @param bConvertToVoxelSpace If true, the transform is in world space. If false, it's in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bSetAllMaterials, bConvertToVoxelSpace, bHideLatentWarnings")) + static void ImportDataAssetFastAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + UVoxelDataAsset* Asset, + FVector Position, + EVoxelAssetMergeMode MergeMode = EVoxelAssetMergeMode::InnerValuesAndInnerMaterials, + bool bConvertToVoxelSpace = true, + bool bHideLatentWarnings = false); +public: + static void InvertDataAssetImpl( + const FVoxelDataAssetData& AssetData, + FVoxelDataAssetData& InvertedAssetData); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools") + static void InvertDataAsset(UVoxelDataAsset* Asset, UVoxelDataAsset*& InvertedAsset); + +public: + static void SetDataAssetMaterialImpl( + const FVoxelDataAssetData& AssetData, + FVoxelDataAssetData& NewAssetData, + FVoxelMaterial Material); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools") + static void SetDataAssetMaterial(UVoxelDataAsset* Asset, UVoxelDataAsset*& NewAsset, FVoxelMaterial Material); + +public: + static void CreateDataAssetFromWorldSectionImpl( + const FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bCopyMaterials, + FVoxelDataAssetData& AssetData); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World")) + static UVoxelDataAsset* CreateDataAssetFromWorldSection( + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bCopyMaterials); + +public: + /** + * Add a disable edits box to the world + * @param World The voxel world to edit + * @param Bounds The bounds of the box in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World")) + static void AddDisableEditsBox( + FVoxelDisableEditsBoxItemReference& Reference, + AVoxelWorld* World, + FVoxelIntBox Bounds); + + /** + * Add a disable edits box to the world + * Runs asynchronously + * @param World The voxel world to edit + * @param Bounds The bounds of the box in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Asset Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void AddDisableEditsBoxAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelDisableEditsBoxItemReference& Reference, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl new file mode 100644 index 00000000..3f0b0e9f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl @@ -0,0 +1,64 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelAssetTools.h" +#include "VoxelTools/VoxelToolHelpers.h" + +template +void UVoxelAssetTools::ImportDataAssetImpl( + TData& Data, + const FVoxelVector& Position, + const FVoxelDataAssetData& AssetData, + bool bSubtractive, + T1 GetValue, + bool bSetMaterials, + T2 GetMaterial) +{ + const FVoxelIntBox Bounds = FVoxelIntBox(Position, Position + AssetData.GetSize()); + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + const FVoxelValue DefaultValue = bSubtractive ? FVoxelValue::Full() : FVoxelValue::Empty(); + const auto GetInstanceValue = [&](int32 X, int32 Y, int32 Z) + { + return AssetData.GetInterpolatedValue( + X - Position.X, + Y - Position.Y, + Z - Position.Z, + DefaultValue); + }; + const auto GetInstanceMaterial = [&](int32 X, int32 Y, int32 Z) + { + return AssetData.HasMaterials() ? AssetData.GetInterpolatedMaterial( + X - Position.X, + Y - Position.Y, + Z - Position.Z) : FVoxelMaterial::Default(); + }; + + if (bSetMaterials) + { + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material) + { + const float InstanceValue = GetInstanceValue(X, Y, Z); + const FVoxelMaterial InstanceMaterial = GetInstanceMaterial(X, Y, Z); + + const float OldValue = Value.ToFloat(); + const float NewValue = GetValue(OldValue, InstanceValue); + + Value = FVoxelValue(NewValue); + Material = GetMaterial(OldValue, NewValue, Material, InstanceValue, InstanceMaterial); + }); + } + else + { + Data.template Set(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + const float InstanceValue = GetInstanceValue(X, Y, Z); + + const float OldValue = Value.ToFloat(); + const float NewValue = GetValue(OldValue, InstanceValue); + + Value = FVoxelValue(NewValue); + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h new file mode 100644 index 00000000..5e794cc8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h @@ -0,0 +1,804 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SubclassOf.h" +#include "Kismet/BlueprintFunctionLibrary.h" + +#include "VoxelIntBox.h" +#include "VoxelPaintMaterial.h" +#include "VoxelTexture.h" +#include "VoxelSpawners/VoxelInstancedMeshSettings.h" +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelRender/VoxelToolRendering.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +#include "VoxelBlueprintLibrary.generated.h" + +enum class EVoxelTaskType : uint8; +class UVoxelHierarchicalInstancedStaticMeshComponent; +class AVoxelSpawnerActor; +class AVoxelWorld; +class APlayerState; +class AController; +class UStaticMesh; + +DECLARE_DYNAMIC_DELEGATE_TwoParams(FVoxelOnChunkGenerationDynamicDelegate, AVoxelWorld*, World, FVoxelIntBox, Bounds); +DECLARE_DYNAMIC_DELEGATE_OneParam(FChunkDynamicDelegate, FVoxelIntBox, Bounds); + +USTRUCT(BlueprintType) +struct FVoxelToolRenderingRef +{ + GENERATED_BODY() + + FVoxelToolRenderingId Id; +}; + +UENUM(BlueprintType) +enum class EVoxelMemoryUsageType : uint8 +{ + Total, + VoxelsDirtyValuesData, + VoxelsDirtyMaterialsData, + VoxelsCachedValuesData, + VoxelsCachedMaterialsData, + UndoRedo, + Multiplayer, + IntermediateBuffers, + MeshesIndices, + MeshesTessellationIndices, + MeshesVertices, + MeshesColors, + MeshesUVsAndTangents, + DataAssets, + HeightmapAssets, + UncompressedSaves, + CompressedSaves +}; + +UCLASS() +class VOXEL_API UVoxelBlueprintLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintPure, Category = "Voxel") + static bool IsVoxelPluginPro(); + + UFUNCTION(BlueprintCallable, Category = "Voxel", meta = (DefaultToSelf = "Object", AdvancedDisplay = "Object")) + static void RaiseInfo(FString Message, UObject* Object = nullptr); + + UFUNCTION(BlueprintCallable, Category = "Voxel", meta = (DefaultToSelf = "Object", AdvancedDisplay = "Object")) + static void RaiseWarning(FString Message, UObject* Object = nullptr); + + UFUNCTION(BlueprintCallable, Category = "Voxel", meta = (DefaultToSelf = "Object", AdvancedDisplay = "Object")) + static void RaiseError(FString Message, UObject* Object = nullptr); + + // Returns the number of cores the CPU has. Ignores hyperthreading unless -usehyperthreading is passed as a command line argument. + UFUNCTION(BlueprintPure, Category = "Voxel") + static int32 NumberOfCores(); + +public: + // Get the current memory usage of different parts of the plugin + UFUNCTION(BlueprintPure, Category = "Voxel|Memory") + static float GetMemoryUsageInMB(EVoxelMemoryUsageType Type); + + // Get the peak memory usage of different parts of the plugin + UFUNCTION(BlueprintPure, Category = "Voxel|Memory") + static float GetPeakMemoryUsageInMB(EVoxelMemoryUsageType Type); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Memory") + static void LogMemoryStats(); + + // Returns the memory used by positions and indices buffers in this voxel world + // Should give a rough estimate of how much memory is used for physics + UFUNCTION(BlueprintCallable, Category = "Voxel|Memory", meta = (DefaultToSelf = "World")) + static float GetEstimatedCollisionsMemoryUsageInMB(AVoxelWorld* World); + +public: + /** + * Transform a box in global space to voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (DefaultToSelf = "World")) + static FVoxelIntBox TransformGlobalBoxToVoxelBox(AVoxelWorld* World, FBox Box); + + /** + * Transform a box in voxel space to global space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (DefaultToSelf = "World")) + static FBox TransformVoxelBoxToGlobalBox(AVoxelWorld* World, FVoxelIntBox Box); + +public: + /** + * Iterate all voxel worlds in the scene, and return the first one that contains Position + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (WorldContext = "WorldContextObject")) + static AVoxelWorld* GetVoxelWorldContainingPosition(UObject* WorldContextObject, FVector Position); + /** + * Iterate all voxel worlds in the scene, and return all the ones that contains Position + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (WorldContext = "WorldContextObject")) + static TArray GetAllVoxelWorldsContainingPosition(UObject* WorldContextObject, FVector Position); + + /** + * Iterate all voxel worlds in the scene, and return the first one that overlaps Box + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (WorldContext = "WorldContextObject")) + static AVoxelWorld* GetVoxelWorldOverlappingBox(UObject* WorldContextObject, FBox Box); + /** + * Iterate all voxel worlds in the scene, and return all the ones that overlaps Box + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (WorldContext = "WorldContextObject")) + static TArray GetAllVoxelWorldsOverlappingBox(UObject* WorldContextObject, FBox Box); + + /** + * Iterate all voxel worlds in the scene, and return the first one that overlaps the actor bounding box + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (DefaultToSelf = "Actor")) + static AVoxelWorld* GetVoxelWorldOverlappingActor(AActor* Actor); + /** + * Iterate all voxel worlds in the scene, and return all the ones that overlaps the actor bounding box + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Helpers", meta = (DefaultToSelf = "Actor")) + static TArray GetAllVoxelWorldsOverlappingActor(AActor* Actor); + +public: + /** + * FVoxelInstancedMeshManager helpers + */ + +public: + // Will replace instanced static mesh instances by actors + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static void SpawnVoxelSpawnerActorsInArea( + TArray& OutActors, + AVoxelWorld* World, + FVoxelIntBox Bounds, + EVoxelSpawnerActorSpawnType SpawnType = EVoxelSpawnerActorSpawnType::OnlyFloating); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static AVoxelSpawnerActor* SpawnVoxelSpawnerActorByInstanceIndex( + AVoxelWorld* World, + UVoxelHierarchicalInstancedStaticMeshComponent* Component, + int32 InstanceIndex); + + /** + * Add instances to a voxel world foliage system + * @param World The voxel world + * @param Mesh The mesh to use + * @param Transforms The transforms, relative to the voxel world (but not in voxel space!) + * @param Colors The colors to send to the instance material (use GetVoxelMaterialFromPerInstanceRandom to get it) + * @param FloatingDetectionOffset Increase this if your foliage is enabling physics too soon + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World", AdvancedDisplay = "FloatingDetectionOffset")) + static void AddInstances( + AVoxelWorld* World, + UStaticMesh* Mesh, + const TArray& Transforms, + const TArray& Colors, + FVoxelInstancedMeshSettings InstanceSettings, + FVoxelSpawnerActorSettings ActorSettings, + FVector FloatingDetectionOffset = FVector(0, 0, -10)); + +public: + /** + * IVoxelSpawnerManager helpers + */ + + // Regenerate spawners in an aera + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static void RegenerateSpawners(AVoxelWorld* World, FVoxelIntBox Bounds); + + // Mark spawners as dirty so that they don't get trash if they go out of scope + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static void MarkSpawnersDirty(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static FVoxelSpawnersSave GetSpawnersSave(AVoxelWorld* World); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Spawners", meta = (DefaultToSelf = "World")) + static void LoadFromSpawnersSave(AVoxelWorld* World, const FVoxelSpawnersSave& Save); + +public: + /** + * FVoxelData helpers + */ + +public: + // Undo last frame. bEnableUndoRedo must be true, and SaveFrame must have been called after any edits + // @return Success + UFUNCTION(BlueprintCallable, Category = "Voxel|UndoRedo", meta = (DefaultToSelf = "World")) + static bool Undo(AVoxelWorld* World, TArray& OutUpdatedBounds); + static bool Undo(AVoxelWorld* World); + // Redo last undone frame. bEnableUndoRedo must be true, and SaveFrame must have been called after any edits + // @return Success + UFUNCTION(BlueprintCallable, Category = "Voxel|UndoRedo", meta = (DefaultToSelf = "World")) + static bool Redo(AVoxelWorld* World, TArray& OutUpdatedBounds); + static bool Redo(AVoxelWorld* World); + // Save the edits since the last call to SaveFrame/Undo/Redo and clear the redo stack. bEnableUndoRedo must be true + UFUNCTION(BlueprintCallable, Category = "Voxel|UndoRedo", meta = (DefaultToSelf = "World")) + static void SaveFrame(AVoxelWorld* World); + // Clear all the frames. bEnableUndoRedo must be true + UFUNCTION(BlueprintCallable, Category = "Voxel|UndoRedo", meta = (DefaultToSelf = "World")) + static void ClearFrames(AVoxelWorld* World); + // Get the current history position + UFUNCTION(BlueprintPure, Category = "Voxel|UndoRedo", meta = (DefaultToSelf = "World")) + static int32 GetHistoryPosition(AVoxelWorld* World); + + // Get the normal at Position using the density gradient. May differ from the mesh normal + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (Keywords = "gradient", DefaultToSelf = "World")) + static FVector GetNormal(AVoxelWorld* World, FIntVector Position); + + // Get a custom float output value + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static float GetFloatOutput(AVoxelWorld* World, FName Name, float X, float Y, float Z, float DefaultValue); + + // Get a custom int32 output value + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static int32 GetIntOutput(AVoxelWorld* World, FName Name, float X, float Y, float Z, int32 DefaultValue); + + // Bounds of this world + UFUNCTION(BlueprintPure, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static FVoxelIntBox GetBounds(AVoxelWorld* World); + + // TODO: delegate to chunk creation to setup material + // Will cleanup the code there as well, as no need to manually call world gen thingy + + // Clear all the data in the world, including items/assets + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static void ClearAllData(AVoxelWorld* World, bool bUpdateRender = true); + + // Clear all the value data in the world + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static void ClearValueData(AVoxelWorld* World, bool bUpdateRender = true); + + // Clear all the material data in the world + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static void ClearMaterialData(AVoxelWorld* World, bool bUpdateRender = true); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static bool HasValueData(AVoxelWorld* World); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static bool HasMaterialData(AVoxelWorld* World); + + // Clear all the edited data in the world, excluding items/assets + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bUpdateRender")) + static void ClearDirtyData(AVoxelWorld* World, bool bUpdateRender = true); + + // Scale the voxel world data. Will recreate the voxel world! + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void ScaleData(AVoxelWorld* World, const FVector& Scale); + +public: + /** + * IVoxelLODManager helpers + */ + +public: + // Update the chunks overlapping Position + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void UpdatePosition(AVoxelWorld* World, FIntVector Position); + // Update the chunks overlapping Bounds. + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void UpdateBounds(AVoxelWorld* World, FVoxelIntBox Bounds); + // Update all the chunks + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void UpdateAll(AVoxelWorld* World); + // Call this after changing the voxel world LODs setting while created + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World", DisplayName = "Apply LOD Settings")) + static void ApplyLODSettings(AVoxelWorld* World); + /** + * Returns whether voxel collisions are enabled at Position + * @param World The voxel world + * @param Position The position to query, in world space if ConvertToVoxelSpace is true + * @param LOD The LOD at that position + * @param bConvertToVoxelSpace If true, the position will be converted to voxel space. Else it will be used directly + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Collisions", meta = (DefaultToSelf = "World", AdvancedDisplay = "bConvertToVoxelSpace")) + static bool AreCollisionsEnabled(AVoxelWorld* World, FVector Position, int32& LOD, bool bConvertToVoxelSpace = true); + +public: + /** + * IVoxelRenderer helpers + */ + +public: + // Number of mesh processing tasks not finished + UFUNCTION(BlueprintPure, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static int32 GetTaskCount(AVoxelWorld* World); + + // Returns true if there are still mesh tasks queued + UFUNCTION(BlueprintPure, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static bool IsVoxelWorldMeshLoading(AVoxelWorld* World); + + // Returns true if there are still foliage tasks queued + UFUNCTION(BlueprintPure, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static bool IsVoxelWorldFoliageLoading(AVoxelWorld* World); + + // Call this after changing a voxel world VoxelMaterial/MaterialCollection to apply it + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void ApplyNewMaterials(AVoxelWorld* World); + + // Call this to recreate the voxel world rendering entirely, keeping data intact + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void RecreateRender(AVoxelWorld* World); + + // Call this to recreate the voxel world spawners + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void RecreateSpawners(AVoxelWorld* World); + + // Call this to recreate the voxel world entirely, optionally keeping data intact by saving it + UFUNCTION(BlueprintCallable, Category = "Voxel|Render", meta = (DefaultToSelf = "World")) + static void Recreate(AVoxelWorld* World, bool bSaveData = true); + +public: + /** + * FVoxelEventManager helpers + */ + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Proc Gen", meta = (DefaultToSelf = "World", AdvancedDisplay = "bFireExistingOnes")) + static void BindVoxelChunkEvents( + AVoxelWorld* World, + FChunkDynamicDelegate OnActivate, + FChunkDynamicDelegate OnDeactivate, + bool bFireExistingOnes = false, + int32 ChunkSize = 32, + int32 ActivationDistanceInChunks = 2); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Proc Gen", meta = (DefaultToSelf = "World", AdvancedDisplay = "bFireExistingOnes")) + static void BindVoxelGenerationEvent( + AVoxelWorld* World, + FChunkDynamicDelegate OnGenerate, + bool bFireExistingOnes = false, + int32 ChunkSize = 32, + int32 GenerationDistanceInChunks = 2); + +public: + /** + * FVoxelToolRenderingManager helpers + */ + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static bool IsValidRef(AVoxelWorld* World, FVoxelToolRenderingRef Ref); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static FVoxelToolRenderingRef CreateToolRendering(AVoxelWorld* World); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static void DestroyToolRendering(AVoxelWorld* World, FVoxelToolRenderingRef Ref); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static void SetToolRenderingMaterial(AVoxelWorld* World, FVoxelToolRenderingRef Ref, UMaterialInterface* Material); + + // Bounds: In world space + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static void SetToolRenderingBounds(AVoxelWorld* World, FVoxelToolRenderingRef Ref, FBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tool Rendering", meta = (DefaultToSelf = "World")) + static void SetToolRenderingEnabled(AVoxelWorld* World, FVoxelToolRenderingRef Ref, bool bEnabled = true); + +public: + /** + * IVoxelPool helpers + */ + +public: + /** + * Create the global voxel thread pool. Must not be already created. + * CreateWorldVoxelThreadPool is preferred, as pools will be per level + * @param NumberOfThreads At least 1 + * @param bConstantPriorities If true won't recompute the tasks priorities once added. Useful if you have many tasks, but will give bad task scheduling when moving fast + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Threads", meta = (AdvancedDisplay = "PriorityCategoriesOverrides, PriorityOffsetsOverrides")) + static void CreateGlobalVoxelThreadPool( + const TMap& PriorityCategoriesOverrides, + const TMap& PriorityOffsetsOverrides, + int32 NumberOfThreads = 2, + bool bConstantPriorities = false); + + // Destroy the global voxel thread pool + UFUNCTION(BlueprintCallable, Category = "Voxel|Threads") + static void DestroyGlobalVoxelThreadPool(); + + // Is the global voxel thread pool created? + UFUNCTION(BlueprintPure, Category = "Voxel|Threads") + static bool IsGlobalVoxelPoolCreated(); + + /** + * Create the voxel thread pool for a specific world. Must not be already created. + * @param NumberOfThreads At least 1 + * @param bConstantPriorities If true won't recompute the tasks priorities once added. Useful if you have many tasks, but will give bad task scheduling when moving fast + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Threads", meta = (AdvancedDisplay = "PriorityCategoriesOverrides, PriorityOffsetsOverrides")) + static void CreateWorldVoxelThreadPool( + UWorld* World, + const TMap& PriorityCategoriesOverrides, + const TMap& PriorityOffsetsOverrides, + int32 NumberOfThreads = 2, + bool bConstantPriorities = false); + + // Destroy the world voxel thread pool + UFUNCTION(BlueprintCallable, Category = "Voxel|Threads") + static void DestroyWorldVoxelThreadPool(UWorld* World); + + // Is the global voxel thread pool created? + UFUNCTION(BlueprintPure, Category = "Voxel|Threads") + static bool IsWorldVoxelPoolCreated(UWorld* World); + +public: + /** + * FVoxelIntBox helpers + */ + +public: + /** + * Make IntBox from global position and radius + * @param Radius in cm + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Utilities", meta = (DefaultToSelf = "World")) + static FVoxelIntBox MakeIntBoxFromGlobalPositionAndRadius(AVoxelWorld* World, FVector GlobalPosition, float Radius); + + // eg if you want to cache all the data that's going to be used by render chunks when updating the world + UFUNCTION(BlueprintPure, Category = "Voxel|Utilities", meta = (DefaultToSelf = "World")) + static FVoxelIntBox GetRenderBoundsOverlappingDataBounds(AVoxelWorld* World, FVoxelIntBox DataBounds, int32 LOD = 0); + +public: + /** + * FIntVector helpers + */ + +public: + UFUNCTION(BlueprintPure, meta = (DisplayName = "IntVector + IntVector", CompactNodeTitle = "+", Keywords = "+ add plus"), Category = "Math|IntVector") + static FIntVector Add_IntVectorIntVector(FIntVector Left, FIntVector Right) + { + return Left + Right; + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "IntVector - IntVector", CompactNodeTitle = "-", Keywords = "- subtract minus"), Category = "Math|IntVector") + static FIntVector Substract_IntVectorIntVector(FIntVector Left, FIntVector Right) + { + return Left - Right; + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "IntVector * IntVector", CompactNodeTitle = "*", Keywords = "* multiply"), Category = "Math|IntVector") + static FIntVector Multiply_IntVectorIntVector(FIntVector Left, FIntVector Right) + { + return FIntVector(Left.X * Right.X, Left.Y * Right.Y, Left.Z * Right.Z); + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "IntVector / int", CompactNodeTitle = "/", Keywords = "/ divide"), Category = "Math|IntVector") + static FIntVector Divide_IntVectorInt(FIntVector Left, int32 Right) + { + return Left / Right; + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "IntVector * int", CompactNodeTitle = "*", Keywords = "* multiply"), Category = "Math|IntVector") + static FIntVector Multiply_IntVectorInt(FIntVector Left, int32 Right) + { + return Left * Right; + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "int * IntVector", CompactNodeTitle = "*", Keywords = "* multiply"), Category = "Math|IntVector") + static FIntVector Multiply_IntIntVector(int32 Left, FIntVector Right) + { + return Right * Left; + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Max"), Category = "Math|IntVector") + static int32 GetMax_Intvector(FIntVector Vector) + { + return Vector.GetMax(); + } + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Min"), Category = "Math|IntVector") + static int32 GetMin_Intvector(FIntVector Vector) + { + return Vector.GetMin(); + } + +public: + /** + * FVoxelPaintMaterial helpers + */ + +public: + // Create from color + UFUNCTION(BlueprintPure, Category = "Voxel|Materials", DisplayName = "Create RGB Paint Material") + static FVoxelPaintMaterial CreateColorPaintMaterial(FVoxelPaintMaterialColor Color) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::Color; + PaintMaterial.Color = Color; + return PaintMaterial; + } + + /** + * Create paint material for 5 way blend + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelPaintMaterial CreateFiveWayBlendPaintMaterial(FVoxelPaintMaterialFiveWayBlend FiveWayBlend); + + // Create for single index + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelPaintMaterial CreateSingleIndexPaintMaterial(FVoxelPaintMaterialSingleIndex SingleIndex) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::SingleIndex; + PaintMaterial.SingleIndex = SingleIndex; + return PaintMaterial; + } + + // Create for multi index + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelPaintMaterial CreateMultiIndexPaintMaterial(FVoxelPaintMaterialMultiIndex MultiIndex) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::MultiIndex; + PaintMaterial.MultiIndex = MultiIndex; + return PaintMaterial; + } + + // Create for multi index wetness + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelPaintMaterial CreateMultiIndexWetnessPaintMaterial(FVoxelPaintMaterialMultiIndexWetness MultiIndexWetness) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::MultiIndexWetness; + PaintMaterial.MultiIndexWetness = MultiIndexWetness; + return PaintMaterial; + } + + // Create for multi index, setting the data directly + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelPaintMaterial CreateMultiIndexRawPaintMaterial(FVoxelPaintMaterialMultiIndexRaw MultiIndexRaw) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::MultiIndexRaw; + PaintMaterial.MultiIndexRaw = MultiIndexRaw; + return PaintMaterial; + } + + // Create UV paint + UFUNCTION(BlueprintPure, Category = "Voxel|Materials", DisplayName = "Create UV Paint Material") + static FVoxelPaintMaterial CreateUVPaintMaterial(FVoxelPaintMaterialUV UV) + { + FVoxelPaintMaterial PaintMaterial; + PaintMaterial.Type = EVoxelPaintMaterialType::UV; + PaintMaterial.UV = UV; + return PaintMaterial; + } + + // Apply a Paint Material to a Voxel Material + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelMaterial ApplyPaintMaterial(FVoxelMaterial Material, FVoxelPaintMaterial PaintMaterial, float Strength = 1.f) + { + PaintMaterial.ApplyToMaterial(Material, Strength); + return Material; + } + +public: + /** + * FVoxelMaterial helpers + */ + +public: + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FLinearColor GetColor(FVoxelMaterial Material) + { + return Material.GetLinearColor(); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static uint8 GetSingleIndex(FVoxelMaterial Material) + { + return Material.GetSingleIndex(); + } + // If SortByStrength is true, Index 0 will have the highest strength, Index 1 the second highest etc + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static void GetMultiIndex( + FVoxelMaterial Material, + bool bSortByStrength, + float& Strength0, uint8& Index0, + float& Strength1, uint8& Index1, + float& Strength2, uint8& Index2, + float& Strength3, uint8& Index3, + float& Wetness); + + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVector2D GetUV(FVoxelMaterial Material, int32 Channel) + { + return Material.GetUV_AsFloat(Channel); + } + + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static void GetRawMaterial( + FVoxelMaterial Material, + uint8& R, uint8& G, uint8& B, uint8& A, + uint8& U0, uint8& V0, + uint8& U1, uint8& V1, + uint8& U2, uint8& V2, + uint8& U3, uint8& V3) + { + R = Material.Impl_GetR(); + G = Material.Impl_GetG(); + B = Material.Impl_GetB(); + A = Material.Impl_GetA(); + U0 = Material.Impl_GetU0(); + V0 = Material.Impl_GetV0(); + U1 = Material.Impl_GetU1(); + V1 = Material.Impl_GetV1(); + U2 = Material.Impl_GetU2(); + V2 = Material.Impl_GetV2(); + U3 = Material.Impl_GetU3(); + V3 = Material.Impl_GetV3(); + } + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static FVoxelMaterial MakeRawMaterial( + uint8 R, uint8 G, uint8 B, uint8 A, + uint8 U0, uint8 V0, + uint8 U1, uint8 V1, + uint8 U2, uint8 V2, + uint8 U3, uint8 V3) + { + FVoxelMaterial Material{ ForceInit }; + Material.Impl_SetR(R); + Material.Impl_SetG(G); + Material.Impl_SetB(B); + Material.Impl_SetA(A); + Material.Impl_SetU0(U0); + Material.Impl_SetV0(V0); + Material.Impl_SetU1(U1); + Material.Impl_SetV1(V1); + Material.Impl_SetU2(U2); + Material.Impl_SetV2(V2); + Material.Impl_SetU3(U3); + Material.Impl_SetV3(V3); + return Material; + } + UFUNCTION(BlueprintPure, Category = "Voxel|Materials") + static int32 MakeMaterialMask( + bool R, bool G, bool B, bool A, + bool U0, bool V0, + bool U1, bool V1, + bool U2, bool V2, + bool U3, bool V3) + { + return + EVoxelMaterialMask::R * R | + EVoxelMaterialMask::G * G | + EVoxelMaterialMask::B * B | + EVoxelMaterialMask::A * A | + EVoxelMaterialMask::U0 * U0 | + EVoxelMaterialMask::U1 * U1 | + EVoxelMaterialMask::U2 * U2 | + EVoxelMaterialMask::U3 * U3 | + EVoxelMaterialMask::V0 * V0 | + EVoxelMaterialMask::V1 * V1 | + EVoxelMaterialMask::V2 * V2 | + EVoxelMaterialMask::V3 * V3; + } + +public: + /** + * FVoxelTexture helpers + */ + +public: + /** + * Will create Texture if null, and set it + * Returns Texture for convenience + * + * Texture will have the following config: + * Pixel format: PF_R32_FLOAT + * Compression settings: TC_HDR + * SRGB: false + * Filter: TF_Bilinear + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static UTexture2D* CreateOrUpdateTextureFromVoxelFloatTexture(FVoxelFloatTexture VoxelTexture, UPARAM(ref) UTexture2D*& Texture) + { + FVoxelTextureUtilities::CreateOrUpdateUTexture2D(VoxelTexture.Texture, Texture); + return Texture; + } + /** + * Same as CreateOrUpdateTextureFromVoxelFloatTexture with nullptr in input + * + * Texture will have the following config: + * Pixel format: PF_R32_FLOAT + * Compression settings: TC_HDR + * SRGB: false + * Filter: TF_Bilinear + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static UTexture2D* CreateTextureFromVoxelFloatTexture(FVoxelFloatTexture VoxelTexture) + { + UTexture2D* Texture = nullptr; + FVoxelTextureUtilities::CreateOrUpdateUTexture2D(VoxelTexture.Texture, Texture); + return Texture; + } + /** + * Creates a voxel float texture from the color channel of a texture + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static FVoxelFloatTexture CreateVoxelFloatTextureFromTextureChannel(UTexture2D* Texture, EVoxelRGBA Channel) + { + return { FVoxelTextureUtilities::CreateFromTexture_Float(Texture, Channel) }; + } + +public: + /** + * Will create Texture if null, and set it + * Returns Texture for convenience + * + * Texture will have the following config: + * Pixel format: PF_B8G8R8A8 + * Compression settings: TC_VectorDisplacementmap + * SRGB: false + * Filter: TF_Bilinear + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static UTexture2D* CreateOrUpdateTextureFromVoxelColorTexture(FVoxelColorTexture VoxelTexture, UPARAM(ref) UTexture2D*& Texture) + { + FVoxelTextureUtilities::CreateOrUpdateUTexture2D(VoxelTexture.Texture, Texture); + return Texture; + } + /** + * Same as CreateOrUpdateTextureFromVoxelFloatTexture with nullptr in input + * + * Texture will have the following config: + * Pixel format: PF_B8G8R8A8 + * Compression settings: TC_VectorDisplacementmap + * SRGB: false + * Filter: TF_Bilinear + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static UTexture2D* CreateTextureFromVoxelColorTexture(FVoxelColorTexture VoxelTexture) + { + UTexture2D* Texture = nullptr; + FVoxelTextureUtilities::CreateOrUpdateUTexture2D(VoxelTexture.Texture, Texture); + return Texture; + } + /** + * Creates a voxel color texture by putting a float texture into a specific channel + * @param bNormalize If true, the float texture min value will be mapped to 0, and max value to 1 + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static FVoxelColorTexture CreateVoxelColorTextureFromVoxelFloatTexture(FVoxelFloatTexture Texture, EVoxelRGBA Channel, bool bNormalize = true) + { + return { FVoxelTextureUtilities::CreateColorTextureFromFloatTexture(Texture.Texture, Channel, bNormalize) }; + } + +public: + UFUNCTION(BlueprintPure, Category = "Voxel|Voxel Texture") + static FIntPoint GetVoxelFloatTextureSize(FVoxelFloatTexture Texture) + { + return { Texture.Texture.GetSizeX(), Texture.Texture.GetSizeY() }; + } + UFUNCTION(BlueprintPure, Category = "Voxel|Voxel Texture") + static FIntPoint GetVoxelColorTextureSize(FVoxelColorTexture Texture) + { + return { Texture.Texture.GetSizeX(), Texture.Texture.GetSizeY() }; + } + UFUNCTION(BlueprintPure, Category = "Voxel|Voxel Texture") + static bool IsVoxelFloatTextureValid(FVoxelFloatTexture Texture) + { + return FMath::Max(Texture.Texture.GetSizeX(), Texture.Texture.GetSizeY()) > 1; + } + UFUNCTION(BlueprintPure, Category = "Voxel|Voxel Texture") + static bool IsVoxelColorTextureValid(FVoxelFloatTexture Texture) + { + return FMath::Max(Texture.Texture.GetSizeX(), Texture.Texture.GetSizeY()) > 1; + } + +public: + /** + * General helpers + */ + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Utilities") + static void AddNeighborsToSet(const TSet& InSet, TSet& OutSet); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.h new file mode 100644 index 00000000..69720a86 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.h @@ -0,0 +1,532 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelData/VoxelSave.h" +// TODO REMOVE +#include "VoxelData/VoxelDataLock.h" +#include "VoxelDataTools.generated.h" + +class FVoxelData; +class AVoxelWorld; +class UVoxelGenerator; +class UVoxelHeightmapAsset; +template +struct TVoxelHeightmapAssetSamplerWrapper; + +USTRUCT(BlueprintType) +struct FVoxelValueMaterial +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Value = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelMaterial Material = FVoxelMaterial(ForceInit); +}; + +USTRUCT(BlueprintType) +struct FVoxelDataMemoryUsageInMB +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float DirtyValues = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float CachedValues = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float DirtyMaterials = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float CachedMaterials = 0; +}; + +USTRUCT(BlueprintType) +struct FVoxelFindClosestNonEmptyVoxelResult +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bSuccess = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Value = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelMaterial Material = FVoxelMaterial(ForceInit); +}; + +UCLASS() +class VOXEL_API UVoxelDataTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** + * Get the density at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Keywords = "density", DisplayName = "Get Density")) + static void GetValue( + float& Value, + AVoxelWorld* World, + FIntVector Position); + /** + * Get the density at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Keywords = "density", DisplayName = "Get Interpolated Density")) + static void GetInterpolatedValue( + float& Value, + AVoxelWorld* World, + FVector Position); + /** + * Set the density at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + * @param Value Density to set + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Keywords = "density", DisplayName = "Set Density")) + static void SetValue( + AVoxelWorld* World, + FIntVector Position, + float Value); + + /** + * Get the material at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static void GetMaterial( + FVoxelMaterial& Material, + AVoxelWorld* World, + FIntVector Position); + /** + * Set the material at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + * @param Material Material to set + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static void SetMaterial( + AVoxelWorld* World, + FIntVector Position, + FVoxelMaterial Material); + + // Cache the values in the bounds + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World")) + static void CacheValues(AVoxelWorld* World, FVoxelIntBox Bounds, bool bMultiThreaded = true); + // Cache the materials in the bounds + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World")) + static void CacheMaterials(AVoxelWorld* World, FVoxelIntBox Bounds, bool bMultiThreaded = true); + +public: + /** + * Get the density at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", Keywords = "density", AdvancedDisplay = "bHideLatentWarnings", DisplayName = "Get Density Async")) + static void GetValueAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + float& Value, + AVoxelWorld* World, + FIntVector Position, + bool bHideLatentWarnings = false); + /** + * Set the density at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + * @param Value Density to set + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", Keywords = "density", AdvancedDisplay = "bHideLatentWarnings", DisplayName = "Set Density Async")) + static void SetValueAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FIntVector Position, + float Value, + bool bHideLatentWarnings = false); + + /** + * Get the material at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void GetMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelMaterial& Material, + AVoxelWorld* World, + FIntVector Position, + bool bHideLatentWarnings = false); + /** + * Set the material at Position + * @param World The voxel world + * @param Position The voxel position (use the World Position to Voxel function of the VoxelWorld to get it) + * @param Material Material to set + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void SetMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FIntVector Position, + FVoxelMaterial Material, + bool bHideLatentWarnings = false); + + // Cache the values in the bounds + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void CacheValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + // Cache the materials in the bounds + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void CacheMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + /** + * Get a save of the world + * @param World The voxel world + * @param OutSave The save + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static void GetSave( + AVoxelWorld* World, + FVoxelUncompressedWorldSave& OutSave); + static void GetSave( + AVoxelWorld* World, + FVoxelUncompressedWorldSaveImpl& OutSave, + TArray& OutObjects); + /** + * Get a save of the world and compress it + * @param World The voxel world + * @param OutSave The compressed save + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static void GetCompressedSave( + AVoxelWorld* World, + FVoxelCompressedWorldSave& OutSave); + static void GetCompressedSave( + AVoxelWorld* World, + FVoxelCompressedWorldSaveImpl& OutSave, + TArray& OutObjects); + /** + * Get a save of the world + * @param World The voxel world + * @param OutSave The save + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void GetSaveAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelUncompressedWorldSave& OutSave, + bool bHideLatentWarnings = false); + /** + * Get a save of the world and compress it + * @param World The voxel world + * @param OutSave The compressed save + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void GetCompressedSaveAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelCompressedWorldSave& OutSave, + bool bHideLatentWarnings = false); + + /** + * Load from a save + * @param World The voxel world + * @param Save The save to load from + * @return If the load was successful + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static bool LoadFromSave( + const AVoxelWorld* World, + const FVoxelUncompressedWorldSave& Save); + static bool LoadFromSave( + const AVoxelWorld* World, + const FVoxelUncompressedWorldSaveImpl& Save, + const TArray& Objects); + + /** + * Load from a compressed save + * @param World The voxel world + * @param Save The compressed save to load from + * @return If the load was successful + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static bool LoadFromCompressedSave( + const AVoxelWorld* World, + const FVoxelCompressedWorldSave& Save); + static bool LoadFromCompressedSave( + const AVoxelWorld* World, + const FVoxelCompressedWorldSaveImpl& Save, + const TArray& Objects); + +public: + // Bounds.Extend(2) must be locked! + // Bounds can be FVoxelIntBox::Infinite + static void RoundVoxelsImpl(FVoxelData& Data, const FVoxelIntBox& Bounds); + + // Round voxels that don't have an impact on the surface. Same visual result but will lead to better compression + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data") + static void RoundVoxels(AVoxelWorld* World, FVoxelIntBox Bounds); + + // Round voxels that don't have an impact on the surface. Same visual result but will lead to better compression + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void RoundVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + // Bounds.Extend(1) must be locked! + // Bounds can be FVoxelIntBox::Infinite + static void ClearUnusedMaterialsImpl(FVoxelData& Data, const FVoxelIntBox& Bounds); + + // Remove materials that do not affect the surface. Same visual result but will lead to better compression. + // Digging will look different. + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data") + static void ClearUnusedMaterials(AVoxelWorld* World, FVoxelIntBox Bounds); + + // Remove materials that do not affect the surface. Same visual result but will lead to better compression. + // Digging will look different. + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void ClearUnusedMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + static void GetVoxelsValueAndMaterialImpl( + FVoxelData& Data, + TArray& Voxels, + const FVoxelIntBox& Bounds, + const TArray& Positions); + + // Read a large number of voxels at a time + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World")) + static void GetVoxelsValueAndMaterial( + TArray& Voxels, + AVoxelWorld* World, + const TArray& Positions); + + // Read a large number of voxels at a time, asynchronously + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void GetVoxelsValueAndMaterialAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& Voxels, + AVoxelWorld* World, + const TArray& Positions, + bool bHideLatentWarnings = false); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Memory", meta = (DefaultToSelf = "World")) + static FVoxelDataMemoryUsageInMB GetDataMemoryUsageInMB(AVoxelWorld* World); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World")) + static void ClearCachedValues(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void ClearCachedValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World")) + static void ClearCachedMaterials(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data|Cache", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void ClearCachedMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void CheckForSingleValues(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void CheckForSingleValuesAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void CheckForSingleMaterials(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void CheckForSingleMaterialsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + // Requires full write lock + template + static void CompressIntoHeightmapImpl(FVoxelData& Data, TVoxelHeightmapAssetSamplerWrapper& Wrapper, bool bCheckAllLeaves); + + /** + * If the voxel generator is a heightmap or if an heightmap asset is provided, + * will update the heightmap to the max Z surface in the voxel world + * Will not edit the data: RoundToGenerator should be called after for best results + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void CompressIntoHeightmap( + AVoxelWorld* World, + UVoxelHeightmapAsset* HeightmapAsset = nullptr, + bool bHeightmapAssetMatchesWorld = false); + +public: + // Bounds.Extend(1) needs to be locked to read the additional densities! + template + static void MergeDistanceFieldImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + T1 GetSDF, + T2 MergeSDF, + bool bMultiThreaded, + bool bSetMaterials = false, + T3 GetMaterial = [](float OldValue, float NewValue, FVoxelMaterial PreviousMaterial) { return PreviousMaterial; }); + +public: + // Requires write lock. + static void RoundToGeneratorImpl(FVoxelData& Data, const FVoxelIntBox& Bounds, bool bPreserveNormals); + + // Will revert the values who don't have a voxel neighbor with a different sign from the generator value + // Will ignore items when computing generator values + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void RoundToGenerator(AVoxelWorld* World, FVoxelIntBox Bounds, bool bPreserveNormals = true); + + // Will revert the values who don't have a voxel neighbor with a different sign from the generator value + // Will ignore items when computing generator values + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void RoundToGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bPreserveNormals = true, + bool bHideLatentWarnings = false); + +public: + // Requires write lock. + static void CheckIfSameAsGeneratorImpl(FVoxelData& Data, const FVoxelIntBox& Bounds); + + // Will undirty the chunks identical to the generator + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void CheckIfSameAsGenerator(AVoxelWorld* World, FVoxelIntBox Bounds); + + // Will undirty the chunks identical to the generator + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void CheckIfSameAsGeneratorAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bHideLatentWarnings = false); + +public: + template + static void SetBoxAsDirtyImpl(FVoxelData& Data, const FVoxelIntBox& Bounds, bool bCompress); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World")) + static void SetBoxAsDirty(AVoxelWorld* World, FVoxelIntBox Bounds, bool bDirtyValues = true, bool bDirtyMaterials = true); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void SetBoxAsDirtyAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bDirtyValues = true, + bool bDirtyMaterials = true, + bool bHideLatentWarnings = false); + +public: + // All neighbors of Position need to be locked (ie, FVoxelIntBox(Position, Position + 1)) + static FVoxelFindClosestNonEmptyVoxelResult FindClosestNonEmptyVoxelImpl( + FVoxelData& Data, + const FVoxelVector& Position, + bool bReadMaterial); + + /** + * Finds the closest voxel to Position that is not empty + * This is useful to do edits, or to query the material at a position + * + * @param World The voxel world + * @param Position The position, in world space if bConvertToVoxelSpace is true + * @param bReadMaterial If false will not read the material + * @param bConvertToVoxelSpace If true, will convert Position to voxel space. If false, assumes it's already in voxels + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", AdvancedDisplay = "bReadMaterial, bConvertToVoxelSpace")) + static void FindClosestNonEmptyVoxel( + FVoxelFindClosestNonEmptyVoxelResult& Result, + AVoxelWorld* World, + FVector Position, + bool bReadMaterial = true, + bool bConvertToVoxelSpace = true); + + /** + * Finds the closest voxel to Position that is not empty + * This is useful to do edits, or to query the material at a position + * + * @param World The voxel world + * @param Position The position, in world space if bConvertToVoxelSpace is true + * @param bReadMaterial If false will not read the material + * @param bConvertToVoxelSpace If true, will convert Position to voxel space. If false, assumes it's already in voxels + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Data", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bReadMaterial, bConvertToVoxelSpace, bHideLatentWarnings")) + static void FindClosestNonEmptyVoxelAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelFindClosestNonEmptyVoxelResult& Result, + AVoxelWorld* World, + FVector Position, + bool bReadMaterial = true, + bool bConvertToVoxelSpace = true, + bool bHideLatentWarnings = false); + +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.inl new file mode 100644 index 00000000..caeedc27 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelDataTools.inl @@ -0,0 +1,78 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" + +template +void UVoxelDataTools::MergeDistanceFieldImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + T1 GetSDF, + T2 MergeSDF, + bool bMultiThreaded, + bool bSetMaterials, + T3 GetMaterial) +{ + VOXEL_TOOL_FUNCTION_COUNTER(Bounds.Count()); + + const FIntVector Size = Bounds.Size(); + + const TArray Values = Data.ParallelGet(Bounds.Extend(1) /* See GetSurfacePositionsFromDensities */, !bMultiThreaded); + + TArray Distances; + TArray SurfacePositions; + FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities(Size, Values, Distances, SurfacePositions); + FVoxelDistanceFieldUtilities::JumpFlood(Size, SurfacePositions, EVoxelComputeDevice::GPU); + FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(Size, SurfacePositions, Distances); + + FVoxelDebug::Broadcast("Values", Bounds.Size(), Data.Get(Bounds)); + FVoxelDebug::Broadcast("Distances", Bounds.Size(), Distances); + + const auto Set = [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material, auto bSetMaterials_Static) + { + checkVoxelSlow(Bounds.Contains(X, Y, Z)); + + const float OtherSDF = GetSDF(X, Y, Z); + const float OldSDF = FVoxelUtilities::Get3D(Distances, Size, X, Y, Z, Bounds.Min); + const float NewSDF = MergeSDF(OldSDF, OtherSDF); + + Value = FVoxelValue(NewSDF); + + if (bSetMaterials_Static) + { + Material = GetMaterial(OldSDF, NewSDF, Material); + } + }; + + if (bSetMaterials) + { + Data.ParallelSet(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value, FVoxelMaterial& Material) + { + Set(X, Y, Z, Value, Material, FVoxelUtilities::FTrueType()); + }, !bMultiThreaded); + } + else + { + // TODO optional + // TODO check surface position is in bounds + Data.ParallelSet(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelMaterial& Material) + { + const FVector SurfacePosition = FVoxelUtilities::Get3D(SurfacePositions, Size, X, Y, Z, Bounds.Min); + const auto Result = FindClosestNonEmptyVoxelImpl(Data, FVoxelVector(Bounds.Min) + SurfacePosition, true); + + if (Result.bSuccess) + { + Material = Result.Material; + } + }, !bMultiThreaded); + Data.ParallelSet(Bounds, [&](int32 X, int32 Y, int32 Z, FVoxelValue& Value) + { + FVoxelMaterial Material; + Set(X, Y, Z, Value, Material, FVoxelUtilities::FFalseType()); + }, !bMultiThreaded); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelHardnessHandler.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelHardnessHandler.h new file mode 100644 index 00000000..2c058bf5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelHardnessHandler.h @@ -0,0 +1,92 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelWorld.h" +#include "VoxelMaterial.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +struct VOXEL_API FVoxelHardnessHandler +{ +public: + explicit FVoxelHardnessHandler(const AVoxelWorld& World); + + FORCEINLINE float GetHardness(const FVoxelMaterial& Material) const + { + return FMath::Max(GetHardnessInternal(Material), KINDA_SMALL_NUMBER); + } + FORCEINLINE bool NeedsToCompute() const + { + return bNeedsToCompute; + } + +private: + const EVoxelMaterialConfig MaterialConfig; + const EVoxelRGBHardness RGBHardness; + TVoxelStaticArray Hardness; + bool bNeedsToCompute; + + FORCEINLINE float GetHardnessInternal(const FVoxelMaterial& Material) const + { + switch (MaterialConfig) + { + case EVoxelMaterialConfig::RGB: + { + switch (RGBHardness) + { + case EVoxelRGBHardness::FourWayBlend: + { + const auto Strengths = FVoxelUtilities::GetFourWayBlendStrengths(Material); + return + Hardness[0] * Strengths[0] + + Hardness[1] * Strengths[1] + + Hardness[2] * Strengths[2] + + Hardness[3] * Strengths[3]; + } + case EVoxelRGBHardness::FiveWayBlend: + { + const auto Strengths = FVoxelUtilities::GetFiveWayBlendStrengths(Material); + return + Hardness[0] * Strengths[0] + + Hardness[1] * Strengths[1] + + Hardness[2] * Strengths[2] + + Hardness[3] * Strengths[3] + + Hardness[4] * Strengths[4]; + } + case EVoxelRGBHardness::R: return Material.GetR_AsFloat(); + case EVoxelRGBHardness::G: return Material.GetG_AsFloat(); + case EVoxelRGBHardness::B: return Material.GetB_AsFloat(); + case EVoxelRGBHardness::A: return Material.GetA_AsFloat(); + case EVoxelRGBHardness::U0: return Material.GetU0_AsFloat(); + case EVoxelRGBHardness::U1: return Material.GetU1_AsFloat(); + case EVoxelRGBHardness::V0: return Material.GetV0_AsFloat(); + case EVoxelRGBHardness::V1: return Material.GetV1_AsFloat(); + default: + { + checkVoxelSlow(false); + return 1; + } + } + } + case EVoxelMaterialConfig::SingleIndex: + { + return Hardness[Material.GetSingleIndex()]; + } + case EVoxelMaterialConfig::MultiIndex: + { + const auto Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + return + Hardness[Material.GetMultiIndex_Index0()] * Strengths[0] + + Hardness[Material.GetMultiIndex_Index1()] * Strengths[1] + + Hardness[Material.GetMultiIndex_Index2()] * Strengths[2] + + Hardness[Material.GetMultiIndex_Index3()] * Strengths[3]; + } + default: + { + checkVoxelSlow(false); + return 1; + } + } + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelMathLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelMathLibrary.h new file mode 100644 index 00000000..fe22bd18 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelMathLibrary.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelMathLibrary.generated.h" + +USTRUCT(BlueprintType, meta = (HasNativeMake = "Voxel.VoxelMathLibrary.MakeHaltonStream")) +struct FVoxelHaltonStream +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config") + int32 InitialSeed = 0; + + UPROPERTY() + mutable uint32 Seed = 0; +}; + +UCLASS() +class VOXEL_API UVoxelMathLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** + * Generates a random position on the unit sphere, given some random input between 0 and 1 + * @param Random Random values, between 0 and 1. Can use RandomFloat, but also more complex noises like Halton or Sobol + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Math|Random") + static FVector GetUnitVectorFromRandom(FVector2D Random); + +public: + UFUNCTION(BlueprintPure, meta = (Keywords = "construct build", NativeMakeFunc), Category = "Voxel|Math|Random") + static FVoxelHaltonStream MakeHaltonStream(int32 InitialSeed); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Math|Random") + static void ResetHaltonStream(const FVoxelHaltonStream& Stream); + + UFUNCTION(BlueprintPure, Category = "Voxel|Math|Random") + static float GetHalton1D(const FVoxelHaltonStream& Stream); + + UFUNCTION(BlueprintPure, Category = "Voxel|Math|Random") + static FVector2D GetHalton2D(const FVoxelHaltonStream& Stream); + + UFUNCTION(BlueprintPure, Category = "Voxel|Math|Random") + static FVector GetHalton3D(const FVoxelHaltonStream& Stream); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPaintMaterial.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPaintMaterial.h new file mode 100644 index 00000000..a9ee781d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPaintMaterial.h @@ -0,0 +1,215 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelMaterial.h" +#include "VoxelPaintMaterial.generated.h" + +class UMaterialInterface; +class UVoxelMaterialCollectionBase; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterial_MaterialCollectionChannel +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + uint8 Channel = 0; + + operator uint8() const{ return Channel; } +}; + +UENUM(BlueprintType) +enum class EVoxelPaintMaterialType : uint8 +{ + Color, + FiveWayBlend, + SingleIndex, + MultiIndex, + MultiIndexWetness, + MultiIndexRaw, + UV +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialColor +{ + GENERATED_BODY() + + // Set to true if you want to use the unreal color picker + // Set to false if you want to set the bytes manually + // + // The unreal color picker will write linear colors to LinearColor, and sRGB colors to Color + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bUseLinearColor = true; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FLinearColor LinearColor = FLinearColor::Transparent; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FColor Color = FColor::Transparent; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintR = true; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintG = true; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintB = true; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintA = true; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialFiveWayBlend +{ + GENERATED_BODY() + + // Between 0 and 4. 1,2,3,4 => R,G,B,A. 0 => material displayed by default + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 4, ClampMin = 0, ClampMax = 4)) + int32 Channel = 0; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1, ClampMin = 0, ClampMax = 1)) + float TargetValue = 1.f; + + // These channels will have their strength locked, and will stay the same + // Useful eg to paint _under_ rocks: lock the rock channel, and paint the channel you want to put under them + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + TArray LockedChannels; + + // If true, will ignore Alpha + UPROPERTY(BlueprintReadWrite, EditAnywhere, AdvancedDisplay, Category = "Voxel") + bool bFourWayBlend = false; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialSingleIndex +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialMultiIndex +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1, ClampMin = 0, ClampMax = 1)) + float TargetValue = 1.f; + + // These channels will have their strength locked, and will stay the same + // Useful eg to paint _under_ rocks: lock the rock channel, and paint the channel you want to put under them + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + TArray LockedChannels; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialMultiIndexWetness +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1, ClampMin = 0, ClampMax = 1)) + float TargetValue = 1.f; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialMultiIndexRaw +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel0; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1)) + float Strength0 = 0.f; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel1; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1)) + float Strength1 = 0.f; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel2; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1)) + float Strength2 = 0.f; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterial_MaterialCollectionChannel Channel3; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 1)) + float Strength3 = 0.f; +}; + +USTRUCT(BlueprintType) +struct FVoxelPaintMaterialUV +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel", meta = (UIMin = 0, UIMax = 4)) + int32 Channel = 0; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVector2D UV = FVector2D::ZeroVector; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintU = true; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + bool bPaintV = true; +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelPaintMaterial +{ + GENERATED_BODY() + +public: + FVoxelPaintMaterial() = default; + + void ApplyToMaterial(FVoxelMaterial& Material, float Strength) const; + +public: +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient, EditAnywhere, Category = "Voxel") + bool bRestrictType = false; + + UPROPERTY(Transient, EditAnywhere, Category = "Voxel") + EVoxelMaterialConfig MaterialConfigToRestrictTo = EVoxelMaterialConfig::RGB; + + UPROPERTY(Transient, EditAnywhere, Category = "Voxel") + UVoxelMaterialCollectionBase* PreviewMaterialCollection = nullptr; +#endif + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + EVoxelPaintMaterialType Type = EVoxelPaintMaterialType::FiveWayBlend; + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialColor Color; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialSingleIndex SingleIndex; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialMultiIndex MultiIndex; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialMultiIndexWetness MultiIndexWetness; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialMultiIndexRaw MultiIndexRaw; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialUV UV; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Voxel") + FVoxelPaintMaterialFiveWayBlend FiveWayBlend; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysics.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysics.h new file mode 100644 index 00000000..db5a77f8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysics.h @@ -0,0 +1,73 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelIntBox.h" +#include "VoxelPhysicsPartSpawnerInterface.h" +#include "VoxelPhysics.generated.h" + +class FVoxelData; +class AVoxelWorld; + +struct FVoxelFloatingPart +{ + FIntVector PartCenter; + TVoxelSharedPtr Data; + TArray Voxels; +}; + +struct FVoxelRemoveFloatingPartsResult +{ + FVoxelIntBoxWithValidity BoxToUpdate; + TArray Parts; +}; + +UCLASS() +class VOXEL_API UVoxelPhysicsTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // Can be run async + static void RemoveFloatingParts( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + int32 MinParts, + bool bCreateData, + bool bCreateVoxels, + bool bDebug, + TWeakObjectPtr DebugWorld, + FVoxelRemoveFloatingPartsResult& OutResult); + + // Must be run on the game thread + static TArray> SpawnFloatingPartsAndUpdateWorld( + IVoxelPhysicsPartSpawner& PartSpawner, + AVoxelWorld* World, + FVoxelRemoveFloatingPartsResult&& RemoveFloatingPartsResult); + +public: + /** + * Apply voxel physics in a section of the voxel world by removing floating parts + * @param Results Each part spawner result correspond to an individual floating part. Cast them to their corresponding class to get info from them. + * @param World The voxel world + * @param Bounds The bounds to search in (caution: keep this small!) + * @param PartSpawner The part spawner that will handle the spawning of new parts (construct a new object of class VoxelPhysicsPartSpawner_Smthg) + * If null will just remove the floating parts + * @param MinParts The minimum number of parts (inclusive) to have before removing them. This is useful to avoid considering the whole world as a floating part + * @param bHideLatentWarnings Hide latent warnings + */ + UFUNCTION(BlueprintCallable, Category = "Voxel", meta = (DefaultToSelf = "World", Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "MinParts, bDebug, bHideLatentWarnings")) + static void ApplyVoxelPhysics( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray>& Results, + AVoxelWorld* World, + FVoxelIntBox Bounds, + TScriptInterface PartSpawner, + int32 MinParts = 2, + bool bDebug = false, + bool bHideLatentWarnings = false); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h new file mode 100644 index 00000000..0bfdd774 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h @@ -0,0 +1,133 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Templates/SubclassOf.h" +#include "VoxelPhysicsPartSpawnerInterface.h" +#include "VoxelPhysicsPartSpawner.generated.h" + +class AStaticMeshActor; +class UStaticMesh; +class UMaterialInterface; +class FVoxelData; +class AVoxelWorld; + +UCLASS(BlueprintType) +class VOXEL_API UVoxelPhysicsPartSpawnerResult_VoxelWorlds : public UObject, public IVoxelPhysicsPartSpawnerResult +{ + GENERATED_BODY() + +public: + // The voxel world representing this part + UPROPERTY(BlueprintReadOnly, Category = "Voxel") + AVoxelWorld* VoxelWorld; +}; + +// Will spawn a voxel world per part +// The voxel worlds are return in the Results +// You can configure each voxel world by binding ConfigureVoxelWorld: this callback will be run before creating the voxel worlds +// The voxel worlds will be created when the initial world update will be done +// By default this will enable SimulatePhysics on the new world, and set the CollisionTraceFlag to SimpleAndComplex +UCLASS(BlueprintType, Blueprintable) +class VOXEL_API UVoxelPhysicsPartSpawner_VoxelWorlds : public UObject, public IVoxelPhysicsPartSpawner +{ + GENERATED_BODY() + +public: + DECLARE_DYNAMIC_DELEGATE_OneParam(FConfigureVoxelWorld, AVoxelWorld*, VoxelWorld); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FConfigureVoxelWorld ConfigureVoxelWorld; + + // All the voxel world properties will be overriden! Use this for events, and use ConfigureVoxelWorld if you want to set properties + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TSubclassOf VoxelWorldClass; + +public: + virtual TScriptInterface SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) override; + + virtual bool NeedData() const override { return true; } +}; + +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(BlueprintType) +class VOXEL_API UVoxelPhysicsPartSpawnerResult_Cubes : public UObject, public IVoxelPhysicsPartSpawnerResult +{ + GENERATED_BODY() + +public: + // The cubes for this part + UPROPERTY(BlueprintReadOnly, Category = "Voxel") + TArray Cubes; +}; + +// Will spawn a cube actor for each floating voxel +// Can get the spawned actors in Cubes +// The actors will have physics enabled once the voxel world will be updated (to avoid spawning cubes inside the world) +UCLASS(BlueprintType, Blueprintable) +class VOXEL_API UVoxelPhysicsPartSpawner_Cubes : public UObject, public IVoxelPhysicsPartSpawner +{ + GENERATED_BODY() + +public: + UVoxelPhysicsPartSpawner_Cubes(); + + // Same material as the voxel world, but instead of a vertex color input use a vector parameter named VertexColor + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UMaterialInterface* Material; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + UStaticMesh* CubeMesh; + + // Spawn probability for each cube. Use this to reduce the amount of cubes spawned. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float SpawnProbability = 1.f; + +public: + virtual TScriptInterface SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) override; + + virtual bool NeedVoxels() const override { return true; } +}; + +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(BlueprintType) +class VOXEL_API UVoxelPhysicsPartSpawnerResult_GetVoxels : public UObject, public IVoxelPhysicsPartSpawnerResult +{ + GENERATED_BODY() + +public: + // The voxels inside this part + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray Voxels; +}; + +// The data is accessible immediately in the result +UCLASS(BlueprintType, Blueprintable) +class VOXEL_API UVoxelPhysicsPartSpawner_GetVoxels : public UObject, public IVoxelPhysicsPartSpawner +{ + GENERATED_BODY() + +public: + virtual TScriptInterface SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) override; + + virtual bool NeedVoxels() const override { return true; } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h new file mode 100644 index 00000000..a34f9166 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h @@ -0,0 +1,75 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterial.h" +#include "VoxelMinimal.h" +#include "UObject/Interface.h" +#include "VoxelPhysicsPartSpawnerInterface.generated.h" + +class AStaticMeshActor; +class UStaticMesh; +class UMaterialInterface; +class FVoxelData; +class AVoxelWorld; + +USTRUCT(BlueprintType) +struct FVoxelPositionValueMaterial +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Value = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelMaterial Material = FVoxelMaterial(ForceInit); +}; + +// Represents the result for a single part +UINTERFACE(BlueprintType) +class VOXEL_API UVoxelPhysicsPartSpawnerResult : public UInterface +{ + GENERATED_BODY() +}; + +class VOXEL_API IVoxelPhysicsPartSpawnerResult : public IInterface +{ + GENERATED_BODY() +}; + + +UINTERFACE(BlueprintType) +class VOXEL_API UVoxelPhysicsPartSpawner : public UInterface +{ + GENERATED_BODY() +}; + +class VOXEL_API IVoxelPhysicsPartSpawner : public IInterface +{ + GENERATED_BODY() + +public: + /** + * @param OutOnWorldUpdateDone Will be triggered once the world update of the removed voxels will be done + * @param World The voxel world + * @param Data The data of this voxel part + * @param Voxels Voxels in this part + * @param PartPosition The position of this part in the voxel world voxel space + */ + virtual TScriptInterface SpawnPart( + TVoxelSharedPtr& OutOnWorldUpdateDone, + AVoxelWorld* World, + TVoxelSharedPtr&& Data, + TArray&& Voxels, + const FIntVector& PartPosition) + { + return nullptr; + } + + virtual bool NeedData() const { return false; } + virtual bool NeedVoxels() const { return false; } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelProjectionTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelProjectionTools.h new file mode 100644 index 00000000..e17f8c21 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelProjectionTools.h @@ -0,0 +1,218 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Kismet/KismetSystemLibrary.h" +#include "CollisionQueryParams.h" +#include "VoxelTools/VoxelSurfaceTools.h" +#include "VoxelProjectionTools.generated.h" + +class AVoxelWorld; + +USTRUCT(BlueprintType, meta = (HasNativeMake="Voxel.VoxelProjectionTools.MakeVoxelLineTraceParameters")) +struct FVoxelLineTraceParameters +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TEnumAsByte CollisionChannel = ECollisionChannel::ECC_WorldDynamic; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray> CollisionChannelsToIgnore; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TArray ActorsToIgnore; + + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Voxel") + TEnumAsByte DrawDebugType = EDrawDebugTrace::None; + + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Voxel") + FLinearColor TraceColor = FLinearColor::Red; + + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Voxel") + FLinearColor TraceHitColor = FLinearColor::Green; + + UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadWrite, Category = "Voxel") + float DrawTime = 5.0f; + + FCollisionQueryParams GetParams() const; + FCollisionResponseContainer GetResponseContainer() const; + void DrawDebug(const UWorld* World, const FVector& Start, const FVector& End, bool bHit, const FHitResult& OutHit) const; +}; + +UENUM(BlueprintType) +enum class EVoxelProjectionShape : uint8 +{ + Circle, + Square +}; + +USTRUCT(BlueprintType) +struct FVoxelProjectionHit +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector VoxelPosition = FIntVector(ForceInit); + + // Position on the projection plane + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVector2D PlanePosition = FVector2D(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FHitResult Hit = FHitResult(ForceInit); +}; + +UCLASS() +class VOXEL_API UVoxelProjectionTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // Needed because else we need to add 2 MakeArrays node... + + // Make voxel line trace parameters + UFUNCTION(BlueprintPure, Category = "Voxel", meta = (AdvancedDisplay = "CollisionChannelsToIgnore, ActorsToIgnore, DrawDebugType, TraceColor, TraceHitColor, DrawTime", AutoCreateRefTerm = "CollisionChannelsToIgnore, ActorsToIgnore")) + static FVoxelLineTraceParameters MakeVoxelLineTraceParameters( + TArray> CollisionChannelsToIgnore, + TArray ActorsToIgnore, + TEnumAsByte CollisionChannel = ECollisionChannel::ECC_WorldDynamic, + TEnumAsByte DrawDebugType = EDrawDebugTrace::None, + FLinearColor TraceColor = FLinearColor::Red, + FLinearColor TraceHitColor = FLinearColor::Green, + float DrawTime = 5.0f); + +public: + /** + * Find voxels using linetraces + * @param World The voxel world + * @param Parameters Linetraces parameters + * @param Position The center of the linetraces + * @param Direction The direction of the linetraces + * @param Radius The radius in world space (cm) + * @param Shape The shape of the rays start positions + * @param NumRays The approximate number of rays to trace + * @param MaxDistance The max ray distance + * @return Number of rays actually traced (should be close to NumRays) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools", meta = (DefaultToSelf = "World")) + static int32 FindProjectionVoxels( + TArray& Hits, + AVoxelWorld* World, + FVoxelLineTraceParameters Parameters, + FVector Position, + FVector Direction, + float Radius = 100.f, + EVoxelProjectionShape Shape = EVoxelProjectionShape::Circle, + float NumRays = 100.f, + float MaxDistance = 1e9); + + /** + * Find voxels using linetraces, asynchronously + * @param World The voxel world + * @param Parameters Linetraces parameters + * @param Position The center of the linetraces + * @param Direction The direction of the linetraces + * @param Radius The radius in world space (cm) + * @param Shape The shape of the rays start positions + * @param NumRays The approximate number of rays to trace + * @param MaxDistance The max ray distance + * @return Number of rays actually traced (should be close to NumRays) + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static int32 FindProjectionVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray& Hits, + AVoxelWorld* World, + FVoxelLineTraceParameters Parameters, + FVector Position, + FVector Direction, + float Radius = 100.f, + EVoxelProjectionShape Shape = EVoxelProjectionShape::Circle, + float NumRays = 100.f, + float MaxDistance = 1e9, + bool bHideLatentWarnings = false); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools") + static TArray GetHitsPositions(const TArray& Hits); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools") + static FVector GetHitsAverageNormal(const TArray& Hits); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools") + static FVector GetHitsAveragePosition(const TArray& Hits); + + // For some surface tools you'll need to use CreateSurfaceVoxelsFromHitsWithExactValues instead + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools") + static FVoxelSurfaceEditsVoxels CreateSurfaceVoxelsFromHits(const TArray& Hits); + + // Will store the voxel values + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Projection Tools", meta = (DefaultToSelf = "World")) + static FVoxelSurfaceEditsVoxels CreateSurfaceVoxelsFromHitsWithExactValues(AVoxelWorld* World, const TArray& Hits); + +public: + // NumRays might be slightly lower than the actual number of traced rays + // Returns num rays actually traced + template + static int32 GenerateRays( + const FVector& Position, + const FVector& Direction, + const float Radius, + const EVoxelProjectionShape Shape, + const float NumRays, + const float MaxDistance, + T Lambda) + { + if (!ensure(Direction.IsNormalized())) return 0; + if (NumRays <= 0) return 0; + + const auto GetTangent = [](const FVector& N) + { + // Compute tangent + // N dot T = 0 + // <=> N.X * T.X + N.Y * T.Y + N.Z * T.Z = 0 + // <=> T.Z = -1 / N.Z * (N.X * T.X + N.Y * T.Y) if N.Z != 0 + if (N.Z != 0) + { + return FVector(1, 1, -1 / double(N.Z) * (N.X + N.Y)).GetSafeNormal(); + } + else + { + return FVector(0, 0, 1); + } + }; + + const FVector Tangent = GetTangent(Direction); + const FVector BiTangent = FVector::CrossProduct(Tangent, Direction).GetSafeNormal(); + // NumRays is the area; get the radius we would get from such area + const float NumRaysInRadius = + Shape == EVoxelProjectionShape::Circle + ? FMath::Sqrt(NumRays / PI) + : FMath::Sqrt(NumRays) / 2; + const int32 Count = FMath::CeilToInt(NumRaysInRadius); + const float RadiusSquared = FMath::Square(Radius); + + int32 NumRaysActuallyTraced = 0; + for (int32 U = -Count; U <= Count; U++) + { + for (int32 V = -Count; V <= Count; V++) + { + const FVector2D PlanePosition = FVector2D(U, V) * Radius / Count; + if (Shape == EVoxelProjectionShape::Circle && PlanePosition.SizeSquared() >= RadiusSquared) + { + continue; + } + + const FVector Start = Position + (Tangent * PlanePosition.X + BiTangent * PlanePosition.Y); + const FVector End = Start + Direction * MaxDistance; + Lambda(Start, End, PlanePosition); + NumRaysActuallyTraced++; + } + } + return NumRaysActuallyTraced; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceEdits.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceEdits.h new file mode 100644 index 00000000..b2bebc74 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceEdits.h @@ -0,0 +1,120 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelSharedPtr.h" +#include "VoxelSurfaceEdits.generated.h" + +USTRUCT(BlueprintType) +struct FVoxelSurfaceEditsVoxelBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FIntVector Position = FIntVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVector Normal = FVector(ForceInit); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Value = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVector SurfacePosition = FVector(ForceInit); +}; + +USTRUCT(BlueprintType) +struct FVoxelSurfaceEditsVoxel : public FVoxelSurfaceEditsVoxelBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float Strength = 1; + +public: + FVoxelSurfaceEditsVoxel() = default; + FVoxelSurfaceEditsVoxel(const FVoxelSurfaceEditsVoxelBase& Other) + : FVoxelSurfaceEditsVoxelBase(Other) + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +struct FVoxelSurfaceEditsVoxelsInfo +{ + bool bHasValues = false; + bool bHasExactDistanceField = false; + bool bHasNormals = false; + bool bHasSurfacePositions = false; + bool bIs2D = false; +}; + +USTRUCT(BlueprintType) +struct FVoxelSurfaceEditsVoxels +{ + GENERATED_BODY() + + FVoxelSurfaceEditsVoxelsInfo Info; + TVoxelSharedRef> Voxels = MakeVoxelShared>(); +}; + +USTRUCT(BlueprintType) +struct FVoxelSurfaceEditsProcessedVoxels +{ + GENERATED_BODY() + + FVoxelIntBox Bounds; + FVoxelSurfaceEditsVoxelsInfo Info; + TVoxelSharedRef> Voxels = MakeVoxelShared>(); +}; + +/////////////////////////////////////////////////////////////////////////////// + +namespace EVoxelSurfaceEditsStackElementFlags +{ + enum Type : uint32 + { + None = 0, + NeedValues = 1 << 0, + NeedNormals = 1 << 1, + ShouldBeLast = 1 << 2, + }; +} + +USTRUCT(BlueprintType) +struct FVoxelSurfaceEditsStackElement +{ + GENERATED_BODY() + + using FApply = TFunction& /*Voxels*/)>; + + FString Name; + uint32 Flags = EVoxelSurfaceEditsStackElementFlags::None; + FApply Apply; + + FVoxelSurfaceEditsStackElement() = default; + + FVoxelSurfaceEditsStackElement(const FString& Name, uint32 Flags, const FApply& Apply) + : Name(Name) + , Flags(Flags) + , Apply(Apply) + { + } +}; + +USTRUCT(BlueprintType) +struct VOXEL_API FVoxelSurfaceEditsStack +{ + GENERATED_BODY() + + TArray Stack; + + void Add(const FVoxelSurfaceEditsStackElement& Element) { Stack.Add(Element); } + + bool HasErrors(const FVoxelSurfaceEditsVoxels& Voxels, FString& OutErrors) const; + // Set bComputeBounds if you already have valid bounds & will use the Impl functions + FVoxelSurfaceEditsProcessedVoxels Execute(const FVoxelSurfaceEditsVoxels& Voxels, bool bComputeBounds = true) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceTools.h new file mode 100644 index 00000000..35b55327 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceTools.h @@ -0,0 +1,278 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelTexture.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelTools/Gen/VoxelToolsBase.h" +#include "VoxelTools/VoxelSurfaceEdits.h" +#include "VoxelSurfaceTools.generated.h" + +struct FVoxelHardnessHandler; +struct FLatentActionInfo; +class FVoxelData; +class UCurveFloat; +class AVoxelWorld; + +UENUM(BlueprintType) +enum class EVoxelSDFMergeMode : uint8 +{ + // Additive mode: will only grow the surface + Union, + // Destructive mode: will only shrink the surface + Intersection, + // Will add and remove at the same time + Override +}; + +UCLASS() +class VOXEL_API UVoxelSurfaceTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // DirectionMask: if not 0, will only add full voxels with an empty voxel in a direction that's in DirectionMask + template + static FVoxelSurfaceEditsVoxels FindSurfaceVoxelsImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bComputeNormals, + bool bOnlyOutputNonEmptyVoxels = false); + + // TODO bComputeNormals + static FVoxelSurfaceEditsVoxels FindSurfaceVoxelsFromDistanceFieldImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bMultiThreaded, + EVoxelComputeDevice ComputeDevice); + + static FVoxelSurfaceEditsVoxels FindSurfaceVoxels2DImpl( + FVoxelData& Data, + const FVoxelIntBox& Bounds, + bool bComputeNormals); + + /** + * Find voxels that are on the surface. Faster than FindSurfaceVoxelsFromDistanceField, but the values won't be exact distances + * @param World The voxel world + * @param Bounds Bounds to look in + * @param bComputeNormals If true, compute the voxel normals. More expensive, but required for some functions. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World")) + static void FindSurfaceVoxels( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals = false); + + /** + * Find voxels that are on the surface. Faster than FindSurfaceVoxelsFromDistanceField, but the values won't be exact distances + * @param World The voxel world + * @param Bounds Bounds to look in + * @param bComputeNormals If true, compute the voxel normals. More expensive, but required for some functions. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void FindSurfaceVoxelsAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals = false, + bool bHideLatentWarnings = false); + + /** + * Find voxels that are on the surface using an exact computation of the distance field using the GPU + * @param World The voxel world + * @param Bounds Bounds to look in + * @param bMultiThreaded If true will multithread the CPU loops + * @param ComputeDevice Whether to use the GPU or not + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World", AdvancedDisplay = "bMultiThreaded, ComputeDevice")) + static void FindSurfaceVoxelsFromDistanceField( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bMultiThreaded = false, + EVoxelComputeDevice ComputeDevice = EVoxelComputeDevice::GPU); + + /** + * Find voxels that are on the surface. Only keep the one with the surface right above them that are facing up. If 2 surface voxels have the same X Y, will only keep the one with the higher Z + * @param World The voxel world + * @param Bounds Bounds to look in + * @param bComputeNormals If true, compute the voxel normals. More expensive, but required for some functions. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World")) + static void FindSurfaceVoxels2D( + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals = false); + + /** + * Find voxels that are on the surface. Only keep the one with the surface right above them that are facing up. If 2 surface voxels have the same X Y, will only keep the one with the higher Z + * @param World The voxel world + * @param Bounds Bounds to look in + * @param bComputeNormals If true, compute the voxel normals. More expensive, but required for some functions. + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World", Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void FindSurfaceVoxels2DAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsVoxels& Voxels, + AVoxelWorld* World, + FVoxelIntBox Bounds, + bool bComputeNormals = false, + bool bHideLatentWarnings = false); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools") + static FVoxelSurfaceEditsStack AddToStack(FVoxelSurfaceEditsStack Stack, FVoxelSurfaceEditsStackElement Element); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools") + static FVoxelSurfaceEditsProcessedVoxels ApplyStack(FVoxelSurfaceEditsVoxels Voxels, FVoxelSurfaceEditsStack Stack); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (Latent, LatentInfo="LatentInfo", WorldContext = "WorldContextObject", AdvancedDisplay = "bHideLatentWarnings")) + static void ApplyStackAsync( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + FVoxelSurfaceEditsVoxels Voxels, + FVoxelSurfaceEditsStack Stack, + bool bHideLatentWarnings = false); + + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools") + static FVoxelIntBox GetBounds(FVoxelSurfaceEditsProcessedVoxels Voxels) { return Voxels.Bounds; } + +public: + // Apply a constant strength to the surface voxels + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools") + static FVoxelSurfaceEditsStackElement ApplyConstantStrength(float Strength = 1); + + /** + * Apply a strength curve to surface voxels, based on their distance from a point: + * Strength = Curve.SampleAt(Distance(Voxel.Position, Center) / Radius) + * @param World The voxel world, can be null if bConvertToVoxelSpace = false + * @param Center The center to compute the distance from, in world space if bConvertToVoxelSpace = true + * @param Radius The radius to divide the distance by, in cm if bConvertToVoxelSpace = true + * @param StrengthCurve The strength curve + * @param bConvertToVoxelSpace Converts Center and Radius from world space to voxel space. Requires World to be non null + * @return New voxels + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "bConvertToVoxelSpace", DefaultToSelf = "World")) + static FVoxelSurfaceEditsStackElement ApplyStrengthCurve( + AVoxelWorld* World, + FVector Center, + float Radius, + UCurveFloat* StrengthCurve, + bool bConvertToVoxelSpace = true); + + /** + * Apply a falloff to surface voxels, based on their distance from a point. + * @param World The voxel world, can be null if bConvertToVoxelSpace = false + * @param FalloffType The type of falloff + * @param Center The center to compute the distance from, in world space if bConvertToVoxelSpace = true + * @param Radius The radius, in cm if bConvertToVoxelSpace = true + * @param Falloff The falloff, between 0 and 1 + * @param bConvertToVoxelSpace Converts Center, Radius and Falloff from world space to voxel space. Requires World to be non null + * @return New voxels + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "bConvertToVoxelSpace", DefaultToSelf = "World")) + static FVoxelSurfaceEditsStackElement ApplyFalloff( + AVoxelWorld* World, + EVoxelFalloff FalloffType, + FVector Center, + float Radius, + float Falloff, + bool bConvertToVoxelSpace = true); + + /** + * Apply a strength mask to surface voxels, based on their position projected onto a plane + * @param World The voxel world, can be null if bConvertToVoxelSpace = false + * @param Mask The mask to apply + * @param EditPosition The voxel positions are computed relative to this. In world space if bConvertToVoxelSpace = true + * @param ScaleX The sampling scale on the X axis. The bigger, the bigger the projected image will be. + * Recommended: Wanted size in voxels of the image / image size in pixels. + * Can use GetStrengthMaskScale. + * @param ScaleY The sampling scale on the Y axis. The bigger, the bigger the projected image will be. + * Recommended: Wanted size in voxels of the image / image size in pixels. + * Can use GetStrengthMaskScale. + * @param PlaneNormal + * @param PlaneTangent + * @param SamplerMode + * @param bConvertToVoxelSpace Converts Center and Radius from world space to voxel space. Requires World to be non null + * @return New voxels + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "bConvertToVoxelSpace", DefaultToSelf = "World")) + static FVoxelSurfaceEditsStackElement ApplyStrengthMask( + AVoxelWorld* World, + FVoxelFloatTexture Mask, + FVector EditPosition, + float ScaleX = 1, + float ScaleY = 1, + FVector PlaneNormal = FVector(0, 0, 1), + FVector PlaneTangent = FVector(1, 0, 0), + EVoxelSamplerMode SamplerMode = EVoxelSamplerMode::Tile, + bool bConvertToVoxelSpace = true); + + /** + * Compute the scale for ApplyStrengthMask from a wanted size + * @param World The voxel world, required if bConvertToVoxelSpace = true + * @param Mask The mask + * @param SizeX The wanted size on the X axis, in cm if bConvertToVoxelSpace = true + * @param SizeY The wanted size on the Y axis, in cm if bConvertToVoxelSpace = true + * @param bConvertToVoxelSpace Converts SizeX and SizeY from world space to voxel space. Requires World to be non null + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "bConvertToVoxelSpace", DefaultToSelf = "World")) + static void GetStrengthMaskScale( + float& ScaleX, + float& ScaleY, + AVoxelWorld* World, + FVoxelFloatTexture Mask, + float SizeX = 1000.f, + float SizeY = 1000.f, + bool bConvertToVoxelSpace = true); + + /** + * Apply terracing + * @param TerraceHeightInVoxels The height of the terraces in voxels + * @param Angle The angle in degrees of the terraces borders. Not entirely precise. Between 0 and 180. + * @param ImmutableVoxels The number of voxels to not change per terrace/height of the "top layer" of each terrace + * @return New voxels + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "ImmutableVoxels")) + static FVoxelSurfaceEditsStackElement ApplyTerrace( + int32 TerraceHeightInVoxels = 5, + float Angle = 75, + int32 ImmutableVoxels = 1); + + /** + * Make surface voxels go towards a plane + * Important: if bExactDistanceField = true, this node should be called last! Modifying strengths after it will result + * in glitchy behavior + * @param World The voxel world, required if bConvertToVoxelSpace = true + * @param PlanePoint A point in the flatten plane, in world space if bConvertToVoxelSpace = true + * @param PlaneNormal The normal of the plane + * @param MergeMode How to merge the plane SDF + * @param bConvertToVoxelSpace If true, converts PlanePoint from world space to voxel space. Requires World to be non null + * @return + */ + UFUNCTION(BlueprintPure, Category = "Voxel|Tools|Surface Tools", meta = (AdvancedDisplay = "bConvertToVoxelSpace", DefaultToSelf = "World")) + static FVoxelSurfaceEditsStackElement ApplyFlatten( + AVoxelWorld* World, + FVector PlanePoint, + FVector PlaneNormal = FVector(0, 0, 1), + EVoxelSDFMergeMode MergeMode = EVoxelSDFMergeMode::Override, + bool bConvertToVoxelSpace = true); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools|Surface Tools", meta = (DefaultToSelf = "World")) + static void DebugSurfaceVoxels( + AVoxelWorld* World, + const FVoxelSurfaceEditsProcessedVoxels& ProcessedVoxels, + float Lifetime = 1); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h new file mode 100644 index 00000000..5bb24099 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h @@ -0,0 +1,176 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTools/VoxelToolHelpers.h" +#include "VoxelTools/VoxelSurfaceTools.h" + +class FVoxelSurfaceToolsImpl +{ +public: + static void ApplyConstantStrengthImpl( + TArray& Voxels, + const float Strength) + { + VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + for (auto& Voxel : Voxels) + { + Voxel.Strength *= Strength; + } + } + template + static void ApplyStrengthFunctionImpl( + TArray& Voxels, + T GetStrength) + { + VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + int32 Num = 0; + for (const auto& Voxel : Voxels) + { + const float Strength = GetStrength(Voxel); + if (Strength != 0) + { + auto& NewVoxel = Voxels[Num++]; + NewVoxel = Voxel; + NewVoxel.Strength *= Strength; + } + } + check(Num <= Voxels.Num()); + Voxels.SetNum(Num, false); + } + template + static void ApplyDistanceStrengthFunctionImpl( + TArray& Voxels, + const FVoxelVector& Center, + bool b2D, + T GetStrengthFromDistance) + { + if (b2D) + { + ApplyStrengthFunctionImpl(Voxels, [=](const FVoxelSurfaceEditsVoxel& Voxel) + { + return GetStrengthFromDistance(FVector2D::Distance(FVector2D(Center.X, Center.Y), FVector2D(Voxel.Position.X, Voxel.Position.Y))); + }); + } + else + { + ApplyStrengthFunctionImpl(Voxels, [=](const FVoxelSurfaceEditsVoxel& Voxel) + { + return GetStrengthFromDistance(FVoxelVector::Distance(Center, FVoxelVector(Voxel.Position))); + }); + } + } + + // Should always be called last if bExactDistanceField = true + template + static void ApplySDFImpl( + const FVoxelSurfaceEditsVoxelsInfo& Info, + TArray& Voxels, + EVoxelSDFMergeMode MergeMode, + T GetDistance) + { + VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + for (auto& Voxel : Voxels) + { + const float CurrentDistance = Voxel.Value; + const float OtherDistance = GetDistance(FVector(Voxel.Position)); + const float WantedDistance = + MergeMode == EVoxelSDFMergeMode::Union + ? FMath::Min(CurrentDistance, OtherDistance) + : MergeMode == EVoxelSDFMergeMode::Intersection + ? FMath::Max(OtherDistance, CurrentDistance) + : OtherDistance; // Override + + if (Info.bHasExactDistanceField) + { + // No strength should be applied after ApplySDFImpl if we want a good result + const float IntermediateDistance = FMath::Lerp(CurrentDistance, WantedDistance, Voxel.Strength); + Voxel.Strength = IntermediateDistance - CurrentDistance; + } + else + { + const float Difference = WantedDistance - Voxel.Value; + // We cannot go too fast if we didn't compute the exact distance field + Voxel.Strength *= FMath::Clamp(Difference, -1.f, 1.f); + } + } + } + static void GetStrengthMaskBasisImpl( + FVector PlaneNormal, + FVector PlaneTangent, + FVector& OutPlaneX, + FVector& OutPlaneY) + { + PlaneNormal.Normalize(); + PlaneTangent.Normalize(); + const FVector PlaneY = FVector::CrossProduct(PlaneNormal, PlaneTangent); + PlaneTangent = FVector::CrossProduct(PlaneY, PlaneNormal); + + OutPlaneX = PlaneTangent; + OutPlaneY = PlaneY; + } + static void ApplyStrengthMaskImpl( + const FVoxelSurfaceEditsVoxelsInfo& Info, + TArray& Voxels, + const TVoxelTexture& Mask, + const FVoxelVector& EditPosition, + const float ScaleX, + const float ScaleY, + const FVector& PlaneX, + const FVector& PlaneY, + const EVoxelSamplerMode SamplerMode) + { + VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + const float HalfSizeX = Mask.GetSizeX() / 2; + const float HalfSizeY = Mask.GetSizeY() / 2; + + for (auto& Voxel : Voxels) + { + const FVector Position = (Voxel.Position - EditPosition).ToFloat(); + const float X = FVector::DotProduct(Position, PlaneX) / ScaleX; + const float Y = FVector::DotProduct(Position, PlaneY) / ScaleY; + + Voxel.Strength *= Mask.Sample(HalfSizeX + X, HalfSizeY + Y, SamplerMode); + } + } + static void ApplyTerraceImpl( + TArray& Voxels, + const int32 TerraceHeightInVoxels, + const float Angle, + const int32 ImmutableVoxels) + { + VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num()); + + if (!ensure(TerraceHeightInVoxels >= 1)) return; + const float AngleLimit = FMath::DegreesToRadians(Angle); + + int32 NewNum = 0; + for (auto& Voxel : Voxels) + { + const int32 RelativePosition = FVoxelUtilities::PositiveMod(Voxel.Position.Z, TerraceHeightInVoxels); + if (RelativePosition < ImmutableVoxels) continue; + + const float VoxelAngle = FMath::Acos(Voxel.Normal.Z); // Dot product with UpVector. 0 when facing up, PI when facing down + ensure(VoxelAngle >= 0); + if (AngleLimit < VoxelAngle) continue; + + // We want 1 went facing up, 0 when facing > angle limit + const float Strength = FMath::Max((AngleLimit - VoxelAngle) / AngleLimit, 0.f); + + auto NewVoxel = Voxel; + NewVoxel.Strength *= Strength; + Voxels[NewNum++] = NewVoxel; + } + check(NewNum <= Voxels.Num()); + Voxels.SetNum(NewNum, false); + } + static void ApplyFlattenImpl( + const FVoxelSurfaceEditsVoxelsInfo& Info, + TArray& Voxels, + const FPlane& Plane, + EVoxelSDFMergeMode MergeMode) + { + ApplySDFImpl(Info, Voxels, MergeMode, [&](const FVector& Position) { return Plane.PlaneDot(Position); }); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTestLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTestLibrary.h new file mode 100644 index 00000000..55000c8b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTestLibrary.h @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelIntBox.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelTestLibrary.generated.h" + +class AVoxelWorld; + +USTRUCT(BlueprintType) +struct FVoxelTestValues +{ + GENERATED_BODY() + + TSharedRef> Values = MakeShared>(); +}; + +UCLASS() +class VOXEL_API UVoxelTestLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Test", meta = (DefaultToSelf = "World")) + static FVoxelTestValues ReadValues(AVoxelWorld* World, FVoxelIntBox Bounds); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Test", meta = (DefaultToSelf = "World")) + static void TestValues(FVoxelTestValues ValuesA, FVoxelTestValues ValuesB); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTextureTools.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTextureTools.h new file mode 100644 index 00000000..6c1cf1c0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelTextureTools.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelTexture.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelTextureTools.generated.h" + +UCLASS() +class VOXEL_API UVoxelTextureTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // Apply the photoshop Minimum filter + // Set each pixel to the min value in a radius Radius + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static FVoxelFloatTexture Minimum( + FVoxelFloatTexture Texture, + float Radius = 2); + + // Apply the photoshop Maximum filter + // Set each pixel to the max value in a radius Radius + UFUNCTION(BlueprintCallable, Category = "Voxel|Voxel Texture") + static FVoxelFloatTexture Maximum( + FVoxelFloatTexture Texture, + float Radius = 2); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolHelpers.h new file mode 100644 index 00000000..6bdd6610 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolHelpers.h @@ -0,0 +1,483 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelIntBox.h" +#include "VoxelMinimal.h" +#include "VoxelWorld.h" +#include "VoxelAsyncWork.h" +#include "VoxelMessages.h" +#include "VoxelData/VoxelData.h" +#include "VoxelData/VoxelDataLock.h" + +// TODO REMOVE +#include "VoxelTools/Impl/VoxelToolsBaseImpl.inl" + +#include "LatentActions.h" +#include "Engine/LatentActionManager.h" + +class AVoxelWorld; +class FVoxelData; +class FPendingLatentAction; +class FVoxelLatentActionAsyncWork; +struct FLatentActionInfo; +enum class EVoxelLockType; + +enum class EVoxelUpdateRender +{ + UpdateRender, + DoNotUpdateRender +}; + +class VOXEL_API FVoxelLatentActionAsyncWork : public FVoxelAsyncWorkWithWait +{ +public: + explicit FVoxelLatentActionAsyncWork(FName Name); + + //~ Begin IVoxelQueuedWork Interface + virtual uint32 GetPriority() const override; + //~ End IVoxelQueuedWork Interface + + //~ Begin FVoxelLatentActionAsyncWork Interface + // Called on the game thread + virtual bool IsValid() const = 0; + //~ End FVoxelLatentActionAsyncWork Interface + +protected: + ~FVoxelLatentActionAsyncWork() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +class VOXEL_API FVoxelLatentActionAsyncWork_WithWorld : public FVoxelLatentActionAsyncWork +{ +public: + const TWeakObjectPtr World; + const TVoxelWeakPtr Data; + const TFunction Function; + + FVoxelLatentActionAsyncWork_WithWorld(FName Name, TWeakObjectPtr World, TFunction Function); + + //~ Begin FVoxelLatentActionAsyncWork Interface + virtual void DoWork() override; + virtual bool IsValid() const override; + //~ End FVoxelLatentActionAsyncWork Interface + +protected: + ~FVoxelLatentActionAsyncWork_WithWorld() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +class VOXEL_API FVoxelLatentActionAsyncWork_WithoutWorld : public FVoxelLatentActionAsyncWork +{ +public: + const TFunction Function; + // Called on the game thread + const TFunction IsValidLambda; + + FVoxelLatentActionAsyncWork_WithoutWorld(FName Name, TFunction Function, TFunction IsValidLambda); + + //~ Begin FVoxelLatentActionAsyncWork Interface + virtual void DoWork() override; + virtual bool IsValid() const override; + //~ End FVoxelLatentActionAsyncWork Interface + +protected: + ~FVoxelLatentActionAsyncWork_WithoutWorld() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +template +class TVoxelLatentActionAsyncWork_WithWorld_WithValue : public FVoxelLatentActionAsyncWork_WithWorld +{ +public: + TValue Value; + + TVoxelLatentActionAsyncWork_WithWorld_WithValue(FName Name, TWeakObjectPtr World, TFunction InFunction) + : FVoxelLatentActionAsyncWork_WithWorld(Name, World, [InFunction, this](FVoxelData& InData) { InFunction(InData, this->Value); }) + { + } + +protected: + ~TVoxelLatentActionAsyncWork_WithWorld_WithValue() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +template +class TVoxelLatentActionAsyncWork_WithoutWorld_WithValue : public FVoxelLatentActionAsyncWork_WithoutWorld +{ +public: + TValue Value; + + TVoxelLatentActionAsyncWork_WithoutWorld_WithValue(FName Name, TFunction InFunction, TFunction IsValidLambda) + : FVoxelLatentActionAsyncWork_WithoutWorld(Name, [InFunction, this]() { InFunction(this->Value); }, MoveTemp(IsValidLambda)) + { + } + +protected: + ~TVoxelLatentActionAsyncWork_WithoutWorld_WithValue() = default; + + template + friend struct TVoxelAsyncWorkDelete; +}; + +template +class TVoxelLatentAction : public FPendingLatentAction +{ +public: + const FName ExecutionFunction; + const int32 OutputLink; + const FWeakObjectPtr CallbackTarget; + + const TUniquePtr> Work; + const TFunction GameThreadCallback; + const FName Name; + + TVoxelLatentAction( + const FLatentActionInfo& LatentInfo, + TWork* Work, + FName Name, + TFunction GameThreadCallback) + : ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + + , Work(Work) + , GameThreadCallback(MoveTemp(GameThreadCallback)) + , Name(Name) + { + check(Work); + } + virtual ~TVoxelLatentAction() override + { + if (!Work->IsDone()) + { + const double StartTime = FPlatformTime::Seconds(); + Work->WaitForCompletion(); + const double Elapsed = FPlatformTime::Seconds() - StartTime; + if (Elapsed > 0.001) + { + LOG_VOXEL( + Warning, + TEXT("Voxel Latent Action: waited %fs for %s on game thread. This is likely because the object that triggered the latent call was destroyed."), + Elapsed, + *Name.ToString()); + } + } + if (!bCallbackCalled && Work->IsValid() && !Work->WasAbandoned()) + { + // Always call callback + GameThreadCallback(*Work); + } + } + + //~ Begin FPendingLatentAction Interface + virtual void UpdateOperation(FLatentResponse& Response) override + { + const bool bFinished = Work->IsDone(); + if (bFinished && ensure(!bCallbackCalled) && Work->IsValid() && !Work->WasAbandoned()) + { + bCallbackCalled = true; + GameThreadCallback(*Work); + } + Response.FinishAndTriggerIf(bFinished, ExecutionFunction, OutputLink, CallbackTarget); + } +#if WITH_EDITOR + virtual FString GetDescription() const override + { + return FString::Printf(TEXT("%s: Waiting for completion"), *Name.ToString()); + } +#endif + //~ End FPendingLatentAction Interface + +private: + bool bCallbackCalled = false; +}; + +struct VOXEL_API FVoxelToolHelpers +{ + // Avoids having to include the LOD Manager header in every tool file + static void UpdateWorld(AVoxelWorld* World, const FVoxelIntBox& Bounds); + // If World is null, will start an async on AnyThread. Else will use the voxel world thread pool. + static void StartAsyncEditTask(AVoxelWorld* World, IVoxelQueuedWork* Work); + + static float GetRealDistance(AVoxelWorld* World, float Distance, bool bConvertToVoxelSpace); + static FVoxelVector GetRealPosition(AVoxelWorld* World, const FVector& Position, bool bConvertToVoxelSpace); + static FTransform GetRealTransform(AVoxelWorld* World, FTransform Transform, bool bConvertToVoxelSpace); + + template + static auto GetRealTemplate(AVoxelWorld* World, T Value, bool bConvertToVoxelSpace); + + static bool StartLatentAction( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FName Name, + bool bHideLatentWarnings, + TFunction CreateLatentAction); + + template + static bool StartAsyncLatentActionImpl( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FName Name, + bool bHideLatentWarnings, + TCreateWork CreateWork, + TFunction GameThreadCallback) + { + return StartLatentAction(WorldContextObject, LatentInfo, Name, bHideLatentWarnings, [&]() + { + TWork* Work = CreateWork(); + StartAsyncEditTask(World, Work); + return new TVoxelLatentAction(LatentInfo, Work, Name, MoveTemp(GameThreadCallback)); + }); + } + + static bool StartAsyncLatentAction_WithWorld( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FName Name, + bool bHideLatentWarnings, + TFunction DoWork, + EVoxelUpdateRender UpdateRender, + const FVoxelIntBox& BoundsToUpdate); + static bool StartAsyncLatentAction_WithoutWorld( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FName Name, + bool bHideLatentWarnings, + TFunction DoWork, + TFunction IsValid = []() { return true; }); + + template + static bool StartAsyncLatentAction_WithWorld_WithValue( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + AVoxelWorld* World, + FName Name, + bool bHideLatentWarnings, + T& Value, + TDoWork DoWork, + EVoxelUpdateRender UpdateRender, + const FVoxelIntBox& BoundsToUpdate, + TFunction GameThreadCallback = nullptr) + { + using FWork = TVoxelLatentActionAsyncWork_WithWorld_WithValue; + return StartAsyncLatentActionImpl( + WorldContextObject, + LatentInfo, + World, + Name, + bHideLatentWarnings, + [&]() { return new FWork(Name, World, DoWork); }, + [=, WeakWorldContextObject = MakeWeakObjectPtr(WorldContextObject), &Value](FWork& Work) + { + if (WeakWorldContextObject.IsValid()) + { + Value = MoveTemp(Work.Value); + if (GameThreadCallback) + { + GameThreadCallback(); + } + } + if (UpdateRender == EVoxelUpdateRender::UpdateRender && Work.World.IsValid()) + { + UpdateWorld(Work.World.Get(), BoundsToUpdate); + } + }); + } + template + static bool StartAsyncLatentAction_WithoutWorld_WithValue( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + FName Name, + bool bHideLatentWarnings, + T& Value, + TDoWork DoWork, + TFunction IsValid = []() { return true; }) + { + using FWork = TVoxelLatentActionAsyncWork_WithoutWorld_WithValue; + return StartAsyncLatentActionImpl( + WorldContextObject, + LatentInfo, + nullptr, + Name, + bHideLatentWarnings, + [&]() { return new FWork(Name, DoWork, MoveTemp(IsValid)); }, + [=, WeakWorldContextObject = MakeWeakObjectPtr(WorldContextObject), &Value](FWork& Work) + { + if (WeakWorldContextObject.IsValid()) + { + Value = MoveTemp(Work.Value); + } + }); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template<> +inline auto FVoxelToolHelpers::GetRealTemplate(AVoxelWorld* World, float Value, bool bConvertToVoxelSpace) +{ + return GetRealDistance(World, Value, bConvertToVoxelSpace); +} +template<> +inline auto FVoxelToolHelpers::GetRealTemplate(AVoxelWorld* World, FVector Value, bool bConvertToVoxelSpace) +{ + return GetRealPosition(World, Value, bConvertToVoxelSpace); +} +template<> +inline auto FVoxelToolHelpers::GetRealTemplate(AVoxelWorld* World, FTransform Value, bool bConvertToVoxelSpace) +{ + return GetRealTransform(World, Value, bConvertToVoxelSpace); +} + +#define GET_VOXEL_TOOL_REAL(Value) FVoxelToolHelpers::GetRealTemplate(World, Value, bConvertToVoxelSpace) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_VOXELWORLD_IS_CREATED_IMPL(World, ReturnValue) \ +if (!World) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Voxel World is invalid!"), *FString(__FUNCTION__))); \ + return ReturnValue; \ +} \ +if (!World->IsCreated()) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Voxel World isn't created!"), *FString(__FUNCTION__))); \ + return ReturnValue; \ +} +#define CHECK_VOXELWORLD_IS_CREATED() CHECK_VOXELWORLD_IS_CREATED_IMPL(World, {}); +#define CHECK_VOXELWORLD_IS_CREATED_VOID() CHECK_VOXELWORLD_IS_CREATED_IMPL(World, PREPROCESSOR_NOTHING); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_OBJECT_PARAMETER_IMPL(Object, ReturnValue) \ +if (!Object) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: "#Object" is invalid!"), *FString(__FUNCTION__))); \ + return ReturnValue; \ +} +#define CHECK_OBJECT_PARAMETER(Object) CHECK_OBJECT_PARAMETER_IMPL(Object, {}); +#define CHECK_OBJECT_PARAMETER_VOID(Object) CHECK_OBJECT_PARAMETER_IMPL(Object, PREPROCESSOR_NOTHING); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE_IMPL(ReturnValue) \ +if (!World && bConvertToVoxelSpace) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Voxel World is invalid, but bConvertToVoxelSpace = true!"), *FString(__FUNCTION__))); \ + return ReturnValue; \ +} +#define CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE() CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE_IMPL({}); +#define CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE_VOID() CHECK_VOXELWORLD_FOR_CONVERT_TO_VOXEL_SPACE_IMPL(PREPROCESSOR_NOTHING); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CHECK_BOUNDS_ARE_VALID_IMPL(ReturnValue) \ +if (!Bounds.IsValid()) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Invalid Bounds! %s"), *FString(__FUNCTION__), *Bounds.ToString())); \ + return ReturnValue; \ +} +#define CHECK_BOUNDS_ARE_VALID() CHECK_BOUNDS_ARE_VALID_IMPL({}); +#define CHECK_BOUNDS_ARE_VALID_VOID() CHECK_BOUNDS_ARE_VALID_IMPL(PREPROCESSOR_NOTHING); + +#define CHECK_BOUNDS_ARE_32BITS_IMPL(ReturnValue) \ +if (!FVoxelUtilities::CountIs32Bits(Bounds.Size())) \ +{ \ + FVoxelMessages::Error(FString::Printf(TEXT("%s: Bounds size is too big! %s"), *FString(__FUNCTION__), *Bounds.ToString())); \ + return ReturnValue; \ +} +#define CHECK_BOUNDS_ARE_32BITS() CHECK_BOUNDS_ARE_32BITS_IMPL({}); +#define CHECK_BOUNDS_ARE_32BITS_VOID() CHECK_BOUNDS_ARE_32BITS_IMPL(PREPROCESSOR_NOTHING); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define VOXEL_TOOL_HELPER_BODY(InLockType, InUpdateRender, ...) \ + auto& Data = World->GetData(); \ + { \ + TVoxelScopeLock Lock(Data, Bounds, FUNCTION_FNAME); \ + __VA_ARGS__; \ + } \ + if (EVoxelUpdateRender::InUpdateRender == EVoxelUpdateRender::UpdateRender) \ + { \ + FVoxelToolHelpers::UpdateWorld(World, Bounds); \ + } + +#define VOXEL_TOOL_LATENT_HELPER_BODY(InLockType, InUpdateRender, ...) \ + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld( \ + WorldContextObject, \ + LatentInfo, \ + World, \ + FUNCTION_FNAME, \ + bHideLatentWarnings, \ + [=](FVoxelData& Data) \ + { \ + TVoxelScopeLock Lock(Data, Bounds, FUNCTION_FNAME); \ + __VA_ARGS__; \ + }, \ + EVoxelUpdateRender::InUpdateRender, \ + Bounds); + +#define VOXEL_TOOL_LATENT_HELPER_WITH_VALUE_BODY(InValue, InLockType, InUpdateRender, ...) \ + FVoxelToolHelpers::StartAsyncLatentAction_WithWorld_WithValue( \ + WorldContextObject, \ + LatentInfo, \ + World, \ + FUNCTION_FNAME, \ + bHideLatentWarnings, \ + InValue, \ + [=](FVoxelData& Data, decltype(InValue) In ## InValue) \ + { \ + static_assert(TIsReferenceType::Value, "Value is not a reference!"); \ + static_assert(!TIsConst::Value, "Value is const!"); \ + TVoxelScopeLock Lock(Data, Bounds, FUNCTION_FNAME); \ + __VA_ARGS__; \ + }, \ + EVoxelUpdateRender::InUpdateRender, \ + Bounds); + +#define VOXEL_TOOL_HELPER(InLockType, InUpdateRender, Prefix, ...) \ + VOXEL_FUNCTION_COUNTER(); \ + CHECK_VOXELWORLD_IS_CREATED_VOID(); \ + Prefix \ + CHECK_BOUNDS_ARE_VALID_VOID(); \ + VOXEL_TOOL_HELPER_BODY(InLockType, InUpdateRender, __VA_ARGS__) + +#define VOXEL_TOOL_LATENT_HELPER(InLockType, InUpdateRender, Prefix, ...) \ + VOXEL_FUNCTION_COUNTER(); \ + CHECK_VOXELWORLD_IS_CREATED_VOID(); \ + Prefix \ + CHECK_BOUNDS_ARE_VALID_VOID(); \ + VOXEL_TOOL_LATENT_HELPER_BODY(InLockType, InUpdateRender, __VA_ARGS__) + +#define VOXEL_TOOL_LATENT_HELPER_WITH_VALUE(InValue, InLockType, InUpdateRender, Prefix, ...) \ + VOXEL_FUNCTION_COUNTER(); \ + CHECK_VOXELWORLD_IS_CREATED_VOID(); \ + Prefix \ + CHECK_BOUNDS_ARE_VALID_VOID(); \ + VOXEL_TOOL_LATENT_HELPER_WITH_VALUE_BODY(InValue, InLockType, InUpdateRender, __VA_ARGS__) + +#define NO_PREFIX \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolManager.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolManager.h new file mode 100644 index 00000000..7b77263b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelTools/VoxelToolManager.h @@ -0,0 +1,83 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Templates/SubclassOf.h" +#include "VoxelToolManager.generated.h" + +class UVoxelToolSharedConfig; +class UVoxelTool; + +UCLASS(BlueprintType, Blueprintable) +class VOXEL_API UVoxelToolManager : public UObject +{ + GENERATED_BODY() + +public: + UVoxelToolManager(); + +private: + UPROPERTY() + UVoxelToolSharedConfig* SharedConfig = nullptr; + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools", meta = (DisplayName = "Get Shared Config")) + UVoxelToolSharedConfig* K2_GetSharedConfig() const { return SharedConfig; } + UVoxelToolSharedConfig& GetSharedConfig() const { check(SharedConfig); return *SharedConfig; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + UVoxelTool* GetActiveTool() const { return ActiveTool; } + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + const TArray& GetTools() const { return Tools; } + +public: + // If bLoadBlueprints is true, all the blueprints inheriting from VoxelTool will be force loaded + // If false, tools whose blueprints are not loaded won't show up + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + void CreateDefaultTools(bool bLoadBlueprints = false); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + void SetActiveTool(UVoxelTool* NewActiveTool); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + void SetActiveToolByClass(TSubclassOf NewActiveTool); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Tools") + void SetActiveToolByName(FName NewActiveTool); + +public: + template + T* GetActiveTool() const + { + return Cast(ActiveTool); + } + template + void SetActiveTool() + { + static_assert(TIsDerivedFrom::IsDerived, "T must be derived from UVoxelTool"); + SetActiveToolByClass(T::StaticClass()); + } + template + T& GetOrSetActiveTool() + { + static_assert(TIsDerivedFrom::IsDerived, "T must be derived from UVoxelTool"); + + if (auto* Tool = Cast(ActiveTool)) + { + return *Tool; + } + + SetActiveTool(); + + return *CastChecked(ActiveTool); + } + +private: + UPROPERTY(Transient) + UVoxelTool* ActiveTool = nullptr; + + UPROPERTY(Transient) + TArray Tools; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUniqueError.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUniqueError.h new file mode 100644 index 00000000..34d8e37a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUniqueError.h @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +template +class TVoxelUniqueError +{ +public: + TVoxelUniqueError() = default; + + bool NeedToRaiseWarning(TKey Key, TValue Value) + { + auto& Set = Map.FindOrAdd(Key); + if (!Set.Contains(Value)) + { + Set.Add(Value); + return true; + } + else + { + return false; + } + } + bool operator()(TKey Key, TValue Value) + { + return NeedToRaiseWarning(Key, Value); + } + +private: + TMap> Map; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUserDefinitions.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUserDefinitions.h new file mode 100644 index 00000000..6b1ba152 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUserDefinitions.h @@ -0,0 +1,31 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +// Add custom defines here + +// Enable double precision +// #define VOXEL_DOUBLE_PRECISION 1 + +// Enable voxel asserts +// #define VOXEL_DEBUG 1 + +// Make a voxel material only one byte for single index +/* +#define VOXEL_MATERIAL_ENABLE_R 0 +#define VOXEL_MATERIAL_ENABLE_G 0 +#define VOXEL_MATERIAL_ENABLE_B 0 +#define VOXEL_MATERIAL_ENABLE_A 1 + +#define VOXEL_MATERIAL_ENABLE_UV0 0 +#define VOXEL_MATERIAL_ENABLE_UV1 0 +#define VOXEL_MATERIAL_ENABLE_UV2 0 +#define VOXEL_MATERIAL_ENABLE_UV3 0 +*/ + +// Use 8 bit voxel value +// #define EIGHT_BITS_VOXEL_VALUE 1 + +// Enable additional UV channels +// #define VOXEL_MATERIAL_ENABLE_UV2 1 +// #define VOXEL_MATERIAL_ENABLE_UV3 1 \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelBaseUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelBaseUtilities.h new file mode 100644 index 00000000..8ee71155 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelBaseUtilities.h @@ -0,0 +1,309 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Math/RandomStream.h" + +namespace FVoxelUtilities +{ + template + FORCEINLINE constexpr bool IsPowerOfTwo(T Value) + { + return ((Value & (Value - 1)) == T(0)); + } + + FORCEINLINE constexpr int32 PositiveMod(int32 X, int32 Y) + { + return ((X % Y) + Y) % Y; + } + + FORCEINLINE constexpr int32 DivideFloor(int32 Dividend, int32 Divisor) + { + int32 Q = Dividend / Divisor; + int32 R = Dividend % Divisor; + if ((R != 0) && ((R < 0) != (Divisor < 0))) + { + Q--; + } + return Q; + } + + FORCEINLINE constexpr int32 DivideCeil(int32 Dividend, int32 Divisor) + { + return (Dividend > 0) ? 1 + (Dividend - 1) / Divisor : (Dividend / Divisor); + } + FORCEINLINE constexpr int64 DivideCeil64(int64 Dividend, int64 Divisor) + { + return (Dividend > 0) ? 1 + (Dividend - 1) / Divisor : (Dividend / Divisor); + } + + FORCEINLINE constexpr int32 DivideRound(int32 Dividend, int32 Divisor) + { + const int32 R = PositiveMod(Dividend, Divisor); + if (R < Divisor / 2) + { + return DivideFloor(Dividend, Divisor); + } + else + { + return DivideCeil(Dividend, Divisor); + } + } + + FORCEINLINE constexpr int32 IntLog2(int32 X) + { + int32 Exp = -1; + while (X) + { + X >>= 1; + ++Exp; + } + return Exp; + } + + FORCEINLINE uint8 CastToUINT8(int32 Value) + { + ensureMsgfVoxelSlowNoSideEffects(0 <= Value && Value < 256, TEXT("Invalid uint8 value: %d"), Value); + return Value; + } + + template + FORCEINLINE constexpr T Clamp( const T X, const T Min, const T Max ) + { + return X < Min ? Min : X < Max ? X : Max; + } + + FORCEINLINE constexpr int8 ClampToINT8(int32 Value) + { + return Clamp(Value, MIN_int8, MAX_int8); + } + FORCEINLINE constexpr uint8 ClampToUINT8(int32 Value) + { + return Clamp(Value, MIN_uint8, MAX_uint8); + } + FORCEINLINE constexpr int8 ClampToINT16(int32 Value) + { + return Clamp(Value, MIN_int16, MAX_int16); + } + FORCEINLINE constexpr uint16 ClampToUINT16(int32 Value) + { + return Clamp(Value, MIN_uint16, MAX_uint16); + } + + FORCEINLINE uint8 FloatToUINT8(float Float) + { + return ClampToUINT8(FMath::FloorToInt(Float * 255.999f)); + } + FORCEINLINE constexpr float UINT8ToFloat(uint8 Int) + { + return Int / 255.f; + } + + // Round up if the new value is higher than the previous one, to avoid being stuck + FORCEINLINE uint8 FloatToUINT8_ForLerp(float NewValue, float OldValue) + { + return ClampToUINT8(NewValue > OldValue + ? FMath::CeilToInt(NewValue * 255.999f) + : FMath::FloorToInt(NewValue * 255.999f)); + } + FORCEINLINE uint8 LerpUINT8(uint8 A, uint8 B, float Alpha) + { + const float Result = FMath::Lerp(A, B, Alpha); + // Do special rounding to not get stuck, eg Lerp(251, 255, 0.1) = 251 should be 252 instead + // and Lerp(255, 251, 0.1) should be 254 + const int32 RoundedResult = (Alpha > 0) == (A < B) ? FMath::CeilToInt(Result) : FMath::FloorToInt(Result); + return ClampToUINT8(RoundedResult); + } + + FORCEINLINE FColor FloatToUINT8(FLinearColor Float) + { + return + FColor + { + FloatToUINT8(Float.R), + FloatToUINT8(Float.G), + FloatToUINT8(Float.B), + FloatToUINT8(Float.A) + }; + } + FORCEINLINE FLinearColor UINT8ToFloat(FColor Int) + { + return + FLinearColor + { + UINT8ToFloat(Int.R), + UINT8ToFloat(Int.G), + UINT8ToFloat(Int.B), + UINT8ToFloat(Int.A) + }; + } + + FORCEINLINE uint16 FloatToUINT16(float Float) + { + return ClampToUINT16(FMath::FloorToInt(Float * 65535.999f)); + } + FORCEINLINE constexpr float UINT16ToFloat(uint16 Int) + { + return Int / 65535.f; + } + + FORCEINLINE constexpr uint32 MurmurHash32(uint32 Hash) + { + Hash ^= Hash >> 16; + Hash *= 0x85ebca6b; + Hash ^= Hash >> 13; + Hash *= 0xc2b2ae35; + Hash ^= Hash >> 16; + return Hash; + } + FORCEINLINE uint32 MurmurHash32(int32 Hash) + { + return MurmurHash32(*reinterpret_cast(&Hash)); + } + // Slow! + template + FORCEINLINE uint32 MurmurHash32(T A, TArgs... Args) + { + return MurmurHash32(MurmurHash32(A) ^ MurmurHash32(Args...)); + } + + FORCEINLINE constexpr uint64 MurmurHash64(uint64 Hash) + { + Hash ^= Hash >> 33; + Hash *= 0xff51afd7ed558ccd; + Hash ^= Hash >> 33; + Hash *= 0xc4ceb9fe1a85ec53; + Hash ^= Hash >> 33; + return Hash; + } + FORCEINLINE uint64 MurmurHash64(int64 Hash) + { + return MurmurHash64(*reinterpret_cast(&Hash)); + } + // Slow! + template + FORCEINLINE uint32 MurmurHash64(T A, TArgs... Args) + { + return MurmurHash32(MurmurHash64(A) ^ MurmurHash64(Args...)); + } + + FORCEINLINE uint32 MurmurHash32xN(uint32 const* RESTRICT Hash, int32 Size, uint32 Seed = 0) + { + uint32 H = Seed; + for (int32 Index = 0; Index < Size; ++Index) + { + uint32 K = Hash[Index]; + K *= 0xcc9e2d51; + K = (K << 15) | (K >> 17); + K *= 0x1b873593; + H ^= K; + H = (H << 13) | (H >> 19); + H = H * 5 + 0xe6546b64; + } + + H ^= uint32(Size); + H ^= H >> 16; + H *= 0x85ebca6b; + H ^= H >> 13; + H *= 0xc2b2ae35; + H ^= H >> 16; + return H; + } + + template + FORCEINLINE float Halton(uint32 Index) + { + float Result = 0.0f; + const float InvBase = 1.0f / Base; + float Fraction = InvBase; + while (Index > 0) + { + Result += (Index % Base) * Fraction; + Index /= Base; + Fraction *= InvBase; + } + return Result; + } + + /** + * Y + * ^ C - D + * | | | + * | A - B + * -----> X + */ + template + FORCEINLINE T BilinearInterpolation(T A, T B, T C, T D, U X, U Y) + { + T AB = FMath::Lerp(A, B, X); + T CD = FMath::Lerp(C, D, X); + return FMath::Lerp(AB, CD, Y); + } + + /** + * Y + * ^ C - D + * | | | + * | A - B + * 0-----> X + * Y + * ^ G - H + * | | | + * | E - F + * 1-----> X + */ + template + FORCEINLINE T TrilinearInterpolation( + T A, T B, T C, T D, + T E, T F, T G, T H, + U X, U Y, U Z) + { + const T ABCD = BilinearInterpolation(A, B, C, D, X, Y); + const T EFGH = BilinearInterpolation(E, F, G, H, X, Y); + return FMath::Lerp(ABCD, EFGH, Z); + } + + template + FORCEINLINE T&& VariadicMin(T&& Val) + { + return Forward(Val); + } + template + FORCEINLINE auto VariadicMin(T0&& Val1, T1&& Val2, Ts&&... Vals) + { + return (Val1 < Val2) ? + VariadicMin(Val1, Forward(Vals)...) : + VariadicMin(Val2, Forward(Vals)...); + } + + template + FORCEINLINE T&& VariadicMax(T&& Val) + { + return Forward(Val); + } + template + FORCEINLINE auto VariadicMax(T0&& Val1, T1&& Val2, Ts&&... Vals) + { + return (Val1 > Val2) ? + VariadicMax(Val1, Forward(Vals)...) : + VariadicMax(Val2, Forward(Vals)...); + } + + FORCEINLINE uint32 Popc(uint32 Value) + { + // Java: use >>> instead of >> + // C or C++: use uint32_t + Value = Value - ((Value >> 1) & 0x55555555); + Value = (Value & 0x33333333) + ((Value >> 2) & 0x33333333); + return (((Value + (Value >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + } + + // Returns distance to voxel with Density + FORCEINLINE float GetAbsDistanceFromDensities(float Density, float OtherDensity) + { + ensureVoxelSlowNoSideEffects(Density > 0 != OtherDensity > 0); + return Density / (Density - OtherDensity); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelConfigUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelConfigUtilities.h new file mode 100644 index 00000000..273b5518 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelConfigUtilities.h @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +namespace FVoxelConfigUtilities +{ + VOXEL_API void SaveConfig(UObject* Object, const FString& BaseSectionName = "Voxel", const FString& Filename = GEditorPerProjectIni); + VOXEL_API void LoadConfig(UObject* Object, const FString& BaseSectionName = "Voxel", const FString& Filename = GEditorPerProjectIni); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDataItemUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDataItemUtilities.h new file mode 100644 index 00000000..ef0cb1c9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDataItemUtilities.h @@ -0,0 +1,230 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelItemStack.h" +#include "VoxelUtilities/VoxelSDFUtilities.h" +#include "VoxelUtilities/VoxelRangeUtilities.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" + +namespace FVoxelUtilities +{ + /** + * Get the distance to data items + * See https://wiki.voxelplugin.com/Voxel_Data_Items + * @param ItemHolder The item holder passed to the generator + * @param X The current X coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Y The current Y coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Z The current Z coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Smoothness The smoothness of the union/intersection. Should be >= 0 + * @param Default The default value to return if no + * @param Mask Use uint32(-1) to match any items + * @param CombineMode How to combine data items + * @param GeneratorValue If set, Default will be ignored and GeneratorValue returned instead. GeneratorValue will be combined with the data item distances. + * @return The resulting distance + */ + template + inline v_flt GetDataItemDistance( + const FVoxelPlaceableItemHolder& ItemHolder, + v_flt X, v_flt Y, v_flt Z, + v_flt Smoothness, + v_flt Default, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode, + const v_flt* GeneratorValue = nullptr) + { + const auto ShouldComputeItem = [&](const FVoxelDataItem& Item) + { + return (Item.Mask & Mask) && Item.Bounds.ContainsFloat(X, Y, Z); + }; + const auto GetItemDistance = [&](const FVoxelDataItem& Item) + { + const auto Stack = FVoxelItemStack::Empty.WithCustomData(&Item.Data); + return Item.Generator->GetValue(X, Y, Z, 0, Stack) * (bInvertDataItemDistances ? -1 : 1); + }; + + auto& DataItems = ItemHolder.GetDataItems(); + int32 Index = 0; + + const auto FindNextItem = [&]() + { + while (Index < DataItems.Num() && !ShouldComputeItem(*DataItems[Index])) + { + Index++; + } + }; + + FindNextItem(); + + if (Index == DataItems.Num()) + { + if (GeneratorValue) + { + return *GeneratorValue; + } + else + { + return Default; + } + } + + // Note: we can't use Default here else SmoothUnion is messed up + v_flt BestDistance = GeneratorValue ? *GeneratorValue : GetItemDistance(*DataItems[Index++]); + + for (;;) + { + FindNextItem(); + + if (Index == DataItems.Num()) + { + return BestDistance; + } + + const v_flt Distance = GetItemDistance(*DataItems[Index++]); + + if (CombineMode == EVoxelDataItemCombineMode::Min) + { + BestDistance = Smoothness <= 0 ? FMath::Min(Distance, BestDistance) : FVoxelSDFUtilities::opSmoothUnion(Distance, BestDistance, Smoothness); + } + else if (CombineMode == EVoxelDataItemCombineMode::Max) + { + BestDistance = Smoothness <= 0 ? FMath::Max(Distance, BestDistance) : FVoxelSDFUtilities::opSmoothIntersection(Distance, BestDistance, Smoothness); + } + else + { + ensureVoxelSlow(CombineMode == EVoxelDataItemCombineMode::Sum); + BestDistance += Distance; + } + } + } + + // Useful for GetValueRange + template + inline TVoxelRange GetDataItemDistanceRange( + const FVoxelPlaceableItemHolder& ItemHolder, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + TVoxelRange Smoothness, + TVoxelRange Default, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode, + const TVoxelRange* GeneratorValue = nullptr) + { + const FVoxelIntBox Bounds = FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + + const auto ShouldComputeItem = [&](const FVoxelDataItem& Item) + { + return (Item.Mask & Mask) && Item.Bounds.Intersect(Bounds); + }; + const auto GetItemDistance = [&](const FVoxelDataItem& Item) + { + const auto Stack = FVoxelItemStack::Empty.WithCustomData(&Item.Data); + auto Range = Item.Generator->GetValueRange(Bounds, 0, Stack); + if (bInvertDataItemDistances) + { + Range = -Range; + } + + if (!GeneratorValue && !Item.Bounds.Contains(Bounds)) + { + // We might return Default if we're queried outside of the item bounds + Range = TVoxelRange::Union(Range, Default); + } + + return Range; + }; + + auto& DataItems = ItemHolder.GetDataItems(); + int32 Index = 0; + + const auto FindNextItem = [&]() + { + while (Index < DataItems.Num() && !ShouldComputeItem(*DataItems[Index])) + { + Index++; + } + }; + + FindNextItem(); + + if (Index == DataItems.Num()) + { + if (GeneratorValue) + { + return *GeneratorValue; + } + else + { + return Default; + } + } + + // Note: we can't use Default here else SmoothUnion is messed up + TVoxelRange BestDistance = GeneratorValue ? *GeneratorValue : GetItemDistance(*DataItems[Index++]); + + for (;;) + { + FindNextItem(); + + if (Index == DataItems.Num()) + { + const v_flt Extent = FMath::Max(Smoothness.Max, 0); + return { BestDistance.Min - Extent, BestDistance.Max + Extent }; + } + + const TVoxelRange Distance = GetItemDistance(*DataItems[Index++]); + + if (CombineMode == EVoxelDataItemCombineMode::Min) + { + BestDistance = FVoxelRangeUtilities::Min(Distance, BestDistance); + } + else if (CombineMode == EVoxelDataItemCombineMode::Max) + { + BestDistance = FVoxelRangeUtilities::Max(Distance, BestDistance); + } + else + { + ensureVoxelSlow(CombineMode == EVoxelDataItemCombineMode::Sum); + BestDistance += Distance; + } + } + } + + /** + * Combine an existing generator value (must be a distance) to the data item distances + * See https://wiki.voxelplugin.com/Voxel_Data_Items + * @param GeneratorValue The existing value to combine with the data items + * @param ItemHolder The item holder passed to the generator + * @param X The current X coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Y The current Y coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Z The current Z coordinate. Should be the one passed to the generator to avoid issues with spatial hashing + * @param Smoothness The smoothness of the union/intersection. Should be >= 0 + * @param Mask Use uint32(-1) to match any items + * @param CombineMode How to combine data items + * @return The resulting distance + */ + template + inline v_flt CombineDataItemDistance( + v_flt GeneratorValue, + const FVoxelPlaceableItemHolder& ItemHolder, + v_flt X, v_flt Y, v_flt Z, + v_flt Smoothness, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode) + { + return GetDataItemDistance(ItemHolder, X, Y, Z, Smoothness, 0, Mask, CombineMode, &GeneratorValue); + } + template + inline TVoxelRange CombineDataItemDistanceRange( + TVoxelRange GeneratorValue, + const FVoxelPlaceableItemHolder& ItemHolder, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + TVoxelRange Smoothness, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode) + { + return GetDataItemDistanceRange(ItemHolder, X, Y, Z, Smoothness, 0, Mask, CombineMode, &GeneratorValue); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.h new file mode 100644 index 00000000..a61a5eca --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.h @@ -0,0 +1,67 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelValue.h" + +struct VOXEL_API FVoxelDistanceFieldUtilities +{ +public: + FORCEINLINE static bool IsSurfacePositionValid(const FVector& P) + { + return P.X < 1e9; + } + FORCEINLINE static FVector MakeInvalidSurfacePosition() + { + return FVector(1e9); + } + +public: + static FColor GetDistanceFieldColor(float Value); + +public: + static void JumpFlood(const FIntVector& Size, TArray& InOutPackedPositions, EVoxelComputeDevice Device, bool bMultiThreaded = false, int32 MaxPasses_Debug = -1); + // Only the InOutDistances sign will be used, not their actual values + static void GetDistancesFromSurfacePositions(const FIntVector& Size, TArrayView SurfacePositions, TArrayView InOutDistances); + +public: + // OutDistances will only have the signs of the values + // Note: densities need to match Size + 2, so that all neighbors can be queried! + template + static void GetSurfacePositionsFromDensities( + const FIntVector& Size, + TArrayView Densities, + TArrayView OutDistances, + TArrayView OutSurfacePositions, + TLambda GetFloatFromT); + + static void GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArrayView OutDistances, TArrayView OutSurfacePositions); + static void GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArrayView OutDistances, TArrayView OutSurfacePositions); + + static void GetSurfacePositionsFromDensities(const FIntVector& Size, TArrayView Densities, TArray& OutDistances, TArray& OutSurfacePositions); + +public: + // Must be called BEFORE JumpFlood + // bShrink: if true, will bias towards shrinking the distance field. If false, will bias towards growing it + // TODO Doesn't work well, signs are leaking & wrongs on the borders + static void DownSample( + const FIntVector& Size, + TArrayView InDistances, + TArrayView InSurfacePositions, + TArrayView OutDistances, + TArrayView OutSurfacePositions, + int32 Divisor, + bool bShrink); + + static void DownSample( + FIntVector& Size, + TArray& Distances, + TArray& SurfacePositions, + int32 Divisor, + bool bShrink); + +private: + static void JumpFloodStep_CPU(const FIntVector& Size, TArrayView InData, TArrayView OutData, int32 Step, bool bMultiThreaded); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.inl new file mode 100644 index 00000000..64bda0b3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.inl @@ -0,0 +1,91 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelUtilities/VoxelMiscUtilities.h" + +template +void FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities( + const FIntVector& Size, + TArrayView Densities, + TArrayView OutDistances, + TArrayView OutSurfacePositions, + TLambda GetFloatFromT) +{ + VOXEL_ASYNC_FUNCTION_COUNTER(); + + const FIntVector DensitiesSize = Size + 2; + const FIntVector DensitiesOffset = FIntVector(-1, -1, -1); + + check(Densities.Num() == DensitiesSize.X * DensitiesSize.Y * DensitiesSize.Z); + check(OutDistances.Num() == Size.X * Size.Y * Size.Z); + check(OutSurfacePositions.Num() == Size.X * Size.Y * Size.Z); + + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + const FIntVector Position(X, Y, Z); + + const float Value = GetFloatFromT(FVoxelUtilities::Get3D(Densities, DensitiesSize, Position, DensitiesOffset)); + + const int32 Index = FVoxelUtilities::Get3DIndex(Size, Position); + FVoxelUtilities::Get(OutDistances, Index) = FMath::Sign(Value); + + // Static branch + const auto Lambda = [&](auto IsNegative) + { + // Take the max: this is the one that will "push" the value the closest to us + // Only consider positive values, so that there's a surface between us + // By symmetry, take the min value negative if Value is positive + float MaxNeighborValue = 0.f; + FIntVector MaxNeighborPosition; + +#define CheckNeighbor(DX, DY, DZ) \ + { \ + const FIntVector NeighborPosition = Position + FIntVector(DX, DY, DZ); \ + const float NeighborValue = GetFloatFromT(FVoxelUtilities::Get3D(Densities, DensitiesSize, NeighborPosition, DensitiesOffset)); \ + \ + if (IsNegative ? (NeighborValue > MaxNeighborValue) : (NeighborValue < MaxNeighborValue)) \ + { \ + MaxNeighborValue = NeighborValue; \ + MaxNeighborPosition = NeighborPosition; \ + } \ + } + + CheckNeighbor(-1, 0, 0); + CheckNeighbor(+1, 0, 0); + CheckNeighbor(0, -1, 0); + CheckNeighbor(0, +1, 0); + CheckNeighbor(0, 0, -1); + CheckNeighbor(0, 0, +1); + +#undef CheckNeighbor + + if (MaxNeighborValue == 0.f) + { + FVoxelUtilities::Get(OutSurfacePositions, Index) = MakeInvalidSurfacePosition(); + } + else + { + const float Alpha = Value / (Value - MaxNeighborValue); + FVoxelUtilities::Get(OutSurfacePositions, Index) = FMath::Lerp(FVector(Position), FVector(MaxNeighborPosition), Alpha); + } + }; + + if (Value <= 0) + { + Lambda(FVoxelUtilities::FTrueType()); + } + else + { + Lambda(FVoxelUtilities::FFalseType()); + } + } + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelExampleUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelExampleUtilities.h new file mode 100644 index 00000000..80777fd8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelExampleUtilities.h @@ -0,0 +1,15 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/UObjectGlobals.h" + +namespace FVoxelExampleUtilities +{ + template + T* LoadExampleObject(const TCHAR* Name) + { + return LoadObject(nullptr, Name, nullptr, LOAD_NoWarn); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelGeneratorUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelGeneratorUtilities.h new file mode 100644 index 00000000..9ce1ca54 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelGeneratorUtilities.h @@ -0,0 +1,44 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +namespace FVoxelUtilities +{ + template + const TCHAR* GetGeneratorOutputTypeName(); + + template<> + inline const TCHAR* GetGeneratorOutputTypeName() + { + return TEXT("float"); + } + template<> + inline const TCHAR* GetGeneratorOutputTypeName() + { + return TEXT("int"); + } + template<> + inline const TCHAR* GetGeneratorOutputTypeName() + { + return TEXT("color"); + } + + template + inline FString GetMissingGeneratorOutputErrorString(FName Name, const TGenerator& Generator) + { + ensure(!Generator.template GetOutputsPtrMap().Contains(Name)); + FString Types; + for (auto& It : Generator.template GetOutputsPtrMap()) + { + if (!Types.IsEmpty()) Types += ", "; + Types += It.Key.ToString(); + } + return FString::Printf( + TEXT("No voxel generator/voxel graph output named %s and with type %s found! Valid names: %s"), + *Name.ToString(), + GetGeneratorOutputTypeName(), + *Types); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelIntVectorUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelIntVectorUtilities.h new file mode 100644 index 00000000..91ad6e44 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelIntVectorUtilities.h @@ -0,0 +1,310 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +namespace FVoxelUtilities +{ + FORCEINLINE FIntVector RoundToInt(const FVector& Vector) + { + return FIntVector( + FMath::RoundToInt(Vector.X), + FMath::RoundToInt(Vector.Y), + FMath::RoundToInt(Vector.Z)); + } + FORCEINLINE FIntVector FloorToInt(const FVector& Vector) + { + return FIntVector( + FMath::FloorToInt(Vector.X), + FMath::FloorToInt(Vector.Y), + FMath::FloorToInt(Vector.Z)); + } + FORCEINLINE FIntVector CeilToInt(const FVector& Vector) + { + return FIntVector( + FMath::CeilToInt(Vector.X), + FMath::CeilToInt(Vector.Y), + FMath::CeilToInt(Vector.Z)); + } + + FORCEINLINE FIntVector Abs(const FIntVector& Vector) + { + return FIntVector( + FMath::Abs(Vector.X), + FMath::Abs(Vector.Y), + FMath::Abs(Vector.Z)); + } + + FORCEINLINE FIntVector ComponentMax(const FIntVector& A, const FIntVector& B) + { + return FIntVector( + FMath::Max(A.X, B.X), + FMath::Max(A.Y, B.Y), + FMath::Max(A.Z, B.Z)); + } + FORCEINLINE FIntVector ComponentMin(const FIntVector& A, const FIntVector& B) + { + return FIntVector( + FMath::Min(A.X, B.X), + FMath::Min(A.Y, B.Y), + FMath::Min(A.Z, B.Z)); + } + + FORCEINLINE FIntVector ComponentMin3(const FIntVector& A, const FIntVector& B, const FIntVector& C) + { + return ComponentMin(A, ComponentMin(B, C)); + } + FORCEINLINE FIntVector ComponentMax3(const FIntVector& A, const FIntVector& B, const FIntVector& C) + { + return ComponentMax(A, ComponentMax(B, C)); + } + + FORCEINLINE FVector ComponentMin3(const FVector& A, const FVector& B, const FVector& C) + { + return A.ComponentMin(B.ComponentMin(C)); + } + FORCEINLINE FVector ComponentMax3(const FVector& A, const FVector& B, const FVector& C) + { + return A.ComponentMax(B.ComponentMax(C)); + } + + FORCEINLINE bool CountIs32Bits(const FIntVector& Size) + { + return FMath::Abs(int64(Size.X) * int64(Size.Y) * int64(Size.Z)) < MAX_int32; + } + + // Defaults to the "lowest" axis if equal (will return X if X and Y are equal) + template + FORCEINLINE int32 GetArgMin(const TVector& V) + { + if (V.X <= V.Y && V.X <= V.Z) + { + return 0; + } + else if (V.Y <= V.Z) + { + return 1; + } + else + { + return 2; + } + } + // Defaults to the "lowest" axis if equal (will return X if X and Y are equal) + template + FORCEINLINE int32 GetArgMax(const TVector& V) + { + if (V.X >= V.Y && V.X >= V.Z) + { + return 0; + } + else if (V.Y >= V.Z) + { + return 1; + } + else + { + return 2; + } + } + + FORCEINLINE FIntVector Clamp(const FIntVector& V, const FIntVector& Min, const FIntVector& Max) + { + return FIntVector( + FMath::Clamp(V.X, Min.X, Max.X), + FMath::Clamp(V.Y, Min.Y, Max.Y), + FMath::Clamp(V.Z, Min.Z, Max.Z)); + } + FORCEINLINE FIntVector DivideFloor(const FIntVector& V, int32 Divisor) + { + return FIntVector( + DivideFloor(V.X, Divisor), + DivideFloor(V.Y, Divisor), + DivideFloor(V.Z, Divisor)); + } + FORCEINLINE FIntVector DivideCeil(const FIntVector& V, int32 Divisor) + { + return FIntVector( + DivideCeil(V.X, Divisor), + DivideCeil(V.Y, Divisor), + DivideCeil(V.Z, Divisor)); + } + FORCEINLINE FIntVector DivideRound(const FIntVector& V, int32 Divisor) + { + return FIntVector( + DivideRound(V.X, Divisor), + DivideRound(V.Y, Divisor), + DivideRound(V.Z, Divisor)); + } + FORCEINLINE uint64 SquaredSize(const FIntVector& V) + { + return FMath::Square(V.X) + FMath::Square(V.Y) + FMath::Square(V.Z); + } + + FORCEINLINE TVoxelStaticArray GetNeighbors(const FVector& P) + { + const int32 MinX = FMath::FloorToInt(P.X); + const int32 MinY = FMath::FloorToInt(P.Y); + const int32 MinZ = FMath::FloorToInt(P.Z); + + const int32 MaxX = FMath::CeilToInt(P.X); + const int32 MaxY = FMath::CeilToInt(P.Y); + const int32 MaxZ = FMath::CeilToInt(P.Z); + + return { + FIntVector(MinX, MinY, MinZ), + FIntVector(MaxX, MinY, MinZ), + FIntVector(MinX, MaxY, MinZ), + FIntVector(MaxX, MaxY, MinZ), + FIntVector(MinX, MinY, MaxZ), + FIntVector(MaxX, MinY, MaxZ), + FIntVector(MinX, MaxY, MaxZ), + FIntVector(MaxX, MaxY, MaxZ) + }; + } + FORCEINLINE TVoxelStaticArray GetNeighbors(float X, float Y) + { + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + + return { + FIntPoint(MinX, MinY), + FIntPoint(MaxX, MinY), + FIntPoint(MinX, MaxY), + FIntPoint(MaxX, MaxY) + }; + } + + inline TArray> GetImmediateNeighbors(const FIntVector& V) + { + return { + FIntVector(V.X - 1, V.Y, V.Z), + FIntVector(V.X + 1, V.Y, V.Z), + FIntVector(V.X, V.Y - 1, V.Z), + FIntVector(V.X, V.Y + 1, V.Z), + FIntVector(V.X, V.Y, V.Z - 1), + FIntVector(V.X, V.Y, V.Z + 1) + }; + } + inline void AddImmediateNeighborsToArray(const FIntVector& V, TArray& Array) + { + const int32& X = V.X; + const int32& Y = V.Y; + const int32& Z = V.Z; + + const uint32 Pos = Array.AddUninitialized(6); + FIntVector* Ptr = Array.GetData() + Pos; + + new (Ptr++) FIntVector(X - 1, Y, Z); + new (Ptr++) FIntVector(X + 1, Y, Z); + + new (Ptr++) FIntVector(X, Y - 1, Z); + new (Ptr++) FIntVector(X, Y + 1, Z); + + new (Ptr++) FIntVector(X, Y, Z - 1); + new (Ptr++) FIntVector(X, Y, Z + 1); + + checkVoxelSlow(Ptr == Array.GetData() + Array.Num()); + } + + FORCEINLINE uint32 MurmurHash(const FIntVector& V) + { + return + FVoxelUtilities::MurmurHash32(V.X) ^ + FVoxelUtilities::MurmurHash32(V.Y) ^ + FVoxelUtilities::MurmurHash32(V.Z); + } +}; + +FORCEINLINE FIntVector operator-(const FIntVector& V) +{ + return FIntVector(-V.X, -V.Y, -V.Z); +} + +FORCEINLINE FIntVector operator-(const FIntVector& V, int32 I) +{ + return FIntVector(V.X - I, V.Y - I, V.Z - I); +} +FORCEINLINE FIntVector operator-(const FIntVector& V, uint32 I) +{ + return FIntVector(V.X - I, V.Y - I, V.Z - I); +} +FORCEINLINE FIntVector operator-(int32 I, const FIntVector& V) +{ + return FIntVector(I - V.X, I - V.Y, I - V.Z); +} +FORCEINLINE FIntVector operator-(uint32 I, const FIntVector& V) +{ + return FIntVector(I - V.X, I - V.Y, I - V.Z); +} + +FORCEINLINE FIntVector operator+(const FIntVector& V, int32 I) +{ + return FIntVector(V.X + I, V.Y + I, V.Z + I); +} +FORCEINLINE FIntVector operator+(const FIntVector& V, uint32 I) +{ + return FIntVector(V.X + I, V.Y + I, V.Z + I); +} +FORCEINLINE FIntVector operator+(int32 I, const FIntVector& V) +{ + return FIntVector(I + V.X, I + V.Y, I + V.Z); +} +FORCEINLINE FIntVector operator+(uint32 I, const FIntVector& V) +{ + return FIntVector(I + V.X, I + V.Y, I + V.Z); +} + +FORCEINLINE FIntVector operator*(int32 I, const FIntVector& V) +{ + return FIntVector(I * V.X, I * V.Y, I * V.Z); +} +FORCEINLINE FIntVector operator*(uint32 I, const FIntVector& V) +{ + return FIntVector(I * V.X, I * V.Y, I * V.Z); +} +FORCEINLINE FIntVector operator*(const FIntVector& V, uint32 I) +{ + return FIntVector(I * V.X, I * V.Y, I * V.Z); +} +FORCEINLINE FIntVector operator*(const FIntVector& A, const FIntVector& B) +{ + return FIntVector(A.X * B.X, A.Y * B.Y, A.Z * B.Z); +} + +FORCEINLINE FIntVector operator%(const FIntVector& A, const FIntVector& B) +{ + return FIntVector(A.X % B.X, A.Y % B.Y, A.Z % B.Z); +} +FORCEINLINE FIntVector operator%(const FIntVector& V, int32 I) +{ + return V % FIntVector(I); +} +FORCEINLINE FIntVector operator%(const FIntVector& V, uint32 I) +{ + return V % FIntVector(I); +} + +template +FIntVector operator-(const FIntVector& V, T A) = delete; +template +FIntVector operator-(T A, const FIntVector& V) = delete; + +template +FIntVector operator+(const FIntVector& V, T A) = delete; +template +FIntVector operator+(T A, const FIntVector& V) = delete; + +template +FIntVector operator*(const FIntVector& V, T A) = delete; +template +FIntVector operator*(T A, const FIntVector& V) = delete; + +template +FIntVector operator%(const FIntVector& V, T A) = delete; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelLambdaUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelLambdaUtilities.h new file mode 100644 index 00000000..a79ed4a1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelLambdaUtilities.h @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +namespace FVoxelLambdaUtilities +{ + const auto ConstantStrength = [](float Distance) { return 1.f; }; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMaterialUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMaterialUtilities.h new file mode 100644 index 00000000..194d3366 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMaterialUtilities.h @@ -0,0 +1,70 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMaterial.h" +#include "VoxelMathUtilities.h" + +class UMaterialInterface; + +namespace FVoxelUtilities +{ + VOXEL_API bool IsMaterialTessellated(UMaterialInterface* Material); + + // NumIndices: else chunks get merged with different number of tex coordinates + VOXEL_API UMaterialInterface* GetDefaultMaterial(int32 NumIndices); + + FORCEINLINE int32 GetMultiIndexIndex(const FVoxelMaterial& Material, int32 Channel) + { + if (Channel == Material.GetMultiIndex_Index0()) + { + return 0; + } + else if (Channel == Material.GetMultiIndex_Index1()) + { + return 1; + } + else if (Channel == Material.GetMultiIndex_Index2()) + { + return 2; + } + else if (Channel == Material.GetMultiIndex_Index3()) + { + return 3; + } + else + { + return -1; + } + } + + FORCEINLINE TVoxelStaticArray GetMultiIndexStrengths(const FVoxelMaterial& Material) + { + return FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>( + { + Material.GetMultiIndex_Blend0_AsFloat(), + Material.GetMultiIndex_Blend1_AsFloat(), + Material.GetMultiIndex_Blend2_AsFloat() + }); + } + FORCEINLINE TVoxelStaticArray GetFiveWayBlendStrengths(const FVoxelMaterial& Material) + { + return FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<5>( + { + Material.GetR_AsFloat(), + Material.GetG_AsFloat(), + Material.GetB_AsFloat(), + Material.GetA_AsFloat() + }); + } + FORCEINLINE TVoxelStaticArray GetFourWayBlendStrengths(const FVoxelMaterial& Material) + { + return FVoxelUtilities::XWayBlend_AlphasToStrengths_Static<4>( + { + Material.GetR_AsFloat(), + Material.GetG_AsFloat(), + Material.GetB_AsFloat() + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMathUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMathUtilities.h new file mode 100644 index 00000000..389fa0c2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMathUtilities.h @@ -0,0 +1,532 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" + +namespace FVoxelUtilities +{ +#define CHECK_CHUNK_SIZE() static_assert(FVoxelUtilities::IsPowerOfTwo(ChunkSize), "ChunkSize must be a power of 2") + // Get required depth such that ChunkSize << Depth >= Size + template + inline int32 GetDepthFromSize(uint32 Size) + { + CHECK_CHUNK_SIZE(); + if (Size <= 0) + { + return 0; + } + else + { + const int32 Depth = 31 - FPlatformMath::CountLeadingZeros(Size / ChunkSize); + if (ChunkSize << Depth == Size) + { + return Depth; + } + else + { + return Depth + 1; + } + } + } + + template + inline constexpr uint32 GetSizeFromDepth(int32 Depth) + { + CHECK_CHUNK_SIZE(); + return ChunkSize << Depth; + } + + template + inline int32 GetDepthFromBounds(const FVoxelIntBox& Bounds) + { + CHECK_CHUNK_SIZE(); + return GetDepthFromSize(Bounds.Size().GetMax()); + } + + template + inline FVoxelIntBox GetBoundsFromDepth(int32 Depth) + { + CHECK_CHUNK_SIZE(); + const FIntVector Size = FIntVector((ChunkSize << Depth) / 2); + return FVoxelIntBox(-Size, Size); + } + + template + inline FVoxelIntBox GetCustomBoundsForDepth(FVoxelIntBox Bounds, int32 Depth) + { + CHECK_CHUNK_SIZE(); + Bounds = Bounds.MakeMultipleOfBigger(ChunkSize); + Bounds = FVoxelUtilities::GetBoundsFromDepth(Depth).Overlap(Bounds); + check(Bounds.IsMultipleOf(ChunkSize)); + return Bounds; + } + + template + inline FVoxelIntBox GetBoundsFromPositionAndDepth(const FIntVector& Position, int32 Depth) + { + CHECK_CHUNK_SIZE(); + return FVoxelIntBox(Position, Position + FIntVector(ChunkSize << Depth)); + } + + // Valid for root node only + template + inline int32 GetOctreeDepthContainingBounds(const FVoxelIntBox& Bounds) + { + CHECK_CHUNK_SIZE(); + const uint32 Max = FMath::Max(FVoxelUtilities::Abs(Bounds.Min).GetMax(), FVoxelUtilities::Abs(Bounds.Max).GetMax()); + return GetDepthFromSize(2 * Max); // 2x: octree doesn't start at 0 0 0 + } + + template + inline constexpr int32 ConvertDepth(int32 Depth) + { + static_assert(IsPowerOfTwo(FromChunkSize), "FromChunkSize must be a power of 2"); + static_assert(IsPowerOfTwo(ToChunkSize), "ToChunkSize must be a power of 2"); + + if (FromChunkSize == ToChunkSize) + { + return Depth; + } + else if (FromChunkSize < ToChunkSize) + { + // Depth should be lower + return Depth - IntLog2(ToChunkSize / FromChunkSize); + } + else + { + // FromChunkSize > ToChunkSize + // Depth should be higher + return Depth + IntLog2(FromChunkSize / ToChunkSize); + } + } + + template + inline int32 ClampDepth(int32 Depth) + { + CHECK_CHUNK_SIZE(); + constexpr int32 ChunkSizeDepth = IntLog2(ChunkSize); + // In theory MaxDepth could be 31 + // To avoid overflows when doing math we use 30 + constexpr int32 MaxDepth = 30; + // ChunkSizeDepth + Depth <= MaxDepth + // Depth <= MaxDepth - ChunkSizeDepth + return FMath::Clamp(Depth, 0, MaxDepth - ChunkSizeDepth); + } +#undef CHECK_CHUNK_SIZE + + inline int32 ClampMesherDepth(int32 Depth) + { + // 2x: Bounds.Size() needs to fit in a int32 for Meshers + return ClampDepth<2 * RENDER_CHUNK_SIZE>(Depth); + } + + template + inline T MergeAsset(T A, T B, bool bSubtractiveAsset) + { + return bSubtractiveAsset ? FMath::Max(A, B) : FMath::Min(A, B); + } + + // Falloff: between 0 and 1 + inline v_flt RoundCylinder(const FVoxelVector& PositionRelativeToCenter, v_flt Radius, v_flt Height, v_flt Falloff) + { + const v_flt InternalRadius = Radius * (1.f - Falloff); + const v_flt ExternalRadius = Radius * Falloff; + + const v_flt DistanceToCenterXY = FVector2D(PositionRelativeToCenter.X, PositionRelativeToCenter.Y).Size(); + const v_flt DistanceToCenterZ = FMath::Abs(PositionRelativeToCenter.Z); + + const v_flt SidesSDF = DistanceToCenterXY - InternalRadius; + const v_flt TopSDF = DistanceToCenterZ - Height / 2 + ExternalRadius; + + return + FMath::Min(FMath::Max(SidesSDF, TopSDF), 0.0f) + + FVector2D(FMath::Max(SidesSDF, 0.f), FMath::Max(TopSDF, 0.f)).Size() + + -ExternalRadius; + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + template + FORCEINLINE void XWayBlend_AlphasToStrengths_Impl(int32 NumChannels, const TIn& Alphas, TOut& Strengths) + { + ensureVoxelSlow(NumChannels > 1); + + // Unpack the strengths from the lerp values + for (int32 Index = 0; Index < NumChannels; Index++) + { + Strengths[Index] = Index == 0 ? 1.f : Alphas[Index - 1]; + for (int32 AlphaIndex = Index; AlphaIndex < NumChannels - 1; AlphaIndex++) + { + Strengths[Index] *= 1.f - Alphas[AlphaIndex]; + } + } + +#if VOXEL_DEBUG + float Sum = 0.f; + for (int32 Index = 0; Index < NumChannels; Index++) + { + Sum += Strengths[Index]; + } + ensure(FMath::IsNearlyEqual(Sum, 1.f, KINDA_SMALL_NUMBER)); +#endif + } + + template + FORCEINLINE void XWayBlend_StrengthsToAlphas_Impl(int32 NumChannels, TIn Strengths, TOut& Alphas, uint32 ChannelsToKeepIntact = 0) + { + ensureVoxelSlow(NumChannels > 1); + + if (!ChannelsToKeepIntact) + { + // Normalize: we want the sum to be 1 + float Sum = 0.f; + for (int32 Index = 0; Index < NumChannels; Index++) + { + Sum += Strengths[Index]; + } + if (Sum != 0.f) // This can very rarely happen if we subtracted all the data when editing + { + for (int32 Index = 0; Index < NumChannels; Index++) + { + Strengths[Index] /= Sum; + } + } + } + else + { + // Normalize so that Strengths[Index] doesn't change if (1 << Index) & ChannelsToKeepIntact + { + // Sum of all the other components + float SumToKeepIntact = 0.f; + float SumToChange = 0.f; + for (int32 Index = 0; Index < NumChannels; Index++) + { + if ((1u << Index) & ChannelsToKeepIntact) + { + SumToKeepIntact += Strengths[Index]; + } + else + { + SumToChange += Strengths[Index]; + } + } + + // If the sum to keep intact is above 1, normalize these channels too + // (but on their own) + if (SumToKeepIntact > 1.f) + { + for (int32 Index = 0; Index < NumChannels; Index++) + { + if ((1u << Index) & ChannelsToKeepIntact) + { + Strengths[Index] /= SumToKeepIntact; + } + } + SumToKeepIntact = 1.f; + } + + // We need to split this into the other channels. + const float SumToSplit = 1.f - SumToKeepIntact; + if (SumToChange == 0.f) + { + // If the sum is 0, increase all the other channels the same way + const float Value = SumToSplit / (NumChannels - FMath::CountBits(ChannelsToKeepIntact)); + + for (int32 Index = 0; Index < NumChannels; Index++) + { + if (!((1u << Index) & ChannelsToKeepIntact)) + { + Strengths[Index] = Value; + } + } + } + else + { + // Else scale them + const float Value = SumToSplit / SumToChange; + + for (int32 Index = 0; Index < NumChannels; Index++) + { + if (!((1u << Index) & ChannelsToKeepIntact)) + { + Strengths[Index] *= Value; + } + } + } + } + } + +#if VOXEL_DEBUG + float Sum = 0.f; + for (int32 Index = 0; Index < NumChannels; Index++) + { + Sum += Strengths[Index]; + } + ensure(FMath::IsNearlyEqual(Sum, 1.f, 0.001f)); +#endif + + const auto SafeDivide = [](float X, float Y) + { + // Here we resolve Y * A = X. If Y = 0, X should be 0 and A can be anything (here return 0) + ensureVoxelSlowNoSideEffects(Y != 0.f || FMath::IsNearlyZero(X)); + return Y == 0.f ? 0.f : X / Y; + }; + + // Pack them back in: do the maths in reverse order + const int32 NumAlphas = NumChannels - 1; + for (int32 AlphaIndex = NumAlphas - 1; AlphaIndex >= 0; AlphaIndex--) + { + float Divisor = 1.f; + for (int32 DivisorIndex = AlphaIndex + 1; DivisorIndex < NumAlphas; DivisorIndex++) + { + Divisor *= 1.f - Alphas[DivisorIndex]; + } + + Alphas[AlphaIndex] = SafeDivide(Strengths[AlphaIndex + 1], Divisor); + + } + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + template + FORCEINLINE TVoxelStaticArray XWayBlend_AlphasToStrengths_Static(const TVoxelStaticArray& Alphas) + { + TVoxelStaticArray Strengths; + XWayBlend_AlphasToStrengths_Impl(NumChannels, Alphas, Strengths); + return Strengths; + } + + template + FORCEINLINE TVoxelStaticArray XWayBlend_StrengthsToAlphas_Static(const TVoxelStaticArray& Strengths, uint32 ChannelsToKeepIntact = 0) + { + TVoxelStaticArray Alphas; + XWayBlend_StrengthsToAlphas_Impl(NumChannels, Strengths, Alphas, ChannelsToKeepIntact); + return Alphas; + } + // Avoid mistakes with ChannelsToKeepIntact not being a mask + template + void XWayBlend_StrengthsToAlphas_Static(const TVoxelStaticArray&, T) = delete; + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + template + FORCEINLINE TArray XWayBlend_AlphasToStrengths_Dynamic(const TInArray& Alphas) + { + TArray Strengths; + Strengths.SetNumUninitialized(Alphas.Num() + 1); + XWayBlend_AlphasToStrengths_Impl(Alphas.Num() + 1, Alphas, Strengths); + return Strengths; + } + + template + FORCEINLINE TArray XWayBlend_StrengthsToAlphas_Dynamic(const TInArray& Strengths, uint32 ChannelsToKeepIntact = 0) + { + TArray Alphas; + Alphas.SetNumUninitialized(Strengths.Num() - 1); + XWayBlend_StrengthsToAlphas_Impl(Strengths.Num(), Strengths, Alphas, ChannelsToKeepIntact); + return Alphas; + } + // Avoid mistakes with ChannelsToKeepIntact not being a mask + template + void XWayBlend_StrengthsToAlphas_Dynamic(const TInArray&, T) = delete; + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + template + FORCEINLINE TVoxelStaticArray, N> FindTopXElements_Impl(const ArrayType& Array, TLambda LessThan = TLess()) + { + checkVoxelSlow(Array.Num() >= N); + + // Biggest elements on top + TVoxelStaticArray, N> Stack; + + // Fill stack with first N values + for (int32 Index = 0; Index < N; Index++) + { + Stack[Index].template Get<0>() = Index; + Stack[Index].template Get<1>() = Array[Index]; + } + + // Sort the stack + Algo::Sort(Stack, [&](const auto& A, const auto& B) { return LessThan(B.template Get<1>(), A.template Get<1>()); }); + + for (int32 Index = N; Index < Array.Num(); Index++) + { + const T& ArrayValue = Array[Index]; + if (!LessThan(Stack[N - 1].template Get<1>(), ArrayValue)) + { + // Smaller than the entire stack + continue; + } + + // Find the element to replace + int32 StackToReplace = N - 1; + while (StackToReplace >= 1 && LessThan(Stack[StackToReplace - 1].template Get<1>(), ArrayValue)) + { + StackToReplace--; + } + + // Move existing elements down + for (int32 StackIndex = N - 1; StackIndex > StackToReplace; StackIndex--) + { + Stack[StackIndex] = Stack[StackIndex - 1]; + } + + // Write new element + Stack[StackToReplace].template Get<0>() = Index; + Stack[StackToReplace].template Get<1>() = ArrayValue; + } + + return Stack; + } + template + FORCEINLINE TVoxelStaticArray, N> FindTopXElements(const TArray& Array, TLambda LessThan = TLess()) + { + return FindTopXElements_Impl(Array, LessThan); + } + template + FORCEINLINE TVoxelStaticArray, N> FindTopXElements(const TVoxelStaticArray& Array, TLambda LessThan = TLess()) + { + return FindTopXElements_Impl(Array, LessThan); + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // Skips expensive bound checks outside of debug + template + FORCEINLINE auto& Get(T& Array, int32 Index) + { + checkVoxelSlow(0 <= Index && Index < GetNum(Array)); + return GetData(Array)[Index]; + } + + FORCEINLINE int32 Get3DIndex(const FIntVector& Size, int32 X, int32 Y, int32 Z, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + X -= Offset.X; + Y -= Offset.Y; + Z -= Offset.Z; + checkVoxelSlow(0 <= X && X < Size.X); + checkVoxelSlow(0 <= Y && Y < Size.Y); + checkVoxelSlow(0 <= Z && Z < Size.Z); + checkVoxelSlow(int64(Size.X) * int64(Size.Y) * int64(Size.X) < MAX_int32); + return X + Y * Size.X + Z * Size.X * Size.Y; + } + FORCEINLINE int32 Get3DIndex(const FIntVector& Size, const FIntVector& Position, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + return Get3DIndex(Size, Position.X, Position.Y, Position.Z, Offset); + } + + template + FORCEINLINE T& Get3D(T* RESTRICT Array, const FIntVector& Size, int32 X, int32 Y, int32 Z, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + return Array[Get3DIndex(Size, X, Y, Z, Offset)]; + } + template + FORCEINLINE T& Get3D(T* RESTRICT Array, const FIntVector& Size, const FIntVector& Position, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + return Get3D(Array, Size, Position.X, Position.Y, Position.Z, Offset); + } + + template + FORCEINLINE auto& Get3D(T& Array, const FIntVector& Size, int32 X, int32 Y, int32 Z, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + checkVoxelSlow(GetNum(Array) == Size.X * Size.Y * Size.Z); + return Get3D(GetData(Array), Size, X, Y, Z, Offset); + } + template + FORCEINLINE auto& Get3D(T& Array, const FIntVector& Size, const FIntVector& Position, const FIntVector& Offset = FIntVector(0, 0, 0)) + { + return Get3D(Array, Size, Position.X, Position.Y, Position.Z, Offset); + } + + template + FORCEINLINE auto Create3DGetter(T& Array, const FIntVector& Size, const FIntVector& Offset = FIntVector(0, 0, 0)) -> decltype(auto) + { + return [&Array, Size, Offset](int32 X, int32 Y, int32 Z) -> decltype(auto) { return Get3D(Array, Size, X, Y, Z, Offset); }; + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + FORCEINLINE float LinearFalloff(float Distance, float Radius, float Falloff) + { + return Distance <= Radius + ? 1.0f + : Radius + Falloff <= Distance + ? 0.f + : 1.0f - (Distance - Radius) / Falloff; + } + FORCEINLINE float SmoothFalloff(float Distance, float Radius, float Falloff) + { + const float X = LinearFalloff(Distance, Radius, Falloff); + return FMath::SmoothStep(0, 1, X); + } + FORCEINLINE float SphericalFalloff(float Distance, float Radius, float Falloff) + { + return Distance <= Radius + ? 1.0f + : Radius + Falloff <= Distance + ? 0.f + : FMath::Sqrt(1.0f - FMath::Square((Distance - Radius) / Falloff)); + } + FORCEINLINE float TipFalloff(float Distance, float Radius, float Falloff) + { + return Distance <= Radius + ? 1.0f + : Radius + Falloff <= Distance + ? 0.f + : 1.0f - FMath::Sqrt(1.0f - FMath::Square((Falloff + Radius - Distance) / Falloff)); + } + + // Falloff: between 0 and 1 + template + FORCEINLINE auto DispatchFalloff(EVoxelFalloff FalloffType, float Radius, float Falloff, T Lambda) -> decltype(auto) + { + Falloff = FMath::Clamp(Falloff, 0.f, 1.f); + if (Falloff == 0.f) + { + return Lambda([&](float Distance) { return 1.f; }); + } + + const float RelativeRadius = Radius * (1.f - Falloff); + const float RelativeFalloff = Radius * Falloff; + switch (FalloffType) + { + default: ensure(false); + case EVoxelFalloff::Linear: + { + return Lambda([=](float Distance) { return LinearFalloff(Distance, RelativeRadius, RelativeFalloff); }); + } + case EVoxelFalloff::Smooth: + { + return Lambda([=](float Distance) { return SmoothFalloff(Distance, RelativeRadius, RelativeFalloff); }); + } + case EVoxelFalloff::Spherical: + { + return Lambda([=](float Distance) { return SphericalFalloff(Distance, RelativeRadius, RelativeFalloff); }); + } + case EVoxelFalloff::Tip: + { + return Lambda([=](float Distance) { return TipFalloff(Distance, RelativeRadius, RelativeFalloff); }); + } + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMiscUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMiscUtilities.h new file mode 100644 index 00000000..00d8abdf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelMiscUtilities.h @@ -0,0 +1,203 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" + +namespace FVoxelUtilities +{ + template + struct TValuesMaterialsSelector; + + template<> + struct TValuesMaterialsSelector + { + template static inline auto& Get(U& Object) + { + return Object.Values; + } + }; + template<> + struct TValuesMaterialsSelector + { + template static inline auto& Get(U& Object) + { + return Object.Materials; + } + }; + + template<> + struct TValuesMaterialsSelector + { + template static inline auto& Get(U& Object) + { + return Object.Values; + } + }; + template<> + struct TValuesMaterialsSelector + { + template static inline auto& Get(U& Object) + { + return Object.Materials; + } + }; + + struct FTrueType + { + FORCEINLINE constexpr operator bool() const + { + return true; + } + }; + struct FFalseType + { + FORCEINLINE constexpr operator bool() const + { + return false; + } + }; + + template + void StaticBranch(bool bA, T Lambda) + { + if (bA) + Lambda(FTrueType()); + else + Lambda(FFalseType()); + } + template + void StaticBranch(bool bA, bool bB, TLambda Lambda) + { + const int32 Value = bA + 2 * bB; + using T = FTrueType; + using F = FFalseType; + switch (Value) + { + case 0: Lambda(F(), F()); return; + case 1: Lambda(T(), F()); return; + case 2: Lambda(F(), T()); return; + case 3: Lambda(T(), T()); return; + default: checkVoxelSlow(false); return; + } + } + template + void StaticBranch(bool bA, bool bB, bool bC, TLambda Lambda) + { + const int32 Value = bA + 2 * bB + 4 * bC; + using T = FTrueType; + using F = FFalseType; + switch (Value) + { + case 0: Lambda(F(), F(), F()); return; + case 1: Lambda(T(), F(), F()); return; + case 2: Lambda(F(), T(), F()); return; + case 3: Lambda(T(), T(), F()); return; + case 4: Lambda(F(), F(), T()); return; + case 5: Lambda(T(), F(), T()); return; + case 6: Lambda(F(), T(), T()); return; + case 7: Lambda(T(), T(), T()); return; + default: checkVoxelSlow(false); return; + } + } + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + template + struct TInteger + { + FORCEINLINE constexpr operator int32() const + { + return N; + } + }; + + template + struct TStaticSwitch; + +#define VOXEL_STATIC_SWITCH_CASE(Index) case Index: Lambda(TInteger()); return; +#define VOXEL_STATIC_SWITCH_CASE_1 VOXEL_STATIC_SWITCH_CASE(0) +#define VOXEL_STATIC_SWITCH_CASE_2 VOXEL_STATIC_SWITCH_CASE_1 VOXEL_STATIC_SWITCH_CASE(1) +#define VOXEL_STATIC_SWITCH_CASE_3 VOXEL_STATIC_SWITCH_CASE_2 VOXEL_STATIC_SWITCH_CASE(2) +#define VOXEL_STATIC_SWITCH_CASE_4 VOXEL_STATIC_SWITCH_CASE_3 VOXEL_STATIC_SWITCH_CASE(3) +#define VOXEL_STATIC_SWITCH_CASE_5 VOXEL_STATIC_SWITCH_CASE_4 VOXEL_STATIC_SWITCH_CASE(4) +#define VOXEL_STATIC_SWITCH_CASE_6 VOXEL_STATIC_SWITCH_CASE_5 VOXEL_STATIC_SWITCH_CASE(5) +#define VOXEL_STATIC_SWITCH_CASE_7 VOXEL_STATIC_SWITCH_CASE_6 VOXEL_STATIC_SWITCH_CASE(6) +#define VOXEL_STATIC_SWITCH_CASE_8 VOXEL_STATIC_SWITCH_CASE_7 VOXEL_STATIC_SWITCH_CASE(7) +#define VOXEL_STATIC_SWITCH_CASE_9 VOXEL_STATIC_SWITCH_CASE_8 VOXEL_STATIC_SWITCH_CASE(8) +#define VOXEL_STATIC_SWITCH_CASE_10 VOXEL_STATIC_SWITCH_CASE_9 VOXEL_STATIC_SWITCH_CASE(9) +#define VOXEL_STATIC_SWITCH_CASE_11 VOXEL_STATIC_SWITCH_CASE_10 VOXEL_STATIC_SWITCH_CASE(10) +#define VOXEL_STATIC_SWITCH_CASE_12 VOXEL_STATIC_SWITCH_CASE_11 VOXEL_STATIC_SWITCH_CASE(11) +#define VOXEL_STATIC_SWITCH_CASE_13 VOXEL_STATIC_SWITCH_CASE_12 VOXEL_STATIC_SWITCH_CASE(12) +#define VOXEL_STATIC_SWITCH_CASE_14 VOXEL_STATIC_SWITCH_CASE_13 VOXEL_STATIC_SWITCH_CASE(13) +#define VOXEL_STATIC_SWITCH_CASE_15 VOXEL_STATIC_SWITCH_CASE_14 VOXEL_STATIC_SWITCH_CASE(14) +#define VOXEL_STATIC_SWITCH_CASE_16 VOXEL_STATIC_SWITCH_CASE_15 VOXEL_STATIC_SWITCH_CASE(15) +#define VOXEL_STATIC_SWITCH_CASE_17 VOXEL_STATIC_SWITCH_CASE_16 VOXEL_STATIC_SWITCH_CASE(16) +#define VOXEL_STATIC_SWITCH_CASE_18 VOXEL_STATIC_SWITCH_CASE_17 VOXEL_STATIC_SWITCH_CASE(17) +#define VOXEL_STATIC_SWITCH_CASE_19 VOXEL_STATIC_SWITCH_CASE_18 VOXEL_STATIC_SWITCH_CASE(18) +#define VOXEL_STATIC_SWITCH_CASE_20 VOXEL_STATIC_SWITCH_CASE_19 VOXEL_STATIC_SWITCH_CASE(19) +#define VOXEL_STATIC_SWITCH_CASE_21 VOXEL_STATIC_SWITCH_CASE_20 VOXEL_STATIC_SWITCH_CASE(20) +#define VOXEL_STATIC_SWITCH_CASE_22 VOXEL_STATIC_SWITCH_CASE_21 VOXEL_STATIC_SWITCH_CASE(21) +#define VOXEL_STATIC_SWITCH_CASE_23 VOXEL_STATIC_SWITCH_CASE_22 VOXEL_STATIC_SWITCH_CASE(22) +#define VOXEL_STATIC_SWITCH_CASE_24 VOXEL_STATIC_SWITCH_CASE_23 VOXEL_STATIC_SWITCH_CASE(23) +#define VOXEL_STATIC_SWITCH_CASE_25 VOXEL_STATIC_SWITCH_CASE_24 VOXEL_STATIC_SWITCH_CASE(24) +#define VOXEL_STATIC_SWITCH_CASE_26 VOXEL_STATIC_SWITCH_CASE_25 VOXEL_STATIC_SWITCH_CASE(25) +#define VOXEL_STATIC_SWITCH_CASE_27 VOXEL_STATIC_SWITCH_CASE_26 VOXEL_STATIC_SWITCH_CASE(26) +#define VOXEL_STATIC_SWITCH_CASE_28 VOXEL_STATIC_SWITCH_CASE_27 VOXEL_STATIC_SWITCH_CASE(27) +#define VOXEL_STATIC_SWITCH_CASE_29 VOXEL_STATIC_SWITCH_CASE_28 VOXEL_STATIC_SWITCH_CASE(28) +#define VOXEL_STATIC_SWITCH_CASE_30 VOXEL_STATIC_SWITCH_CASE_29 VOXEL_STATIC_SWITCH_CASE(29) +#define VOXEL_STATIC_SWITCH_CASE_31 VOXEL_STATIC_SWITCH_CASE_30 VOXEL_STATIC_SWITCH_CASE(30) +#define VOXEL_STATIC_SWITCH_CASE_32 VOXEL_STATIC_SWITCH_CASE_31 VOXEL_STATIC_SWITCH_CASE(31) + +#define VOXEL_STATIC_SWITCH(Index) \ + template<> \ + struct TStaticSwitch \ + { \ + template \ + static void Switch(int32 Value, TLambda Lambda) \ + { \ + switch (Value) \ + { \ + VOXEL_STATIC_SWITCH_CASE_##Index \ + default: ensureVoxelSlow(false); return; \ + } \ + } \ + }; + + VOXEL_STATIC_SWITCH(4); + VOXEL_STATIC_SWITCH(6); + VOXEL_STATIC_SWITCH(8); + VOXEL_STATIC_SWITCH(12); + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + template + struct THasForceInitConstructor + { + template + static uint8 Test(X*); + + template + static uint32 Test(...); + + static constexpr bool Value = sizeof(Test(nullptr)) == sizeof(uint8); + }; + + // Works with void + template + typename TEnableIf::Value, T>::Type GetDefaultValue() + { + return T(); + } + template + typename TEnableIf::Value, T>::Type GetDefaultValue() + { + return T(ForceInit); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelOctreeUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelOctreeUtilities.h new file mode 100644 index 00000000..3150ab76 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelOctreeUtilities.h @@ -0,0 +1,196 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +enum class EVoxelOctreeLeafQuery +{ + CreateIfNull, + ReturnIfNull +}; + +namespace FVoxelOctreeUtilities +{ + template + inline auto GetLeaf(T& Tree, int32 X, int32 Y, int32 Z) -> decltype(&Tree.AsLeaf()) + { + checkVoxelSlow(Tree.IsInOctree(X, Y, Z)); + auto* Ptr = &Tree; + while (!Ptr->IsLeaf()) + { + if (!Ptr->AsParent().HasChildren()) + { + if (Query == EVoxelOctreeLeafQuery::CreateIfNull) + { + Ptr->AsParent().CreateChildren(); + } + else + { + return nullptr; + } + } + Ptr = &Ptr->AsParent().GetChild(X, Y, Z); + } + checkVoxelSlow(Ptr->IsInOctree(X, Y, Z)); + return &Ptr->AsLeaf(); + } + template + inline auto* GetLeaf(T& Tree, const FIntVector& P) + { + return GetLeaf(Tree, P.X, P.Y, P.Z); + } + + template + inline T& GetBottomNode(T& Tree, int32 X, int32 Y, int32 Z) + { + checkVoxelSlow(Tree.IsInOctree(X, Y, Z)); + auto* Ptr = &Tree; + while (!Ptr->IsLeaf() && Ptr->AsParent().HasChildren()) + { + Ptr = &Ptr->AsParent().GetChild(X, Y, Z); + } + checkVoxelSlow(Ptr->IsInOctree(X, Y, Z)); + return *Ptr; + } + + template + inline T& GetOctreeById(T& Tree, const TId& Id) + { + checkVoxelSlow(Tree.IsInOctree(Id.Position)); + checkVoxelSlow(Tree.Height >= Id.Height); + + auto* Ptr = &Tree; + for (int32 Height = Tree.Height; Height > Id.Height; Height--) + { + check(!Ptr->IsLeaf() && Ptr->AsParent().HasChildren()); + Ptr = &Ptr->AsParent().GetChild(Id.Position); + } + checkVoxelSlow(Ptr->Height == Id.Height); + checkVoxelSlow(Ptr->Position == Id.Position); + return *Ptr; + } + + /** + * It is safe to create children in Apply + */ + template + inline void IterateTreeByPred(T& Tree, F1 ShouldApply, F2 Apply) + { + if (!ShouldApply(Tree)) + { + return; + } + Apply(Tree); + + if (!Tree.IsLeaf() && Tree.AsParent().HasChildren()) + { + for (auto& Child : Tree.AsParent().GetChildren()) + { + IterateTreeByPred(Child, ShouldApply, Apply); + } + } + } + template + inline void IterateEntireTree(T& Tree, F1 Apply) + { + IterateTreeByPred(Tree, [](auto&) { return true; }, Apply); + } + template + inline void IterateTreeInBounds(T& Tree, const FVoxelIntBox& Bounds, F1 Apply) + { + IterateTreeByPred(Tree, [&](auto& IterTree) { return IterTree.GetBounds().Intersect(Bounds); }, Apply); + } + // Return true if the search completed. + // To exit, return false in Apply + template + inline bool IterateTreeInBoundsEarlyExit(T& Tree, const FVoxelIntBox& Bounds, F1 Apply) + { + bool bContinue = true; + IterateTreeByPred( + Tree, + [&](auto& IterTree) { return bContinue && IterTree.GetBounds().Intersect(Bounds); }, + [&](auto& Chunk) { bContinue &= Apply(Chunk); }); + return bContinue; + } + + template + inline void IterateLeavesByPred(T& Tree, F1 ShouldApply, F2 Apply) + { + if (!ShouldApply(Tree)) + { + return; + } + if (Tree.IsLeaf()) + { + Apply(Tree.AsLeaf()); + } + else if (Tree.AsParent().HasChildren()) + { + for (auto& Child : Tree.AsParent().GetChildren()) + { + IterateLeavesByPred(Child, ShouldApply, Apply); + } + } + } + template + inline void IterateAllLeaves(T& Tree, F1 Apply) + { + IterateLeavesByPred(Tree, [](auto&) { return true; }, Apply); + } + template + inline void IterateLeavesInBounds(T& Tree, const FVoxelIntBox& Bounds, F1 Apply) + { + IterateLeavesByPred(Tree, [&](auto& IterTree) { return IterTree.GetBounds().Intersect(Bounds); }, Apply); + } + // Return true if the search completed. + // To exit, return false in Apply + template + inline bool IterateLeavesInBoundsEarlyExit(T& Tree, const FVoxelIntBox& Bounds, F1 Apply) + { + bool bContinue = true; + IterateLeavesByPred( + Tree, + [&](auto& IterTree) { return bContinue && IterTree.GetBounds().Intersect(Bounds); }, + [&](auto& Chunk) { bContinue &= Apply(Chunk); }); + return bContinue; + } + + template + inline TOptional ReduceByPred(T& Tree, F1 ShouldApply, F2 Apply, F3 Reduction) + { + if (!ShouldApply(Tree)) + { + return {}; + } + if (Tree.IsLeaf() || !Tree.AsParent().HasChildren()) + { + return U{ Apply(Tree) }; + } + else + { + TOptional Result; + for (auto& Child : Tree.AsParent().GetChildren()) + { + const auto ChildResult = ReduceByPred(Child, ShouldApply, Apply, Reduction); + if (ChildResult.IsSet()) + { + if (Result.IsSet()) + { + Result = Reduction(Result.GetValue(), ChildResult.GetValue()); + } + else + { + Result = ChildResult; + } + } + } + return Result; + } + } + template + inline TOptional ReduceInBounds(T& Tree, const FVoxelIntBox& Bounds, F1 Apply, F2 Reduction) + { + return ReduceByPred(Tree, [&](auto& IterTree) { return IterTree.GetBounds().Intersect(Bounds); }, Apply, Reduction); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRangeUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRangeUtilities.h new file mode 100644 index 00000000..6e8b42a9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRangeUtilities.h @@ -0,0 +1,118 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRange.h" +#include "VoxelIntBox.h" + +#include + +namespace FVoxelRangeUtilities +{ + template + inline TVoxelRange Min(const TVoxelRange& A, const TVoxelRange& B) + { + return { FMath::Min(A.Min, B.Min), FMath::Min(A.Max, B.Max) }; + } + template + inline TVoxelRange Max(const TVoxelRange& A, const TVoxelRange& B) + { + return { FMath::Max(A.Min, B.Min), FMath::Max(A.Max, B.Max) }; + } + template + inline TVoxelRange Clamp(const TVoxelRange& Value, const TVoxelRange& Min, const TVoxelRange& Max) + { + if (Min.IsSingleValue() && Max.IsSingleValue()) + { + return + { + FMath::Clamp(Value.Min, Min.GetSingleValue(), Max.GetSingleValue()), + FMath::Clamp(Value.Max, Min.GetSingleValue(), Max.GetSingleValue()) + }; + } + if (Min.Max <= Value.Min && Value.Max <= Max.Min) // If already clamped + { + return Value; + } + else if (Max.Max <= Value.Min) + { + return Max.Max; + } + else if (Value.Max <= Min.Min) + { + return Min.Min; + } + else + { + return { Min.Min, Max.Max }; + } + } + template + inline TVoxelRange Abs(const TVoxelRange& A) + { + return { A.Contains(0) ? 0 : FMath::Min(FMath::Abs(A.Min), FMath::Abs(A.Max)), FMath::Max(FMath::Abs(A.Min), FMath::Abs(A.Max)) }; + } + template + inline TVoxelRange Sign(const TVoxelRange& A) + { + if (0 < A.Min) + { + return { 1, 1 }; + } + else if (A.Max < 0) + { + return { -1, -1 }; + } + else if (A.Min == 0 && A.Max == 0) + { + return { 0, 0 }; + } + else + { + return { -1, 1 }; + } + } + inline v_flt Sqrt(v_flt F) + { + if (F <= 0) + { + return 0; + } + else + { + return std::sqrt(F); + } + } + inline TVoxelRange Sqrt(const TVoxelRange& F) + { + return { Sqrt(FMath::Max(0., F.Min)), Sqrt(FMath::Max(0., F.Max)) }; + } + inline TVoxelRange Lerp(const TVoxelRange& A, const TVoxelRange& B, const TVoxelRange& Alpha) + { + if (Alpha.IsSingleValue() && 0 <= Alpha.GetSingleValue() && Alpha.GetSingleValue() <= 1) + { + return + { + FMath::Lerp(A.Min, B.Min, Alpha.GetSingleValue()), + FMath::Lerp(A.Max, B.Max, Alpha.GetSingleValue()) + }; + } + else + { + return A + Alpha * (B - A); + } + } + inline FVoxelIntBox BoundsFromRanges(TVoxelRange X, TVoxelRange Y, TVoxelRange Z) + { + return FVoxelIntBox( + FIntVector( + FMath::FloorToInt(X.Min), + FMath::FloorToInt(Y.Min), + FMath::FloorToInt(Z.Min)), + FIntVector( + FMath::CeilToInt(X.Max) + 1, + FMath::CeilToInt(Y.Max) + 1, + FMath::CeilToInt(Z.Max) + 1)); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRichCurveUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRichCurveUtilities.h new file mode 100644 index 00000000..81a0240f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelRichCurveUtilities.h @@ -0,0 +1,143 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Curves/RichCurve.h" + +namespace FVoxelRichCurveUtilities +{ + inline float BezierInterp(float P0, float P1, float P2, float P3, float Alpha) + { + const float P01 = FMath::Lerp(P0, P1, Alpha); + const float P12 = FMath::Lerp(P1, P2, Alpha); + const float P23 = FMath::Lerp(P2, P3, Alpha); + const float P012 = FMath::Lerp(P01, P12, Alpha); + const float P123 = FMath::Lerp(P12, P23, Alpha); + const float P0123 = FMath::Lerp(P012, P123, Alpha); + + return P0123; + } + + inline float EvalForTwoKeys(const FRichCurveKey& Key1, const FRichCurveKey& Key2, const float InTime) + { + const float Diff = Key2.Time - Key1.Time; + + if (Diff > 0.f && Key1.InterpMode != RCIM_Constant) + { + const float Alpha = (InTime - Key1.Time) / Diff; + const float P0 = Key1.Value; + const float P3 = Key2.Value; + + if (Key1.InterpMode == RCIM_Linear) + { + return FMath::Lerp(P0, P3, Alpha); + } + else + { + const float OneThird = 1.0f / 3.0f; + const float P1 = P0 + (Key1.LeaveTangent * Diff * OneThird); + const float P2 = P3 - (Key2.ArriveTangent * Diff * OneThird); + + return BezierInterp(P0, P1, P2, P3, Alpha); + } + } + else + { + return Key1.Value; + } + } + + inline float Eval(const FRichCurve& Curve, float InTime) // Inline + doesn't trigger a stat on every call + { + constexpr float InDefaultValue = 0.f; + const int32 NumKeys = Curve.Keys.Num(); + + // Remap time if extrapolation is present and compute offset value to use if cycling + float CycleValueOffset = 0; + Curve.RemapTimeValue(InTime, CycleValueOffset); + + // If the default value hasn't been initialized, use the incoming default value + float InterpVal = Curve.DefaultValue == MAX_flt ? InDefaultValue : Curve.DefaultValue; + + if (NumKeys == 0) + { + // If no keys in curve, return the Default value. + } + else if (NumKeys < 2 || (InTime <= Curve.Keys[0].Time)) + { + if (Curve.PreInfinityExtrap == RCCE_Linear && NumKeys > 1) + { + const float DT = Curve.Keys[1].Time - Curve.Keys[0].Time; + + if (FMath::IsNearlyZero(DT)) + { + InterpVal = Curve.Keys[0].Value; + } + else + { + const float DV = Curve.Keys[1].Value - Curve.Keys[0].Value; + const float Slope = DV / DT; + + InterpVal = Slope * (InTime - Curve.Keys[0].Time) + Curve.Keys[0].Value; + } + } + else + { + // Otherwise if constant or in a cycle or oscillate, always use the first key value + InterpVal = Curve.Keys[0].Value; + } + } + else if (InTime < Curve.Keys[NumKeys - 1].Time) + { + // perform a lower bound to get the second of the interpolation nodes + int32 first = 1; + int32 last = NumKeys - 1; + int32 count = last - first; + + while (count > 0) + { + int32 step = count / 2; + int32 middle = first + step; + + if (InTime >= Curve.Keys[middle].Time) + { + first = middle + 1; + count -= step + 1; + } + else + { + count = step; + } + } + + InterpVal = EvalForTwoKeys(Curve.Keys[first - 1], Curve.Keys[first], InTime); + } + else + { + if (Curve.PostInfinityExtrap == RCCE_Linear) + { + float DT = Curve.Keys[NumKeys - 2].Time - Curve.Keys[NumKeys - 1].Time; + + if (FMath::IsNearlyZero(DT)) + { + InterpVal = Curve.Keys[NumKeys - 1].Value; + } + else + { + float DV = Curve.Keys[NumKeys - 2].Value - Curve.Keys[NumKeys - 1].Value; + float Slope = DV / DT; + + InterpVal = Slope * (InTime - Curve.Keys[NumKeys - 1].Time) + Curve.Keys[NumKeys - 1].Value; + } + } + else + { + // Otherwise if constant or in a cycle or oscillate, always use the last key value + InterpVal = Curve.Keys[NumKeys - 1].Value; + } + } + + return InterpVal + CycleValueOffset; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFRangeUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFRangeUtilities.h new file mode 100644 index 00000000..5e3c0358 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFRangeUtilities.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRangeUtilities.h" + +namespace FVoxelSDFRangeUtilities +{ +#if !INTELLISENSE_PARSER + using flt = TVoxelRange; + + namespace Impl + { + FORCEINLINE flt Min(flt a, flt b) { return FVoxelRangeUtilities::Min(a, b); } + FORCEINLINE flt Max(flt a, flt b) { return FVoxelRangeUtilities::Max(a, b); } + FORCEINLINE flt Clamp(flt x, flt a, flt b) { return FVoxelRangeUtilities::Clamp(x, a, b); } + FORCEINLINE flt Abs(flt x) { return FVoxelRangeUtilities::Abs(x); } + FORCEINLINE flt Sign(flt x) { return FVoxelRangeUtilities::Sign(x); } + FORCEINLINE flt Sqrt(flt x) { return FVoxelRangeUtilities::Sqrt(x); } + FORCEINLINE flt Lerp(flt a, flt b, flt x) { return FVoxelRangeUtilities::Lerp(a, b, x); } + } +#endif + +#include "VoxelSDFUtilitiesImpl.inl" +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilities.h new file mode 100644 index 00000000..62954d4d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilities.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +namespace FVoxelSDFUtilities +{ +#if !INTELLISENSE_PARSER + using flt = v_flt; + + namespace Impl + { + FORCEINLINE flt Min(flt a, flt b) { return FMath::Min(a, b); } + FORCEINLINE flt Max(flt a, flt b) { return FMath::Max(a, b); } + FORCEINLINE flt Clamp(flt x, flt a, flt b) { return FMath::Clamp(x, a, b); } + FORCEINLINE flt Abs(flt x) { return FMath::Abs(x); } + FORCEINLINE flt Sign(flt x) { return FMath::Sign(x); } + FORCEINLINE flt Sqrt(flt x) { return FMath::Sqrt(x); } + FORCEINLINE flt Lerp(flt a, flt b, flt x) { return FMath::Lerp(a, b, x); } + } +#endif + +#include "VoxelSDFUtilitiesImpl.inl" +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilitiesImpl.inl b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilitiesImpl.inl new file mode 100644 index 00000000..c303f856 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilitiesImpl.inl @@ -0,0 +1,501 @@ +// Copyright 2020 Phyronnaz + +#if defined(__INTELLISENSE__) || defined(__RSCPP_VERSION) +#include "CoreMinimal.h" +#include "VoxelRange.h" +#include "VoxelVector.h" + +using flt = TVoxelRange; + +namespace Impl +{ + flt Min(flt a, flt b); + flt Max(flt a, flt b); + flt Clamp(flt x, flt a, flt b); + flt Abs(flt x); + flt Sign(flt x); + flt Sqrt(flt x); + flt Lerp(flt a, flt b, flt x); +} +#error "Compiler defined as parser" +#endif + +// Most functions here are from https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm +// Huge thanks to Inigo Quilez for sharing these! + +// NOTE: we FORCEINLINE math functions to allow for better optimizations + +////////////////////////////////////////////////////////////////////////////// +// Wrappers to make copy pasting shader code easier + +struct vec2 +{ + flt x; + flt y; + + vec2() = default; + FORCEINLINE vec2(flt v) : x(v), y(v) {} + FORCEINLINE vec2(flt x, flt y) : x(x), y(y) {} + template, TIsSame, TIsSame>::Value>::Type> + FORCEINLINE vec2(T Vector) : x(Vector.X), y(Vector.Y) {} + +#define SWIZZLE(a, b) FORCEINLINE const vec2 a##b() const { return { a, b }; } + SWIZZLE(x, y); + SWIZZLE(y, x); +#undef SWIZZLE + +#define OP(operator_op, op) \ +FORCEINLINE vec2 operator_op(const vec2& v) { x op v.x; y op v.y; return *this; } +OP(operator+=, +=); +OP(operator-=, -=); +OP(operator*=, *=); +OP(operator/=, /=); +#undef OP +}; + +struct vec3 +{ + flt x; + flt y; + flt z; + + vec3() = default; + FORCEINLINE vec3(flt v) : x(v), y(v), z(v) {} + FORCEINLINE vec3(flt x, flt y, flt z) : x(x), y(y), z(z) {} + template, TIsSame, TIsSame>::Value>::Type> + FORCEINLINE vec3(T Vector) : x(Vector.X), y(Vector.Y), z(Vector.Z) {} + +#define SWIZZLE(a, b) \ + FORCEINLINE const vec2 a##b() const { return { a, b }; } \ + FORCEINLINE void set_##a##b(vec2 v) { a = v.x, b = v.y; } + SWIZZLE(x, y); + SWIZZLE(x, z); + SWIZZLE(y, x); + SWIZZLE(y, z); + SWIZZLE(z, x); + SWIZZLE(z, y); +#undef SWIZZLE + +#define SWIZZLE(a, b, c) \ + FORCEINLINE const vec3 a##b##c() const { return { a, b, c }; } \ + FORCEINLINE void set_##a##b##c(vec3 v) { a = v.x, b = v.y; c = v.z; } + SWIZZLE(x, y, z); + SWIZZLE(x, z, y); + SWIZZLE(y, x, z); + SWIZZLE(y, z, x); + SWIZZLE(z, x, y); + SWIZZLE(z, y, x); +#undef SWIZZLE + +#define OP(operator_op,op) \ +FORCEINLINE vec3 operator_op(const vec3& v) { x op v.x; y op v.y; z op v.z; return *this; } +OP(operator+=, +=); +OP(operator-=, -=); +OP(operator*=, *=); +OP(operator/=, /=); +#undef OP +}; + +FORCEINLINE flt min(flt a, flt b) { return Impl::Min(a, b); } +FORCEINLINE flt max(flt a, flt b) { return Impl::Max(a, b); } +FORCEINLINE flt abs(flt a) { return Impl::Abs(a); } +FORCEINLINE flt sign(flt a) { return Impl::Sign(a); } +FORCEINLINE flt sqrt(flt a) { return Impl::Sqrt(a); } +FORCEINLINE flt clamp(flt x, flt a, flt b) { return Impl::Clamp(x, a, b); } +FORCEINLINE flt mix(flt a, flt b, flt x) { return Impl::Lerp(a, b, x); } + +FORCEINLINE flt length(vec2 v) { return sqrt(v.x * v.x + v.y * v.y); } +FORCEINLINE flt length(vec3 v) { return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); } + +FORCEINLINE flt dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; } +FORCEINLINE flt dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + +FORCEINLINE vec3 cross(vec3 a, vec3 b) +{ + return + { + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x + }; +} + +FORCEINLINE vec2 clamp(vec2 x, vec2 a, vec2 b) { return { Impl::Clamp(x.x, a.x, b.x), Impl::Clamp(x.y, a.y, b.y) }; } +FORCEINLINE vec3 clamp(vec3 x, vec3 a, vec3 b) { return { Impl::Clamp(x.x, a.x, b.x), Impl::Clamp(x.y, a.y, b.y), Impl::Clamp(x.z, a.z, b.z) }; } + +template +FORCEINLINE flt dot2(T v) { return dot(v, v); } + +#define OP(operator_op, op) \ +FORCEINLINE vec2 operator_op(const vec2& a, const vec2& b) { return { a.x op b.x, a.y op b.y }; } \ +FORCEINLINE vec3 operator_op(const vec3& a, const vec3& b) { return { a.x op b.x, a.y op b.y, a.z op b.z }; } +OP(operator+, +); +OP(operator-, -); +OP(operator*, *); +OP(operator/, /); +#undef OP + +#define OP(name, op) \ +FORCEINLINE vec2 name(const vec2& a, const vec2& b) { return { op(a.x, b.x), op(a.y, b.y) }; } \ +FORCEINLINE vec3 name(const vec3& a, const vec3& b) { return { op(a.x, b.x), op(a.y, b.y), op(a.z, b.z) }; } +OP(min, Impl::Min); +OP(max, Impl::Max); +#undef OP + +#define OP(name, op) \ +FORCEINLINE vec2 name(const vec2& a) { return { op(a.x), op(a.y) }; } \ +FORCEINLINE vec3 name(const vec3& a) { return { op(a.x), op(a.y), op(a.z) }; } +OP(abs, Impl::Abs); +#undef OP + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// Sphere - exact +FORCEINLINE flt sdSphere( vec3 p, flt s ) +{ + return length(p) - s; +} + +// Box - exact +FORCEINLINE flt sdBox(vec3 p, vec3 b) +{ + vec3 q = abs(p) - b; + return length(max(q, flt(0.0))) + min(max(q.x, max(q.y, q.z)), flt(0.0)); +} + +// Round Box - exact +FORCEINLINE flt sdRoundBox(vec3 p, vec3 b, flt r) +{ + vec3 q = abs(p) - b; + return length(max(q, flt(0.0))) + min(max(q.x, max(q.y, q.z)), flt(0.0)) - r; +} + +// Torus - exact +FORCEINLINE flt sdTorus(vec3 p, vec2 t) +{ + vec2 q = vec2(length(p.xz()) - t.x, p.y); + return length(q) - t.y; +} + +// Capped Torus - exact +FORCEINLINE flt sdCappedTorus(vec3 p, vec2 sc, flt ra, flt rb) +{ + p.x = abs(p.x); + flt k = (sc.y * p.x > sc.x* p.y) ? dot(p.xy(), sc) : length(p.xy()); + return sqrt(dot(p, p) + ra * ra - 2.0 * ra * k) - rb; +} + +// Link - exact +FORCEINLINE flt sdLink(vec3 p, flt le, flt r1, flt r2) +{ + vec3 q = vec3(p.x, max(abs(p.y) - le, 0.0), p.z); + return length(vec2(length(q.xy()) - r1, q.z)) - r2; +} + +// Infinite Cylinder - exact +FORCEINLINE flt sdCylinder(vec3 p, vec3 c) +{ + return length(p.xz() - c.xy()) - c.z; +} + +// Cone - exact +FORCEINLINE flt sdCone(vec3 p, vec2 c, flt h) +{ + // c is the sin/cos of the angle, h is height + // Alternatively pass q instead of (c,h), + // which is the point at the base 2D + vec2 q = h * vec2(c.x / c.y, -1.0); + + vec2 w = vec2(length(p.xz()), p.y); + vec2 a = w - q * clamp(dot(w, q) / dot(q, q), 0.0, 1.0); + vec2 b = w - q * vec2(clamp(w.x / q.x, 0.0, 1.0), 1.0); + flt k = sign(q.y); + flt d = min(dot(a, a), dot(b, b)); + flt s = max(k * (w.x * q.y - w.y * q.x), k * (w.y - q.y)); + return sqrt(d) * sign(s); +} + +// Cone - bound(not exact!) +FORCEINLINE flt sdConeFast(vec3 p, vec2 c, flt h) +{ + flt q = length(p.xz()); + return max(dot(c.xy(), vec2(q, p.y)), -h - p.y); +} + +// Infinite Cone - exact +FORCEINLINE flt sdCone(vec3 p, vec2 c) +{ + // c is the sin/cos of the angle + vec2 q = vec2(length(p.xz()), -p.y); + flt d = length(q - c * max(dot(q, c), 0.0)); + return d * ((q.x * c.y - q.y * c.x < 0.0) ? -1.0 : 1.0); +} + +// Plane - exact +FORCEINLINE flt sdPlane(vec3 p, vec3 n) +{ + // n must be normalized + return dot(p, n.xyz()); +} + +// Hexagonal Prism - exact +FORCEINLINE flt sdHexPrism(vec3 p, vec2 h) +{ + const vec3 k = vec3(-0.8660254, 0.5, 0.57735); + p = abs(p); + p.set_xy(p.xy() - 2.0 * min(dot(k.xy(), p.xy()), 0.0) * k.xy()); + vec2 d = vec2( + length(p.xy() - vec2(clamp(p.x, -k.z * h.x, k.z * h.x), h.x)) * sign(p.y - h.x), + p.z - h.y); + return min(max(d.x, d.y), 0.0) + length(max(d, flt(0.0))); +} + +// Triangular Prism - bound +FORCEINLINE flt sdTriPrism(vec3 p, vec2 h) +{ + vec3 q = abs(p); + return max(q.z - h.y, max(q.x * 0.866025 + p.y * 0.5, -p.y) - h.x * 0.5); +} + +// Capsule / Line - exact +FORCEINLINE flt sdCapsule(vec3 p, vec3 a, vec3 b, flt r) +{ + vec3 pa = p - a, ba = b - a; + flt h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba * h) - r; +} + +// Capsule / Line - exact +FORCEINLINE flt sdVerticalCapsule(vec3 p, flt h, flt r) +{ + p.y -= clamp(p.y, 0.0, h); + return length(p) - r; +} + +// Capped Cylinder - exact +FORCEINLINE flt sdCappedCylinder(vec3 p, flt h, flt r) +{ + vec2 d = abs(vec2(length(p.xz()), p.y)) - vec2(h, r); + return min(max(d.x, d.y), flt(0.0)) + length(max(d, flt(0.0))); +} + +// Capped Cylinder - exact +FORCEINLINE flt sdCappedCylinder(vec3 p, vec3 a, vec3 b, flt r) +{ + vec3 ba = b - a; + vec3 pa = p - a; + flt baba = dot(ba, ba); + flt paba = dot(pa, ba); + flt x = length(pa * baba - ba * paba) - r * baba; + flt y = abs(paba - baba * 0.5) - baba * 0.5; + flt x2 = x * x; + flt y2 = y * y * baba; + flt d = (max(x, y) < 0.0) ? -min(x2, y2) : (((x > 0.0) ? x2 : 0.0) + ((y > 0.0) ? y2 : 0.0)); + return sign(d) * sqrt(abs(d)) / baba; +} + +// Rounded Cylinder - exact +FORCEINLINE flt sdRoundedCylinder(vec3 p, flt ra, flt rb, flt h) +{ + vec2 d = vec2(length(p.xz()) - 2.0 * ra + rb, abs(p.y) - h); + return min(max(d.x, d.y), flt(0.0)) + length(max(d, flt(0.0))) - rb; +} + +// Capped Cone - exact +FORCEINLINE flt sdCappedCone(vec3 p, flt h, flt r1, flt r2) +{ + vec2 q = vec2(length(p.xz()), p.y); + vec2 k1 = vec2(r2, h); + vec2 k2 = vec2(r2 - r1, 2.0 * h); + vec2 ca = vec2(q.x - min(q.x, (q.y < 0.0) ? r1 : r2), abs(q.y) - h); + vec2 cb = q - k1 + k2 * clamp(dot(k1 - q, k2) / dot2(k2), 0.0, 1.0); + flt s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0; + return s * sqrt(min(dot2(ca), dot2(cb))); +} + +// Capped Cone - exact +FORCEINLINE flt sdCappedCone(vec3 p, vec3 a, vec3 b, flt ra, flt rb) +{ + flt rba = rb - ra; + flt baba = dot(b - a, b - a); + flt papa = dot(p - a, p - a); + flt paba = dot(p - a, b - a) / baba; + flt x = sqrt(papa - paba * paba * baba); + flt cax = max(0.0, x - ((paba < 0.5) ? ra : rb)); + flt cay = abs(paba - 0.5) - 0.5; + flt k = rba * rba + baba; + flt f = clamp((rba * (x - ra) + paba * baba) / k, 0.0, 1.0); + flt cbx = x - ra - f * rba; + flt cby = paba - f; + flt s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0; + return s * sqrt(min(cax * cax + cay * cay * baba, + cbx * cbx + cby * cby * baba)); +} + +// Solid Angle - exact +FORCEINLINE flt sdSolidAngle(vec3 p, vec2 c, flt ra) +{ + // c is the sin/cos of the angle + vec2 q = vec2(length(p.xz()), p.y); + flt l = length(q) - ra; + flt m = length(q - c * clamp(dot(q, c), 0.0, ra)); + return max(l, m * sign(c.y * q.x - c.x * q.y)); +} + +// Round cone - exact +FORCEINLINE flt sdRoundCone(vec3 p, flt r1, flt r2, flt h) +{ + vec2 q = vec2(length(p.xz()), p.y); + + flt b = (r1 - r2) / h; + flt a = sqrt(1.0 - b * b); + flt k = dot(q, vec2(-b, a)); + + if (k < 0.0) return length(q) - r1; + if (k > a* h) return length(q - vec2(0.0, h)) - r2; + + return dot(q, vec2(a, b)) - r1; +} + +// Round Cone - exact +FORCEINLINE flt sdRoundCone(vec3 p, vec3 a, vec3 b, flt r1, flt r2) +{ + // sampling independent computations (only depend on shape) + vec3 ba = b - a; + flt l2 = dot(ba, ba); + flt rr = r1 - r2; + flt a2 = l2 - rr * rr; + flt il2 = 1.0 / l2; + + // sampling dependant computations + vec3 pa = p - a; + flt y = dot(pa, ba); + flt z = y - l2; + flt x2 = dot2(pa * l2 - ba * y); + flt y2 = y * y * l2; + flt z2 = z * z * l2; + + // single square root! + flt k = sign(rr) * rr * rr * x2; + if (sign(z) * a2 * z2 > k) return sqrt(x2 + z2) * il2 - r2; + if (sign(y) * a2 * y2 < k) return sqrt(x2 + y2) * il2 - r1; + return (sqrt(x2 * a2 * il2) + y * rr) * il2 - r1; +} + +// Ellipsoid - bound(not exact!) +FORCEINLINE flt sdEllipsoid(vec3 p, vec3 r) +{ + flt k0 = length(p / r); + flt k1 = length(p / (r * r)); + return k0 * (k0 - 1.0) / k1; +} + +// Octahedron - exact +FORCEINLINE flt sdOctahedron(vec3 p, flt s) +{ + p = abs(p); + flt m = p.x + p.y + p.z - s; + vec3 q; + if (3.0 * p.x < m) q = p.xyz(); + else if (3.0 * p.y < m) q = p.yzx(); + else if (3.0 * p.z < m) q = p.zxy(); + else return m * 0.57735027; + + flt k = clamp(0.5 * (q.z - q.y + s), 0.0, s); + return length(vec3(q.x, q.y - s + k, q.z - k)); +} + +// Octahedron - bound(not exact) +FORCEINLINE flt sdOctahedronFast(vec3 p, flt s) +{ + p = abs(p); + return (p.x + p.y + p.z - s) * 0.57735027; +} + +// Pyramid - exact +FORCEINLINE flt sdPyramid(vec3 p, flt h) +{ + flt m2 = h * h + 0.25; + + p.set_xz(abs(p.xz())); + p.set_xz((p.z > p.x) ? p.zx() : p.xz()); + p.set_xz(p.xz() - flt(0.5)); + + vec3 q = vec3(p.z, h * p.y - 0.5 * p.x, h * p.x + 0.5 * p.y); + + flt s = max(-q.x, 0.0); + flt t = clamp((q.y - 0.5 * p.z) / (m2 + 0.25), 0.0, 1.0); + + flt a = m2 * (q.x + s) * (q.x + s) + q.y * q.y; + flt b = m2 * (q.x + 0.5 * t) * (q.x + 0.5 * t) + (q.y - m2 * t) * (q.y - m2 * t); + + flt d2 = min(q.y, -q.x * m2 - q.y * 0.5) > 0.0 ? 0.0 : min(a, b); + + return sqrt((d2 + q.z * q.z) / m2) * sign(max(q.z, -p.y)); +} + +// Triangle - exact +FORCEINLINE flt udTriangle(vec3 p, vec3 a, vec3 b, vec3 c) +{ + vec3 ba = b - a; vec3 pa = p - a; + vec3 cb = c - b; vec3 pb = p - b; + vec3 ac = a - c; vec3 pc = p - c; + vec3 nor = cross(ba, ac); + + return sqrt( + (sign(dot(cross(ba, nor), pa)) + + sign(dot(cross(cb, nor), pb)) + + sign(dot(cross(ac, nor), pc)) < 2.0) + ? + min(min( + dot2(ba * clamp(dot(ba, pa) / dot2(ba), 0.0, 1.0) - pa), + dot2(cb * clamp(dot(cb, pb) / dot2(cb), 0.0, 1.0) - pb)), + dot2(ac * clamp(dot(ac, pc) / dot2(ac), 0.0, 1.0) - pc)) + : + dot(nor, pa) * dot(nor, pa) / dot2(nor)); +} + +// Quad - exact +FORCEINLINE flt udQuad(vec3 p, vec3 a, vec3 b, vec3 c, vec3 d) +{ + vec3 ba = b - a; vec3 pa = p - a; + vec3 cb = c - b; vec3 pb = p - b; + vec3 dc = d - c; vec3 pc = p - c; + vec3 ad = a - d; vec3 pd = p - d; + vec3 nor = cross(ba, ad); + + return sqrt( + (sign(dot(cross(ba, nor), pa)) + + sign(dot(cross(cb, nor), pb)) + + sign(dot(cross(dc, nor), pc)) + + sign(dot(cross(ad, nor), pd)) < 3.0) + ? + min(min(min( + dot2(ba * clamp(dot(ba, pa) / dot2(ba), 0.0, 1.0) - pa), + dot2(cb * clamp(dot(cb, pb) / dot2(cb), 0.0, 1.0) - pb)), + dot2(dc * clamp(dot(dc, pc) / dot2(dc), 0.0, 1.0) - pc)), + dot2(ad * clamp(dot(ad, pd) / dot2(ad), 0.0, 1.0) - pd)) + : + dot(nor, pa) * dot(nor, pa) / dot2(nor)); +} + +////////////////////////////////////////////////////////////////////////////// + +FORCEINLINE flt opSmoothUnion(flt d1, flt d2, flt k) +{ + flt h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0); + return mix(d2, d1, h) - k * h * (1.0 - h); +} + +FORCEINLINE flt opSmoothSubtraction(flt d1, flt d2, flt k) { + flt h = clamp(0.5 - 0.5 * (d2 + d1) / k, 0.0, 1.0); + return mix(d2, -d1, h) + k * h * (1.0 - h); +} + +FORCEINLINE flt opSmoothIntersection(flt d1, flt d2, flt k) +{ + flt h = clamp(0.5 - 0.5 * (d2 - d1) / k, 0.0, 1.0); + return mix(d2, d1, h) + k * h * (1.0 - h); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSerializationUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSerializationUtilities.h new file mode 100644 index 00000000..d011e827 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelSerializationUtilities.h @@ -0,0 +1,115 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" + +class FArchive; +class FLargeMemoryWriter; + +namespace FVoxelSerializationVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +}; + +namespace EVoxelCompressionLevel +{ + enum Type : int32 + { + NoCompression = 0, + BestSpeed = 1, + BestCompression = 9, + DefaultCompression = -1, + VoxelDefault = -2 + }; +} + +namespace FVoxelSerializationUtilities +{ + VOXEL_API void SerializeValues(FArchive& Archive, TNoGrowArray& Values, uint32 ValueConfigFlag, FVoxelSerializationVersion::Type VoxelCustomVersion); + VOXEL_API void SerializeMaterials(FArchive& Archive, TNoGrowArray& Materials, uint32 MaterialConfigFlag, FVoxelSerializationVersion::Type VoxelCustomVersion); + + template + void SerializeMaterials(FArchive& Archive, TNoGrowArray>& Materials, uint32 MaterialConfigFlag) + { + VOXEL_ASYNC_FUNCTION_COUNTER(); + + static_assert(sizeof(TVoxelMaterialStorage) / sizeof(T) == TVoxelMaterialStorage::NumChannels, "Serialization below will be broken"); + + if (Archive.IsLoading()) + { + if (MaterialConfigFlag == GVoxelMaterialConfigFlag) + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + Archive.Serialize(Materials.GetData(), MaterialsSize * sizeof(TVoxelMaterialStorage)); + } + else + { + int32 MaterialsSize; + Archive << MaterialsSize; + Materials.Empty(MaterialsSize); + Materials.SetNumUninitialized(MaterialsSize); + for (int32 I = 0; I < MaterialsSize; I++) + { + Materials[I] = TVoxelMaterialStorage::SerializeWithCustomConfig(Archive, MaterialConfigFlag); + } + } + } + else if (Archive.IsSaving()) + { + int32 MaterialsSize = Materials.Num(); + Archive << MaterialsSize; + Archive.Serialize(Materials.GetData(), MaterialsSize * sizeof(TVoxelMaterialStorage)); + } + } + + ////////////////////////////////////////////////////////////////////////////// + + VOXEL_API void CompressData( + const uint8* UncompressedData, + int64 UncompressedDataNum, + TArray& OutCompressedData, + EVoxelCompressionLevel::Type CompressionLevel = EVoxelCompressionLevel::VoxelDefault); + VOXEL_API void CompressData( + FLargeMemoryWriter& UncompressedData, + TArray& CompressedData, + EVoxelCompressionLevel::Type CompressionLevel = EVoxelCompressionLevel::VoxelDefault); + + inline void CompressData( + const TArray& UncompressedData, + TArray& CompressedData, + EVoxelCompressionLevel::Type CompressionLevel = EVoxelCompressionLevel::VoxelDefault) + { + CompressData(UncompressedData.GetData(), UncompressedData.Num(), CompressedData, CompressionLevel); + } + + VOXEL_API bool DecompressData(const TArray& CompressedData, TArray64& UncompressedData); + + VOXEL_API void TestCompression(int64 Size, EVoxelCompressionLevel::Type CompressionLevel); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h new file mode 100644 index 00000000..ab035300 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +namespace FVoxelUtilities +{ +#if CPUPROFILERTRACE_ENABLED + using FCpuProfilerIdTraceType = UE_25_SWITCH(uint16, uint32); + + template + FCpuProfilerIdTraceType GetStatsIdFromStringFormat(TUnique Unique, const T& Format, TArgs... Args) + { + thread_local TMap* IdMap = nullptr; + if (!IdMap) + { + IdMap = new TMap(); + } + const FString Name = FString::Printf(Format, Args...); + FCpuProfilerIdTraceType* Id = IdMap->Find(Name); + if (!Id && ensureMsgf(IdMap->Num() < 1000, TEXT("More than 1000 different stats for %s!"), Format)) + { + Id = &IdMap->Add(Name, FCpuProfilerTrace::OutputEventType(*Name ONLY_UE_24_AND_LOWER(, CpuProfilerGroup_Default))); + } + return *Id; + }; + +#define VOXEL_SCOPE_COUNTER_FORMAT(Format, ...) \ + FCpuProfilerTrace::FEventScope PREPROCESSOR_JOIN(CpuProfilerEventScope, __LINE__)(FVoxelUtilities::GetStatsIdFromStringFormat([](){}, TEXT(Format), ##__VA_ARGS__) ONLY_UE_25_AND_HIGHER(, CpuChannel)); +#else +#define VOXEL_SCOPE_COUNTER_FORMAT(Format, ...) VOXEL_ASYNC_SCOPE_COUNTER(Format) +#endif +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelTextureUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelTextureUtilities.h new file mode 100644 index 00000000..a8ed750b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelTextureUtilities.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UTexture2D; + +namespace FVoxelTextureUtilities +{ + VOXEL_API void UpdateColorTexture(UTexture2D*& Texture, const FIntPoint& Size, const TArray& Colors); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h new file mode 100644 index 00000000..a3e784c8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h @@ -0,0 +1,107 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Async/Async.h" +#include "Engine/World.h" +#include "TimerManager.h" + +namespace FVoxelUtilities +{ + // Call this when you pin a shared ptr on another thread that needs to always be deleted on the game thread + template + void DeleteOnGameThread_AnyThread(TVoxelSharedPtr& Ptr) + { + if (!ensure(!IsInGameThread())) + { + Ptr.Reset(); + return; + } + if (!ensure(Ptr.IsValid())) + { + return; + } + + check(FTaskGraphInterface::IsRunning()); + + // Always start a task to avoid race conditions + AsyncTask(ENamedThreads::GameThread, [Ptr = MoveTemp(Ptr)]() { ensure(Ptr.IsValid()); }); + + check(!Ptr.IsValid()); + } + + template + void DeleteTickable(UWorld* World, TVoxelSharedPtr& Ptr) + { + // There is a bug in 4.23/24 where FTickableGameObject gets added to a set of deleted tickable objects on destruction + // This set is then checked in the next frame before adding a new tickable to see if it has been deleted + // See Engine/Source/Runtime/Engine/Private/Tickable.cpp:107 + // The problem is that when deleting a tickable, there is a large chance than if we create another tickable of the same class + // it'll get assigned the same ptr (as the memory allocator will have a request of the exact same size, so will reuse freshly deleted ptr) + // This set of ptr is only valid one frame. To bypass this bug, we are postponing the tickable deletion for 1s + // Fixed by https://github.com/EpicGames/UnrealEngine/commit/70d70e56f2df9ba6941b91d9893ba6c6e99efc4c + ensure(Ptr.IsValid()); + if (World && ENGINE_MINOR_VERSION < 25) + { + // No world when exiting + FTimerManager& TimerManager = World->GetTimerManager(); + FTimerHandle Handle; + TimerManager.SetTimer( + Handle, + FTimerDelegate::CreateLambda([PtrPtr = MakeVoxelShared>(Ptr)]() { ensure(PtrPtr->IsValid()); PtrPtr->Reset(); }), + 1.f, + false); + ensure(!Ptr.IsUnique()); + } + Ptr.Reset(); + } + + template + void ParallelFor_PerThreadData(int32 Num, TGetPerThreadData GetPerThreadData, TLambda Lambda, bool bForceSingleThread = false) + { + if (Num == 0 || !ensure(Num > 0)) + { + return; + } + + if (bForceSingleThread) + { + auto PerThreadData = GetPerThreadData(); + for (int32 Index = 0; Index < Num; Index++) + { + Lambda(PerThreadData, Index); + } + } + else + { + const int32 NumThreads = FMath::Min(FTaskGraphInterface::Get().GetNumWorkerThreads(), Num); + ensure(NumThreads < 64); // Else bad perf below + + using TData = typename TDecay::Type; + TArray> PerThreadDataArray; + PerThreadDataArray.Reserve(NumThreads); + for (int32 Index = 0; Index < NumThreads; Index++) + { + PerThreadDataArray.Emplace(GetPerThreadData()); + } + + const int32 ChunkSize = Num / NumThreads; + check(ChunkSize >= 1); + + ParallelFor(NumThreads, [&](int32 ThreadIndex) + { + auto& ThreadData = PerThreadDataArray[ThreadIndex]; + + const int32 Start = ThreadIndex * ChunkSize; + const int32 End = FMath::Min((ThreadIndex + 1) * ChunkSize, Num); + for (int32 Index = Start; Index < End; Index++) + { + Lambda(ThreadData, Index); + } + }); + } + + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelVectorUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelVectorUtilities.h new file mode 100644 index 00000000..ac218d74 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelUtilities/VoxelVectorUtilities.h @@ -0,0 +1,127 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelVector.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +namespace FVoxelUtilities +{ + FORCEINLINE int64 FloorToInt64(v_flt Value) + { + return int64(std::floor(Value)); + } + FORCEINLINE int64 FloorToInt32(v_flt Value) + { + const int64 Int = FloorToInt64(Value); + ensure(MIN_int32 <= Int && Int <= MAX_int32); + return int32(Int); + } + + FORCEINLINE int64 CeilToInt64(v_flt Value) + { + return int64(std::ceil(Value)); + } + FORCEINLINE int64 CeilToInt32(v_flt Value) + { + const int64 Int = CeilToInt64(Value); + ensure(MIN_int32 <= Int && Int <= MAX_int32); + return int32(Int); + } + + FORCEINLINE int64 RoundToInt64(v_flt Value) + { +#if PLATFORM_ANDROID + // Android NDK doesn't have std::round :( + return int64(round(Value)); +#else + return int64(std::round(Value)); +#endif + } + FORCEINLINE int64 RoundToInt32(v_flt Value) + { + const int64 Int = RoundToInt64(Value); + ensure(MIN_int32 <= Int && Int <= MAX_int32); + return int32(Int); + } + + FORCEINLINE FIntVector RoundToInt(const FVoxelVector& Vector) + { + return FIntVector( + RoundToInt32(Vector.X), + RoundToInt32(Vector.Y), + RoundToInt32(Vector.Z)); + } + FORCEINLINE FIntVector FloorToInt(const FVoxelVector& Vector) + { + return FIntVector( + FloorToInt32(Vector.X), + FloorToInt32(Vector.Y), + FloorToInt32(Vector.Z)); + } + FORCEINLINE FIntVector CeilToInt(const FVoxelVector& Vector) + { + return FIntVector( + CeilToInt32(Vector.X), + CeilToInt32(Vector.Y), + CeilToInt32(Vector.Z)); + } + + FORCEINLINE FVoxelVector Abs(const FVoxelVector& Vector) + { + return FVoxelVector( + FMath::Abs(Vector.X), + FMath::Abs(Vector.Y), + FMath::Abs(Vector.Z)); + } + + FORCEINLINE FVoxelVector ComponentMax(const FVoxelVector& A, const FVoxelVector& B) + { + return FVoxelVector( + FMath::Max(A.X, B.X), + FMath::Max(A.Y, B.Y), + FMath::Max(A.Z, B.Z)); + } + FORCEINLINE FVoxelVector ComponentMin(const FVoxelVector& A, const FVoxelVector& B) + { + return FVoxelVector( + FMath::Min(A.X, B.X), + FMath::Min(A.Y, B.Y), + FMath::Min(A.Z, B.Z)); + } + + FORCEINLINE FVoxelVector ComponentMin3(const FVoxelVector& A, const FVoxelVector& B, const FVoxelVector& C) + { + return ComponentMin(A, ComponentMin(B, C)); + } + FORCEINLINE FVoxelVector ComponentMax3(const FVoxelVector& A, const FVoxelVector& B, const FVoxelVector& C) + { + return ComponentMax(A, ComponentMax(B, C)); + } + + FORCEINLINE TVoxelStaticArray GetNeighbors(const FVoxelVector& P) + { + const int32 MinX = FloorToInt32(P.X); + const int32 MinY = FloorToInt32(P.Y); + const int32 MinZ = FloorToInt32(P.Z); + + const int32 MaxX = CeilToInt32(P.X); + const int32 MaxY = CeilToInt32(P.Y); + const int32 MaxZ = CeilToInt32(P.Z); + + return + { + FIntVector(MinX, MinY, MinZ), + FIntVector(MaxX, MinY, MinZ), + FIntVector(MinX, MaxY, MinZ), + FIntVector(MaxX, MaxY, MinZ), + FIntVector(MinX, MinY, MaxZ), + FIntVector(MaxX, MinY, MaxZ), + FIntVector(MinX, MaxY, MaxZ), + FIntVector(MaxX, MaxY, MaxZ) + }; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelValue.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelValue.h new file mode 100644 index 00000000..ca18a302 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelValue.h @@ -0,0 +1,258 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +namespace ConstExprUtils +{ + template + static FORCEINLINE constexpr T Clamp(const T X, const T Min, const T Max) + { + return X < Min ? Min : X < Max ? X : Max; + } + static FORCEINLINE constexpr int32 Floor(float F) + { + if (int32(F) == F) + { + return int32(F); + } + if (F < 0) + { + return int32(F) - 1; + } + else + { + return int32(F); + } + } + static FORCEINLINE constexpr int32 RoundToInt(float F) + { + return Floor(F + 0.5f); + } +} + +namespace EVoxelValueConfigFlag +{ + enum Type : uint32 + { + EightBitsValue = 0x01, + SixteenBitsValue = 0x02 + }; +} +constexpr uint32 GVoxelValueConfigFlag = EIGHT_BITS_VOXEL_VALUE ? EVoxelValueConfigFlag::EightBitsValue : EVoxelValueConfigFlag::SixteenBitsValue; + +template +struct TVoxelValueImpl +{ + static constexpr T MAX_VOXELVALUE = TNumericLimits::Max(); + static constexpr T MIN_VOXELVALUE = -TNumericLimits::Max(); + static constexpr T INVALID_VOXELVALUE = TNumericLimits::Min(); + + static_assert(int64(INVALID_VOXELVALUE) == int64(MIN_VOXELVALUE) - 1, ""); +public: + // Full voxel + FORCEINLINE constexpr static TVoxelValueImpl Full() + { + return InternalConstructor(-MAX_VOXELVALUE); + } + // Completely empty voxel + FORCEINLINE constexpr static TVoxelValueImpl Empty() + { + return InternalConstructor(MAX_VOXELVALUE); + } + // Special voxel value, never reached using the normal constructor + FORCEINLINE constexpr static TVoxelValueImpl Special() + { + return InternalConstructor(INVALID_VOXELVALUE); + } + // Precision of FVoxelValue used for comparisons + FORCEINLINE constexpr static TVoxelValueImpl Precision() + { + return InternalConstructor(1); + } + +public: + TVoxelValueImpl() + { + } + + FORCEINLINE explicit constexpr TVoxelValueImpl(EForceInit) + : F(0) + { + } + FORCEINLINE explicit constexpr TVoxelValueImpl(float InValue) + : F(ClampToStorage(ConstExprUtils::RoundToInt(ConstExprUtils::Clamp(InValue, -10.f, 10.f) * MAX_VOXELVALUE))) + { + // Float clamp: to avoid integer overflow. Can use 10.f as ClampToStorage is done on int32 + } + FORCEINLINE explicit constexpr TVoxelValueImpl(double InValue) + : TVoxelValueImpl(float(InValue)) + { + } + +public: + FORCEINLINE constexpr bool IsNull() const { return GetStorage() == 0; } + FORCEINLINE constexpr bool IsEmpty() const { return GetStorage() > 0; } + FORCEINLINE constexpr bool IsTotallyEmpty() const { return GetStorage() == MAX_VOXELVALUE; } + FORCEINLINE constexpr bool IsTotallyFull() const { return GetStorage() == -MAX_VOXELVALUE; } + + FORCEINLINE constexpr int32 Sign() const { return GetStorage() > 0 ? 1 : GetStorage() == 0 ? 0 : -1; } + + FORCEINLINE constexpr float ToFloat() const { return float(GetStorage()) / float(MAX_VOXELVALUE); } + FORCEINLINE constexpr TVoxelValueImpl GetInverse() const { return InternalConstructor(-GetStorage()); } + + FString ToString() const { return FString::SanitizeFloat(ToFloat()); } + +public: + FORCEINLINE constexpr bool operator==(const TVoxelValueImpl& Rhs) const { return GetStorage() == Rhs.GetStorage(); } + FORCEINLINE constexpr bool operator!=(const TVoxelValueImpl& Rhs) const { return GetStorage() != Rhs.GetStorage(); } + FORCEINLINE constexpr bool operator<(const TVoxelValueImpl& Rhs) const { return GetStorage() < Rhs.GetStorage(); } + FORCEINLINE constexpr bool operator>(const TVoxelValueImpl& Rhs) const { return GetStorage() > Rhs.GetStorage(); } + FORCEINLINE constexpr bool operator<=(const TVoxelValueImpl& Rhs) const { return GetStorage() <= Rhs.GetStorage(); } + FORCEINLINE constexpr bool operator>=(const TVoxelValueImpl& Rhs) const { return GetStorage() >= Rhs.GetStorage(); } + + FORCEINLINE constexpr TVoxelValueImpl& operator-=(const TVoxelValueImpl& Rhs) { GetStorage() = ClampToStorage(int32(GetStorage()) - int32(Rhs.GetStorage())); return *this; } + FORCEINLINE constexpr TVoxelValueImpl& operator+=(const TVoxelValueImpl& Rhs) { GetStorage() = ClampToStorage(int32(GetStorage()) + int32(Rhs.GetStorage())); return *this; } + FORCEINLINE constexpr TVoxelValueImpl operator-(const TVoxelValueImpl& Rhs) const { auto Copy = *this; return Copy -= Rhs; } + FORCEINLINE constexpr TVoxelValueImpl operator+(const TVoxelValueImpl& Rhs) const { auto Copy = *this; return Copy += Rhs; } + +public: + FORCEINLINE constexpr T& GetStorage() + { + return F; + } + FORCEINLINE constexpr T GetStorage() const + { + return F; + } + +public: + FORCEINLINE static constexpr TVoxelValueImpl InternalConstructor(T F) + { + TVoxelValueImpl Value(ForceInit); + Value.F = F; + return Value; + } + FORCEINLINE static constexpr int16 ClampToStorage(int32 F) + { + return ConstExprUtils::Clamp(F, -MAX_VOXELVALUE, MAX_VOXELVALUE); + } + +private: + T F; +}; + +using FVoxelValue8 = TVoxelValueImpl; +using FVoxelValue16 = TVoxelValueImpl; + +#if EIGHT_BITS_VOXEL_VALUE +using FVoxelValue = FVoxelValue8; +#else +using FVoxelValue = FVoxelValue16; +#endif + +static_assert(FVoxelValue(0.f).IsNull(), "FVoxelValue error"); +static_assert(!FVoxelValue(0.f).IsEmpty(), "FVoxelValue error"); // 0 is inside +static_assert(FVoxelValue::Empty().IsTotallyEmpty(), "FVoxelValue error"); +static_assert(FVoxelValue::Full().IsTotallyFull(), "FVoxelValue error"); +static_assert(FVoxelValue::Empty().ToFloat() == 1.f, "FVoxelValue error"); +static_assert(FVoxelValue::Full().ToFloat() == -1.f, "FVoxelValue error"); +static_assert(FVoxelValue(1.f) == FVoxelValue::Empty(), "FVoxelValue error"); +static_assert(FVoxelValue(-1.f) == FVoxelValue::Full(), "FVoxelValue error"); +static_assert(FVoxelValue(FVoxelValue::Precision().ToFloat()) == FVoxelValue::Precision(), "FVoxelValue error"); + +template <> +struct TTypeTraits : TTypeTraitsBase +{ + enum { IsBytewiseComparable = true }; +}; + +struct FVoxelValueConverter +{ + template::Value && TIsSame::Value, int>::Type = 0> + FORCEINLINE static constexpr FVoxelValue ConvertValue(T Value) + { + // Need to make FVoxelValue16 a dependent type + typename TEnableIf::Type Result(ForceInit); + // Make sure special cases are handled correctly + // eg FVoxelValue16::Empty() >> 8 == FVoxelValue16::Special() >> 8 + if (Value == FVoxelValue8::Full()) + { + Result = FVoxelValue16::Full(); + } + else if (Value == FVoxelValue8::Empty()) + { + Result = FVoxelValue16::Empty(); + } + else if (Value == FVoxelValue8::Special()) + { + Result = FVoxelValue16::Special(); + } + else + { + Result = FVoxelValue16::InternalConstructor(int16(Value.GetStorage()) << 8); + } + ensureVoxelSlowNoSideEffects(Result.IsEmpty() == Value.IsEmpty()); + return Result; + } + template::Value && TIsSame::Value, int>::Type = 0> + FORCEINLINE static constexpr FVoxelValue ConvertValue(T Value) + { + // Need to make FVoxelValue8 a dependent type + typename TEnableIf::Type Result(ForceInit); + // Make sure special cases are handled correctly + // eg FVoxelValue16::Empty() >> 8 == FVoxelValue16::Special() >> 8 + if (Value == FVoxelValue16::Full()) + { + Result = FVoxelValue8::Full(); + } + else if (Value == FVoxelValue16::Empty()) + { + Result = FVoxelValue8::Empty(); + } + else if (Value == FVoxelValue16::Special()) + { + Result = FVoxelValue8::Special(); + } + else + { + // DivideCeil: so that data between 0 and 255 is mapped to 1 and not 0 which would have a different sign + Result = FVoxelValue8::InternalConstructor(FVoxelUtilities::ClampToINT8(FVoxelUtilities::DivideCeil(Value.GetStorage(), 256))); + } + ensureVoxelSlowNoSideEffects(Result.IsEmpty() == Value.IsEmpty()); + return Result; + } + template::Value, int>::Type = 0> + FORCEINLINE static constexpr FVoxelValue ConvertValue(T Value) + { + return Value; + } + + template::Value, int>::Type = 0> + FORCEINLINE static TArray ConvertValues(TArray&& Values) + { + return MoveTemp(Values); + } + template::Value, int>::Type = 0> + static TArray ConvertValues(TArray&& Values) + { + static_assert(!TIsSame::Value, ""); + TArray Result; + Result.Reserve(Values.Num()); + for (auto& Value : Values) + { + Result.Add(ConvertValue(Value)); + } + return Result; + } +}; + +static_assert(FVoxelValueConverter::ConvertValue(FVoxelValue8::Full()) == FVoxelValueConverter::ConvertValue(FVoxelValue16::Full()), "FVoxelValue error"); +static_assert(FVoxelValueConverter::ConvertValue(FVoxelValue8::Empty()) == FVoxelValueConverter::ConvertValue(FVoxelValue16::Empty()), "FVoxelValue error"); +static_assert(FVoxelValueConverter::ConvertValue(FVoxelValue8::Special()) == FVoxelValueConverter::ConvertValue(FVoxelValue16::Special()), "FVoxelValue error"); +static_assert(FVoxelValueConverter::ConvertValue(FVoxelValue8::InternalConstructor(0)) == FVoxelValueConverter::ConvertValue(FVoxelValue16::InternalConstructor(0)), "FVoxelValue error"); +static_assert(FVoxelValueConverter::ConvertValue(FVoxelValue16::InternalConstructor(+1)).IsEmpty(), "FVoxelValue error"); +static_assert(!FVoxelValueConverter::ConvertValue(FVoxelValue16::InternalConstructor(-1)).IsEmpty(), "FVoxelValue error"); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelVector.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelVector.h new file mode 100644 index 00000000..43f3b54a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelVector.h @@ -0,0 +1,403 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +// We use std functions here as they support both float and doubles +#include + +// Vector that optionally has double precision +struct FVoxelVector +{ + v_flt X; + v_flt Y; + v_flt Z; + + FVoxelVector() = default; + FVoxelVector(v_flt X, v_flt Y, v_flt Z) + : X(X) + , Y(Y) + , Z(Z) + { + } + explicit FVoxelVector(EForceInit) + : X(0.0f) + , Y(0.0f) + , Z(0.0f) + { + } + FVoxelVector(const FVector& Vector) + : X(Vector.X) + , Y(Vector.Y) + , Z(Vector.Z) + { + } + FVoxelVector(const FIntVector& Vector) + : X(Vector.X) + , Y(Vector.Y) + , Z(Vector.Z) + { + } + + /** A zero vector (0,0,0) */ + static VOXEL_API const FVoxelVector ZeroVector; + + /** One vector (1,1,1) */ + static VOXEL_API const FVoxelVector OneVector; + + /** Unreal up vector (0,0,1) */ + static VOXEL_API const FVoxelVector UpVector; + + /** Unreal down vector (0,0,-1) */ + static VOXEL_API const FVoxelVector DownVector; + + /** Unreal forward vector (1,0,0) */ + static VOXEL_API const FVoxelVector ForwardVector; + + /** Unreal backward vector (-1,0,0) */ + static VOXEL_API const FVoxelVector BackwardVector; + + /** Unreal right vector (0,1,0) */ + static VOXEL_API const FVoxelVector RightVector; + + /** Unreal left vector (0,-1,0) */ + static VOXEL_API const FVoxelVector LeftVector; + + FORCEINLINE FIntVector ToInt() const + { + return FIntVector(X, Y, Z); + } + FORCEINLINE FVector ToFloat() const + { + return FVector(X, Y, Z); + } + + FORCEINLINE FString ToString() const + { + return FString::Printf(TEXT("X=%3.3f Y=%3.3f Z=%3.3f"), X, Y, Z); + } + + static FORCEINLINE FIntVector ToInt(const FVoxelVector& V) + { + return V.ToInt(); + } + static FORCEINLINE FVector ToFloat(const FVoxelVector& V) + { + return V.ToFloat(); + } + + FORCEINLINE FVoxelVector operator+(const FVoxelVector& V) const + { + return FVoxelVector(X + V.X, Y + V.Y, Z + V.Z); + } + FORCEINLINE FVoxelVector operator+(const FIntVector& V) const + { + return FVoxelVector(X + V.X, Y + V.Y, Z + V.Z); + } + + FORCEINLINE FVoxelVector operator-(const FVoxelVector& V) const + { + return FVoxelVector(X - V.X, Y - V.Y, Z - V.Z); + } + FORCEINLINE FVoxelVector operator-(const FIntVector& V) const + { + return FVoxelVector(X - V.X, Y - V.Y, Z - V.Z); + } + + FORCEINLINE FVoxelVector operator-(v_flt Bias) const + { + return FVoxelVector(X - Bias, Y - Bias, Z - Bias); + } + + FORCEINLINE FVoxelVector operator+(v_flt Bias) const + { + return FVoxelVector(X + Bias, Y + Bias, Z + Bias); + } + + FORCEINLINE FVoxelVector operator*(v_flt Scale) const + { + return FVoxelVector(X * Scale, Y * Scale, Z * Scale); + } + + FORCEINLINE FVoxelVector operator/(v_flt Scale) const + { + const v_flt RScale = 1.f / Scale; + return FVoxelVector(X * RScale, Y * RScale, Z * RScale); + } + + FORCEINLINE FVoxelVector operator*(const FVoxelVector& V) const + { + return FVoxelVector(X * V.X, Y * V.Y, Z * V.Z); + } + + FORCEINLINE FVoxelVector operator/(const FVoxelVector& V) const + { + return FVoxelVector(X / V.X, Y / V.Y, Z / V.Z); + } + + FORCEINLINE bool operator==(const FVoxelVector& V) const + { + return X == V.X && Y == V.Y && Z == V.Z; + } + + FORCEINLINE bool operator!=(const FVoxelVector& V) const + { + return X != V.X || Y != V.Y || Z != V.Z; + } + + FORCEINLINE bool Equals(const FVoxelVector& V, v_flt Tolerance) const + { + return FMath::Abs(X - V.X) <= Tolerance && FMath::Abs(Y - V.Y) <= Tolerance && FMath::Abs(Z - V.Z) <= Tolerance; + } + + FORCEINLINE bool AllComponentsEqual(v_flt Tolerance) const + { + return FMath::Abs(X - Y) <= Tolerance && FMath::Abs(X - Z) <= Tolerance && FMath::Abs(Y - Z) <= Tolerance; + } + + + FORCEINLINE FVoxelVector operator-() const + { + return FVoxelVector(-X, -Y, -Z); + } + + + FORCEINLINE FVoxelVector operator+=(const FVoxelVector& V) + { + X += V.X; Y += V.Y; Z += V.Z; + return *this; + } + + FORCEINLINE FVoxelVector operator-=(const FVoxelVector& V) + { + X -= V.X; Y -= V.Y; Z -= V.Z; + return *this; + } + + FORCEINLINE FVoxelVector operator*=(v_flt Scale) + { + X *= Scale; Y *= Scale; Z *= Scale; + return *this; + } + + FORCEINLINE FVoxelVector operator/=(v_flt V) + { + const v_flt RV = 1.f / V; + X *= RV; Y *= RV; Z *= RV; + return *this; + } + + FORCEINLINE FVoxelVector operator*=(const FVoxelVector& V) + { + X *= V.X; Y *= V.Y; Z *= V.Z; + return *this; + } + + FORCEINLINE FVoxelVector operator/=(const FVoxelVector& V) + { + X /= V.X; Y /= V.Y; Z /= V.Z; + return *this; + } + + FORCEINLINE v_flt& operator[](int32 Index) + { + check(Index >= 0 && Index < 3); + return (&X)[Index]; + } + + FORCEINLINE v_flt operator[](int32 Index)const + { + check(Index >= 0 && Index < 3); + return (&X)[Index]; + } + + FORCEINLINE v_flt GetMax() const + { + return FMath::Max(FMath::Max(X, Y), Z); + } + + FORCEINLINE v_flt GetAbsMax() const + { + return FMath::Max(FMath::Max(FMath::Abs(X), FMath::Abs(Y)), FMath::Abs(Z)); + } + + FORCEINLINE v_flt GetMin() const + { + return FMath::Min(FMath::Min(X, Y), Z); + } + + FORCEINLINE v_flt GetAbsMin() const + { + return FMath::Min(FMath::Min(FMath::Abs(X), FMath::Abs(Y)), FMath::Abs(Z)); + } + + FORCEINLINE FVoxelVector ComponentMin(const FVoxelVector& Other) const + { + return FVoxelVector(FMath::Min(X, Other.X), FMath::Min(Y, Other.Y), FMath::Min(Z, Other.Z)); + } + + FORCEINLINE FVoxelVector ComponentMax(const FVoxelVector& Other) const + { + return FVoxelVector(FMath::Max(X, Other.X), FMath::Max(Y, Other.Y), FMath::Max(Z, Other.Z)); + } + + FORCEINLINE FVoxelVector GetAbs() const + { + return FVoxelVector(FMath::Abs(X), FMath::Abs(Y), FMath::Abs(Z)); + } + + FORCEINLINE v_flt Size() const + { + return std::sqrt(X * X + Y * Y + Z * Z); + } + + FORCEINLINE v_flt SizeSquared() const + { + return X * X + Y * Y + Z * Z; + } + + FORCEINLINE FVoxelVector GetSafeNormal(v_flt Tolerance = SMALL_NUMBER) const + { + const v_flt SquareSum = X * X + Y * Y + Z * Z; + + // Not sure if it's safe to add tolerance in there. Might introduce too many errors + if (SquareSum == 1.f) + { + return *this; + } + else if (SquareSum < Tolerance) + { + return FVoxelVector(0, 0, 0); + } + + return *this / std::sqrt(SquareSum); + } + + FORCEINLINE bool Normalize(v_flt Tolerance = SMALL_NUMBER) + { + const v_flt SquareSum = X * X + Y * Y + Z * Z; + if (SquareSum > Tolerance) + { + *this /= std::sqrt(SquareSum); + return true; + } + return false; + } + + FORCEINLINE FVoxelVector operator^(const FVoxelVector& V) const + { + return FVoxelVector + ( + Y * V.Z - Z * V.Y, + Z * V.X - X * V.Z, + X * V.Y - Y * V.X + ); + } + + FORCEINLINE float operator|(const FVoxelVector& V) const + { + return X * V.X + Y * V.Y + Z * V.Z; + } + + static FORCEINLINE float DotProduct(const FVoxelVector& A, const FVoxelVector& B) + { + return A | B; + } + + static FORCEINLINE float Dist(const FVoxelVector& V1, const FVoxelVector& V2) + { + return FMath::Sqrt(DistSquared(V1, V2)); + } + static FORCEINLINE float Distance(const FVoxelVector& V1, const FVoxelVector& V2) + { + return Dist(V1, V2); + } + + static FORCEINLINE float DistXY(const FVoxelVector& V1, const FVoxelVector& V2) + { + return FMath::Sqrt(DistSquaredXY(V1, V2)); + } + + static FORCEINLINE float DistSquared(const FVoxelVector& V1, const FVoxelVector& V2) + { + return FMath::Square(V2.X - V1.X) + FMath::Square(V2.Y - V1.Y) + FMath::Square(V2.Z - V1.Z); + } + + static FORCEINLINE float DistSquaredXY(const FVoxelVector& V1, const FVoxelVector& V2) + { + return FMath::Square(V2.X - V1.X) + FMath::Square(V2.Y - V1.Y); + } +}; + +FORCEINLINE FVoxelVector operator*(v_flt Scale, const FVoxelVector& V) +{ + return V.operator*(Scale); +} + +FORCEINLINE FVoxelVector operator-(const FVector& A, const FVoxelVector& B) +{ + return FVoxelVector(A) - B; +} +FORCEINLINE FVoxelVector operator-(const FIntVector& A, const FVoxelVector& B) +{ + return FVoxelVector(A) - B; +} + +FORCEINLINE FVoxelVector operator+(const FVector& A, const FVoxelVector& B) +{ + return FVoxelVector(A) + B; +} +FORCEINLINE FVoxelVector operator+(const FIntVector& A, const FVoxelVector& B) +{ + return FVoxelVector(A) + B; +} + +struct FVoxelVector2D +{ + v_flt X; + v_flt Y; + + FVoxelVector2D() = default; + FVoxelVector2D(v_flt X, v_flt Y) + : X(X) + , Y(Y) + { + } + explicit FVoxelVector2D(EForceInit) + : X(0.0f) + , Y(0.0f) + { + } + FVoxelVector2D(const FVector2D& Vector) + : X(Vector.X) + , Y(Vector.Y) + { + } + FVoxelVector2D(const FIntPoint& Vector) + : X(Vector.X) + , Y(Vector.Y) + { + } + + FORCEINLINE v_flt Size() const + { + return std::sqrt(X * X + Y * Y); + } + + FORCEINLINE v_flt operator^(const FVoxelVector2D& V) const + { + return X * V.Y - Y * V.X; + } + + FORCEINLINE float operator|(const FVoxelVector2D& V) const + { + return X * V.X + Y * V.Y; + } + + static FORCEINLINE float DotProduct(const FVoxelVector2D& A, const FVoxelVector2D& B) + { + return A | B; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorld.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorld.h new file mode 100644 index 00000000..e7319616 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorld.h @@ -0,0 +1,872 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "Templates/SubclassOf.h" +#include "PhysicsEngine/BodyInstance.h" +#include "Components/PrimitiveComponent.h" + +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelWorldInterface.h" +#include "VoxelEditorDelegatesInterface.h" +#include "VoxelRender/VoxelMeshConfig.h" +#include "VoxelRender/VoxelLODMaterials.h" +#include "VoxelWorldCreateInfo.h" +#include "VoxelWorld.generated.h" + +class UVoxelSpawnerConfig; +class UVoxelGeneratorCache; +class UVoxelWorldSaveObject; +class UVoxelWorldRootComponent; +class UVoxelLineBatchComponent; +class UVoxelMultiplayerInterface; +class UVoxelPlaceableItemManager; +class UVoxelMaterialCollectionBase; +class UVoxelProceduralMeshComponent; +class UVoxelPlaceableItemActorHelper; +class IVoxelPool; +class IVoxelRenderer; +class IVoxelLODManager; +class FVoxelData; +class FVoxelDebugManager; +class FVoxelEventManager; +class IVoxelSpawnerManager; +class FVoxelMultiplayerManager; +class FVoxelInstancedMeshManager; +class FVoxelToolRenderingManager; +struct FVoxelLODDynamicSettings; +struct FVoxelUncompressedWorldSave; +struct FVoxelRendererDynamicSettings; +enum class EVoxelTaskType : uint8; + +/** + * Voxel World actor class + */ +UCLASS() +class VOXEL_API AVoxelWorld : public AVoxelWorldInterface, public IVoxelEditorDelegatesInterface +{ + GENERATED_BODY() + +public: + class FGameThreadTasks + { + public: + void AddTask(const TFunction& Task) + { + Tasks.Enqueue(Task); + } + + private: + TQueue, EQueueMode::Mpsc> Tasks; + + void Flush(); + + friend AVoxelWorld; + }; + +public: + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGenerateWorld); + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWorldLoaded); + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWorldDestroyed); + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMaxFoliageInstancesReached); + + // Called when generating the world, right after it's created + // Bind this to add data items, or to do something right after the world is created + UPROPERTY(BlueprintAssignable) + FOnGenerateWorld OnGenerateWorld; + + UPROPERTY(BlueprintAssignable) + FOnWorldLoaded OnWorldLoaded; + + // Called right before destroying the world. Use this if you want to save data + UPROPERTY(BlueprintAssignable) + FOnWorldDestroyed OnWorldDestroyed; + + // Called when max foliage instances is reached + UPROPERTY(BlueprintAssignable) + FOnMaxFoliageInstancesReached OnMaxFoliageInstancesReached; + +protected: + UPROPERTY(Category = "Voxel", VisibleAnywhere, BlueprintReadOnly) + UVoxelWorldRootComponent* WorldRoot; + + UPROPERTY() + UVoxelLineBatchComponent* LineBatchComponent; + +public: + UVoxelWorldRootComponent& GetWorldRoot() const { check(WorldRoot); return *WorldRoot; } + UVoxelLineBatchComponent& GetLineBatchComponent() const { check(LineBatchComponent); return *LineBatchComponent; } + +public: + // Automatically loaded on creation + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Save", meta = (Recreate)) + UVoxelWorldSaveObject* SaveObject = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Save") + FString SaveFilePath; + + // If true, will save the world to SaveFilePath each time it's saved to the save object + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Save") + bool bAutomaticallySaveToFile = false; + + // If true, will add the current time & date to the filepath when saving + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Save") + bool bAppendDateToSavePath = false; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Bake") + bool bRecomputeNormalsBeforeBaking = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Bake") + UStaticMesh* BakedMeshTemplate = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Bake") + TSubclassOf BakedMeshComponentTemplate; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Bake", meta = (RelativeToGameContentDir)) + FFilePath BakedDataPath = { "/Game/VoxelStaticData" }; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Preview", meta = (Recreate, ClampMin = 1)) + int32 NumberOfThreadsForPreview = 2; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Preview", meta = (Recreate)) + bool bEnableFoliageInEditor = true; + + // Turns this off if there's a significant lag when changing material properties + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Preview") + bool bAutomaticallyRefreshMaterials = true; + + // Turns this off if there's a significant lag when changing foliage properties + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Preview") + bool bAutomaticallyRefreshFoliage = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Preview", meta = (DisplayName = "New Scale")) + FVector EditorOnly_NewScale = FVector(2, 2, 2); + + ////////////////////////////////////////////////////////////////////////////// + + // Size of a voxel in cm + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - General", meta = (Recreate, ClampMin = 0.0001)) + float VoxelSize = 100; + + // Generator of this world + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - General", meta = (Recreate)) + FVoxelGeneratorPicker Generator; + + // Will be automatically created if not set + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - General", Instanced, meta = (Recreate)) + UVoxelPlaceableItemManager* PlaceableItemManager = nullptr; + + VOXEL_DEPRECATED(1.2, "Seeds are now regular generator parameters") + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General", meta = (DisplayName = "Seeds (DEPRECATED)")) + TMap Seeds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bCreateWorldAutomatically = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General", meta = (DisplayName = "Use camera if no invokers found")) + bool bUseCameraIfNoInvokersFound = false; + + // Keep all the changes in memory to enable undo/redo. Can be expensive + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General", meta = (Recreate)) + bool bEnableUndoRedo = false; + + // If true, the voxel world will try to stay near its original coordinates when rebasing, and will offset the voxel coordinates instead + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bEnableCustomWorldRebasing = false; + + // If true, will merge asset actors in the scene on create. + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bMergeAssetActors = true; + + // If true, will merge disable edits boxes in the scene on create. + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bMergeDisableEditsBoxes = true; + + // Will hide voxel messages + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bDisableOnScreenMessages = false; + + // Will disable all debug features + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - General") + bool bDisableDebugManager = false; + + ////////////////////////////////////////////////////////////////////////////// + + // WorldSizeInVoxel = RENDER_CHUNK_SIZE * 2^DataOctreeDepth. + UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "Voxel - World Size", meta = (Recreate, ClampMin = 1, ClampMax = 26, UIMin = 1, UIMax = 26)) + int32 RenderOctreeDepth = 10; + + // Size of an edge of the world + UPROPERTY(EditAnywhere, Category = "Voxel - World Size", meta = (Recreate, ClampMin = 1, DisplayName = "World Size (in voxel)")) + uint32 WorldSizeInVoxel = FVoxelUtilities::GetSizeFromDepth(10); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - World Size", meta = (Recreate, InlineEditConditionToggle)) + bool bUseCustomWorldBounds = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - World Size", meta = (Recreate, EditCondition = "bUseCustomWorldBounds")) + FVoxelIntBox CustomWorldBounds; + + ////////////////////////////////////////////////////////////////////////////// + + // Chunks can't have a LOD strictly higher than this. Useful is background has a too low resolution. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - LOD Settings", meta = (UpdateLODs, ClampMin = 0, ClampMax = 25, UIMin = 0, UIMax = 25)) + int32 MaxLOD = FVoxelUtilities::ClampMesherDepth(32); + + // Chunks can't have a LOD strictly lower than this. Useful when in space for instance, combined with a manual BP call to ApplyLODSettings + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - LOD Settings", meta = (UpdateLODs, ClampMin = 0, ClampMax = 25, UIMin = 0, UIMax = 25)) + int32 MinLOD = 0; + + // In world space. If invokers move by less than this distance LODs won't be updated + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - LOD Settings", meta = (UpdateLODs)) + float InvokerDistanceThreshold = 100; + + // Min delay between two LOD updates, in seconds + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - LOD Settings", meta = (RecreateRender, ClampMin = 0), DisplayName = "Min Delay Between LOD Updates") + float MinDelayBetweenLODUpdates = 0.1; + + // If true, the LODs will be updated only once at start + // LODs can still be updated using ForceLODsUpdate or ApplyLODSettings + // For example, can be useful when used with a Max LOD of 0 for worlds that have the highest resolution LOD everywhere + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - LOD Settings", meta = (RecreateRender)) + bool bConstantLOD = false; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials", meta = (Recreate /* also used by generator */)) + EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig::RGB; + + // Only used if Material Config = RGB + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials", meta = (UpdateRenderer)) + UMaterialInterface* VoxelMaterial = nullptr; + + // The material collection to use in Single Index or Double Index material config + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials", meta = (UpdateRenderer)) + UVoxelMaterialCollectionBase* MaterialCollection; + + // Per LOD material overrides + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|LOD", meta = (UpdateRenderer, DisplayName = "LOD Materials")) + TArray LODMaterials; + + // Per LOD material collections overrides + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|LOD", meta = (UpdateRenderer, DisplayName = "LOD Material Collections")) + TArray LODMaterialCollections; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|UVs", meta = (RecreateRender, DisplayName = "UV Config")) + EVoxelUVConfig UVConfig = EVoxelUVConfig::GlobalUVs; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|UVs", meta = (RecreateRender, DisplayName = "UV Scale")) + float UVScale = 1; + + // Normal config, only respected by Marching Cubes + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|Normals", meta = (RecreateRender)) + EVoxelNormalConfig NormalConfig = EVoxelNormalConfig::GradientNormal; + + // Hardness settings for RGB + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|Hardness", meta = (DisplayName = "RGB Hardness")) + EVoxelRGBHardness RGBHardness = EVoxelRGBHardness::FiveWayBlend; + + // Material Index -> Hardness, for Single/Multi index, or RGB if Four/Five Way Blend + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials|Hardness") + TMap MaterialsHardness; + + // If true, then in RGB mode additional vertices will be created to ensure that no colors are ever blended + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + bool bHardColorTransitions = false; + + // Only for Cubic mode. If true, the material index will be 3 x Index + 0 for top, 3 x Index + 1 for sides and 3 x Index + 2 for bottom + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + bool bOneMaterialPerCubeSide = false; + + // These materials won't be rendered nor have any collision + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + TArray HolesMaterials; + + // Apply custom mesh settings per material index + // Will create more mesh components! + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + TMap MaterialsMeshConfigs; + + // Use 16 bits float instead of 32 bits. Halves the UVs memory usage, but lower precision + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + bool bHalfPrecisionCoordinates = false; + + // If true, will interpolate the adjacent voxels colors to find the exact vertex color + // In SingleIndex, will interpolate DataA/B/C + // In MultiIndex, will interpolate Blend and Wetness + // Twice as expensive, as requires to make twice as many material queries! + // Might not look as great if the material outside of the voxel surface isn't set to something nice + // Only works with marching cubes for now + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + bool bInterpolateColors = false; + + // If true, will interpolate the adjacent voxels uvs to find the exact vertex uvs + // Twice as expensive, as requires to make twice as many material queries! + // Only works with marching cubes for now + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender)) + bool bInterpolateUVs = false; + + // When ticked, will convert the color stored in the material (as a 4 bytes color) from sRGB to Linear + // However, since the target will still be 4 bytes, the conversion won't be perfect + // This is a limitation of vertex colors sadly + // NOTE: It is recommended to leave this off, and to tick bLinearColor when painting colors instead + // That way color operations are done in linear space, which is recommended + // NOTE: DO NOT enable in Multi Index, it will just mess up the blend parameters + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Materials", meta = (RecreateRender, DisplayName = "sRGB Colors")) + bool bSRGBColors = false; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Rendering", meta = (RecreateRender)) + EVoxelRenderType RenderType = EVoxelRenderType::MarchingCubes; + + // For marching cubes only + // If 0, will do nothing + // If above zero, will round the vertices positions to the nearest multiple of (1 / RenderSharpness) + // Visually, it will give a more "sharp" look, 1 being the sharpest, 2 3 etc being less and less sharp + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, UIMin = 0, UIMax = 10, ClampMin = 0)) + int32 RenderSharpness = 0; + + // If true, a dynamic instance will be created for each chunk. Else, the material will be used directly + // Disable this if you want to use dynamic material instances as voxel world materials + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bCreateMaterialInstances = true; + + // Whether to dither chunks + // Requires CreateMaterialInstances + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, EditCondition = bCreateMaterialInstances)) + bool bDitherChunks = true; + + // Dithering duration when changing LODs + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, ClampMin = 0, EditCondition = bDitherChunks)) + float ChunksDitheringDuration = 1; + + // When enabled, the component will be rendering into the far shadow cascades (only for directional lights). + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bCastFarShadow = false; + + // Custom procedural mesh class to use + // Use this to override specific rendering settings such as cast shadows, render custom depth... + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + TSubclassOf ProcMeshClass; + + // Chunks with a LOD strictly higher than this won't be rendered + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (UpdateLODs, ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26)) + int32 ChunksCullingLOD = FVoxelUtilities::ClampDepth(32); + + // Whether to render the world, or to just use it for collisions/navmesh + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (Recreate)) + bool bRenderWorld = true; + + // Will destroy any intermediate render data to free up memory + // Does not support any kind of updates + // Note: if MergeChunks is true, chunk meshes memory won't be cleared as it can't know if a new mesh will be added to the cluster + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bStaticWorld = false; + + // If true, the mesh indices will be sorted to improve GPU cache performance. Adds a cost to the async mesh building. If you don't see any perf difference, leave it off + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bOptimizeIndices = false; + + // Will generate distance fields on LOD 0 chunks + // Has a cost of around 1 ms per chunk (on async thread) + // Doesn't work with chunks merging or single/double index material config with different materials per chunk + // Requires UE 4.23+ + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bGenerateDistanceFields = false; + + // Chunks with LOD <= this will have distance fields + // Be careful when increasing because of the memory usage caused by distance fields + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26, EditCondition = "bGenerateDistanceFields")) + int32 MaxDistanceFieldLOD = 4; + + // By how many voxels to extend the chunks distance fields (on every side) + // This is needed so that distance fields nicely overlap + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, ClampMin = 0, ClampMax = 32, UIMin = 0, UIMax = 8, EditCondition = "bGenerateDistanceFields")) + int32 DistanceFieldBoundsExtension = 4; + + // By how much to divide the distance field resolution + // By default it'll be 32x32x32: if the divisor is 2, it'll be 16x16x16, if 4 8x8x8... + // Increasing this decreases quality of the distance field, but saves huge amount of VRAM + // NOTE: increasing this can lead to messy distance fields as some signs are messy + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, ClampMin = 1, ClampMax = 32, UIMin = 1, UIMax = 8, EditCondition = "bGenerateDistanceFields")) + int32 DistanceFieldResolutionDivisor = 1; + + /** Useful for reducing self shadowing from distance field methods when using world position offset to animate the mesh's vertices. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, EditCondition = "bGenerateDistanceFields")) + float DistanceFieldSelfShadowBias = 0.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bEnableTransitions = true; + + // Will merge chunks together to reduce draw calls. + // Only merges chunks of the same LOD! + // Enabling this disables CreateMaterialInstances and DitherChunks. + // When turning this on, it is recommended to reduce the priority of the Mesh Merge category (eg set it to 0) + // Else mesh merge are done before meshing tasks, even if these meshing tasks would have made the merge invalid + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + bool bMergeChunks = false; + + // Size in voxels of the clusters. Scales with LOD (eg if 64, for LOD 3 it will be 64 * 2 * 2 * 2 = 512) + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, EditCondition = "bMergeChunks")) + int32 ChunksClustersSize = 64; + + // If true, additional meshes with the normal chunk size will be spawned only for collisions & navmesh + // Recommended, as cooking collision for merged chunks takes forever + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender, EditCondition = "bMergeChunks")) + bool bDoNotMergeCollisionsAndNavmesh = true; + + // Increases the chunks bounding boxes, useful when using tessellation + // Setting it to 0 can cause issues on flat worlds + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Rendering", meta = (RecreateRender)) + float BoundsExtension = 100; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Spawners", meta = (RecreateSpawners)) + UVoxelSpawnerConfig* SpawnerConfig; + + // The chunk size, in voxels, of a single HISM component + // Lower = higher draw calls/object count + // Higher = more delay when building the occlusion tree + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Spawners", meta = (RecreateSpawners, DisplayName = "HISM Chunk Size")) + int32 HISMChunkSize = 2048; + + // Only nearby instances have collisions + // Configure the distance using this + // In voxels! + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Spawners", meta = (RecreateSpawners, ClampMin = 0)) + int32 SpawnersCollisionDistanceInVoxel = 64; + + // If more instances than this are spawned, they will not be displayed + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Spawners", meta = (RecreateSpawners, ClampMin = 0)) + int64 MaxNumberOfFoliageInstances = MAX_int32; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions", meta = (RecreateRender)) + bool bEnableCollisions = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions", meta = (Recreate, EditCondition = bEnableCollisions, ShowOnlyInnerProperties)) + FBodyInstance CollisionPresets; + + // Whether to compute simple collision meshes or not + // Change this only if you want to use the voxel world as a rigidbody + // Simple collision won't match the geometry exactly + // Using simple collision is not less expensive than using complex collisions, as the convex hulls are far from optimized + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions", meta = (Recreate, EditCondition = bEnableCollisions)) + TEnumAsByte CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple; + + /** + * Determine whether a Character can step up onto this component. + * This controls whether they can try to step up on it when they bump in to it, not whether they can walk on it after landing on it. + * @see FWalkableSlopeOverride + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions", meta = (Recreate, EditCondition = bEnableCollisions)) + TEnumAsByte CanCharacterStepUpOn = ECanBeCharacterBase(1); // ECB_Yes + + /** Should 'Hit' events fire when this object collides during physics simulation. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions|Events", meta = (Recreate, EditCondition = bEnableCollisions, DisplayName = "Simulation Generates Hit Events")) + bool bNotifyRigidBodyCollision = false; + + /** + * If true, this component will generate overlap events when it is overlapping other components (eg Begin Overlap). + * Both components (this and the other) must have this enabled for overlap events to occur. + * + * @see [Overlap Events](https://docs.unrealengine.com/latest/INT/Engine/Physics/Collision/index.html#overlapandgenerateoverlapevents) + * @see UpdateOverlaps(), BeginComponentOverlap(), EndComponentOverlap() + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions|Events", meta = (Recreate, EditCondition = bEnableCollisions)) + bool bGenerateOverlapEvents = false; + + // If false, use only invokers collisions settings + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions|Visible Chunks", meta = (UpdateLODs, EditCondition = bEnableCollisions)) + bool bComputeVisibleChunksCollisions = true; + + // Max LOD to compute collisions on. Inclusive. If not 0 collisions won't be precise. Does not affect invokers + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Collisions|Visible Chunks", meta = (UpdateLODs, ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26, EditCondition = "bComputeVisibleChunksCollisions && bEnableCollisions")) + int32 VisibleChunksCollisionsMaxLOD = 5; + + /** Allows you to override the PhysicalMaterial to use for simple collision on this body. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Collisions", meta = (Recreate, EditCondition = bEnableCollisions)) + UPhysicalMaterial* PhysMaterialOverride = nullptr; + + /** If true Continuous Collision Detection (CCD) will be used for this component */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Collisions", meta = (Recreate, EditCondition = bEnableCollisions)) + bool bUseCCD = false; + + // Number of convex hulls to create per chunk per axis for simple collisions + // More hulls = more precise collisions, but much more expensive physics + // You can check the result in the Player Collision view + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Collisions", meta = (RecreateRender, ClampMin = 1, ClampMax = 32, UIMin = 1, UIMax = 32, EditCondition = bEnableCollisions)) + int32 NumConvexHullsPerAxis = 2; + + // Clean collisions meshes when cooking them. + // Disabling this makes cooking collision slightly faster, but might lead to physx crashing in case of invalid geometry. + // Enable this if you are getting crashes in the physx code + // To check the performance improvements: voxel.LogCollisionCookingTimes 1 + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Collisions", meta = (RecreateRender, EditCondition = bEnableCollisions)) + bool bCleanCollisionMeshes = true; + + ////////////////////////////////////////////////////////////////////////////// + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Navmesh", meta = (RecreateRender)) + bool bEnableNavmesh = false; + + // If false, use only invokers navmesh settings + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Navmesh", meta = (UpdateLODs, EditCondition = bEnableNavmesh)) + bool bComputeVisibleChunksNavmesh = true; + + // Max LOD to compute navmesh on. Inclusive. Does not affect invokers + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Navmesh", meta = (UpdateLODs, ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26, EditCondition = "bEnableNavmesh && bComputeVisibleChunksNavmesh")) + int32 VisibleChunksNavmeshMaxLOD = 0; + + ////////////////////////////////////////////////////////////////////////////// + + // If you have more than one voxel world, set this to false and call CreateGlobalVoxelThreadPool at BeginPlay (for instance in your level blueprint) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Performance") + bool bCreateGlobalPool = true; + + // Number of threads allocated for the voxel background processing. Setting it too high may impact performance + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Performance", meta = (Recreate, ClampMin = 1, EditCondition = "bCreateGlobalPool")) + int32 NumberOfThreads = 2; + + // Async tasks are sorted based on 2 values: + // - first, their priority category + // - then, their own priority (most of the time their distance from voxel invokers) + // Using priority categories, you can determine which tasks to compute first + // Setting 2 task type to the same category will allow to sort them only based on their distance from a voxel invoker + // eg, for foliage and meshing tasks: meshes will spawn at the same time as the foliage on top of them + // If you want to spawn the meshes slightly before foliage, you can offset the tasks own priorities using the PriorityOffsets below + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (Recreate, EditCondition = "bCreateGlobalPool")) + TMap PriorityCategories; + + // Allows to offset tasks own priorities + // Only useful for tasks that have the same priority category! + // Most values are in voxel + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (Recreate, EditCondition = "bCreateGlobalPool")) + TMap PriorityOffsets; + + // If true, won't recompute task priorities once they are queued + // If false, will recompute task priorities with the new voxel invoker positions every PriorityDuration seconds + // True: useful if you have many tasks + // False: useful if you want precise task scheduling, eg if you are moving relatively fast + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (Recreate, EditCondition = "bCreateGlobalPool")) + bool bConstantPriorities = false; + + // Only used if ConstantPriorities is false + // Time, in seconds, during which a task priority is valid and does not need to be recomputed + // Lowering this will increase async cost to recompute priorities, but will lead to more precise scheduling + // Increasing this will decreasing async cost to recompute priorities, but might lead to imprecise scheduling if the invokers are moving fast + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (RecreateRender, ClampMin = 0, EditCondition = "!bConstantPriorities")) + float PriorityDuration = 0.5; + + // Max time in milliseconds to spend on mesh updates per tick + // If this is too low world will generate very slowly + // If this is too high you will get lag spikes + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (RecreateRender, ClampMin = 0.001)) + float MeshUpdatesBudget = 1000; + + // The rate at which events are fired (number of updates per seconds). Used for foliage spawning, foliage collision, binded BP events... + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (RecreateRender, UIMin = 1, UIMax = 60)) + float EventsTickRate = 15; + + // Depth to which to subdivide the data octree on start + // Will create 8^X nodes, so keep low! + // Useful to avoid update tasks locking the entire octree + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Performance", meta = (Recreate, ClampMin = 0)) + int32 DataOctreeInitialSubdivisionDepth = 4; + + ////////////////////////////////////////////////////////////////////////////// + + // Is this world synchronized using the plugin multiplayer system? + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Multiplayer", meta = (Recreate)) + bool bEnableMultiplayer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Multiplayer", meta = (Recreate, EditCondition = "bMultiplayer")) + TSubclassOf MultiplayerInterface; + + // Number of sync per second + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Voxel - Multiplayer", meta = (Recreate, EditCondition = "bMultiplayer")) + float MultiplayerSyncRate = 15; + +public: + AVoxelWorld(); + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void CreateWorld(FVoxelWorldCreateInfo Info); + void CreateWorld() { CreateWorld({}); } + + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void DestroyWorld(); + +public: + IVoxelPool& GetPool() const { return *Pool; } + FVoxelData& GetData() const { return *Data; } + IVoxelLODManager& GetLODManager() const { return *LODManager; } + IVoxelRenderer& GetRenderer() const { return *Renderer; } + FVoxelDebugManager& GetDebugManager() const { return *DebugManager; } + FVoxelEventManager& GetEventManager() const { return *EventManager; } + FVoxelToolRenderingManager& GetToolRenderingManager() const { return *ToolRenderingManager; } + FVoxelMultiplayerManager* GetMultiplayerManager() const { return MultiplayerManager.Get(); } + FVoxelInstancedMeshManager& GetInstancedMeshManager() const { return *InstancedMeshManager; } + IVoxelSpawnerManager& GetSpawnerManager() const { return *SpawnerManager; } + + const UVoxelGeneratorCache& GetGeneratorCache() const { return *GeneratorCache; } + + const TVoxelSharedPtr& GetGameThreadTasks() const { return GameThreadTasks; } + const TVoxelSharedPtr& GetDataSharedPtr() const { return Data; } + const TVoxelSharedPtr& GetLODManagerSharedPtr() const { return LODManager; } + const TVoxelSharedPtr& GetPoolSharedPtr() const { return Pool; } + const TVoxelSharedRef& GetWorldOffsetPtr() const { return WorldOffset; } + const TVoxelSharedRef& GetRendererDynamicSettings() const { return RendererDynamicSettings; } + EVoxelPlayType GetPlayType() const { return PlayType; } + + FVoxelIntBox GetWorldBounds() const; + FIntVector GetWorldOffset() const { return *WorldOffset; } + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void SetGeneratorObject(UVoxelGenerator* NewGenerator); + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void SetGeneratorClass(TSubclassOf NewGeneratorClass); + + // Set the render octree depth + UFUNCTION(BlueprintCallable, Category = "Voxel|World Size") + void SetRenderOctreeDepth(int32 NewDepth); + + UFUNCTION(BlueprintCallable, Category = "Voxel|World Size") + void SetWorldSize(int32 NewWorldSizeInVoxels); + void SetWorldSize(uint32 NewWorldSizeInVoxels); + +public: + // Is this world created? + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + inline bool IsCreated() const { return bIsCreated; } + + // Has the VoxelRenderer finished loading? + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + inline bool IsLoaded() const { return bIsLoaded; } + +public: + /** + * Convert position from world space to voxel space + * @param Position Position in world space + * @param Rounding How to round + * @return Position in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|General|Coordinates", meta = (DisplayName = "World Position to Voxel", AdvancedDisplay = "Rounding")) + virtual FIntVector GlobalToLocal(const FVector& Position, EVoxelWorldCoordinatesRounding Rounding = EVoxelWorldCoordinatesRounding::RoundToNearest) const override final; + + UFUNCTION(BlueprintCallable, Category = "Voxel|General|Coordinates", meta = (DisplayName = "World Position to Voxel Float")) + FVector GlobalToLocalFloatBP(const FVector& Position) const; + virtual FVoxelVector GlobalToLocalFloat(const FVector& Position) const override final; + + /** + * Convert position from voxel space to world space + * @param Position Position in voxel space + * @return Position in world space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|General|Coordinates", meta = (DisplayName = "Voxel Position to World")) + virtual FVector LocalToGlobal(const FIntVector& Position) const override final; + + UFUNCTION(BlueprintCallable, Category = "Voxel|General|Coordinates", meta = (DisplayName = "Voxel Position to World Float")) + FVector LocalToGlobalFloatBP(const FVector& Position) const; + virtual FVector LocalToGlobalFloat(const FVoxelVector& Position) const override final; + + /** + * Get the 8 neighbors in voxel space of GlobalPosition + * @param GlobalPosition The position in world space + * @return The 8 neighbors in voxel space + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|General|Coordinates") + TArray GetNeighboringPositions(const FVector& GlobalPosition) const; + + /** + * Set the voxel world voxel offset + * @param OffsetInVoxels Offset + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void SetOffset(const FIntVector& OffsetInVoxels); + + /** + * Add an offset to the world coordinate system (eg for rebasing) + * @param OffsetInVoxels Offset + * @param bMoveActor If false, the actor will keep its current position + */ + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + void AddOffset(const FIntVector& OffsetInVoxels, bool bMoveActor = true); + + // The generator cache allows to reuse generator objects + // This is required for DataItemActors to allow for smaller update when moving them + UFUNCTION(BlueprintCallable, Category = "Voxel|General", DisplayName = "Get Generator Cache") + UVoxelGeneratorCache* K2_GetGeneratorCache() const { return GeneratorCache; } + + // Used to init generators + UFUNCTION(BlueprintCallable, Category = "Voxel|General") + FVoxelGeneratorInit GetGeneratorInit() const; + + UFUNCTION(BlueprintCallable, Category = "Voxel|Multiplayer") + UVoxelMultiplayerInterface* CreateMultiplayerInterfaceInstance(); + + UFUNCTION(BlueprintCallable, Category = "Voxel|Multiplayer") + UVoxelMultiplayerInterface* GetMultiplayerInterfaceInstance() const; + + // Can be called at runtime + UFUNCTION(BlueprintCallable, Category="Voxel|Collision") + void SetCollisionResponseToChannel(ECollisionChannel Channel, ECollisionResponse NewResponse); + +public: + //~ Begin AActor Interface + virtual void BeginPlay() override; + virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override; + virtual void Tick(float DeltaTime) override; + virtual void ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) override; + virtual void OnConstruction(const FTransform& Transform) override; + virtual void UnregisterAllComponents(bool bForReregister = false) override; +#if WITH_EDITOR + virtual bool ShouldTickIfViewportsOnly() const override; +#endif // WITH_EDITOR + //~ End AActor Interface + + //~ Begin UObject Interface + virtual void BeginDestroy() override; + virtual void Serialize(FArchive& Ar) override; + virtual void PostLoad() override; +#if WITH_EDITOR + virtual void PreEditChange(FProperty* PropertyThatWillChange) override; + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR + //~ End UObject Interface + + void UpdateCollisionProfile(); + +private: +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient) + AActor* VoxelWorldEditor; +#endif + + UPROPERTY(Transient) + mutable UVoxelMultiplayerInterface* MultiplayerInterfaceInstance; + + UPROPERTY(Transient) + UVoxelGeneratorCache* GeneratorCache = nullptr; + + UPROPERTY(Transient) + UVoxelPlaceableItemActorHelper* PlaceableItemActorHelper = nullptr; + + UPROPERTY() + bool bIsToggled = false; + + bool bIsCreated = false; + bool bIsLoaded = false; + EVoxelPlayType PlayType = EVoxelPlayType::Game; + double TimeOfCreation = 0; + +#if WITH_EDITOR + // Temporary variable set in PreEditChange to avoid re-registering proc meshes + bool bDisableComponentUnregister = false; +#endif + +private: + TVoxelSharedPtr DebugManager; + TVoxelSharedPtr Data; + TVoxelSharedPtr Pool; + TVoxelSharedPtr Renderer; + TVoxelSharedPtr LODManager; + TVoxelSharedPtr EventManager; + TVoxelSharedPtr ToolRenderingManager; + TVoxelSharedPtr MultiplayerManager; + TVoxelSharedPtr InstancedMeshManager; + TVoxelSharedPtr SpawnerManager; + + TVoxelSharedRef WorldOffset = MakeVoxelShared(FIntVector::ZeroValue); + TVoxelSharedRef LODDynamicSettings = TVoxelSharedPtr().ToSharedRef(); // else the VTABLE constructor doesn't compile... + TVoxelSharedRef RendererDynamicSettings = TVoxelSharedPtr().ToSharedRef(); + + TVoxelSharedPtr GameThreadTasks; + +private: + void OnWorldLoadedCallback(); + + TVoxelSharedRef CreateDebugManager() const; + TVoxelSharedRef CreateData() const; + TVoxelSharedRef CreatePool() const; + TVoxelSharedRef CreateRenderer() const; + TVoxelSharedRef CreateLODManager() const; + TVoxelSharedPtr CreateEventManager() const; + TVoxelSharedPtr CreateToolRenderingManager() const; + TVoxelSharedRef CreateMultiplayerManager() const; + TVoxelSharedRef CreateInstancedMeshManager() const; + TVoxelSharedRef CreateSpawnerManager() const; + + void CreateWorldInternal(const FVoxelWorldCreateInfo& Info); + void DestroyWorldInternal(); + void DestroyVoxelComponents(); + +public: + void LoadFromSaveObject(); + void ApplyPlaceableItems(); + + void UpdateDynamicLODSettings() const; + void UpdateDynamicRendererSettings() const; + void ApplyCollisionSettingsToRoot() const; + + void RecreateRender(); + void RecreateSpawners(); + void RecreateAll(const FVoxelWorldCreateInfo& Info); + +#if WITH_EDITOR + FSimpleMulticastDelegate OnPropertyChanged; + FSimpleMulticastDelegate OnPropertyChanged_Interactive; + + void Toggle(); + void CreateInEditor(const FVoxelWorldCreateInfo& Info = {}); + void SaveData(); + void LoadFromSaveObjectEditor(); + bool SaveToFile(const FString& Path, FText& Error); + bool LoadFromFile(const FString& Path, FText& Error); + FString GetDefaultFilePath() const; + + //~ Begin IVoxelEditorDelegatesInterface Interface + virtual void OnPreSaveWorld(uint32 SaveFlags, UWorld* World) override; + virtual void OnPreBeginPIE(bool bIsSimulating) override; + virtual void OnEndPIE(bool bIsSimulating) override; + virtual void OnPrepareToCleanseEditorObject(UObject* Object) override; + virtual void OnPreExit() override; + virtual void OnApplyObjectToActor(UObject* Object, AActor* Actor) override; + //~ End IVoxelEditorDelegatesInterface Interface +#endif +}; + +#if WITH_EDITOR +class VOXEL_API IVoxelWorldEditor +{ +public: + virtual ~IVoxelWorldEditor() = default; + + virtual UVoxelWorldSaveObject* CreateSaveObject() = 0; + virtual UClass* GetVoxelWorldEditorClass() = 0; + virtual void RegisterTransaction(AVoxelWorld* VoxelWorld, FName Name) = 0; + +public: + // Sets the voxel world editor implementation.* + static void SetVoxelWorldEditor(TSharedPtr InVoxelWorldEditor); + inline static IVoxelWorldEditor* GetVoxelWorldEditor() { return VoxelWorldEditor.Get(); } + +private: + // Ptr to interface to voxel editor operations. + static TSharedPtr VoxelWorldEditor; +}; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldCreateInfo.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldCreateInfo.h new file mode 100644 index 00000000..12b5a071 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldCreateInfo.h @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelData/VoxelSave.h" +#include "VoxelWorldCreateInfo.generated.h" + +class AVoxelWorld; +class FVoxelData; + +USTRUCT(BlueprintType) +struct FVoxelWorldCreateInfo +{ + GENERATED_BODY() + +public: + // If OverrideSave is true, the world will load SaveOverride instead of the save object + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bOverrideSave = false; + + // If OverrideSave is true, the world will load SaveOverride instead of the save object + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + FVoxelUncompressedWorldSave SaveOverride; + +public: + // If bOverrideData is true, will use DataSource data instead of creating a new data + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bOverrideData = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + AVoxelWorld* DataOverride = nullptr; + + TVoxelSharedPtr DataOverride_Raw; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldInterface.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldInterface.h new file mode 100644 index 00000000..8c6bb3aa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldInterface.h @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelVector.h" +#include "GameFramework/Actor.h" +#include "VoxelWorldInterface.generated.h" + +UENUM(BlueprintType) +enum class EVoxelWorldCoordinatesRounding : uint8 +{ + RoundToNearest, + RoundUp, + RoundDown +}; + +#if CPP +class IVoxelWorldInterface +{ +public: + virtual ~IVoxelWorldInterface() = default; + + virtual FIntVector GlobalToLocal(const FVector& Position, EVoxelWorldCoordinatesRounding Rounding = EVoxelWorldCoordinatesRounding::RoundToNearest) const { unimplemented(); return {}; } + virtual FVoxelVector GlobalToLocalFloat(const FVector& Position) const { unimplemented(); return {}; } + + virtual FVector LocalToGlobal(const FIntVector& Position) const { unimplemented(); return {}; } + virtual FVector LocalToGlobalFloat(const FVoxelVector& Position) const { unimplemented(); return {}; } +}; +#endif + +// AActor so we can keep a weak ptr to it +UCLASS(Abstract) +class VOXEL_API AVoxelWorldInterface + : public AActor +#if CPP + , public IVoxelWorldInterface +#endif +{ + GENERATED_BODY() +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldRootComponent.h b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldRootComponent.h new file mode 100644 index 00000000..2181f092 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Public/VoxelWorldRootComponent.h @@ -0,0 +1,63 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "PhysicsEngine/BodySetup.h" // Can't forward decl anything with uobjects generated constructors... +#include "Components/PrimitiveComponent.h" +#include "VoxelWorldRootComponent.generated.h" + +UCLASS(editinlinenew) +class VOXEL_API UVoxelWorldRootComponent : public UPrimitiveComponent +{ + GENERATED_BODY() + +public: + UVoxelWorldRootComponent(); + ~UVoxelWorldRootComponent(); + + ECollisionTraceFlag CollisionTraceFlag = {}; + + //~ Begin UPrimitiveComponent Interface + virtual UBodySetup* GetBodySetup() override final; + virtual FPrimitiveSceneProxy* CreateSceneProxy() override final; + virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; + //~ End UPrimitiveComponent Interface + + // Only need to tick when created + void TickWorldRoot(); + +public: +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + void UpdateConvexCollision(uint64 Id, const FBox& Bounds, TArray&& ConvexElements, TArray&& ConvexMeshes); + void SetCookedTriMeshes(const TArray& TriMeshes); +#endif + +private: + UPROPERTY(Transient) + UBodySetup* BodySetup; + + FBoxSphereBounds LocalBounds; + + // For debug draw + FCriticalSection BodySetupLock; + +#if WITH_PHYSX && PHYSICS_INTERFACE_PHYSX + struct FConvexElements + { + const FBox Bounds; + const TArray ConvexElements; + const TArray ConvexMeshes; + + FConvexElements(const FBox& InBounds, TArray&& InConvexElements, TArray&& InConvexMeshes); + ~FConvexElements(); + }; + TMap> Elements; + + bool bRebuildQueued = false; + + void RebuildConvexCollision(); +#endif + + friend class FVoxelRenderSimpleCollisionSceneProxy; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Voxel.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Voxel.Build.cs new file mode 100644 index 00000000..59996702 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/Voxel/Voxel.Build.cs @@ -0,0 +1,79 @@ +// Copyright 2020 Phyronnaz + +#define VOXEL_PLUGIN_PRO + +using System.IO; +using UnrealBuildTool; + +public class Voxel : ModuleRules +{ + public Voxel(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + // For raytracing + PrivateIncludePaths.Add(EngineDirectory + "/Shaders/Shared"); + // For HLSL translator + PrivateIncludePaths.Add(EngineDirectory + "/Source/Runtime/Engine/Private"); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "Networking", + "Sockets", + "RHI", +#if UE_4_23_OR_LATER + "PhysicsCore", +#endif + "RenderCore", + "Landscape", + "PhysX", +#if UE_4_26_OR_LATER + "DeveloperSettings", + "TraceLog", +#endif + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "nvTessLib", + "Embree3", + "HTTP", + "Projects", + "Slate", + "SlateCore", + //"VHACD", // Not used, too slow + } + ); + + SetupModulePhysicsSupport(Target); + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + PrivateDependencyModuleNames.Add("ForsythTriOptimizer"); + } + PrivateDependencyModuleNames.Add("zlib"); + + if (Target.Configuration == UnrealTargetConfiguration.DebugGame || + Target.Configuration == UnrealTargetConfiguration.Debug) + { + PublicDefinitions.Add("VOXEL_DEBUG=1"); + } + + PublicDefinitions.Add("VOXEL_PLUGIN_NAME=TEXT(\"VoxelPro\")"); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelBase.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelBase.h new file mode 100644 index 00000000..a9f44b7f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelBase.h @@ -0,0 +1,22 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "AssetTypeActions_Base.h" + +class FAssetTypeActions_VoxelBase : public FAssetTypeActions_Base +{ +public: + FAssetTypeActions_VoxelBase(EAssetTypeCategories::Type AssetCategory) + : AssetCategory(AssetCategory) + { + } + + virtual uint32 GetCategories() override { return AssetCategory; } + +private: + EAssetTypeCategories::Type AssetCategory; +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.cpp new file mode 100644 index 00000000..c8e47b65 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.cpp @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#include "AssetTypeActions_VoxelDataAsset.h" +#include "VoxelEditorModule.h" +#include "VoxelMessages.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Commands/UIAction.h" +#include "EditorStyleSet.h" +#include "EditorReimportHandler.h" + +void FAssetTypeActions_VoxelDataAsset::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; + + for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) + { + auto* DataAsset = Cast(*ObjIt); + if (DataAsset) + { + IVoxelEditorModule* VoxelEditorModule = &FModuleManager::LoadModuleChecked("VoxelEditor"); + VoxelEditorModule->CreateVoxelDataAssetEditor(Mode, EditWithinLevelEditor, DataAsset); + } + } +} + +void FAssetTypeActions_VoxelDataAsset::GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) +{ + const auto Assets = GetTypedWeakObjectPtrs(InObjects); + + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Reimport"), + VOXEL_LOCTEXT("Reimport the selected asset(s)."), + FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.ReimportAsset"), + FUIAction( + FExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelDataAsset::ExecuteReimport, Assets), + FCanExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelDataAsset::CanExecuteReimport, Assets) + ) + ); +} + +bool FAssetTypeActions_VoxelDataAsset::CanExecuteReimport(const TArray> Objects) const +{ + for (auto& Object : Objects) + { + if (Object.IsValid() && (Object->Source == EVoxelDataAssetImportSource::MagicaVox || Object->Source == EVoxelDataAssetImportSource::RawVox)) + { + return true; + } + } + return false; +} + +void FAssetTypeActions_VoxelDataAsset::ExecuteReimport(const TArray> Objects) const +{ + for (auto& Object : Objects) + { + if (Object.IsValid() && (Object->Source == EVoxelDataAssetImportSource::MagicaVox || Object->Source == EVoxelDataAssetImportSource::RawVox)) + { + FReimportManager::Instance()->Reimport(Object.Get(), /*bAskForNewFileIfMissing=*/true); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.h new file mode 100644 index 00000000..e2741bb8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelAssets/VoxelDataAsset.h" + +class FAssetTypeActions_VoxelDataAsset : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Data Asset"); } + virtual FColor GetTypeColor() const override { return FColor(128, 0, 64); } + virtual UClass* GetSupportedClass() const override { return UVoxelDataAsset::StaticClass(); } + virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) override; + virtual bool HasActions(const TArray& InObjects) const override { return true; } + virtual void GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) override; + +private: + /** Can we execute a reimport for the selected objects? */ + bool CanExecuteReimport(const TArray> Objects) const; + + /** Handler for when Reimport is selected */ + void ExecuteReimport(const TArray> Objects) const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h new file mode 100644 index 00000000..535c3a48 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/IToolkitHost.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelGraphDataItemConfig.h" + +class FAssetTypeActions_VoxelGraphDataItemConfig : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Graph Data Item Config"); } + virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); } + virtual UClass* GetSupportedClass() const override { return UVoxelGraphDataItemConfig::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.cpp new file mode 100644 index 00000000..f4b4d8cb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#include "AssetTypeActions_VoxelGraphGenerator.h" +#include "Misc/PackageName.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphEditorModule.h" + +UClass* FAssetTypeActions_VoxelGraphGenerator::GetSupportedClass() const +{ + return UVoxelGraphGenerator::StaticClass(); +} + +void FAssetTypeActions_VoxelGraphGenerator::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; + + for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) + { + auto* VoxelGraphGenerator = Cast(*ObjIt); + if (VoxelGraphGenerator) + { + IVoxelGraphEditorModule& VoxelGraphEditorModule = FModuleManager::LoadModuleChecked("VoxelGraphEditor"); + VoxelGraphEditorModule.CreateVoxelGraphEditor(Mode, EditWithinLevelEditor, VoxelGraphGenerator); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.h new file mode 100644 index 00000000..3a306f0d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/IToolkitHost.h" +#include "AssetTypeActions_VoxelBase.h" + +class FMenuBuilder; +class UVoxelGraphGenerator; + +class FAssetTypeActions_VoxelGraphGenerator : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Graph"); } + virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); } + virtual UClass* GetSupportedClass() const override; + virtual void OpenAssetEditor( const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr() ) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.cpp new file mode 100644 index 00000000..cf10b3cb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#include "AssetTypeActions_VoxelGraphMacro.h" +#include "Misc/PackageName.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelGraphEditorModule.h" + +UClass* FAssetTypeActions_VoxelGraphMacro::GetSupportedClass() const +{ + return UVoxelGraphMacro::StaticClass(); +} + +void FAssetTypeActions_VoxelGraphMacro::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) +{ + const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; + + for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) + { + auto* VoxelGraphMacro = Cast(*ObjIt); + if (VoxelGraphMacro) + { + IVoxelGraphEditorModule& VoxelGraphEditorModule = FModuleManager::LoadModuleChecked("VoxelGraphEditor"); + VoxelGraphEditorModule.CreateVoxelGraphEditor(Mode, EditWithinLevelEditor, VoxelGraphMacro); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.h new file mode 100644 index 00000000..0b77fcb5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/IToolkitHost.h" +#include "AssetTypeActions_VoxelBase.h" + +class FMenuBuilder; + +class FAssetTypeActions_VoxelGraphMacro : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Graph Macro"); } + virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); } + virtual UClass* GetSupportedClass() const override; + virtual void OpenAssetEditor( const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr() ) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h new file mode 100644 index 00000000..ce241156 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/IToolkitHost.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelGraphOutputsConfig.h" + +class FAssetTypeActions_VoxelGraphOutputsConfig : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Graph Outputs Config"); } + virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); } + virtual UClass* GetSupportedClass() const override { return UVoxelGraphOutputsConfig::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.cpp new file mode 100644 index 00000000..46cba132 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.cpp @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#include "AssetTools/AssetTypeActions_VoxelHeightmapAsset.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Commands/UIAction.h" +#include "EditorStyleSet.h" +#include "EditorReimportHandler.h" + +void FAssetTypeActions_VoxelHeightmapAsset::GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) +{ + const auto Heightmaps = GetTypedWeakObjectPtrs(InObjects); + + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Reimport"), + VOXEL_LOCTEXT("Reimport the selected heightmaps(s)."), + FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.ReimportAsset"), + FUIAction( + FExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelHeightmapAsset::ExecuteReimport, Heightmaps), + FCanExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelHeightmapAsset::CanExecuteReimport, Heightmaps) + ) + ); +} + +bool FAssetTypeActions_VoxelHeightmapAsset::CanExecuteReimport(const TArray> Objects) const +{ + for (auto& Object : Objects) + { + if (Object.IsValid() && Object->IsA()) + { + return true; + } + } + return false; +} + +void FAssetTypeActions_VoxelHeightmapAsset::ExecuteReimport(const TArray> Objects) const +{ + for (auto& Object : Objects) + { + if (Object.IsValid() && Object->IsA()) + { + FReimportManager::Instance()->Reimport(Object.Get(), /*bAskForNewFileIfMissing=*/true); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.h new file mode 100644 index 00000000..80c851e3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" + +class FAssetTypeActions_VoxelHeightmapAsset : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Heightmap Asset"); } + virtual FColor GetTypeColor() const override { return FColor(200, 80, 80); } + virtual UClass* GetSupportedClass() const override { return UVoxelHeightmapAsset::StaticClass(); } + virtual bool HasActions(const TArray& InObjects) const override { return true; } + virtual void GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) override; + +private: + /** Can we execute a reimport for the selected objects? */ + bool CanExecuteReimport(const TArray> Objects) const; + + /** Handler for when Reimport is selected */ + void ExecuteReimport(const TArray> Objects) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelMaterialCollection.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelMaterialCollection.h new file mode 100644 index 00000000..5e544cdb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelMaterialCollection.h @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h" +#include "VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h" +#include "VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h" + +class FAssetTypeActions_VoxelBasicMaterialCollection : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Basic Material Collection"); } + virtual FColor GetTypeColor() const override { return FColor(0, 192, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelBasicMaterialCollection::StaticClass(); } +}; + +class FAssetTypeActions_VoxelInstancedMaterialCollectionTemplates : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Instanced Material Collection Templates"); } + virtual FColor GetTypeColor() const override { return FColor(0, 192, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelInstancedMaterialCollectionTemplates::StaticClass(); } +}; + +class FAssetTypeActions_VoxelInstancedMaterialCollection : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Instanced Material Collection"); } + virtual FColor GetTypeColor() const override { return FColor(0, 192, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelInstancedMaterialCollection::StaticClass(); } +}; + +class FAssetTypeActions_VoxelInstancedMaterialCollectionInstance : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Instanced Material Collection Instance"); } + virtual FColor GetTypeColor() const override { return FColor(0, 192, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelInstancedMaterialCollectionInstance::StaticClass(); } +}; + +class FAssetTypeActions_VoxelLandscapeMaterialCollection : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Landscape Material Collection"); } + virtual FColor GetTypeColor() const override { return FColor(0, 192, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelLandscapeMaterialCollection::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawnerConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawnerConfig.h new file mode 100644 index 00000000..1547e880 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawnerConfig.h @@ -0,0 +1,17 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" + +class FAssetTypeActions_VoxelSpawnerConfig : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Spawner Config"); } + virtual FColor GetTypeColor() const override { return FColor(128, 255, 128); } + virtual UClass* GetSupportedClass() const override { return UVoxelSpawnerConfig::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawners.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawners.h new file mode 100644 index 00000000..6e0ec6f7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawners.h @@ -0,0 +1,53 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" + +class FAssetTypeActions_VoxelSpawnerBase : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FColor GetTypeColor() const override { return FColor(128, 255, 128); } +}; + +class FAssetTypeActions_VoxelMeshSpawner : public FAssetTypeActions_VoxelSpawnerBase +{ +public: + using FAssetTypeActions_VoxelSpawnerBase::FAssetTypeActions_VoxelSpawnerBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Mesh Spawner"); } + virtual UClass* GetSupportedClass() const override { return UVoxelMeshSpawner::StaticClass(); } +}; + +class FAssetTypeActions_VoxelMeshSpawnerGroup : public FAssetTypeActions_VoxelSpawnerBase +{ +public: + using FAssetTypeActions_VoxelSpawnerBase::FAssetTypeActions_VoxelSpawnerBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Mesh Spawner Group"); } + virtual UClass* GetSupportedClass() const override { return UVoxelMeshSpawnerGroup::StaticClass(); } +}; + +class FAssetTypeActions_VoxelAssetSpawner : public FAssetTypeActions_VoxelSpawnerBase +{ +public: + using FAssetTypeActions_VoxelSpawnerBase::FAssetTypeActions_VoxelSpawnerBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Asset Spawner"); } + virtual UClass* GetSupportedClass() const override { return UVoxelAssetSpawner::StaticClass(); } +}; + +class FAssetTypeActions_VoxelSpawnerGroup : public FAssetTypeActions_VoxelSpawnerBase +{ +public: + using FAssetTypeActions_VoxelSpawnerBase::FAssetTypeActions_VoxelSpawnerBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel Spawner Group"); } + virtual UClass* GetSupportedClass() const override { return UVoxelSpawnerGroup::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelWorldSaveObject.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelWorldSaveObject.h new file mode 100644 index 00000000..ac691bc8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelWorldSaveObject.h @@ -0,0 +1,17 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_VoxelBase.h" +#include "VoxelData/VoxelSave.h" + +class FAssetTypeActions_VoxelWorldSaveObject : public FAssetTypeActions_VoxelBase +{ +public: + using FAssetTypeActions_VoxelBase::FAssetTypeActions_VoxelBase; + + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel World Save Object"); } + virtual FColor GetTypeColor() const override { return FColor(255, 140, 0); } + virtual UClass* GetSupportedClass() const override { return UVoxelWorldSaveObject::StaticClass(); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/IVoxelDataAssetEditor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/IVoxelDataAssetEditor.h new file mode 100644 index 00000000..776b2daf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/IVoxelDataAssetEditor.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/AssetEditorToolkit.h" + +class FAdvancedPreviewScene; +class FVoxelEditorToolsPanel; +class AVoxelWorld; +class UVoxelDataAsset; + +class IVoxelDataAssetEditor : public FAssetEditorToolkit +{ +public: + virtual FAdvancedPreviewScene& GetPreviewScene() const = 0; + virtual AVoxelWorld& GetVoxelWorld() const = 0; + virtual UVoxelDataAsset& GetDataAsset() const = 0; + virtual FVoxelEditorToolsPanel& GetPanel() const = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.cpp new file mode 100644 index 00000000..d711bd0c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.cpp @@ -0,0 +1,155 @@ +// Copyright 2020 Phyronnaz + +#include "DataAssetEditor/SVoxelDataAssetEditorViewport.h" +#include "DataAssetEditor/VoxelDataAssetEditorToolkit.h" +#include "DataAssetEditor/VoxelDataAssetEditorViewportClient.h" + +#include "AssetViewerSettings.h" +#include "SEditorViewport.h" +#include "AdvancedPreviewScene.h" +#include "EditorViewportClient.h" +#include "Slate/SceneViewport.h" +#include "Widgets/Docking/SDockTab.h" +#include "VoxelDataAssetEditorCommands.h" + +void SVoxelDataAssetEditorViewportToolBar::Construct(const FArguments& InArgs, TSharedPtr InViewport) +{ + SCommonEditorViewportToolbarBase::Construct(SCommonEditorViewportToolbarBase::FArguments(), InViewport); +} + +TSharedRef SVoxelDataAssetEditorViewportToolBar::GenerateShowMenu() const +{ + GetInfoProvider().OnFloatingButtonClicked(); + + const TSharedRef ViewportRef = GetInfoProvider().GetViewportWidget(); + + const bool bInShouldCloseWindowAfterMenuSelection = true; + FMenuBuilder ShowMenuBuilder(bInShouldCloseWindowAfterMenuSelection, ViewportRef->GetCommandList()); + { + auto& Commands = FVoxelDataAssetEditorCommands::Get(); + + ShowMenuBuilder.AddMenuEntry(Commands.TogglePreviewGrid); + ShowMenuBuilder.AddMenuEntry(Commands.TogglePreviewBackground); + } + + return ShowMenuBuilder.MakeWidget(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SVoxelDataAssetEditorViewport::Construct(const FArguments& InArgs) +{ + DataAssetEditor = InArgs._Editor; + check(DataAssetEditor); + AdvancedPreviewScene = &DataAssetEditor->GetPreviewScene(); + + SEditorViewport::Construct( SEditorViewport::FArguments() ); +} + +SVoxelDataAssetEditorViewport::~SVoxelDataAssetEditorViewport() +{ + UAssetViewerSettings::Get()->OnAssetViewerSettingsChanged().RemoveAll(this); + + if (EditorViewportClient.IsValid()) + { + EditorViewportClient->Viewport = nullptr; + } +} + +void SVoxelDataAssetEditorViewport::RefreshViewport() +{ + SceneViewport->InvalidateDisplay(); + + if (EditorViewportClient.IsValid()) + { + UAssetViewerSettings* Settings = UAssetViewerSettings::Get(); + const int32 ProfileIndex = AdvancedPreviewScene->GetCurrentProfileIndex(); + if (Settings->Profiles.IsValidIndex(ProfileIndex)) + { + AdvancedPreviewScene->UpdateScene(Settings->Profiles[ProfileIndex]); + } + } +} + +bool SVoxelDataAssetEditorViewport::IsVisible() const +{ + return ViewportWidget.IsValid() && (!ParentTab.IsValid() || ParentTab.Pin()->IsForeground()) && SEditorViewport::IsVisible(); +} + +void SVoxelDataAssetEditorViewport::BindCommands() +{ + SEditorViewport::BindCommands(); + + const FVoxelDataAssetEditorCommands& Commands = FVoxelDataAssetEditorCommands::Get(); + + CommandList->Append(DataAssetEditor->GetToolkitCommands()); + + CommandList->MapAction( + Commands.TogglePreviewGrid, + FExecuteAction::CreateSP(EditorViewportClient.ToSharedRef(), &FVoxelDataAssetEditorViewportClient::ToggleShowGrid), + FCanExecuteAction(), + FIsActionChecked::CreateSP(EditorViewportClient.ToSharedRef(), &FVoxelDataAssetEditorViewportClient::IsShowGridToggled)); + + CommandList->MapAction( + Commands.TogglePreviewBackground, + FExecuteAction::CreateSP(this, &SVoxelDataAssetEditorViewport::TogglePreviewBackground), + FCanExecuteAction(), + FIsActionChecked::CreateSP(this, &SVoxelDataAssetEditorViewport::IsPreviewBackgroundToggled)); +} + +void SVoxelDataAssetEditorViewport::TogglePreviewBackground() +{ + UAssetViewerSettings* Settings = UAssetViewerSettings::Get(); + const int32 ProfileIndex = AdvancedPreviewScene->GetCurrentProfileIndex(); + if (Settings->Profiles.IsValidIndex(ProfileIndex)) + { + AdvancedPreviewScene->SetEnvironmentVisibility(!Settings->Profiles[ProfileIndex].bShowEnvironment); + } + RefreshViewport(); +} + +bool SVoxelDataAssetEditorViewport::IsPreviewBackgroundToggled() const +{ + UAssetViewerSettings* Settings = UAssetViewerSettings::Get(); + const int32 ProfileIndex = AdvancedPreviewScene->GetCurrentProfileIndex(); + if (Settings->Profiles.IsValidIndex(ProfileIndex)) + { + return Settings->Profiles[ProfileIndex].bShowEnvironment; + } + return false; +} + +TSharedRef SVoxelDataAssetEditorViewport::GetViewportWidget() +{ + return SharedThis(this); +} + +TSharedPtr SVoxelDataAssetEditorViewport::GetExtenders() const +{ + return MakeShared(); +} + +TSharedRef SVoxelDataAssetEditorViewport::MakeEditorViewportClient() +{ + EditorViewportClient = FVoxelDataAssetEditorViewportClient::Create( + DataAssetEditor->GetVoxelWorld(), + DataAssetEditor->GetDataAsset(), + DataAssetEditor->GetPanel(), + *AdvancedPreviewScene, + *this); + + EditorViewportClient->VisibilityDelegate.BindSP(this, &SVoxelDataAssetEditorViewport::IsVisible); + + return EditorViewportClient.ToSharedRef(); +} + +void SVoxelDataAssetEditorViewport::PopulateViewportOverlays(TSharedRef Overlay) +{ + Overlay->AddSlot() + .VAlign(VAlign_Top) + [ + SNew(SVoxelDataAssetEditorViewportToolBar, SharedThis(this)) + ]; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.h new file mode 100644 index 00000000..51a6932f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.h @@ -0,0 +1,66 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "SCommonEditorViewportToolbarBase.h" +#include "SEditorViewport.h" + +class FVoxelDataAssetEditorViewportClient; +class IVoxelDataAssetEditor; +class SDockTab; +class FAdvancedPreviewScene; + +class SVoxelDataAssetEditorViewportToolBar : public SCommonEditorViewportToolbarBase +{ +public: + SLATE_BEGIN_ARGS(SVoxelDataAssetEditorViewportToolBar) {} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, TSharedPtr InViewport); + + //~ Begin SCommonEditorViewportToolbarBase interface + virtual TSharedRef GenerateShowMenu() const override; + //~ End SCommonEditorViewportToolbarBase interface +}; + +class SVoxelDataAssetEditorViewport : public SEditorViewport, public ICommonEditorViewportToolbarInfoProvider +{ +public: + SLATE_BEGIN_ARGS(SVoxelDataAssetEditorViewport){} + SLATE_ARGUMENT(IVoxelDataAssetEditor*, Editor) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + ~SVoxelDataAssetEditorViewport(); + + void RefreshViewport(); + + /** Event handlers */ + void TogglePreviewBackground(); + bool IsPreviewBackgroundToggled() const; + + //~ Begin ICommonEditorViewportToolbarInfoProvider interface + virtual TSharedRef GetViewportWidget() override; + virtual TSharedPtr GetExtenders() const override; + virtual void OnFloatingButtonClicked() override {} + //~ End ICommonEditorViewportToolbarInfoProvider interface + +protected: + //~ Begin SEditorViewport interface + virtual TSharedRef MakeEditorViewportClient() override; + virtual void PopulateViewportOverlays(TSharedRef Overlay) override; + virtual void BindCommands() override; + virtual bool IsVisible() const override; + //~ End SEditorViewport interface + +private: + /** The parent tab where this viewport resides */ + TWeakPtr ParentTab; + + IVoxelDataAssetEditor* DataAssetEditor = nullptr; + FAdvancedPreviewScene* AdvancedPreviewScene = nullptr; + + /** Level viewport client */ + TSharedPtr EditorViewportClient; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorCommands.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorCommands.h new file mode 100644 index 00000000..ffbf634c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorCommands.h @@ -0,0 +1,43 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "Framework/Commands/Commands.h" + +class FVoxelDataAssetEditorCommands : public TCommands +{ +public: + /** Constructor */ + FVoxelDataAssetEditorCommands() + : TCommands + ( + "VoxelDataAssetEditor", // Context name for icons + VOXEL_LOCTEXT("Voxel Data Asset Editor"), // Localized context name for displaying + NAME_None, // Parent + "VoxelStyle" // Icon Style Set + ) + { + } + + /** Toggles the preview pane's grid */ + TSharedPtr TogglePreviewGrid; + + /** Toggles the preview pane's background */ + TSharedPtr TogglePreviewBackground; + + /** Invert data asset */ + TSharedPtr InvertDataAsset; + + + /** Initialize commands */ + virtual void RegisterCommands() override + { +#define LOCTEXT_NAMESPACE "Voxel" + UI_COMMAND(TogglePreviewGrid, "Grid", "Toggles the preview pane's grid.", EUserInterfaceActionType::ToggleButton, FInputChord()); + UI_COMMAND(TogglePreviewBackground, "Background", "Toggles the preview pane's background.", EUserInterfaceActionType::ToggleButton, FInputChord()); + UI_COMMAND(InvertDataAsset, "Invert", "Invert the asset.", EUserInterfaceActionType::Button, FInputChord()); +#undef LOCTEXT_NAMESPACE + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.cpp new file mode 100644 index 00000000..f1b172cd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.cpp @@ -0,0 +1,185 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDataAssetEditorManager.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelMinimal.h" +#include "VoxelWorld.h" +#include "VoxelDebug/VoxelDebugUtilities.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelTools/VoxelDataTools.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelUtilities/VoxelConfigUtilities.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelSettings.h" +#include "VoxelFeedbackContext.h" + +#include "DrawDebugHelpers.h" +#include "PreviewScene.h" +#include "EngineUtils.h" +#include "Materials/MaterialInterface.h" + +FVoxelDataAssetEditorManager::FVoxelDataAssetEditorManager(UVoxelDataAsset* DataAsset, FPreviewScene& PreviewScene) + : DataAsset(DataAsset) +{ + check(DataAsset); + + if (!DataAsset->VoxelWorldTemplate) + { + auto* NewWorld = NewObject(DataAsset); + NewWorld->MaterialCollection = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Materials/Quixel/MC_Quixel")); + NewWorld->VoxelMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst")); + + FVoxelConfigUtilities::LoadConfig(NewWorld, "VoxelDataAssetEditor.DefaultVoxelWorld"); + + DataAsset->VoxelWorldTemplate = NewWorld; + DataAsset->MarkPackageDirty(); + } + + FActorSpawnParameters SpawnParameters; + SpawnParameters.Template = DataAsset->VoxelWorldTemplate; + World = PreviewScene.GetWorld()->SpawnActor(SpawnParameters); + + CreateWorld(); +} + +FVoxelDataAssetEditorManager::~FVoxelDataAssetEditorManager() +{ + World->GetData().ClearDirtyFlag(); // Avoid annoying save popup from the voxel world + World->DestroyWorld(); + + check(DataAsset->VoxelWorldTemplate); + DataAsset->VoxelWorldTemplate->ReinitializeProperties(World); +} + +void FVoxelDataAssetEditorManager::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(World); + check(World); +} + +AVoxelWorld& FVoxelDataAssetEditorManager::GetVoxelWorld() const +{ + check(World); + return *World; +} + +void FVoxelDataAssetEditorManager::Save(bool bShowDebug) +{ + FVoxelScopedSlowTask Progress(6); + + auto& Data = World->GetData(); + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Rounding voxels")); + if (GetDefault()->bRoundBeforeSaving) + { + UVoxelDataTools::RoundVoxels(World, FVoxelIntBox::Infinite); + } + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Finding dirty voxels")); + FVoxelIntBoxWithValidity OptionalDirtyBounds; + bool bHasMaterials = false; + { + FVoxelReadScopeLock Lock(Data, FVoxelIntBox::Infinite, "data asset save"); + FVoxelOctreeUtilities::IterateAllLeaves(Data.GetOctree(), [&](FVoxelDataOctreeLeaf& Leaf) + { + bHasMaterials |= Leaf.Materials.IsDirty(); + if (Leaf.Values.IsDirty() || Leaf.Materials.IsDirty()) + { + OptionalDirtyBounds += Leaf.GetBounds(); + } + }); + } + + // Should always have at least one dirty voxel, else it would mean that the original data asset had a size of 0 which is invalid + if (!ensure(OptionalDirtyBounds.IsValid())) return; + + const auto DirtyBounds = OptionalDirtyBounds.GetBox(); + + const bool bSubtractiveAsset = DataAsset->bSubtractiveAsset; + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Finding voxels to save")); + FVoxelIntBox BoundsToSave; + TArray PointsAlone; + { + FVoxelReadScopeLock Lock(Data, DirtyBounds, "data asset save"); + FVoxelConstDataAccelerator OctreeAccelerator(Data, DirtyBounds); + DirtyBounds.Iterate([&](int32 X, int32 Y, int32 Z) + { + const FVoxelValue Value = OctreeAccelerator.Get(X, Y, Z, 0); + if ((bSubtractiveAsset && !Value.IsTotallyFull()) || (!bSubtractiveAsset && !Value.IsTotallyEmpty())) + { + if (!BoundsToSave.IsValid()) + { + BoundsToSave = FVoxelIntBox(X, Y, Z); + } + else if (!BoundsToSave.Contains(X, Y, Z)) + { + BoundsToSave = BoundsToSave + FIntVector(X, Y, Z); + PointsAlone.Emplace(X, Y, Z); + } + } + }); + } + + const FIntVector PositionOffset = BoundsToSave.Min; + const FIntVector Size = BoundsToSave.Size(); + + const auto AssetData = MakeVoxelShared(); + AssetData->SetSize(Size, bHasMaterials); + + { + FVoxelReadScopeLock Lock(Data, BoundsToSave, "Data Asset Save"); + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Copying values")); + { + TVoxelQueryZone QueryZone(BoundsToSave, AssetData->GetRawValues()); + Data.Get(QueryZone, 0); + } + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Copying materials")); + if (bHasMaterials) + { + TVoxelQueryZone QueryZone(BoundsToSave, AssetData->GetRawMaterials()); + Data.Get(QueryZone, 0); + } + } + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Compressing")); + DataAsset->PositionOffset = PositionOffset; + DataAsset->SetData(AssetData); + + Data.ClearDirtyFlag(); + + LOG_VOXEL(Log, TEXT("Data asset saved. Has materials: %s"), bHasMaterials ? TEXT("yes") : TEXT("no")); + + if (bShowDebug) + { + UVoxelDebugUtilities::DrawDebugIntBox(World, BoundsToSave, 10, 100, FColor::Red); + for (auto& Point : PointsAlone) + { + DrawDebugPoint(World->GetWorld(), World->LocalToGlobal(Point), 10, FColor::Magenta, false, 10); + } + } +} + +void FVoxelDataAssetEditorManager::RecreateWorld() +{ + World->DestroyWorld(); + CreateWorld(); +} + +bool FVoxelDataAssetEditorManager::IsDirty() const +{ + return ensure(World->IsCreated()) && World->GetData().IsDirty(); +} + +void FVoxelDataAssetEditorManager::CreateWorld() +{ + check(!World->IsCreated()); + World->SetGeneratorObject(DataAsset); + World->CreateInEditor(); + UVoxelDataTools::SetBoxAsDirty(World, DataAsset->GetBounds(), true, DataAsset->GetData()->HasMaterials()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h new file mode 100644 index 00000000..0b8346bf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/GCObject.h" + +class AVoxelWorld; +class UVoxelDataAsset; +class FPreviewScene; + +class FVoxelDataAssetEditorManager : public FGCObject +{ +public: + FVoxelDataAssetEditorManager(UVoxelDataAsset* DataAsset, FPreviewScene& PreviewScene); + ~FVoxelDataAssetEditorManager(); + + //~ Begin FGCObject Interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + //~ End FGCObject Interface + +public: + AVoxelWorld& GetVoxelWorld() const; + +public: + void Save(bool bShowDebug); + void RecreateWorld(); + + bool IsDirty() const; + +private: + UVoxelDataAsset* const DataAsset; + AVoxelWorld* World; + + void CreateWorld(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.cpp new file mode 100644 index 00000000..493b9f93 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.cpp @@ -0,0 +1,502 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDataAssetEditorToolkit.h" +#include "VoxelEditorToolsPanel.h" +#include "DataAssetEditor/VoxelDataAssetEditorCommands.h" +#include "DataAssetEditor/VoxelDataAssetEditorManager.h" +#include "DataAssetEditor/SVoxelDataAssetEditorViewport.h" +#include "Details/VoxelWorldDetails.h" + +#include "VoxelWorld.h" +#include "VoxelFeedbackContext.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.h" +#include "VoxelTools/VoxelAssetTools.h" +#include "VoxelUtilities/VoxelConfigUtilities.h" + +#include "IDetailsView.h" +#include "PropertyEditorModule.h" +#include "Modules/ModuleManager.h" +#include "AdvancedPreviewSceneModule.h" +#include "AdvancedPreviewScene.h" +#include "PreviewScene.h" +#include "EngineUtils.h" +#include "EditorStyleSet.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Widgets/Docking/SDockTab.h" +#include "UnrealEdGlobals.h" +#include "Editor/UnrealEdEngine.h" +#include "ThumbnailRendering/ThumbnailManager.h" +#include "Misc/MessageDialog.h" + +const FName FVoxelDataAssetEditorToolkit::EditToolsTabId(TEXT("VoxelDataAssetEditor_EditTools")); +const FName FVoxelDataAssetEditorToolkit::PreviewSettingsTabId(TEXT("VoxelDataAssetEditor_PreviewSettings")); +const FName FVoxelDataAssetEditorToolkit::DetailsTabId(TEXT("VoxelDataAssetEditor_Details")); +const FName FVoxelDataAssetEditorToolkit::AdvancedPreviewSettingsTabId(TEXT("VoxelDataAssetEditor_AdvancedPreviewSettings")); +const FName FVoxelDataAssetEditorToolkit::PreviewTabId(TEXT("VoxelDataAssetEditor_Preview")); + +class FVoxelAdvancedPreviewScene : public FAdvancedPreviewScene +{ +public: + FVoxelAdvancedPreviewScene() + : FAdvancedPreviewScene(ConstructionValues()) + { + if (SkyComponent) + { + SkyComponent->SetWorldScale3D(FVector(1000000)); + } + } +}; + +FVoxelDataAssetEditorToolkit::FVoxelDataAssetEditorToolkit() +{ + PreviewScene = MakeShared(); + PreviewScene->SetFloorVisibility(false); + PreviewScene->SetSkyCubemap(GUnrealEd->GetThumbnailManager()->AmbientCubemap); + + UWorld* PreviewWorld = PreviewScene->GetWorld(); + for (FActorIterator It(PreviewWorld); It; ++It) + { + It->DispatchBeginPlay(); + } + PreviewWorld->bBegunPlay = true; +} + +FVoxelDataAssetEditorToolkit::~FVoxelDataAssetEditorToolkit() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataAssetEditorToolkit::RegisterTabSpawners(const TSharedRef& InTabManager) +{ + WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(VOXEL_LOCTEXT("Voxel Editor")); + auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); + + FAssetEditorToolkit::RegisterTabSpawners(InTabManager); + + InTabManager->RegisterTabSpawner(EditToolsTabId, FOnSpawnTab::CreateSP(this, &FVoxelDataAssetEditorToolkit::SpawnTab_EditTools)) + .SetDisplayName(VOXEL_LOCTEXT("Edit Tools")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(PreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FVoxelDataAssetEditorToolkit::SpawnTab_PreviewSettings)) + .SetDisplayName(VOXEL_LOCTEXT("Preview Settings")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(DetailsTabId, FOnSpawnTab::CreateSP(this, &FVoxelDataAssetEditorToolkit::SpawnTab_Details)) + .SetDisplayName(VOXEL_LOCTEXT("Details")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(AdvancedPreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FVoxelDataAssetEditorToolkit::SpawnTab_AdvancedPreviewSettings)) + .SetDisplayName(VOXEL_LOCTEXT("Advanced Preview Settings")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(PreviewTabId, FOnSpawnTab::CreateSP(this, &FVoxelDataAssetEditorToolkit::SpawnTab_Preview)) + .SetDisplayName(VOXEL_LOCTEXT("Preview")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports")); +} + +void FVoxelDataAssetEditorToolkit::UnregisterTabSpawners(const TSharedRef& InTabManager) +{ + FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); + + InTabManager->UnregisterTabSpawner(EditToolsTabId); + InTabManager->UnregisterTabSpawner(PreviewSettingsTabId); + InTabManager->UnregisterTabSpawner(DetailsTabId); + InTabManager->UnregisterTabSpawner(AdvancedPreviewSettingsTabId); + InTabManager->UnregisterTabSpawner(PreviewTabId); +} + +void FVoxelDataAssetEditorToolkit::InitVoxelEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit) +{ + DataAsset = CastChecked(ObjectToEdit); + + // Support undo/redo + DataAsset->SetFlags(RF_Transactional); + + Manager = MakeUnique(DataAsset, *PreviewScene); + + ToolsPanel = MakeShared(); + ToolsPanel->Init(); + // To have a nice screenshot + ToolsPanel->ClearTool(); + + FVoxelDataAssetEditorCommands::Register(); + + BindCommands(); + CreateInternalWidgets(); + + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_VoxelDataAssetEditor_Layout_v3") + ->AddArea + ( + FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.1f) + ->SetHideTabWell( true ) + ->AddTab(GetToolbarTabId(), ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation(Orient_Horizontal) ->SetSizeCoefficient(0.9f) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical) ->SetSizeCoefficient(0.2f) + ->Split + ( + FTabManager::NewStack() + ->AddTab( EditToolsTabId, ETabState::OpenedTab ) + ->AddTab( PreviewSettingsTabId, ETabState::OpenedTab) + ->AddTab( AdvancedPreviewSettingsTabId, ETabState::ClosedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->AddTab( DetailsTabId, ETabState::OpenedTab) + ) + ) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation( Orient_Vertical ) + ->SetSizeCoefficient(0.80f) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.8f) + ->SetHideTabWell( true ) + ->AddTab( PreviewTabId, ETabState::OpenedTab ) + ) + + ) + ) + ); + + const bool bCreateDefaultStandaloneMenu = true; + const bool bCreateDefaultToolbar = true; + FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, TEXT("VoxelDataAssetEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit, false); + + ExtendToolbar(); + RegenerateMenusAndToolbars(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataAssetEditorToolkit::CreateInternalWidgets() +{ + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + + { + FDetailsViewArgs Args; + Args.bHideSelectionTip = true; + Args.NotifyHook = nullptr; + Args.bShowOptions = false; + Args.bShowActorLabel = false; + + PreviewSettings = PropertyModule.CreateDetailView(Args); + PreviewSettings->RegisterInstancedCustomPropertyLayout( + AVoxelWorld::StaticClass(), + FOnGetDetailCustomizationInstance::CreateLambda([]() { return MakeShared(true); })); + PreviewSettings->SetObject(&Manager->GetVoxelWorld()); + PreviewSettings->OnFinishedChangingProperties().AddLambda([=](const FPropertyChangedEvent& Event) + { + if (Event.ChangeType != EPropertyChangeType::Interactive) + { + if (DataAsset->bUseSettingsAsDefault) + { + FVoxelConfigUtilities::SaveConfig(&Manager->GetVoxelWorld(), "VoxelDataAssetEditor.DefaultVoxelWorld"); + } + } + }); + } + + { + FDetailsViewArgs Args; + Args.bHideSelectionTip = true; + Args.NotifyHook = this; + Args.bShowOptions = false; + Args.bShowActorLabel = false; + Details = PropertyModule.CreateDetailView(Args); + Details->SetObject(DataAsset); + } + + FAdvancedPreviewSceneModule& AdvancedPreviewSceneModule = FModuleManager::LoadModuleChecked("AdvancedPreviewScene"); + AdvancedPreviewSettingsWidget = AdvancedPreviewSceneModule.CreateAdvancedPreviewSceneSettingsWidget(PreviewScene.ToSharedRef()); + + Preview = SNew(SVoxelDataAssetEditorViewport).Editor(this); +} + +void FVoxelDataAssetEditorToolkit::ExtendToolbar() +{ + TSharedPtr ToolbarExtender = MakeShared(); + + ToolbarExtender->AddToolBarExtension( + "Asset", + EExtensionHook::After, + GetToolkitCommands(), + FToolBarExtensionDelegate::CreateRaw(this, &FVoxelDataAssetEditorToolkit::FillToolbar) + ); + + AddToolbarExtender(ToolbarExtender); +} + +void FVoxelDataAssetEditorToolkit::FillToolbar(FToolBarBuilder& ToolbarBuilder) +{ + auto& Commands = FVoxelDataAssetEditorCommands::Get(); + + ToolbarBuilder.BeginSection("Toolbar"); + ToolbarBuilder.AddToolBarButton(Commands.InvertDataAsset); + ToolbarBuilder.EndSection(); +} + +void FVoxelDataAssetEditorToolkit::BindCommands() +{ + auto& Commands = FVoxelDataAssetEditorCommands::Get(); + + ToolkitCommands->MapAction( + Commands.InvertDataAsset, + FExecuteAction::CreateSP(this, &FVoxelDataAssetEditorToolkit::InvertDataAsset)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataAssetEditorToolkit::SaveAsset_Execute() +{ + FVoxelScopedSlowTask Progress(2, VOXEL_LOCTEXT("Saving asset")); + Progress.MakeDialog(false, true); + + Progress.EnterProgressFrame(); + Manager->Save(true); + + Progress.EnterProgressFrame(); + FAssetEditorToolkit::SaveAsset_Execute(); +} + +bool FVoxelDataAssetEditorToolkit::OnRequestClose() +{ + if (Manager->IsDirty()) + { + const auto Result = FMessageDialog::Open( + EAppMsgType::YesNoCancel, + EAppReturnType::Cancel, + FText::Format(VOXEL_LOCTEXT("Voxel Data Asset {0}: \nSave your changes?"), + FText::FromString(DataAsset->GetName()))); + if (Result == EAppReturnType::Yes) + { + SaveAsset_Execute(); + return true; + } + else if (Result == EAppReturnType::No) + { + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataAssetEditorToolkit::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(DataAsset); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataAssetEditorToolkit::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged) +{ + if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive || + !ensure(PropertyChangedEvent.GetNumObjectsBeingEdited() == 1) || + !ensure(PropertyChangedEvent.MemberProperty) || + !ensure(PropertyChangedEvent.GetObjectBeingEdited(0) == DataAsset)) + { + return; + } + + const auto SaveAsset = [&]() + { + if (Manager->IsDirty()) + { + FVoxelScopedSlowTask Progress(1, VOXEL_LOCTEXT("Saving asset")); + Progress.MakeDialog(false, true); + Progress.EnterProgressFrame(); + Manager->Save(false); + } + }; + + const FName Name = PropertyChangedEvent.MemberProperty->GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(UVoxelDataAsset, bSubtractiveAsset)) + { + if (Manager->IsDirty()) + { + const bool bNewSubtractiveAsset = DataAsset->bSubtractiveAsset; + const bool bOldSubtractiveAsset = !bNewSubtractiveAsset; + DataAsset->bSubtractiveAsset = bOldSubtractiveAsset; + + SaveAsset(); + + DataAsset->bSubtractiveAsset = bNewSubtractiveAsset; + } + Manager->RecreateWorld(); + } + else if (Name == GET_MEMBER_NAME_STATIC(UVoxelDataAsset, PositionOffset)) + { + // Save position offset, as it will be changed by Save + const FIntVector PositionOffset = DataAsset->PositionOffset; + + SaveAsset(); + + DataAsset->PositionOffset = PositionOffset; + Manager->RecreateWorld(); + } + else if (Name == GET_MEMBER_NAME_STATIC(UVoxelDataAsset, Tolerance)) + { + SaveAsset(); + Manager->RecreateWorld(); + } + else if (Name == GET_MEMBER_NAME_STATIC(UVoxelDataAsset, bUseSettingsAsDefault)) + { + // Nothing to do + } + else + { + ensure(false); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FAdvancedPreviewScene& FVoxelDataAssetEditorToolkit::GetPreviewScene() const +{ + return *PreviewScene; +} + +AVoxelWorld& FVoxelDataAssetEditorToolkit::GetVoxelWorld() const +{ + return Manager->GetVoxelWorld(); +} + +UVoxelDataAsset& FVoxelDataAssetEditorToolkit::GetDataAsset() const +{ + return *DataAsset; +} + +FVoxelEditorToolsPanel& FVoxelDataAssetEditorToolkit::GetPanel() const +{ + return *ToolsPanel; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TSharedRef FVoxelDataAssetEditorToolkit::SpawnTab_EditTools(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == EditToolsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Edit Tools")) + [ + ToolsPanel->GetWidget() + ]; + return Tab; +} + +TSharedRef FVoxelDataAssetEditorToolkit::SpawnTab_PreviewSettings(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PreviewSettingsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Preview Settings")) + [ + PreviewSettings.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelDataAssetEditorToolkit::SpawnTab_Details(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == DetailsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Details")) + [ + Details.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelDataAssetEditorToolkit::SpawnTab_AdvancedPreviewSettings(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == AdvancedPreviewSettingsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Advanced Preview Settings")) + [ + AdvancedPreviewSettingsWidget.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelDataAssetEditorToolkit::SpawnTab_Preview(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PreviewTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports")) + .Label(VOXEL_LOCTEXT("Preview")) + [ + Preview.ToSharedRef() + ]; + return Tab; +} + +void FVoxelDataAssetEditorToolkit::InvertDataAsset() +{ + if (Manager->IsDirty()) + { + FVoxelScopedSlowTask Progress(1, VOXEL_LOCTEXT("Saving asset")); + Progress.MakeDialog(false, true); + Progress.EnterProgressFrame(); + Manager->Save(false); + } + + const auto NewData = MakeVoxelShared(); + UVoxelAssetTools::InvertDataAssetImpl(*DataAsset->GetData(), *NewData); + DataAsset->SetData(NewData); + + Manager->RecreateWorld(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h new file mode 100644 index 00000000..22eb3f1f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h @@ -0,0 +1,112 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "UObject/GCObject.h" +#include "Misc/NotifyHook.h" +#include "IVoxelDataAssetEditor.h" + +class UVoxelDataAsset; +class FVoxelDataAssetEditorManager; +class IDetailsView; +class SWidget; +class FAdvancedPreviewScene; +class SVoxelDataAssetEditorViewport; +class FVoxelEditorToolsPanel; + +class FVoxelDataAssetEditorToolkit : public IVoxelDataAssetEditor, public FGCObject, public FNotifyHook +{ +public: + FVoxelDataAssetEditorToolkit(); + virtual ~FVoxelDataAssetEditorToolkit(); + + virtual void RegisterTabSpawners(const TSharedRef& TabManager) override; + virtual void UnregisterTabSpawners(const TSharedRef& TabManager) override; + + void InitVoxelEditor(EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit); + +private: + // Creates all internal widgets for the tabs to point at + void CreateInternalWidgets(); + // Builds the toolbar widget for the Voxel editor + void ExtendToolbar(); + // Called by ExtendToolbar + void FillToolbar(FToolBarBuilder& ToolbarBuilder); + // Binds new graph commands to delegates + void BindCommands(); + +public: + //~ Begin IToolkit interface + virtual FName GetToolkitFName() const override { return "VoxelDataAssetEditor"; } + virtual FText GetBaseToolkitName() const override { return VOXEL_LOCTEXT("Voxel Data Asset Editor"); } + virtual FString GetWorldCentricTabPrefix() const override { return "VoxelDataAssetEditor"; } + virtual FLinearColor GetWorldCentricTabColorScale() const override { return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f); } + //~ End IToolkit interface + + //~ Begin FAssetEditorToolkit interface + virtual void SaveAsset_Execute() override; + virtual bool OnRequestClose() override; + //~ End FAssetEditorToolkit interface + + //~ Begin FGCObject interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + //~ End FGCObject interface + + //~ Begin FNotifyHook interface + virtual void NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged) override; + //~ End FNotifyHook interface + + //~ Begin IVoxelDataAssetEditor interface + virtual FAdvancedPreviewScene& GetPreviewScene() const override; + virtual AVoxelWorld& GetVoxelWorld() const override; + virtual UVoxelDataAsset& GetDataAsset() const override; + virtual FVoxelEditorToolsPanel& GetPanel() const override; + //~ End IVoxelDataAssetEditor interface + +private: + TSharedRef SpawnTab_EditTools(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_PreviewSettings(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Details(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_AdvancedPreviewSettings(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Preview(const FSpawnTabArgs& Args); + +private: + void InvertDataAsset(); + +private: + // The Voxel asset being inspected + UVoxelDataAsset* DataAsset = nullptr; + + // Manager, handles the voxel world + TUniquePtr Manager; + +private: + /** + * Tabs + */ + + // Preview settings tab + TSharedPtr PreviewSettings; + + // Asset details + TSharedPtr Details; + + // Advanced preview settings tab + TSharedPtr AdvancedPreviewSettingsWidget; + + // Preview tab + TSharedPtr PreviewScene; + TSharedPtr Preview; + + // Editor tools tab + TSharedPtr ToolsPanel; + + // The tab ids for all the tabs used + static const FName EditToolsTabId; + static const FName PreviewSettingsTabId; + static const FName DetailsTabId; + static const FName AdvancedPreviewSettingsTabId; + static const FName PreviewTabId; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp new file mode 100644 index 00000000..6b2cc7c5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp @@ -0,0 +1,262 @@ +// Copyright 2020 Phyronnaz + +#include "DataAssetEditor/VoxelDataAssetEditorViewportClient.h" +#include "DataAssetEditor/SVoxelDataAssetEditorViewport.h" +#include "VoxelEditorToolsPanel.h" +#include "VoxelWorldEditorControls.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelRender/IVoxelRenderer.h" +#include "VoxelData/VoxelData.h" +#include "VoxelWorld.h" +#include "VoxelIntBox.h" + +#include "UnrealEdGlobals.h" +#include "Editor/UnrealEdEngine.h" +#include "Editor/EditorPerProjectUserSettings.h" +#include "Components/LineBatchComponent.h" +#include "Engine/World.h" +#include "PreviewScene.h" +#include "EngineUtils.h" +#include "ImageUtils.h" +#include "TimerManager.h" + +TSharedRef FVoxelDataAssetEditorViewportClient::Create( + AVoxelWorld& VoxelWorld, + UVoxelDataAsset& DataAsset, + FVoxelEditorToolsPanel& Panel, + FPreviewScene& PreviewScene, + SVoxelDataAssetEditorViewport& DataAssetEditorViewport) +{ + const auto Result = TSharedRef(new FVoxelDataAssetEditorViewportClient( + VoxelWorld, + DataAsset, + Panel, + PreviewScene, + DataAssetEditorViewport)); + + check(VoxelWorld.IsCreated()); + VoxelWorld.GetRenderer().OnWorldLoaded.AddSP(Result, &FVoxelDataAssetEditorViewportClient::ScheduleUpdateThumbnail); + + return Result; +} + +FVoxelDataAssetEditorViewportClient::FVoxelDataAssetEditorViewportClient( + AVoxelWorld& VoxelWorld, + UVoxelDataAsset& DataAsset, + FVoxelEditorToolsPanel& Panel, + FPreviewScene& InPreviewScene, + SVoxelDataAssetEditorViewport& DataAssetEditorViewport) + : FEditorViewportClient(nullptr, &InPreviewScene, StaticCastSharedRef(DataAssetEditorViewport.AsShared())) + , VoxelWorld(VoxelWorld) + , DataAsset(DataAsset) + , Panel(Panel) +{ + Widget->SetSnapEnabled(true); + DrawHelper.bDrawGrid = bShowGrid; + + { + // View the asset so that Camera Forward = Min Axis + + const FVoxelIntBox Bounds = DataAsset.GetBounds(); + const FIntVector Size = Bounds.Size(); + + const int32 Axis = FVoxelUtilities::GetArgMin(Size); + + // Max of vertical and horizontal size of the viewed object + const float MaxSize = FMath::Max(Size[(Axis + 1) % 3], Size[(Axis + 2) % 3]); + + const float HalfHorizontalFOV = FMath::DegreesToRadians(ViewFOV / 2); + const float HalfVerticalFOV = FMath::Atan(FMath::Tan(HalfHorizontalFOV) / AspectRatio); + + // Distance required to fit the object into view + const float Distance = (MaxSize / 2) / FMath::Tan(FMath::Min(HalfVerticalFOV, HalfHorizontalFOV)); + + FVector Position = Bounds.GetCenter().ToFloat(); + // Be far enough to have the asset in view + Position[Axis] = Size[Axis] + Distance; + SetViewLocation(Position * VoxelWorld.VoxelSize); + + FVector Direction = FVector::ZeroVector; + Direction[Axis] = -1; // negative so that if the axis is Z we look down and not up + SetViewRotation(Direction.ToOrientationRotator()); + } + + SetRealtime(true); + bSetListenerPosition = false; + + EngineShowFlags.EnableAdvancedFeatures(); + EngineShowFlags.SetLighting(true); + EngineShowFlags.SetIndirectLightingCache(true); + EngineShowFlags.SetPostProcessing(true); + EngineShowFlags.SetSelectionOutline(false); + + Invalidate(); +} + +void FVoxelDataAssetEditorViewportClient::Tick(float DeltaSeconds) +{ + FEditorViewportClient::Tick(DeltaSeconds); + + if (!ensure(VoxelWorld.IsCreated())) return; + + UWorld* World = VoxelWorld.GetWorld(); + for (auto* VoxelWorldEditor : TActorRange(World)) + { + VoxelWorldEditor->bOverrideLocation = true; + VoxelWorldEditor->LocationOverride = GetViewLocation(); + } + + World->Tick(LEVELTICK_All, DeltaSeconds); + Panel.Tick(this, DeltaSeconds); + + if (VoxelWorld.GetData().IsDirty()) + { + DataAsset.MarkPackageDirty(); + } +} + +void FVoxelDataAssetEditorViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) +{ + FEditorViewportClient::Draw(View, PDI); +} + +void FVoxelDataAssetEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas) +{ + FEditorViewportClient::Draw(InViewport, Canvas); + TArray EmptyPropertyArray; + DrawStatsHUD(VoxelWorld.GetWorld(), InViewport, Canvas, nullptr, EmptyPropertyArray, GetViewLocation(), GetViewRotation()); +} + +bool FVoxelDataAssetEditorViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool bGamepad) +{ + bool bHandled = GUnrealEd->ComponentVisManager.HandleInputKey(this, InViewport, Key, Event);; + + if (Key == EKeys::MouseScrollDown || Key == EKeys::MouseScrollUp) + { + bHandled = true; + } + + if (Key == EKeys::LeftMouseButton && Event != EInputEvent::IE_Repeat) + { + bMousePressed = (Event == EInputEvent::IE_Pressed); + } + + if (Event == IE_Released && (Key == EKeys::LeftMouseButton || Key == EKeys::MiddleMouseButton || Key == EKeys::RightMouseButton)) + { + //Set the cursor position to that of the slate cursor so it wont snap back + Viewport->SetPreCaptureMousePosFromSlateCursor(); + } + + bHandled &= Panel.InputKey(this, InViewport, Key, Event); + + if (!bHandled) + { + bHandled = FEditorViewportClient::InputKey(InViewport, ControllerId, Key, Event, AmountDepressed, bGamepad); + } + + return bHandled; +} + +bool FVoxelDataAssetEditorViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad) +{ + return Panel.InputAxis(this, InViewport, Key, Delta, DeltaTime) || FEditorViewportClient::InputAxis(InViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad); +} + +void FVoxelDataAssetEditorViewportClient::ProcessClick(class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) +{ + const FViewportClick Click(&View, this, Key, Event, HitX, HitY); + GUnrealEd->ComponentVisManager.HandleClick(this, HitProxy, Click); + Panel.HandleClick(this, HitProxy, Click); +} + +int32 FVoxelDataAssetEditorViewportClient::GetCameraSpeedSetting() const +{ + return GetDefault()->SCSViewportCameraSpeed; +} + +void FVoxelDataAssetEditorViewportClient::SetCameraSpeedSetting(int32 SpeedSetting) +{ + GetMutableDefault()->SCSViewportCameraSpeed = SpeedSetting; +} + +void FVoxelDataAssetEditorViewportClient::MouseMove(FViewport* InViewport, int32 x, int32 y) +{ + FEditorViewportClient::MouseMove(InViewport, x, y); + + Panel.MouseMove(this, InViewport, x, y); +} + +void FVoxelDataAssetEditorViewportClient::UpdateMouseDelta() +{ + if (!bMousePressed) + { + FEditorViewportClient::UpdateMouseDelta(); + } +} + +bool FVoxelDataAssetEditorViewportClient::IsShowGridToggled() +{ + return bShowGrid; +} + +void FVoxelDataAssetEditorViewportClient::ToggleShowGrid() +{ + bShowGrid = !bShowGrid; + DrawHelper.bDrawGrid = bShowGrid; + Invalidate(); +} + +void FVoxelDataAssetEditorViewportClient::ScheduleUpdateThumbnail() +{ + // Wait a bit to make sure chunks are rendered + FTimerHandle Handle; + auto& TimerManager = VoxelWorld.GetWorldTimerManager(); + TimerManager.SetTimer(Handle, FTimerDelegate::CreateSP(this, &FVoxelDataAssetEditorViewportClient::UpdateThumbnail), 0.5, false); +} + +void FVoxelDataAssetEditorViewportClient::UpdateThumbnail() +{ + uint32 SrcWidth = Viewport->GetSizeXY().X; + uint32 SrcHeight = Viewport->GetSizeXY().Y; + + // Read the contents of the viewport into an array. + TArray OrigBitmap; + if (Viewport->ReadPixels(OrigBitmap)) + { + check(OrigBitmap.Num() == SrcWidth * SrcHeight); + + // Pin to smallest value + int32 CropSize = FMath::Min(SrcWidth, SrcHeight); + + // Calculations for cropping + TArray CroppedBitmap; + CroppedBitmap.AddUninitialized(CropSize * CropSize); + + // Crop the image + int32 CroppedSrcTop = (SrcHeight - CropSize) / 2; + int32 CroppedSrcLeft = (SrcWidth - CropSize) / 2; + + for (int32 Row = 0; Row < CropSize; Row++) + { + // Row*Side of a row*byte per color + int32 SrcPixelIndex = (CroppedSrcTop + Row) * SrcWidth + CroppedSrcLeft; + const void* SrcPtr = &(OrigBitmap[SrcPixelIndex]); + void* DstPtr = &(CroppedBitmap[Row * CropSize]); + FMemory::Memcpy(DstPtr, SrcPtr, CropSize * 4); + } + + // Scale image down if needed + TArray ScaledBitmap; + if (DATA_ASSET_THUMBNAIL_RES != CropSize) + { + FImageUtils::ImageResize(CropSize, CropSize, CroppedBitmap, DATA_ASSET_THUMBNAIL_RES, DATA_ASSET_THUMBNAIL_RES, ScaledBitmap, true); + } + else + { + //just copy the data over. sizes are the same + ScaledBitmap = CroppedBitmap; + } + + DataAsset.SetThumbnail(MoveTemp(ScaledBitmap)); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.h new file mode 100644 index 00000000..8175e422 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.h @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "EditorViewportClient.h" + +class AVoxelWorld; +class FVoxelEditorToolsPanel; +class SVoxelDataAssetEditorViewport; +class FPreviewScene; +class UVoxelDataAsset; + +class FVoxelDataAssetEditorViewportClient : public FEditorViewportClient, public TSharedFromThis +{ +public: + static TSharedRef Create( + AVoxelWorld& VoxelWorld, + UVoxelDataAsset& DataAsset, + FVoxelEditorToolsPanel& Panel, + FPreviewScene& PreviewScene, + SVoxelDataAssetEditorViewport& DataAssetEditorViewport); + + //~ Begin FEditorViewportClient interface + virtual void Tick(float DeltaSeconds) override; + virtual void Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) override; + virtual void Draw(FViewport* Viewport, FCanvas* Canvas) override; + virtual bool InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed = 1.f, bool bGamepad = false) override; + virtual bool InputAxis(FViewport* Viewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad) override; + virtual void ProcessClick(class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) override; + virtual int32 GetCameraSpeedSetting() const override; + virtual void SetCameraSpeedSetting(int32 SpeedSetting) override; + virtual void MouseMove(FViewport* Viewport, int32 x, int32 y) override; + virtual void UpdateMouseDelta() override; + virtual FWidget::EWidgetMode GetWidgetMode() const override { return FWidget::WM_Max; } + //~ End FEditorViewportClient interface + + bool IsShowGridToggled(); + void ToggleShowGrid(); + +private: + AVoxelWorld& VoxelWorld; + UVoxelDataAsset& DataAsset; + FVoxelEditorToolsPanel& Panel; + + bool bShowGrid = false; + bool bShowFloor = false; + bool bMousePressed = false; + + FVoxelDataAssetEditorViewportClient( + AVoxelWorld& VoxelWorld, + UVoxelDataAsset& DataAsset, + FVoxelEditorToolsPanel& Panel, + FPreviewScene& InPreviewScene, + SVoxelDataAssetEditorViewport& DataAssetEditorViewport); + + void ScheduleUpdateThumbnail(); + void UpdateThumbnail(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.cpp new file mode 100644 index 00000000..11105f0f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#include "RangeAnalysisDebuggerDetails.h" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelNodes/VoxelOptimizationNodes.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" +#include "DetailWidgetRow.h" + +void FRangeAnalysisDebuggerDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + if (Objects.Num() != 1) + { + return; + } + Node = CastChecked(Objects[0].Get()); + + Node->UpdateFromBin(); + + SAssignNew(ResetButton, SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .OnClicked_Lambda([=]() { if (Node.IsValid()) { Node->Reset(); } return FReply::Handled(); }) + .IsEnabled_Lambda([=]() { return Node.IsValid() && Node->Bins->bMinMaxInit; }) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Reset")) + ]; + + SAssignNew(UpdateButton, SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .OnClicked_Lambda([=]() { if (Node.IsValid()) { Node->UpdateGraph(); } return FReply::Handled(); }) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Update")) + ]; + + DetailLayout.EditCategory("Bounds") + .AddCustomRow(VOXEL_LOCTEXT("Reset")) + .NameContent() + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Reset bounds")) + ] + .ValueContent() + [ + ResetButton.ToSharedRef() + ]; + + DetailLayout.EditCategory("Graph") + .AddCustomRow(VOXEL_LOCTEXT("Update")) + .NameContent() + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Update Graph")) + ] + .ValueContent() + [ + UpdateButton.ToSharedRef() + ]; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.h new file mode 100644 index 00000000..59406141 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.h @@ -0,0 +1,25 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" + +class UVoxelNode_RangeAnalysisDebuggerFloat; +class SButton; + +class FRangeAnalysisDebuggerDetails : public IDetailCustomization +{ +public: + FRangeAnalysisDebuggerDetails() = default; + + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + +private: + TWeakObjectPtr Node; + + TSharedPtr ResetButton; + TSharedPtr UpdateButton; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.cpp new file mode 100644 index 00000000..29600f7e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.cpp @@ -0,0 +1,75 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAssetActorDetails.h" +#include "VoxelPlaceableItems/Actors/VoxelAssetActor.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelWorld.h" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelScopedTransaction.h" + +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" + +void FVoxelAssetActorDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + FVoxelEditorUtilities::EnableRealtime(); + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + if (Objects.Num() != 1) + { + return; + } + AssetActor = CastChecked(Objects[0].Get()); + + DetailLayout.EditCategory("Editor Tools", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Placeable Item Actor Settings", FText(), ECategoryPriority::Important); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Preview Settings", + VOXEL_LOCTEXT("Voxel Asset Update"), + VOXEL_LOCTEXT("Update Render"), + VOXEL_LOCTEXT("Update"), + false, + FOnClicked::CreateLambda([=]() + { + if (AssetActor.IsValid()) + { + AssetActor->UpdatePreview(); + } + return FReply::Handled(); + }), + TAttribute::Create([=]() + { + return AssetActor.IsValid() + && AssetActor->GetWorld() + && AssetActor->GetWorld()->WorldType == EWorldType::Editor + && AssetActor->Generator.IsValid() + && AssetActor->PreviewWorld; + })); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Editor Tools", + VOXEL_LOCTEXT("Stamp"), + VOXEL_LOCTEXT("Stamp"), + VOXEL_LOCTEXT("Stamp"), + false, + FOnClicked::CreateLambda([=]() + { + FVoxelScopedTransaction Transaction(AssetActor->PreviewWorld, STATIC_FNAME("Stamp"), EVoxelChangeType::Edit); + const auto Bounds = AssetActor->AddItemToData( + AssetActor->PreviewWorld, + &AssetActor->PreviewWorld->GetData()); + UVoxelBlueprintLibrary::UpdateBounds(AssetActor->PreviewWorld, Bounds); + UVoxelBlueprintLibrary::SaveFrame(AssetActor->PreviewWorld); + return FReply::Handled(); + }), + TAttribute::Create([=]() + { + return AssetActor.IsValid() + && AssetActor->Generator.IsValid() + && AssetActor->PreviewWorld + && AssetActor->PreviewWorld->IsCreated(); + })); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.h new file mode 100644 index 00000000..6fddc806 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.h @@ -0,0 +1,22 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" +#include "Widgets/Input/SButton.h" + +class AVoxelAssetActor;; + +class FVoxelAssetActorDetails : public IDetailCustomization +{ +public: + FVoxelAssetActorDetails() = default; + + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + +private: + TWeakObjectPtr AssetActor; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.cpp new file mode 100644 index 00000000..cf9b58da --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.cpp @@ -0,0 +1,58 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelBasicSpawnerScaleSettingsCustomization.h" +#include "VoxelSpawners/VoxelBasicSpawner.h" + +#include "PropertyHandle.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "IDetailPropertyRow.h" +#include "IPropertyUtilities.h" + +void FVoxelBasicSpawnerScaleSettingsCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ +} + +#define GET_CHILD_PROPERTY(PropertyHandle, Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() + +void FVoxelBasicSpawnerScaleSettingsCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + auto TypeHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBasicSpawnerScaleSettings, Scaling); + auto ScaleXHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBasicSpawnerScaleSettings, ScaleX); + auto ScaleYHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBasicSpawnerScaleSettings, ScaleY); + auto ScaleZHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBasicSpawnerScaleSettings, ScaleZ); + + FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&CustomizationUtils]() + { + auto Utilities = CustomizationUtils.GetPropertyUtilities(); + if (Utilities.IsValid()) + { + Utilities->ForceRefresh(); + } + }); + TypeHandle->SetOnPropertyValueChanged(RefreshDelegate); + + ChildBuilder.AddProperty(TypeHandle); + + FString Type; + TypeHandle->GetValueAsFormattedString(Type); + if (Type == "Uniform") + { + ChildBuilder.AddProperty(ScaleXHandle).DisplayName(VOXEL_LOCTEXT("Scale")); + } + else if (Type == "Free") + { + ChildBuilder.AddProperty(ScaleXHandle); + ChildBuilder.AddProperty(ScaleYHandle); + ChildBuilder.AddProperty(ScaleZHandle); + } + else if (Type == "LockXY") + { + ChildBuilder.AddProperty(ScaleXHandle).DisplayName(VOXEL_LOCTEXT("Scale XY")); + ChildBuilder.AddProperty(ScaleZHandle).DisplayName(VOXEL_LOCTEXT("Scale Z")); + } + else + { + ensure(false); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.h new file mode 100644 index 00000000..8a6abfff --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class FVoxelBasicSpawnerScaleSettingsCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.cpp new file mode 100644 index 00000000..aae42c0e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.cpp @@ -0,0 +1,90 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelBoolVectorCustomization.h" +#include "VoxelMinimal.h" +#include "VoxelBoolVector.h" + +#include "DetailWidgetRow.h" +#include "DetailLayoutBuilder.h" +#include "Widgets/Text/STextBlock.h" + +#define GET_CHILD_PROPERTY(PropertyHandle, Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() + +void FVoxelBoolVectorCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + const float XYZPadding = 5.0f; + + const auto X = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBoolVector, bX); + const auto Y = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBoolVector, bY); + const auto Z = GET_CHILD_PROPERTY(PropertyHandle, FVoxelBoolVector, bZ); + + HeaderRow + .NameContent() + [ + SNew(STextBlock) + .Text(VOXEL_LOCTEXT("Lock Position")) + .ToolTipText(VOXEL_LOCTEXT("Locks movement along the specified axis")) + .Font(IDetailLayoutBuilder::GetDetailFont()) + ] + .ValueContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(0.f, 0.f, XYZPadding, 0.f) + .AutoWidth() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + X->CreatePropertyNameWidget() + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + X->CreatePropertyValueWidget() + ] + ] + + + SHorizontalBox::Slot() + .Padding(0.f, 0.f, XYZPadding, 0.f) + .AutoWidth() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + Y->CreatePropertyNameWidget() + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + Y->CreatePropertyValueWidget() + ] + ] + + + SHorizontalBox::Slot() + .Padding(0.f, 0.f, XYZPadding, 0.f) + .AutoWidth() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + Z->CreatePropertyNameWidget() + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + Z->CreatePropertyValueWidget() + ] + ] + ]; +} + +void FVoxelBoolVectorCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + +} + +#undef GET_CHILD_PROPERTY \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.h new file mode 100644 index 00000000..312feb2c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class FVoxelBoolVectorCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.cpp new file mode 100644 index 00000000..fce072fd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.cpp @@ -0,0 +1,581 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGeneratorPickerCustomization.h" + +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" +#include "VoxelEditorDetailsIncludes.h" + +#include "EdGraphSchema_K2.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Kismet2/BlueprintEditorUtils.h" + +void FVoxelGeneratorPickerCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + const auto ClassHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelGeneratorPicker, Class)); + const auto ObjectHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelGeneratorPicker, Object)); + const auto TypeHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelGeneratorPicker, Type)); + + ClassHandle->SetOnPropertyValueChanged(FVoxelEditorUtilities::MakeRefreshDelegate(CustomizationUtils)); + ObjectHandle->SetOnPropertyValueChanged(FVoxelEditorUtilities::MakeRefreshDelegate(CustomizationUtils)); + TypeHandle->SetOnPropertyValueChanged(FVoxelEditorUtilities::MakeRefreshDelegate(CustomizationUtils)); + + ComboBoxArray.Add(MakeSharedCopy(EVoxelGeneratorPickerType::Class)); + ComboBoxArray.Add(MakeSharedCopy(EVoxelGeneratorPickerType::Object)); + + PickerType = GetPicker(*PropertyHandle).Type; + + { + void* Address = nullptr; + if (!ensure(TypeHandle->GetValueData(Address) == FPropertyAccess::Success) || !ensure(Address)) + { + return; + } + + PickerType = *static_cast(Address); + } + + HeaderRow + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .HAlign(HAlign_Fill) + .MaxDesiredWidth(TOptional()) + [ + SNew(SHorizontalBox) + + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SBox) + .VAlign(VAlign_Center) + [ + SNew(SComboBox>) + .IsEnabled(!TypeHandle->IsEditConst()) + .OptionsSource(&ComboBoxArray) + .OnSelectionChanged_Lambda([=](TSharedPtr Value, ESelectInfo::Type) + { + PickerType = *Value; + TypeHandle->SetValueFromFormattedString(UEnum::GetDisplayValueAsText(PickerType).ToString()); + }) + .OnGenerateWidget_Lambda([=](TSharedPtr Value) + { + return + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(UEnum::GetDisplayValueAsText(*Value)); + }) + .InitiallySelectedItem(PickerType == EVoxelGeneratorPickerType::Class ? ComboBoxArray[0] : ComboBoxArray[1]) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text_Lambda([=]() + { + return UEnum::GetDisplayValueAsText(PickerType); + }) + ] + ] + ] + + + SHorizontalBox::Slot() + [ + SNew(SBox) + .HAlign(HAlign_Left) + .Visibility_Lambda([=]() + { + return PickerType == EVoxelGeneratorPickerType::Class ? EVisibility::Visible : EVisibility::Collapsed; + }) + [ + ClassHandle->CreatePropertyValueWidget() + ] + ] + + + SHorizontalBox::Slot() + [ + SNew(SBox) + .HAlign(HAlign_Left) + .Visibility_Lambda([=]() + { + return PickerType == EVoxelGeneratorPickerType::Object ? EVisibility::Visible : EVisibility::Collapsed; + }) + [ + ObjectHandle->CreatePropertyValueWidget() + ] + ] + ]; +} + +class FVoxelGeneratorPickerCustomizationChildBuilder : public IDetailCustomNodeBuilder +{ +public: + const FString Name; + const TWeakObjectPtr Object; + TArray PropertyNames; + + FVoxelGeneratorPickerCustomizationChildBuilder(const FString& Name, const TWeakObjectPtr& Object) + : Name(Name) + , Object(Object) + { + } + + virtual void SetOnRebuildChildren(FSimpleDelegate InOnRegenerateChildren) override {} + virtual void GenerateHeaderRowContent(FDetailWidgetRow& NodeRow) override + { + NodeRow + .NameContent() + [ + SNew(STextBlock) + .Text(FText::FromString(Name)) + .Font(FEditorStyle::GetFontStyle(PropertyEditorConstants::CategoryFontStyle)) + ]; + } + virtual void GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder) override + { + if (!ensure(Object.IsValid())) return; + + for (auto& PropertyName : PropertyNames) + { + if (ensure(Object->GetClass()->FindPropertyByName(PropertyName))) + { + ChildrenBuilder.AddExternalObjectProperty({ Object.Get() }, PropertyName UE_24_ONLY(, FAddPropertyParams())); + } + } + } + + virtual void Tick(float DeltaTime) override {} + virtual bool RequiresTick() const override { return false; } + virtual bool InitiallyCollapsed() const override { return false; } + virtual FName GetName() const override { return *Name; } +}; + +void FVoxelGeneratorPickerCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + // Important: Picker might actually be a FVoxelTransformableGeneratorPicker + // We CANNOT call GetInstance on it + FVoxelGeneratorPicker& Picker = GetPicker(*PropertyHandle); + + if (!Picker.IsValid() || PropertyHandle->GetNumOuterObjects() != 1) + { + return; + } + + TArray Parameters; + if (auto* Generator = Picker.GetGenerator()) + { + Generator->GetParameters(Parameters); + } + + TMap NameToParameter; + for (auto& Parameter : Parameters) + { + NameToParameter.Add(Parameter.Id, Parameter); + } + + // Needs to match FVoxelCppConfig::BuildExposedVariablesArray + Parameters.Sort([](const FVoxelGeneratorParameter& A, const FVoxelGeneratorParameter& B) + { + if (A.Priority != B.Priority) + { + return A.Priority < B.Priority; + } + return A.Id.LexicalLess(B.Id); + }); + + Parameters.RemoveAll([](const FVoxelGeneratorParameter& Parameter) { return Parameter.MetaData.Contains("HideInGenerator"); }); + + TMap> CategoriesToParameters; + for (auto& Parameter : Parameters) + { + CategoriesToParameters.FindOrAdd(*Parameter.Category).Add(Parameter); + } + + CategoriesToParameters.KeySort([](const FName& A, const FName& B) + { + if (A.IsNone() != B.IsNone()) + { + // Put empty on top + return A.IsNone() > B.IsNone(); + } + return A.LexicalLess(B); + }); + + UVoxelGeneratorPickerEditorData* EditorData = Cast(Picker.EditorData); + if (!EditorData || + !ensure(EditorData->Blueprint) || + !ensure(EditorData->BlueprintInstance) || + EditorData->GeneratorObject != Picker.GetObject() || + EditorData->Parameters != Parameters) + { + auto& BlueprintPool = GetMutableDefault()->Blueprints; + + if (EditorData) + { + if (EditorData->Blueprint) + { + // Pool blueprints, as we can't have a BP deleted before its class + BlueprintPool.Add(EditorData->Blueprint); + } + if (EditorData->BlueprintInstance) + { + EditorData->BlueprintInstance->MarkPendingKill(); + } + + EditorData->GeneratorObject = nullptr; + EditorData->Parameters = {}; + EditorData->Blueprint = nullptr; + EditorData->BlueprintInstance = nullptr; + } + else + { + EditorData = NewObject(); + Picker.EditorData = EditorData; + } + + UBlueprint* Blueprint = nullptr; + if (BlueprintPool.Num() > 0) + { + Blueprint = BlueprintPool.Pop(); + } + if (!Blueprint) + { + Blueprint = NewObject(GetTransientPackage(), NAME_None, RF_Transient); + } + + Blueprint->NewVariables.Reset(); + FBlueprintEditorUtils::RemoveGeneratedClasses(Blueprint); + + { + Blueprint->ParentClass = UObject::StaticClass(); + + for (auto& Parameter : Parameters) + { + FBlueprintEditorUtils::AddMemberVariable(Blueprint, Parameter.Id, GetParameterPinType(Parameter.Type)); + } + + for (auto& Variable : Blueprint->NewVariables) + { + auto* Parameter = NameToParameter.Find(Variable.VarName); + if (!ensure(Parameter)) continue; + + if (!Parameter->Category.IsEmpty()) + { + Variable.Category = FText::FromString(Parameter->Category); + } + + Variable.MetaDataArray.Add(FBPVariableMetaDataEntry(TEXT("Tooltip"), Parameter->ToolTip + "\n\nUnique Name: " + Parameter->Id.ToString())); + + for (auto& It : Parameter->MetaData) + { + Variable.MetaDataArray.Add(FBPVariableMetaDataEntry(It.Key, It.Value)); + } + } + + FKismetEditorUtilities::CompileBlueprint(Blueprint, + EBlueprintCompileOptions::SkipGarbageCollection | + EBlueprintCompileOptions::BatchCompile | + EBlueprintCompileOptions::SkipFiBSearchMetaUpdate); + + // Fixup the defaults on the CDO + // They're ignored when set on the variables above + for (TFieldIterator It(Blueprint->GeneratedClass); It; ++It) + { + auto* Property = *It; + + auto* Parameter = NameToParameter.Find(Property->GetFName()); + if (!ensure(Parameter)) continue; + + auto* CDO = Blueprint->GeneratedClass->GetDefaultObject(); + Property->ImportText(*Parameter->DefaultValue, It->ContainerPtrToValuePtr(CDO), PPF_None, CDO); + } + } + + EditorData->GeneratorObject = Picker.GetObject(); + EditorData->Parameters = Parameters; + EditorData->Blueprint = Blueprint; + EditorData->BlueprintInstance = NewObject(GetTransientPackage(), Blueprint->GeneratedClass, NAME_None, RF_Transient | RF_Transactional); + } + + auto* BlueprintInstance = EditorData->BlueprintInstance; + + // Apply the overrides + for (TFieldIterator It(BlueprintInstance->GetClass()); It; ++It) + { + auto* Property = *It; + + auto* Value = Picker.Parameters.Find(Property->GetFName()); + if (!Value) continue; + + Property->ImportText(**Value, It->ContainerPtrToValuePtr(BlueprintInstance), PPF_None, BlueprintInstance); + } + + FCoreUObjectDelegates::OnObjectPropertyChanged.Add(MakeWeakPtrDelegate(PropertyHandle, + [=, &Picker, Handle = &PropertyHandle.Get()](UObject* InObject, FPropertyChangedEvent& PropertyChangedEvent) + { + if (BlueprintInstance != InObject) return; + + if (GIsTransacting) + { + // OnObjectPropertyChanged is called when undoing + return; + } + + if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) + { + return; + } + + if (!ensure(Handle) || !ensure(IsValid(BlueprintInstance))) + { + return; + } + + FScopedTransaction Transaction(TEXT("VoxelGeneratorParameters"), VOXEL_LOCTEXT("Edit Generator Parameters"), BlueprintInstance); + + Handle->NotifyPreChange(); + + for (TFieldIterator It(BlueprintInstance->GetClass()); It; ++It) + { + auto* Property = *It; + + auto* Parameter = NameToParameter.Find(Property->GetFName()); + if (!ensure(Parameter)) continue; + + FString Value; + Property->ExportTextItem(Value, It->ContainerPtrToValuePtr(BlueprintInstance), nullptr, BlueprintInstance, PPF_None); + + if (Parameter->DefaultValue == Value) + { + Picker.Parameters.Remove(Parameter->Id); + } + else + { + Picker.Parameters.Add(Parameter->Id, Value); + } + } + + Handle->NotifyPostChange(); + })); + + { + const auto ButtonHBox = SNew(SHorizontalBox); + + ChildBuilder.AddCustomRow({}) + .ValueContent() + .HAlign(HAlign_Fill) + .MaxDesiredWidth(TOptional()) // This is needed to not be clipped by the engine + [ + ButtonHBox + ]; + + ButtonHBox->AddSlot() + .Padding(0.f, 0.f, 4.f, 0.f) + [ + SNew(SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .ToolTipText(VOXEL_LOCTEXT("Refresh the parameters list")) + .OnClicked_Lambda([&Picker, Utilities = MakeWeakPtr(CustomizationUtils.GetPropertyUtilities())]() + { + // Force blueprint recompile + if (auto* EditorData = Cast(Picker.EditorData)) + { + EditorData->GeneratorObject = nullptr; + } + if (auto Pinned = Utilities.Pin()) Pinned->ForceRefresh(); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Refresh")) + ] + ]; + + ButtonHBox->AddSlot() + .Padding(0.f, 0.f, 4.f, 0.f) + [ + SNew(SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .ToolTipText(VOXEL_LOCTEXT("Reset all parameters to their default values")) + .OnClicked_Lambda([&Picker, PropertyHandle, Utilities = MakeWeakPtr(CustomizationUtils.GetPropertyUtilities())]() + { + { + FScopedTransaction Transaction(TEXT("VoxelGeneratorParameters"), VOXEL_LOCTEXT("Reset"), nullptr); + PropertyHandle->NotifyPreChange(); + Picker.Parameters.Reset(); + // Force blueprint recompile + if (auto* EditorData = Cast(Picker.EditorData)) + { + EditorData->GeneratorObject = nullptr; + } + PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + } + + if (auto Pinned = Utilities.Pin()) Pinned->ForceRefresh(); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Clear")) + ] + ]; + + if (Picker.IsObject()) + { + ButtonHBox->AddSlot() + .Padding(0.f, 0.f, 4.f, 0.f) + [ + SNew(SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .ToolTipText(VOXEL_LOCTEXT("Store the current parameters in the object as the new defaults. If it's a voxel graph, will change the parameter nodes values")) + .OnClicked_Lambda([&Picker, PropertyHandle, Utilities = MakeWeakPtr(CustomizationUtils.GetPropertyUtilities())]() + { + auto* Generator = Picker.GetGenerator(); + check(Generator); + + { + FScopedTransaction Transaction(TEXT("VoxelGeneratorParameters"), VOXEL_LOCTEXT("Set Defaults"), Generator); + PropertyHandle->NotifyPreChange(); + + Generator->Modify(); + Generator->ApplyParameters(Picker.Parameters); + + Picker.Parameters.Reset(); + + PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + } + + if (auto Pinned = Utilities.Pin()) Pinned->ForceRefresh(); + + return FReply::Handled(); + }) + .IsEnabled_Lambda([&Picker]() + { + return Picker.Parameters.Num() > 0 && Picker.GetGenerator(); + }) + [ + SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(VOXEL_LOCTEXT("Set Defaults")) + ] + ]; + } + } + + // ChildBuilder.AddExternalObjects is broken, so add properties manually + for (auto& CategoryIt : CategoriesToParameters) + { + if (CategoryIt.Key.IsNone()) + { + for (auto& Parameter : CategoryIt.Value) + { + ChildBuilder.AddExternalObjectProperty({ BlueprintInstance }, Parameter.Id UE_24_ONLY(, FAddPropertyParams())); + } + } + else + { + auto Builder = MakeShared(CategoryIt.Value[0].Category, BlueprintInstance); + for (auto& Parameter : CategoryIt.Value) + { + Builder->PropertyNames.Add(Parameter.Id); + } + ChildBuilder.AddCustomBuilder(Builder); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelGeneratorPicker& FVoxelGeneratorPickerCustomization::GetPicker(IPropertyHandle& Handle) +{ + void* Address = nullptr; + if (!ensure(Handle.GetValueData(Address) == FPropertyAccess::Success) || !ensure(Address)) + { + static FVoxelGeneratorPicker Static; + return Static; + } + + return *static_cast(Address); +} + +FEdGraphPinType FVoxelGeneratorPickerCustomization::GetParameterPinType(const FVoxelGeneratorParameterType& ParameterType) +{ + FEdGraphPinType BaseType = FEdGraphPinType::GetPinTypeForTerminalType(GetParameterTerminalPinType(ParameterType)); + + switch (ParameterType.ContainerType) + { + default: ensure(false); + case EVoxelGeneratorParameterContainerType::None: + { + BaseType.ContainerType = EPinContainerType::None; + return BaseType; + } + case EVoxelGeneratorParameterContainerType::Array: + { + BaseType.ContainerType = EPinContainerType::Array; + return BaseType; + } + case EVoxelGeneratorParameterContainerType::Set: + { + BaseType.ContainerType = EPinContainerType::Set; + return BaseType; + } + case EVoxelGeneratorParameterContainerType::Map: + { + BaseType.ContainerType = EPinContainerType::Map; + BaseType.PinValueType = GetParameterTerminalPinType(ParameterType.ValueType); + return BaseType; + } + } +} + +FEdGraphTerminalType FVoxelGeneratorPickerCustomization::GetParameterTerminalPinType(const FVoxelGeneratorParameterTerminalType& ParameterType) +{ + const auto Make = [](FName TerminalCategory, FName TerminalSubCategory, TWeakObjectPtr TerminalSubCategoryObject) + { + FEdGraphTerminalType Result; + Result.TerminalCategory = TerminalCategory; + Result.TerminalSubCategory = TerminalSubCategory; + Result.TerminalSubCategoryObject = TerminalSubCategoryObject; + return Result; + }; + + switch (ParameterType.PropertyType) + { + default: ensure(false); return FEdGraphTerminalType(); + case EVoxelGeneratorParameterPropertyType::Float: + { + return Make(UEdGraphSchema_K2::PC_Float, NAME_None, nullptr); + } + case EVoxelGeneratorParameterPropertyType::Int: + { + return Make(UEdGraphSchema_K2::PC_Int, NAME_None, nullptr); + } + case EVoxelGeneratorParameterPropertyType::Bool: + { + return Make(UEdGraphSchema_K2::PC_Boolean, NAME_None, nullptr); + } + case EVoxelGeneratorParameterPropertyType::Object: + { + auto* Class = FindObject(ANY_PACKAGE, *ParameterType.PropertyClass.ToString()); + ensure(Class); + return Make(UEdGraphSchema_K2::PC_Object, ParameterType.PropertyClass, Class); + } + case EVoxelGeneratorParameterPropertyType::Struct: + { + auto* Struct = FindObject(ANY_PACKAGE, *ParameterType.PropertyClass.ToString()); + ensure(Struct); + return Make(UEdGraphSchema_K2::PC_Struct, ParameterType.PropertyClass, Struct); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.h new file mode 100644 index 00000000..99f7fea3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.h @@ -0,0 +1,64 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" +#include "VoxelGeneratorPickerCustomization.generated.h" + +class SWidget; +class IPropertyHandle; +struct FEdGraphPinType; +struct FEdGraphTerminalType; +struct FVoxelGeneratorPicker; +enum class EVoxelGeneratorPickerType : uint8; + +UCLASS() +class UVoxelGeneratorPickerEditorData : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY() + UObject* GeneratorObject = nullptr; + + UPROPERTY() + TArray Parameters; + + UPROPERTY() + UBlueprint* Blueprint = nullptr; + + UPROPERTY() + UObject* BlueprintInstance = nullptr; +}; + +UCLASS() +class UVoxelGeneratorPickerBlueprintPool : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY() + TArray Blueprints; +}; + +class FVoxelGeneratorPickerCustomization : public IPropertyTypeCustomization +{ +public: + FVoxelGeneratorPickerCustomization() = default; + + //~ Begin IPropertyTypeCustomization Interface + virtual void CustomizeHeader(TSharedRef PropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + //~ End IPropertyTypeCustomization Interface + +private: + EVoxelGeneratorPickerType PickerType{}; + TArray> ComboBoxArray; + +private: + static FVoxelGeneratorPicker& GetPicker(IPropertyHandle& Handle); + static FEdGraphPinType GetParameterPinType(const FVoxelGeneratorParameterType& ParameterType); + static FEdGraphTerminalType GetParameterTerminalPinType(const FVoxelGeneratorParameterTerminalType& ParameterType); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.cpp new file mode 100644 index 00000000..e342cafc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.cpp @@ -0,0 +1,31 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelGraphOutputCustomization.h" +#include "VoxelGraphOutputs.h" +#include "VoxelMinimal.h" + +#include "PropertyHandle.h" +#include "DetailWidgetRow.h" +#include "Widgets/Layout/SSpacer.h" + +#define GET_CHILD_PROPERTY(PropertyHandle, Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() + +void FVoxelGraphOutputCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + auto NameHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelGraphOutput, Name); + auto CategoryHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelGraphOutput, Category); + + HeaderRow + .NameContent() + .MinDesiredWidth(125.0f) + .MaxDesiredWidth(125.0f) + [ + NameHandle->CreatePropertyValueWidget() + ] + .ValueContent() + [ + CategoryHandle->CreatePropertyValueWidget() + ]; +} + +#undef GET_CHILD_PROPERTY \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.h new file mode 100644 index 00000000..9131a83a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class FVoxelGraphOutputCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override {} +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.cpp new file mode 100644 index 00000000..46c633f8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.cpp @@ -0,0 +1,31 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelInt32IntervalCustomization.h" +#include "VoxelMinimal.h" + +void FVoxelInt32IntervalCustomization::GetSortedChildren(TSharedRef StructPropertyHandle, TArray>& OutChildren) +{ + TSharedPtr IntervalChildren[2]; + + uint32 NumChildren; + StructPropertyHandle->GetNumChildren(NumChildren); + + for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) + { + const TSharedRef ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef(); + const FName PropertyName = ChildHandle->GetProperty()->GetFName(); + + if (PropertyName == STATIC_FNAME("Min")) + { + IntervalChildren[0] = ChildHandle; + } + else + { + check(PropertyName == STATIC_FNAME("Max")); + IntervalChildren[1] = ChildHandle; + } + } + + OutChildren.Add(IntervalChildren[0].ToSharedRef()); + OutChildren.Add(IntervalChildren[1].ToSharedRef()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.h new file mode 100644 index 00000000..7a7f1f00 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Customizations/MathStructCustomizations.h" + +class FVoxelInt32IntervalCustomization : public FMathStructCustomization +{ +public: + virtual void GetSortedChildren(TSharedRef StructPropertyHandle, TArray>& OutChildren) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.cpp new file mode 100644 index 00000000..25818133 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.cpp @@ -0,0 +1,76 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelLandscapeImporterDetails.h" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "Factories/VoxelHeightmapAssetFactory.h" +#include "VoxelImporters/VoxelLandscapeImporter.h" + +#include "Landscape.h" + +#include "DetailLayoutBuilder.h" +#include "Misc/MessageDialog.h" + +void FVoxelLandscapeImporterDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + if (Objects.Num() != 1) + { + return; + } + Importer = CastChecked(Objects[0].Get()); + + auto LayerInfos = DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelLandscapeImporter, LayerInfos))->AsArray();; + uint32 Num = 0; + LayerInfos->GetNumElements(Num); + for (uint32 Index = 0; Index < Num; Index++) + { + auto Handle = LayerInfos->GetElement(Index); + switch (Importer->MaterialConfig) + { + case EVoxelHeightmapImporterMaterialConfig::RGB: + DetailLayout.HideProperty(Handle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelLandscapeImporterLayerInfo, Index))); + break; + case EVoxelHeightmapImporterMaterialConfig::FourWayBlend: + case EVoxelHeightmapImporterMaterialConfig::FiveWayBlend: + case EVoxelHeightmapImporterMaterialConfig::SingleIndex: + case EVoxelHeightmapImporterMaterialConfig::MultiIndex: + DetailLayout.HideProperty(Handle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelLandscapeImporterLayerInfo, Layer))); + break; + default: + check(false); + break; + } + } + + FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&DetailLayout]() + { + DetailLayout.ForceRefreshDetails(); + }); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelLandscapeImporter, MaterialConfig))->SetOnPropertyValueChanged(RefreshDelegate); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelLandscapeImporter, LayerInfos))->SetOnPropertyValueChanged(RefreshDelegate); + + FVoxelEditorUtilities::AddButtonToCategory(DetailLayout, + "Create VoxelLandscapeAsset from Landscape", + VOXEL_LOCTEXT("Create"), + VOXEL_LOCTEXT("Create From Landscape"), + VOXEL_LOCTEXT("Create"), + false, + FOnClicked::CreateSP(this, &FVoxelLandscapeImporterDetails::OnCreateFromLandscape), + TAttribute::Create(TAttribute::FGetter::CreateLambda([Importer = Importer]() { return Importer.IsValid() && Importer->Landscape; }))); +} + +FReply FVoxelLandscapeImporterDetails::OnCreateFromLandscape() +{ + auto* Factory = NewObject(); + Factory->MaterialConfig = Importer->MaterialConfig; + Factory->LayerInfos = Importer->LayerInfos; + Factory->Components = Importer->Landscape->LandscapeComponents; + Factory->ActorLocation = Importer->Landscape->GetActorLocation(); + Factory->AssetName = Importer->Landscape->GetName(); + + FVoxelEditorUtilities::CreateAssetWithDialog(UVoxelHeightmapAssetFloat::StaticClass(), Factory); + + return FReply::Handled(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.h new file mode 100644 index 00000000..ba8f9605 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.h @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" + +// See sky light details in the engine code + +class AVoxelLandscapeImporter; + +class FVoxelLandscapeImporterDetails : public IDetailCustomization +{ +public: + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + + FReply OnCreateFromLandscape(); + +private: + /** The selected landscape modifier */ + TWeakObjectPtr Importer; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.cpp new file mode 100644 index 00000000..d0262cae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.cpp @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMeshImporterDetails.h" +#include "VoxelImporters/VoxelMeshImporter.h" + +#include "Factories/VoxelDataAssetFactory.h" +#include "VoxelAssets/VoxelDataAsset.h" + +#include "VoxelEditorDetailsUtilities.h" +#include "DetailLayoutBuilder.h" + +void FVoxelMeshImporterDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + FVoxelEditorUtilities::EnableRealtime(); + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + if (Objects.Num() != 1) + { + return; + } + MeshImporter = CastChecked(Objects[0].Get());; + + FVoxelEditorUtilities::AddButtonToCategory(DetailLayout, + "Create VoxelDataAsset from Mesh", + VOXEL_LOCTEXT("Create"), + VOXEL_LOCTEXT("Create From Mesh"), + VOXEL_LOCTEXT("Create"), + false, + FOnClicked::CreateSP(this, &FVoxelMeshImporterDetails::OnCreateFromMesh), + TAttribute::Create(TAttribute::FGetter::CreateLambda([Importer = MeshImporter]() { return Importer.IsValid() && Importer->StaticMesh; }))); +} + +FReply FVoxelMeshImporterDetails::OnCreateFromMesh() +{ + auto* Factory = NewObject(); + Factory->MeshImporter = MeshImporter.Get(); + + FVoxelEditorUtilities::CreateAssetWithDialog(UVoxelDataAsset::StaticClass(), Factory); + + return FReply::Handled(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.h new file mode 100644 index 00000000..0b8c91ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.h @@ -0,0 +1,23 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" + +class AVoxelMeshImporter; + +// See sky light details in the engine code + +class FVoxelMeshImporterDetails : public IDetailCustomization +{ +public: + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + + FReply OnCreateFromMesh(); + +private: + TWeakObjectPtr MeshImporter; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.cpp new file mode 100644 index 00000000..9e8122c1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.cpp @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMeshSpawnerBaseDetails.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" + +#include "DetailLayoutBuilder.h" + +void FVoxelMeshSpawnerBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + DetailLayout.EditCategory("General Settings", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Actor Settings", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Instance Settings", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Placement", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Placement - Offset", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Placement - Scale", FText(), ECategoryPriority::Important); + DetailLayout.EditCategory("Placement - Rotation", FText(), ECategoryPriority::Important); + + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + if (Objects.Num() == 1) + { + auto* MeshSpawner = CastChecked(Objects[0]); + switch (MeshSpawner->InstanceRandom) + { + case EVoxelMeshSpawnerInstanceRandom::Random: + case EVoxelMeshSpawnerInstanceRandom::VoxelMaterial: + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(UVoxelMeshSpawnerBase, ColorOutputName)); + break; + case EVoxelMeshSpawnerInstanceRandom::ColorOutput: + break; + default: + ensure(false); + break; + } + + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&DetailLayout]() + { + DetailLayout.ForceRefreshDetails(); + }); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(UVoxelMeshSpawnerBase, InstanceRandom))->SetOnPropertyValueChanged(RefreshDelegate); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.h new file mode 100644 index 00000000..6f8ea5d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" +#include "Widgets/Input/SButton.h" + +class FVoxelMeshSpawnerBaseDetails : public IDetailCustomization +{ +public: + FVoxelMeshSpawnerBaseDetails() = default; + +private: + /** IDetailCustomization interface */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.cpp new file mode 100644 index 00000000..3bc22611 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.cpp @@ -0,0 +1,483 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelPaintMaterialCustomization.h" + +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelEditorDetailsUtilities.h" + +#include "Materials/MaterialInterface.h" +#include "PropertyHandle.h" +#include "DetailWidgetRow.h" +#include "IDetailChildrenBuilder.h" +#include "IPropertyUtilities.h" +#include "IDetailGroup.h" +#include "IDetailPropertyRow.h" +#include "Widgets/Input/SComboBox.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" + +#define GET_CHILD_PROPERTY(PropertyHandle, Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() + +void FVoxelPaintMaterialCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ +} + +void FVoxelPaintMaterialCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + const auto& PaintMaterialEnum = *StaticEnum(); + const auto& MaterialConfigEnum = *StaticEnum(); + + TypeHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, Type); + const auto RestrictTypeHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, bRestrictType); + const auto MaterialConfigToRestrictToHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, MaterialConfigToRestrictTo); + + FString TypeString; + FString MaterialConfigToRestrictToString; + + EVoxelPaintMaterialType Type; + bool bRestrictType = false; + EVoxelMaterialConfig MaterialConfigToRestrictTo; + + { + if (!ensure(TypeHandle->GetValueAsFormattedString(TypeString) == FPropertyAccess::Success)) return; + if (!ensure(RestrictTypeHandle->GetValue(bRestrictType) == FPropertyAccess::Success)) return; + if (!ensure(MaterialConfigToRestrictToHandle->GetValueAsFormattedString(MaterialConfigToRestrictToString) == FPropertyAccess::Success)) return; + + const int64 TypeValue = PaintMaterialEnum.GetValueByNameString(TypeString); + if (!ensure(TypeValue != -1)) return; + Type = EVoxelPaintMaterialType(TypeValue); + + const int64 MaterialConfigValue = MaterialConfigEnum.GetValueByNameString(MaterialConfigToRestrictToString); + if (!ensure(MaterialConfigValue != -1)) return; + MaterialConfigToRestrictTo = EVoxelMaterialConfig(MaterialConfigValue); + } + + TSharedPtr TypeWidget; + if (bRestrictType) + { + OptionsSource.Reset(); + if (MaterialConfigToRestrictTo == EVoxelMaterialConfig::RGB) + { + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::Color)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::FiveWayBlend)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::UV)); + } + else if (MaterialConfigToRestrictTo == EVoxelMaterialConfig::SingleIndex) + { + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::Color)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::FiveWayBlend)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::SingleIndex)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::UV)); + } + else + { + ensure(MaterialConfigToRestrictTo == EVoxelMaterialConfig::MultiIndex); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::MultiIndex)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::MultiIndexWetness)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::MultiIndexRaw)); + OptionsSource.Add(MakeShared(EVoxelPaintMaterialType::UV)); + } + + const auto SearchTypePredicate = [&](auto& Ptr) { return *Ptr == Type; }; + + if (!OptionsSource.ContainsByPredicate(SearchTypePredicate)) + { + switch (MaterialConfigToRestrictTo) + { + case EVoxelMaterialConfig::RGB: Type = EVoxelPaintMaterialType::FiveWayBlend; break; + case EVoxelMaterialConfig::SingleIndex: Type = EVoxelPaintMaterialType::SingleIndex; break; + case EVoxelMaterialConfig::MultiIndex: Type = EVoxelPaintMaterialType::MultiIndex; break; + default: ensure(false); + } + + TypeHandle->SetValueFromFormattedString(PaintMaterialEnum.GetNameStringByValue(int64(Type))); + } + + auto* TypeOptionSourcePtr = OptionsSource.FindByPredicate(SearchTypePredicate); + check(TypeOptionSourcePtr); + + ComboBoxText = FVoxelEditorUtilities::CreateText(PaintMaterialEnum.GetDisplayNameTextByValue(int64(Type))); + + TypeWidget = SNew(SComboBox>) + .IsEnabled_Lambda([TypeHandle = TWeakPtr(TypeHandle)](){ return TypeHandle.IsValid() && !TypeHandle.Pin()->IsEditConst(); }) + .OptionsSource(&OptionsSource) + .OnSelectionChanged(this, &FVoxelPaintMaterialCustomization::HandleComboBoxSelectionChanged) + .OnGenerateWidget_Lambda([&](TSharedPtr InValue) + { + return FVoxelEditorUtilities::CreateText(PaintMaterialEnum.GetDisplayNameTextByValue(int64(*InValue))); + }) + .InitiallySelectedItem(*TypeOptionSourcePtr) + [ + ComboBoxText.ToSharedRef() + ]; + } + else + { + TypeWidget = TypeHandle->CreatePropertyValueWidget(); + } + + // Make sure to do that after possibly editing the type + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([PropertyUtilities = MakeWeakPtr(CustomizationUtils.GetPropertyUtilities())]() + { + if (PropertyUtilities.IsValid()) + { + PropertyUtilities.Pin()->ForceRefresh(); + } + }); + TypeHandle->SetOnPropertyValueChanged(RefreshDelegate); + + const bool bShowOnlyInnerProperties = PropertyHandle->HasMetaData(STATIC_FNAME("ShowOnlyInnerProperties")); + + IDetailGroup* Group = nullptr; + if (bShowOnlyInnerProperties) + { + ChildBuilder.AddCustomRow(VOXEL_LOCTEXT("Type")) + .NameContent() + [ + TypeHandle->CreatePropertyNameWidget() + ] + .ValueContent() + [ + TypeWidget.ToSharedRef() + ]; + } + else + { + Group = &ChildBuilder.AddGroup(TEXT("Paint Material Type"), PropertyHandle->GetPropertyDisplayName()); + Group->HeaderRow() + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + [ + TypeWidget.ToSharedRef() + ]; + } + + const auto AddProperty = [&](const auto& InPropertyHandle) -> IDetailPropertyRow& + { + if (bShowOnlyInnerProperties) + { + return ChildBuilder.AddProperty(InPropertyHandle); + } + else + { + return Group->AddPropertyRow(InPropertyHandle); + } + }; + + if (Type == EVoxelPaintMaterialType::Color) + { + const auto ColorHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, Color); + const auto UseLinearColorHandle = GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bUseLinearColor); + + UseLinearColorHandle->SetOnPropertyValueChanged(RefreshDelegate); + + auto& UseLinearColor = AddProperty(UseLinearColorHandle); + + bool bUseLinearColor = false; + if (UseLinearColorHandle->GetValue(bUseLinearColor) == FPropertyAccess::Success) + { + if (bUseLinearColor) + { + auto& LinearColor = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, LinearColor)); + } + else + { + auto& Color = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, Color)); + } + } + else + { + auto& LinearColor = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, LinearColor)); + auto& Color = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, Color)); + } + + auto& PaintR = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bPaintR)); + auto& PaintG = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bPaintG)); + auto& PaintB = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bPaintB)); + auto& PaintA = AddProperty(GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bPaintA)); + + if (bRestrictType && MaterialConfigToRestrictTo == EVoxelMaterialConfig::SingleIndex) + { + PaintA.IsEnabled(false); + PaintA.ToolTip(VOXEL_LOCTEXT("Disabled in Single Index, as it's used to store the index")); + GET_CHILD_PROPERTY(ColorHandle, FVoxelPaintMaterialColor, bPaintA)->SetValue(false); + } + } + else if (Type == EVoxelPaintMaterialType::FiveWayBlend) + { + const auto FiveWayBlendHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, FiveWayBlend); + + auto& Channel = AddProperty(GET_CHILD_PROPERTY(FiveWayBlendHandle, FVoxelPaintMaterialFiveWayBlend, Channel)); + auto& TargetValue = AddProperty(GET_CHILD_PROPERTY(FiveWayBlendHandle, FVoxelPaintMaterialFiveWayBlend, TargetValue)); + auto& LockedChannels = AddProperty(GET_CHILD_PROPERTY(FiveWayBlendHandle, FVoxelPaintMaterialFiveWayBlend, LockedChannels)); + auto& FourWayBlend = AddProperty(GET_CHILD_PROPERTY(FiveWayBlendHandle, FVoxelPaintMaterialFiveWayBlend, bFourWayBlend)); + + if (bRestrictType && MaterialConfigToRestrictTo == EVoxelMaterialConfig::SingleIndex) + { + FourWayBlend.IsEnabled(false); + FourWayBlend.ToolTip(VOXEL_LOCTEXT("Always enabled in Single Index, as alpha is used to store the index")); + GET_CHILD_PROPERTY(FiveWayBlendHandle, FVoxelPaintMaterialFiveWayBlend, bFourWayBlend)->SetValue(true); + } + } + else if (Type == EVoxelPaintMaterialType::SingleIndex) + { + const auto SingleIndexHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, SingleIndex); + + auto& Channel = AddProperty(GET_CHILD_PROPERTY(SingleIndexHandle, FVoxelPaintMaterialSingleIndex, Channel)); + } + else if (Type == EVoxelPaintMaterialType::MultiIndex) + { + const auto MultiIndexHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, MultiIndex); + + auto& Channel = AddProperty(GET_CHILD_PROPERTY(MultiIndexHandle, FVoxelPaintMaterialMultiIndex, Channel)); + auto& TargetValue = AddProperty(GET_CHILD_PROPERTY(MultiIndexHandle, FVoxelPaintMaterialMultiIndex, TargetValue)); + auto& LockedChannels = AddProperty(GET_CHILD_PROPERTY(MultiIndexHandle, FVoxelPaintMaterialMultiIndex, LockedChannels)); + } + else if (Type == EVoxelPaintMaterialType::MultiIndexWetness) + { + const auto MultiIndexWetnessHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, MultiIndexWetness); + + auto& TargetValue = AddProperty(GET_CHILD_PROPERTY(MultiIndexWetnessHandle, FVoxelPaintMaterialMultiIndexWetness, TargetValue)); + } + else if (Type == EVoxelPaintMaterialType::MultiIndexRaw) + { + const auto MultiIndexRawHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, MultiIndexRaw); + + auto& Channel0 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Channel0)); + auto& Strength0 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Strength0)); + auto& Channel1 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Channel1)); + auto& Strength1 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Strength1)); + auto& Channel2 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Channel2)); + auto& Strength2 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Strength2)); + auto& Channel3 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Channel3)); + auto& Strength3 = AddProperty(GET_CHILD_PROPERTY(MultiIndexRawHandle, FVoxelPaintMaterialMultiIndexRaw, Strength3)); + } + else if (Type == EVoxelPaintMaterialType::UV) + { + const auto UVHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial, UV); + auto& Channel = AddProperty(GET_CHILD_PROPERTY(UVHandle, FVoxelPaintMaterialUV, Channel)); + auto& UV = AddProperty(GET_CHILD_PROPERTY(UVHandle, FVoxelPaintMaterialUV, UV)); + auto& PaintU = AddProperty(GET_CHILD_PROPERTY(UVHandle, FVoxelPaintMaterialUV, bPaintU)); + auto& PaintV = AddProperty(GET_CHILD_PROPERTY(UVHandle, FVoxelPaintMaterialUV, bPaintV)); + + if (bRestrictType && MaterialConfigToRestrictTo == EVoxelMaterialConfig::MultiIndex) + { + Channel.ToolTip(VOXEL_LOCTEXT("In multi index, the first 2 UV channels are used to store the indices")); + const auto Handle = GET_CHILD_PROPERTY(UVHandle, FVoxelPaintMaterialUV, Channel); + const auto FixupChannel = FSimpleDelegate::CreateLambda([=]() + { + int32 Value = 0; + if (!ensure(Handle->GetValue(Value) == FPropertyAccess::Success)) + { + return; + } + if (Value < 2) + { + Handle->SetValue(2); + } + }); + FixupChannel.Execute(); + Handle->SetOnPropertyValueChanged(FixupChannel); + } + } + else + { + ensure(false); + } +} + +void FVoxelPaintMaterialCustomization::HandleComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) const +{ + if (ensure(NewSelection.IsValid()) && ensure(TypeHandle.IsValid()) && ensure(ComboBoxText.IsValid())) + { + const auto& Enum = *StaticEnum(); + const int64 Value = int64(*NewSelection); + TypeHandle->SetValueFromFormattedString(Enum.GetNameStringByValue(Value)); + ComboBoxText->SetText(Enum.GetDisplayNameTextByValue(Value)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelPaintMaterial_MaterialCollectionChannelCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + TSharedPtr CollectionHandle; + { + auto ParentHandle = PropertyHandle; + while (!CollectionHandle.IsValid() && ensure(ParentHandle->GetParentHandle().IsValid())) + { + ParentHandle = ParentHandle->GetParentHandle().ToSharedRef(); + CollectionHandle = ParentHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelPaintMaterial, PreviewMaterialCollection)); + } + } + if (!ensure(CollectionHandle.IsValid())) + { + return; + } + + const auto ChannelHandle = GET_CHILD_PROPERTY(PropertyHandle, FVoxelPaintMaterial_MaterialCollectionChannel, Channel); + + const auto Thumbnail = MakeShared(nullptr, 64, 64, CustomizationUtils.GetThumbnailPool()); + + const auto SelectedMaterial = MakeShared(); + const auto AssetsToMaterials = MakeShared, UVoxelMaterialCollectionBase::FMaterialInfo>>(); + const auto IndicesToMaterials = MakeShared>(); + + const auto OnChanged = FSimpleDelegate::CreateLambda([=]() + { + UObject* Collection; + CollectionHandle->GetValue(Collection); + UVoxelMaterialCollectionBase* MaterialCollection = Cast(Collection); + if (!MaterialCollection) + { + return; + } + + AssetsToMaterials->Reset(); + for (auto& MaterialInfo : MaterialCollection->GetMaterials()) + { + if (MaterialInfo.Material.IsValid() && MaterialInfo.Name.IsNone()) + { + // Fixup name if needed + MaterialInfo.Name = MaterialInfo.Material->GetFName(); + } + AssetsToMaterials->Add(MaterialInfo.Material, MaterialInfo); + IndicesToMaterials->Add(MaterialInfo.Index, MaterialInfo); + } + + void* Data = nullptr; + if (PropertyHandle->GetValueData(Data) != FPropertyAccess::Success) + { + return; + } + + auto& Channel = *static_cast(Data); + *SelectedMaterial = IndicesToMaterials->FindRef(Channel); + Thumbnail->SetAsset(SelectedMaterial->Material.Get()); + }); + OnChanged.Execute(); + + const auto OnClose = MakeShared(); + + const auto AssetComboButton = SNew(SComboButton) + .ButtonStyle( FEditorStyle::Get(), "PropertyEditor.AssetComboStyle" ) + .ForegroundColor(FEditorStyle::GetColor("PropertyEditor.AssetName.ColorAndOpacity")) + .OnGetMenuContent_Lambda([=]() + { + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + + FSetARFilterDelegate FilterDelegate; + + FAssetPickerConfig PickerConfig; + PickerConfig.SelectionMode = ESelectionMode::Single; + PickerConfig.bAllowDragging = false; + PickerConfig.bAllowNullSelection = false; + PickerConfig.InitialAssetViewType = EAssetViewType::Tile; + PickerConfig.InitialAssetSelection = SelectedMaterial->Material.Get(); + PickerConfig.Filter.ObjectPaths.Add("FAKE"); // Remove all real results, we add our own assets below + PickerConfig.OnGetCustomSourceAssets = FOnGetCustomSourceAssets::CreateLambda([=](const FARFilter& SourceFilter, TArray& AddedAssets) + { + for (auto& It : *AssetsToMaterials) + { + if (It.Key.IsValid()) + { + FAssetData AssetData(It.Key.Get()); + AssetData.AssetName = It.Value.Name; + AddedAssets.Add(AssetData); + } + } + }); + + PickerConfig.OnAssetSelected.BindLambda([=](const FAssetData& AssetData) + { + auto* NewAsset = AssetData.GetAsset(); + if (auto* Info = AssetsToMaterials->Find(NewAsset)) + { + ChannelHandle->SetValue(Info->Index); + OnClose->ExecuteIfBound(); + } + + }); + return + SNew(SBox) + .WidthOverride(300.f) + .HeightOverride(300.f) + [ + ContentBrowserModule.Get().CreateAssetPicker(PickerConfig) + ]; + }) + .IsEnabled(ChannelHandle, &IPropertyHandle::IsEditable) + .ContentPadding(2.0f) + .ButtonContent() + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .FillWidth(1) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .TextStyle( FEditorStyle::Get(), "PropertyEditor.AssetClass" ) + .Font( FEditorStyle::GetFontStyle( "PropertyWindow.NormalFont" ) ) + .Text_Lambda([=]() + { + return FText::FromName(SelectedMaterial->Name); + }) + ] + ]; + + *OnClose = FSimpleDelegate::CreateLambda([=]() { AssetComboButton->SetIsOpen(false); }); + + ChannelHandle->SetOnPropertyValueChanged(OnChanged); + + HeaderRow + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MinDesiredWidth(140.f) + .MaxDesiredWidth(140.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(4.f, 0.f, 4.f, 0.f) + .AutoWidth() + [ + SNew(SBox) + .WidthOverride(64.f) + .HeightOverride(64.f) + [ + Thumbnail->MakeThumbnailWidget() + ] + ] + + SHorizontalBox::Slot() + .Padding(4.f, 0.f, 4.f, 0.f) + .AutoWidth() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Left) + [ + AssetComboButton + ] + + SVerticalBox::Slot() + .Padding(0.f, 4.f, 0.f, 0.f) + .AutoHeight() + .HAlign(HAlign_Left) + [ + SNew(SBox) + .WidthOverride(40.f) + [ + ChannelHandle->CreatePropertyValueWidget() + ] + ] + ] + ]; +} + +#undef GET_CHILD_PROPERTY \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.h new file mode 100644 index 00000000..83e1827d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +enum class EVoxelPaintMaterialType : uint8; +class STextBlock; + +class FVoxelPaintMaterialCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + + private: + TArray> OptionsSource; + TSharedPtr ComboBoxText; + TSharedPtr TypeHandle; + + void HandleComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) const; +}; + +class FVoxelPaintMaterial_MaterialCollectionChannelCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override {} +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.cpp new file mode 100644 index 00000000..2b2a97e7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.cpp @@ -0,0 +1,170 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelSpawnerConfigSpawnerCustomization.h" +#include "VoxelMinimal.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" + +#include "DetailWidgetRow.h" +#include "DetailLayoutBuilder.h" +#include "IDetailChildrenBuilder.h" +#include "IDetailGroup.h" +#include "IPropertyUtilities.h" +#include "PropertyCustomizationHelpers.h" + +#include "Widgets/SBoxPanel.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Layout/SBox.h" + +#define GET_CHILD_PROPERTY_IMPL(PropertyHandle, Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() +#define GET_CHILD_PROPERTY(Class, Property) GET_CHILD_PROPERTY_IMPL(PropertyHandle, Class, Property) + +void FVoxelSpawnerConfigSpawnerCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + const auto& SpawnerTypeEnum = *StaticEnum(); + + const auto TypeHandle = GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, SpawnerType); + + EVoxelSpawnerType Type; + { + FString TypeString; + if (!ensure(TypeHandle->GetValueAsFormattedString(TypeString) == FPropertyAccess::Success)) return; + + const int64 TypeValue = SpawnerTypeEnum.GetValueByNameString(TypeString); + if (!ensure(TypeValue != -1)) return; + + Type = EVoxelSpawnerType(TypeValue); + } + + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&CustomizationUtils]() + { + auto Utilities = CustomizationUtils.GetPropertyUtilities(); + if (Utilities.IsValid()) + { + Utilities->ForceRefresh(); + } + }); + TypeHandle->SetOnPropertyValueChanged(RefreshDelegate); + + IDetailGroup& Group = ChildBuilder.AddGroup(TEXT("Spawner Config"), PropertyHandle->GetPropertyDisplayName()); + Group.HeaderRow() + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SBox) + .MinDesiredWidth(FDetailWidgetRow::DefaultValueMinWidth) + [ + TypeHandle->CreatePropertyValueWidget() + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .Padding(2.0f, 1.0f) + [ + PropertyCustomizationHelpers::MakeInsertDeleteDuplicateButton( + FExecuteAction::CreateLambda([=]() { PropertyHandle->GetParentHandle()->AsArray()->Insert(PropertyHandle->GetIndexInArray()); }), + FExecuteAction::CreateLambda([=]() { PropertyHandle->GetParentHandle()->AsArray()->DeleteItem(PropertyHandle->GetIndexInArray()); }), + FExecuteAction::CreateLambda([=]() { PropertyHandle->GetParentHandle()->AsArray()->DuplicateItem(PropertyHandle->GetIndexInArray()); })) + ] + ]; + + Group.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, Spawner)); + Group.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, Density)); + + if (Type == EVoxelSpawnerType::Height) + { + Group.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, HeightGraphOutputName_HeightOnly)); + } + + Group.AddWidgetRow() + .NameContent() + [ + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, ChunkSize_EditorOnly)->CreatePropertyNameWidget() + ] + .ValueContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SBox) + .MinDesiredWidth(FDetailWidgetRow::DefaultValueMinWidth) + [ + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, ChunkSize_EditorOnly)->CreatePropertyValueWidget() + ] + ] + + SHorizontalBox::Slot() + .Padding(5.f, 0.f, 0.f, 0.f) + .AutoWidth() + [ + SNew(STextBlock) + .Text_Lambda([=]() + { + FText Value; + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, LOD)->GetValueAsDisplayText(Value); + return FText::Format(VOXEL_LOCTEXT("LOD: {0}"), Value); + }) + ] + ]; + + Group.AddWidgetRow() + .NameContent() + [ + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, GenerationDistanceInVoxels_EditorOnly)->CreatePropertyNameWidget() + ] + .ValueContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SBox) + .MinDesiredWidth(FDetailWidgetRow::DefaultValueMinWidth) + [ + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, GenerationDistanceInVoxels_EditorOnly)->CreatePropertyValueWidget() + ] + ] + + SHorizontalBox::Slot() + .Padding(5.f, 0.f, 0.f, 0.f) + .AutoWidth() + [ + SNew(STextBlock) + .Text_Lambda([=]() + { + FText Value; + GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, GenerationDistanceInChunks)->GetValueAsDisplayText(Value); + return FText::Format(VOXEL_LOCTEXT("{0} chunks"), Value); + }) + ] + ]; + + IDetailGroup& AdvancedGroup = Group.AddGroup("Advanced", VOXEL_LOCTEXT("Advanced")); + + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, bSave)); + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, bDoNotDespawn)); + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, Seed)); + + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, RandomGenerator)); + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, Guid)); + + if (Type == EVoxelSpawnerType::Ray) + { + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, DensityMultiplier_RayOnly)); + } + if (Type == EVoxelSpawnerType::Height) + { + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, bComputeDensityFirst_HeightOnly)); + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, bCheckIfFloating_HeightOnly)); + AdvancedGroup.AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerConfigSpawner, bCheckIfCovered_HeightOnly)); + } +} + +#undef GET_CHILD_PROPERTY \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.h new file mode 100644 index 00000000..63130497 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class FVoxelSpawnerConfigSpawnerCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override {} + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.cpp new file mode 100644 index 00000000..0724873f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.cpp @@ -0,0 +1,97 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelSpawnerDensityCustomization.h" +#include "VoxelMinimal.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" + +#include "DetailWidgetRow.h" +#include "DetailLayoutBuilder.h" +#include "IDetailChildrenBuilder.h" +#include "IDetailGroup.h" +#include "IPropertyUtilities.h" + +#define GET_CHILD_PROPERTY(Class, Property) PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(Class, Property)).ToSharedRef() + +void FVoxelSpawnerDensityCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + const auto& DensityTypeEnum = *StaticEnum(); + + const auto TypeHandle = GET_CHILD_PROPERTY(FVoxelSpawnerDensity, Type); + + EVoxelSpawnerDensityType Type; + { + FString TypeString; + if (!ensure(TypeHandle->GetValueAsFormattedString(TypeString) == FPropertyAccess::Success)) return; + + const int64 TypeValue = DensityTypeEnum.GetValueByNameString(TypeString); + if (!ensure(TypeValue != -1)) return; + + Type = EVoxelSpawnerDensityType(TypeValue); + } + + // Make sure to do that after the possible SetValue + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&CustomizationUtils]() + { + auto Utilities = CustomizationUtils.GetPropertyUtilities(); + if (Utilities.IsValid()) + { + Utilities->ForceRefresh(); + } + }); + TypeHandle->SetOnPropertyValueChanged(RefreshDelegate); + + IDetailGroup* Group = &ChildBuilder.AddGroup(TEXT("Spawner Density Type"), PropertyHandle->GetPropertyDisplayName()); + Group->HeaderRow() + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + [ + TypeHandle->CreatePropertyValueWidget() + ]; + + switch (Type) + { + default: ensure(false); + case EVoxelSpawnerDensityType::Constant: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, Constant)); + break; + } + case EVoxelSpawnerDensityType::GeneratorOutput: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, GeneratorOutputName)); + break; + } + case EVoxelSpawnerDensityType::MaterialRGBA: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, RGBAChannel)); + break; + } + case EVoxelSpawnerDensityType::MaterialUVs: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, UVChannel)); + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, UVAxis)); + break; + } + case EVoxelSpawnerDensityType::MaterialFiveWayBlend: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, FiveWayBlendChannel)); + break; + } + case EVoxelSpawnerDensityType::SingleIndex: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, SingleIndexChannels)); + break; + } + case EVoxelSpawnerDensityType::MultiIndex: + { + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, MultiIndexChannels)); + break; + } + } + Group->AddPropertyRow(GET_CHILD_PROPERTY(FVoxelSpawnerDensity, Transform)); +} + +#undef GET_CHILD_PROPERTY \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.h new file mode 100644 index 00000000..3a520bc9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.h @@ -0,0 +1,15 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class STextBlock; + +class FVoxelSpawnerDensityCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override {} + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.cpp new file mode 100644 index 00000000..107ace46 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.cpp @@ -0,0 +1,120 @@ +// Copyright 2020 Phyronnaz + +#include "Details/VoxelSpawnerOutputNameCustomization.h" +#include "VoxelMinimal.h" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" +#include "VoxelSpawners/VoxelSpawnerOutputsConfig.h" + +#include "DetailWidgetRow.h" +#include "DetailLayoutBuilder.h" +#include "IPropertyUtilities.h" +#include "Widgets/Input/SComboBox.h" + +void FVoxelSpawnerOutputNameCustomization::CustomizeHeader( + const TSharedRef PropertyHandle, + FDetailWidgetRow& HeaderRow, + IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + NameHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(FVoxelSpawnerOutputName, Name)).ToSharedRef(); + + TSharedPtr OutputsHandle; + { + auto ParentHandle = PropertyHandle; + while (!OutputsHandle.IsValid() && ensure(ParentHandle->GetParentHandle().IsValid())) + { + ParentHandle = ParentHandle->GetParentHandle().ToSharedRef(); + OutputsHandle = ParentHandle->GetChildHandle(GET_MEMBER_NAME_STATIC(UVoxelSpawnerConfig, GeneratorOutputs)); + } + } + + if (!ensure(OutputsHandle.IsValid())) + { + return; + } + + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&CustomizationUtils]() + { + auto Utilities = CustomizationUtils.GetPropertyUtilities(); + if (Utilities.IsValid()) + { + Utilities->ForceRefresh(); + } + }); + OutputsHandle->SetOnPropertyValueChanged(RefreshDelegate); + + UObject* OutputsObject = nullptr; + if (!ensure(OutputsHandle->GetValue(OutputsObject) == FPropertyAccess::Success)) + { + return; + } + + UVoxelSpawnerOutputsConfig* Outputs = Cast(OutputsObject); + if (!Outputs) + { + HeaderRow + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MaxDesiredWidth(250.f) + [ + FVoxelEditorUtilities::CreateText(VOXEL_LOCTEXT("Invalid Generator Outputs"), FSlateColor(FColor::Red)) + ]; + return; + } + + const TArray ValidOutputNames = Outputs->GetFloatOutputs(); + + FName Value; + if (!ensure(NameHandle->GetValue(Value) == FPropertyAccess::Success)) + { + return; + } + + OptionsSource.Reset(); + for (auto& Name : ValidOutputNames) + { + OptionsSource.Add(MakeShared(Name)); + } + + const auto ValuePtrPtr = OptionsSource.FindByPredicate([&](auto& Ptr) { return *Ptr == Value; }); + ensure(ValuePtrPtr || !ValidOutputNames.Contains(Value)); + + ComboBoxText = FVoxelEditorUtilities::CreateText(FText::FromName(Value), FSlateColor(ValuePtrPtr ? FColor::Black : FColor::Red)); + + HeaderRow + .NameContent() + [ + PropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + [ + SNew(SBox) + .MinDesiredWidth(FDetailWidgetRow::DefaultValueMinWidth) + [ + SNew(SComboBox>) + .OptionsSource(&OptionsSource) + .OnSelectionChanged(this, &FVoxelSpawnerOutputNameCustomization::HandleComboBoxSelectionChanged) + .OnGenerateWidget_Lambda([&](TSharedPtr InValue) + { + return FVoxelEditorUtilities::CreateText(FText::FromName(*InValue)); + }) + .InitiallySelectedItem(ValuePtrPtr ? *ValuePtrPtr : TSharedPtr()) + [ + ComboBoxText.ToSharedRef() + ] + ] + ]; +} + +void FVoxelSpawnerOutputNameCustomization::HandleComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) +{ + if (ensure(NewSelection.IsValid()) && ensure(NameHandle.IsValid()) && ensure(ComboBoxText.IsValid())) + { + NameHandle->SetValue(*NewSelection); + ComboBoxText->SetText(FText::FromName(*NewSelection)); + ComboBoxText->SetColorAndOpacity(FSlateColor(FColor::Black)); // If it's selected, it's always valid + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.h new file mode 100644 index 00000000..5c7b9a41 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.h @@ -0,0 +1,22 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IPropertyTypeCustomization.h" + +class STextBlock; + +class FVoxelSpawnerOutputNameCustomization : public IPropertyTypeCustomization +{ +public: + virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override {} + +private: + TArray> OptionsSource; + TSharedPtr ComboBoxText; + TSharedPtr NameHandle; + + void HandleComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp new file mode 100644 index 00000000..112e5619 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp @@ -0,0 +1,693 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelWorldDetails.h" +#include "VoxelWorld.h" +#include "VoxelStaticWorld.h" +#include "VoxelData/VoxelData.h" +#include "VoxelRender/VoxelProceduralMeshComponent.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelRender/VoxelMaterialInterface.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelTools/VoxelDataTools.inl" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelMessages.h" +#include "VoxelFeedbackContext.h" +#include "VoxelScopedTransaction.h" + +#include "Modules/ModuleManager.h" + +#include "Materials/MaterialInstanceDynamic.h" +#include "Framework/Application/SlateApplication.h" +#include "DesktopPlatformModule.h" +#include "Misc/FileHelper.h" +#include "Misc/MessageDialog.h" +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" +#include "AssetRegistryModule.h" +#include "Editor.h" +#include "RawMesh.h" +#include "PhysicsEngine/BodySetup.h" + +// The sort order is being silly, so force set it +#include "Editor/PropertyEditor/Private/DetailCategoryBuilderImpl.h" + +inline FString GetVoxelWorldSaveFilePath(AVoxelWorld& World, bool bIsLoad) +{ + if ((bIsLoad && FPaths::FileExists(World.SaveFilePath)) || (!bIsLoad && !World.SaveFilePath.IsEmpty())) + { + return World.SaveFilePath; + } + else + { + TArray OutFiles; + if (FDesktopPlatformModule::Get()->OpenFileDialog( + FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr), + TEXT("Choose File"), + FPaths::ProjectSavedDir(), + "", + TEXT("Voxel Save (*.voxelsave)|*.voxelsave"), + EFileDialogFlags::None, + OutFiles)) + { + ensure(OutFiles.Num() == 1); + return OutFiles[0]; + } + else + { + return ""; + } + } +} + +void FVoxelWorldDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + FVoxelEditorUtilities::EnableRealtime(); + TArray> Objects; + DetailLayout.GetObjectsBeingCustomized(Objects); + // Disabled as it makes BP compilation crash when calling PostEditChange + //for (auto& Object : Objects) + //{ + // World->UpdateCollisionProfile(); + // World->PostEditChange(); + //} + + // Material config specific setup + if (Objects.Num() == 1) + { + auto* World = CastChecked(Objects[0]); + switch (World->MaterialConfig) + { + case EVoxelMaterialConfig::RGB: + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, MaterialCollection)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, LODMaterialCollections)); + if (World->RGBHardness != EVoxelRGBHardness::FourWayBlend && World->RGBHardness != EVoxelRGBHardness::FiveWayBlend) + { + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, MaterialsHardness)); + } + break; + case EVoxelMaterialConfig::SingleIndex: + case EVoxelMaterialConfig::MultiIndex: + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, VoxelMaterial)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, LODMaterials)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, RGBHardness)); + break; + default: + ensure(false); + break; + } + + switch (World->UVConfig) + { + case EVoxelUVConfig::GlobalUVs: + break; + case EVoxelUVConfig::PackWorldUpInUVs: + case EVoxelUVConfig::PerVoxelUVs: + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, UVScale)); + break; + default: + ensure(false); + break; + } + + const FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([Properties = MakeWeakPtr(DetailLayout.GetPropertyUtilities())]() + { + if (Properties.IsValid()) + { + Properties.Pin()->ForceRefresh(); + } + }); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, MaterialConfig))->SetOnPropertyValueChanged(RefreshDelegate); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, UVConfig))->SetOnPropertyValueChanged(RefreshDelegate); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, RGBHardness))->SetOnPropertyValueChanged(RefreshDelegate); + } + + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, EditorOnly_NewScale)); + + if (bIsDataAssetEditor) + { + DetailLayout.HideCategory("Voxel - Save"); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, Generator)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bCreateWorldAutomatically)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bUseCameraIfNoInvokersFound)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bEnableUndoRedo)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bEnableCustomWorldRebasing)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bMergeAssetActors)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bMergeDisableEditsBoxes)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bCreateGlobalPool)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, ProcMeshClass)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bRenderWorld)); + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, bStaticWorld)); + DetailLayout.HideCategory("Voxel - Spawners"); + DetailLayout.HideCategory("Physics"); + DetailLayout.HideCategory("Voxel - Collisions"); + DetailLayout.HideCategory("Voxel - Navmesh"); + DetailLayout.HideCategory("Voxel - Multiplayer"); + DetailLayout.HideCategory("Replication"); + DetailLayout.HideCategory("Input"); + DetailLayout.HideCategory("Actor"); + DetailLayout.HideCategory("Cooking"); + DetailLayout.HideCategory("TransformCommon"); + DetailLayout.HideCategory("ComponentReplication"); + DetailLayout.HideCategory("Variable"); + DetailLayout.HideCategory("Tick"); + DetailLayout.HideCategory("Voxel - Preview"); + DetailLayout.HideCategory("Voxel - Bake"); + } + + { + // Component settings not affecting the voxel world + DetailLayout.HideCategory("Lighting"); + DetailLayout.HideCategory("Tags"); + DetailLayout.HideCategory("Activation"); + DetailLayout.HideCategory("Rendering"); + DetailLayout.HideCategory("AssetUserData"); + DetailLayout.HideCategory("Mobile"); + + // No HLOD for voxels + DetailLayout.HideCategory("HLOD"); + + // Manually handling those + DetailLayout.HideCategory("Collision"); + + const auto SortCategory = [&](FName Name, uint32 Order, bool bCollapsed, FString NewName = {}) + { + FText NewNameText; + if (!NewName.IsEmpty()) + { + NewNameText = FText::FromString(NewName); + } + auto& Builder = static_cast(DetailLayout.EditCategory(Name, NewNameText)); + Builder.SetSortOrder(Order); + Builder.InitiallyCollapsed(bCollapsed); + }; + + uint32 Order = 1000; + SortCategory("Voxel - Preview", Order++, false); + SortCategory("Voxel - Save", Order++, true); + SortCategory("Voxel - General", Order++, false); + SortCategory("Voxel - World Size", Order++, false); + SortCategory("Voxel - Rendering", Order++, false); + SortCategory("Voxel - Materials", Order++, false); + SortCategory("Voxel - Spawners", Order++, true); + SortCategory("Physics", Order++, true, "Voxel - Physics"); + SortCategory("Voxel - Collisions", Order++, true); + SortCategory("Voxel - Navmesh", Order++, true); + SortCategory("Voxel - LOD Settings", Order++, true); + SortCategory("Voxel - Performance", Order++, true); + SortCategory("Voxel - Multiplayer", Order++, true); + SortCategory("Voxel - Bake", Order++, true); + SortCategory("Replication", Order++, true); + SortCategory("Input", Order++, true); + SortCategory("Actor", Order++, true); + SortCategory("Cooking", Order++, true); + SortCategory("VirtualTexture", Order++, true); + } + + const auto CreateWorldsDelegate = [Objects](auto Lambda) + { + return FOnClicked::CreateLambda([=]() + { + for (auto& Object : Objects) + { + auto* World = Cast(Object); + if (World) + { + Lambda(*World); + } + } + return FReply::Handled(); + }); + }; + const auto CreateWorldsEnabledDelegate = [Objects](auto Lambda) + { + return TAttribute::Create([=]() + { + for (auto& Object : Objects) + { + auto* World = Cast(Object); + if (World) + { + if (!Lambda(*World)) + { + return false; + } + } + } + return true; + }); + }; + + bool bIsBPEditor = false; + bool bIsEditor = false; + for (auto& Object : Objects) + { + bIsBPEditor = Object->GetWorld() == nullptr; + bIsEditor = !bIsBPEditor && Object->GetWorld()->WorldType == EWorldType::Editor; + } + + if (!bIsBPEditor && !bIsDataAssetEditor) + { + if (bIsEditor) + { + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Toggle"), + VOXEL_LOCTEXT("Toggle World Preview"), + VOXEL_LOCTEXT("Toggle"), + false, + CreateWorldsDelegate([](AVoxelWorld& World) + { + World.Toggle(); + GEditor->SelectActor(&World, true, true, true, true); + })); + + FVoxelEditorUtilities::AddPropertyToCategory( + DetailLayout, + "Voxel - Preview", + GET_MEMBER_NAME_STATIC(AVoxelWorld, EditorOnly_NewScale), + true); + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Scale"), + VOXEL_LOCTEXT("Scale World Data"), + VOXEL_LOCTEXT("Scale"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("Scaling data might take a while/crash your PC! Do you want to continue?"))) + { + FVoxelScopedSlowTask Scope(1.f, VOXEL_LOCTEXT("Scaling data")); + Scope.MakeDialog(); + Scope.EnterProgressFrame(); + + FVoxelScopedTransaction Transaction(&World, "Scaling data", EVoxelChangeType::DataSwap); + UVoxelBlueprintLibrary::ScaleData(&World, World.EditorOnly_NewScale); + } + } + })); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Clear All"), + VOXEL_LOCTEXT("Clear World Data"), + VOXEL_LOCTEXT("Clear All"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("This will clear all the voxel world edits! Do you want to continue?"))) + { + World.GetData().ClearData(); + World.Toggle(); + World.Toggle(); + } + } + })); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Clear Values"), + VOXEL_LOCTEXT("Clear Value Data"), + VOXEL_LOCTEXT("Clear Values"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("This will clear all the voxel world value edits! Do you want to continue?"))) + { + FVoxelScopedTransaction Transaction(&World, "Clear values", EVoxelChangeType::DataSwap); + UVoxelBlueprintLibrary::ClearValueData(&World); + } + } + })); + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Clear Materials"), + VOXEL_LOCTEXT("Clear Material Data"), + VOXEL_LOCTEXT("Clear Materials"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("This will clear all the voxel world material edits! Do you want to continue?"))) + { + FVoxelScopedTransaction Transaction(&World, "Clear materials", EVoxelChangeType::DataSwap); + UVoxelBlueprintLibrary::ClearMaterialData(&World); + } + } + })); + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Set Values Dirty"), + VOXEL_LOCTEXT("Set Values as Dirty"), + VOXEL_LOCTEXT("Set Values Dirty"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("Setting values as dirty might take a while/crash your PC! Make sure your World Size is as small as possible. Do you want to continue?"))) + { + FVoxelScopedSlowTask Scope(1.f, VOXEL_LOCTEXT("Setting values as dirty")); + Scope.MakeDialog(); + Scope.EnterProgressFrame(); + + FVoxelScopedTransaction Transaction(&World, "Setting values as dirty", EVoxelChangeType::DataSwap); + UVoxelDataTools::SetBoxAsDirty(&World, FVoxelIntBox::Infinite, true, false); + World.GetData().MarkAsDirty(); + } + } + })); + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Preview", + VOXEL_LOCTEXT("Set Materials Dirty"), + VOXEL_LOCTEXT("Set Materials as Dirty"), + VOXEL_LOCTEXT("Set Materials Dirty"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + if (EAppReturnType::Yes == FMessageDialog::Open( + EAppMsgType::YesNoCancel, + VOXEL_LOCTEXT("Setting materials as dirty might take a while/crash your PC! Make sure your World Size is as small as possible. Do you want to continue?"))) + { + FVoxelScopedSlowTask Scope(1.f, VOXEL_LOCTEXT("Setting materials as dirty")); + Scope.MakeDialog(); + Scope.EnterProgressFrame(); + + FVoxelScopedTransaction Transaction(&World, "Setting materials as dirty", EVoxelChangeType::DataSwap); + UVoxelDataTools::SetBoxAsDirty(&World, FVoxelIntBox::Infinite, false, true); + World.GetData().MarkAsDirty(); + } + } + })); + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Bake", + VOXEL_LOCTEXT("Bake"), + VOXEL_LOCTEXT("Bake World To Static Meshes"), + VOXEL_LOCTEXT("Bake"), + false, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (World.IsCreated()) + { + BakeWorld(World); + } + }), + CreateWorldsEnabledDelegate([](AVoxelWorld& World) + { + return World.IsCreated(); + })); + } + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Save", + VOXEL_LOCTEXT("Load"), + VOXEL_LOCTEXT("Load from Save Object"), + VOXEL_LOCTEXT("Load"), + false, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (ensure(World.IsCreated()) && ensure(World.SaveObject)) + { + World.LoadFromSaveObjectEditor(); + } + }), + CreateWorldsEnabledDelegate([](AVoxelWorld& World) + { + return World.IsCreated() && World.SaveObject; + })); + + DetailLayout.HideProperty(GET_MEMBER_NAME_STATIC(AVoxelWorld, SaveObject)); + FVoxelEditorUtilities::AddPropertyToCategory( + DetailLayout, + "Voxel - Save", + GET_MEMBER_NAME_STATIC(AVoxelWorld, SaveObject), + false); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Save", + VOXEL_LOCTEXT("Save File"), + VOXEL_LOCTEXT("Save to File"), + VOXEL_LOCTEXT("Save"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (!ensure(World.IsCreated())) return; + + const FString Path = GetVoxelWorldSaveFilePath(World, false); + if (Path.IsEmpty()) return; + + FText Error; + if (World.SaveToFile(Path, Error)) + { + World.SaveFilePath = Path; + } + else + { + FMessageDialog::Open(EAppMsgType::Ok, Error); + } + }), + CreateWorldsEnabledDelegate([](AVoxelWorld& World) + { + return World.IsCreated(); + })); + + FVoxelEditorUtilities::AddButtonToCategory( + DetailLayout, + "Voxel - Save", + VOXEL_LOCTEXT("Load File"), + VOXEL_LOCTEXT("Load from File"), + VOXEL_LOCTEXT("Load"), + true, + CreateWorldsDelegate([](AVoxelWorld& World) + { + if (!ensure(World.IsCreated())) return; + + const FString Path = GetVoxelWorldSaveFilePath(World, true); + if (Path.IsEmpty()) return; + + FText Error; + if (World.LoadFromFile(Path, Error)) + { + World.SaveFilePath = Path; + } + else + { + FMessageDialog::Open(EAppMsgType::Ok, Error); + } + + }), + CreateWorldsEnabledDelegate([](AVoxelWorld& World) + { + return World.IsCreated(); + })); +} +} + +inline UStaticMesh* BakeProcMesh(UVoxelProceduralMeshComponent* ProcMeshComp, const FString& PackageName, UStaticMesh* StaticMeshTemplate, bool bRecomputeNormals) +{ + const FName MeshName(*FPackageName::GetLongPackageAssetName(PackageName)); + + // Raw mesh data we are filling in + FRawMesh RawMesh; + // Materials to apply to new mesh + TArray MeshMaterials; + + int32 VertexBase = 0; + + ProcMeshComp->IterateSections([&](const FVoxelProcMeshSectionSettings& SectionSettings, const FVoxelProcMeshBuffers& Buffers) + { + // Copy verts + RawMesh.VertexPositions.Reserve(RawMesh.VertexPositions.Num() + Buffers.GetNumVertices()); + auto& PositionBuffer = Buffers.VertexBuffers.PositionVertexBuffer; + for (int32 Index = 0; Index < Buffers.GetNumVertices(); Index++) + { + RawMesh.VertexPositions.Add(PositionBuffer.VertexPosition(Index)); + } + + auto& IndexBuffer = Buffers.IndexBuffer; + auto& StaticMeshBuffer = Buffers.VertexBuffers.StaticMeshVertexBuffer; + auto& ColorBuffer = Buffers.VertexBuffers.ColorVertexBuffer; + + // Copy 'wedge' info + check(StaticMeshBuffer.GetNumTexCoords() <= MAX_MESH_TEXTURE_COORDS); + for (int32 IndexIterator = 0; IndexIterator < IndexBuffer.GetNumIndices(); IndexIterator++) + { + const uint32 VertexIndex = IndexBuffer.GetIndex(IndexIterator); + + RawMesh.WedgeIndices.Add(VertexIndex + VertexBase); + + RawMesh.WedgeTangentX.Add(StaticMeshBuffer.VertexTangentX(VertexIndex)); + RawMesh.WedgeTangentY.Add(StaticMeshBuffer.VertexTangentY(VertexIndex)); + RawMesh.WedgeTangentZ.Add(StaticMeshBuffer.VertexTangentZ(VertexIndex)); + + for (uint32 Tex = 0; Tex < StaticMeshBuffer.GetNumTexCoords(); Tex++) + { + RawMesh.WedgeTexCoords[Tex].Add(StaticMeshBuffer.GetVertexUV(VertexIndex, Tex)); + } + RawMesh.WedgeColors.Add(ColorBuffer.VertexColor(VertexIndex)); + } + + // copy face info + for (int32 Index = 0; Index < IndexBuffer.GetNumIndices() / 3; Index++) + { + RawMesh.FaceMaterialIndices.Add(MeshMaterials.Num()); + RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false + } + + UMaterialInterface* Material = nullptr; + if (SectionSettings.Material.IsValid()) + { + Material = SectionSettings.Material->GetMaterial(); + } + if (auto* Instance = Cast(Material)) + { + Material = Instance->Parent; + } + + // Remember material + MeshMaterials.Add(Material); + + // Update offset for creating one big index/vertex buffer + VertexBase += Buffers.GetNumVertices(); + }); + + // If we got some valid data. + if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3) + { + // Then find/create it. +#if ENGINE_MINOR_VERSION < 26 + UPackage* Package = CreatePackage(nullptr, *PackageName); +#else + UPackage* Package = CreatePackage(*PackageName); +#endif + check(Package); + + // Create StaticMesh object + UStaticMesh* StaticMesh = NewObject(Package, MeshName, RF_Public | RF_Standalone, StaticMeshTemplate); + StaticMesh->InitResources(); + + StaticMesh->LightingGuid = FGuid::NewGuid(); + + // Add source to new StaticMesh + FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel(); + SrcModel.BuildSettings.bRecomputeNormals = bRecomputeNormals; + SrcModel.BuildSettings.bRecomputeTangents = bRecomputeNormals; + SrcModel.BuildSettings.bRemoveDegenerates = false; + SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false; + SrcModel.BuildSettings.bUseFullPrecisionUVs = false; + SrcModel.BuildSettings.bGenerateLightmapUVs = true; + SrcModel.BuildSettings.SrcLightmapIndex = 0; + SrcModel.BuildSettings.DstLightmapIndex = 1; + SrcModel.SaveRawMesh(RawMesh); + + // Copy materials to new mesh + for (UMaterialInterface* Material : MeshMaterials) + { + StaticMesh->UE_27_SWITCH(StaticMaterials, GetStaticMaterials()).Add(FStaticMaterial(Material)); + } + + // Configure collision + StaticMesh->CreateBodySetup(); + StaticMesh->UE_27_SWITCH(BodySetup, GetBodySetup())->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple; + + // Set the Imported version before calling the build + StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion; + + // Build mesh from source + StaticMesh->Build(false); + StaticMesh->PostEditChange(); + + // Notify asset registry of new asset + FAssetRegistryModule::AssetCreated(StaticMesh); + Package->MarkPackageDirty(); + + return StaticMesh; + } + else + { + return nullptr; + } +} + +void FVoxelWorldDetails::BakeWorld(AVoxelWorld& World) +{ + if (World.MaterialConfig != EVoxelMaterialConfig::RGB) + { + FMessageDialog::Open(EAppMsgType::Ok, VOXEL_LOCTEXT("Baking to static mesh requires to use RGB as material config.")); + return; + } + + int32 ProcMeshCount = 0; + const FString Path = World.BakedDataPath.FilePath + + "/" + + World.GetName() + + "_" + + FString::Printf(TEXT("%d_%d_%d_%d_%d_%d"), + FDateTime::Now().GetSecond(), + FDateTime::Now().GetMinute(), + FDateTime::Now().GetHour(), + FDateTime::Now().GetDay(), + FDateTime::Now().GetMonth(), + FDateTime::Now().GetYear()); + + UClass* const StaticComponentClass = World.BakedMeshComponentTemplate ? World.BakedMeshComponentTemplate.Get() : UStaticMeshComponent::StaticClass(); + + AVoxelStaticWorld* const StaticWorld = World.GetWorld()->SpawnActor(AVoxelStaticWorld::StaticClass()); + StaticWorld->BaseMesh = NewObject(StaticWorld, StaticComponentClass); + StaticWorld->BaseMesh->SetWorldTransform(World.GetTransform()); + StaticWorld->SetRootComponent(StaticWorld->BaseMesh); + + FVoxelScopedSlowTask Progress(World.GetComponents().Num(), VOXEL_LOCTEXT("Baking the voxel world to static meshes")); + Progress.MakeDialog(true, true); + for (auto* Component : World.GetComponents()) + { + Progress.EnterProgressFrame(); + if (Progress.ShouldCancel()) + { + StaticWorld->Destroy(); + return; + } + if (auto* ProcMesh = Cast(Component)) + { + auto* StaticMesh = BakeProcMesh(ProcMesh, Path + "/" + FString::Printf(TEXT("Mesh_%d"), ProcMeshCount), World.BakedMeshTemplate, World.bRecomputeNormalsBeforeBaking); + if (StaticMesh) + { + auto* StaticMeshComp = NewObject(StaticWorld, StaticComponentClass); + StaticMeshComp->AttachToComponent(StaticWorld->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform); + StaticMeshComp->SetStaticMesh(StaticMesh); + StaticMeshComp->SetRelativeTransform(ProcMesh->GetRelativeTransform()); + StaticMeshComp->RegisterComponent(); + StaticWorld->Meshes.Add(StaticMeshComp); + ProcMeshCount++; + } + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.h new file mode 100644 index 00000000..4c460ded --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Details/VoxelWorldDetails.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Input/Reply.h" +#include "IDetailCustomization.h" + +class AVoxelWorld; +class SButton; + +class FVoxelWorldDetails : public IDetailCustomization +{ +public: + const bool bIsDataAssetEditor; + + explicit FVoxelWorldDetails(bool bIsDataAssetEditor = false) + : bIsDataAssetEditor(bIsDataAssetEditor) + { + } + +private: + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; + + static void BakeWorld(AVoxelWorld& World); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.cpp new file mode 100644 index 00000000..77424dab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.cpp @@ -0,0 +1,158 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEdMode.h" +#include "VoxelEdModeToolkit.h" +#include "VoxelEditorToolsPanel.h" +#include "VoxelEditorDetailsUtilities.h" + +#include "Toolkits/ToolkitManager.h" +#include "EditorViewportClient.h" +#include "EditorModeManager.h" + +const FEditorModeID FEdModeVoxel::EM_Voxel = TEXT("EM_Voxel"); + +void FEdModeVoxel::Enter() +{ + FEdMode::Enter(); + + Panel = MakeShareable(new FVoxelEditorToolsPanel()); + Toolkit = MakeShareable(new FVoxelEdModeToolkit); + + // Toolkit needs the panel to be created + Toolkit->Init(Owner->GetToolkitHost()); + Panel->Init(Toolkit->GetToolkitCommands()); + Toolkit->GetInlineContent(); + + FVoxelEditorUtilities::EnableRealtime(); +} + +void FEdModeVoxel::Exit() +{ + if (Toolkit.IsValid()) + { + FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef()); + Toolkit.Reset(); + } + + if (Panel.IsValid()) + { + Panel.Reset(); + } + + FEdMode::Exit(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FEdModeVoxel::MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) +{ + if (!Panel.IsValid()) + { + return false; + } + + Panel->MouseMove(ViewportClient, Viewport, MouseX, MouseY); + ViewportClient->Invalidate(false, false); + + return bMousePressed; +} + +void FEdModeVoxel::Tick(FEditorViewportClient* ViewportClient, float DeltaTime) +{ + if (!Panel.IsValid()) + { + return; + } + + if (Panel.IsValid()) + { + Panel->Tick(ViewportClient, DeltaTime); + } + + ViewportClient->Viewport->CaptureMouse(true); +} + +bool FEdModeVoxel::HandleClick(FEditorViewportClient* ViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) +{ + if (!Panel.IsValid()) + { + return false; + } + + if (Panel.IsValid()) + { + Panel->HandleClick(ViewportClient, HitProxy, Click); + } + + return true; +} + +bool FEdModeVoxel::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) +{ + if (!Panel.IsValid()) + { + return false; + } + + if (Event == IE_Released && (Key == EKeys::LeftMouseButton || Key == EKeys::MiddleMouseButton || Key == EKeys::RightMouseButton)) + { + //Set the cursor position to that of the slate cursor so it wont snap back + Viewport->SetPreCaptureMousePosFromSlateCursor(); + } + if (Key == EKeys::LeftMouseButton && Event != EInputEvent::IE_Repeat) + { + bMousePressed = (Event == EInputEvent::IE_Pressed); + } + + if (Panel.IsValid()) + { + return Panel->InputKey(ViewportClient, Viewport, Key, Event); + } + + return false; +} + +bool FEdModeVoxel::InputAxis(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime) +{ + if (Panel.IsValid()) + { + return Panel->InputAxis(ViewportClient, Viewport, Key, Delta, DeltaTime); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FEdModeVoxel::CapturedMouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) +{ + return MouseMove(ViewportClient, Viewport, MouseX, MouseY); +} + +bool FEdModeVoxel::DisallowMouseDeltaTracking() const +{ + // We never want to use the mouse delta tracker while painting + return Panel.IsValid() && bMousePressed; +} + +bool FEdModeVoxel::IsSelectionAllowed(AActor* InActor, bool bInSelection) const +{ + return true; +} + +bool FEdModeVoxel::GetCursor(EMouseCursor::Type& OutCursor) const +{ + if (Panel.IsValid()) + { + OutCursor = EMouseCursor::Crosshairs; + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.h new file mode 100644 index 00000000..066ff8e5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdMode.h @@ -0,0 +1,43 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Editor.h" +#include "EdMode.h" + +class AVoxelWorld; +class FEdModeVoxel; +class FVoxelEditorToolsPanel; +struct FViewportClick; + +class FEdModeVoxel : public FEdMode +{ +public: + const static FEditorModeID EM_Voxel; + +public: + virtual void Enter() override; + virtual void Exit() override; + +public: + virtual bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) override; + virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override; + virtual bool HandleClick(FEditorViewportClient* ViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) override; + virtual bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) override; + virtual bool InputAxis(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime) override; + +public: + virtual bool CapturedMouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) override; + virtual bool DisallowMouseDeltaTracking() const override; + virtual bool UsesToolkits() const override { return true; } + virtual bool IsSelectionAllowed( AActor* InActor, bool bInSelection ) const override; + virtual bool GetCursor(EMouseCursor::Type& OutCursor) const override; + +public: + FVoxelEditorToolsPanel& GetPanel() const { return *Panel; } + +private: + bool bMousePressed = false; + TSharedPtr Panel; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.cpp new file mode 100644 index 00000000..0c422dc3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.cpp @@ -0,0 +1,70 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEdModeToolkit.h" +#include "VoxelEdMode.h" +#include "VoxelEditorToolsPanel.h" +#include "EditorModeManager.h" + +void FVoxelEdModeToolkit::Init(const TSharedPtr& InitToolkitHost) +{ + FModeToolkit::Init(InitToolkitHost); +} + +FName FVoxelEdModeToolkit::GetToolkitFName() const +{ + return FName("VoxelEdMode"); +} + +FText FVoxelEdModeToolkit::GetBaseToolkitName() const +{ + return VOXEL_LOCTEXT("VoxelEdMode Tool"); +} + +class FEdMode* FVoxelEdModeToolkit::GetEditorMode() const +{ + return GLevelEditorModeTools().GetActiveMode(FEdModeVoxel::EM_Voxel); +} + +TSharedPtr FVoxelEdModeToolkit::GetInlineContent() const +{ + return GetPanel().GetWidget(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEdModeToolkit::GetToolPaletteNames(TArray& InPaletteName) const +{ + InPaletteName = { STATIC_FNAME("Main") }; +} + +void FVoxelEdModeToolkit::BuildToolPalette(FName PaletteName, FToolBarBuilder& ToolBarBuilder) +{ + GetPanel().CustomizeToolbar(ToolBarBuilder); +} + +void FVoxelEdModeToolkit::OnToolPaletteChanged(FName PaletteName) +{ +} + +FText FVoxelEdModeToolkit::GetActiveToolDisplayName() const +{ + return {}; +} + +FText FVoxelEdModeToolkit::GetActiveToolMessage() const +{ + return {}; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelEditorToolsPanel& FVoxelEdModeToolkit::GetPanel() const +{ + FEdModeVoxel* VoxelEdMode = static_cast(GetEditorMode()); + check(VoxelEdMode); + return VoxelEdMode->GetPanel(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.h new file mode 100644 index 00000000..ac934f42 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.h @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelMinimal.h" +#include "Toolkits/BaseToolkit.h" + +class FToolBarBuilder; +class FVoxelEditorToolsPanel; + +class FVoxelEdModeToolkit : public FModeToolkit +{ +public: + virtual void Init(const TSharedPtr& InitToolkitHost) override; + + virtual FName GetToolkitFName() const override; + virtual FText GetBaseToolkitName() const override; + virtual FEdMode* GetEditorMode() const override; + virtual TSharedPtr GetInlineContent() const override; + + /** Mode Toolbar Palettes **/ + virtual void GetToolPaletteNames(TArray& InPaletteName) const UE_24_SWITCH(,override); + virtual void BuildToolPalette(FName PaletteName, FToolBarBuilder& ToolbarBuilder) UE_24_SWITCH(,override); + virtual void OnToolPaletteChanged(FName PaletteName) UE_24_SWITCH(,override); + + /** Modes Panel Header Information **/ + virtual FText GetActiveToolDisplayName() const UE_25_SWITCH(,override); + virtual FText GetActiveToolMessage() const UE_25_SWITCH(,override); + +private: + FVoxelEditorToolsPanel& GetPanel() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp new file mode 100644 index 00000000..05345e0f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp @@ -0,0 +1,333 @@ +// Copyright 2020 Phyronnaz + +#include "Factories/VoxelDataAssetFactory.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelImporters/VoxelMeshImporter.h" +#include "Importers/MagicaVox.h" +#include "Importers/RawVox.h" +#include "VoxelFeedbackContext.h" +#include "VoxelMessages.h" + +#include "Editor.h" +#include "PropertyEditorModule.h" +#include "Modules/ModuleManager.h" +#include "Engine/StaticMesh.h" +#include "Framework/Notifications/NotificationManager.h" + +#include "Widgets/SWindow.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SUniformGridPanel.h" +#include "Widgets/Notifications/SNotificationList.h" + +UVoxelDataAssetFactory::UVoxelDataAssetFactory() +{ + bCreateNew = true; + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = UVoxelDataAsset::StaticClass(); +} + +UObject* UVoxelDataAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + auto* NewDataAsset = NewObject(InParent, Class, Name, Flags | RF_Transactional); + + auto Data = MakeVoxelShared(); + Data->SetSize(FIntVector(1, 1, 3), false); + Data->SetValue(0, 0, 0, FVoxelValue::Full()); + Data->SetValue(0, 0, 1, FVoxelValue::Empty()); + Data->SetValue(0, 0, 2, FVoxelValue::Full()); + NewDataAsset->SetData(Data); + + return NewDataAsset; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelDataAssetFromMeshImporterFactory::UVoxelDataAssetFromMeshImporterFactory() +{ + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = UVoxelDataAsset::StaticClass(); +} + +UObject* UVoxelDataAssetFromMeshImporterFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + FVoxelScopedSlowTask Progress(2, VOXEL_LOCTEXT("Importing from mesh")); + Progress.MakeDialog(true); + + auto* NewDataAsset = NewObject(InParent, Class, Name, Flags | RF_Transactional); + int32 NumLeaks = 0; + Progress.EnterProgressFrame(); + const auto Data = MakeVoxelShared(); + + FVoxelMeshImporterInputData InputData; + UVoxelMeshImporterLibrary::CreateMeshDataFromStaticMesh(MeshImporter->StaticMesh, InputData); + FTransform Transform = MeshImporter->GetTransform(); + Transform.SetTranslation(FVector::ZeroVector); + FVoxelMeshImporterRenderTargetCache Cache; + if (UVoxelMeshImporterLibrary::ConvertMeshToVoxels(MeshImporter, InputData, Transform, MeshImporter->Settings, Cache, *Data, NewDataAsset->PositionOffset, NumLeaks)) + { + Progress.EnterProgressFrame(); + NewDataAsset->Source = EVoxelDataAssetImportSource::Mesh; + NewDataAsset->SetData(Data); + if (NumLeaks > 0) + { + FNotificationInfo Info = FNotificationInfo(FText::Format( + VOXEL_LOCTEXT("{0} leaks in the mesh (or bug in the voxelizer)"), + FText::AsNumber(NumLeaks))); + Info.ExpireDuration = 10; + FSlateNotificationManager::Get().AddNotification(Info); + } + return NewDataAsset; + } + else + { + Progress.EnterProgressFrame(); + return nullptr; + } +} + +FString UVoxelDataAssetFromMeshImporterFactory::GetDefaultNewAssetName() const +{ + return MeshImporter->StaticMesh->GetName(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelDataAssetFromMagicaVoxFactory::UVoxelDataAssetFromMagicaVoxFactory() +{ + bEditorImport = true; + SupportedClass = UVoxelDataAsset::StaticClass(); + Formats.Add(TEXT("vox;Magica Voxel Asset")); +} + +bool UVoxelDataAssetFromMagicaVoxFactory::ConfigureProperties() +{ + // Load from default + bUsePalette = GetDefault()->bUsePalette; + + TSharedRef PickerWindow = SNew(SWindow) + .Title(VOXEL_LOCTEXT("Import Magica Vox")) + .SizingRule(ESizingRule::Autosized); + + bool bSuccess = false; + + auto OnOkClicked = FOnClicked::CreateLambda([&]() + { + bSuccess = true; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }); + auto OnCancelClicked = FOnClicked::CreateLambda([&]() + { + bSuccess = false; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }); + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea); + + auto DetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + DetailsPanel->SetObject(this); + + auto Widget = + SNew(SBorder) + .Visibility(EVisibility::Visible) + .BorderImage(FEditorStyle::GetBrush("Menu.Background")) + [ + SNew(SBox) + .Visibility(EVisibility::Visible) + .WidthOverride(520.0f) + [ + SNew(SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + [ + DetailsPanel + ] + +SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + .VAlign(VAlign_Bottom) + .Padding(8) + [ + SNew(SUniformGridPanel) + .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding")) + + SUniformGridPanel::Slot(0, 0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Create")) + .HAlign(HAlign_Center) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnOkClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Success") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + +SUniformGridPanel::Slot(1,0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Cancel")) + .HAlign(HAlign_Center) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnCancelClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Default") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + ] + ] + ]; + + PickerWindow->SetContent(Widget); + + GEditor->EditorAddModalWindow(PickerWindow); + + // Save to default + GetMutableDefault()->bUsePalette = bUsePalette; + + return bSuccess; +} + +bool UVoxelDataAssetFromMagicaVoxFactory::FactoryCanImport(const FString& Filename) +{ + return FPaths::GetExtension(Filename) == TEXT("vox"); +} + +UObject* UVoxelDataAssetFromMagicaVoxFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) +{ + auto NewDataAsset = NewObject(InParent, InName, Flags | RF_Transactional); + const auto Data = MakeVoxelShared(); + if (MagicaVox::ImportToAsset(Filename, *Data, bUsePalette)) + { + NewDataAsset->Source = EVoxelDataAssetImportSource::MagicaVox; + NewDataAsset->Paths = { Filename }; + NewDataAsset->ImportSettings_MagicaVox.bUsePalette = bUsePalette; + NewDataAsset->SetData(Data); + return NewDataAsset; + } + else + { + return nullptr; + } +} + +bool UVoxelDataAssetFromMagicaVoxFactory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + if (auto* Asset = Cast(Obj)) + { + if (Asset->Source == EVoxelDataAssetImportSource::MagicaVox) + { + OutFilenames = Asset->Paths; + OutFilenames.SetNum(1); + return true; + } + } + return false; +} + +void UVoxelDataAssetFromMagicaVoxFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + if (auto* Asset = Cast(Obj)) + { + Asset->Paths = NewReimportPaths; + ensure(Asset->Paths.Num() == 1); + } +} + +EReimportResult::Type UVoxelDataAssetFromMagicaVoxFactory::Reimport(UObject* Obj) +{ + if (auto* Asset = Cast(Obj)) + { + const auto Data = MakeVoxelShared(); + if (MagicaVox::ImportToAsset(Asset->Paths[0], *Data, Asset->ImportSettings_MagicaVox.bUsePalette)) + { + Asset->SetData(Data); + return EReimportResult::Succeeded; + } + } + return EReimportResult::Failed; +} + +int32 UVoxelDataAssetFromMagicaVoxFactory::GetPriority() const +{ + return ImportPriority; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelDataAssetFromRawVoxFactory::UVoxelDataAssetFromRawVoxFactory() +{ + bEditorImport = true; + SupportedClass = UVoxelDataAsset::StaticClass(); + Formats.Add(TEXT("rawvox;3D Coat RawVox")); +} + +bool UVoxelDataAssetFromRawVoxFactory::FactoryCanImport(const FString& Filename) +{ + return FPaths::GetExtension(Filename) == TEXT("rawvox"); +} + +UObject* UVoxelDataAssetFromRawVoxFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) +{ + auto NewDataAsset = NewObject(InParent, InName, Flags | RF_Transactional); + const auto Data = MakeVoxelShared(); + if (RawVox::ImportToAsset(Filename, *Data)) + { + NewDataAsset->Source = EVoxelDataAssetImportSource::RawVox; + NewDataAsset->Paths = { Filename }; + NewDataAsset->SetData(Data); + return NewDataAsset; + } + else + { + return nullptr; + } +} + +bool UVoxelDataAssetFromRawVoxFactory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + if (auto* Asset = Cast(Obj)) + { + if (Asset->Source == EVoxelDataAssetImportSource::RawVox) + { + OutFilenames = Asset->Paths; + return true; + } + } + return false; +} + +void UVoxelDataAssetFromRawVoxFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + if (auto* Asset = Cast(Obj)) + { + Asset->Paths = NewReimportPaths; + } +} + +EReimportResult::Type UVoxelDataAssetFromRawVoxFactory::Reimport(UObject* Obj) +{ + if (auto* Asset = Cast(Obj)) + { + const auto Data = MakeVoxelShared(); + if (RawVox::ImportToAsset(Asset->Paths[0], *Data)) + { + Asset->SetData(Data); + return EReimportResult::Succeeded; + } + } + return EReimportResult::Failed; +} + +int32 UVoxelDataAssetFromRawVoxFactory::GetPriority() const +{ + return ImportPriority; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.h new file mode 100644 index 00000000..c49c9a62 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.h @@ -0,0 +1,91 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "EditorReimportHandler.h" +#include "Engine/EngineTypes.h" +#include "VoxelDataAssetFactory.generated.h" + +class AVoxelMeshImporter; + +UCLASS() +class UVoxelDataAssetFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelDataAssetFactory(); + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + // End of UFactory interface +}; + +UCLASS() +class UVoxelDataAssetFromMeshImporterFactory : public UFactory +{ + GENERATED_BODY() + +public: + UPROPERTY() + AVoxelMeshImporter* MeshImporter; + + UVoxelDataAssetFromMeshImporterFactory(); + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual FString GetDefaultNewAssetName() const override; + // End of UFactory interface +}; + +UCLASS() +class UVoxelDataAssetFromMagicaVoxFactory : public UFactory, public FReimportHandler +{ + GENERATED_BODY() + +public: + // If false, the material index will be set to the palette index (Single Index material config) + // If true, the palette will be read and the color will be imported instead (RGB material config) + // If you're not sure, leave to true + UPROPERTY(EditAnywhere, Category = "Import configuration") + bool bUsePalette = true; + +public: + UVoxelDataAssetFromMagicaVoxFactory(); + + // UFactory interface + virtual bool ConfigureProperties() override; + virtual bool FactoryCanImport(const FString& Filename) override; + virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override; + // End of UFactory interface + + //~ Begin FReimportHandler Interface + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + virtual int32 GetPriority() const override; + //~ End FReimportHandler Interface +}; + +UCLASS() +class UVoxelDataAssetFromRawVoxFactory : public UFactory, public FReimportHandler +{ + GENERATED_BODY() + +public: + UVoxelDataAssetFromRawVoxFactory(); + + // UFactory interface + virtual bool FactoryCanImport(const FString& Filename) override; + virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override; + // End of UFactory interface + + //~ Begin FReimportHandler Interface + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + virtual int32 GetPriority() const override; + //~ End FReimportHandler Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphDataItemConfigFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphDataItemConfigFactory.h new file mode 100644 index 00000000..ceb3fff6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphDataItemConfigFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelGraphDataItemConfig.h" +#include "VoxelGraphDataItemConfigFactory.generated.h" + +UCLASS() +class UVoxelGraphDataItemConfigFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelGraphDataItemConfigFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelGraphDataItemConfig::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphGeneratorAssetFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphGeneratorAssetFactory.h new file mode 100644 index 00000000..c24d87aa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphGeneratorAssetFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphGeneratorAssetFactory.generated.h" + +UCLASS() +class UVoxelGraphGeneratorAssetFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelGraphGeneratorAssetFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelGraphGenerator::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphMacroAssetFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphMacroAssetFactory.h new file mode 100644 index 00000000..241aa333 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphMacroAssetFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelGraphMacroAssetFactory.generated.h" + +UCLASS() +class UVoxelGraphMacroAssetFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelGraphMacroAssetFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelGraphMacro::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphOutputsConfigFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphOutputsConfigFactory.h new file mode 100644 index 00000000..77ed549b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelGraphOutputsConfigFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelGraphOutputsConfig.h" +#include "VoxelGraphOutputsConfigFactory.generated.h" + +UCLASS() +class UVoxelGraphOutputsConfigFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelGraphOutputsConfigFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelGraphOutputsConfig::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp new file mode 100644 index 00000000..be141d95 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp @@ -0,0 +1,816 @@ +// Copyright 2020 Phyronnaz + +#include "Factories/VoxelHeightmapAssetFactory.h" +#include "VoxelAssets/VoxelHeightmapAssetData.inl" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelMessages.h" +#include "VoxelFeedbackContext.h" +#include "VoxelEditorDetailsUtilities.h" + +#include "Widgets/SWindow.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SUniformGridPanel.h" +#include "Widgets/Layout/SScrollBox.h" + +#include "Editor.h" +#include "EditorStyleSet.h" +#include "PropertyEditorModule.h" +#include "IDetailCustomization.h" +#include "DetailLayoutBuilder.h" + +#include "Modules/ModuleManager.h" + +#include "LandscapeEditorModule.h" +#include "LandscapeComponent.h" +#include "LandscapeDataAccess.h" + +namespace HeightmapHelpers +{ + template + const TLandscapeMapFileFormat* GetFormat(const TCHAR* Extension, ILandscapeEditorModule& LandscapeEditorModule); + + template<> + inline const ILandscapeHeightmapFileFormat* GetFormat(const TCHAR* Extension, ILandscapeEditorModule& LandscapeEditorModule) + { + return LandscapeEditorModule.GetHeightmapFormatByExtension(Extension); + } + template<> + inline const ILandscapeWeightmapFileFormat* GetFormat(const TCHAR* Extension, ILandscapeEditorModule& LandscapeEditorModule) + { + return LandscapeEditorModule.GetWeightmapFormatByExtension(Extension); + } + + inline FLandscapeHeightmapInfo Validate(const TCHAR* Filename, const ILandscapeHeightmapFileFormat* Format) + { + return Format->Validate(Filename); + } + inline FLandscapeWeightmapInfo Validate(const TCHAR* Filename, const ILandscapeWeightmapFileFormat* Format) + { + return Format->Validate(Filename, ""); + } + + inline FLandscapeHeightmapImportData Import(const TCHAR* Filename, FLandscapeFileResolution ExpectedResolution, const ILandscapeHeightmapFileFormat* Format) + { + return Format->Import(Filename, ExpectedResolution); + } + inline FLandscapeWeightmapImportData Import(const TCHAR* Filename, FLandscapeFileResolution ExpectedResolution, const ILandscapeWeightmapFileFormat* Format) + { + return Format->Import(Filename, "", ExpectedResolution); + } + + template + bool GetMap(const FString& Filename, int32& OutWidth, int32& OutHeight, TLandscapeMapImportData& OutMapImportData) + { + if (Filename.IsEmpty()) + { + FVoxelEditorUtilities::ShowError(VOXEL_LOCTEXT("Error: Empty filename!")); + return false; + } + + ILandscapeEditorModule& LandscapeEditorModule = FModuleManager::GetModuleChecked("LandscapeEditor"); + FString Extension = FPaths::GetExtension(Filename, true); + const TLandscapeMapFileFormat* Format = HeightmapHelpers::GetFormat(*Extension, LandscapeEditorModule); + + if (!Format) + { + FVoxelEditorUtilities::ShowError(FText::FromString("Error: Unknown extension " + Extension)); + return false; + } + + auto Info = HeightmapHelpers::Validate(*Filename, Format); + switch (Info.ResultCode) + { + case ELandscapeImportResult::Success: + break; + case ELandscapeImportResult::Warning: + { + if (!FVoxelEditorUtilities::ShowWarning(Info.ErrorMessage)) + { + return false; + } + break; + } + case ELandscapeImportResult::Error: + { + FVoxelEditorUtilities::ShowError(Info.ErrorMessage); + return false; + } + default: + check(false); + } + + const int32 Index = Info.PossibleResolutions.Num() / 2; + OutWidth = Info.PossibleResolutions[Index].Width; + OutHeight = Info.PossibleResolutions[Index].Height; + OutMapImportData = HeightmapHelpers::Import(*Filename, Info.PossibleResolutions[0], Format); + + switch (OutMapImportData.ResultCode) + { + case ELandscapeImportResult::Success: + return true; + case ELandscapeImportResult::Warning: + { + return FVoxelEditorUtilities::ShowWarning(OutMapImportData.ErrorMessage); + } + case ELandscapeImportResult::Error: + { + FVoxelEditorUtilities::ShowError(OutMapImportData.ErrorMessage); + return false; + } + default: + check(false); + return false; + } + } + + bool GetHeightmap(const FString& Filename, int32& OutWidth, int32& OutHeight, FLandscapeHeightmapImportData& OutHeightmapImportData) + { + return HeightmapHelpers::GetMap(Filename, OutWidth, OutHeight, OutHeightmapImportData); + } + + bool GetWeightmap(const FString& Filename, int32& OutWidth, int32& OutHeight, FLandscapeWeightmapImportData& OutWeightmapImportData) + { + return HeightmapHelpers::GetMap(Filename, OutWidth, OutHeight, OutWeightmapImportData); + } +} + +template +void ImportMaterialAsXWayBlend( + TVoxelStaticArray& OutMaterialIndices, + TVoxelStaticArray& OutMaterialStrengths, + const TArray& Weightmaps, const uint32 WeightmapDataIndex) +{ + OutMaterialIndices.Memzero(); + OutMaterialStrengths.Memzero(); + + if (Weightmaps.Num() <= NumChannels) + { + for (int32 Index = 0; Index < Weightmaps.Num(); Index++) + { + OutMaterialIndices[Index] = Weightmaps[Index].Index; + OutMaterialStrengths[Index] = FVoxelUtilities::UINT8ToFloat(Weightmaps[Index].Data[WeightmapDataIndex]); + } + } + else + { + TArray> WeightmapIndices; + for (int32 Index = 0; Index < Weightmaps.Num(); Index++) + { + WeightmapIndices.Add(Index); + } + + const TVoxelStaticArray, NumChannels> TopElements = FVoxelUtilities::FindTopXElements( + WeightmapIndices, [&](int32 A, int32 B) + { + return Weightmaps[A].Data[WeightmapDataIndex] < Weightmaps[B].Data[WeightmapDataIndex]; + }); + + for (int32 Index = 0; Index < NumChannels; Index++) + { + auto& Weightmap = Weightmaps[TopElements[Index].template Get<0>()]; + OutMaterialIndices[Index] = Weightmap.Index; + OutMaterialStrengths[Index] = FVoxelUtilities::UINT8ToFloat(Weightmap.Data[WeightmapDataIndex]); + } + } +} + +template +void ImportMaterialFromWeightmaps( + EVoxelHeightmapImporterMaterialConfig MaterialConfig, + const TArray& Weightmaps, const uint32 WeightmapDataIndex, + TVoxelHeightmapAssetData& HeightmapAssetData, const int32 X, const int32 Y) +{ + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::RGB) + { + FColor Color(ForceInit); + for (auto& Weightmap : Weightmaps) + { + const uint8 Value = Weightmap.Data[WeightmapDataIndex]; + switch (Weightmap.Layer) + { + case EVoxelRGBA::R: + Color.R = Value; + break; + case EVoxelRGBA::G: + Color.G = Value; + break; + case EVoxelRGBA::B: + Color.B = Value; + break; + case EVoxelRGBA::A: + Color.A = Value; + break; + } + } + HeightmapAssetData.SetMaterial_RGB(X, Y, Color); + } + else if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::SingleIndex) + { + uint8 MaxValue = Weightmaps[0].Data[WeightmapDataIndex]; + uint8 MaxIndex = Weightmaps[0].Index; + for (int32 WeightmapIndex = 1; WeightmapIndex < Weightmaps.Num(); WeightmapIndex++) + { + const auto& Weightmap = Weightmaps[WeightmapIndex]; + const uint8 Value = Weightmap.Data[WeightmapDataIndex]; + if (Value > MaxValue) + { + MaxValue = Value; + MaxIndex = Weightmap.Index; + } + } + HeightmapAssetData.SetMaterial_SingleIndex(X, Y, MaxIndex); + } + else if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FourWayBlend) + { + TVoxelStaticArray MaterialIndices; + TVoxelStaticArray MaterialStrengths; + ImportMaterialAsXWayBlend<4>(MaterialIndices, MaterialStrengths, Weightmaps, WeightmapDataIndex); + + TVoxelStaticArray FourWayBlendStrengths{ ForceInit }; + for (int32 Index = 0; Index < 4; Index++) + { + if (ensure(MaterialIndices[Index] < 4)) + { + FourWayBlendStrengths[MaterialIndices[Index]] += MaterialStrengths[Index]; + } + } + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(FourWayBlendStrengths); + + FColor Color{ ForceInit }; + Color.R = FVoxelUtilities::FloatToUINT8(Alphas[0]); + Color.G = FVoxelUtilities::FloatToUINT8(Alphas[1]); + Color.B = FVoxelUtilities::FloatToUINT8(Alphas[2]); + + HeightmapAssetData.SetMaterial_RGB(X, Y, Color); + } + else if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FiveWayBlend) + { + TVoxelStaticArray MaterialIndices; + TVoxelStaticArray MaterialStrengths; + ImportMaterialAsXWayBlend<5>(MaterialIndices, MaterialStrengths, Weightmaps, WeightmapDataIndex); + + TVoxelStaticArray FiveWayBlendStrengths{ ForceInit }; + for (int32 Index = 0; Index < 5; Index++) + { + if (ensure(MaterialIndices[Index] < 5)) + { + FiveWayBlendStrengths[MaterialIndices[Index]] += MaterialStrengths[Index]; + } + } + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<5>(FiveWayBlendStrengths); + + FColor Color; + Color.R = FVoxelUtilities::FloatToUINT8(Alphas[0]); + Color.G = FVoxelUtilities::FloatToUINT8(Alphas[1]); + Color.B = FVoxelUtilities::FloatToUINT8(Alphas[2]); + Color.A = FVoxelUtilities::FloatToUINT8(Alphas[3]); + + HeightmapAssetData.SetMaterial_RGB(X, Y, Color); + } + else if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::MultiIndex) + { + TVoxelStaticArray MaterialIndices; + TVoxelStaticArray MaterialStrengths; + ImportMaterialAsXWayBlend<4>(MaterialIndices, MaterialStrengths, Weightmaps, WeightmapDataIndex); + + const auto Alphas = FVoxelUtilities::XWayBlend_StrengthsToAlphas_Static<4>(MaterialStrengths); + + FVoxelMaterial Material; + + Material.SetMultiIndex_Index0(MaterialIndices[0]); + Material.SetMultiIndex_Index1(MaterialIndices[1]); + Material.SetMultiIndex_Index2(MaterialIndices[2]); + Material.SetMultiIndex_Index3(MaterialIndices[3]); + + Material.SetMultiIndex_Blend0_AsFloat(Alphas[0]); + Material.SetMultiIndex_Blend1_AsFloat(Alphas[1]); + Material.SetMultiIndex_Blend2_AsFloat(Alphas[2]); + + HeightmapAssetData.SetMaterial_MultiIndex(X, Y, Material); + } + else + { + check(false); + } +} + +inline EVoxelMaterialConfig GetMaterialConfigFromHeightmapConfig(EVoxelHeightmapImporterMaterialConfig MaterialConfig) +{ + switch (MaterialConfig) + { + default: ensure(false); + case EVoxelHeightmapImporterMaterialConfig::RGB: return EVoxelMaterialConfig::RGB; + case EVoxelHeightmapImporterMaterialConfig::FiveWayBlend: return EVoxelMaterialConfig::RGB; + case EVoxelHeightmapImporterMaterialConfig::FourWayBlend: return EVoxelMaterialConfig::RGB; + case EVoxelHeightmapImporterMaterialConfig::SingleIndex: return EVoxelMaterialConfig::SingleIndex; + case EVoxelHeightmapImporterMaterialConfig::MultiIndex: return EVoxelMaterialConfig::MultiIndex; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelHeightmapAssetFloatFactory::UVoxelHeightmapAssetFloatFactory() +{ + bCreateNew = false; + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = UVoxelHeightmapAssetFloat::StaticClass(); +} + +UObject* UVoxelHeightmapAssetFloatFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + for (auto& Layer : LayerInfos) + { + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FourWayBlend && Layer.Index > 3) + { + FVoxelMessages::Error("In Four Way blend, all indices must be between 0 and 3!"); + return nullptr; + } + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FiveWayBlend && Layer.Index > 4) + { + FVoxelMessages::Error("In Five Way blend, all indices must be between 0 and 4!"); + return nullptr; + } + } + + auto* Asset = NewObject(InParent, Class, Name, Flags | RF_Transactional); + + int32 Width = 0; + int32 Height = 0; + for (auto& Component : Components) + { + Width = FMath::Max(Width, Component->SectionBaseX + Component->ComponentSizeQuads); + Height = FMath::Max(Height, Component->SectionBaseY + Component->ComponentSizeQuads); + } + + // Account for additional vertex row/column at the end + Width++; + Height++; + + auto& Data = Asset->GetData(); + Data.SetSize(Width, Height, LayerInfos.Num() > 0, GetMaterialConfigFromHeightmapConfig(MaterialConfig)); + + for (auto& Component : Components) + { + FLandscapeComponentDataInterface DataInterface(Component); + + // + 1 : account for additional vertex row/column at the end of each components + // This will result in some data being written twice, but that's fine + const int32 ComponentSize = Component->ComponentSizeQuads + 1; + + if (Data.HasMaterials()) + { + TArray Weightmaps; + Weightmaps.SetNum(LayerInfos.Num()); + + for (int32 Index = 0; Index < Weightmaps.Num(); Index++) + { + auto& Weightmap = Weightmaps[Index]; + auto& WeightmapInfo = LayerInfos[Index]; + DataInterface.GetWeightmapTextureData(WeightmapInfo.LayerInfo, Weightmap.Data); + Weightmap.Layer = WeightmapInfo.Layer; + Weightmap.Index = WeightmapInfo.Index; + } + Weightmaps.RemoveAll([&](auto& Weightmap) { return Weightmap.Data.Num() == 0; }); + + const int32 WeightmapSize = (Component->SubsectionSizeQuads + 1) * Component->NumSubsections; + + for (int32 X = 0; X < ComponentSize; X++) + { + for (int32 Y = 0; Y < ComponentSize; Y++) + { + ImportMaterialFromWeightmaps( + MaterialConfig, + Weightmaps, + X + WeightmapSize * Y, + Data, + Component->SectionBaseX + X, + Component->SectionBaseY + Y); + } + } + } + + for (int32 X = 0; X < ComponentSize; X++) + { + for (int32 Y = 0; Y < ComponentSize; Y++) + { + const FVector Vertex = DataInterface.GetWorldVertex(X, Y); + const FVector LocalVertex = (Vertex - ActorLocation) / Component->GetComponentTransform().GetScale3D(); + if (ensure(Data.IsValidIndex(LocalVertex.X, LocalVertex.Y))) + { + Data.SetHeight(LocalVertex.X, LocalVertex.Y, Vertex.Z); + } + } + } + } + + Asset->Save(); + + return Asset; +} + +FString UVoxelHeightmapAssetFloatFactory::GetDefaultNewAssetName() const +{ + return AssetName; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelHeightmapAssetUINT16Factory::UVoxelHeightmapAssetUINT16Factory() +{ + bCreateNew = true; + bEditAfterNew = true; + bEditorImport = true; + SupportedClass = UVoxelHeightmapAssetUINT16::StaticClass(); +} + +bool UVoxelHeightmapAssetUINT16Factory::ConfigureProperties() +{ + // Load from default + Heightmap = GetDefault()->Heightmap; + MaterialConfig = GetDefault()->MaterialConfig; + WeightmapsInfos = GetDefault()->WeightmapsInfos; + + TSharedRef PickerWindow = SNew(SWindow) + .Title(VOXEL_LOCTEXT("Import Heightmap")) + .SizingRule(ESizingRule::Autosized); + + bool bSuccess = false; + + auto OnOkClicked = FOnClicked::CreateLambda([&]() + { + if (TryLoad()) + { + bSuccess = true; + PickerWindow->RequestDestroyWindow(); + } + return FReply::Handled(); + }); + auto OnCancelClicked = FOnClicked::CreateLambda([&]() + { + bSuccess = false; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }); + + class FVoxelHeightmapFactoryDetails : public IDetailCustomization + { + public: + FVoxelHeightmapFactoryDetails() = default; + + private: + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override + { + FSimpleDelegate RefreshDelegate = FSimpleDelegate::CreateLambda([&DetailLayout]() + { + DetailLayout.ForceRefreshDetails(); + }); + DetailLayout.GetProperty(GET_MEMBER_NAME_STATIC(UVoxelHeightmapAssetUINT16Factory, MaterialConfig))->SetOnPropertyValueChanged(RefreshDelegate); + } + }; + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea); + + auto DetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + FOnGetDetailCustomizationInstance LayoutDelegateDetails = FOnGetDetailCustomizationInstance::CreateLambda([]() { return MakeShared(); }); + DetailsPanel->RegisterInstancedCustomPropertyLayout(UVoxelHeightmapAssetUINT16Factory::StaticClass(), LayoutDelegateDetails); + DetailsPanel->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateLambda([&](const FPropertyAndParent& Property) + { + FName Name = Property.Property.GetFName(); + if (Name == GET_MEMBER_NAME_STATIC(FVoxelHeightmapImporterWeightmapInfos, Layer)) + { + return MaterialConfig == EVoxelHeightmapImporterMaterialConfig::RGB; + } + else if (Name == GET_MEMBER_NAME_STATIC(FVoxelHeightmapImporterWeightmapInfos, Index)) + { + return MaterialConfig != EVoxelHeightmapImporterMaterialConfig::RGB; + } + else + { + return true; + } + })); + DetailsPanel->SetObject(this); + + auto Widget = + SNew(SBorder) + .Visibility(EVisibility::Visible) + .BorderImage(FEditorStyle::GetBrush("Menu.Background")) + [ + SNew(SBox) + .Visibility(EVisibility::Visible) + .WidthOverride(520.0f) + [ + SNew(SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + .MaxHeight(500) + [ + SNew(SScrollBox) + + SScrollBox::Slot() + [ + DetailsPanel + ] + ] + +SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + .VAlign(VAlign_Bottom) + .Padding(8) + [ + SNew(SUniformGridPanel) + .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding")) + + SUniformGridPanel::Slot(0, 0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Create")) + .HAlign(HAlign_Center) + .Visibility(TAttribute::Create(TAttribute::FGetter::CreateLambda([&]() + { + if (Heightmap.FilePath.IsEmpty()) + { + return EVisibility::Hidden; + } + for (auto& Weightmap : WeightmapsInfos) + { + if (Weightmap.File.FilePath.IsEmpty()) + { + return EVisibility::Hidden; + } + } + return EVisibility::Visible; + }))) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnOkClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Success") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + +SUniformGridPanel::Slot(1,0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Cancel")) + .HAlign(HAlign_Center) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnCancelClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Default") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + ] + ] + ]; + + PickerWindow->SetContent(Widget); + + GEditor->EditorAddModalWindow(PickerWindow); + + // Save to default + GetMutableDefault()->Heightmap = Heightmap; + GetMutableDefault()->MaterialConfig = MaterialConfig; + GetMutableDefault()->WeightmapsInfos = WeightmapsInfos; + + return bSuccess; +} + +UObject* UVoxelHeightmapAssetUINT16Factory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + auto* Asset = NewObject(InParent, Class, Name, Flags | RF_Transactional); + if (DoImport(Asset)) + { + return Asset; + } + else + { + return nullptr; + } +} + +FString UVoxelHeightmapAssetUINT16Factory::GetDefaultNewAssetName() const +{ + return FPaths::GetBaseFilename(Heightmap.FilePath); +} + +bool UVoxelHeightmapAssetUINT16Factory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + if (auto* Asset = Cast(Obj)) + { + OutFilenames.Add(Asset->Heightmap); + for (auto& Weightmap : Asset->WeightmapsInfos) + { + OutFilenames.Add(Weightmap.File.FilePath); + } + return true; + } + else + { + return false; + } +} + +void UVoxelHeightmapAssetUINT16Factory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + if (auto* Asset = Cast(Obj)) + { + for (int32 Index = 0; Index < NewReimportPaths.Num(); Index++) + { + if (Index == 0) + { + Asset->Heightmap = NewReimportPaths[0]; + } + else if (ensure(Index - 1 < Asset->WeightmapsInfos.Num())) + { + Asset->WeightmapsInfos[Index - 1].File.FilePath = NewReimportPaths[Index]; + } + } + } +} + +EReimportResult::Type UVoxelHeightmapAssetUINT16Factory::Reimport(UObject* Obj) +{ + if (auto* Asset = Cast(Obj)) + { + Heightmap.FilePath = Asset->Heightmap; + MaterialConfig = Asset->MaterialConfig; + WeightmapsInfos = Asset->WeightmapsInfos; + if (!TryLoad()) + { + return EReimportResult::Failed; + } + return DoImport(Asset) ? EReimportResult::Succeeded : EReimportResult::Cancelled; + } + else + { + return EReimportResult::Failed; + } +} + +int32 UVoxelHeightmapAssetUINT16Factory::GetPriority() const +{ + return ImportPriority; +} + +bool UVoxelHeightmapAssetUINT16Factory::TryLoad() +{ + FVoxelScopedSlowTask Progress(1 + WeightmapsInfos.Num(), VOXEL_LOCTEXT("Creating heightmap asset...")); + Progress.MakeDialog(); + + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Processing heightmap")); + if (!HeightmapHelpers::GetHeightmap(Heightmap.FilePath, Width, Height, HeightmapImportData)) + { + return false; + } + + Weightmaps.SetNum(WeightmapsInfos.Num()); + for (int32 Index = 0; Index < Weightmaps.Num(); Index++) + { + Progress.EnterProgressFrame(1, VOXEL_LOCTEXT("Processing Weightmaps")); + + auto& Weightmap = Weightmaps[Index]; + auto& WeightmapInfo = WeightmapsInfos[Index]; + + int32 WeightmapWidth; + int32 WeightmapHeight; + FLandscapeWeightmapImportData Result; + if (!HeightmapHelpers::GetWeightmap(WeightmapInfo.File.FilePath, WeightmapWidth, WeightmapHeight, Result)) + { + return false; + } + if (WeightmapWidth != Width || WeightmapHeight != Height) + { + FVoxelEditorUtilities::ShowError(FText::Format(VOXEL_LOCTEXT("Weightmap resolution is not the same as Heightmap ({0})"), FText::FromString(WeightmapInfo.File.FilePath))); + return false; + } + Weightmap.Data = MoveTemp(Result.Data); + Weightmap.Layer = WeightmapInfo.Layer; + Weightmap.Index = WeightmapInfo.Index; + } + + return true; +} + +bool UVoxelHeightmapAssetUINT16Factory::DoImport(UVoxelHeightmapAssetUINT16* Asset) +{ + for (auto& Layer : WeightmapsInfos) + { + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FourWayBlend && Layer.Index > 3) + { + FVoxelMessages::Error("In Four Way blend, all indices must be between 0 and 3!"); + return false; + } + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::FiveWayBlend && Layer.Index > 4) + { + FVoxelMessages::Error("In Five Way blend, all indices must be between 0 and 4!"); + return false; + } + } + +#define RETURN_IF_CANCEL() { if (Progress.ShouldCancel()) { FVoxelEditorUtilities::ShowError(VOXEL_LOCTEXT("Canceled!")); return false; } } + + FVoxelScopedSlowTask Progress(3.f, VOXEL_LOCTEXT("Creating heightmap asset...")); + Progress.MakeDialog(true, true); + + RETURN_IF_CANCEL(); + + auto& Data = Asset->GetData(); + Data.SetSize(Width, Height, Weightmaps.Num() > 0, GetMaterialConfigFromHeightmapConfig(MaterialConfig)); + + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Copying heightmap")); + { + const int64 Total = int64(Width) * int64(Height); + FVoxelScopedSlowTask HeightmapProgress(Total); + for (int32 X = 0; X < Width; X++) + { + for (int32 Y = 0; Y < Height; Y++) + { + const int64 Index = Data.GetIndex(X, Y); + + if ((Index & 0x0000FFFF) == 0) + { + HeightmapProgress.EnterProgressFrame(FMath::Min(0x0000FFFF, Total - Index)); + RETURN_IF_CANCEL(); + } + + Data.SetHeight(X, Y, HeightmapImportData.Data[Index]); + } + } + } + + RETURN_IF_CANCEL(); + + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Copying weightmaps")); + if (Data.HasMaterials()) + { + FVoxelScopedSlowTask WeightmapProgress(int64(Width) * int64(Height)); + for (int32 X = 0; X < Width; X++) + { + for (int32 Y = 0; Y < Height; Y++) + { + const int64 Index = Data.GetIndex(X, Y); + + if ((Index & 0x0000FFFF) == 0) + { + WeightmapProgress.EnterProgressFrame(0x0000FFFF); + RETURN_IF_CANCEL(); + } + + ImportMaterialFromWeightmaps( + MaterialConfig, + Weightmaps, + Index, + Data, + X, Y); + } + } + } + + RETURN_IF_CANCEL(); + + // Copy config + Asset->Heightmap = Heightmap.FilePath; + Asset->MaterialConfig = MaterialConfig; + Asset->WeightmapsInfos = WeightmapsInfos; + Asset->Weightmaps.Reset(); + for (auto& Weightmap : WeightmapsInfos) + { + FString Path; + if (MaterialConfig == EVoxelHeightmapImporterMaterialConfig::RGB) + { + switch (Weightmap.Layer) + { + case EVoxelRGBA::R: + Path += "Channel = R"; + break; + case EVoxelRGBA::G: + Path += "Channel = G"; + break; + case EVoxelRGBA::B: + Path += "Channel = B"; + break; + case EVoxelRGBA::A: + Path += "Channel = A"; + break; + default: + check(false); + break; + } + } + else + { + Path += "Index = " + FString::FromInt(Weightmap.Index); + } + Path += "; Path = " + Weightmap.File.FilePath; + Asset->Weightmaps.Add(Path); + } + + Progress.EnterProgressFrame(1.f, VOXEL_LOCTEXT("Compressing")); + + Asset->Save(); + + return true; +#undef RETURN_IF_CANCEL +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.h new file mode 100644 index 00000000..9172b205 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.h @@ -0,0 +1,95 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "EditorReimportHandler.h" +#include "LandscapeFileFormatInterface.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelImporters/VoxelLandscapeImporter.h" +#include "VoxelHeightmapAssetFactory.generated.h" + +class ULandscapeComponent; + +UCLASS() +class UVoxelHeightmapAssetFloatFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelHeightmapAssetFloatFactory(); + + UPROPERTY() + EVoxelHeightmapImporterMaterialConfig MaterialConfig; + + UPROPERTY() + TArray LayerInfos; + + UPROPERTY() + TArray Components; + + UPROPERTY() + FVector ActorLocation; + + UPROPERTY() + FString AssetName; + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual FString GetDefaultNewAssetName() const override; + // End of UFactory interface +}; + +namespace FVoxelHeightmapImportersHelpers +{ + struct FWeightmap + { + TArray Data; + EVoxelRGBA Layer = EVoxelRGBA::R; + uint8 Index = 0; + + FWeightmap() = default; + }; +} + +UCLASS(HideCategories=Object, CollapseCategories) +class UVoxelHeightmapAssetUINT16Factory : public UFactory, public FReimportHandler +{ + GENERATED_BODY() + +public: + UVoxelHeightmapAssetUINT16Factory(); + + UPROPERTY(EditAnywhere, Category = "Import configuration") + FFilePath Heightmap; + + UPROPERTY(EditAnywhere, Category = "Import configuration") + EVoxelHeightmapImporterMaterialConfig MaterialConfig; + + UPROPERTY(EditAnywhere, Category = "Import configuration", meta = (DisplayName = "Weightmaps")) + TArray WeightmapsInfos; + + //~ Begin UFactory Interface + virtual bool ConfigureProperties() override; + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual FString GetDefaultNewAssetName() const override; + //~ End UFactory Interface + + //~ Begin FReimportHandler Interface + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + virtual int32 GetPriority() const override; + //~ End FReimportHandler Interface + +private: + int32 Width = 0; + int32 Height = 0; + FLandscapeHeightmapImportData HeightmapImportData; + TArray Weightmaps; + + bool TryLoad(); + bool DoImport(UVoxelHeightmapAssetUINT16* Asset); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelMaterialCollectionFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelMaterialCollectionFactory.h new file mode 100644 index 00000000..476ceaf9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelMaterialCollectionFactory.h @@ -0,0 +1,116 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h" +#include "VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h" +#include "VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h" +#include "VoxelMaterialCollectionFactory.generated.h" + +UCLASS() +class UVoxelBasicMaterialCollectionFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelBasicMaterialCollectionFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelBasicMaterialCollection::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelInstancedMaterialCollectionTemplatesFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelInstancedMaterialCollectionTemplatesFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelInstancedMaterialCollectionTemplates::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelInstancedMaterialCollectionFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelInstancedMaterialCollectionFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelInstancedMaterialCollection::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelInstancedMaterialCollectionInstanceFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelInstancedMaterialCollectionInstanceFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelInstancedMaterialCollectionInstance::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelLandscapeMaterialCollectionFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelLandscapeMaterialCollectionFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelLandscapeMaterialCollection::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnerConfigFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnerConfigFactory.h new file mode 100644 index 00000000..32062648 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnerConfigFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelSpawners/VoxelSpawnerConfig.h" +#include "VoxelSpawnerConfigFactory.generated.h" + +UCLASS() +class UVoxelSpawnerConfigFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelSpawnerConfigFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelSpawnerConfig::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnersFactories.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnersFactories.h new file mode 100644 index 00000000..fadb23a0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelSpawnersFactories.h @@ -0,0 +1,95 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" +#include "VoxelSpawnersFactories.generated.h" + +UCLASS() +class UVoxelMeshSpawnerFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelMeshSpawnerFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelMeshSpawner::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelMeshSpawnerGroupFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelMeshSpawnerGroupFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelMeshSpawnerGroup::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelAssetSpawnerFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelAssetSpawnerFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelAssetSpawner::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; + +UCLASS() +class UVoxelSpawnerGroupFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelSpawnerGroupFactory() + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelSpawnerGroup::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags | RF_Transactional); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelWorldSaveObjectFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelWorldSaveObjectFactory.h new file mode 100644 index 00000000..d19b196a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Factories/VoxelWorldSaveObjectFactory.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Factories/Factory.h" +#include "VoxelData/VoxelSave.h" +#include "VoxelWorldSaveObjectFactory.generated.h" + +UCLASS() +class UVoxelWorldSaveObjectFactory : public UFactory +{ + GENERATED_BODY() + +public: + UVoxelWorldSaveObjectFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + { + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = UVoxelWorldSaveObject::StaticClass(); + } + + // UFactory interface + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override + { + return NewObject(InParent, Class, Name, Flags); + } + // End of UFactory interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.cpp new file mode 100644 index 00000000..34468973 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.cpp @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#include "Importers/MagicaVox.h" +#include "VoxelAssets/VoxelDataAssetData.inl" + +#define OGT_VOX_IMPLEMENTATION +#include "ogt_vox.h" + +#include "Misc/FileHelper.h" +#include "Misc/ScopeExit.h" +#include "Misc/MessageDialog.h" +#include "Modules/ModuleManager.h" + +bool MagicaVox::ImportToAsset(const FString& Filename, FVoxelDataAssetData& Asset, bool bUsePalette) +{ + TArray Bytes; + if (!FFileHelper::LoadFileToArray(Bytes, *Filename)) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Error when opening the file"))); + return false; + } + + const ogt_vox_scene* Scene = ogt_vox_read_scene(Bytes.GetData(), Bytes.Num()); + if (!Scene) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Error when decoding the scene"))); + return false; + } + + ON_SCOPE_EXIT + { + ogt_vox_destroy_scene(Scene); + }; + + const auto Models = TArrayView(Scene->models, Scene->num_models); + if (Models.Num() == 0) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("No models in the file"))); + return false; + } + if (Models.Num() > 1) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("More than one model in the file. Only the first one will be imported."))); + } + if (!ensure(Models[0])) + { + return false; + } + auto& Model = *Models[0]; + check(Model.voxel_data); + + Asset.SetSize(FIntVector(Model.size_y, Model.size_x, Model.size_z), true); + + for (uint32 Z = 0; Z < Model.size_z; Z++) + { + for (uint32 Y = 0; Y < Model.size_y; Y++) + { + for (uint32 X = 0; X < Model.size_x; X++) + { + const uint32 Index = X + Model.size_x * Y + Model.size_x * Model.size_y * Z; + const uint8 Voxel = Model.voxel_data[Index]; + + Asset.SetValue(Y, X, Z, Voxel > 0 ? FVoxelValue::Full() : FVoxelValue::Empty()); + + FVoxelMaterial Material(ForceInit); + if (bUsePalette) + { + const auto MagicaColor = Scene->palette.color[Voxel]; + // Store the color as a linear color, so edits can be in linear space + const FColor Color = FLinearColor(FColor(MagicaColor.r, MagicaColor.g, MagicaColor.b, MagicaColor.a)).ToFColor(false); + Material.SetColor(Color); + } + else + { + Material.SetSingleIndex(Voxel); + } + + Asset.SetMaterial(Y, X, Z, Material); + } + } + } + + return true; +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.h new file mode 100644 index 00000000..91deb715 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/MagicaVox.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelDataAssetData; + +namespace MagicaVox +{ + bool ImportToAsset(const FString& Filename, FVoxelDataAssetData& Asset, bool bUsePalette); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.cpp new file mode 100644 index 00000000..c298103a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.cpp @@ -0,0 +1,91 @@ +// Copyright 2020 Phyronnaz + +#include "Importers/RawVox.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "Misc/FileHelper.h" +#include "Misc/MessageDialog.h" + +bool RawVox::ImportToAsset(const FString& File, FVoxelDataAssetData& Asset) +{ + TArray Result; + bool bSuccess = FFileHelper::LoadFileToArray(Result, *File); + if (!bSuccess) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Error when opening the file"))); + return false; + } + + int32 Position = 0; + TCHAR Start[4]; + for (int32 i = 0; i < 4; i++) + { + Start[i] = Result[Position]; + Position++; + } + bSuccess = Start[0] == 'X' && Start[1] == 'O' && Start[2] == 'V' && Start[3] == 'R'; + if (!bSuccess) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("File is corrupted"))); + return false; + } + + const int32 SizeX = Result[Position] + 256 * Result[Position + 1] + 256 * 256 * Result[Position + 2] + 256 * 256 * 256 * Result[Position + 3]; + Position += 4; + const int32 SizeY = Result[Position] + 256 * Result[Position + 1] + 256 * 256 * Result[Position + 2] + 256 * 256 * 256 * Result[Position + 3]; + Position += 4; + const int32 SizeZ = Result[Position] + 256 * Result[Position + 1] + 256 * 256 * Result[Position + 2] + 256 * 256 * 256 * Result[Position + 3]; + Position += 4; + + const int32 BitsPerVoxel = Result[Position]; + Position += 4; + + union + { + float f; + uint8 b[4]; + } U; + + Asset.SetSize(FIntVector(SizeX, SizeZ, SizeY), false); + + for (int32 Z = 0; Z < SizeZ; Z++) + { + for (int32 Y = 0; Y < SizeY; Y++) + { + for (int32 X = 0; X < SizeX; X++) + { + float Val; + if (BitsPerVoxel == 8) + { + Val = (Result[Position] - 128) / 128.f; + Position++; + } + else if (BitsPerVoxel == 16) + { + Val = (Result[Position] + 256 * Result[Position + 1] - 32768) / 32768.f; + Position += 2; + } + else if (BitsPerVoxel == 32) + { + U.b[0] = Result[Position]; + Position++; + U.b[1] = Result[Position]; + Position++; + U.b[2] = Result[Position]; + Position++; + U.b[3] = Result[Position]; + Position++; + + Val = -(U.f - 0.5) * 2; + } + else + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("File is corrupted"))); + return false; + } + Asset.SetValue(X, Z, Y, FVoxelValue(Val)); + } + } + } + + return true; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.h new file mode 100644 index 00000000..4e745a81 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/RawVox.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct FVoxelDataAssetData; + +namespace RawVox +{ + bool ImportToAsset(const FString& File, FVoxelDataAssetData& Asset); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/ogt_vox.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/ogt_vox.h new file mode 100644 index 00000000..6a783f7f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Importers/ogt_vox.h @@ -0,0 +1,1959 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +/* + opengametools vox file reader/writer - v0.6 - MIT license - Justin Paver, Oct 2019 + + This is a single-header-file library that provides easy-to-use + support for reading MagicaVoxel .vox files into structures that + are easy to dereference and extract information from. It also + supports writing back out to .vox file from those structures. + + Please see the MIT license information at the end of this file. + + Also, please consider sharing any improvements you make. + + For more information and more tools, visit: + https://github.com/jpaver/opengametools + + HOW TO COMPILE THIS LIBRARY + + 1. To compile this library, do this in *one* C or C++ file: + #define OGT_VOX_IMPLEMENTATION + #include "ogt_vox.h" + + 2. From any other module, it is sufficient to just #include this as usual: + #include "ogt_vox.h" + + HOW TO READ A VOX SCENE (See demo_vox.cpp) + + 1. load a .vox file off disk into a memory buffer. + + 2. construct a scene from the memory buffer: + ogt_vox_scene* scene = ogt_vox_read_scene(buffer, buffer_size); + + 3. use the scene members to extract the information you need. eg. + printf("# of layers: %u\n", scene->num_layers ); + + 4. destroy the scene: + ogt_vox_destroy_scene(scene); + + HOW TO MERGE MULTIPLE VOX SCENES (See merge_vox.cpp) + + 1. construct multiple scenes from files you want to merge. + + // read buffer1/buffer_size1 from "test1.vox" + // read buffer2/buffer_size2 from "test2.vox" + // read buffer3/buffer_size3 from "test3.vox" + ogt_vox_scene* scene1 = ogt_vox_read_scene(buffer1, buffer_size1); + ogt_vox_scene* scene2 = ogt_vox_read_scene(buffer2, buffer_size2); + ogt_vox_scene* scene3 = ogt_vox_read_scene(buffer3, buffer_size3); + + 2. construct a merged scene + + const ogt_vox_scene* scenes[] = {scene1, scene2, scene3}; + ogt_vox_scene* merged_scene = ogt_vox_merge_scenes(scenes, 3, NULL, 0); + + 3. save out the merged scene + + uint8_t* out_buffer = ogt_vox_write_scene(merged_scene, &out_buffer_size); + // save out_buffer to disk as a .vox file (it has length out_buffer_size) + + 4. destroy the merged scene: + + ogt_vox_destroy_scene(merged_scene); + + EXPLANATION OF SCENE ELEMENTS: + + A ogt_vox_scene comprises primarily a set of instances, models, layers and a palette. + + A ogt_vox_palette contains a set of 256 colors that is used for the scene. + Each color is represented by a 4-tuple called an ogt_vox_rgba which contains red, + green, blue and alpha values for the color. + + A ogt_vox_model is a 3-dimensional grid of voxels, where each of those voxels + is represented by an 8-bit color index. Voxels are arranged in order of increasing + X then increasing Y then increasing Z. + + Given the x,y,z values for a voxel within the model dimensions, the voxels index + in the grid can be obtained as follows: + + voxel_index = x + (y * model->size_x) + (z * model->size_x * model->size_y) + + The index is only valid if the coordinate x,y,z satisfy the following conditions: + 0 <= x < model->size_x -AND- + 0 <= y < model->size_y -AND- + 0 <= z < model->size_z + + A voxels color index can be obtained as follows: + + uint8_t color_index = model->voxel_data[voxel_index]; + + If color_index == 0, the voxel is not solid and can be skipped, + If color_index != 0, the voxel is solid and can be used to lookup the color in the palette: + + ogt_vox_rgba color = scene->palette.color[ color_index] + + A ogt_vox_instance is an individual placement of a voxel model within the scene. Each + instance has a transform that determines its position and orientation within the scene, + but it also has an index that specifies which model the instance uses for its shape. It + is expected that there is a many-to-one mapping of instances to models. + + An ogt_vox_layer is used to conceptually group instances. Each instance indexes the + layer that it belongs to, but the layer itself has its own name and hidden/shown state. + + EXPLANATION OF MERGED SCENES: + + A merged scene contains all the models and all the scene instances from + each of the scenes that were passed into it. + + The merged scene will have a combined palette of all the source scene + palettes by trying to match existing colors exactly, and falling back + to an RGB-distance matched color when all 256 colors in the merged + scene palette has been allocated. + + You can explicitly control up to 255 merge palette colors by providing + those colors to ogt_vox_merge_scenes in the required_colors parameters eg. + + const ogt_vox_palette palette; // load this via .vox or procedurally or whatever + const ogt_vox_scene* scenes[] = {scene1, scene2, scene3}; + // palette.color[0] is always the empty color which is why we pass 255 colors starting from index 1 only: + ogt_vox_scene* merged_scene = ogt_vox_merge_scenes(scenes, 3, &palette.color[1], 255); +*/ +#ifndef OGT_VOX_H__ +#define OGT_VOX_H__ + +#if defined(_MSC_VER) + // general VS* + #include +#elif defined(__APPLE__) + // general Apple compiler +#elif defined(__GNUC__) + // any GCC* + #include + #include // for size_t +#else + #error some fixup needed for this platform? +#endif + + // denotes an invalid group index. Usually this is only applicable to the scene's root group's parent. + static const uint32_t k_invalid_group_index = UINT32_MAX; + + // color + typedef struct ogt_vox_rgba + { + uint8_t r,g,b,a; // red, green, blue and alpha components of a color. + } ogt_vox_rgba; + + // column-major 4x4 matrix + typedef struct ogt_vox_transform + { + float m00, m01, m02, m03; // column 0 of 4x4 matrix, 1st three elements = x axis vector, last element always 0.0 + float m10, m11, m12, m13; // column 1 of 4x4 matrix, 1st three elements = y axis vector, last element always 0.0 + float m20, m21, m22, m23; // column 2 of 4x4 matrix, 1st three elements = z axis vector, last element always 0.0 + float m30, m31, m32, m33; // column 3 of 4x4 matrix. 1st three elements = translation vector, last element always 1.0 + } ogt_vox_transform; + + // a palette of colors + typedef struct ogt_vox_palette + { + ogt_vox_rgba color[256]; // palette of colors. use the voxel indices to lookup color from the palette. + } ogt_vox_palette; + + // a 3-dimensional model of voxels + typedef struct ogt_vox_model + { + uint32_t size_x; // number of voxels in the local x dimension + uint32_t size_y; // number of voxels in the local y dimension + uint32_t size_z; // number of voxels in the local z dimension + uint32_t voxel_hash; // hash of the content of the grid. + const uint8_t* voxel_data; // grid of voxel data comprising color indices in x -> y -> z order. a color index of 0 means empty, all other indices mean solid and can be used to index the scene's palette to obtain the color for the voxel. + } ogt_vox_model; + + // an instance of a model within the scene + typedef struct ogt_vox_instance + { + const char* name; // name of the instance if there is one, will be NULL otherwise. + ogt_vox_transform transform; // orientation and position of this instance within the scene. This is relative to its group local transform if group_index is not 0 + uint32_t model_index; // index of the model used by this instance. used to lookup the model in the scene's models[] array. + uint32_t layer_index; // index of the layer used by this instance. used to lookup the layer in the scene's layers[] array. + uint32_t group_index; // this will be the index of the group in the scene's groups[] array. If group is zero it will be the scene root group and the instance transform will be a world-space transform, otherwise the transform is relative to the group. + bool hidden; // whether this instance is individually hidden or not. Note: the instance can also be hidden when its layer is hidden, or if it belongs to a group that is hidden. + } ogt_vox_instance; + + // describes a layer within the scene + typedef struct ogt_vox_layer + { + const char* name; // name of this layer if there is one, will be NULL otherwise. + bool hidden; // whether this layer is hidden or not. + } ogt_vox_layer; + + // describes a group within the scene + typedef struct ogt_vox_group + { + ogt_vox_transform transform; // transform of this group relative to its parent group (if any), otherwise this will be relative to world-space. + uint32_t parent_group_index; // if this group is parented to another group, this will be the index of its parent in the scene's groups[] array, otherwise this group will be the scene root group and this value will be k_invalid_group_index + uint32_t layer_index; // which layer this group belongs to. used to lookup the layer in the scene's layers[] array. + bool hidden; // whether this group is hidden or not. + } ogt_vox_group; + + // the scene parsed from a .vox file. + typedef struct ogt_vox_scene + { + uint32_t num_models; // number of models within the scene. + uint32_t num_instances; // number of instances in the scene + uint32_t num_layers; // number of layers in the scene + uint32_t num_groups; // number of groups in the scene + const ogt_vox_model** models; // array of models. size is num_models + const ogt_vox_instance* instances; // array of instances. size is num_instances + const ogt_vox_layer* layers; // array of layers. size is num_layers + const ogt_vox_group* groups; // array of groups. size is num_groups + ogt_vox_palette palette; // the palette for this scene + } ogt_vox_scene; + + // allocate memory function interface. pass in size, and get a pointer to memory with at least that size available. + typedef void* (*ogt_vox_alloc_func)(size_t size); + + // free memory function interface. pass in a pointer previously allocated and it will be released back to the system managing memory. + typedef void (*ogt_vox_free_func)(void* ptr); + + // override the default scene memory allocator if you need to control memory precisely. + void ogt_vox_set_memory_allocator(ogt_vox_alloc_func alloc_func, ogt_vox_free_func free_func); + void* ogt_vox_malloc(size_t size); + void ogt_vox_free(void* mem); + + // flags for ogt_vox_read_scene_with_flags + static const uint32_t k_read_scene_flags_groups = 1 << 0; // if not specified, all instance transforms will be flattened into world space. If specified, will read group information and keep all transforms as local transform relative to the group they are in. + + // creates a scene from a vox file within a memory buffer of a given size. + // you can destroy the input buffer once you have the scene as this function will allocate separate memory for the scene objecvt. + const ogt_vox_scene* ogt_vox_read_scene(const uint8_t* buffer, uint32_t buffer_size); + + // just like ogt_vox_read_scene, but you can additionally pass a union of k_read_scene_flags + const ogt_vox_scene* ogt_vox_read_scene_with_flags(const uint8_t* buffer, uint32_t buffer_size, uint32_t read_flags); + + // destroys a scene object to release its memory. + void ogt_vox_destroy_scene(const ogt_vox_scene* scene); + + // writes the scene to a new buffer and returns the buffer size. free the buffer with ogt_vox_free + uint8_t* ogt_vox_write_scene(const ogt_vox_scene* scene, uint32_t* buffer_size); + + // merges the specified scenes together to create a bigger scene. Merged scene can be destroyed using ogt_vox_destroy_scene + // If you require specific colors in the merged scene palette, provide up to and including 255 of them via required_colors/required_color_count. + ogt_vox_scene* ogt_vox_merge_scenes(const ogt_vox_scene** scenes, uint32_t scene_count, const ogt_vox_rgba* required_colors, const uint32_t required_color_count); + +#endif // OGT_VOX_H__ + +//----------------------------------------------------------------------------------------------------------------- +// +// If you're only interested in using this library, everything you need is above this point. +// If you're interested in how this library works, everything you need is below this point. +// +//----------------------------------------------------------------------------------------------------------------- +#ifdef OGT_VOX_IMPLEMENTATION + #include + #include + #include + #include + + // MAKE_VOX_CHUNK_ID: used to construct a literal to describe a chunk in a .vox file. + #define MAKE_VOX_CHUNK_ID(c0,c1,c2,c3) ( (c0<<0) | (c1<<8) | (c2<<16) | (c3<<24) ) + + static const uint32_t CHUNK_ID_VOX_ = MAKE_VOX_CHUNK_ID('V','O','X',' '); + static const uint32_t CHUNK_ID_MAIN = MAKE_VOX_CHUNK_ID('M','A','I','N'); + static const uint32_t CHUNK_ID_SIZE = MAKE_VOX_CHUNK_ID('S','I','Z','E'); + static const uint32_t CHUNK_ID_XYZI = MAKE_VOX_CHUNK_ID('X','Y','Z','I'); + static const uint32_t CHUNK_ID_RGBA = MAKE_VOX_CHUNK_ID('R','G','B','A'); + static const uint32_t CHUNK_ID_nTRN = MAKE_VOX_CHUNK_ID('n','T','R','N'); + static const uint32_t CHUNK_ID_nGRP = MAKE_VOX_CHUNK_ID('n','G','R','P'); + static const uint32_t CHUNK_ID_nSHP = MAKE_VOX_CHUNK_ID('n','S','H','P'); + static const uint32_t CHUNK_ID_IMAP = MAKE_VOX_CHUNK_ID('I','M','A','P'); + static const uint32_t CHUNK_ID_LAYR = MAKE_VOX_CHUNK_ID('L','A','Y','R'); + static const uint32_t CHUNK_ID_MATL = MAKE_VOX_CHUNK_ID('M','A','T','L'); + static const uint32_t CHUNK_ID_MATT = MAKE_VOX_CHUNK_ID('M','A','T','T'); + static const uint32_t CHUNK_ID_rOBJ = MAKE_VOX_CHUNK_ID('r','O','B','J'); + + // Some older .vox files will not store a palette, in which case the following palette will be used! + static const uint8_t k_default_vox_palette[256 * 4] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xff, 0xff, 0xff, 0x99, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xcc, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xff, + 0xff, 0xcc, 0x99, 0xff, 0xff, 0xcc, 0x66, 0xff, 0xff, 0xcc, 0x33, 0xff, 0xff, 0xcc, 0x00, 0xff, 0xff, 0x99, 0xff, 0xff, 0xff, 0x99, 0xcc, 0xff, 0xff, 0x99, 0x99, 0xff, 0xff, 0x99, 0x66, 0xff, + 0xff, 0x99, 0x33, 0xff, 0xff, 0x99, 0x00, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xcc, 0xff, 0xff, 0x66, 0x99, 0xff, 0xff, 0x66, 0x66, 0xff, 0xff, 0x66, 0x33, 0xff, 0xff, 0x66, 0x00, 0xff, + 0xff, 0x33, 0xff, 0xff, 0xff, 0x33, 0xcc, 0xff, 0xff, 0x33, 0x99, 0xff, 0xff, 0x33, 0x66, 0xff, 0xff, 0x33, 0x33, 0xff, 0xff, 0x33, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xcc, 0xff, + 0xff, 0x00, 0x99, 0xff, 0xff, 0x00, 0x66, 0xff, 0xff, 0x00, 0x33, 0xff, 0xff, 0x00, 0x00, 0xff, 0xcc, 0xff, 0xff, 0xff, 0xcc, 0xff, 0xcc, 0xff, 0xcc, 0xff, 0x99, 0xff, 0xcc, 0xff, 0x66, 0xff, + 0xcc, 0xff, 0x33, 0xff, 0xcc, 0xff, 0x00, 0xff, 0xcc, 0xcc, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0x99, 0xff, 0xcc, 0xcc, 0x66, 0xff, 0xcc, 0xcc, 0x33, 0xff, 0xcc, 0xcc, 0x00, 0xff, + 0xcc, 0x99, 0xff, 0xff, 0xcc, 0x99, 0xcc, 0xff, 0xcc, 0x99, 0x99, 0xff, 0xcc, 0x99, 0x66, 0xff, 0xcc, 0x99, 0x33, 0xff, 0xcc, 0x99, 0x00, 0xff, 0xcc, 0x66, 0xff, 0xff, 0xcc, 0x66, 0xcc, 0xff, + 0xcc, 0x66, 0x99, 0xff, 0xcc, 0x66, 0x66, 0xff, 0xcc, 0x66, 0x33, 0xff, 0xcc, 0x66, 0x00, 0xff, 0xcc, 0x33, 0xff, 0xff, 0xcc, 0x33, 0xcc, 0xff, 0xcc, 0x33, 0x99, 0xff, 0xcc, 0x33, 0x66, 0xff, + 0xcc, 0x33, 0x33, 0xff, 0xcc, 0x33, 0x00, 0xff, 0xcc, 0x00, 0xff, 0xff, 0xcc, 0x00, 0xcc, 0xff, 0xcc, 0x00, 0x99, 0xff, 0xcc, 0x00, 0x66, 0xff, 0xcc, 0x00, 0x33, 0xff, 0xcc, 0x00, 0x00, 0xff, + 0x99, 0xff, 0xff, 0xff, 0x99, 0xff, 0xcc, 0xff, 0x99, 0xff, 0x99, 0xff, 0x99, 0xff, 0x66, 0xff, 0x99, 0xff, 0x33, 0xff, 0x99, 0xff, 0x00, 0xff, 0x99, 0xcc, 0xff, 0xff, 0x99, 0xcc, 0xcc, 0xff, + 0x99, 0xcc, 0x99, 0xff, 0x99, 0xcc, 0x66, 0xff, 0x99, 0xcc, 0x33, 0xff, 0x99, 0xcc, 0x00, 0xff, 0x99, 0x99, 0xff, 0xff, 0x99, 0x99, 0xcc, 0xff, 0x99, 0x99, 0x99, 0xff, 0x99, 0x99, 0x66, 0xff, + 0x99, 0x99, 0x33, 0xff, 0x99, 0x99, 0x00, 0xff, 0x99, 0x66, 0xff, 0xff, 0x99, 0x66, 0xcc, 0xff, 0x99, 0x66, 0x99, 0xff, 0x99, 0x66, 0x66, 0xff, 0x99, 0x66, 0x33, 0xff, 0x99, 0x66, 0x00, 0xff, + 0x99, 0x33, 0xff, 0xff, 0x99, 0x33, 0xcc, 0xff, 0x99, 0x33, 0x99, 0xff, 0x99, 0x33, 0x66, 0xff, 0x99, 0x33, 0x33, 0xff, 0x99, 0x33, 0x00, 0xff, 0x99, 0x00, 0xff, 0xff, 0x99, 0x00, 0xcc, 0xff, + 0x99, 0x00, 0x99, 0xff, 0x99, 0x00, 0x66, 0xff, 0x99, 0x00, 0x33, 0xff, 0x99, 0x00, 0x00, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xcc, 0xff, 0x66, 0xff, 0x99, 0xff, 0x66, 0xff, 0x66, 0xff, + 0x66, 0xff, 0x33, 0xff, 0x66, 0xff, 0x00, 0xff, 0x66, 0xcc, 0xff, 0xff, 0x66, 0xcc, 0xcc, 0xff, 0x66, 0xcc, 0x99, 0xff, 0x66, 0xcc, 0x66, 0xff, 0x66, 0xcc, 0x33, 0xff, 0x66, 0xcc, 0x00, 0xff, + 0x66, 0x99, 0xff, 0xff, 0x66, 0x99, 0xcc, 0xff, 0x66, 0x99, 0x99, 0xff, 0x66, 0x99, 0x66, 0xff, 0x66, 0x99, 0x33, 0xff, 0x66, 0x99, 0x00, 0xff, 0x66, 0x66, 0xff, 0xff, 0x66, 0x66, 0xcc, 0xff, + 0x66, 0x66, 0x99, 0xff, 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x33, 0xff, 0x66, 0x66, 0x00, 0xff, 0x66, 0x33, 0xff, 0xff, 0x66, 0x33, 0xcc, 0xff, 0x66, 0x33, 0x99, 0xff, 0x66, 0x33, 0x66, 0xff, + 0x66, 0x33, 0x33, 0xff, 0x66, 0x33, 0x00, 0xff, 0x66, 0x00, 0xff, 0xff, 0x66, 0x00, 0xcc, 0xff, 0x66, 0x00, 0x99, 0xff, 0x66, 0x00, 0x66, 0xff, 0x66, 0x00, 0x33, 0xff, 0x66, 0x00, 0x00, 0xff, + 0x33, 0xff, 0xff, 0xff, 0x33, 0xff, 0xcc, 0xff, 0x33, 0xff, 0x99, 0xff, 0x33, 0xff, 0x66, 0xff, 0x33, 0xff, 0x33, 0xff, 0x33, 0xff, 0x00, 0xff, 0x33, 0xcc, 0xff, 0xff, 0x33, 0xcc, 0xcc, 0xff, + 0x33, 0xcc, 0x99, 0xff, 0x33, 0xcc, 0x66, 0xff, 0x33, 0xcc, 0x33, 0xff, 0x33, 0xcc, 0x00, 0xff, 0x33, 0x99, 0xff, 0xff, 0x33, 0x99, 0xcc, 0xff, 0x33, 0x99, 0x99, 0xff, 0x33, 0x99, 0x66, 0xff, + 0x33, 0x99, 0x33, 0xff, 0x33, 0x99, 0x00, 0xff, 0x33, 0x66, 0xff, 0xff, 0x33, 0x66, 0xcc, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x66, 0xff, 0x33, 0x66, 0x33, 0xff, 0x33, 0x66, 0x00, 0xff, + 0x33, 0x33, 0xff, 0xff, 0x33, 0x33, 0xcc, 0xff, 0x33, 0x33, 0x99, 0xff, 0x33, 0x33, 0x66, 0xff, 0x33, 0x33, 0x33, 0xff, 0x33, 0x33, 0x00, 0xff, 0x33, 0x00, 0xff, 0xff, 0x33, 0x00, 0xcc, 0xff, + 0x33, 0x00, 0x99, 0xff, 0x33, 0x00, 0x66, 0xff, 0x33, 0x00, 0x33, 0xff, 0x33, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xcc, 0xff, 0x00, 0xff, 0x99, 0xff, 0x00, 0xff, 0x66, 0xff, + 0x00, 0xff, 0x33, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xcc, 0xff, 0xff, 0x00, 0xcc, 0xcc, 0xff, 0x00, 0xcc, 0x99, 0xff, 0x00, 0xcc, 0x66, 0xff, 0x00, 0xcc, 0x33, 0xff, 0x00, 0xcc, 0x00, 0xff, + 0x00, 0x99, 0xff, 0xff, 0x00, 0x99, 0xcc, 0xff, 0x00, 0x99, 0x99, 0xff, 0x00, 0x99, 0x66, 0xff, 0x00, 0x99, 0x33, 0xff, 0x00, 0x99, 0x00, 0xff, 0x00, 0x66, 0xff, 0xff, 0x00, 0x66, 0xcc, 0xff, + 0x00, 0x66, 0x99, 0xff, 0x00, 0x66, 0x66, 0xff, 0x00, 0x66, 0x33, 0xff, 0x00, 0x66, 0x00, 0xff, 0x00, 0x33, 0xff, 0xff, 0x00, 0x33, 0xcc, 0xff, 0x00, 0x33, 0x99, 0xff, 0x00, 0x33, 0x66, 0xff, + 0x00, 0x33, 0x33, 0xff, 0x00, 0x33, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xcc, 0xff, 0x00, 0x00, 0x99, 0xff, 0x00, 0x00, 0x66, 0xff, 0x00, 0x00, 0x33, 0xff, 0xee, 0x00, 0x00, 0xff, + 0xdd, 0x00, 0x00, 0xff, 0xbb, 0x00, 0x00, 0xff, 0xaa, 0x00, 0x00, 0xff, 0x88, 0x00, 0x00, 0xff, 0x77, 0x00, 0x00, 0xff, 0x55, 0x00, 0x00, 0xff, 0x44, 0x00, 0x00, 0xff, 0x22, 0x00, 0x00, 0xff, + 0x11, 0x00, 0x00, 0xff, 0x00, 0xee, 0x00, 0xff, 0x00, 0xdd, 0x00, 0xff, 0x00, 0xbb, 0x00, 0xff, 0x00, 0xaa, 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, 0x00, 0x77, 0x00, 0xff, 0x00, 0x55, 0x00, 0xff, + 0x00, 0x44, 0x00, 0xff, 0x00, 0x22, 0x00, 0xff, 0x00, 0x11, 0x00, 0xff, 0x00, 0x00, 0xee, 0xff, 0x00, 0x00, 0xdd, 0xff, 0x00, 0x00, 0xbb, 0xff, 0x00, 0x00, 0xaa, 0xff, 0x00, 0x00, 0x88, 0xff, + 0x00, 0x00, 0x77, 0xff, 0x00, 0x00, 0x55, 0xff, 0x00, 0x00, 0x44, 0xff, 0x00, 0x00, 0x22, 0xff, 0x00, 0x00, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff, 0xdd, 0xdd, 0xdd, 0xff, 0xbb, 0xbb, 0xbb, 0xff, + 0xaa, 0xaa, 0xaa, 0xff, 0x88, 0x88, 0x88, 0xff, 0x77, 0x77, 0x77, 0xff, 0x55, 0x55, 0x55, 0xff, 0x44, 0x44, 0x44, 0xff, 0x22, 0x22, 0x22, 0xff, 0x11, 0x11, 0x11, 0xff, 0x00, 0x00, 0x00, 0xff, + }; + + // internal math/helper utilities + static inline uint32_t _vox_max(uint32_t a, uint32_t b) { + return (a > b) ? a : b; + } + static inline uint32_t _vox_min(uint32_t a, uint32_t b) { + return (a < b) ? a : b; + } + + // string utilities + #ifdef _MSC_VER + #define _vox_str_scanf(str,...) sscanf_s(str,__VA_ARGS__) + #define _vox_strcpy_static(dst,src) strcpy_s(dst,src) + #define _vox_strcasecmp(a,b) _stricmp(a,b) + #define _vox_strcmp(a,b) strcmp(a,b) + #define _vox_strlen(a) strlen(a) + #define _vox_sprintf(str,str_max,fmt,...) sprintf_s(str, str_max, fmt, __VA_ARGS__) + #else + #define _vox_str_scanf(str,...) sscanf(str,__VA_ARGS__) + #define _vox_strcpy_static(dst,src) strcpy(dst,src) + #define _vox_strcasecmp(a,b) strcasecmp(a,b) + #define _vox_strcmp(a,b) strcmp(a,b) + #define _vox_strlen(a) strlen(a) + #define _vox_sprintf(str,str_max,fmt,...) snprintf(str, str_max, fmt, __VA_ARGS__) + #endif + + // 3d vector utilities + struct vec3 { + float x, y, z; + }; + static inline vec3 vec3_make(float x, float y, float z) { vec3 v; v.x = x; v.y = y; v.z = z; return v; } + static inline vec3 vec3_negate(const vec3& v) { vec3 r; r.x = -v.x; r.y = -v.y; r.z = -v.z; return r; } + + // API for emulating file transactions on an in-memory buffer of data. + struct _vox_file { + const uint8_t* buffer; // source buffer data + const uint32_t buffer_size; // size of the data in the buffer + uint32_t offset; // current offset in the buffer data. + }; + + static bool _vox_file_read(_vox_file* fp, void* data, uint32_t data_size) { + size_t data_to_read = _vox_min(fp->buffer_size - fp->offset, data_size); + memcpy(data, &fp->buffer[fp->offset], data_to_read); + fp->offset += data_size; + return data_to_read == data_size; + } + + static void _vox_file_seek_forwards(_vox_file* fp, uint32_t offset) { + fp->offset += offset; + } + + static bool _vox_file_eof(const _vox_file* fp) { + return fp->offset >= fp->buffer_size; + } + + static const void* _vox_file_data_pointer(const _vox_file* fp) { + return &fp->buffer[fp->offset]; + } + + // hash utilities + static uint32_t _vox_hash(const uint8_t* data, uint32_t data_size) { + uint32_t hash = 0; + for (uint32_t i = 0; i < data_size; i++) + hash = data[i] + (hash * 65559); + return hash; + } + + // memory allocation utils. + static void* _ogt_priv_alloc_default(size_t size) { return malloc(size); } + static void _ogt_priv_free_default(void* pPtr) { free(pPtr); } + static ogt_vox_alloc_func g_alloc_func = _ogt_priv_alloc_default; // default function for allocating + static ogt_vox_free_func g_free_func = _ogt_priv_free_default; // default function for freeing. + + // set the provided allocate/free functions if they are non-null, otherwise reset to default allocate/free functions + void ogt_vox_set_memory_allocator(ogt_vox_alloc_func alloc_func, ogt_vox_free_func free_func) + { + assert((alloc_func && free_func) || // both alloc/free must be non-NULL -OR- + (!alloc_func && !free_func)); // both alloc/free must be NULL. No mixing 'n matching. + if (alloc_func && free_func) { + g_alloc_func = alloc_func; + g_free_func = free_func; + } + else { + // reset to default allocate/free functions. + g_alloc_func = _ogt_priv_alloc_default; + g_free_func = _ogt_priv_free_default; + } + } + + static void* _vox_malloc(size_t iSize) { + return iSize ? g_alloc_func(iSize) : NULL; + } + + static void* _vox_calloc(size_t iSize) { + void* pMem = _vox_malloc(iSize); + if (pMem) + memset(pMem, 0, iSize); + return pMem; + } + + static void _vox_free(void* old_ptr) { + if (old_ptr) + g_free_func(old_ptr); + } + + static void* _vox_realloc(void* old_ptr, size_t old_size, size_t new_size) { + // early out if new size is non-zero and no resize is required. + if (new_size && old_size >= new_size) + return old_ptr; + + // memcpy from the old ptr only if both sides are valid. + void* new_ptr = _vox_malloc(new_size); + if (new_ptr) { + // copy any existing elements over + if (old_ptr && old_size) + memcpy(new_ptr, old_ptr, old_size); + // zero out any new tail elements + assert(new_size > old_size); // this should be guaranteed by the _vox_realloc early out case above. + uintptr_t new_tail_ptr = (uintptr_t)new_ptr + old_size; + memset((void*)new_tail_ptr, 0, new_size - old_size); + } + if (old_ptr) + _vox_free(old_ptr); + return new_ptr; + } + + // std::vector-style allocator, which use client-provided allocation functions. + template struct _vox_array { + _vox_array() : data(NULL), capacity(0), count(0) { } + ~_vox_array() { + _vox_free(data); + data = NULL; + count = 0; + capacity = 0; + } + void reserve(size_t new_capacity) { + data = (T*)_vox_realloc(data, capacity * sizeof(T), new_capacity * sizeof(T)); + capacity = new_capacity; + } + void grow_to_fit_index(size_t index) { + if (index >= count) + resize(index + 1); + } + void resize(size_t new_count) { + if (new_count > capacity) + reserve(new_count); + count = new_count; + } + void push_back(const T & new_element) { + if (count == capacity) { + size_t new_capacity = capacity ? (capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements. + reserve(new_capacity); + assert(capacity > count); + } + data[count++] = new_element; + } + void push_back_many(const T * new_elements, size_t num_elements) { + if (count + num_elements > capacity) { + size_t new_capacity = capacity + num_elements; + new_capacity = new_capacity ? (new_capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements. + reserve(new_capacity); + assert(capacity >= (count + num_elements)); + } + for (size_t i = 0; i < num_elements; i++) + data[count + i] = new_elements[i]; + count += num_elements; + } + size_t size() const { + return count; + } + T& operator[](size_t index) { + assert(index < count); + return data[index]; + } + const T& operator[](size_t index) const { + assert(index < count); + return data[index]; + } + T* data; // data for the array + size_t capacity; // capacity of the array + size_t count; // size of the array + }; + + // matrix utilities + static ogt_vox_transform _vox_transform_identity() { + ogt_vox_transform t; + t.m00 = 1.0f; t.m01 = 0.0f; t.m02 = 0.0f; t.m03 = 0.0f; + t.m10 = 0.0f; t.m11 = 1.0f; t.m12 = 0.0f; t.m13 = 0.0f; + t.m20 = 0.0f; t.m21 = 0.0f; t.m22 = 1.0f; t.m23 = 0.0f; + t.m30 = 0.0f; t.m31 = 0.0f; t.m32 = 0.0f; t.m33 = 1.0f; + return t; + } + + static ogt_vox_transform _vox_transform_multiply(const ogt_vox_transform& a, const ogt_vox_transform& b) { + ogt_vox_transform r; + r.m00 = (a.m00 * b.m00) + (a.m01 * b.m10) + (a.m02 * b.m20) + (a.m03 * b.m30); + r.m01 = (a.m00 * b.m01) + (a.m01 * b.m11) + (a.m02 * b.m21) + (a.m03 * b.m31); + r.m02 = (a.m00 * b.m02) + (a.m01 * b.m12) + (a.m02 * b.m22) + (a.m03 * b.m32); + r.m03 = (a.m00 * b.m03) + (a.m01 * b.m13) + (a.m02 * b.m23) + (a.m03 * b.m33); + r.m10 = (a.m10 * b.m00) + (a.m11 * b.m10) + (a.m12 * b.m20) + (a.m13 * b.m30); + r.m11 = (a.m10 * b.m01) + (a.m11 * b.m11) + (a.m12 * b.m21) + (a.m13 * b.m31); + r.m12 = (a.m10 * b.m02) + (a.m11 * b.m12) + (a.m12 * b.m22) + (a.m13 * b.m32); + r.m13 = (a.m10 * b.m03) + (a.m11 * b.m13) + (a.m12 * b.m23) + (a.m13 * b.m33); + r.m20 = (a.m20 * b.m00) + (a.m21 * b.m10) + (a.m22 * b.m20) + (a.m23 * b.m30); + r.m21 = (a.m20 * b.m01) + (a.m21 * b.m11) + (a.m22 * b.m21) + (a.m23 * b.m31); + r.m22 = (a.m20 * b.m02) + (a.m21 * b.m12) + (a.m22 * b.m22) + (a.m23 * b.m32); + r.m23 = (a.m20 * b.m03) + (a.m21 * b.m13) + (a.m22 * b.m23) + (a.m23 * b.m33); + r.m30 = (a.m30 * b.m00) + (a.m31 * b.m10) + (a.m32 * b.m20) + (a.m33 * b.m30); + r.m31 = (a.m30 * b.m01) + (a.m31 * b.m11) + (a.m32 * b.m21) + (a.m33 * b.m31); + r.m32 = (a.m30 * b.m02) + (a.m31 * b.m12) + (a.m32 * b.m22) + (a.m33 * b.m32); + r.m33 = (a.m30 * b.m03) + (a.m31 * b.m13) + (a.m32 * b.m23) + (a.m33 * b.m33); + return r; + } + + // dictionary utilities + static const uint32_t k_vox_max_dict_buffer_size = 4096; + static const uint32_t k_vox_max_dict_key_value_pairs = 256; + struct _vox_dictionary { + const char* keys[k_vox_max_dict_key_value_pairs]; + const char* values[k_vox_max_dict_key_value_pairs]; + uint32_t num_key_value_pairs; + char buffer[k_vox_max_dict_buffer_size + 4]; // max 4096, +4 for safety + uint32_t buffer_mem_used; + }; + + static bool _vox_file_read_dict(_vox_dictionary * dict, _vox_file * fp) { + uint32_t num_pairs_to_read = 0; + _vox_file_read(fp, &num_pairs_to_read, sizeof(uint32_t)); + assert(num_pairs_to_read <= k_vox_max_dict_key_value_pairs); + + dict->buffer_mem_used = 0; + dict->num_key_value_pairs = 0; + for (uint32_t i = 0; i < num_pairs_to_read; i++) { + // get the size of the key string + uint32_t key_string_size = 0; + _vox_file_read(fp, &key_string_size, sizeof(uint32_t)); + // allocate space for the key, and read it in. + if (dict->buffer_mem_used + key_string_size > k_vox_max_dict_buffer_size) + return false; + char* key = &dict->buffer[dict->buffer_mem_used]; + dict->buffer_mem_used += key_string_size + 1; // + 1 for zero terminator + _vox_file_read(fp, key, key_string_size); + key[key_string_size] = 0; // zero-terminate + assert(_vox_strlen(key) == key_string_size); // sanity check + + // get the size of the value string + uint32_t value_string_size = 0; + _vox_file_read(fp, &value_string_size, sizeof(uint32_t)); + // allocate space for the value, and read it in. + if (dict->buffer_mem_used + value_string_size > k_vox_max_dict_buffer_size) + return (false); + char* value = &dict->buffer[dict->buffer_mem_used]; + dict->buffer_mem_used += value_string_size + 1; // + 1 for zero terminator + _vox_file_read(fp, value, value_string_size); + value[value_string_size] = 0; // zero-terminate + assert(_vox_strlen(value) == value_string_size); // sanity check + // now assign it in the dictionary + dict->keys[dict->num_key_value_pairs] = key; + dict->values[dict->num_key_value_pairs] = value; + dict->num_key_value_pairs++; + } + + return true; + } + + // helper for looking up in the dictionary + static const char* _vox_dict_get_value_as_string(const _vox_dictionary* dict, const char* key_to_find, const char* default_value = NULL) { + for (uint32_t i = 0; i < dict->num_key_value_pairs; i++) + if (_vox_strcasecmp(dict->keys[i], key_to_find) == 0) + return dict->values[i]; + return default_value; + } + + static ogt_vox_transform _vox_make_transform_from_dict_strings(const char* rotation_string, const char* translation_string) { + ogt_vox_transform transform = _vox_transform_identity(); + + if (rotation_string != NULL) { + static vec3 k_vectors[4] = { + vec3_make(1.0f, 0.0f, 0.0f), + vec3_make(0.0f, 1.0f, 0.0f), + vec3_make(0.0f, 0.0f, 1.0f), + vec3_make(0.0f, 0.0f, 0.0f) // invalid! + }; + + static const uint32_t k_row2_index[] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 2, UINT32_MAX, 1, 0, UINT32_MAX }; + + // compute the per-row indexes into k_vectors[] array. + // unpack rotation bits. + // bits : meaning + // 0 - 1 : index of the non-zero entry in the first row + // 2 - 3 : index of the non-zero entry in the second row + uint32_t packed_rotation_bits = atoi(rotation_string); + uint32_t row0_vec_index = (packed_rotation_bits >> 0) & 3; + uint32_t row1_vec_index = (packed_rotation_bits >> 2) & 3; + uint32_t row2_vec_index = k_row2_index[(1 << row0_vec_index) | (1 << row1_vec_index)]; // process of elimination to determine row 2 index based on row0/row1 being one of {0,1,2} choose 2. + assert(row2_vec_index != UINT32_MAX); // if you hit this, you probably have invalid indices for row0_vec_index/row1_vec_index. + + // unpack rotation bits for vector signs + // bits : meaning + // 4 : the sign in the first row (0 : positive; 1 : negative) + // 5 : the sign in the second row (0 : positive; 1 : negative) + // 6 : the sign in the third row (0 : positive; 1 : negative) + vec3 row0 = k_vectors[row0_vec_index]; + vec3 row1 = k_vectors[row1_vec_index]; + vec3 row2 = k_vectors[row2_vec_index]; + if (packed_rotation_bits & (1 << 4)) + row0 = vec3_negate(row0); + if (packed_rotation_bits & (1 << 5)) + row1 = vec3_negate(row1); + if (packed_rotation_bits & (1 << 6)) + row2 = vec3_negate(row2); + + // magicavoxel stores rows, we need columns, so we do the swizzle here into columns + transform.m00 = row0.x; transform.m01 = row1.x; transform.m02 = row2.x; + transform.m10 = row0.y; transform.m11 = row1.y; transform.m12 = row2.y; + transform.m20 = row0.z; transform.m21 = row1.z; transform.m22 = row2.z; + } + + if (translation_string != NULL) { + int32_t x = 0; + int32_t y = 0; + int32_t z = 0; + _vox_str_scanf(translation_string, "%i %i %i", &x, &y, &z); + transform.m30 = (float)x; + transform.m31 = (float)y; + transform.m32 = (float)z; + } + return transform; + } + + enum _vox_scene_node_type + { + k_nodetype_invalid = 0, // has not been parsed yet. + k_nodetype_group = 1, + k_nodetype_transform = 2, + k_nodetype_shape = 3, + }; + + struct _vox_scene_node_ { + _vox_scene_node_type node_type; // only gets assigned when this has been parsed, otherwise will be k_nodetype_invalid + union { + // used only when node_type == k_nodetype_transform + struct { + char name[64]; // max name size is 64 + ogt_vox_transform transform; + uint32_t child_node_id; + uint32_t layer_id; + bool hidden; + } transform; + // used only when node_type == k_nodetype_group + struct { + uint32_t first_child_node_id_index; // the index of the first child node ID within the ChildNodeID array + uint32_t num_child_nodes; // number of child node IDs starting at the first index + } group; + // used only when node_type == k_nodetype_shape + struct { + uint32_t model_id; // will be UINT32_MAX if there is no model. Unlikely, there should always be a model. + } shape; + } u; + }; + + static void generate_instances_for_node( + const _vox_array<_vox_scene_node_> & nodes, uint32_t node_index, const _vox_array & child_id_array, uint32_t layer_index, + const ogt_vox_transform& transform, const _vox_array & model_ptrs, const char* transform_last_name, bool transform_last_hidden, + _vox_array & instances, _vox_array & string_data, _vox_array& groups, uint32_t group_index, bool generate_groups) + { + const _vox_scene_node_* node = &nodes[node_index]; + assert(node); + switch (node->node_type) + { + case k_nodetype_transform: + { + ogt_vox_transform new_transform = (generate_groups) ? node->u.transform.transform // don't multiply by the parent transform. caller wants the group-relative transform + : _vox_transform_multiply(node->u.transform.transform, transform); // flatten the transform if we're not generating groups: child transform * parent transform + const char* new_transform_name = node->u.transform.name[0] ? node->u.transform.name : NULL; + transform_last_name = transform_last_name ? transform_last_name : new_transform_name; // if there was already a transform name, keep that otherwise keep the new transform name + generate_instances_for_node(nodes, node->u.transform.child_node_id, child_id_array, node->u.transform.layer_id, new_transform, model_ptrs, transform_last_name, node->u.transform.hidden, instances, string_data, groups, group_index, generate_groups); + break; + } + case k_nodetype_group: + { + // create a new group only if we're generating groups. + uint32_t next_group_index = 0; + if (generate_groups) { + next_group_index = (uint32_t)groups.size(); + ogt_vox_group group; + group.parent_group_index = group_index; + group.transform = transform; + group.hidden = transform_last_hidden; + group.layer_index = layer_index; + groups.push_back(group); + } + // child nodes will only be hidden if their immediate transform is hidden. + transform_last_hidden = false; + + const uint32_t* child_node_ids = (const uint32_t*)& child_id_array[node->u.group.first_child_node_id_index]; + for (uint32_t i = 0; i < node->u.group.num_child_nodes; i++) { + generate_instances_for_node(nodes, child_node_ids[i], child_id_array, layer_index, transform, model_ptrs, transform_last_name, transform_last_hidden, instances, string_data, groups, next_group_index, generate_groups); + } + break; + } + case k_nodetype_shape: + { + assert(node->u.shape.model_id < model_ptrs.size()); + if (node->u.shape.model_id < model_ptrs.size() && // model ID is valid + model_ptrs[node->u.shape.model_id] != NULL ) // model is non-NULL. + { + assert(generate_groups || group_index == 0); // if we're not generating groups, group_index should be zero to map to the root group. + ogt_vox_instance new_instance; + new_instance.model_index = node->u.shape.model_id; + new_instance.transform = transform; + new_instance.layer_index = layer_index; + new_instance.group_index = group_index; + new_instance.hidden = transform_last_hidden; + // if we got a transform name, allocate space in string_data for it and keep track of the index + // within string data. This will be patched to a real pointer at the very end. + new_instance.name = 0; + if (transform_last_name && transform_last_name[0]) { + new_instance.name = (const char*)(string_data.size()); + size_t name_size = _vox_strlen(transform_last_name) + 1; // +1 for terminator + string_data.push_back_many(transform_last_name, name_size); + } + // create the instance + instances.push_back(new_instance); + } + break; + } + default: + { + assert(0); // unhandled node type! + } + } + } + + // ensure instances are ordered in order of increasing model_index + static int _vox_ordered_compare_instance(const void* _lhs, const void* _rhs) { + const ogt_vox_instance* lhs = (const ogt_vox_instance*)_lhs; + const ogt_vox_instance* rhs = (const ogt_vox_instance*)_rhs; + return lhs->model_index < rhs->model_index ? -1 : + lhs->model_index > rhs->model_index ? 1 : 0; + } + + // returns true if the 2 models are content-wise identical. + static bool _vox_models_are_equal(const ogt_vox_model* lhs, const ogt_vox_model* rhs) { + // early out: if hashes don't match, they can't be equal + // if hashes match, they might be equal OR there might be a hash collision. + if (lhs->voxel_hash != rhs->voxel_hash) + return false; + // early out: if number of voxels in the model's grid don't match, they can't be equal. + uint32_t num_voxels_lhs = lhs->size_x * lhs->size_y * lhs->size_z; + uint32_t num_voxels_rhs = rhs->size_x * rhs->size_y * rhs->size_z; + if (num_voxels_lhs != num_voxels_rhs) + return false; + // Finally, we know their hashes are the same, and their dimensions are the same + // but they are only equal if they have exactly the same voxel data. + return memcmp(lhs->voxel_data, rhs->voxel_data, num_voxels_lhs) == 0 ? true : false; + } + + const ogt_vox_scene* ogt_vox_read_scene_with_flags(const uint8_t * buffer, uint32_t buffer_size, uint32_t read_flags) { + _vox_file file = { buffer, buffer_size, 0 }; + _vox_file* fp = &file; + + // parsing state/context + _vox_array model_ptrs; + _vox_array<_vox_scene_node_> nodes; + _vox_array instances; + _vox_array string_data; + _vox_array layers; + _vox_array groups; + _vox_array child_ids; + ogt_vox_palette palette; + _vox_dictionary dict; + uint32_t size_x = 0; + uint32_t size_y = 0; + uint32_t size_z = 0; + uint8_t index_map[256]; + bool found_index_map_chunk = false; + + // size some of our arrays to prevent resizing during the parsing for smallish cases. + model_ptrs.reserve(64); + instances.reserve(256); + child_ids.reserve(256); + nodes.reserve(16); + layers.reserve(8); + groups.reserve(0); + string_data.reserve(256); + + // push a sentinel character into these datastructures. This allows us to keep indexes + // rather than pointers into data-structures that grow, and still allow an index of 0 + // to means invalid + string_data.push_back('X'); + child_ids.push_back(-1); + + // copy the default palette into the scene. It may get overwritten by a palette chunk later + memcpy(&palette, k_default_vox_palette, sizeof(ogt_vox_palette)); + + // load and validate fileheader and file version. + uint32_t file_header; + uint32_t file_version; + _vox_file_read(fp, &file_header, sizeof(uint32_t)); + _vox_file_read(fp, &file_version, sizeof(uint32_t)); + if (file_header != CHUNK_ID_VOX_ || file_version != 150) + return NULL; + + // parse chunks until we reach the end of the file/buffer + while (!_vox_file_eof(fp)) + { + // read the fields common to all chunks + uint32_t chunk_id = 0; + uint32_t chunk_size = 0; + uint32_t chunk_child_size = 0; + _vox_file_read(fp, &chunk_id, sizeof(uint32_t)); + _vox_file_read(fp, &chunk_size, sizeof(uint32_t)); + _vox_file_read(fp, &chunk_child_size, sizeof(uint32_t)); + + // process the chunk. + switch (chunk_id) + { + case CHUNK_ID_MAIN: + { + assert(chunk_size == 0); + break; + } + case CHUNK_ID_SIZE: + { + assert(chunk_size == 12 && chunk_child_size == 0); + _vox_file_read(fp, &size_x, sizeof(uint32_t)); + _vox_file_read(fp, &size_y, sizeof(uint32_t)); + _vox_file_read(fp, &size_z, sizeof(uint32_t)); + break; + } + case CHUNK_ID_XYZI: + { + assert(chunk_child_size == 0 && size_x && size_y && size_z); // must have read a SIZE chunk prior to XYZI. + // read the number of voxels to process for this moodel + uint32_t num_voxels_in_chunk = 0; + _vox_file_read(fp, &num_voxels_in_chunk, sizeof(uint32_t)); + if (num_voxels_in_chunk != 0) { + uint32_t voxel_count = size_x * size_y * size_z; + ogt_vox_model * model = (ogt_vox_model*)_vox_calloc(sizeof(ogt_vox_model) + voxel_count); // 1 byte for each voxel + if (!model) + return NULL; + uint8_t * voxel_data = (uint8_t*)&model[1]; + + // insert the model into the model array + model_ptrs.push_back(model); + + // now setup the model + model->size_x = size_x; + model->size_y = size_y; + model->size_z = size_z; + model->voxel_data = voxel_data; + + // setup some strides for computing voxel index based on x/y/z + const uint32_t k_stride_x = 1; + const uint32_t k_stride_y = size_x; + const uint32_t k_stride_z = size_x * size_y; + + // read this many voxels and store it in voxel data. + const uint8_t * packed_voxel_data = (const uint8_t*)_vox_file_data_pointer(fp); + for (uint32_t i = 0; i < num_voxels_in_chunk; i++) { + uint8_t x = packed_voxel_data[i * 4 + 0]; + uint8_t y = packed_voxel_data[i * 4 + 1]; + uint8_t z = packed_voxel_data[i * 4 + 2]; + uint8_t colorIdx = packed_voxel_data[i * 4 + 3]; + assert(x < size_x && y < size_y && z < size_z); + voxel_data[(x * k_stride_x) + (y * k_stride_y) + (z * k_stride_z)] = colorIdx; + } + _vox_file_seek_forwards(fp, num_voxels_in_chunk * 4); + // compute the hash of the voxels in this model-- used to accelerate duplicate models checking. + model->voxel_hash = _vox_hash(voxel_data, size_x * size_y * size_z); + } + else { + model_ptrs.push_back(NULL); + } + break; + } + case CHUNK_ID_RGBA: + { + assert(chunk_size == sizeof(palette)); + _vox_file_read(fp, &palette, sizeof(palette)); + break; + } + case CHUNK_ID_nTRN: + { + uint32_t node_id; + _vox_file_read(fp, &node_id, sizeof(node_id)); + + // Parse the node dictionary, which can contain: + // _name: string + // _hidden: 0/1 + char node_name[64]; + bool hidden = false; + node_name[0] = 0; + { + _vox_file_read_dict(&dict, fp); + const char* name_string = _vox_dict_get_value_as_string(&dict, "_name"); + if (name_string) + _vox_strcpy_static(node_name, name_string); + // if we got a hidden attribute - assign it now. + const char* hidden_string = _vox_dict_get_value_as_string(&dict, "_hidden", "0"); + if (hidden_string) + hidden = (hidden_string[0] == '1' ? true : false); + } + + + // get other properties. + uint32_t child_node_id, reserved_id, layer_id, num_frames; + _vox_file_read(fp, &child_node_id, sizeof(child_node_id)); + _vox_file_read(fp, &reserved_id, sizeof(reserved_id)); + _vox_file_read(fp, &layer_id, sizeof(layer_id)); + _vox_file_read(fp, &num_frames, sizeof(num_frames)); + assert(reserved_id == UINT32_MAX && num_frames == 1); // must be these values according to the spec + + // Parse the frame dictionary that contains: + // _r : int8 ROTATION (c) + // _t : int32x3 translation + // and extract a transform + ogt_vox_transform frame_transform; + { + _vox_file_read_dict(&dict, fp); + const char* rotation_value = _vox_dict_get_value_as_string(&dict, "_r"); + const char* translation_value = _vox_dict_get_value_as_string(&dict, "_t"); + frame_transform = _vox_make_transform_from_dict_strings(rotation_value, translation_value); + } + // setup the transform node. + { + nodes.grow_to_fit_index(node_id); + _vox_scene_node_* transform_node = &nodes[node_id]; + assert(transform_node); + transform_node->node_type = k_nodetype_transform; + transform_node->u.transform.child_node_id = child_node_id; + transform_node->u.transform.layer_id = layer_id; + transform_node->u.transform.transform = frame_transform; + transform_node->u.transform.hidden = hidden; + // assign the name + _vox_strcpy_static(transform_node->u.transform.name, node_name); + } + break; + } + case CHUNK_ID_nGRP: + { + uint32_t node_id; + _vox_file_read(fp, &node_id, sizeof(node_id)); + + // parse the node dictionary - data is unused. + _vox_file_read_dict(&dict, fp); + + // setup the group node + nodes.grow_to_fit_index(node_id); + _vox_scene_node_* group_node = &nodes[node_id]; + group_node->node_type = k_nodetype_group; + group_node->u.group.first_child_node_id_index = 0; + group_node->u.group.num_child_nodes = 0; + + // setup all child scene nodes to point back to this node. + uint32_t num_child_nodes = 0; + _vox_file_read(fp, &num_child_nodes, sizeof(num_child_nodes)); + + // allocate space for all the child node IDs + if (num_child_nodes) { + size_t prior_size = child_ids.size(); + assert(prior_size > 0); // should be guaranteed by the sentinel we reserved at the very beginning. + child_ids.resize(prior_size + num_child_nodes); + _vox_file_read(fp, &child_ids[prior_size], sizeof(uint32_t) * num_child_nodes); + group_node->u.group.first_child_node_id_index = (uint32_t)prior_size; + group_node->u.group.num_child_nodes = num_child_nodes; + } + break; + } + case CHUNK_ID_nSHP: + { + uint32_t node_id; + _vox_file_read(fp, &node_id, sizeof(node_id)); + + // setup the shape node + nodes.grow_to_fit_index(node_id); + _vox_scene_node_* shape_node = &nodes[node_id]; + shape_node->node_type = k_nodetype_shape; + shape_node->u.shape.model_id = UINT32_MAX; + + // parse the node dictionary - data is unused. + _vox_file_read_dict(&dict, fp); + + uint32_t num_models = 0; + _vox_file_read(fp, &num_models, sizeof(num_models)); + assert(num_models == 1); // must be 1 according to the spec. + + // assign instances + _vox_file_read(fp, &shape_node->u.shape.model_id, sizeof(uint32_t)); + assert(shape_node->u.shape.model_id < model_ptrs.size()); + + // parse the model dictionary - data is unsued. + _vox_file_read_dict(&dict, fp); + break; + } + case CHUNK_ID_IMAP: + { + assert(chunk_size == 256); + _vox_file_read(fp, index_map, 256); + found_index_map_chunk = true; + break; + } + case CHUNK_ID_LAYR: + { + int32_t layer_id = 0; + int32_t reserved_id = 0; + _vox_file_read(fp, &layer_id, sizeof(layer_id)); + _vox_file_read_dict(&dict, fp); + _vox_file_read(fp, &reserved_id, sizeof(reserved_id)); + assert(reserved_id == -1); + + layers.grow_to_fit_index(layer_id); + layers[layer_id].name = NULL; + layers[layer_id].hidden = false; + + // if we got a layer name from the LAYR dictionary, allocate space in string_data for it and keep track of the index + // within string data. This will be patched to a real pointer at the very end. + const char* name_string = _vox_dict_get_value_as_string(&dict, "_name", NULL); + if (name_string) { + layers[layer_id].name = (const char*)(string_data.size()); + size_t name_size = _vox_strlen(name_string) + 1; // +1 for terminator + string_data.push_back_many(name_string, name_size); + } + // if we got a hidden attribute - assign it now. + const char* hidden_string = _vox_dict_get_value_as_string(&dict, "_hidden", "0"); + if (hidden_string) + layers[layer_id].hidden = (hidden_string[0] == '1' ? true : false); + break; + } + // we don't handle MATL/MATT/rOBJ or any other chunks for now, so we just skip the chunk payload. + case CHUNK_ID_MATL: + case CHUNK_ID_MATT: + case CHUNK_ID_rOBJ: + default: + { + _vox_file_seek_forwards(fp, chunk_size); + break; + } + } // end switch + } + + // ok, now that we've parsed all scene nodes - walk the scene hierarchy, and generate instances + // we can't do this while parsing chunks unfortunately because some chunks reference chunks + // that are later in the file than them. + if (nodes.size()) { + bool generate_groups = read_flags & k_read_scene_flags_groups ? true : false; + // if we're not reading scene-embedded groups, we generate only one and then flatten all instance transforms. + if (!generate_groups) { + ogt_vox_group root_group; + root_group.transform = _vox_transform_identity(); + root_group.parent_group_index = k_invalid_group_index; + root_group.layer_index = 0; + root_group.hidden = false; + groups.push_back(root_group); + } + generate_instances_for_node(nodes, 0, child_ids, 0, _vox_transform_identity(), model_ptrs, NULL, false, instances, string_data, groups, k_invalid_group_index, generate_groups); + } + else if (model_ptrs.size() == 1) { + // add a single instance + ogt_vox_instance new_instance; + new_instance.model_index = 0; + new_instance.transform = _vox_transform_identity(); + new_instance.layer_index = 0; + new_instance.name = 0; + new_instance.hidden = false; + instances.push_back(new_instance); + } + + // if we didn't get a layer chunk -- just create a default layer. + if (layers.size() == 0) { + // go through all instances and ensure they are only mapped to layer 0 + for (uint32_t i = 0; i < instances.size(); i++) + instances[i].layer_index = 0; + // add a single layer + ogt_vox_layer new_layer; + new_layer.hidden = false; + new_layer.name = NULL; + layers.push_back(new_layer); + } + + // To support index-level assumptions (eg. artists using top 16 colors for color/palette cycling, + // other ranges for emissive etc), we must ensure the order of colors that the artist sees in the + // magicavoxel tool matches the actual index we'll end up using here. Unfortunately, magicavoxel + // does an unexpected thing when remapping colors in the editor using ctrl+drag within the palette. + // Instead of remapping all indices in all models, it just keeps track of a display index to actual + // palette map and uses that to show reordered colors in the palette window. This is how that + // map works: + // displaycolor[k] = paletteColor[imap[k]] + // To ensure our indices are in the same order as displayed by magicavoxel within the palette + // window, we apply the mapping from the IMAP chunk both to the color palette and indices within each + // voxel model. + if (found_index_map_chunk) + { + // the imap chunk maps from display index to actual index. + // generate an inverse index map (maps from actual index to display index) + uint8_t index_map_inverse[256]; + for (uint32_t i = 0; i < 256; i++) { + index_map_inverse[index_map[i]] = (uint8_t)i; + } + + // reorder colors in the palette so the palette contains colors in display order + ogt_vox_palette old_palette = palette; + for (uint32_t i = 0; i < 256; i++) { + uint32_t remapped_index = (index_map[i] + 255) & 0xFF; + palette.color[i] = old_palette.color[remapped_index]; + } + + // ensure that all models are remapped so they are using display order palette indices. + for (uint32_t i = 0; i < model_ptrs.size(); i++) { + ogt_vox_model* model = model_ptrs[i]; + if (model) { + uint32_t num_voxels = model->size_x * model->size_y * model->size_z; + uint8_t* voxels = (uint8_t*)&model[1]; + for (uint32_t j = 0; j < num_voxels; j++) + voxels[j] = 1 + index_map_inverse[voxels[j]]; + } + } + } + + // rotate the scene palette now so voxel indices can just map straight into the palette + { + ogt_vox_rgba last_color = palette.color[255]; + for (uint32_t i = 255; i > 0; i--) + palette.color[i] = palette.color[i - 1]; + palette.color[0] = last_color; + palette.color[0].a = 0; // alpha is zero for the 0th color as that color index represents a transparent voxel. + } + + // check for models that are identical by doing a pair-wise compare. If we find identical + // models, we'll end up with NULL gaps in the model_ptrs array, but instances will have + // been remapped to keep the earlier model. + for (uint32_t i = 0; i < model_ptrs.size(); i++) { + if (!model_ptrs[i]) + continue; + for (uint32_t j = i+1; j < model_ptrs.size(); j++) { + if (!model_ptrs[j] || !_vox_models_are_equal(model_ptrs[i], model_ptrs[j])) + continue; + // model i and model j are the same, so free model j and keep model i. + _vox_free(model_ptrs[j]); + model_ptrs[j] = NULL; + // remap all instances that were referring to j to now refer to i. + for (uint32_t k = 0; k < instances.size(); k++) + if (instances[k].model_index == j) + instances[k].model_index = i; + } + } + + // sometimes a model can be created which has no solid voxels within just due to the + // authoring flow within magicavoxel. We have already have prevented creation of + // instances that refer to empty models, but here we want to compact the model_ptrs + // array such that it contains no more NULL models. This also requires we remap the + // indices for instances so they continue to refer to their correct models. + { + // first, check to see if we find any empty model. No need to do work otherwise. + bool found_empty_model = false; + for (uint32_t i = 0; i < model_ptrs.size() && !found_empty_model; i++) { + if (model_ptrs[i] == NULL) + found_empty_model = true; + } + if (found_empty_model) { + // build a remap table for all instances and simultaneously compact the model_ptrs array. + uint32_t* model_remap = (uint32_t*)_vox_malloc(model_ptrs.size() * sizeof(uint32_t)); + uint32_t num_output_models = 0; + for (uint32_t i = 0; i < model_ptrs.size(); i++) { + if (model_ptrs[i] != NULL) { + model_ptrs[num_output_models] = model_ptrs[i]; + model_remap[i] = num_output_models; + num_output_models++; + } + else { + model_remap[i] = UINT32_MAX; + } + } + model_ptrs.resize(num_output_models); + + // remap all instances to point to the compacted model index + for (uint32_t i = 0; i < instances.size(); i++) { + uint32_t new_model_index = model_remap[instances[i].model_index]; + assert(new_model_index != UINT32_MAX); // we should have suppressed instances already that point to NULL models. + instances[i].model_index = new_model_index; + } + + // free remap table + _vox_free(model_remap); + model_remap = NULL; + } + } + + // finally, construct the output scene.. + size_t scene_size = sizeof(ogt_vox_scene) + string_data.size(); + ogt_vox_scene* scene = (ogt_vox_scene*)_vox_calloc(scene_size); + { + // copy name data into the scene + char* scene_string_data = (char*)&scene[1]; + memcpy(scene_string_data, &string_data[0], sizeof(char) * string_data.size()); + + // copy instances over to scene, and sort them so that instances with the same model are contiguous. + size_t num_scene_instances = instances.size(); + ogt_vox_instance* scene_instances = (ogt_vox_instance*)_vox_malloc(sizeof(ogt_vox_instance) * num_scene_instances); + if (num_scene_instances) { + memcpy(scene_instances, &instances[0], sizeof(ogt_vox_instance) * num_scene_instances); + qsort(scene_instances, num_scene_instances, sizeof(ogt_vox_instance), _vox_ordered_compare_instance); + } + scene->instances = scene_instances; + scene->num_instances = (uint32_t)instances.size(); + + // copy model pointers over to the scene, + size_t num_scene_models = model_ptrs.size(); + ogt_vox_model** scene_models = (ogt_vox_model * *)_vox_malloc(sizeof(ogt_vox_model*) * num_scene_models); + if (num_scene_models) + memcpy(scene_models, &model_ptrs[0], sizeof(ogt_vox_model*) * num_scene_models); + scene->models = (const ogt_vox_model **)scene_models; + scene->num_models = (uint32_t)num_scene_models; + + // copy layer pointers over to the scene + size_t num_scene_layers = layers.size(); + ogt_vox_layer* scene_layers = (ogt_vox_layer*)_vox_malloc(sizeof(ogt_vox_layer) * num_scene_layers); + memcpy(scene_layers, &layers[0], sizeof(ogt_vox_layer) * num_scene_layers); + scene->layers = scene_layers; + scene->num_layers = (uint32_t)num_scene_layers; + + // copy group pointers over to the scene + size_t num_scene_groups = groups.size(); + ogt_vox_group* scene_groups = num_scene_groups ? (ogt_vox_group*)_vox_malloc(sizeof(ogt_vox_group) * num_scene_groups) : NULL; + if (num_scene_groups) + memcpy(scene_groups, &groups[0], sizeof(ogt_vox_group)* num_scene_groups); + scene->groups = scene_groups; + scene->num_groups = (uint32_t)num_scene_groups; + + // now patch up instance name pointers to point into the scene string area + for (uint32_t i = 0; i < num_scene_instances; i++) + if (scene_instances[i].name) + scene_instances[i].name = scene_string_data + (size_t)scene_instances[i].name; + + // now patch up layer name pointers to point into the scene string area + for (uint32_t i = 0; i < num_scene_layers; i++) + if (scene_layers[i].name) + scene_layers[i].name = scene_string_data + (size_t)scene_layers[i].name; + + // copy the palette. + scene->palette = palette; + } + return scene; + } + + const ogt_vox_scene* ogt_vox_read_scene(const uint8_t* buffer, uint32_t buffer_size) { + return ogt_vox_read_scene_with_flags(buffer, buffer_size, 0); + } + + void ogt_vox_destroy_scene(const ogt_vox_scene * _scene) { + ogt_vox_scene* scene = const_cast(_scene); + // free models from model array + for (uint32_t i = 0; i < scene->num_models; i++) + _vox_free((void*)scene->models[i]); + // free model array itself + if (scene->models) { + _vox_free(scene->models); + scene->models = NULL; + } + // free instance array + if (scene->instances) { + _vox_free(const_cast(scene->instances)); + scene->instances = NULL; + } + // free layer array + if (scene->layers) { + _vox_free(const_cast(scene->layers)); + scene->layers = NULL; + } + // free groups array + if (scene->groups) { + _vox_free(const_cast(scene->groups)); + scene->groups = NULL; + } + // finally, free the scene. + _vox_free(scene); + } + + // the vector should be a unit vector aligned along one of the cardinal directions exactly. eg. (1,0,0) or (0, 0, -1) + // this function returns the non-zero column index in out_index and the returns whether that entry is negative. + static bool _vox_get_vec3_rotation_bits(const vec3& vec, uint32_t& out_index) { + const float* f = &vec.x; + out_index = 3; + bool is_negative = false; + for (uint32_t i = 0; i < 3; i++) { + if (f[i] == 1.0f || f[i] == -1.0f) { + out_index = i; + is_negative = f[i] < 0.0f ? true : false; + } + else { + assert(f[i] == 0.0f); // must be zero + } + } + assert(out_index != 3); // if you hit this, you probably have all zeroes in the vector! + return is_negative; + } + + static uint8_t _vox_make_packed_rotation_from_transform(const ogt_vox_transform * transform) { + // magicavoxel stores rows, and we have columns, so we do the swizzle here into rows + vec3 row0 = vec3_make(transform->m00, transform->m10, transform->m20); + vec3 row1 = vec3_make(transform->m01, transform->m11, transform->m21); + vec3 row2 = vec3_make(transform->m02, transform->m12, transform->m22); + uint32_t row0_index = 3, row1_index = 3, row2_index = 3; + bool row0_negative = _vox_get_vec3_rotation_bits(row0, row0_index); + bool row1_negative = _vox_get_vec3_rotation_bits(row1, row1_index); + bool row2_negative = _vox_get_vec3_rotation_bits(row2, row2_index); + assert(((1 << row0_index) | (1 << row1_index) | (1 << row2_index)) == 7); // check that rows are orthogonal. There must be a non-zero entry in column 0, 1 and 2 across these 3 rows. + return (row0_index) | (row1_index << 2) | (row0_negative ? 1 << 4 : 0) | (row1_negative ? 1 << 5 : 0) | (row2_negative ? 1 << 6 : 0); + } + + struct _vox_file_writeable { + _vox_array data; + }; + + static void _vox_file_writeable_init(_vox_file_writeable* fp) { + fp->data.reserve(1024); + } + static void _vox_file_write(_vox_file_writeable* fp, const void* data, uint32_t data_size) { + fp->data.push_back_many((const uint8_t*)data, data_size); + } + static void _vox_file_write_uint32(_vox_file_writeable* fp, uint32_t data) { + fp->data.push_back_many((const uint8_t*)&data, sizeof(uint32_t)); + } + static void _vox_file_write_uint8(_vox_file_writeable* fp, uint8_t data) { + fp->data.push_back_many((const uint8_t*)&data, sizeof(uint8_t)); + } + static uint32_t _vox_file_get_offset(const _vox_file_writeable* fp) { + return (uint32_t)fp->data.count; + } + static uint8_t* _vox_file_get_data(_vox_file_writeable* fp) { + return &fp->data[0]; + } + static void _vox_file_write_dict_key_value(_vox_file_writeable* fp, const char* key, const char* value) { + if (key == NULL || value == NULL) + return; + uint32_t key_len = (uint32_t)_vox_strlen(key); + uint32_t value_len = (uint32_t)_vox_strlen(value); + _vox_file_write_uint32(fp, key_len); + _vox_file_write(fp, key, key_len); + _vox_file_write_uint32(fp, value_len); + _vox_file_write(fp, value, value_len); + } + + static uint32_t _vox_dict_key_value_size(const char* key, const char* value) { + if (key == NULL || value == NULL) + return 0; + size_t size = sizeof(uint32_t) + _vox_strlen(key) + sizeof(uint32_t) + _vox_strlen(value); + return (uint32_t)size; + } + + static void _vox_file_write_chunk_nTRN(_vox_file_writeable* fp, uint32_t node_id, uint32_t child_node_id, const char* name, bool hidden, const ogt_vox_transform* transform, uint32_t layer_id) + { + // obtain dictionary string pointers + const char* hidden_string = hidden ? "1" : NULL; + const char* t_string = NULL; + const char* r_string = NULL; + char t_string_buf[64]; + char r_string_buf[64]; + t_string_buf[0] = 0; + r_string_buf[0] = 0; + if (transform != NULL) { + uint8_t packed_rotation_bits = _vox_make_packed_rotation_from_transform(transform); + _vox_sprintf(t_string_buf, sizeof(t_string_buf), "%i %i %i", (int32_t)transform->m30, (int32_t)transform->m31, (int32_t)transform->m32); + _vox_sprintf(r_string_buf, sizeof(r_string_buf), "%u", packed_rotation_bits); + t_string = t_string_buf; + r_string = r_string_buf; + } + + uint32_t node_dict_size = + sizeof(uint32_t) + // num key value pairs + _vox_dict_key_value_size("_name", name) + + _vox_dict_key_value_size("_hidden", hidden_string); + + uint32_t frame_dict_size = + sizeof(uint32_t) + // num key value pairs + _vox_dict_key_value_size("_t", t_string) + + _vox_dict_key_value_size("_r", r_string); + + uint32_t chunk_size_ntrn = + sizeof(uint32_t) + // node_id + node_dict_size + // node dictionary + 4 * sizeof(uint32_t) + // middle section - 4 uint32s + frame_dict_size; + + // write the nTRN header + _vox_file_write_uint32(fp, CHUNK_ID_nTRN); + _vox_file_write_uint32(fp, chunk_size_ntrn); + _vox_file_write_uint32(fp, 0); + + // write the nTRN payload + _vox_file_write_uint32(fp, node_id); + + // write the node dictionary + uint32_t node_dict_keyvalue_count = (name ? 1 : 0) + (hidden_string ? 1 : 0); + _vox_file_write_uint32(fp, node_dict_keyvalue_count); // num key values + _vox_file_write_dict_key_value(fp, "_name", name); + _vox_file_write_dict_key_value(fp, "_hidden", hidden_string); + + // get other properties. + _vox_file_write_uint32(fp, child_node_id); + _vox_file_write_uint32(fp, UINT32_MAX); // reserved_id must have all bits set. + _vox_file_write_uint32(fp, layer_id); + _vox_file_write_uint32(fp, 1); // num_frames must be 1 + + // write the frame dictionary + _vox_file_write_uint32(fp, (r_string ? 1 : 0) + (t_string ? 1 : 0)); // num key values + _vox_file_write_dict_key_value(fp, "_r", r_string); + _vox_file_write_dict_key_value(fp, "_t", t_string); + } + + // saves the scene out to a buffer that when saved as a .vox file can be loaded with magicavoxel. + uint8_t* ogt_vox_write_scene(const ogt_vox_scene* scene, uint32_t* buffer_size) { + _vox_file_writeable file; + _vox_file_writeable_init(&file); + _vox_file_writeable* fp = &file; + + // write file header and file version + _vox_file_write_uint32(fp, CHUNK_ID_VOX_); + _vox_file_write_uint32(fp, 150); + + // write the main chunk + _vox_file_write_uint32(fp, CHUNK_ID_MAIN); + _vox_file_write_uint32(fp, 0); + _vox_file_write_uint32(fp, 0); // this main_chunk_child_size will get patched up once everything is written. + + // we need to know how to patch up the main chunk size after we've written everything + const uint32_t offset_post_main_chunk = _vox_file_get_offset(fp); + + // write out all model chunks + for (uint32_t i = 0; i < scene->num_models; i++) { + const ogt_vox_model* model = scene->models[i]; + assert(model->size_x <= 126 && model->size_y <= 126 && model->size_z <= 126); + // count the number of solid voxels in the grid + uint32_t num_voxels_in_grid = model->size_x * model->size_y * model->size_z; + uint32_t num_solid_voxels = 0; + for (uint32_t voxel_index = 0; voxel_index < num_voxels_in_grid; voxel_index++) + if (model->voxel_data[voxel_index] != 0) + num_solid_voxels++; + uint32_t chunk_size_xyzi = sizeof(uint32_t) + 4 * num_solid_voxels; + + // write the SIZE chunk header + _vox_file_write_uint32(fp, CHUNK_ID_SIZE); + _vox_file_write_uint32(fp, 12); + _vox_file_write_uint32(fp, 0); + + // write the SIZE chunk payload + _vox_file_write_uint32(fp, model->size_x); + _vox_file_write_uint32(fp, model->size_y); + _vox_file_write_uint32(fp, model->size_z); + + // write the XYZI chunk header + _vox_file_write_uint32(fp, CHUNK_ID_XYZI); + _vox_file_write_uint32(fp, chunk_size_xyzi); + _vox_file_write_uint32(fp, 0); + + // write out XYZI chunk payload + _vox_file_write_uint32(fp, num_solid_voxels); + uint32_t voxel_index = 0; + for (uint32_t z = 0; z < model->size_z; z++) { + for (uint32_t y = 0; y < model->size_y; y++) { + for (uint32_t x = 0; x < model->size_x; x++, voxel_index++) { + uint8_t color_index = model->voxel_data[voxel_index]; + if (color_index != 0) { + _vox_file_write_uint8(fp, (uint8_t)x); + _vox_file_write_uint8(fp, (uint8_t)y); + _vox_file_write_uint8(fp, (uint8_t)z); + _vox_file_write_uint8(fp, color_index); + } + } + } + } + } + + // define our node_id ranges. + assert(scene->num_groups); + uint32_t first_group_transform_node_id = 0; + uint32_t first_group_node_id = first_group_transform_node_id + scene->num_groups; + uint32_t first_shape_node_id = first_group_node_id + scene->num_groups; + uint32_t first_instance_transform_node_id = first_shape_node_id + scene->num_models; + + // write the nTRN nodes for each of the groups in the scene. + for (uint32_t group_index = 0; group_index < scene->num_groups; group_index++) { + const ogt_vox_group* group = &scene->groups[group_index]; + _vox_file_write_chunk_nTRN(fp, first_group_transform_node_id + group_index, first_group_node_id + group_index, NULL, group->hidden, &group->transform, group->layer_index); + } + // write the group nodes for each of the groups in the scene + for (uint32_t group_index = 0; group_index < scene->num_groups; group_index++) { + // count how many childnodes there are. This is simply the sum of all + // groups and instances that have this group as its parent + uint32_t num_child_nodes = 0; + for (uint32_t child_group_index = 0; child_group_index < scene->num_groups; child_group_index++) + if (scene->groups[child_group_index].parent_group_index == group_index) + num_child_nodes++; + for (uint32_t child_instance_index = 0; child_instance_index < scene->num_instances; child_instance_index++) + if (scene->instances[child_instance_index].group_index == group_index) + num_child_nodes++; + + // count number of dictionary items + const char* hidden_string = scene->groups[group_index].hidden ? "1" : NULL; + uint32_t group_dict_keyvalue_count = (hidden_string ? 1 : 0); + + // compute the chunk size. + uint32_t chunk_size_ngrp = + sizeof(uint32_t) + // node_id + sizeof(uint32_t) + // num keyvalue pairs in node dictionary + _vox_dict_key_value_size("_hidden", hidden_string) + + sizeof(uint32_t) + // num_child_nodes field + sizeof(uint32_t) * num_child_nodes; // uint32_t for each child node id. + + // write the nGRP header + _vox_file_write_uint32(fp, CHUNK_ID_nGRP); + _vox_file_write_uint32(fp, chunk_size_ngrp); + _vox_file_write_uint32(fp, 0); + // write the nGRP payload + _vox_file_write_uint32(fp, first_group_node_id + group_index); // node_id + _vox_file_write_uint32(fp, group_dict_keyvalue_count); // num keyvalue pairs in node dictionary + _vox_file_write_dict_key_value(fp, "_hidden", hidden_string); + _vox_file_write_uint32(fp, num_child_nodes); + // write the child group transform nodes + for (uint32_t child_group_index = 0; child_group_index < scene->num_groups; child_group_index++) + if (scene->groups[child_group_index].parent_group_index == group_index) + _vox_file_write_uint32(fp, first_group_transform_node_id + child_group_index); + // write the child instance transform nodes + for (uint32_t child_instance_index = 0; child_instance_index < scene->num_instances; child_instance_index++) + if (scene->instances[child_instance_index].group_index == group_index) + _vox_file_write_uint32(fp, first_instance_transform_node_id + child_instance_index); + } + + // write out an nSHP chunk for each of the models + for (uint32_t i = 0; i < scene->num_models; i++) { + // compute the size of the nSHP chunk + uint32_t chunk_size_nshp = + sizeof(uint32_t) + // node_id + sizeof(uint32_t) + // num keyvalue pairs in node dictionary + sizeof(uint32_t) + // num_models + sizeof(uint32_t) + // model_id + sizeof(uint32_t); // num keyvalue pairs in model dictionary + // write the nSHP chunk header + _vox_file_write_uint32(fp, CHUNK_ID_nSHP); + _vox_file_write_uint32(fp, chunk_size_nshp); + _vox_file_write_uint32(fp, 0); + // write the nSHP chunk payload + _vox_file_write_uint32(fp, first_shape_node_id + i); // node_id + _vox_file_write_uint32(fp, 0); // num keyvalue pairs in node dictionary + _vox_file_write_uint32(fp, 1); // num_models must be 1 + _vox_file_write_uint32(fp, i); // model_id + _vox_file_write_uint32(fp, 0); // num keyvalue pairs in model dictionary + } + // write out an nTRN chunk for all instances - and make them point to the relevant nSHP chunk + for (uint32_t i = 0; i < scene->num_instances; i++) { + const ogt_vox_instance* instance = &scene->instances[i]; + uint32_t node_id = first_instance_transform_node_id + i; + uint32_t child_node_id = first_shape_node_id + instance->model_index; + _vox_file_write_chunk_nTRN(fp, node_id, child_node_id, instance->name, instance->hidden, &instance->transform, instance->layer_index); + } + + // write out RGBA chunk for the palette + { + // .vox stores palette rotated by 1 color index, so do that now. + ogt_vox_palette rotated_palette; + for (uint32_t i = 0; i < 256; i++) + rotated_palette.color[i] = scene->palette.color[(i + 1) & 255]; + + // write the palette chunk header + _vox_file_write_uint32(fp, CHUNK_ID_RGBA); + _vox_file_write_uint32(fp, sizeof(ogt_vox_palette)); + _vox_file_write_uint32(fp, 0); + // write the palette chunk payload + _vox_file_write(fp, &rotated_palette, sizeof(ogt_vox_palette)); + } + + // write all layer chunks out. + for (uint32_t i = 0; i < scene->num_layers; i++) { + const char* layer_name_string = scene->layers[i].name; + const char* hidden_string = scene->layers[i].hidden ? "1" : NULL; + uint32_t layer_chunk_size = + sizeof(int32_t) + // layer_id + sizeof(uint32_t) + // num key value pairs + _vox_dict_key_value_size("_name", layer_name_string) + + _vox_dict_key_value_size("_hidden", hidden_string) + + sizeof(int32_t); // reserved id, must be -1 + uint32_t layer_dict_keyvalue_count = (layer_name_string ? 1 : 0) + (hidden_string ? 1 : 0); + // write the layer chunk header + _vox_file_write_uint32(fp, CHUNK_ID_LAYR); + _vox_file_write_uint32(fp, layer_chunk_size); + _vox_file_write_uint32(fp, 0); + // write the layer chunk payload + _vox_file_write_uint32(fp, i); // layer_id + _vox_file_write_uint32(fp, layer_dict_keyvalue_count); // num keyvalue pairs in layer dictionary + _vox_file_write_dict_key_value(fp, "_name", layer_name_string); + _vox_file_write_dict_key_value(fp, "_hidden", hidden_string); + _vox_file_write_uint32(fp, UINT32_MAX); // reserved id + } + + // we deliberately don't free the fp->data field, just pass the buffer pointer and size out to the caller + *buffer_size = (uint32_t)fp->data.count; + uint8_t* buffer_data = _vox_file_get_data(fp); + // we deliberately clear this pointer so it doesn't get auto-freed on exiting. The caller will own the memory hereafter. + fp->data.data = NULL; + + // patch up the main chunk's child chunk size now that we've written everything we're going to write. + { + uint32_t* main_chunk_child_size = (uint32_t*)& buffer_data[offset_post_main_chunk - sizeof(uint32_t)]; + *main_chunk_child_size = *buffer_size - offset_post_main_chunk; + } + + return buffer_data; + } + + void* ogt_vox_malloc(size_t size) { + return _vox_malloc(size); + } + + void ogt_vox_free(void* mem) { + _vox_free(mem); + } + + // compute the minimum and maximum x coordinate within the scene. + static void compute_scene_bounding_box_x(const ogt_vox_scene * scene, int32_t & out_min_x, int32_t & out_max_x) { + if (scene->num_instances && scene->num_models) + { + // We don't apply orientation to the model dimensions and compute the exact min/max. + // Instead we just conservatively use the maximum dimension of the model. + int32_t scene_min_x = 0x7ffffff; + int32_t scene_max_x = -0x7ffffff; + for (uint32_t instance_index = 0; instance_index < scene->num_instances; instance_index++) { + const ogt_vox_instance* instance = &scene->instances[instance_index]; + // compute the instance transform, taking into account the group hierarchy. + ogt_vox_transform instance_transform = instance->transform; + uint32_t parent_group_index = instance->group_index; + while (parent_group_index != k_invalid_group_index) { + const ogt_vox_group* group = &scene->groups[parent_group_index]; + instance_transform = _vox_transform_multiply(instance_transform, group->transform); + parent_group_index = group->parent_group_index; + } + + const ogt_vox_model* model = scene->models[instance->model_index]; + // the instance_transform can be rotated, so we try to figure out whether the + // model's local x, y or z size is aligned along the world x axis. + // One of the column vectors of the transform must have a non-zero in its + // x field and the dimension associated with that column is the correct choice of rus. + int32_t max_dim = instance_transform.m00 != 0.0f ? model->size_x : + instance_transform.m10 != 0.0f ? model->size_y : + instance_transform.m20 != 0.0f ? model->size_z : model->size_x; + int32_t half_dim = max_dim / 2; + int32_t min_x = (int32_t)instance_transform.m30 - half_dim; + int32_t max_x = (int32_t)instance_transform.m30 + half_dim; + scene_min_x = min_x < scene_min_x ? min_x : scene_min_x; + scene_max_x = max_x > scene_max_x ? max_x : scene_max_x; + } + // pass out the dimensions. + out_min_x = scene_min_x; + out_max_x = scene_max_x; + } + else { + out_min_x = 0; + out_max_x = 0; + } + } + + // returns a mask of which color indices are used by the specified scene. + // used_mask[0] can be false at the end of this if all models 100% fill their voxel grid with solid voxels, so callers + // should handle that case properly. + static void compute_scene_used_color_index_mask(bool* used_mask, const ogt_vox_scene * scene) { + memset(used_mask, 0, 256); + for (uint32_t model_index = 0; model_index < scene->num_models; model_index++) { + const ogt_vox_model* model = scene->models[model_index]; + uint32_t voxel_count = model->size_x * model->size_y * model->size_z; + for (uint32_t voxel_index = 0; voxel_index < voxel_count; voxel_index++) { + uint8_t color_index = model->voxel_data[voxel_index]; + used_mask[color_index] = true; + } + } + } + + // finds an exact color in the specified palette if it exists, and UINT32_MAX otherwise + static uint32_t find_exact_color_in_palette(const ogt_vox_rgba * palette, uint32_t palette_count, const ogt_vox_rgba color_to_find) { + for (uint32_t color_index = 1; color_index < palette_count; color_index++) { + const ogt_vox_rgba color_to_match = palette[color_index]; + // we only try to match r,g,b components exactly. + if (color_to_match.r == color_to_find.r && color_to_match.g == color_to_find.g && color_to_match.b == color_to_find.b) + return color_index; + } + // no exact color found + return UINT32_MAX; + } + + // finds the index within the specified palette that is closest to the color we want to find + static uint32_t find_closest_color_in_palette(const ogt_vox_rgba * palette, uint32_t palette_count, const ogt_vox_rgba color_to_find) { + // the lower the score the better, so initialize this to the maximum possible score + int32_t best_score = INT32_MAX; + uint32_t best_index = 1; + // Here we compute a score based on the pythagorean distance between each color in the palette and the color to find. + // The distance is in R,G,B space, and we choose the color with the lowest score. + for (uint32_t color_index = 1; color_index < palette_count; color_index++) { + int32_t r_diff = (int32_t)color_to_find.r - (int32_t)palette[color_index].r; + int32_t g_diff = (int32_t)color_to_find.g - (int32_t)palette[color_index].g; + int32_t b_diff = (int32_t)color_to_find.b - (int32_t)palette[color_index].b; + // There are 2 aspects of our treatment of color here you may want to experiment with: + // 1. differences in R, differences in G, differences in B are weighted the same rather than perceptually. Different weightings may be better for you. + // 2. We treat R,G,B as if they are in a perceptually linear within each channel. eg. the differences between + // a value of 5 and 8 in any channel is perceptually the same as the difference between 233 and 236 in the same channel. + int32_t score = (r_diff * r_diff) + (g_diff * g_diff) + (b_diff * b_diff); + if (score < best_score) { + best_score = score; + best_index = color_index; + } + } + assert(best_score < UINT32_MAX); // this might indicate a completely degenerate palette. + return best_index; + } + + static void update_master_palette_from_scene(ogt_vox_rgba * master_palette, uint32_t & master_palette_count, const ogt_vox_scene * scene, uint32_t * scene_to_master_map) { + // compute the mask of used colors in the scene. + bool scene_used_mask[256]; + compute_scene_used_color_index_mask(scene_used_mask, scene); + + // initialize the map that converts from scene color_index to master color_index + scene_to_master_map[0] = 0; // zero/empty always maps to zero/empty in the master palette + for (uint32_t i = 1; i < 256; i++) + scene_to_master_map[i] = UINT32_MAX; // UINT32_MAX means unassigned + + // for each used color in the scene, now allocate it into the master palette. + for (uint32_t color_index = 1; color_index < 256; color_index++) { + if (scene_used_mask[color_index]) { + const ogt_vox_rgba color = scene->palette.color[color_index]; + // find the exact color in the master palette. Will be UINT32_MAX if the color doesn't already exist + uint32_t master_index = find_exact_color_in_palette(master_palette, master_palette_count, color); + if (master_index == UINT32_MAX) { + if (master_palette_count < 256) { + // master palette capacity hasn't been exceeded so far, allocate the color to it. + master_palette[master_palette_count] = color; + master_index = master_palette_count++; + } + else { + // otherwise, find the color that is perceptually closest to the original color. + + // TODO(jpaver): It is potentially problematic if we hit this path for a many-scene merge. + // Earlier scenes will reserve their colors exactly into the master palette, whereas later + // scenes will end up having some of their colors remapped to different colors. + + // A more holistic approach to color allocation may be necessary here eg. + // we might allow the master palette to grow to more than 256 entries, and then use + // similarity/frequency metrics to reduce the palette from that down to 256 entries. This + // will mean all scenes will have be equally important if they have a high-frequency + // usage of a color. + master_index = find_closest_color_in_palette(master_palette, master_palette_count, color); + } + } + // caller needs to know how to map its original color index into the master palette + scene_to_master_map[color_index] = master_index; + } + } + } + + ogt_vox_scene* ogt_vox_merge_scenes(const ogt_vox_scene** scenes, uint32_t scene_count, const ogt_vox_rgba* required_colors, const uint32_t required_color_count) { + assert(required_color_count <= 255); // can't exceed the maximum colors in the master palette plus the empty slot. + + // initialize the master palette. If required colors are specified, map them into the master palette now. + ogt_vox_rgba master_palette[256]; + uint32_t master_palette_count = 1; // color_index 0 is reserved for empty color! + memset(&master_palette, 0, sizeof(master_palette)); + for (uint32_t required_index = 0; required_index < required_color_count; required_index++) + master_palette[master_palette_count++] = required_colors[required_index]; + + // count the number of required models, instances in the master scene + uint32_t max_layers = 1; // we don't actually merge layers. Every instance will be in layer 0. + uint32_t max_models = 0; + uint32_t max_instances = 0; + uint32_t max_groups = 1; // we add 1 root global group that everything will ultimately be parented to. + for (uint32_t scene_index = 0; scene_index < scene_count; scene_index++) { + if (!scenes[scene_index]) + continue; + max_instances += scenes[scene_index]->num_instances; + max_models += scenes[scene_index]->num_models; + max_groups += scenes[scene_index]->num_groups; + } + + // allocate the master instances array + ogt_vox_instance* instances = (ogt_vox_instance*)_vox_malloc(sizeof(ogt_vox_instance) * max_instances); + ogt_vox_model** models = (ogt_vox_model**)_vox_malloc(sizeof(ogt_vox_model*) * max_models); + ogt_vox_layer* layers = (ogt_vox_layer*)_vox_malloc(sizeof(ogt_vox_layer) * max_layers); + ogt_vox_group* groups = (ogt_vox_group*)_vox_malloc(sizeof(ogt_vox_group) * max_groups); + uint32_t num_instances = 0; + uint32_t num_models = 0; + uint32_t num_layers = 0; + uint32_t num_groups = 0; + + // add a single layer. + layers[num_layers].hidden = false; + layers[num_layers].name = "merged"; + num_layers++; + + // magicavoxel expects exactly 1 root group, so if we have multiple scenes with multiple roots, + // we must ensure all merged scenes are parented to the same root group. Allocate it now for the + // merged scene. + uint32_t global_root_group_index = num_groups; + { + assert(global_root_group_index == 0); + ogt_vox_group root_group; + root_group.hidden = false; + root_group.layer_index = 0; + root_group.parent_group_index = k_invalid_group_index; + root_group.transform = _vox_transform_identity(); + groups[num_groups++] = root_group; + } + + // go ahead and do the merge now! + size_t string_data_size = 0; + int32_t offset_x = 0; + for (uint32_t scene_index = 0; scene_index < scene_count; scene_index++) { + const ogt_vox_scene* scene = scenes[scene_index]; + if (!scene) + continue; + + // update the master palette, and get the map of this scene's color indices into the master palette. + uint32_t scene_color_index_to_master_map[256]; + update_master_palette_from_scene(master_palette, master_palette_count, scene, scene_color_index_to_master_map); + + // cache away the base model index for this scene. + uint32_t base_model_index = num_models; + uint32_t base_group_index = num_groups; + + // create copies of all models that have color indices remapped. + for (uint32_t model_index = 0; model_index < scene->num_models; model_index++) { + const ogt_vox_model* model = scene->models[model_index]; + uint32_t voxel_count = model->size_x * model->size_y * model->size_z; + // clone the model + ogt_vox_model* override_model = (ogt_vox_model*)_vox_malloc(sizeof(ogt_vox_model) + voxel_count); + uint8_t * override_voxel_data = (uint8_t*)& override_model[1]; + + // remap all color indices in the cloned model so they reference the master palette now! + for (uint32_t voxel_index = 0; voxel_index < voxel_count; voxel_index++) { + uint8_t old_color_index = model->voxel_data[voxel_index]; + uint32_t new_color_index = scene_color_index_to_master_map[old_color_index]; + assert(new_color_index < 256); + override_voxel_data[voxel_index] = (uint8_t)new_color_index; + } + // assign the new model. + *override_model = *model; + override_model->voxel_data = override_voxel_data; + override_model->voxel_hash = _vox_hash(override_voxel_data, voxel_count); + + models[num_models++] = override_model; + } + + // compute the scene bounding box on x dimension. this is used to offset instances + // and groups in the merged model along X dimension such that they do not overlap + // with instances from another scene in the merged model. + int32_t scene_min_x, scene_max_x; + compute_scene_bounding_box_x(scene, scene_min_x, scene_max_x); + float scene_offset_x = (float)(offset_x - scene_min_x); + + // each scene has a root group, and it must the 0th group in its local groups[] array, + assert(scene->groups[0].parent_group_index == k_invalid_group_index); + // create copies of all groups into the merged scene (except the root group from each scene -- which is why we start group_index at 1 here) + for (uint32_t group_index = 1; group_index < scene->num_groups; group_index++) { + const ogt_vox_group* src_group = &scene->groups[group_index]; + assert(src_group->parent_group_index != k_invalid_group_index); // there can be only 1 root group per scene and it must be the 0th group. + ogt_vox_group dst_group = *src_group; + assert(dst_group.parent_group_index < scene->num_groups); + dst_group.layer_index = 0; + dst_group.parent_group_index = (dst_group.parent_group_index == 0) ? global_root_group_index : base_group_index + (dst_group.parent_group_index - 1); + // if this group belongs to the global root group, it must be translated so it doesn't overlap with other scenes. + if (dst_group.parent_group_index == global_root_group_index) + dst_group.transform.m30 += scene_offset_x; + groups[num_groups++] = dst_group; + } + + // create copies of all instances (and bias them such that minimum on x starts at zero) + for (uint32_t instance_index = 0; instance_index < scene->num_instances; instance_index++) { + const ogt_vox_instance* src_instance = &scene->instances[instance_index]; + assert(src_instance->group_index < scene->num_groups); // every instance must be mapped to a group. + ogt_vox_instance* dst_instance = &instances[num_instances++]; + *dst_instance = *src_instance; + dst_instance->layer_index = 0; + dst_instance->group_index = (dst_instance->group_index == 0) ? global_root_group_index : base_group_index + (dst_instance->group_index - 1); + dst_instance->model_index += base_model_index; + if (dst_instance->name) + string_data_size += _vox_strlen(dst_instance->name) + 1; // + 1 for zero terminator + // if this instance belongs to the global rot group, it must be translated so it doesn't overlap with other scenes. + if (dst_instance->group_index == global_root_group_index) + dst_instance->transform.m30 += scene_offset_x; + } + + offset_x += (scene_max_x - scene_min_x); // step the width of the scene in x dimension + offset_x += 4; // a margin of this many voxels between scenes + } + + // fill any unused master palette entries with purple/invalid color. + const ogt_vox_rgba k_invalid_color = { 255, 0, 255, 255 }; // purple = invalid + for (uint32_t color_index = master_palette_count; color_index < 256; color_index++) + master_palette[color_index] = k_invalid_color; + + // assign the master scene on output. string_data is part of the scene allocation. + size_t scene_size = sizeof(ogt_vox_scene) + string_data_size; + ogt_vox_scene * merged_scene = (ogt_vox_scene*)_vox_calloc(scene_size); + + // copy name data into the string section and make instances point to it. This makes the merged model self-contained. + char* scene_string_data = (char*)&merged_scene[1]; + for (uint32_t instance_index = 0; instance_index < num_instances; instance_index++) { + if (instances[instance_index].name) { + size_t string_len = _vox_strlen(instances[instance_index].name) + 1; // +1 for zero terminator + memcpy(scene_string_data, instances[instance_index].name, string_len); + instances[instance_index].name = scene_string_data; + scene_string_data += string_len; + } + } + + assert(num_groups <= max_groups); + + memset(merged_scene, 0, sizeof(ogt_vox_scene)); + merged_scene->instances = instances; + merged_scene->num_instances = max_instances; + merged_scene->models = (const ogt_vox_model * *)models; + merged_scene->num_models = max_models; + merged_scene->layers = layers; + merged_scene->num_layers = max_layers; + merged_scene->groups = groups; + merged_scene->num_groups = num_groups; + // copy color palette into the merged scene + for (uint32_t color_index = 0; color_index < 256; color_index++) + merged_scene->palette.color[color_index] = master_palette[color_index]; + + return merged_scene; + } + + #endif // #ifdef OGT_VOX_IMPLEMENTATION + +/* ------------------------------------------------------------------------------------------------------------------------------------------------- + + MIT License + + Copyright (c) 2019 Justin Paver + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + +------------------------------------------------------------------------------------------------------------------------------------------------- */ diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp new file mode 100644 index 00000000..b6aaf0a6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp @@ -0,0 +1,250 @@ +// Copyright 2020 Phyronnaz + +#include "K2Node_AddDataItem.h" + +#include "VoxelGenerators/VoxelGeneratorInstanceWrapper.h" +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" + +#include "AssetRegistryModule.h" +#include "KismetCompiler.h" +#include "BlueprintActionDatabaseRegistrar.h" +#include "BlueprintNodeSpawner.h" +#include "K2Node_CallFunction.h" +#include "K2Node_MakeArray.h" +#include "K2Node_MakeStruct.h" + +const FName UK2Node_AddDataItem::PC_Generator = "Generator"; +const FName UK2Node_AddDataItem::PC_Bounds = "Bounds"; +const FName UK2Node_AddDataItem::PC_Mask = "Mask"; + +UEdGraphPin* FindOutputStructPinChecked(UEdGraphNode* Node) +{ + check(NULL != Node); + UEdGraphPin* OutputPin = NULL; + for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) + { + UEdGraphPin* Pin = Node->Pins[PinIndex]; + if (Pin && (EGPD_Output == Pin->Direction)) + { + OutputPin = Pin; + break; + } + } + check(NULL != OutputPin); + return OutputPin; +} + +void UK2Node_AddDataItem::AllocateDefaultPins() +{ + Super::AllocateDefaultPins(); + + // Execution pins + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); + CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then); + + if (!ensure(DataItemConfig)) + { + return; + } + + if (DataItemConfig->HasAnyFlags(RF_NeedLoad)) + { + // Preload to get correct pins + PreloadObject(const_cast(DataItemConfig)); + } + + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UVoxelPlaceableItemManager::StaticClass(), UEdGraphSchema_K2::PSC_Self); + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UVoxelGeneratorInstanceWrapper::StaticClass(), PC_Generator); + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Struct, FVoxelIntBox::StaticStruct(), PC_Bounds); + auto* MaskPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, UEdGraphSchema_K2::PSC_Bitmask, StaticEnum(), PC_Mask); + MaskPin->DefaultValue = "1"; + + for (auto& Parameter : DataItemConfig->Parameters) + { + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Float, Parameter); + } +} + +FText UK2Node_AddDataItem::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + return GetTitle(DataItemConfig ? DataItemConfig->GetName() : FString()); +} + +FText UK2Node_AddDataItem::GetTooltipText() const +{ + return GetTooltip(DataItemConfig ? DataItemConfig->GetName() : FString()); +} + +FText UK2Node_AddDataItem::GetMenuCategory() const +{ + return VOXEL_LOCTEXT("Voxel"); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UK2Node_AddDataItem::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) +{ + Super::ExpandNode(CompilerContext, SourceGraph); + + if (!DataItemConfig) + { + CompilerContext.MessageLog.Error(TEXT("DataItemConfig is not set!"), this); + return; + } + + TArray ParameterInputPins; + { + TArray InputPinsName; + for (auto& Pin : Pins) + { + if (Pin->Direction == EGPD_Input && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Float) + { + ParameterInputPins.Add(Pin); + InputPinsName.Add(Pin->GetFName()); + } + } + if (DataItemConfig->Parameters != InputPinsName) + { + CompilerContext.MessageLog.Error(TEXT("Outdated node @@! Right click it and click Refresh Node"), this); + return; + } + } + + UK2Node_MakeArray* MakeArrayNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); + MakeArrayNode->AllocateDefaultPins(); + CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeArrayNode, this); + + UK2Node_MakeStruct* MakeStruct = CompilerContext.SpawnIntermediateNode(this, SourceGraph); + MakeStruct->StructType = FVoxelDataItemConstructionInfo::StaticStruct(); + MakeStruct->AllocateDefaultPins(); + MakeStruct->bMadeAfterOverridePinRemoval = true; + CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeStruct, this); + + UEdGraphPin* ArrayOut = MakeArrayNode->GetOutputPin(); + // Connect the output of the "Make Array" pin to the function's "Parameters" pin + ArrayOut->MakeLinkTo(MakeStruct->FindPinChecked(GET_MEMBER_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Parameters))); + // This will set the "Make Array" node's type, only works if one pin is connected. + MakeArrayNode->PinConnectionListChanged(ArrayOut); + + for (int32 Index = 0; Index < ParameterInputPins.Num(); Index++) + { + // The "Make Array" node already has one pin available, so don't create one for ArgIdx == 0 + if (Index > 0) + { + MakeArrayNode->AddInputPin(); + } + + // Find the input pin on the "Make Array" node by index. + const FString PinName = FString::Printf(TEXT("[%d]"), Index); + UEdGraphPin* ArrayPin = MakeArrayNode->FindPinChecked(PinName); + + // Move our input pin to the array one + CompilerContext.MovePinLinksToIntermediate(*ParameterInputPins[Index], *ArrayPin); + } + + UFunction* BlueprintFunction = UVoxelPlaceableItemManager::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UVoxelPlaceableItemManager, AddDataItem)); + + UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode(this, SourceGraph); + CallFunction->SetFromFunction(BlueprintFunction); + CallFunction->AllocateDefaultPins(); + CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, this); + + FindOutputStructPinChecked(MakeStruct)->MakeLinkTo(CallFunction->FindPinChecked(TEXT("Info"))); + + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PSC_Self), *CallFunction->FindPinChecked(UEdGraphSchema_K2::PSC_Self)); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Generator), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Generator))); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Bounds), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Bounds))); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PC_Mask), *MakeStruct->FindPinChecked(GET_FUNCTION_NAME_STRING_CHECKED(FVoxelDataItemConstructionInfo, Mask))); + + CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFunction->GetExecPin()); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(UEdGraphSchema_K2::PN_Then), *CallFunction->GetThenPin()); + + BreakAllNodeLinks(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UK2Node_AddDataItem::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const +{ + if (!ActionRegistrar.GetActionKeyFilter()) + { + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + + FARFilter Filter; + Filter.ClassNames.Add(UVoxelGraphDataItemConfig::StaticClass()->GetFName()); + Filter.bRecursiveClasses = true; + + TArray Assets; + AssetRegistryModule.Get().GetAssets(Filter, Assets); + + for (auto& Asset : Assets) + { + UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); + NodeSpawner->DefaultMenuSignature.MenuName = GetTitle(Asset.AssetName.ToString()); + NodeSpawner->DefaultMenuSignature.Tooltip = GetTooltip(Asset.AssetName.ToString()); + + // Force load + // This sucks, but else we get weird errors when assets are loaded as AddDataItem nodes dependencies + // The item configs cannot have any dependencies, and are relatively small assets, so it should be fine + Asset.GetAsset(); + + if (Asset.IsAssetLoaded()) + { + UVoxelGraphDataItemConfig* NewDataItemConfig = CastChecked(Asset.GetAsset()); + + NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda( + [NewDataItemConfig = MakeWeakObjectPtr(NewDataItemConfig)](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/) + { + CastChecked(NewNode)->DataItemConfig = NewDataItemConfig.Get(); + }); + } + else + { + NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda( + [Asset](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/) + { + CastChecked(NewNode)->DataItemConfig = CastChecked(Asset.GetAsset()); + }); + } + + ActionRegistrar.AddBlueprintAction(Asset, NodeSpawner); + } + } + else + { + auto* NewDataItemConfig = Cast(ActionRegistrar.GetActionKeyFilter()); + if (NewDataItemConfig) + { + UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); + + NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda( + [NewDataItemConfig = MakeWeakObjectPtr(NewDataItemConfig)](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/) + { + CastChecked(NewNode)->DataItemConfig = NewDataItemConfig.Get(); + }); + + NodeSpawner->DefaultMenuSignature.MenuName = GetTitle(NewDataItemConfig->GetName()); + NodeSpawner->DefaultMenuSignature.Tooltip = GetTooltip(NewDataItemConfig->GetName()); + + ActionRegistrar.AddBlueprintAction(NewDataItemConfig, NodeSpawner); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FText UK2Node_AddDataItem::GetTitle(const FString& AssetName) +{ + return FText::Format(VOXEL_LOCTEXT("Add Data Item: {0}"), FText::FromString(FName::NameToDisplayString(AssetName, false))); +} + +FText UK2Node_AddDataItem::GetTooltip(const FString& AssetName) +{ + return FText::Format(VOXEL_LOCTEXT("Use this to add a new voxel data item with the parameters defined in {0} to your voxel world"), FText::FromString(FName::NameToDisplayString(AssetName, false))); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.h new file mode 100644 index 00000000..b7be14e1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.h @@ -0,0 +1,40 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "K2Node.h" +#include "VoxelGraphDataItemConfig.h" +#include "K2Node_AddDataItem.generated.h" + +UCLASS() +class VOXELEDITOR_API UK2Node_AddDataItem : public UK2Node +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + const UVoxelGraphDataItemConfig* DataItemConfig; + +public: + //~ Begin UEdGraphNode Interface + virtual void AllocateDefaultPins() override; + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + virtual FText GetTooltipText() const override; + //~ End UEdGraphNode Interface + + //~ Begin K2Node Interface + virtual FText GetMenuCategory() const override; + virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override; + virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; + //~ End K2Node Interface + +private: + static FText GetTitle(const FString& AssetName); + static FText GetTooltip(const FString& AssetName); + + static const FName PC_Generator; + static const FName PC_Bounds; + static const FName PC_Mask; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelDataAssetThumbnailRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelDataAssetThumbnailRenderer.h new file mode 100644 index 00000000..67215f79 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelDataAssetThumbnailRenderer.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Texture2D.h" +#include "ThumbnailRendering/TextureThumbnailRenderer.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelDataAssetThumbnailRenderer.generated.h" + +UCLASS() +class VOXELEDITOR_API UVoxelDataAssetThumbnailRenderer : public UTextureThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override + { + return Object->IsA(UVoxelDataAsset::StaticClass()); + } + virtual void GetThumbnailSize(UObject* Object, float Zoom, uint32& OutWidth, uint32& OutHeight) const override + { + UTextureThumbnailRenderer::GetThumbnailSize(CastChecked(Object)->GetThumbnail(), Zoom, OutWidth, OutHeight); + } + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetThumbnail(), X, Y, Width, Height, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h new file mode 100644 index 00000000..7909d110 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Texture2D.h" +#include "VoxelGraphGenerator.h" +#include "ThumbnailRendering/TextureThumbnailRenderer.h" +#include "VoxelGraphGeneratorThumbnailRenderer.generated.h" + +UCLASS() +class VOXELEDITOR_API UVoxelGraphGeneratorThumbnailRenderer : public UTextureThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override + { + return Object->IsA(UVoxelGraphGenerator::StaticClass()); + } + virtual void GetThumbnailSize(UObject* Object, float Zoom, uint32& OutWidth, uint32& OutHeight) const override + { + UTextureThumbnailRenderer::GetThumbnailSize(CastChecked(Object)->GetPreviewTexture(), Zoom, OutWidth, OutHeight); + } + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetPreviewTexture(), X, Y, Width, Height, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h new file mode 100644 index 00000000..3e2b0678 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Texture2D.h" +#include "ThumbnailRendering/TextureThumbnailRenderer.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelHeightmapAssetThumbnailRenderer.generated.h" + +UCLASS() +class VOXELEDITOR_API UVoxelHeightmapAssetThumbnailRenderer : public UTextureThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override + { + return Object->IsA(UVoxelHeightmapAsset::StaticClass()); + } + virtual void GetThumbnailSize(UObject* Object, float Zoom, uint32& OutWidth, uint32& OutHeight) const override + { + UTextureThumbnailRenderer::GetThumbnailSize(CastChecked(Object)->GetThumbnail(), Zoom, OutWidth, OutHeight); + } + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetThumbnail(), X, Y, Width, Height, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.cpp new file mode 100644 index 00000000..1ce6963f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.cpp @@ -0,0 +1,175 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawnersThumbnailRenderer.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" + +#include "Engine/StaticMesh.h" +#include "ThumbnailRendering/ThumbnailManager.h" +#include "UnrealEdGlobals.h" +#include "Editor/UnrealEdEngine.h" + +bool UVoxelMeshSpawnerThumbnailRenderer::CanVisualizeAsset(UObject* Object) +{ + return Object->IsA(UVoxelMeshSpawner::StaticClass()) && CastChecked(Object)->Mesh; +} + +void UVoxelMeshSpawnerThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) +{ + UStaticMeshThumbnailRenderer::Draw(CastChecked(Object)->Mesh, X, Y, Width, Height, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelAssetSpawnerThumbnailRenderer::CanVisualizeAsset(UObject* Object) +{ + return Object->IsA(UVoxelAssetSpawner::StaticClass()) && CastChecked(Object)->Generator.IsValid(); +} + +void UVoxelAssetSpawnerThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) +{ + const auto& Generator = CastChecked(Object)->Generator; + auto* GeneratorObject = Generator.GetObject(); + if (GeneratorObject) + { + FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo(GeneratorObject); + if (RenderInfo && RenderInfo->Renderer) + { + RenderInfo->Renderer->Draw(GeneratorObject, X, Y, Width, Height, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +auto& GetStaticStack() +{ + static TArray Stack; + return Stack; +} + +template +inline void DrawGroup(UObject* Object, const int32 Num, const int32 X, const int32 Y, const uint32 Width, const uint32 Height, T Lambda) +{ + if (Width < 4 || Height < 4) + { + return; + } + + if (GetStaticStack().Contains(Object)) + { + return; + } + GetStaticStack().Add(Object); + + if (Num <= 4) + { + const uint32 HalfWidth = Width / 2; + const uint32 HalfHeight = Height / 2; + const int32 MiddleX = X + HalfWidth; + const int32 MiddleY = Y + HalfHeight; + const uint32 SecondHalfWidth = Width - HalfWidth; + const uint32 SecondHalfHeight = Height - HalfHeight; + + const TArray> Xs = {X, MiddleX, X, MiddleX}; + const TArray> Ys = {Y, Y, MiddleY, MiddleY}; + const TArray> Widths = {HalfWidth, SecondHalfWidth, HalfWidth, SecondHalfWidth}; + const TArray> Heights = {HalfHeight, SecondHalfHeight, HalfHeight, SecondHalfHeight}; + + for (int32 Index = 0; Index < 4; Index++) + { + Lambda(Index, Xs[Index], Ys[Index], Widths[Index], Heights[Index]); + } + } + else + { + const uint32 ThirdWidth = Width / 3; + const uint32 ThirdHeight = Height / 3; + const int32 OneThirdX = X + ThirdWidth; + const int32 TwoThirdX = X + 2 * ThirdWidth; + const int32 OneThirdY = Y + ThirdHeight; + const int32 TwoThirdY = Y + 2 * ThirdHeight; + const uint32 LastThirdWidth = Width - 2 * ThirdWidth; + const uint32 LastThirdHeight = Height - 2 * ThirdHeight; + + const TArray> Xs = { + X, OneThirdX, TwoThirdX, + X, OneThirdX, TwoThirdX, + X, OneThirdX, TwoThirdX }; + const TArray> Ys = { + Y, Y, Y, + OneThirdY, OneThirdY, OneThirdY, + TwoThirdY, TwoThirdY, TwoThirdY }; + const TArray> Widths = { + ThirdWidth, ThirdWidth, LastThirdWidth, + ThirdWidth, ThirdWidth, LastThirdWidth, + ThirdWidth, ThirdWidth, LastThirdWidth }; + const TArray> Heights = { + ThirdHeight,ThirdHeight,ThirdHeight, + ThirdHeight,ThirdHeight,ThirdHeight, + LastThirdHeight,LastThirdHeight,LastThirdHeight + }; + + for (int32 Index = 0; Index < 9; Index++) + { + Lambda(Index, Xs[Index], Ys[Index], Widths[Index], Heights[Index]); + } + } + + ensure(GetStaticStack().Pop() == Object); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelSpawnerGroupThumbnailRenderer::CanVisualizeAsset(UObject* Object) +{ + return Object->IsA(UVoxelSpawnerGroup::StaticClass()) && CastChecked(Object)->Children.Num() > 0; +} + +void UVoxelSpawnerGroupThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) +{ + auto* Group = CastChecked(Object); + auto& Children = Group->Children; + DrawGroup(Object, Children.Num(), X, Y, Width, Height, [&](int32 Index, int32 NewX, int32 NewY, uint32 NewWidth, uint32 NewHeight) + { + if (Children.IsValidIndex(Index)) + { + auto* Spawner = Children[Index].Spawner; + if (Spawner) + { + FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo(Spawner); + if (RenderInfo && RenderInfo->Renderer) + { + // TODO proper bAdditionalViewFamily + RenderInfo->Renderer->Draw(Spawner, NewX, NewY, NewWidth, NewHeight, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } + } + } + }); +} + +bool UVoxelMeshSpawnerGroupThumbnailRenderer::CanVisualizeAsset(UObject* Object) +{ + return Object->IsA(UVoxelMeshSpawnerGroup::StaticClass()) && CastChecked(Object)->Meshes.Num() > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelMeshSpawnerGroupThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) +{ + auto* Group = CastChecked(Object); + auto& Meshes = Group->Meshes; + DrawGroup(Object, Meshes.Num(), X, Y, Width, Height, [&](int32 Index, int32 NewX, int32 NewY, uint32 NewWidth, uint32 NewHeight) + { + if (Meshes.IsValidIndex(Index)) + { + auto* Mesh = Meshes[Index]; + if (Mesh) + { + // TODO proper bAdditionalViewFamily + UStaticMeshThumbnailRenderer::Draw(Mesh, NewX, NewY, NewWidth, NewHeight, Target, Canvas ONLY_UE_25_AND_HIGHER(, bAdditionalViewFamily)); + } + } + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.h new file mode 100644 index 00000000..877f20c4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "ThumbnailRendering/StaticMeshThumbnailRenderer.h" +#include "VoxelSpawnersThumbnailRenderer.generated.h" + +UCLASS() +class VOXELEDITOR_API UVoxelMeshSpawnerThumbnailRenderer : public UStaticMeshThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override; + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override; +}; + +UCLASS() +class VOXELEDITOR_API UVoxelAssetSpawnerThumbnailRenderer : public UDefaultSizedThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override; + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override; +}; + +UCLASS() +class VOXELEDITOR_API UVoxelSpawnerGroupThumbnailRenderer : public UDefaultSizedThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override; + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override; +}; + +UCLASS() +class VOXELEDITOR_API UVoxelMeshSpawnerGroupThumbnailRenderer : public UStaticMeshThumbnailRenderer +{ + GENERATED_BODY() + +public: + virtual bool CanVisualizeAsset(UObject* Object) override; + virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Target, FCanvas* Canvas ONLY_UE_25_AND_HIGHER(, bool bAdditionalViewFamily)) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp new file mode 100644 index 00000000..026bb228 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp @@ -0,0 +1,154 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelConvertLandscapeMaterial.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelConfigUtilities.h" +#include "VoxelRender/VoxelMaterialExpressions.h" + +#include "Editor.h" +#include "Materials/Material.h" +#include "Containers/Ticker.h" +#include "ContentBrowserModule.h" +#include "ScopedTransaction.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Framework/Notifications/NotificationManager.h" + +void FVoxelConvertLandscapeMaterial::Init() +{ + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")); + ContentBrowserModule.GetAllAssetViewContextMenuExtenders().Add(FContentBrowserMenuExtender_SelectedAssets::CreateLambda([=](const TArray& SelectedAssets) + { + const auto Extender = MakeShared(); + + for (auto& Asset : SelectedAssets) + { + if (Asset.GetClass() != UMaterial::StaticClass()) + { + return Extender; + } + } + + Extender->AddMenuExtension( + "CommonAssetActions", + EExtensionHook::After, + nullptr, + FMenuExtensionDelegate::CreateLambda([=](FMenuBuilder& MenuBuilder) + { + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Convert landscape material to voxel"), + VOXEL_LOCTEXT("Will replace all landscape layer nodes with nodes compatible with both voxels and landscapes"), + FSlateIcon(NAME_None, NAME_None), + FUIAction(FExecuteAction::CreateLambda([=]() + { + for (auto& Asset : SelectedAssets) + { + auto* Material = Cast(Asset.GetAsset()); + if (ensure(Material)) + { + ConvertMaterial(Material); + } + } + }))); + })); + + return Extender; + })); +} + +void FVoxelConvertLandscapeMaterial::ConvertMaterial(UMaterial* Material) +{ + FScopedTransaction Transaction(TEXT("ConvertMaterial"), VOXEL_LOCTEXT("Convert landscape material to voxel"), Material); + + TSet VisitedFunctions; + const int32 NumReplaced = ConvertExpressions(Material, Material->Expressions, VisitedFunctions); + + const FText Text = FText::Format(VOXEL_LOCTEXT("{0} expressions replaced in {1}"), NumReplaced, FText::FromName(Material->GetFName())); + LOG_VOXEL(Log, TEXT("%s"), *Text.ToString()); + + FNotificationInfo Info(Text); + Info.ExpireDuration = 10.f; + Info.CheckBoxState = ECheckBoxState::Checked; + FSlateNotificationManager::Get().AddNotification(Info); +} + +int32 FVoxelConvertLandscapeMaterial::ConvertExpressions(UObject* Owner, const TArray& Expressions, TSet& VisitedFunctions) +{ + int32 NumReplaced = 0; + + const auto ExpressionsCopy = Expressions; + for (auto* Expression : ExpressionsCopy) + { + auto* VoxelClass = FVoxelMaterialExpressionUtilities::GetVoxelExpression(Expression->GetClass()); + if (VoxelClass) + { + ConvertExpression(Owner, Expression, VoxelClass); + NumReplaced++; + } + if (auto* FunctionCall = Cast(Expression)) + { + auto* Function = Cast(FunctionCall->MaterialFunction); + if (Function && !VisitedFunctions.Contains(Function)) + { + VisitedFunctions.Add(Function); + NumReplaced += ConvertExpressions(Function, Function->FunctionExpressions, VisitedFunctions); + } + } + } + + return NumReplaced; +} + +void FVoxelConvertLandscapeMaterial::ConvertExpression(UObject* Owner, UMaterialExpression* Expression, UClass* NewClass) +{ + Owner->Modify(); + + auto* NewExpression = NewObject(Owner, NewClass, NAME_None, RF_Transactional); + check(NewClass->IsChildOf(Expression->GetClass())); + + Expression->Modify(); + NewExpression->Modify(); + + auto& Expressions = Owner->IsA() ? CastChecked(Owner)->Expressions : CastChecked(Owner)->FunctionExpressions; + ensure(Expressions.Remove(Expression) == 1); + Expressions.Add(NewExpression); + + // Copy data + for (TFieldIterator It(Expression->GetClass()); It; ++It) + { + auto* Property = *It; + if (!Property->HasAnyPropertyFlags(CPF_Transient)) + { + Property->CopyCompleteValue(Property->ContainerPtrToValuePtr(NewExpression), Property->ContainerPtrToValuePtr(Expression)); + } + } + + // Fixup other links + for (UMaterialExpression* OtherExpression : Expressions) + { + if (OtherExpression != Expression) + { + for (FExpressionInput* Input : OtherExpression->GetInputs()) + { + if (Input->Expression == Expression) + { + OtherExpression->Modify(); + Input->Expression = NewExpression; + } + } + } + } + + // Check material parameter inputs + if (auto* Material = Cast(Owner)) + { + for (int32 InputIndex = 0; InputIndex < MP_MAX; InputIndex++) + { + FExpressionInput* Input = Material->GetExpressionInputForProperty((EMaterialProperty)InputIndex); + if (Input && Input->Expression == Expression) + { + Input->Expression = NewExpression; + } + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.h new file mode 100644 index 00000000..1fc8bddb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UMaterial; +class UMaterialFunction; +class UMaterialExpression; + +class FVoxelConvertLandscapeMaterial +{ +public: + static void Init(); + + static void ConvertMaterial(UMaterial* Material); + + static int32 ConvertExpressions(UObject* Owner, const TArray& Expressions, TSet& VisitedFunctions); + static void ConvertExpression(UObject* Owner, UMaterialExpression* Expression, UClass* NewClass); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.cpp new file mode 100644 index 00000000..cfae4800 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.cpp @@ -0,0 +1,478 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDebugEditor.h" +#include "VoxelDebug.h" + +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelUtilities/VoxelTextureUtilities.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelUtilities/VoxelConfigUtilities.h" + +#include "Styling/SlateStyleRegistry.h" +#include "Framework/Docking/TabManager.h" +#include "Widgets/Docking/SDockTab.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Layout/SScaleBox.h" +#include "Widgets/Layout/SSplitter.h" +#include "Widgets/Layout/SGridPanel.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Input/SSpinBox.h" +#include "Widgets/Input/SCheckBox.h" + +#include "Engine/Texture2D.h" +#include "Engine/StaticMesh.h" +#include "Modules/ModuleManager.h" +#include "UObject/StrongObjectPtr.h" +#include "PropertyEditorModule.h" +#include "Async/Async.h" + +class SVoxelDebug : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SVoxelDebug) + { + } + SLATE_END_ARGS() + + void Construct(const FArguments& Args) + { + FVoxelDebug::GetDelegate().Add(MakeWeakPtrDelegate(this, [=](FName Name, const FIntVector& Size, TArrayView Data) + { + AsyncTask(ENamedThreads::GameThread, [Name, Size, Data = TArray(Data.GetData(), Data.Num())]() + { + auto& CustomData = *GetMutableDefault(); + auto& Array = CustomData.FloatData.FindOrAdd(Name); + Array.Insert({ Data, Size }, 0); + Array.SetNum(FMath::Min(Array.Num(), 100)); + CustomData.DataToDisplay.FindOrAdd(Name); + }); + })); + FVoxelDebug::GetDelegate().Add(MakeWeakPtrDelegate(this, [=](FName Name, const FIntVector& Size, TArrayView Data) + { + AsyncTask(ENamedThreads::GameThread, [Name, Size, Data = TArray(Data.GetData(), Data.Num())]() + { + auto& CustomData = *GetMutableDefault(); + auto& Array = CustomData.ValueData.FindOrAdd(Name); + Array.Insert({ Data, Size }, 0); + Array.SetNum(FMath::Min(Array.Num(), 100)); + CustomData.DataToDisplay.FindOrAdd(Name); + }); + })); + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea); + DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic; + + Details = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + const auto BaseDetails = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + + const auto Lambda = [=](const FPropertyChangedEvent& PropertyChangedEvent) + { + UpdateTexture(); + }; + Details->OnFinishedChangingProperties().AddLambda(Lambda); + BaseDetails->OnFinishedChangingProperties().AddLambda(Lambda); + + BaseDetails->SetObject(GetMutableDefault()); + + ChildSlot + [ + SNew(SSplitter) + + SSplitter::Slot() + .Value(1.f) + [ + SNew(SScaleBox) + .Stretch(EStretch::ScaleToFit) + [ + SNew(SOverlay) + + SOverlay::Slot() + [ + SNew(SImage) + .Image(&*Brush) + ] + + SOverlay::Slot() + [ + SAssignNew(Grid, SGridPanel) + ] + ] + ] + + SSplitter::Slot() + .Value(1.f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + BaseDetails + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + Details.ToSharedRef() + ] + ] + ]; + + GetMutableDefault()->PostEditChange.AddSP(this, &SVoxelDebug::UpdateTexture); + + FVoxelConfigUtilities::LoadConfig(GetMutableDefault(), "VoxelDebugParameters_JumpFlood"); + FVoxelConfigUtilities::LoadConfig(GetMutableDefault(), "VoxelDebugParameters_CustomData"); + UpdateTexture(); + } + + void UpdateTexture() + { + const auto GetSliceIndex = [](auto& Parameters, int32 X, int32 Y, const FIntVector& Size) + { + Parameters.Slice = FMath::Clamp(Parameters.Slice, 0, Size[int32(Parameters.SliceAxis)] - 1); + + FIntVector Position; + Position[int32(Parameters.SliceAxis)] = Parameters.Slice; + Position[(int32(Parameters.SliceAxis) + 1) % 3] = Parameters.bFlip ? Y : X; + Position[(int32(Parameters.SliceAxis) + 2) % 3] = Parameters.bFlip ? X : Y; + return Position.X + Size.X * Position.Y + Size.X * Size.Y * Position.Z; + }; + const auto ProjectToTexture = [](auto& Parameters, auto X, auto Y, auto Z, auto& OutX, auto& OutY) + { + TArray::Type, TFixedAllocator<3>> Array = { X, Y, Z }; + OutX = Array[(int32(Parameters.SliceAxis) + 1) % 3]; + OutY = Array[(int32(Parameters.SliceAxis) + 2) % 3]; + if (Parameters.bFlip) + { + Swap(OutX, OutY); + } + }; + const auto GetTextureSize = [&](auto& Parameters, const FIntVector& Size) + { + FIntPoint TextureSize; + ProjectToTexture(Parameters, Size.X, Size.Y, Size.Z, TextureSize.X, TextureSize.Y); + return TextureSize; + }; + const auto GetColorIndex = [](const FIntPoint& TextureSize, int32 X, int32 Y) + { + return X + TextureSize.X * (TextureSize.Y - 1 - Y); + }; + + GetMutableDefault()->bUpdate = false; + + const auto Type = GetDefault()->DebugType; + if (Type == EVoxelDebugType::JumpFlood) + { + auto& Parameters = *GetMutableDefault(); + Details->SetObject(&Parameters); + FVoxelConfigUtilities::SaveConfig(&Parameters, "VoxelDebugParameters_JumpFlood"); + + FIntVector Size = FIntVector::ZeroValue; + + TArray SurfacePositions; + TArray Distances; + + if (Parameters.bUseMesh) + { + if (!Parameters.Mesh) + { + return; + } + + if (Mesh != Parameters.Mesh) + { + UVoxelMeshImporterLibrary::CreateMeshDataFromStaticMesh(Parameters.Mesh, MeshData); + Mesh = Parameters.Mesh; + } + + const double StartTime = FPlatformTime::Seconds(); + + FIntVector PositionOffset; + int32 NumLeaks = 0; + UVoxelMeshImporterLibrary::ConvertMeshToDistanceField( + MeshData, + Parameters.Transform, + Parameters.MeshImporterSettings, + Parameters.BoxExtension, + Distances, + SurfacePositions, + Size, + PositionOffset, + NumLeaks, + Parameters.bUseCPU ? EVoxelComputeDevice::CPU : EVoxelComputeDevice::GPU, + Parameters.bMultiThreaded, + Parameters.Passes); + + const double EndTime = FPlatformTime::Seconds(); + + Parameters.TimeInSeconds = EndTime - StartTime; + } + else + { + Size = FIntVector(Parameters.TextureSize); + + SurfacePositions.SetNumUninitialized(Size.X * Size.Y * Size.Z); + Distances.SetNumUninitialized(Size.X * Size.Y * Size.Z); + + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + for (int32 Z = 0; Z < Size.Z; Z++) + { + FVector Position; + float Distance; + if (FVector(X - Size.X / 2.f, Y - Size.Y / 2.f, Z - Size.Z / 2.f).Size() < 16) + { + Position = FVector(X, Y, Z); + Distance = 0.f; + } + else + { + Position = FVector(1e9); + Distance = 1e9; + } + + const int32 Index = X + Size.X * Y + Size.X * Size.Y * Z; + SurfacePositions[Index] = Position; + Distances[Index] = Distance; + } + } + } + + const double StartTime = FPlatformTime::Seconds(); + + FVoxelDistanceFieldUtilities::DownSample(Size, Distances, SurfacePositions, Parameters.Divisor, Parameters.bShrink); + FVoxelDistanceFieldUtilities::JumpFlood(Size, SurfacePositions, Parameters.bUseCPU ? EVoxelComputeDevice::CPU : EVoxelComputeDevice::GPU, Parameters.bMultiThreaded, Parameters.Passes); + FVoxelDistanceFieldUtilities::GetDistancesFromSurfacePositions(Size, SurfacePositions, Distances); + + const double EndTime = FPlatformTime::Seconds(); + + Parameters.TimeInSeconds = EndTime - StartTime; + } + + Parameters.Size = Size; + + const auto TextureSize = GetTextureSize(Parameters, Size); + + TArray Colors; + Colors.Empty(TextureSize.X * TextureSize.Y); + Colors.SetNumUninitialized(TextureSize.X * TextureSize.Y); + + for (int32 X = 0; X < TextureSize.X; X++) + { + for (int32 Y = 0; Y < TextureSize.Y; Y++) + { + const int32 ColorIndex = GetColorIndex(TextureSize, X, Y); + + const int32 Index = GetSliceIndex(Parameters, X, Y, Size); + const FVector Position = SurfacePositions[Index]; + const float Distance = Distances[Index]; + + if (Position.X >= 1e9) + { + Colors[ColorIndex] = FColor::Blue; + } + else + { + if (Parameters.bShowDistances) + { + Colors[ColorIndex] = FVoxelDistanceFieldUtilities::GetDistanceFieldColor(Distance / float(TextureSize.GetMax())); + } + else + { + float FX; + float FY; + ProjectToTexture(Parameters, Position.X / Size.X, Position.Y / Size.Y, Position.Z / Size.Z, FX, FY); + Colors[ColorIndex] = FColor( + FVoxelUtilities::FloatToUINT8(FX), + FVoxelUtilities::FloatToUINT8(FY), + 0); + } + + } + } + } + + SetColors(Colors, TextureSize); + SetGrid({}, {}); + } + else + { + auto& Parameters = *GetMutableDefault(); + Details->SetObject(&Parameters); + FVoxelConfigUtilities::SaveConfig(&Parameters, "VoxelDebugParameters_CustomData"); + + FName Name; + for (auto& It : Parameters.DataToDisplay) + { + if (It.Value) + { + Name = It.Key; + break; + } + } + + TArray Colors; + TArray Text; + FIntVector Size { ForceInit }; + FIntPoint TextureSize { ForceInit }; + + if (auto* ValueData = Parameters.ValueData.Find(Name)) + { + Parameters.Frame = FMath::Clamp(Parameters.Frame, 0, ValueData->Num() - 1); + auto& Data = (*ValueData)[Parameters.Frame].Data; + Size = (*ValueData)[Parameters.Frame].Size; + + TextureSize = GetTextureSize(Parameters, Size); + Colors.Empty(TextureSize.X * TextureSize.Y); + Colors.SetNumUninitialized(TextureSize.X * TextureSize.Y); + Text.Empty(TextureSize.X * TextureSize.Y); + Text.SetNum(TextureSize.X * TextureSize.Y); + + for (int32 X = 0; X < TextureSize.X; X++) + { + for (int32 Y = 0; Y < TextureSize.Y; Y++) + { + const int32 Index = GetSliceIndex(Parameters, X, Y, Size); + const int32 ColorIndex = GetColorIndex(TextureSize, X, Y); + + const FVoxelValue Value = Data[Index]; + + if (Value.GetStorage() == 0) + { + Colors[ColorIndex] = FColor::Green; + } + else + { + Colors[ColorIndex] = FColor( + FVoxelUtilities::FloatToUINT8(-Value.ToFloat()), + 0, + FVoxelUtilities::FloatToUINT8(Value.ToFloat())); + } + Text[ColorIndex] = LexToString(Value.GetStorage()); + } + } + } + if (auto* FloatData = Parameters.FloatData.Find(Name)) + { + Parameters.Frame = FMath::Clamp(Parameters.Frame, 0, FloatData->Num() - 1); + auto& Data = (*FloatData)[Parameters.Frame].Data; + Size = (*FloatData)[Parameters.Frame].Size; + + TextureSize = GetTextureSize(Parameters, Size); + Colors.Empty(TextureSize.X * TextureSize.Y); + Colors.SetNumUninitialized(TextureSize.X * TextureSize.Y); + Text.Empty(TextureSize.X * TextureSize.Y); + Text.SetNum(TextureSize.X * TextureSize.Y); + + for (int32 X = 0; X < TextureSize.X; X++) + { + for (int32 Y = 0; Y < TextureSize.Y; Y++) + { + const int32 Index = GetSliceIndex(Parameters, X, Y, Size); + const int32 ColorIndex = GetColorIndex(TextureSize, X, Y); + + const float Distance = Data[Index]; + + if (Distance >= 1e9) + { + Colors[ColorIndex] = FColor::Green; + } + else + { + const float Value = Distance / float(TextureSize.GetMax()); + Colors[ColorIndex] = FColor( + FVoxelUtilities::FloatToUINT8(-Value), + 0, + FVoxelUtilities::FloatToUINT8(Value)); + } + + Text[ColorIndex] = LexToString(Distance); + } + } + } + + Parameters.Size = Size; + SetColors(Colors, TextureSize); + SetGrid(Text, TextureSize); + } + } + + void SetColors(const TArray& Colors, const FIntPoint& Size) + { + auto* Texture = TexturePtr.Get(); + if (Size.X == 0 || Size.Y == 0) + { + FVoxelTextureUtilities::UpdateColorTexture(Texture, { 1, 1 }, { FColor::Black }); + } + else + { + FVoxelTextureUtilities::UpdateColorTexture(Texture, Size, Colors); + } + if (ensure(Texture)) + { + Texture->Filter = TF_Nearest; + TexturePtr.Reset(Texture); + } + Brush->SetImageSize(Size); + Brush->SetResourceObject(Texture); + } + void SetGrid(const TArray& Data, const FIntPoint& Size) const + { + Grid->ClearChildren(); + if (Size.X == 0 || Size.Y == 0 || Data.Num() == 0) + { + return; + } + + check(Data.Num() == Size.X * Size.Y); + for (int32 X = 0; X < Size.X; X++) + { + for (int32 Y = 0; Y < Size.Y; Y++) + { + Grid->AddSlot(X, Y) + [ + SNew(SBox) + .WidthOverride(50) + .HeightOverride(50) + .Padding(FMargin(2)) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(SScaleBox) + .Stretch(EStretch::ScaleToFitX) + [ + SNew(STextBlock) + .Text(FText::FromString(Data[X + Size.X * Y])) + ] + ] + ]; + } + } + } + +private: + const TSharedRef Brush = MakeShared(); + TStrongObjectPtr TexturePtr; + + TSharedPtr Details; + TSharedPtr Grid; + + TWeakObjectPtr Mesh; + FVoxelMeshImporterInputData MeshData; +}; + +void UVoxelDebugParameters_CustomData::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + PostEditChange.Broadcast(); +} + +TSharedRef FVoxelDebugEditor::CreateTab(const FSpawnTabArgs& Args) +{ + return + SNew(SDockTab) + .Icon(FSlateStyleRegistry::FindSlateStyle("VoxelStyle")->GetBrush("VoxelIcon")) + .TabRole(ETabRole::NomadTab) + [ + SNew(SVoxelDebug) + ]; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.h new file mode 100644 index 00000000..b9277b54 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelDebugEditor.h @@ -0,0 +1,144 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelImporters/VoxelMeshImporter.h" +#include "VoxelDebugEditor.generated.h" + +class UStaticMesh; +class SDockTab; +class FSpawnTabArgs; + +UENUM() +enum class EVoxelDebugType +{ + JumpFlood, + CustomData +}; + +UENUM() +enum class EVoxelDebugSliceAxis +{ + X, + Y, + Z +}; + +UCLASS() +class UVoxelDebugParameters_Base : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "General") + EVoxelDebugType DebugType = EVoxelDebugType::CustomData; + + UPROPERTY(EditAnywhere, Category = "General") + bool bUpdate = false; +}; + +UCLASS() +class UVoxelDebugParameters_JumpFlood : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Source") + bool bUseMesh = false; + + UPROPERTY(EditAnywhere, Category = "Source|Sphere", meta = (ClampMin = 1, ClampMax = 512, UIMin = 1, UIMax = 512, EditCondition = "!bUseMesh")) + int32 TextureSize = 32; + + UPROPERTY(EditAnywhere, Category = "Source|Mesh", meta = (EditCondition = "bUseMesh")) + UStaticMesh* Mesh = nullptr; + + UPROPERTY(EditAnywhere, Category = "Source|Mesh", meta = (EditCondition = "bUseMesh")) + FVoxelMeshImporterSettingsBase MeshImporterSettings; + + UPROPERTY(EditAnywhere, Category = "Source|Mesh", meta = (EditCondition = "bUseMesh")) + float BoxExtension = 0.f; + + UPROPERTY(EditAnywhere, Category = "Source|Mesh", meta = (EditCondition = "bUseMesh")) + FTransform Transform; + + UPROPERTY(EditAnywhere, Category = "Preview") + EVoxelDebugSliceAxis SliceAxis = EVoxelDebugSliceAxis::Z; + + UPROPERTY(EditAnywhere, Category = "Preview") + int32 Slice = 0; + + UPROPERTY(EditAnywhere, Category = "Preview") + bool bFlip = false; + + UPROPERTY(EditAnywhere, Category = "Config", meta = (ClampMin = -1)) + int32 Passes = -1; + + UPROPERTY(EditAnywhere, Category = "Config") + bool bShowDistances = false; + + UPROPERTY(EditAnywhere, Category = "Config", meta = (ClampMin = 1)) + int32 Divisor = 1; + + UPROPERTY(EditAnywhere, Category = "Config") + bool bShrink = false; + + UPROPERTY(EditAnywhere, Category = "Config") + bool bUseCPU = false; + + UPROPERTY(EditAnywhere, Category = "Config") + bool bMultiThreaded = false; + + UPROPERTY(VisibleAnywhere, Category = "Info") + FIntVector Size; + + UPROPERTY(VisibleAnywhere, Category = "Info") + float TimeInSeconds = 0; +}; + +UCLASS() +class UVoxelDebugParameters_CustomData : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + EVoxelDebugSliceAxis SliceAxis = EVoxelDebugSliceAxis::Z; + + UPROPERTY(EditAnywhere, Category = "Config") + int32 Slice = 0; + + UPROPERTY(EditAnywhere, Category = "Config") + bool bFlip = false; + + UPROPERTY(EditAnywhere, Category = "Source", Transient) + TMap DataToDisplay; + + UPROPERTY(EditAnywhere, Category = "Source", Transient, meta = (UIMin = 0, UIMax = 99)) + int32 Frame = 0; + + UPROPERTY(VisibleAnywhere, Category = "Info") + FIntVector Size; + +public: + template + struct TData + { + TArray Data = { T{} }; + FIntVector Size = { 1, 1, 1}; + }; + + TMap>> ValueData; + TMap>> FloatData; + + FSimpleMulticastDelegate PostEditChange; + + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +}; + +class FVoxelDebugEditor +{ +public: + static TSharedRef CreateTab(const FSpawnTabArgs& Args); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.cpp new file mode 100644 index 00000000..f60e1f42 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.cpp @@ -0,0 +1,16 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEditorDetailsIncludes.h" +#include "UserInterface/PropertyEditor/PropertyEditorConstants.cpp" + +FSimpleDelegate FVoxelEditorUtilities::MakeRefreshDelegate(const IPropertyTypeCustomizationUtils& CustomizationUtils) +{ + return FSimpleDelegate::CreateLambda([Utilities = MakeWeakPtr(CustomizationUtils.GetPropertyUtilities())]() + { + auto Pinned = Utilities.Pin(); + if (Pinned.IsValid()) + { + Pinned->ForceRefresh(); + } + }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.h new file mode 100644 index 00000000..514a4218 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelMinimal.h" + +#include "UnrealEd.h" +#include "IDetailGroup.h" +#include "PropertyHandle.h" +#include "ScopedTransaction.h" +#include "IPropertyUtilities.h" +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" +#include "IDetailChildrenBuilder.h" +#include "PropertyCustomizationHelpers.h" + +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Input/SNumericEntryBox.h" + +#include "UserInterface/PropertyEditor/PropertyEditorConstants.h" + +namespace FVoxelEditorUtilities +{ + FSimpleDelegate MakeRefreshDelegate(const IPropertyTypeCustomizationUtils& CustomizationUtils); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.cpp new file mode 100644 index 00000000..0c806ce8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.cpp @@ -0,0 +1,174 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelMinimal.h" + +#include "Misc/MessageDialog.h" +#include "UnrealClient.h" +#include "Editor.h" +#include "EditorViewportClient.h" +#include "EditorDirectories.h" +#include "Editor/EditorEngine.h" +#include "ObjectTools.h" + +#include "DetailLayoutBuilder.h" +#include "DetailCategoryBuilder.h" +#include "DetailWidgetRow.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" +#include "Factories/Factory.h" +#include "Modules/ModuleManager.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "AssetToolsModule.h" + +bool FVoxelEditorUtilities::ShowWarning(const FText& Text) +{ + return FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(VOXEL_LOCTEXT("Warning: {0} \nContinue?"), Text)) == EAppReturnType::Yes; +} + +void FVoxelEditorUtilities::ShowError(const FText& Text) +{ + FMessageDialog::Open(EAppMsgType::Ok, FText::Format(VOXEL_LOCTEXT("Error: {0}"), Text)); +} + +void FVoxelEditorUtilities::EnableRealtime() +{ + FViewport* Viewport = GEditor->GetActiveViewport(); + if (Viewport) + { + FViewportClient* Client = Viewport->GetClient(); + if (Client) + { + for (FEditorViewportClient* EditorViewportClient : GEditor->GetAllViewportClients()) + { + if (EditorViewportClient == Client) + { + EditorViewportClient->SetRealtime(true); + EditorViewportClient->SetShowStats(true); // Show stats as well + break; + } + } + } + } +} + +TSharedRef FVoxelEditorUtilities::CreateText(const FText& Text, TAttribute ColorAndOpacity) +{ + return SNew(STextBlock) + .Font(IDetailLayoutBuilder::GetDetailFont()) + .Text(Text) + .ColorAndOpacity(ColorAndOpacity); +} + +TSharedRef FVoxelEditorUtilities::CreateButton( + const FText& Text, + const FOnClicked& OnClicked, + const TAttribute& IsEnabled) +{ + return SNew(SButton) + .ContentPadding(2) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .OnClicked(OnClicked) + .IsEnabled(IsEnabled) + [ + CreateText(Text) + ]; +} + +void FVoxelEditorUtilities::AddButtonToCategory( + IDetailLayoutBuilder& DetailLayout, + const FName& CategoryName, + const FText& FilterString, + const FText& TextLeftToButton, + const FText& ButtonText, + bool bForAdvanced, + const FOnClicked& OnClicked, + const TAttribute& IsEnabled) +{ + DetailLayout.EditCategory(CategoryName) + .AddCustomRow(FilterString, bForAdvanced) + .NameContent() + [ + CreateText(TextLeftToButton) + ] + .ValueContent() + .MinDesiredWidth(125.0f) + .MaxDesiredWidth(125.0f) + [ + CreateButton(ButtonText, OnClicked, IsEnabled) + ]; +} + +void FVoxelEditorUtilities::AddPropertyToCategory( + IDetailLayoutBuilder& DetailLayout, + const FName& CategoryName, + const FName& PropertyName, + bool bForAdvanced, + UClass* Class) +{ + const auto Property = DetailLayout.GetProperty(PropertyName, Class); + DetailLayout.EditCategory(CategoryName).AddProperty(Property, bForAdvanced ? EPropertyLocation::Advanced : EPropertyLocation::Default); +} + +UObject* FVoxelEditorUtilities::CreateAssetWithDialog( + const FString& AssetName, + const FString& PackagePath, + UClass* AssetClass, + UFactory* Factory, + FName CallingContext) +{ + FSaveAssetDialogConfig SaveAssetDialogConfig; + SaveAssetDialogConfig.DialogTitleOverride = VOXEL_LOCTEXT("Save As"); + SaveAssetDialogConfig.DefaultPath = PackagePath; + SaveAssetDialogConfig.DefaultAssetName = AssetName; + SaveAssetDialogConfig.ExistingAssetPolicy = ESaveAssetDialogExistingAssetPolicy::Disallow; + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + FString SaveObjectPath = ContentBrowserModule.Get().CreateModalSaveAssetDialog(SaveAssetDialogConfig); + if (!SaveObjectPath.IsEmpty()) + { + FEditorDelegates::OnConfigureNewAssetProperties.Broadcast(Factory); + if (Factory->ConfigureProperties()) + { + const FString SavePackageName = FPackageName::ObjectPathToPackageName(SaveObjectPath); + const FString SavePackagePath = FPaths::GetPath(SavePackageName); + const FString SaveAssetName = FPaths::GetBaseFilename(SavePackageName); + FEditorDirectories::Get().SetLastDirectory(ELastDirectory::NEW_ASSET, PackagePath); + + return FAssetToolsModule::GetModule().Get().CreateAsset(SaveAssetName, SavePackagePath, AssetClass, Factory, CallingContext); + } + } + + return nullptr; +} + +UObject* FVoxelEditorUtilities::CreateAssetWithDialog( + UClass* AssetClass, + UFactory* Factory, + FName CallingContext) +{ + if (Factory != nullptr) + { + // Determine the starting path. Try to use the most recently used directory + FString AssetPath; + + const FString DefaultFilesystemDirectory = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::NEW_ASSET); + if (DefaultFilesystemDirectory.IsEmpty() || !FPackageName::TryConvertFilenameToLongPackageName(DefaultFilesystemDirectory, AssetPath)) + { + // No saved path, just use the game content root + AssetPath = TEXT("/Game"); + } + + FString PackageName; + FString AssetName; + FAssetToolsModule::GetModule().Get().CreateUniqueAssetName(AssetPath / Factory->GetDefaultNewAssetName(), TEXT(""), PackageName, AssetName); + + return CreateAssetWithDialog(AssetName, AssetPath, AssetClass, Factory, CallingContext); + } + + return nullptr; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.h new file mode 100644 index 00000000..7bcba7be --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.h @@ -0,0 +1,56 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Framework/SlateDelegates.h" + +class STextBlock; +class SButton; +class UFactory; +class IDetailLayoutBuilder; + +namespace FVoxelEditorUtilities +{ + bool ShowWarning(const FText& Text); + void ShowError(const FText& Text); + void EnableRealtime(); + + TSharedRef CreateText(const FText& Text, TAttribute ColorAndOpacity = {}); + + TSharedRef CreateButton( + const FText& Text, + const FOnClicked& OnClicked, + const TAttribute& IsEnabled = true); + + void AddButtonToCategory( + IDetailLayoutBuilder& DetailLayout, + const FName& CategoryName, + const FText& FilterString, + const FText& TextLeftToButton, + const FText& ButtonText, + bool bForAdvanced, + const FOnClicked& OnClicked, + const TAttribute& IsEnabled = true); + + void AddPropertyToCategory( + IDetailLayoutBuilder& DetailLayout, + const FName& CategoryName, + const FName& PropertyName, + bool bForAdvanced, + UClass* Class = nullptr); + + // Same as engine one, but don't allow replacing assets + UObject* CreateAssetWithDialog( + const FString& AssetName, + const FString& PackagePath, + UClass* AssetClass, + UFactory* Factory, + FName CallingContext); + + // Same as engine one, but don't allow replacing assets + UObject* CreateAssetWithDialog( + UClass* AssetClass, + UFactory* Factory, + FName CallingContext = NAME_None); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorModule.cpp new file mode 100644 index 00000000..80718cae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorModule.cpp @@ -0,0 +1,561 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEditorModule.h" +#include "VoxelMinimal.h" + +#include "Interfaces/IPluginManager.h" +#include "IPlacementModeModule.h" +#include "PropertyEditorModule.h" +#include "Styling/SlateStyle.h" +#include "Styling/SlateStyleRegistry.h" +#include "EditorModeRegistry.h" +#include "EditorSupportDelegates.h" +#include "LevelEditor.h" +#include "EngineUtils.h" +#include "MessageLogModule.h" +#include "EditorReimportHandler.h" +#include "Framework/Commands/Commands.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + +#include "WorkspaceMenuStructure.h" +#include "WorkspaceMenuStructureModule.h" + +#include "VoxelGraphGenerator.h" +#include "IVoxelPool.h" +#include "VoxelWorld.h" +#include "VoxelTexture.h" +#include "VoxelMessages.h" +#include "VoxelBoolVector.h" +#include "VoxelMessagesEditor.h" +#include "VoxelEditorDelegates.h" +#include "VoxelOpenAssetsOnStartup.h" +#include "VoxelConvertLandscapeMaterial.h" +#include "VoxelTools/VoxelPaintMaterial.h" +#include "VoxelNodes/VoxelOptimizationNodes.h" +#include "VoxelPlaceableItems/Actors/VoxelAssetActor.h" +#include "VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h" + +#include "AssetTools/AssetTypeActions_VoxelDataAsset.h" +#include "AssetTools/AssetTypeActions_VoxelHeightmapAsset.h" +#include "AssetTools/AssetTypeActions_VoxelGraphGenerator.h" +#include "AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h" +#include "AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h" +#include "AssetTools/AssetTypeActions_VoxelSpawnerConfig.h" +#include "AssetTools/AssetTypeActions_VoxelSpawners.h" +#include "AssetTools/AssetTypeActions_VoxelGraphMacro.h" +#include "AssetTools/AssetTypeActions_VoxelWorldSaveObject.h" +#include "AssetTools/AssetTypeActions_VoxelMaterialCollection.h" + +#include "Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h" +#include "Thumbnails/VoxelSpawnersThumbnailRenderer.h" +#include "Thumbnails/VoxelDataAssetThumbnailRenderer.h" +#include "Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h" + +#include "DataAssetEditor/VoxelDataAssetEditorToolkit.h" +#include "EdMode/VoxelEdMode.h" +#include "VoxelToolsCommands.h" + +#include "ActorFactoryVoxelWorld.h" +#include "ActorFactoryVoxelPlaceableItems.h" +#include "ActorFactoryVoxelMeshImporter.h" + +#include "Details/VoxelWorldDetails.h" +#include "Details/VoxelLandscapeImporterDetails.h" +#include "Details/VoxelMeshImporterDetails.h" +#include "Details/VoxelAssetActorDetails.h" +#include "Details/VoxelGeneratorPickerCustomization.h" +#include "Details/RangeAnalysisDebuggerDetails.h" +#include "Details/VoxelPaintMaterialCustomization.h" +#include "Details/VoxelMeshSpawnerBaseDetails.h" +#include "Details/VoxelBasicSpawnerScaleSettingsCustomization.h" +#include "Details/VoxelSpawnerOutputNameCustomization.h" +#include "Details/VoxelSpawnerDensityCustomization.h" +#include "Details/VoxelSpawnerConfigSpawnerCustomization.h" +#include "Details/VoxelGraphOutputCustomization.h" +#include "Details/VoxelInt32IntervalCustomization.h" +#include "Details/VoxelBoolVectorCustomization.h" + +#include "VoxelImporters/VoxelMeshImporter.h" +#include "VoxelImporters/VoxelLandscapeImporter.h" +#include "VoxelComponents/VoxelInvokerComponent.h" + +#include "Factories/VoxelWorldSaveObjectFactory.h" +#include "VoxelEditorDetailsUtilities.h" +#include "VoxelDebugEditor.h" +#include "VoxelScopedTransaction.h" +#include "VoxelWorldEditorControls.h" + +const FVector2D Icon14x14(14.0f, 14.0f); +const FVector2D Icon16x16(16.0f, 16.0f); +const FVector2D Icon20x20(20.0f, 20.0f); +const FVector2D Icon40x40(40.0f, 40.0f); +const FVector2D Icon64x64(64.0f, 64.0f); +const FVector2D Icon512x512(512.0f, 512.0f); + +class FVoxelEditorCommands : public TCommands +{ +public: + FVoxelEditorCommands() + : TCommands + ( + "VoxelEditor", // Context name for icons + VOXEL_LOCTEXT("Voxel Editor"), // Localized context name for displaying + NAME_None, // Parent + "VoxelStyle" // Icon Style Set + ) + { + } + + TSharedPtr RefreshVoxelWorlds; + +#define LOCTEXT_NAMESPACE "Voxel" + virtual void RegisterCommands() override + { + UI_COMMAND( + RefreshVoxelWorlds, + "Retoggle", + "Retoggle the voxel worlds", + EUserInterfaceActionType::Button, + FInputChord(EModifierKey::Control, EKeys::F5)); + } +#undef LOCTEXT_NAMESPACE +}; + +static void RefreshVoxelWorlds_Execute(UObject* MatchingGenerator = nullptr) +{ + FViewport* Viewport = GEditor->GetActiveViewport(); + if (Viewport) + { + FViewportClient* Client = Viewport->GetClient(); + if (Client) + { + UWorld* World = Client->GetWorld(); + if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview)) + { + for (TActorIterator It(World); It; ++It) + { + if (It->IsCreated() && (!MatchingGenerator || It->Generator.GetObject() == MatchingGenerator)) + { + It->Toggle(); + It->Toggle(); + } + } + for (TActorIterator It(World); It; ++It) + { + It->UpdatePreview(); + } + } + } + } +} + +static void BindEditorDelegates(IVoxelEditorDelegatesInterface* Interface, UObject* Object) +{ + check(Interface && Object); + + if (!FEditorDelegates::PreSaveWorld.IsBoundToObject(Object)) + { + FEditorDelegates::PreSaveWorld.AddWeakLambda(Object, [=](uint32 SaveFlags, UWorld* World) { Interface->OnPreSaveWorld(SaveFlags, World); }); + } + if (!FEditorDelegates::PreBeginPIE.IsBoundToObject(Object)) + { + FEditorDelegates::PreBeginPIE.AddWeakLambda(Object, [=](bool bIsSimulating) { Interface->OnPreBeginPIE(bIsSimulating); }); + } + if (!FEditorDelegates::EndPIE.IsBoundToObject(Object)) + { + FEditorDelegates::EndPIE.AddWeakLambda(Object, [=](bool bIsSimulating) { Interface->OnEndPIE(bIsSimulating); }); + } + if (!FEditorDelegates::OnApplyObjectToActor.IsBoundToObject(Object)) + { + FEditorDelegates::OnApplyObjectToActor.AddWeakLambda(Object, [=](UObject* InObject, AActor* InActor) { Interface->OnApplyObjectToActor(InObject, InActor); }); + } + if (!FEditorSupportDelegates::PrepareToCleanseEditorObject.IsBoundToObject(Object)) + { + FEditorSupportDelegates::PrepareToCleanseEditorObject.AddWeakLambda(Object, [=](UObject* InObject) { Interface->OnPrepareToCleanseEditorObject(InObject); }); + } + if (!FCoreDelegates::OnPreExit.IsBoundToObject(Object)) + { + FCoreDelegates::OnPreExit.AddWeakLambda(Object, [=]() { Interface->OnPreExit(); }); + } +} + +class FVoxelWorldEditor : public IVoxelWorldEditor +{ +public: + FVoxelWorldEditor() = default; + + virtual UVoxelWorldSaveObject* CreateSaveObject() override + { + return Cast(FVoxelEditorUtilities::CreateAssetWithDialog(UVoxelWorldSaveObject::StaticClass(), NewObject())); + } + + virtual UClass* GetVoxelWorldEditorClass() override + { + return AVoxelWorldEditorControls::StaticClass(); + } + + virtual void RegisterTransaction(AVoxelWorld* VoxelWorld, FName Name) override + { + FVoxelScopedTransaction Transaction(VoxelWorld, Name, EVoxelChangeType::Edit); + } +}; + +/** + * Implements the VoxelEditor module. + */ +class FVoxelEditorModule : public IVoxelEditorModule +{ +public: + virtual void StartupModule() override + { + UVoxelOpenAssetsOnStartup::Init(); + FVoxelConvertLandscapeMaterial::Init(); + + // Voxel World Editor + if (!IVoxelWorldEditor::GetVoxelWorldEditor()) + { + IVoxelWorldEditor::SetVoxelWorldEditor(MakeShared()); + } + + FVoxelEditorDelegates::FixVoxelLandscapeMaterial.AddStatic(&FVoxelConvertLandscapeMaterial::ConvertMaterial); + + // Destroy global pool on end PIE + FEditorDelegates::EndPIE.AddLambda([](bool bIsSimulating) + { + if (IVoxelPool::GetGlobalPool()) + { + IVoxelPool::DestroyGlobalPool(); + } + }); + + // Clear texture cache on reimport + FReimportManager::Instance()->OnPostReimport().AddLambda([](UObject*, bool) { FVoxelTextureUtilities::ClearCache(); }); + + // Global commands + FVoxelEditorCommands::Register(); + FVoxelToolsCommands::Register(); + + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + LevelEditorModule.GetGlobalLevelEditorActions()->MapAction( + FVoxelEditorCommands::Get().RefreshVoxelWorlds, + FExecuteAction::CreateStatic(&RefreshVoxelWorlds_Execute, (UObject*)nullptr), + FCanExecuteAction()); + + IVoxelEditorDelegatesInterface::BindEditorDelegatesDelegate.AddStatic(&BindEditorDelegates); + + // Blueprint errors + FVoxelMessages::LogMessageDelegate.AddStatic(&FVoxelMessagesEditor::LogMessage); + FVoxelMessages::ShowNotificationDelegate.AddStatic(&FVoxelMessagesEditor::ShowNotification); + + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + FMessageLogInitializationOptions InitOptions; + InitOptions.bShowFilters = true; + InitOptions.bShowPages = false; + InitOptions.bAllowClear = true; + MessageLogModule.RegisterLogListing("Voxel", VOXEL_LOCTEXT("Voxel"), InitOptions); + + // Voxel asset category + IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + VoxelAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory("Voxel", VOXEL_LOCTEXT("Voxel")); + + RegisterPlacementModeExtensions(); + RegisterCustomClassLayouts(); + RegisterAssetTools(); + + // Thumbnails + auto& ThumbnailManager = UThumbnailManager::Get(); + ThumbnailManager.RegisterCustomRenderer(UVoxelGraphGenerator ::StaticClass(), UVoxelGraphGeneratorThumbnailRenderer ::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelDataAsset ::StaticClass(), UVoxelDataAssetThumbnailRenderer ::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelHeightmapAsset ::StaticClass(), UVoxelHeightmapAssetThumbnailRenderer ::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelMeshSpawner ::StaticClass(), UVoxelMeshSpawnerThumbnailRenderer ::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelMeshSpawnerGroup::StaticClass(), UVoxelMeshSpawnerGroupThumbnailRenderer::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelAssetSpawner ::StaticClass(), UVoxelAssetSpawnerThumbnailRenderer ::StaticClass()); + ThumbnailManager.RegisterCustomRenderer(UVoxelSpawnerGroup ::StaticClass(), UVoxelSpawnerGroupThumbnailRenderer ::StaticClass()); + + // Icons + { + FString ContentDir = IPluginManager::Get().FindPlugin(VOXEL_PLUGIN_NAME)->GetContentDir() + "/"; + + StyleSet = MakeShareable(new FSlateStyleSet("VoxelStyle")); + StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate")); + StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); + + // For menus + StyleSet->Set("VoxelIcon" , new FSlateImageBrush(ContentDir + TEXT("Editor/UIIcons/mode_40.png"), Icon16x16)); + + // VoxelWorld + StyleSet->Set("ClassThumbnail.VoxelWorld" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/World_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelWorld" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/World_16x.png"), Icon16x16)); + + // Voxel Material Collection + StyleSet->Set("ClassThumbnail.VoxelMaterialCollectionBase" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/MaterialCollection_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelMaterialCollectionBase" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/MaterialCollection_16x.png"), Icon16x16)); + + // Importers + StyleSet->Set("ClassThumbnail.VoxelLandscapeImporter" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Import_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelLandscapeImporter" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Import_16x.png"), Icon16x16)); + StyleSet->Set("ClassThumbnail.VoxelMeshImporter" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Import_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelMeshImporter" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Import_16x.png"), Icon16x16)); + + // Spawners + StyleSet->Set("ClassThumbnail.VoxelSpawnerConfig" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerConfig_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelSpawnerConfig" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerConfig_16x.png"), Icon16x16)); + StyleSet->Set("ClassThumbnail.VoxelSpawner" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Spawner_64x.png") , Icon64x64)); + StyleSet->Set("ClassIcon.VoxelSpawner" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Spawner_16x.png") , Icon16x16)); + StyleSet->Set("ClassThumbnail.VoxelSpawnerGroup" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerGroup_64x.png") , Icon64x64)); + StyleSet->Set("ClassIcon.VoxelSpawnerGroup" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerGroup_16x.png") , Icon16x16)); + StyleSet->Set("ClassThumbnail.VoxelMeshSpawnerGroup" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerGroup_64x.png") , Icon64x64)); + StyleSet->Set("ClassIcon.VoxelMeshSpawnerGroup" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/SpawnerGroup_16x.png") , Icon16x16)); + + // Voxel Graph + StyleSet->Set("ClassThumbnail.VoxelGraphGenerator" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/VoxelGraph_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelGraphGenerator" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/VoxelGraph_16x.png"), Icon16x16)); + + // Data Asset + StyleSet->Set("ClassThumbnail.VoxelDataAsset" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/DataAsset_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelDataAsset" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/DataAsset_16x.png"), Icon16x16)); + + // Landscape asset + StyleSet->Set("ClassThumbnail.VoxelLandscapeAsset" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Landscape_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelLandscapeAsset" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Landscape_16x.png"), Icon16x16)); + + // Data Asset Editor + StyleSet->Set("VoxelDataAssetEditor.InvertDataAsset" , new FSlateImageBrush(ContentDir + TEXT("Editor/UIIcons/InvertDataAsset_40x.png"), Icon40x40)); + StyleSet->Set("VoxelDataAssetEditor.InvertDataAsset.Small" , new FSlateImageBrush(ContentDir + TEXT("Editor/UIIcons/InvertDataAsset_16x.png"), Icon16x16)); + + // Voxel Editor Tools + StyleSet->Set("VoxelTools.Tab" , new FSlateImageBrush(ContentDir + TEXT("Editor/UIIcons/mode_40.png"), Icon40x40)); + StyleSet->Set("VoxelTools.Tab.Small" , new FSlateImageBrush(ContentDir + TEXT("Editor/UIIcons/mode_40.png"), Icon16x16)); + + // Generator + StyleSet->Set("ClassThumbnail.VoxelGenerator" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Generator_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelGenerator" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/Generator_16x.png"), Icon16x16)); + + // Voxel World Object Save + StyleSet->Set("ClassThumbnail.VoxelWorldSaveObject" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/VoxelWorldSaveObject_64x.png"), Icon64x64)); + StyleSet->Set("ClassIcon.VoxelWorldSaveObject" , new FSlateImageBrush(ContentDir + TEXT("Editor/AssetIcons/VoxelWorldSaveObject_16x.png"), Icon16x16)); + + // Tools + const auto AddTool = [&](const FString& Name) + { + StyleSet->Set(*("VoxelTools." + Name), new FSlateImageBrush(ContentDir + "Editor/UIIcons/Tools/" + Name + "_40.png", Icon40x40)); + StyleSet->Set(*("VoxelTools." + Name + ".Small"), new FSlateImageBrush(ContentDir + "Editor/UIIcons/Tools/" + Name + "_40.png", Icon16x16)); + }; + AddTool("FlattenTool"); + AddTool("LevelTool"); + AddTool("MeshTool"); + AddTool("RevertTool"); + AddTool("SmoothTool"); + AddTool("SphereTool"); + AddTool("SurfaceTool"); + AddTool("TrimTool"); + + FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); + } + + { + FTabSpawnerEntry& SpawnerEntry = FGlobalTabmanager::Get()->RegisterNomadTabSpawner("VoxelDebug", FOnSpawnTab::CreateStatic(&FVoxelDebugEditor::CreateTab)) + .SetDisplayName(VOXEL_LOCTEXT("Voxel Debug")) + .SetIcon(FSlateIcon(StyleSet->GetStyleSetName(), "VoxelIcon")); + SpawnerEntry.SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory()); + } + + // Voxel Editor Tools + FEditorModeRegistry::Get().RegisterMode(FEdModeVoxel::EM_Voxel, VOXEL_LOCTEXT("Voxels"), FSlateIcon("VoxelStyle", "VoxelTools.Tab", "VoxelTools.Tab.Small"), true); + } + + virtual void ShutdownModule() override + { + FEditorModeRegistry::Get().UnregisterMode(FEdModeVoxel::EM_Voxel); + + if (UObjectInitialized()) + { + auto& ThumbnailManager = UThumbnailManager::Get(); + ThumbnailManager.UnregisterCustomRenderer(UVoxelGraphGenerator::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelDataAsset::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelHeightmapAsset::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelMeshSpawner::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelMeshSpawnerGroup::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelAssetSpawner::StaticClass()); + ThumbnailManager.UnregisterCustomRenderer(UVoxelSpawnerGroup::StaticClass()); + } + + UnregisterPlacementModeExtensions(); + UnregisterClassLayout(); + UnregisterAssetTools(); + + if (StyleSet.IsValid()) + { + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); + StyleSet.Reset(); + } + } + + virtual bool SupportsDynamicReloading() override + { + return true; + } + +private: + template + void RegisterPlacementModeExtension(IPlacementModeModule& PlacementModeModule, UActorFactory* Factory = nullptr) + { + PlacementModeModule.RegisterPlaceableItem(PlacementCategoryInfo.UniqueHandle, MakeShared(Factory, FAssetData(T::StaticClass()))); + } + void RegisterPlacementModeExtensions() + { + IPlacementModeModule& PlacementModeModule = IPlacementModeModule::Get(); + PlacementModeModule.RegisterPlacementCategory(PlacementCategoryInfo); + + RegisterPlacementModeExtension(PlacementModeModule, GetMutableDefault()); + RegisterPlacementModeExtension(PlacementModeModule, GetMutableDefault()); + RegisterPlacementModeExtension(PlacementModeModule, GetMutableDefault()); + RegisterPlacementModeExtension(PlacementModeModule, GetMutableDefault()); + + RegisterPlacementModeExtension(PlacementModeModule); + RegisterPlacementModeExtension(PlacementModeModule); + + PlacementModeModule.RegenerateItemsForCategory(FBuiltInPlacementCategories::AllClasses()); + } + void UnregisterPlacementModeExtensions() + { + if (IPlacementModeModule::IsAvailable()) + { + IPlacementModeModule::Get().UnregisterPlacementCategory(PlacementCategoryInfo.UniqueHandle); + } + } + +private: + template + void RegisterCustomClassLayout() + { + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + + const FName Name = TClass::StaticClass()->GetFName(); + PropertyModule.RegisterCustomClassLayout(Name, FOnGetDetailCustomizationInstance::CreateLambda([]() { return MakeShared(); })); + RegisteredCustomClassLayouts.Add(Name); + } + template + void RegisterCustomPropertyLayout() + { + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + + const FName Name = TStruct::StaticStruct()->GetFName(); + PropertyModule.RegisterCustomPropertyTypeLayout(Name, FOnGetPropertyTypeCustomizationInstance::CreateLambda([]() { return MakeShared(); })); + RegisteredCustomPropertyLayouts.Add(Name); + } + + void RegisterCustomClassLayouts() + { + RegisterCustomClassLayout(); + RegisterCustomClassLayout(); + RegisterCustomClassLayout(); + RegisterCustomClassLayout(); + RegisterCustomClassLayout(); + RegisterCustomClassLayout(); + + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + RegisterCustomPropertyLayout(); + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + PropertyModule.NotifyCustomizationModuleChanged(); + } + + void UnregisterClassLayout() + { + if (auto* PropertyModule = FModuleManager::GetModulePtr("PropertyEditor")) + { + for (auto& Name : RegisteredCustomClassLayouts) + { + PropertyModule->UnregisterCustomClassLayout(Name); + } + for (auto& Name : RegisteredCustomPropertyLayouts) + { + PropertyModule->UnregisterCustomPropertyTypeLayout(Name); + } + PropertyModule->NotifyCustomizationModuleChanged(); + } + } + +private: + template + void RegisterAssetTypeAction() + { + IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + + auto Action = MakeShared(VoxelAssetCategoryBit); + AssetTools.RegisterAssetTypeActions(Action); + RegisteredAssetTypeActions.Add(Action); + } + + void RegisterAssetTools() + { + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + RegisterAssetTypeAction(); + } + + void UnregisterAssetTools() + { + if (auto* AssetToolsModule = FModuleManager::GetModulePtr("AssetTools")) + { + IAssetTools& AssetTools = AssetToolsModule->Get(); + for (auto& Action : RegisteredAssetTypeActions) + { + AssetTools.UnregisterAssetTypeActions(Action); + } + } + } + +public: + virtual void CreateVoxelDataAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UVoxelDataAsset* DataAsset) override + { + TSharedRef NewVoxelEditor(new FVoxelDataAssetEditorToolkit()); + NewVoxelEditor->InitVoxelEditor(Mode, InitToolkitHost, DataAsset); + } + + virtual void RefreshVoxelWorlds(UObject* MatchingGenerator) override + { + RefreshVoxelWorlds_Execute(MatchingGenerator); + } + + virtual EAssetTypeCategories::Type GetVoxelAssetTypeCategory() const override + { + return VoxelAssetCategoryBit; + } + +private: + /** The collection of registered asset type actions. */ + TArray> RegisteredAssetTypeActions; + TArray RegisteredCustomClassLayouts; + TArray RegisteredCustomPropertyLayouts; + + EAssetTypeCategories::Type VoxelAssetCategoryBit = EAssetTypeCategories::None; + FPlacementCategoryInfo PlacementCategoryInfo = FPlacementCategoryInfo(VOXEL_LOCTEXT("Voxel"), "Voxel", TEXT("PMVoxel"), 25); + TSharedPtr StyleSet; +}; + +IMPLEMENT_MODULE(FVoxelEditorModule, VoxelEditor); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp new file mode 100644 index 00000000..1b28288a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp @@ -0,0 +1,795 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEditorToolsPanel.h" +#include "VoxelTools/VoxelToolManager.h" +#include "VoxelTools/Tools/VoxelTool.h" + +#include "VoxelTools/Tools/VoxelFlattenTool.h" +#include "VoxelTools/Tools/VoxelLevelTool.h" +#include "VoxelTools/Tools/VoxelMeshTool.h" +#include "VoxelTools/Tools/VoxelRevertTool.h" +#include "VoxelTools/Tools/VoxelSmoothTool.h" +#include "VoxelTools/Tools/VoxelSphereTool.h" +#include "VoxelTools/Tools/VoxelSurfaceTool.h" +#include "VoxelTools/Tools/VoxelTrimTool.h" + +#include "VoxelWorld.h" +#include "VoxelToolsCommands.h" +#include "VoxelDelegateHelpers.h" +#include "VoxelScopedTransaction.h" +#include "ActorFactoryVoxelWorld.h" +#include "VoxelUtilities/VoxelConfigUtilities.h" + +#include "EngineUtils.h" +#include "Editor.h" +#include "EditorViewportClient.h" +#include "DetailLayoutBuilder.h" +#if ENGINE_MINOR_VERSION >= 25 +#include "VariablePrecisionNumericInterface.h" +#endif +#include "Modules/ModuleManager.h" +#include "PropertyEditorModule.h" +#include "Misc/ConfigCacheIni.h" +#include "Misc/MessageDialog.h" +#include "Widgets/Layout/SScrollBox.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Input/SSpinBox.h" +#include "Slate/SceneViewport.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Application/SlateApplication.h" +#include "HAL/PlatformApplicationMisc.h" + +static const FString ToolConfigSectionName = "VoxelTool"; + +FVoxelEditorToolsPanel::FVoxelEditorToolsPanel() + : Widget(SNew(SBox)) +{ +} + +FVoxelEditorToolsPanel::~FVoxelEditorToolsPanel() +{ + if (ToolManager) + { + // Always save before exiting the panel (eg shortcuts do not dirty saves) + FVoxelConfigUtilities::SaveConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName); + if (auto* Tool = ToolManager->GetActiveTool()) + { + FVoxelConfigUtilities::SaveConfig(Tool, ToolConfigSectionName); + } + + ToolManager->SetActiveTool(nullptr); + } +} + +void FVoxelEditorToolsPanel::Init(const TSharedPtr& CommandListOverride) +{ + CommandList = CommandListOverride; + if (!CommandList.IsValid()) + { + CommandList = MakeShared(); + } + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea); + DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic; + + ToolManager = NewObject(GetTransientPackage(), NAME_None, RF_Transient | RF_Transactional); + ToolManager->CreateDefaultTools(true); + ToolManager->GetSharedConfig().RefreshDetails.AddSP(this, &FVoxelEditorToolsPanel::RefreshDetails); + ToolManager->GetSharedConfig().RegisterTransaction.AddLambda([](FName Name, AVoxelWorld* VoxelWorld) + { + FVoxelScopedTransaction Transaction(VoxelWorld, Name, EVoxelChangeType::Edit); + }); + ToolsOptions.Reset(); + for (auto* Tool : ToolManager->GetTools()) + { + FVoxelConfigUtilities::LoadConfig(Tool, ToolConfigSectionName); + ToolsOptions.Add(MakeSharedCopy(Tool->GetClass())); + } + FVoxelConfigUtilities::LoadConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName); + + const auto IsPropertyVisibleDelegate = MakeWeakPtrDelegate(this, [=](const FPropertyAndParent& PropertyAndParent) + { +#if ENGINE_MINOR_VERSION < 24 + TArray ParentProperties; + if (PropertyAndParent.ParentProperty) + { + ParentProperties.Add(PropertyAndParent.ParentProperty); + } +#else + const auto& ParentProperties = PropertyAndParent.ParentProperties; +#endif + return IsPropertyVisible(PropertyAndParent.Property, ParentProperties); + }); + + SharedConfigDetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + SharedConfigDetailsPanel->SetObject(&ToolManager->GetSharedConfig()); + SharedConfigDetailsPanel->SetIsPropertyVisibleDelegate(IsPropertyVisibleDelegate); + SharedConfigDetailsPanel->OnFinishedChangingProperties().AddWeakLambda(ToolManager, [=](auto&) + { + FVoxelConfigUtilities::SaveConfig(&ToolManager->GetSharedConfig(), ToolConfigSectionName); + }); + + ToolDetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + ToolDetailsPanel->SetObject(ToolManager->GetActiveTool()); + ToolDetailsPanel->SetIsPropertyVisibleDelegate(IsPropertyVisibleDelegate); + ToolDetailsPanel->OnFinishedChangingProperties().AddWeakLambda(ToolManager, [=](auto&) + { + if (auto* Tool = ToolManager->GetActiveTool()) + { + FVoxelConfigUtilities::SaveConfig(Tool, ToolConfigSectionName); + } + }); + + TSharedPtr ToolBarsVerticalBox; + TSharedPtr CustomToolBarsVerticalBox; + + Widget->SetContent( + SNew(SScrollBox) + + SScrollBox::Slot() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(FMargin(3, 5)) + .AutoHeight() + [ + SNew(SBorder) + .Visibility(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FVoxelEditorToolsPanel::GetAddVoxelWorldVisibility))) + .BorderBackgroundColor(FLinearColor::Red) + .BorderImage(FCoreStyle::Get().GetBrush("ErrorReporting.Box")) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .Padding(FMargin(3, 0)) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + SNew(STextBlock) + .ColorAndOpacity(FLinearColor::White) + .Font(FCoreStyle::GetDefaultFontStyle("Regular",12)) + .Text(VOXEL_LOCTEXT("No Voxel World in the scene!")) + ] + + SVerticalBox::Slot() + .Padding(FMargin(0, 5)) + [ + SNew(SButton) + .OnClicked(FOnClicked::CreateSP(this, &FVoxelEditorToolsPanel::AddVoxelWorld)) + [ + SNew(STextBlock) + .ColorAndOpacity(FLinearColor::Black) + .Font(FCoreStyle::GetDefaultFontStyle("Regular",12)) + .Text(VOXEL_LOCTEXT("Add Voxel World")) + .Justification(ETextJustify::Center) + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(2) + [ + SAssignNew(ToolBarsVerticalBox, SVerticalBox) + ] + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Center) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Browse to tool BP")) + .Visibility_Lambda([=]() + { + if (ToolManager->GetActiveTool() && !ToolManager->GetActiveTool()->GetClass()->HasAnyClassFlags(CLASS_Native)) + { + return EVisibility::Visible; + } + else + { + return EVisibility::Collapsed; + } + }) + .OnClicked_Lambda([=]() + { + TArray Objects = { ToolManager->GetActiveTool()->GetClass() }; + GEditor->SyncBrowserToObjects(Objects); + return FReply::Handled(); + }) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SharedConfigDetailsPanel.ToSharedRef() + ] + + SVerticalBox::Slot() + .FillHeight(1) + [ + ToolDetailsPanel.ToSharedRef() + ] + ]); + + TArray ToolBarBuilders; + TArray CustomToolBarBuilders; + BuildToolBars(ToolBarBuilders, CustomToolBarBuilders); + BindCommands(); + + for (auto& ToolBarBuilder : ToolBarBuilders) + { + ToolBarsVerticalBox->AddSlot() + .AutoHeight() + .HAlign(HAlign_Center) + [ + ToolBarBuilder.MakeWidget() + ]; + } + + ToolBarsVerticalBox->AddSlot() + .AutoHeight() + [ + SAssignNew(CustomToolBarsVerticalBox, SVerticalBox) + .Visibility_Lambda([=]() + { + return bShowCustomTools ? EVisibility::Visible : EVisibility::Collapsed; + }) + ]; + + GConfig->GetBool(TEXT("VoxelEditorToolsPanel"), TEXT("ShowCustomTools"), bShowCustomTools, GEditorPerProjectIni); + + ToolBarsVerticalBox->AddSlot() + .AutoHeight() + [ + SNew(SBorder) + .BorderImage(FEditorStyle::GetBrush("DetailsView.AdvancedDropdownBorder")) + .Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f)) + [ + SAssignNew(ExpanderButton, SButton) + .ButtonStyle(FEditorStyle::Get(), "NoBorder") + .HAlign(HAlign_Center) + .ContentPadding(2) + .OnClicked_Lambda([=]() + { + bShowCustomTools = !bShowCustomTools; + GConfig->SetBool(TEXT("VoxelEditorToolsPanel"), TEXT("ShowCustomTools"), bShowCustomTools, GEditorPerProjectIni); + return FReply::Handled(); + }) + .ToolTipText_Lambda([=]() + { + return bShowCustomTools ? VOXEL_LOCTEXT("Hide custom tools") : VOXEL_LOCTEXT("Show custom tools"); + }) + [ + SNew(SImage) + .Image_Lambda([=]() + { + if (ExpanderButton->IsHovered()) + { + return bShowCustomTools ? FEditorStyle::GetBrush("DetailsView.PulldownArrow.Up.Hovered") : FEditorStyle::GetBrush("DetailsView.PulldownArrow.Down.Hovered"); + } + else + { + return bShowCustomTools ? FEditorStyle::GetBrush("DetailsView.PulldownArrow.Up") : FEditorStyle::GetBrush("DetailsView.PulldownArrow.Down"); + } + }) + ] + ] + ]; + + CustomToolBarsVerticalBox->AddSlot() + .AutoHeight() + [ + SNew(SBorder) + .BorderImage(FEditorStyle::GetBrush("DetailsView.CategoryMiddle")) + .Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f)) + [ + SNew(SImage) + .Image(FEditorStyle::GetBrush("DetailsView.AdvancedDropdownBorder.Open")) + ] + ]; + + for (auto& ToolBarBuilder : CustomToolBarBuilders) + { + CustomToolBarsVerticalBox->AddSlot() + .AutoHeight() + .HAlign(HAlign_Center) + [ + ToolBarBuilder.MakeWidget() + ]; + } + + FString ActiveTool; + GConfig->GetString(TEXT("VoxelEditorToolsPanel"), TEXT("ActiveTool"), ActiveTool, GEditorPerProjectIni); + + SetActiveTool(*ToolsOptions[0]); + for (auto& It : ToolsOptions) + { + if ((**It).GetName() == ActiveTool) + { + SetActiveTool(*It); + } + } + + // Show tooltip dialog + bool bShowToolsShortcutsDialog = true; + if (!GConfig->GetBool( TEXT("VoxelEditorToolsPanel"), TEXT("ShowToolsShortcutsDialog"), bShowToolsShortcutsDialog, GEditorPerProjectIni) || bShowToolsShortcutsDialog) + { + const auto Result = FMessageDialog::Open(EAppMsgType::YesNo, VOXEL_LOCTEXT( + "The voxel plugin tools shortcuts are the following:\n" + "1-8 (alphanum): select tools\n" + "[ ]: decrease/increase brush size\n" + "< >: decrease/increase brush strength\n\n" + "You can configure these shortcuts at any time in Edit/Editor Preferences/Keyboard Shortcuts/Voxel\n\n" + "Continue to show this popup?")); + + if (Result == EAppReturnType::No) + { + GConfig->SetBool( TEXT("VoxelEditorToolsPanel"), TEXT("ShowToolsShortcutsDialog"), false, GEditorPerProjectIni); + } + } +} + +void FVoxelEditorToolsPanel::CustomizeToolbar(FToolBarBuilder& ToolBarBuilder) +{ + const auto& Commands = FVoxelToolsCommands::Get(); + +#if ENGINE_MINOR_VERSION >= 25 + ToolBarBuilder.AddToolBarButton(Commands.SurfaceTool); + ToolBarBuilder.AddToolBarButton(Commands.SmoothTool); + ToolBarBuilder.AddToolBarButton(Commands.MeshTool); + ToolBarBuilder.AddToolBarButton(Commands.SphereTool); + + ToolBarBuilder.AddToolBarButton(Commands.FlattenTool); + ToolBarBuilder.AddToolBarButton(Commands.LevelTool); + ToolBarBuilder.AddToolBarButton(Commands.TrimTool); + ToolBarBuilder.AddToolBarButton(Commands.RevertTool); + + ToolBarBuilder.AddSeparator(); + + const auto NumericInterface = MakeShared(); + + // Brush Size + { + FProperty* BrushRadiusProperty = UVoxelToolSharedConfig::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UVoxelToolSharedConfig, BrushSize)); + const FString& UIMinString = BrushRadiusProperty->GetMetaData("UIMin"); + const FString& UIMaxString = BrushRadiusProperty->GetMetaData("UIMax"); + const FString& SliderExponentString = BrushRadiusProperty->GetMetaData("SliderExponent"); + float UIMin = TNumericLimits::Lowest(); + float UIMax = TNumericLimits::Max(); + TTypeFromString::FromString(UIMin, *UIMinString); + TTypeFromString::FromString(UIMax, *UIMaxString); + float SliderExponent = 1.0f; + if (SliderExponentString.Len()) + { + TTypeFromString::FromString(SliderExponent, *SliderExponentString); + } + + const auto SizeControl = + SNew(SSpinBox) + .Style(&FEditorStyle::Get().GetWidgetStyle("LandscapeEditor.SpinBox")) + .PreventThrottling(true) + .Value_Lambda([=]() -> float { return ToolManager->GetSharedConfig().BrushSize; }) + .OnValueChanged_Lambda([=](float NewValue) { ToolManager->GetSharedConfig().BrushSize = NewValue; }) + + .MinValue(UIMin) + .MaxValue(UIMax) + .SliderExponent(SliderExponent) + .Font(FCoreStyle::GetDefaultFontStyle("Regular", 11)) + .MinDesiredWidth(40.f) + .TypeInterface(NumericInterface) + .Justification(ETextJustify::Center); + ToolBarBuilder.AddToolBarWidget( SizeControl, VOXEL_LOCTEXT("Brush Size") ); + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEditorToolsPanel::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(ToolManager); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEditorToolsPanel::MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY) +{ +} + +void FVoxelEditorToolsPanel::Tick(FEditorViewportClient* ViewportClient, float DeltaTime) +{ + auto* World = ViewportClient->GetWorld(); + if (!ensure(World)) return; + + if (!LastWorld.IsValid()) + { + // Toggle voxel worlds if none are created on first tick + const auto ToggleVoxelWorld = [&]() + { + for (auto* VoxelWorld : TActorRange(World)) + { + if (VoxelWorld->IsCreated()) + { + return; + } + } + for (auto* VoxelWorld : TActorRange(World)) + { + if (!VoxelWorld->IsCreated()) + { + VoxelWorld->Toggle(); + } + } + }; + ToggleVoxelWorld(); + } + LastWorld = ViewportClient->GetWorld(); + + if (ToolManager) + { + auto* Tool = ToolManager->GetActiveTool(); + if (Tool) + { + check(!ViewportClientForDeproject); + TGuardValue Guard(ViewportClientForDeproject, ViewportClient); + + FViewport* Viewport = ViewportClient->Viewport; + TUniquePtr ViewFamily; + FSceneView* SceneView = GetSceneView(ViewFamily); + + if (Viewport && SceneView) + { + TMap Keys; + Keys.Add(FVoxelToolKeys::AlternativeMode, + ViewportClient->Viewport->KeyState(EKeys::LeftShift) || + ViewportClient->Viewport->KeyState(EKeys::RightShift)); + + const bool bClick = ViewportClient->Viewport->KeyState(EKeys::LeftMouseButton); + + FVoxelToolTickData TickData; + { + auto* SceneViewport = static_cast(Viewport); + + const auto& Geometry = SceneViewport->GetCachedGeometry(); + + FVector2D MousePosition = FSlateApplication::Get().GetCursorPos(); + if (Geometry.IsUnderLocation(MousePosition) || bClick) // Don't modify the position if editing + { + MousePosition = Geometry.AbsoluteToLocal(MousePosition); + } + else + { + MousePosition = Geometry.Size / 2; + } + + // Make sure to use the window scale factor and not the scale under the cursor, + // as the window DPI scale is uniform + MousePosition *= ViewportClient->GetDPIScale(); + + TickData.MousePosition = FVector2D(MousePosition); + TickData.CameraViewDirection = SceneView->ViewMatrices.GetInvViewMatrix().TransformVector(FVector(0, 0, 1)); + TickData.bEdit = bClick; + TickData.Keys = Keys; + + TickData.Axes.Add(FVoxelToolAxes::BrushSize, BrushSizeDelta); + TickData.Axes.Add(FVoxelToolAxes::Falloff, FalloffDelta); + TickData.Axes.Add(FVoxelToolAxes::Strength, StrengthDelta); + + BrushSizeDelta = 0; + FalloffDelta = 0; + StrengthDelta = 0; + + const auto DeprojectLambda = [this, WeakPtr = MakeWeakPtr(this)](const FVector2D& InScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection) + { + return WeakPtr.IsValid() && Deproject(InScreenPosition, OutWorldPosition, OutWorldDirection); + }; + TickData.Init(DeprojectLambda); + } + Tool->AdvancedTick(World, TickData); + + if (LastVoxelWorld != Tool->GetVoxelWorld()) + { + LastVoxelWorld = Tool->GetVoxelWorld(); + SharedConfigDetailsPanel->ForceRefresh(); + ToolDetailsPanel->ForceRefresh(); + } + + ViewportClientForDeproject = nullptr; + } + } + } + + TimeUntilNextGC -= DeltaTime; + if (TimeUntilNextGC <= 0) + { + TimeUntilNextGC = 30; + LOG_VOXEL(Log, TEXT("Forcing GC")); + GEngine->ForceGarbageCollection(true); + } +} + +void FVoxelEditorToolsPanel::HandleClick(FEditorViewportClient* ViewportClient, HHitProxy* HitProxy, const FViewportClick& Click) +{ +} + +bool FVoxelEditorToolsPanel::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) +{ + if (Event != IE_Released && CommandList->ProcessCommandBindings(Key, FSlateApplication::Get().GetModifierKeys(), false/*Event == IE_Repeat*/)) + { + return true; + } + + if (Key == EKeys::LeftMouseButton) + { + return true; + } + else if (Key == EKeys::LeftShift || Key == EKeys::RightShift) + { + return true; + } + else + { + return false; + } +} + +bool FVoxelEditorToolsPanel::InputAxis(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, float Delta, float DeltaTime) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +EVisibility FVoxelEditorToolsPanel::GetAddVoxelWorldVisibility() const +{ + if (!LastWorld.IsValid()) + { + return EVisibility::Collapsed; + } + + for (auto* VoxelWorld : TActorRange(LastWorld.Get())) + { + return EVisibility::Collapsed; + } + + return EVisibility::Visible; +} + +FReply FVoxelEditorToolsPanel::AddVoxelWorld() const +{ + if (!LastWorld.IsValid()) + { + return FReply::Handled(); + } + + auto* Factory = NewObject(); + if (ensure(Factory)) + { + Factory->CreateActor(nullptr, LastWorld->GetLevel(0), FTransform::Identity); + } + + return FReply::Handled(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEditorToolsPanel::RefreshDetails() const +{ + SharedConfigDetailsPanel->ForceRefresh(); + ToolDetailsPanel->ForceRefresh(); +} + +bool FVoxelEditorToolsPanel::IsPropertyVisible(const FProperty& Property, const TArray& ParentProperties, int32 ParentPropertyIndex) const +{ + if (Property.HasMetaData(STATIC_FNAME("HideInPanel"))) + { + return false; + } + + if (Property.HasMetaData(STATIC_FNAME("PaintMaterial"))) + { + auto* ActiveTool = ToolManager->GetActiveTool(); + if (ActiveTool && !ActiveTool->bShowPaintMaterial) + { + return false; + } + } + + if (Property.HasMetaData(STATIC_FNAME("ShowForMaterialConfigs"))) + { + const FString& ShowForMaterialConfigs = Property.GetMetaData(STATIC_FNAME("ShowForMaterialConfigs")); + if (LastVoxelWorld.IsValid()) + { + const FString MaterialConfig = StaticEnum()->GetNameStringByValue(int64(LastVoxelWorld->MaterialConfig)); + if (!ShowForMaterialConfigs.Contains(MaterialConfig)) + { + return false; + } + } + } + + // TODO + + if (ParentProperties.IsValidIndex(ParentPropertyIndex)) + { + return IsPropertyVisible(*ParentProperties[ParentPropertyIndex], ParentProperties, ParentPropertyIndex + 1); + } + else + { + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelEditorToolsPanel::SetActiveTool(UClass* ToolClass) +{ + ToolManager->SetActiveToolByClass(ToolClass); + ToolDetailsPanel->SetObject(ToolManager->GetActiveTool()); + SharedConfigDetailsPanel->ForceRefresh(); + + if (ComboBox) + { + ComboBox->SetSelectedItem(*ToolsOptions.FindByPredicate([&](auto& ClassPtr) { return *ClassPtr == ToolClass; })); + } + + GConfig->SetString(TEXT("VoxelEditorToolsPanel"), TEXT("ActiveTool"), ToolClass ? *ToolClass->GetName() : TEXT(""), GEditorPerProjectIni); +} + +bool FVoxelEditorToolsPanel::IsToolActive(UClass* ToolClass) const +{ + auto* Tool = ToolManager->GetActiveTool(); + return Tool && Tool->GetClass() == ToolClass; +} + +void FVoxelEditorToolsPanel::BuildToolBars(TArray& OutToolBars, TArray& OutCustomToolBars) +{ + const auto& Commands = FVoxelToolsCommands::Get(); + + int32 NumTools = 0; + const auto GetToolBar = [&]() + { + if (NumTools % 4 == 0) + { + OutToolBars.Emplace(CommandList, FMultiBoxCustomization("VoxelEditorTools")); + } + NumTools++; + return OutToolBars.Last(); + }; + + const auto GetUIAction = [&](auto* Class) + { + return FUIAction( + FExecuteAction::CreateSP(this, &FVoxelEditorToolsPanel::SetActiveTool, Class), + FCanExecuteAction::CreateLambda([]() { return true; }), + FIsActionChecked::CreateSP(this, &FVoxelEditorToolsPanel::IsToolActive, Class)); + }; + + const auto AddTool = [&](auto& Command, auto* Class) + { + CommandList->MapAction(Command, GetUIAction(Class)); + }; + + AddTool(Commands.FlattenTool, UVoxelFlattenTool::StaticClass()); + AddTool(Commands.LevelTool, UVoxelLevelTool::StaticClass()); + AddTool(Commands.MeshTool, UVoxelMeshTool::StaticClass()); + AddTool(Commands.SmoothTool, UVoxelSmoothTool::StaticClass()); + AddTool(Commands.SphereTool, UVoxelSphereTool::StaticClass()); + AddTool(Commands.SurfaceTool, UVoxelSurfaceTool::StaticClass()); + AddTool(Commands.RevertTool, UVoxelRevertTool::StaticClass()); + AddTool(Commands.TrimTool, UVoxelTrimTool::StaticClass()); + + GetToolBar().AddToolBarButton(Commands.SurfaceTool); + GetToolBar().AddToolBarButton(Commands.SmoothTool); + GetToolBar().AddToolBarButton(Commands.MeshTool); + GetToolBar().AddToolBarButton(Commands.SphereTool); + + GetToolBar().AddToolBarButton(Commands.FlattenTool); + GetToolBar().AddToolBarButton(Commands.LevelTool); + GetToolBar().AddToolBarButton(Commands.TrimTool); + GetToolBar().AddToolBarButton(Commands.RevertTool); + + int32 NumCustomTools = 0; + const auto GetCustomToolBar = [&]() + { + if (NumCustomTools % 4 == 0) + { + OutCustomToolBars.Emplace(CommandList, FMultiBoxCustomization("VoxelEditorTools")); + } + NumCustomTools++; + return OutCustomToolBars.Last(); + }; + + for (auto* Tool : ToolManager->GetTools()) + { + auto* Class = Tool->GetClass(); + if (Class->GetPathName().StartsWith(TEXT("/Script/Voxel."))) + { + // Builtin tools + continue; + } + + GetCustomToolBar().AddToolBarButton( + GetUIAction(Class), + NAME_None, + FText::FromName(Tool->GetToolName()), + Tool->ToolTip, + FSlateIcon("VoxelStyle", "VoxelTools.Surface"), + EUserInterfaceActionType::ToggleButton); + } + +} + +void FVoxelEditorToolsPanel::BindCommands() +{ + const auto& Commands = FVoxelToolsCommands::Get(); + + CommandList->MapAction(Commands.IncreaseBrushSize, MakeWeakPtrDelegate(this, [&](){ BrushSizeDelta++; })); + CommandList->MapAction(Commands.DecreaseBrushSize, MakeWeakPtrDelegate(this, [&](){ BrushSizeDelta--; })); + + CommandList->MapAction(Commands.IncreaseBrushFalloff, MakeWeakPtrDelegate(this, [&](){ FalloffDelta++; })); + CommandList->MapAction(Commands.DecreaseBrushFalloff, MakeWeakPtrDelegate(this, [&](){ FalloffDelta--; })); + + CommandList->MapAction(Commands.IncreaseBrushStrength, MakeWeakPtrDelegate(this, [&](){ StrengthDelta++; })); + CommandList->MapAction(Commands.DecreaseBrushStrength, MakeWeakPtrDelegate(this, [&](){ StrengthDelta--; })); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FSceneView* FVoxelEditorToolsPanel::GetSceneView(TUniquePtr& ViewFamily) const +{ + if (!ensure(ViewportClientForDeproject)) + { + return nullptr; + } + + FViewport* Viewport = ViewportClientForDeproject->Viewport; + + // Make sure we have a valid viewport, otherwise we won't be able to construct an FSceneView + if (!Viewport || Viewport->GetSizeXY().GetMin() <= 0) + { + return nullptr; + } + + ViewFamily = MakeUnique(FSceneViewFamily::ConstructionValues( + Viewport, + ViewportClientForDeproject->GetScene(), + ViewportClientForDeproject->EngineShowFlags) + .SetRealtimeUpdate(ViewportClientForDeproject->IsRealtime())); + + FSceneView* SceneView = ViewportClientForDeproject->CalcSceneView(ViewFamily.Get()); + ensure(SceneView); + return SceneView; +} + +bool FVoxelEditorToolsPanel::Deproject(const FVector2D& ScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection) const +{ + TUniquePtr ViewFamily; + auto* SceneView = GetSceneView(ViewFamily); + if (!SceneView) + { + return false; + } + + const FViewportCursorLocation MouseViewportRay(SceneView, ViewportClientForDeproject, FMath::RoundToInt(ScreenPosition.X), FMath::RoundToInt(ScreenPosition.Y)); + + OutWorldPosition = MouseViewportRay.GetOrigin(); + OutWorldDirection = MouseViewportRay.GetDirection(); + + // If we're dealing with an orthographic view, push the origin of the ray backward along the viewport forward axis + // to make sure that we can select objects that are behind the origin! + if (!ViewportClientForDeproject->IsPerspective()) + { + OutWorldPosition -= OutWorldDirection * WORLD_MAX / 2; + } + + return true; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h new file mode 100644 index 00000000..3f4724e8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h @@ -0,0 +1,104 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "UObject/GCObject.h" +#include "Engine/EngineBaseTypes.h" +#include "Widgets/Input/SComboBox.h" + +class AVoxelWorld; +class UVoxelToolManager; +class HHitProxy; +class SWidget; +class IDetailsView; +class FReply; +class FViewport; +class FSceneView; +class FUICommandList; +class FToolBarBuilder; +class FEditorViewportClient; +class FSceneViewFamilyContext; + +struct FTransactionContext; +struct FViewportClick; +struct FKey; +struct EVisibility; + +class FVoxelEditorToolsPanel : public TSharedFromThis, public FGCObject +{ +public: + FVoxelEditorToolsPanel(); + ~FVoxelEditorToolsPanel(); + + void Init(const TSharedPtr& CommandListOverride = nullptr); + void CustomizeToolbar(FToolBarBuilder& ToolBarBuilder); + +public: + //~ Begin FGCObject Interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + //~ End FGCObject Interface + +public: + const TSharedRef& GetWidget() const + { + return Widget; + } + +public: + void MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 MouseX, int32 MouseY); + void Tick(FEditorViewportClient* ViewportClient, float DeltaTime); + void HandleClick(FEditorViewportClient* ViewportClient, HHitProxy* HitProxy, const FViewportClick& Click); + bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event); + bool InputAxis(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, float Delta, float DeltaTime); + +public: + EVisibility GetAddVoxelWorldVisibility() const; + FReply AddVoxelWorld() const; + void ClearTool() + { + SetActiveTool(nullptr); + } + +private: + UVoxelToolManager* ToolManager = nullptr; + + TSharedPtr SharedConfigDetailsPanel; + TSharedPtr ToolDetailsPanel; + + TSharedPtr>> ComboBox; + TSharedRef Widget; + + TWeakObjectPtr LastWorld; + TWeakObjectPtr LastVoxelWorld; + + TArray> ToolsOptions; + TSharedPtr CommandList; + + float TimeUntilNextGC = 0; + + float BrushSizeDelta = 0.f; + float FalloffDelta = 0.f; + float StrengthDelta = 0.f; + + bool bShowCustomTools = false; + TSharedPtr ExpanderButton; + +private: + void RefreshDetails() const; + bool IsPropertyVisible(const FProperty& Property, const TArray& ParentProperties, int32 ParentPropertyIndex = 0) const; + +private: + void SetActiveTool(UClass* ToolClass); + bool IsToolActive(UClass* ToolClass) const; + void BuildToolBars(TArray& OutToolBars, TArray& OutCustomToolBars); + void BindCommands(); + +private: + // Only valid in Tick + FEditorViewportClient* ViewportClientForDeproject = nullptr; + + FSceneView* GetSceneView(TUniquePtr& ViewFamily) const; + bool Deproject(const FVector2D& ScreenPosition, FVector& OutWorldPosition, FVector& OutWorldDirection) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp new file mode 100644 index 00000000..409986e9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp @@ -0,0 +1,203 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMessagesEditor.h" +#include "VoxelSettings.h" +#include "VoxelMinimal.h" + +#include "Logging/MessageLog.h" +#include "Misc/CoreMisc.h" +#include "Misc/UObjectToken.h" +#include "UObject/Stack.h" +#include "Engine/Blueprint.h" +#include "Engine/BlueprintGeneratedClass.h" +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphNode.h" +#include "Kismet2/KismetDebugUtilities.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Editor.h" + +inline void AddButton( + FNotificationInfo& Info, + const FSimpleDelegate& OnClick, + const FText& Text, + const FText& Tooltip, + bool bCloseOnClick, + const TSharedRef>& PtrToPtr) +{ + const auto Callback = FSimpleDelegate::CreateLambda([=]() + { + OnClick.ExecuteIfBound(); + + if (bCloseOnClick) + { + auto Ptr = PtrToPtr->Pin(); + if (Ptr.IsValid()) + { + Ptr->SetFadeOutDuration(0); + Ptr->Fadeout(); + } + } + }); + + Info.ButtonDetails.Add(FNotificationButtonInfo( + Text, + Tooltip, + Callback, + SNotificationItem::CS_None)); +} + +void FVoxelMessagesEditor::LogMessage(const TSharedRef& Message, EVoxelShowNotification ShouldShow) +{ + struct Local + { + static void OnMessageLogLinkActivated(const class TSharedRef& Token) + { + if (Token->GetType() == EMessageToken::Object) + { + const TSharedRef UObjectToken = StaticCastSharedRef(Token); + if (UObjectToken->GetObject().IsValid()) + { + FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(UObjectToken->GetObject().Get()); + } + } + } + }; + +#if ENGINE_MINOR_VERSION < 26 + FBlueprintExceptionTracker& BlueprintExceptionTracker = FBlueprintExceptionTracker::Get(); + auto& ScriptStack =BlueprintExceptionTracker.ScriptStack; +#else + const TArray& ScriptStack = FBlueprintContextTracker::Get().GetScriptStack(); +#endif + + TArray> ReversedTokens; + if (ScriptStack.Num() > 0) + { + const FFrame& StackFrame = *ScriptStack.Last(); + UClass* ClassContainingCode = FKismetDebugUtilities::FindClassForNode(nullptr, StackFrame.Node); + UBlueprint* BlueprintObj = (ClassContainingCode ? Cast(ClassContainingCode->ClassGeneratedBy) : NULL); + if (BlueprintObj) + { + const int32 BreakpointOffset = StackFrame.Code - StackFrame.Node->Script.GetData() - 1; + + ReversedTokens.Add(FUObjectToken::Create(BlueprintObj, FText::FromString(BlueprintObj->GetName())) + ->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&Local::OnMessageLogLinkActivated)) + ); + ReversedTokens.Add(FTextToken::Create(VOXEL_LOCTEXT("Blueprint: "))); + + // NOTE: StackFrame.Node is not a blueprint node like you may think ("Node" has some legacy meaning) + ReversedTokens.Add(FUObjectToken::Create(StackFrame.Node, StackFrame.Node->GetDisplayNameText()) + ->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&Local::OnMessageLogLinkActivated)) + ); + ReversedTokens.Add(FTextToken::Create(VOXEL_LOCTEXT("Function: "))); + +#if WITH_EDITORONLY_DATA // to protect access to GeneratedClass->DebugData + UBlueprintGeneratedClass* GeneratedClass = Cast(ClassContainingCode); + if ((GeneratedClass != NULL) && GeneratedClass->DebugData.IsValid()) + { + UEdGraphNode* BlueprintNode = GeneratedClass->DebugData.FindSourceNodeFromCodeLocation(StackFrame.Node, BreakpointOffset, true); + // if instead, there is a node we can point to... + if (BlueprintNode != NULL) + { + ReversedTokens.Add(FUObjectToken::Create(BlueprintNode->GetGraph(), FText::FromString(GetNameSafe(BlueprintNode->GetGraph()))) + ->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&Local::OnMessageLogLinkActivated)) + ); + ReversedTokens.Add(FTextToken::Create(VOXEL_LOCTEXT("Graph: "))); + + ReversedTokens.Add(FUObjectToken::Create(BlueprintNode, BlueprintNode->GetNodeTitle(ENodeTitleType::ListView)) + ->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&Local::OnMessageLogLinkActivated)) + ); + ReversedTokens.Add(FTextToken::Create(VOXEL_LOCTEXT("Node: "))); + } + } +#endif // WITH_EDITORONLY_DATA + } + } + for (int32 Index = ReversedTokens.Num() - 1; Index >= 0; --Index) + { + Message->AddToken(ReversedTokens[Index].ToSharedRef()); + } + + if (GEditor->PlayWorld || GIsPlayInEditorWorld) + { + FMessageLog("PIE").AddMessage(Message); + } + else + { + FMessageLog("Voxel").AddMessage(Message); + if (GetDefault()->bShowNotifications && ShouldShow == EVoxelShowNotification::Show) + { + struct FLastNotification + { + TWeakPtr Ptr; + FText Text; + int32 Count; + }; + static TArray LastNotifications; + + const FText Text = Message->ToText(); + LastNotifications.RemoveAll([](auto& Notification) { return !Notification.Ptr.IsValid(); }); + + for (auto& LastNotification : LastNotifications) + { + auto LastNotificationPtr = LastNotification.Ptr.Pin(); + if (ensure(LastNotificationPtr.IsValid()) && LastNotification.Text.EqualToCaseIgnored(Text)) + { + LastNotification.Text = Text; + LastNotification.Count++; + + LastNotificationPtr->SetText(FText::Format(VOXEL_LOCTEXT("{0} (x{1})"), Text, FText::AsNumber(LastNotification.Count))); + LastNotificationPtr->ExpireAndFadeout(); + + return; + } + } + + FNotificationInfo Info = FNotificationInfo(Text); + Info.CheckBoxState = ECheckBoxState::Unchecked; + Info.ExpireDuration = 10; + + const TSharedRef> PtrToPtr = MakeShared>(); + AddButton(Info, {}, VOXEL_LOCTEXT("Close"), VOXEL_LOCTEXT("Close"), true, PtrToPtr); + const auto Ptr = FSlateNotificationManager::Get().AddNotification(Info); + *PtrToPtr = Ptr; + + LastNotifications.Emplace(FLastNotification{ Ptr, Text, 1 }); + } + } +} + +void FVoxelMessagesEditor::ShowNotification(const FVoxelMessages::FNotification& Notification) +{ + struct FLastNotification + { + TWeakPtr Ptr; + uint64 UniqueId; + }; + static TArray LastNotifications; + + LastNotifications.RemoveAll([](auto& It) { return !It.Ptr.IsValid(); }); + if (LastNotifications.FindByPredicate([&](auto& It) { return It.UniqueId == Notification.UniqueId; })) + { + return; + } + + FNotificationInfo Info(FText::FromString(Notification.Message)); + Info.CheckBoxState = ECheckBoxState::Unchecked; + Info.ExpireDuration = Notification.Duration; + + const TSharedRef> PtrToPtr = MakeShared>(); + + for (auto& Button : Notification.Buttons) + { + AddButton(Info, Button.OnClick, FText::FromString(Button.Text), FText::FromString(Button.Tooltip), Button.bCloseOnClick, PtrToPtr); + } + AddButton(Info, Notification.OnClose, VOXEL_LOCTEXT("Close"), VOXEL_LOCTEXT("Close"), true, PtrToPtr); + + const auto Ptr = FSlateNotificationManager::Get().AddNotification(Info); + *PtrToPtr = Ptr; + + LastNotifications.Add({ Ptr, Notification.UniqueId }); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.h new file mode 100644 index 00000000..930bc047 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelMessagesEditor.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMessages.h" + +namespace FVoxelMessagesEditor +{ + void LogMessage(const TSharedRef& Message, EVoxelShowNotification ShouldShow); + void ShowNotification(const FVoxelMessages::FNotification& Notification); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp new file mode 100644 index 00000000..a3cf6982 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp @@ -0,0 +1,107 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelOpenAssetsOnStartup.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelConfigUtilities.h" + +#include "Editor.h" +#include "Engine/World.h" +#include "GameMapsSettings.h" +#include "Containers/Ticker.h" +#include "ContentBrowserModule.h" +#include "Toolkits/AssetEditorManager.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + +void UVoxelOpenAssetsOnStartup::Init() +{ + FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([=](float) + { + GetMutableDefault()->ActualInit(); + return false; + })); +} + +void UVoxelOpenAssetsOnStartup::ActualInit() +{ + FVoxelConfigUtilities::LoadConfig(this, "OpenAssetsOnStartup"); + + if (bEnableOpenAssetsOnStartup) + { + for (auto& It : AssetsToOpenOnStartup) + { + if (!It.Value) + { + continue; + } + +#if ENGINE_MINOR_VERSION < 24 + FAssetEditorManager::Get().OpenEditorForAsset(It.Key.ToString()); +#else + GEditor->GetEditorSubsystem()->OpenEditorForAsset(It.Key.ToString()); +#endif + } + } + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")); + ContentBrowserModule.GetAllAssetViewContextMenuExtenders().Add(FContentBrowserMenuExtender_SelectedAssets::CreateLambda([=](const TArray& SelectedAssets) + { + const auto Extender = MakeShared(); + + if (bEnableOpenAssetsOnStartup && SelectedAssets.Num() == 1) + { + const auto Asset = SelectedAssets[0]; + const FString Path = Asset.PackagePath.ToString() / Asset.AssetName.ToString(); + + Extender->AddMenuExtension( + "CommonAssetActions", + EExtensionHook::After, + nullptr, + FMenuExtensionDelegate::CreateLambda([=](FMenuBuilder& MenuBuilder) + { + MenuBuilder.AddMenuEntry( + TAttribute::Create(TAttribute::FGetter::CreateLambda([=]() + { + return AssetsToOpenOnStartup.FindRef(*Path) ? VOXEL_LOCTEXT("Stop opening on startup") : VOXEL_LOCTEXT("Open on startup"); + })), + TAttribute(), + FSlateIcon(NAME_None, NAME_None), + FUIAction(FExecuteAction::CreateLambda([=]() + { + bool& bValue = AssetsToOpenOnStartup.FindOrAdd(*Path); + bValue = !bValue; + FVoxelConfigUtilities::SaveConfig(this, "OpenAssetsOnStartup"); + }))); + })); + } + + if (bShowSetAsStartupMap && + SelectedAssets.Num() == 1 && + SelectedAssets[0].GetClass() == UWorld::StaticClass() && + GetDefault()->EditorStartupMap != SelectedAssets[0].ToSoftObjectPath()) + { + const auto Asset = SelectedAssets[0]; + + Extender->AddMenuExtension( + "CommonAssetActions", + EExtensionHook::After, + nullptr, + FMenuExtensionDelegate::CreateLambda([=](FMenuBuilder& MenuBuilder) + { + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Set as editor startup map"), + TAttribute(), + FSlateIcon(NAME_None, NAME_None), + FUIAction(FExecuteAction::CreateLambda([=]() + { + auto* Settings = GetMutableDefault(); + Settings->EditorStartupMap = SelectedAssets[0].ToSoftObjectPath(); + + auto* Property = UE_25_SWITCH(FindField, FindFProperty)(UGameMapsSettings::StaticClass(), GET_MEMBER_NAME_CHECKED(UGameMapsSettings, EditorStartupMap)); + Settings->UpdateSinglePropertyInConfigFile(Property, Settings->GetDefaultConfigFilename()); + }))); + })); + } + + return Extender; + })); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.h new file mode 100644 index 00000000..f2ddf151 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.h @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DeveloperSettings.h" +#include "VoxelOpenAssetsOnStartup.generated.h" + +class FUICommandList; + +UCLASS(config=EditorPerProjectUserSettings, defaultconfig, meta=(DisplayName="Open Assets On Startup")) +class UVoxelOpenAssetsOnStartup : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + static void Init(); + +private: + void ActualInit(); + +public: + // If true, will add a Open Asset On Startup option to all assets context menus in the content browser + // Assets marked as such will automatically open on engine startup + // Useful to iterate quickly when restarting the editor + UPROPERTY(Config, EditAnywhere, Category="Config") + bool bEnableOpenAssetsOnStartup = false; + + // If true, will show a context menu option to change the project editor startup map in the context menu + UPROPERTY(Config, EditAnywhere, Category="Config") + bool bShowSetAsStartupMap = false; + + UPROPERTY() + TMap AssetsToOpenOnStartup; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp new file mode 100644 index 00000000..1c7ca468 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp @@ -0,0 +1,121 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelScopedTransaction.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelSpawners/VoxelSpawnerManager.h" +#include "VoxelWorld.h" +#include "Editor.h" + +FVoxelChangeBase::FVoxelChangeBase(FName Name) +{ +} + +FString FVoxelChangeBase::ToString() const +{ + return FString::Printf(TEXT("Voxel: %s"), *Name.ToString()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelEditChange::FVoxelEditChange(const TVoxelWeakPtr& DataWeakPtr, FName Name, bool bIsUndo) + : FVoxelChangeBase(Name) + , DataWeakPtr(DataWeakPtr) + , bIsUndo(bIsUndo) +{ +} + +TUniquePtr FVoxelEditChange::Execute(UObject* Object) +{ + auto* VoxelWorld = Cast(Object); + + // Check that the world wasn't recreated since + if (ensure(VoxelWorld) && VoxelWorld->GetDataSharedPtr() == DataWeakPtr.Pin()) + { + TArray UpdatedBounds; + if (bIsUndo) + { + ensure(UVoxelBlueprintLibrary::Undo(VoxelWorld, UpdatedBounds)); + } + else + { + ensure(UVoxelBlueprintLibrary::Redo(VoxelWorld, UpdatedBounds)); + } + if (UpdatedBounds.Num() > 0) + { + UVoxelBlueprintLibrary::RegenerateSpawners(VoxelWorld, FVoxelIntBox(UpdatedBounds)); + } + } + + return MakeUnique(DataWeakPtr, Name, !bIsUndo); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelDataSwapChange::FVoxelDataSwapChange(const TVoxelSharedRef& Data, FName Name) + : FVoxelChangeBase(Name) + , Data(Data) +{ +} + +TUniquePtr FVoxelDataSwapChange::Execute(UObject* Object) +{ + auto* VoxelWorld = Cast(Object); + if (!ensure(VoxelWorld)) + { + return nullptr; + } + + const auto NewData = VoxelWorld->GetDataSharedPtr(); + + VoxelWorld->DestroyWorld(); + + FVoxelWorldCreateInfo Info; + Info.bOverrideData = true; + Info.DataOverride_Raw = Data; + VoxelWorld->CreateWorld(Info); + + if (!ensure(NewData.IsValid())) + { + return nullptr; + } + else + { + return MakeUnique(NewData.ToSharedRef(), Name); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelScopedTransaction::FVoxelScopedTransaction(AVoxelWorld* World, FName Name, EVoxelChangeType ChangeType) + : bValid(ensure(World) && ensure(World->IsCreated())) +{ + if (bValid) + { + GEditor->BeginTransaction(TEXT("VoxelEditorTools"), FText::FromName(Name), nullptr); + if (!ensure(GUndo)) return; + + if (ChangeType == EVoxelChangeType::Edit) + { + GUndo->StoreUndo(World, MakeUnique(World->GetDataSharedPtr(), Name, true)); + } + else + { + check(ChangeType == EVoxelChangeType::DataSwap); + GUndo->StoreUndo(World, MakeUnique(World->GetDataSharedPtr().ToSharedRef(), Name)); + } + } +} + +FVoxelScopedTransaction::~FVoxelScopedTransaction() +{ + if (bValid) + { + GEditor->EndTransaction(); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.cpp new file mode 100644 index 00000000..495f7011 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.cpp @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelToolsCommands.h" +#include "EditorStyleSet.h" + +#define LOCTEXT_NAMESPACE "VoxelToolsCommands" + +FVoxelToolsCommands::FVoxelToolsCommands() + : TCommands( + "VoxelTools", + NSLOCTEXT("Contexts", "VoxelTools", "Voxel Tools"), + NAME_None, + "VoxelStyle") +{ +} + +void FVoxelToolsCommands::RegisterCommands() +{ + UI_COMMAND(FlattenTool, "Flatten", "Progressively flatten an area", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Five)); + UI_COMMAND(LevelTool, "Level", "Quickly block out flat surfaces. Stamps a cylinder.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Six)); + UI_COMMAND(MeshTool, "Mesh", "Smoothly or progressively stamps any mesh", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Three)); + UI_COMMAND(RevertTool, "Revert", "Reverts edits to the generator (shift) or a specific history position", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Eight)); + UI_COMMAND(SmoothTool, "Smooth", "Very useful with voxels. Shift makes it slower.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Two)); + UI_COMMAND(SphereTool, "Sphere", "Add/remove spheres", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Four)); + UI_COMMAND(SurfaceTool, "Surface", "Main sculpt tool to smoothly edit the voxels, optionally with masks", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::One)); + UI_COMMAND(TrimTool, "Trim", "Quickly trim/flatten an area. Stamps/remove a half sphere based on the average position/normal under the cursor.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Seven)); + + UI_COMMAND(IncreaseBrushSize, "Increase Brush Size", "Press this key to increase brush size.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::RightBracket)); + UI_COMMAND(DecreaseBrushSize, "Decrease Brush Size", "Press this key to decrease brush size.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::LeftBracket)); + UI_COMMAND(IncreaseBrushFalloff, "Increase Brush Falloff", "Press this key to increase brush falloff.", EUserInterfaceActionType::RadioButton, FInputChord()); + UI_COMMAND(DecreaseBrushFalloff, "Decrease Brush Falloff", "Press this key to decrease brush falloff.", EUserInterfaceActionType::RadioButton, FInputChord()); + UI_COMMAND(IncreaseBrushStrength, "Increase Brush Strength", "Press this key to increase brush strength.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Period)); + UI_COMMAND(DecreaseBrushStrength, "Decrease Brush Strength", "Press this key to decrease brush strength.", EUserInterfaceActionType::RadioButton, FInputChord(EKeys::Comma)); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.h new file mode 100644 index 00000000..54a0e82b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelToolsCommands.h @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" + +/** + * Unreal landscape editor actions + */ +class FVoxelToolsCommands : public TCommands +{ + +public: + FVoxelToolsCommands(); + + virtual void RegisterCommands() override; + + TSharedPtr FlattenTool; + TSharedPtr LevelTool; + TSharedPtr MeshTool; + TSharedPtr RevertTool; + TSharedPtr SmoothTool; + TSharedPtr SphereTool; + TSharedPtr SurfaceTool; + TSharedPtr TrimTool; + + TSharedPtr IncreaseBrushSize; + TSharedPtr DecreaseBrushSize; + TSharedPtr IncreaseBrushFalloff; + TSharedPtr DecreaseBrushFalloff; + TSharedPtr IncreaseBrushStrength; + TSharedPtr DecreaseBrushStrength; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.cpp new file mode 100644 index 00000000..c7b9ab4d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.cpp @@ -0,0 +1,76 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelWorldEditorControls.h" + +#include "LevelEditorViewport.h" +#include "Editor.h" + +AVoxelWorldEditorControls::AVoxelWorldEditorControls() +{ + PrimaryActorTick.bCanEverTick = true; + + RootComponent = Invoker = CreateDefaultSubobject(FName("Editor Invoker")); +} + +void AVoxelWorldEditorControls::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + if (GetWorld()->WorldType == EWorldType::Editor || + GetWorld()->WorldType == EWorldType::EditorPreview) + { + if (bOverrideLocation) + { + SetActorLocation(LocationOverride); + } + else + { + FViewport* Viewport = GEditor->GetActiveViewport(); + if (Viewport) + { + FViewportClient* Client = Viewport->GetClient(); + if (Client) + { + for (FEditorViewportClient* EditorViewportClient : GEditor->GetAllViewportClients()) + { + if (EditorViewportClient == Client) + { + const FVector CameraPosition = EditorViewportClient->GetViewLocation(); + SetActorLocation(CameraPosition); + break; + } + } + } + } + } + } + else + { + Destroy(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelInvokerEditorComponent::UVoxelInvokerEditorComponent() +{ + bUseForLOD = true; + LODToSet = 0; + LODRange = 10000; + + bUseForCollisions = false; + bUseForNavmesh = false; +} + +void UVoxelInvokerEditorComponent::OnRegister() +{ + Super::OnRegister(); + + if (GetWorld()->WorldType != EWorldType::Editor && + GetWorld()->WorldType != EWorldType::EditorPreview) + { + DisableInvoker(); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.h new file mode 100644 index 00000000..ea21733f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Private/VoxelWorldEditorControls.h @@ -0,0 +1,49 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "VoxelComponents/VoxelInvokerComponent.h" +#include "VoxelWorldEditorControls.generated.h" + +class AVoxelWorld; +class UVoxelInvokerEditorComponent; + +UCLASS(NotPlaceable) +class VOXELEDITOR_API AVoxelWorldEditorControls : public AActor +{ + GENERATED_BODY() + +public: + AVoxelWorldEditorControls(); + + //~ Begin AActor interface + void Tick(float DeltaTime) override; +#if WITH_EDITOR + virtual bool ShouldTickIfViewportsOnly() const override final { return true; } + virtual bool IsEditorOnly() const override final { return true; } +#endif + //~ End AActor interface + + UPROPERTY(Transient) + bool bOverrideLocation = false; + UPROPERTY(Transient) + FVector LocationOverride; + + UPROPERTY(VisibleAnywhere, Category = "Voxel") + UVoxelInvokerEditorComponent* Invoker; +}; + +UCLASS(NotPlaceable) +class VOXELEDITOR_API UVoxelInvokerEditorComponent : public UVoxelSimpleInvokerComponent +{ + GENERATED_BODY() + +public: + UVoxelInvokerEditorComponent(); + + //~ Begin UActorComponent Interface + virtual void OnRegister() override; + //~ End UActorComponent Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelEditorModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelEditorModule.h new file mode 100644 index 00000000..76bbeec5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelEditorModule.h @@ -0,0 +1,19 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" +#include "Toolkits/IToolkit.h" +#include "AssetTypeCategories.h" + +class IToolkitHost; +class UVoxelDataAsset; + +class IVoxelEditorModule : public IModuleInterface +{ +public: + virtual void CreateVoxelDataAssetEditor(EToolkitMode::Type Mode, const TSharedPtr& InitToolkitHost, UVoxelDataAsset* DataAsset) = 0; + virtual void RefreshVoxelWorlds(UObject* MatchingGenerator = nullptr) = 0; + virtual EAssetTypeCategories::Type GetVoxelAssetTypeCategory() const = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelScopedTransaction.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelScopedTransaction.h new file mode 100644 index 00000000..06ca9c42 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/Public/VoxelScopedTransaction.h @@ -0,0 +1,57 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Misc/Change.h" +#include "VoxelMinimal.h" + +class FVoxelData; +class AVoxelWorld; + +enum class EVoxelChangeType +{ + Edit, + DataSwap +}; + +class VOXELEDITOR_API FVoxelChangeBase : public FSwapChange +{ +public: + const FName Name; + + explicit FVoxelChangeBase(FName Name); + + virtual FString ToString() const override; +}; + +class VOXELEDITOR_API FVoxelEditChange : public FVoxelChangeBase +{ +public: + const TVoxelWeakPtr DataWeakPtr; + const bool bIsUndo; + + FVoxelEditChange(const TVoxelWeakPtr& DataWeakPtr, FName Name, bool bIsUndo); + + virtual TUniquePtr Execute(UObject* Object) override; +}; + +class VOXELEDITOR_API FVoxelDataSwapChange : public FVoxelChangeBase +{ +public: + const TVoxelSharedRef Data; + + FVoxelDataSwapChange(const TVoxelSharedRef& Data, FName Name); + + virtual TUniquePtr Execute(UObject* Object) override; +}; + +class VOXELEDITOR_API FVoxelScopedTransaction +{ +public: + FVoxelScopedTransaction(AVoxelWorld* World, FName Name, EVoxelChangeType ChangeType); + ~FVoxelScopedTransaction(); + +private: + const bool bValid; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/VoxelEditor.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/VoxelEditor.Build.cs new file mode 100644 index 00000000..a7f6fefe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditor/VoxelEditor.Build.cs @@ -0,0 +1,70 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelEditor : ModuleRules +{ + public VoxelEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PrivateIncludePaths.Add(Path.Combine(EngineDirectory, "Source/Editor/PropertyEditor/Private")); + + DynamicallyLoadedModuleNames.AddRange( + new string[] { + "VoxelGraphEditor", + "AssetRegistry", + }); + + PrivateDependencyModuleNames.AddRange( + new string[] { + "Core", + "CoreUObject", + "Engine", + "Voxel", + "VoxelGraph", + "VoxelEditorDefault", + "Engine", + "Landscape", + "LandscapeEditor", + "PlacementMode", + "AdvancedPreviewScene", + "DesktopPlatform", + "UnrealEd", + "InputCore", + "ImageWrapper", + "Slate", + "SlateCore", + "PropertyEditor", + "EditorStyle", + "Projects", + "RHI", + "MessageLog", + "RawMesh", + "DetailCustomizations", + "WorkspaceMenuStructure", + "BlueprintGraph", + "KismetCompiler", + "ApplicationCore", + "EngineSettings", +#if UE_4_26_OR_LATER + "DeveloperSettings", +#endif + }); + + PrivateIncludePathModuleNames.AddRange( + new string[] { + "VoxelGraphEditor" + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelPlaceableItems.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelPlaceableItems.cpp new file mode 100644 index 00000000..6fbe059a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelPlaceableItems.cpp @@ -0,0 +1,101 @@ +// Copyright 2020 Phyronnaz + +#include "ActorFactoryVoxelPlaceableItems.h" +#include "VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h" +#include "VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h" +#include "VoxelPlaceableItems/Actors/VoxelAssetActor.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelWorld.h" +#include "EngineUtils.h" + +UActorFactoryVoxelPlaceableItem::UActorFactoryVoxelPlaceableItem() +{ + DisplayName = VOXEL_LOCTEXT("Voxel Placeable Item Actor"); + NewActorClass = AVoxelPlaceableItemActor::StaticClass(); +} + +void UActorFactoryVoxelPlaceableItem::PostSpawnActor(UObject* Asset, AActor* NewActor) +{ + Super::PostSpawnActor(Asset, NewActor); + + AVoxelPlaceableItemActor* ItemActor = CastChecked(NewActor); + + for (TActorIterator ActorItr(ItemActor->GetWorld()); ActorItr; ++ActorItr) + { + ItemActor->PreviewWorld = *ActorItr; + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +UActorFactoryVoxelDisableEditsBox::UActorFactoryVoxelDisableEditsBox() +{ + DisplayName = VOXEL_LOCTEXT("Voxel Disable Edits Box"); + NewActorClass = AVoxelDisableEditsBox::StaticClass(); +} + +/////////////////////////////////////////////////////////////////////////////// + +UActorFactoryVoxelAssetActor::UActorFactoryVoxelAssetActor() +{ + DisplayName = VOXEL_LOCTEXT("Voxel Asset Actor"); + NewActorClass = AVoxelAssetActor::StaticClass(); +} + +void UActorFactoryVoxelAssetActor::PostSpawnActor(UObject* Asset, AActor* NewActor) +{ + Super::PostSpawnActor(Asset, NewActor); + InitActor(Asset, NewActor); +} + +void UActorFactoryVoxelAssetActor::PostCreateBlueprint(UObject* Asset, AActor* CDO) +{ + Super::PostCreateBlueprint(Asset, CDO); + InitActor(Asset, CDO); +} + +bool UActorFactoryVoxelAssetActor::CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg) +{ + // We allow creating AVoxelAssetActor without an existing asset + if (UActorFactory::CanCreateActorFrom(AssetData, OutErrorMsg)) + { + return true; + } + if (!ensure(AssetData.IsValid())) + { + return false; + } + auto* Class = AssetData.GetClass(); + if(!Class) + { + return false; + } + if (Class->IsChildOf(UVoxelTransformableGenerator::StaticClass())) + { + return true; + } + return false; +} + +UObject* UActorFactoryVoxelAssetActor::GetAssetFromActorInstance(AActor* ActorInstance) +{ + check(ActorInstance->IsA(NewActorClass)); + AVoxelAssetActor* AssetActor = CastChecked(ActorInstance); + return AssetActor->Generator.GetObject(); +} + +void UActorFactoryVoxelAssetActor::InitActor(UObject* Asset, AActor* NewActor) +{ + if (!Asset || !NewActor) + { + return; + } + + AVoxelAssetActor* AssetActor = CastChecked(NewActor); + if (auto* Generator = Cast(Asset)) + { + AssetActor->Generator = Generator; + AssetActor->bOverrideAssetBounds = !Asset->IsA(); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp new file mode 100644 index 00000000..7b6ee1aa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#include "ActorFactoryVoxelWorld.h" +#include "VoxelWorld.h" +#include "VoxelUtilities/VoxelExampleUtilities.h" +#include "VoxelGenerators/VoxelFlatGenerator.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" + +#include "Materials/MaterialInterface.h" + +UActorFactoryVoxelWorld::UActorFactoryVoxelWorld() +{ + DisplayName = VOXEL_LOCTEXT("Voxel World"); + NewActorClass = AVoxelWorld::StaticClass(); +} + +void UActorFactoryVoxelWorld::PostSpawnActor(UObject* Asset, AActor* NewActor) +{ + Super::PostSpawnActor(Asset, NewActor); + + AVoxelWorld* VoxelWorld = CastChecked(NewActor); + VoxelWorld->bCreateWorldAutomatically = true; + VoxelWorld->bUseCameraIfNoInvokersFound = true; + VoxelWorld->SetGeneratorClass(UVoxelFlatGenerator::StaticClass()); + VoxelWorld->MaterialConfig = EVoxelMaterialConfig::RGB; + VoxelWorld->MaterialCollection = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Materials/Quixel/MC_Quixel")); + VoxelWorld->VoxelMaterial = FVoxelExampleUtilities::LoadExampleObject(TEXT("/Voxel/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst")); + VoxelWorld->Toggle(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/VoxelEditorDefaultModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/VoxelEditorDefaultModule.cpp new file mode 100644 index 00000000..b214dd8b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Private/VoxelEditorDefaultModule.cpp @@ -0,0 +1,7 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEditorDefaultModule.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FVoxelEditorDefaultModule, VoxelEditorDefault); + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelMeshImporter.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelMeshImporter.h new file mode 100644 index 00000000..877dd911 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelMeshImporter.h @@ -0,0 +1,22 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "ActorFactories/ActorFactory.h" +#include "VoxelImporters/VoxelMeshImporter.h" +#include "ActorFactoryVoxelMeshImporter.generated.h" + +UCLASS() +class VOXELEDITORDEFAULT_API UActorFactoryVoxelMeshImporter : public UActorFactory +{ + GENERATED_BODY() + +public: + UActorFactoryVoxelMeshImporter() + { + DisplayName = VOXEL_LOCTEXT("Voxel Mesh Importer"); + NewActorClass = AVoxelMeshImporter::StaticClass(); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelPlaceableItems.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelPlaceableItems.h new file mode 100644 index 00000000..19faf341 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelPlaceableItems.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "ActorFactories/ActorFactory.h" +#include "ActorFactoryVoxelPlaceableItems.generated.h" + +UCLASS() +class VOXELEDITORDEFAULT_API UActorFactoryVoxelPlaceableItem : public UActorFactory +{ + GENERATED_BODY() + +public: + UActorFactoryVoxelPlaceableItem(); + + //~ Begin UActorFactory Interface + void PostSpawnActor(UObject* Asset, AActor* NewActor) override; + //~ End UActorFactory Interface +}; + +UCLASS() +class VOXELEDITORDEFAULT_API UActorFactoryVoxelDisableEditsBox : public UActorFactoryVoxelPlaceableItem +{ + GENERATED_BODY() + +public: + UActorFactoryVoxelDisableEditsBox(); +}; + +UCLASS() +class VOXELEDITORDEFAULT_API UActorFactoryVoxelAssetActor : public UActorFactoryVoxelPlaceableItem +{ + GENERATED_BODY() + +public: + UActorFactoryVoxelAssetActor(); + + //~ Begin UActorFactory Interface + virtual void PostSpawnActor(UObject* Asset, AActor* NewActor) override; + virtual void PostCreateBlueprint(UObject* Asset, AActor* CDO) override; + virtual bool CanCreateActorFrom(const FAssetData& AssetData, FText& OutErrorMsg) override; + virtual UObject* GetAssetFromActorInstance(AActor* ActorInstance) override; + //~ End UActorFactory Interface + +private: + virtual void InitActor(UObject* Asset, AActor* NewActor); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelWorld.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelWorld.h new file mode 100644 index 00000000..2d905285 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/ActorFactoryVoxelWorld.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "ActorFactories/ActorFactory.h" +#include "ActorFactoryVoxelWorld.generated.h" + +UCLASS() +class VOXELEDITORDEFAULT_API UActorFactoryVoxelWorld : public UActorFactory +{ + GENERATED_BODY() + +public: + UActorFactoryVoxelWorld(); + + //~ Begin UActorFactory Interface + void PostSpawnActor(UObject* Asset, AActor* NewActor) override; + //~ End UActorFactory Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/VoxelEditorDefaultModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/VoxelEditorDefaultModule.h new file mode 100644 index 00000000..4fb95b26 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/Public/VoxelEditorDefaultModule.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelEditorDefaultModule : public IModuleInterface +{ +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/VoxelEditorDefault.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/VoxelEditorDefault.Build.cs new file mode 100644 index 00000000..b4f95ba3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelEditorDefault/VoxelEditorDefault.Build.cs @@ -0,0 +1,38 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelEditorDefault : ModuleRules +{ + public VoxelEditorDefault(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + DynamicallyLoadedModuleNames.AddRange( + new string[] { + }); + + PrivateDependencyModuleNames.AddRange( + new string[] { + "Core", + "CoreUObject", + "Engine", + "Voxel", + "UnrealEd" + }); + + PrivateIncludePathModuleNames.AddRange( + new string[] { + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelExamplesModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelExamplesModule.cpp new file mode 100644 index 00000000..091bc4ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelExamplesModule.cpp @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExamplesModule.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FVoxelExamples, VoxelExamples) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelGeneratorExample.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelGeneratorExample.cpp new file mode 100644 index 00000000..28b0ccc9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Private/VoxelGeneratorExample.cpp @@ -0,0 +1,90 @@ +// Copyright 2020 Phyronnaz + +#if 0 + +#include "VoxelGeneratorExample.h" +#include "FastNoise/VoxelFastNoise.inl" +#include "VoxelMaterialBuilder.h" + +TVoxelSharedRef UVoxelGeneratorExample::GetInstance() +{ + return MakeVoxelShared(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelGeneratorExampleInstance::FVoxelGeneratorExampleInstance(const UVoxelGeneratorExample& MyGenerator) + : Super(&MyGenerator) + , NoiseHeight(MyGenerator.NoiseHeight) + , Seed(MyGenerator.Seed) +{ +} + +void FVoxelGeneratorExampleInstance::Init(const FVoxelGeneratorInit& InitStruct) +{ + Noise.SetSeed(Seed); +} + +v_flt FVoxelGeneratorExampleInstance::GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + const float Height = Noise.GetPerlin_2D(X, Y, 0.01f) * NoiseHeight; + + // Positive value -> empty voxel + // Negative value -> full voxel + // Value positive when Z > Height, and negative Z < Height + float Value = Z - Height; + + // The voxel value is clamped between -1 and 1. That can result in a bad gradient/normal. To solve that we divide it + Value /= 5; + + return Value; +} + +FVoxelMaterial FVoxelGeneratorExampleInstance::GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const +{ + FVoxelMaterialBuilder Builder; + + // RGB + Builder.SetMaterialConfig(EVoxelMaterialConfig::RGB); + Builder.SetColor(FColor::Red); + + // Single index + //Builder.SetMaterialConfig(EVoxelMaterialConfig::SingleIndex); + //Builder.SetSingleIndex(0); + + // Multi index + //Builder.SetMaterialConfig(EVoxelMaterialConfig::MultiIndex); + //Builder.AddMultiIndex(0, 0.5f); + //Builder.AddMultiIndex(1, 0.5f); + + return Builder.Build(); +} + +TVoxelRange FVoxelGeneratorExampleInstance::GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const +{ + // Return the values that GetValueImpl can return in Bounds + // Used to skip chunks where the value does not change + // Be careful, if wrong your world will have holes! + // By default return infinite range to be safe + return TVoxelRange::Infinite(); + + // Example for the GetValueImpl above + + // Noise is between -1 and 1 + const TVoxelRange Height = TVoxelRange(-1, 1) * NoiseHeight; + + // Z can go from min to max + TVoxelRange Value = TVoxelRange(Bounds.Min.Z, Bounds.Max.Z) - Height; + + Value /= 5; + + return Value; +} + +FVector FVoxelGeneratorExampleInstance::GetUpVector(v_flt X, v_flt Y, v_flt Z) const +{ + // Used by spawners + return FVector::UpVector; +} + +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelExamplesModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelExamplesModule.h new file mode 100644 index 00000000..56e293a2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelExamplesModule.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" +#include "VoxelExamplesModule.generated.h" + +class FVoxelExamples : public IModuleInterface +{ +}; + +// UBT doesn't like not having any UObject in a module +UCLASS() +class UVoxelExamplesModuleDummy : public UObject +{ + GENERATED_BODY() +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelGeneratorExample.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelGeneratorExample.h new file mode 100644 index 00000000..e2dd01fb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/Public/VoxelGeneratorExample.h @@ -0,0 +1,53 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#if 0 + +#include "CoreMinimal.h" +#include "FastNoise/VoxelFastNoise.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelGeneratorExample.generated.h" + +UCLASS(Blueprintable) +class UVoxelGeneratorExample : public UVoxelGenerator +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Generator") + float NoiseHeight = 10.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Generator") + int32 Seed = 1337; + + //~ Begin UVoxelGenerator Interface + virtual TVoxelSharedRef GetInstance() override; + //~ End UVoxelGenerator Interface +}; + +class FVoxelGeneratorExampleInstance : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + explicit FVoxelGeneratorExampleInstance(const UVoxelGeneratorExample& MyGenerator); + + //~ Begin FVoxelGeneratorInstance Interface + virtual void Init(const FVoxelGeneratorInit& InitStruct) override; + + v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const; + + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const; + + virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final; + //~ End FVoxelGeneratorInstance Interface + +private: + const float NoiseHeight; + const int32 Seed; + FVoxelFastNoise Noise; +}; + +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/VoxelExamples.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/VoxelExamples.Build.cs new file mode 100644 index 00000000..7136ef88 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelExamples/VoxelExamples.Build.cs @@ -0,0 +1,31 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelExamples : ModuleRules +{ + public VoxelExamples(ReadOnlyTargetRules Target) : base(Target) +{ + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Voxel", + "Core", + "CoreUObject", + "Engine" + } + ); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.cpp new file mode 100644 index 00000000..90484687 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/RangeAnalysisPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" + +void FVoxelDisconnectRangeAnalysisConstantsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + FVoxelGraphCompilerHelpers::BreakNodeLinks(*Node); + } + } +} + +void FVoxelRemoveAllSeedNodesPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsSeedNode()) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.h new file mode 100644 index 00000000..d8fa6abe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/RangeAnalysisPass.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelDisconnectRangeAnalysisConstantsPass +{ + VOXEL_PASS_BODY(FVoxelDisconnectRangeAnalysisConstantsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +struct FVoxelRemoveAllSeedNodesPass +{ + VOXEL_PASS_BODY(FVoxelRemoveAllSeedNodesPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.cpp new file mode 100644 index 00000000..aa8494bd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.cpp @@ -0,0 +1,149 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/ReplaceBiomeMergePass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "VoxelNodes/VoxelMathNodes.h" +#include "VoxelNodes/VoxelBinaryNodes.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelNodes/VoxelIfNode.h" + +inline void AddMultiplyAdd( + FVoxelGraphCompiler& Compiler, + const FVoxelBiomeMergeCompilationNode& SourceNode, + FVoxelCompilationPin& ExecPin, + FVoxelCompilationPin& SumPin, + FVoxelCompilationPin& ValuePin, + FVoxelCompilationPin& AlphaPin, + FVoxelCompilationPin*& OutExecPin, + FVoxelCompilationPin*& OutSumPin) +{ + auto* ClampNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + ClampNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + ClampNode->GetInputPin(0).LinkTo(AlphaPin); + ClampNode->GetInputPin(1).SetDefaultValue("0"); + ClampNode->GetInputPin(2).SetDefaultValue("1"); + + auto* EqualNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + EqualNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + EqualNode->GetInputPin(0).LinkTo(ClampNode->GetOutputPin(0)); + EqualNode->GetInputPin(1).SetDefaultValue(FString::SanitizeFloat(SourceNode.Tolerance)); + + auto* IfNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + IfNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + IfNode->GetInputPin(0).LinkTo(ExecPin); + IfNode->GetInputPin(1).LinkTo(EqualNode->GetOutputPin(0)); + + auto* MultiplyNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + MultiplyNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + MultiplyNode->GetInputPin(0).LinkTo(ClampNode->GetOutputPin(0)); + MultiplyNode->GetInputPin(1).LinkTo(ValuePin); + + auto* AddNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + AddNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + AddNode->GetInputPin(0).LinkTo(SumPin); + AddNode->GetInputPin(1).LinkTo(MultiplyNode->GetOutputPin(0)); + + auto* FlowMergeNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + FlowMergeNode->SourceNodes.Insert(SourceNode.SourceNodes, 0); + + FlowMergeNode->GetInputPin(0).LinkTo(IfNode->GetOutputPin(0)); + FlowMergeNode->GetInputPin(1).LinkTo(SumPin); + FlowMergeNode->GetInputPin(2).LinkTo(IfNode->GetOutputPin(1)); + FlowMergeNode->GetInputPin(3).LinkTo(AddNode->GetOutputPin(0)); + + OutExecPin = &FlowMergeNode->GetOutputPin(0); + OutSumPin = &FlowMergeNode->GetOutputPin(1); +} + +void FVoxelReplaceBiomeMergePass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (auto* BiomeMergeNode = CastVoxel(Node)) + { + ensure((BiomeMergeNode->GetInputCount() - 1) % 2 == 0); + const int32 BiomesNum = (BiomeMergeNode->GetInputCount() - 1) / 2; + + auto& ExecInputPin = BiomeMergeNode->GetInputPin(0); + ensure(ExecInputPin.PinCategory == EVoxelPinCategory::Exec); + auto& ExecOutputPin = BiomeMergeNode->GetOutputPin(0); + ensure(ExecOutputPin.PinCategory == EVoxelPinCategory::Exec); + + auto& ResultOutputPin = BiomeMergeNode->GetOutputPin(1); + ensure(ResultOutputPin.PinCategory == EVoxelPinCategory::Float); + + TArray BiomesInputPins; + for (int32 Index = 0; Index < 2 * BiomesNum; Index++) + { + auto& InputPin = BiomeMergeNode->GetInputPin(1 + Index); + auto* Passthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, InputPin, BiomeMergeNode); + BiomesInputPins.Add(&Passthrough->GetOutputPin(0)); + } + + auto* ExecInputPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ExecInputPin, BiomeMergeNode); + FVoxelCompilationPin* LastExecOutput = &ExecInputPassthrough->GetOutputPin(0); + auto* SumInputPassthrough = Compiler.AddNode(FVoxelGraphCompilerHelpers::GetPassthroughNode(EVoxelPinCategory::Float, BiomeMergeNode->SourceNodes)); + FVoxelCompilationPin* LastSumOutput = &SumInputPassthrough->GetOutputPin(0); + + TArray LastBiomesOutputs; + for (int32 Index = 0; Index < BiomesNum; Index++) + { + // Value + LastBiomesOutputs.Add(BiomesInputPins[2 * Index + 0]); + // Alpha + LastBiomesOutputs.Add(BiomesInputPins[2 * Index + 1]); + } + + for (int32 BiomeIndex = 0; BiomeIndex < BiomesNum; BiomeIndex++) + { + AddMultiplyAdd( + Compiler, + *BiomeMergeNode, + *LastExecOutput, + *LastSumOutput, + *LastBiomesOutputs[2 * BiomeIndex + 0], + *LastBiomesOutputs[2 * BiomeIndex + 1], + LastExecOutput, + LastSumOutput); + + auto* FunctionSeparator = Compiler.AddNode(GetDefault()->GetCompilationNode(), BiomeMergeNode); + FunctionSeparator->GetInputPin(0).LinkTo(*LastExecOutput); + LastExecOutput = &FunctionSeparator->GetOutputPin(0); + + for (int32 Index = 0; Index < BiomesNum; Index++) + { + if (Index <= BiomeIndex) + { + LastBiomesOutputs[2 * Index + 0] = nullptr; + LastBiomesOutputs[2 * Index + 1] = nullptr; + } + } + } + + auto* ExecOutputPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ExecOutputPin, BiomeMergeNode); + ExecOutputPassthrough->GetInputPin(0).LinkTo(*LastExecOutput); + auto* ResultOutputPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ResultOutputPin, BiomeMergeNode); + ResultOutputPassthrough->GetInputPin(0).LinkTo(*LastSumOutput); + + auto& OutputPassthroughs = CastCheckedVoxel(BiomeMergeNode).OutputPassthroughs; + OutputPassthroughs.Add(ResultOutputPassthrough); + + if (Compiler.FirstNode == BiomeMergeNode) + { + ensure(Compiler.FirstNodePinIndex == 0); + Compiler.FirstNode = ExecInputPassthrough; + Compiler.FirstNodePinIndex = 0; + } + + BiomeMergeNode->BreakAllLinks(); + Compiler.RemoveNode(BiomeMergeNode); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.h new file mode 100644 index 00000000..212c6397 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/ReplaceBiomeMergePass.h @@ -0,0 +1,14 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelReplaceBiomeMergePass +{ + VOXEL_PASS_BODY(FVoxelReplaceBiomeMergePass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.cpp new file mode 100644 index 00000000..87697fb3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.cpp @@ -0,0 +1,80 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelCompactPassthroughsPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphErrorReporter.h" + +inline bool RemovePassthroughIfPossible(FVoxelGraphCompiler& Compiler, FVoxelCompilationNode* PassthroughNode) +{ + if (PassthroughNode == Compiler.FirstNode) + { + return false; + } + + auto& InputPin = PassthroughNode->GetInputPin(0); + auto& OutputPin = PassthroughNode->GetOutputPin(0); + const auto Category = InputPin.PinCategory; + check(Category == OutputPin.PinCategory); + + if (InputPin.NumLinkedTo() == 0) + { + if (Category != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : OutputPin.IterateLinkedTo()) + { + LinkedToPin.SetDefaultValue(InputPin.GetDefaultValue()); + } + } + + PassthroughNode->BreakAllLinks(); + Compiler.RemoveNode(PassthroughNode); + return true; + } + else if (OutputPin.NumLinkedTo() > 0) + { + if (InputPin.PinCategory == EVoxelPinCategory::Exec) + { + check(OutputPin.NumLinkedTo() == 1); + auto& PinLinkedToOutputPin = OutputPin.GetLinkedTo(0); + FVoxelGraphCompilerHelpers::MovePin(InputPin, PinLinkedToOutputPin); + } + else + { + check(InputPin.NumLinkedTo() == 1); + auto& PinLinkedToInputPin = InputPin.GetLinkedTo(0); + FVoxelGraphCompilerHelpers::MovePin(OutputPin, PinLinkedToInputPin); + } + + PassthroughNode->BreakAllLinks(); + Compiler.RemoveNode(PassthroughNode); + return true; + } + else + { + return false; + } +} + +void FVoxelCompactPassthroughsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + bool bContinue = true; + while (bContinue && !Compiler.ErrorReporter.HasError()) + { + bContinue = false; + for (auto& Node : Compiler.GetAllNodes()) + { + if (Node->IsA() && RemovePassthroughIfPossible(Compiler, Node)) + { + bContinue = true; + break; + } + if (Compiler.ErrorReporter.HasError()) + { + break; + } + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.h new file mode 100644 index 00000000..9cff167b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompactPassthroughsPass.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelCompactPassthroughsPass +{ + VOXEL_PASS_BODY(FVoxelCompactPassthroughsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompilationPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompilationPass.h new file mode 100644 index 00000000..f3ee6b10 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelCompilationPass.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class FVoxelGraphCompiler; + +#define VOXEL_PASS_BODY(Class) static const TCHAR* GetName() { sizeof(Class); return TEXT(#Class); } + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.cpp new file mode 100644 index 00000000..41a8c93f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.cpp @@ -0,0 +1,125 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelDependenciesPass.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Runtime/VoxelComputeNode.h" +#include "VoxelGraphErrorReporter.h" + +void FVoxelMarkDependenciesPass::Apply(FVoxelGraphCompiler& Compiler) +{ + // Mark + for (auto* Node : Compiler.GetAllNodes()) + { + // Functions separators depend on X, else their outputs are cached + const uint8 Flag = Node->GetDefaultAxisDependencies(); + + if (Flag != 0) + { + // Ignore exec successors as we can't affect them (no side effects) + // Will still get the exec nodes with a data input + TSet Successors; + FVoxelGraphCompilerHelpers::GetAllDataSuccessors(Node, Successors); + for (auto& Successor : Successors) + { + Successor->Dependencies |= Flag; + } + } + } + + // All nodes after a branch node that are in only one branch must be flagged at least like it, unless they are constants. Need to loop to propagate changes + bool bContinue = true; + while (bContinue) + { + bContinue = false; + for (auto* Node : Compiler.GetAllNodes()) + { + if (Node->IsA()) + { + TSet AlwaysComputedNodes = FVoxelGraphCompilerHelpers::GetAlwaysComputedNodes(Node); + TSet Successors = FVoxelGraphCompilerHelpers::GetAllExecSuccessorsAndTheirDataDependencies(Node, false); + for (auto* Successor : Successors) + { + const bool bAlwaysComputed = AlwaysComputedNodes.Contains(Successor); + check(!(Successor->IsExecNode() && bAlwaysComputed)); + if (!bAlwaysComputed && (Successor->IsExecNode() || !FVoxelAxisDependencies::IsConstant(Successor->Dependencies))) + { + const uint8 OldDependencies = Successor->Dependencies; + Successor->Dependencies |= Node->Dependencies; + bContinue |= (OldDependencies != Successor->Dependencies); + } + } + } + } + // Propagate dependencies + // Needed for some rare cases where a predecessor is used in more branches than a successor + // See https://i.imgur.com/PBwJAz7.png ("X -" on the left) + for (auto* Node : Compiler.GetAllNodes()) + { + if (Node->Dependencies != 0) + { + TSet Successors; + FVoxelGraphCompilerHelpers::GetAllDataSuccessors(Node, Successors); + for (auto& Successor : Successors) + { + const uint8 OldDependencies = Successor->Dependencies; + Successor->Dependencies |= Node->Dependencies; + bContinue |= (OldDependencies != Successor->Dependencies); + } + } + } + } +} + +void FVoxelDebugDependenciesPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto& Node : Compiler.GetAllNodes()) + { + Compiler.ErrorReporter.AddMessageToNode( + Node, + FVoxelAxisDependencies::ToString(FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(Node->Dependencies)), + EVoxelGraphNodeMessageType::Info, + false, + false); + } +} + +void FVoxelGetSortedConstantsAndRemoveConstantsPass::Apply( + FVoxelGraphCompiler& Compiler, + FVoxelCreatedComputeNodes& CreatedNodes, + TArray>& ConstantNodes, + TArray>& SeedNodes) +{ + TArray Nodes; + for (auto* Node : Compiler.GetAllNodes()) + { + // Note: don't add any other checks here, or it'll break the compilation tree + if (!Node->IsExecNode() && FVoxelAxisDependencies::IsConstant(Node->Dependencies)) + { + Nodes.Add(Node); + } + } + + FVoxelGraphCompilerHelpers::SortNodes(Nodes); + + for (auto* Node : Nodes) + { + auto ComputeNode = CreatedNodes.GetComputeNode(*Node); + + if (Node->IsSeedNode()) + { + check(ComputeNode->Type == EVoxelComputeNodeType::Seed); + SeedNodes.Add(StaticCastSharedRef(ComputeNode)); + } + else + { + check(ComputeNode->Type == EVoxelComputeNodeType::Data); + ConstantNodes.Add(StaticCastSharedRef(ComputeNode)); + + // Don't remove seed nodes, as they are needed for each function initialization in C++ (as there is no global state) + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.h new file mode 100644 index 00000000..770eb72a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelDependenciesPass.h @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelCompilationPass.h" + +class FVoxelComputeNode; +class FVoxelDataComputeNode; +class FVoxelSeedComputeNode; +class FVoxelCreatedComputeNodes; + +struct FVoxelMarkDependenciesPass +{ + VOXEL_PASS_BODY(FVoxelMarkDependenciesPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +struct FVoxelDebugDependenciesPass +{ + VOXEL_PASS_BODY(FVoxelDebugDependenciesPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +struct FVoxelGetSortedConstantsAndRemoveConstantsPass +{ + VOXEL_PASS_BODY(FVoxelGetSortedConstantsAndRemoveConstantsPass); + + static void Apply( + FVoxelGraphCompiler& Compiler, + FVoxelCreatedComputeNodes& CreatedNodes, + TArray>& ConstantNodes, + TArray>& SeedNodes); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.cpp new file mode 100644 index 00000000..b5b74710 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.cpp @@ -0,0 +1,151 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelFlowMergePass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelGraphErrorReporter.h" + +inline void ReplaceFlowMerge(FVoxelGraphCompiler& Compiler, FVoxelFlowMergeCompilationNode* FlowMergeNode) +{ + const FString& Name = FlowMergeNode->GetPrettyName(); + const auto& Types = CastChecked(&FlowMergeNode->Node)->Types; + + FVoxelCompilationNode* PassthroughExecOutput; + TArray PassthroughValueOutputs; + + FVoxelCompilationNode* PassthroughExecInputA; + TArray PassthroughValueInputsA; + FVoxelCompilationNode* PassthroughExecInputB; + TArray PassthroughValueInputsB; + + { + int32 OutputIndex = 0; + auto& ExecOutputPin = FlowMergeNode->GetOutputPin(OutputIndex++); + PassthroughExecOutput = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ExecOutputPin); + for (int32 Index = 0; Index < Types.Num(); Index++) + { + auto& ValueOutputPin = FlowMergeNode->GetOutputPin(OutputIndex++); + PassthroughValueOutputs.Add(FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ValueOutputPin)); + } + + int32 InputIndex = 0; + auto& ExecInputPinA = FlowMergeNode->GetInputPin(InputIndex++); + PassthroughExecInputA = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ExecInputPinA); + for (int32 Index = 0; Index < Types.Num(); Index++) + { + auto& ValueInputPinA = FlowMergeNode->GetInputPin(InputIndex++); + PassthroughValueInputsA.Add(FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ValueInputPinA)); + } + auto& ExecInputPinB = FlowMergeNode->GetInputPin(InputIndex++); + PassthroughExecInputB = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ExecInputPinB); + for (int32 Index = 0; Index < Types.Num(); Index++) + { + auto& ValueInputPinB = FlowMergeNode->GetInputPin(InputIndex++); + PassthroughValueInputsB.Add(FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, ValueInputPinB)); + } + } + check(PassthroughValueOutputs.Num() == Types.Num()); + check(PassthroughValueInputsA.Num() == Types.Num()); + check(PassthroughValueInputsB.Num() == Types.Num()); + + TSet NodesToDuplicate; + FVoxelGraphCompilerHelpers::GetAllSuccessors(FlowMergeNode, NodesToDuplicate); + NodesToDuplicate.Remove(FlowMergeNode); + + for (auto& Node : NodesToDuplicate) + { + FVoxelCompilationNode* FaultyNode = nullptr; + if (Node->IsExecNode() && !FVoxelGraphCompilerHelpers::AreAllNodePredecessorsChildOfStartNodeExecOnly(Node, FlowMergeNode, FaultyNode)) + { + Compiler.ErrorReporter.AddError("Invalid FlowMerge node " + Name + ": child " + Node->GetPrettyName() + " has a parent that isn't child of this flow merge node: " + FaultyNode->GetPrettyName()); + Compiler.ErrorReporter.AddNodeToSelect(FlowMergeNode); + Compiler.ErrorReporter.AddNodeToSelect(Node); + Compiler.ErrorReporter.AddNodeToSelect(FaultyNode); + Compiler.ErrorReporter.AddMessageToNode(FlowMergeNode, "flow merge", EVoxelGraphNodeMessageType::Info); + Compiler.ErrorReporter.AddMessageToNode(Node, "child", EVoxelGraphNodeMessageType::Info); + Compiler.ErrorReporter.AddMessageToNode(FaultyNode, "should be child of flow merge", EVoxelGraphNodeMessageType::Error); + return; + } + } + + TMap OldPinsToNewPins; + TMap OldNodesToNewNodes; + FVoxelGraphCompilerHelpers::DuplicateNodes(Compiler, NodesToDuplicate, OldPinsToNewPins, OldNodesToNewNodes); + + if (Compiler.ErrorReporter.HasError()) + { + return; + } + + PassthroughExecInputA->GetOutputPin(0).LinkTo(PassthroughExecOutput->GetInputPin(0)); + PassthroughExecInputB->GetOutputPin(0).LinkTo(*OldPinsToNewPins[&PassthroughExecOutput->GetInputPin(0)]); + for (int32 Index = 0; Index < Types.Num(); Index++) + { + PassthroughValueInputsA[Index]->GetOutputPin(0).LinkTo(PassthroughValueOutputs[Index]->GetInputPin(0)); + PassthroughValueInputsB[Index]->GetOutputPin(0).LinkTo(*OldPinsToNewPins[&PassthroughValueOutputs[Index]->GetInputPin(0)]); + } + + FlowMergeNode->BreakAllLinks(); + Compiler.RemoveNode(FlowMergeNode); +} + + + +void FVoxelReplaceFlowMergePass::Apply(FVoxelGraphCompiler& Compiler) +{ + bool bContinue = true; + while (bContinue && !Compiler.ErrorReporter.HasError()) + { + bContinue = false; + + TArray SortedNodes; + FVoxelGraphCompilerHelpers::GetSortedExecNodes(Compiler.FirstNode, SortedNodes); + + for (auto& Node : SortedNodes) + { + if (auto* FlowMerge = CastVoxel(Node)) + { + ReplaceFlowMerge(Compiler, FlowMerge); + bContinue = true; + break; + } + if (Compiler.ErrorReporter.HasError()) + { + return; + } + } + } + + if (!Compiler.ErrorReporter.HasError()) + { + TSet Nodes; + FVoxelGraphCompilerHelpers::GetAllUsedNodes(Compiler.FirstNode, Nodes); + for (auto& Node : Nodes) + { + if (Node->IsA()) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "FlowMerge data output is used but exec output isn't connected", EVoxelGraphNodeMessageType::Error); + break; + } + } + } +} + +void FVoxelFixMultipleOutputsExecPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->GetExecOutputCount() > 1) + { + for (auto& OutputPin : Node->IteratePins()) + { + if (OutputPin.PinCategory == EVoxelPinCategory::Exec && OutputPin.NumLinkedTo() == 0) + { + FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, OutputPin); + } + } + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.h new file mode 100644 index 00000000..3e85c8ec --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFlowMergePass.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelReplaceFlowMergePass +{ + VOXEL_PASS_BODY(FVoxelReplaceFlowMergePass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +// Make sure ifs nodes have multiple childs +struct FVoxelFixMultipleOutputsExecPass +{ + VOXEL_PASS_BODY(FVoxelFixMultipleOutputsExecPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.cpp new file mode 100644 index 00000000..c44a8b07 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.cpp @@ -0,0 +1,400 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelFunctionsPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelNode.h" + +struct FVoxelFunctionTree +{ + FVoxelFunctionSeparatorCompilationNode& Separator; + + FVoxelFunctionTree* const Parent; + TArray> Children; + + TSet Nodes; + + struct FPinToForward + { + FVoxelCompilationPin* From; + FVoxelCompilationPin* To; + }; + TArray PinsToForward; + // Pin in PinsToForward.To to separator output pin + // Use To because it's unique, unlike From + TMap ForwardedPins; + +public: + explicit FVoxelFunctionTree(FVoxelFunctionSeparatorCompilationNode& Separator, FVoxelFunctionTree* Parent) + : Separator(Separator) + , Parent(Parent) + { + TArray ExecNodeQueue; + ExecNodeQueue.Add(&Separator); + while (ExecNodeQueue.Num() > 0) + { + auto* ExecNode = ExecNodeQueue.Pop(false); + for (auto& OutputPin : ExecNode->IteratePins()) + { + if (OutputPin.PinCategory == EVoxelPinCategory::Exec && OutputPin.NumLinkedTo() > 0) + { + check(OutputPin.NumLinkedTo() == 1); + auto& OtherNode = OutputPin.GetLinkedTo(0).Node; + + if (auto* OtherFunctionSeparator = CastVoxel(&OtherNode)) + { + bool bAlreadyAdded = false; + for (auto& Child : Children) + { + if (&Child->Separator == OtherFunctionSeparator) + { + bAlreadyAdded = true; + break; + } + } + if (!bAlreadyAdded) + { + Children.Emplace(MakeUnique(*OtherFunctionSeparator, this)); + } + } + else + { + ExecNodeQueue.Add(&OtherNode); + } + } + } + } + } + + void Validate(FVoxelGraphErrorReporter& ErrorReporter, TMap& Parents) + { + for (auto& Child : Children) + { + auto*& ExistingParent = Parents.FindOrAdd(&Child->Separator); + if (ExistingParent) + { + ErrorReporter.AddError("separator has multiple separators in its predecessors that are not following each others"); + ErrorReporter.AddMessageToNode(ExistingParent, "separator A", EVoxelGraphNodeMessageType::Info); + ErrorReporter.AddMessageToNode(&Separator, "separator B", EVoxelGraphNodeMessageType::Info); + ErrorReporter.AddMessageToNode(&Child->Separator, "separator is child of both A and B", EVoxelGraphNodeMessageType::Error); + return; + } + else + { + ExistingParent = &Separator; + } + } + + for (auto& Child : Children) + { + Child->Validate(ErrorReporter, Parents); + } + } + +public: + void FindUsedNodes(const TSet& ParentsNodes) + { + auto& OutputPin = Separator.GetOutputPin(0); + if (OutputPin.NumLinkedTo() > 0) + { + FVoxelGraphCompilerHelpers::GetFunctionNodes(&OutputPin.GetLinkedTo(0).Node, Nodes); + } + + for (auto* Node : ParentsNodes) + { + Nodes.Remove(Node); + } + + auto ParentsNodesCopy = ParentsNodes; + ParentsNodesCopy.Append(Nodes); + for (auto& Child : Children) + { + Child->FindUsedNodes(ParentsNodesCopy); + } + } + + void FindPinsToForward() + { + for (auto* Node : Nodes) + { + for (auto& InputPin : Node->IteratePins()) + { + if (InputPin.PinCategory != EVoxelPinCategory::Exec && InputPin.NumLinkedTo() > 0) + { + check(InputPin.NumLinkedTo() == 1); + auto& LinkedTo = InputPin.GetLinkedTo(0); + auto* LinkedToNode = &LinkedTo.Node; + if (!Nodes.Contains(LinkedToNode)) + { + // If this pin is linked to a node we don't own, we need to ask for that pin to be forwarded + bool bFound = false; + for (auto* It = this; It; It = It->Parent) + { + if (It->Nodes.Contains(LinkedToNode)) + { + // No need to go further up + bFound = true; + break; + } + else + { + It->PinsToForward.Add({ &LinkedTo, &InputPin }); + } + } + ensure(bFound); + } + } + } + } + + for (auto& Child : Children) + { + Child->FindPinsToForward(); + } + } + + void CreatePins(FVoxelGraphCompiler& Compiler) + { + TArray PinCategories; + PinCategories.Add(EVoxelPinCategory::Exec); + for (auto& PinToForward : PinsToForward) + { + ensure(PinToForward.From->PinCategory == PinToForward.To->PinCategory); + PinCategories.Add(PinToForward.From->PinCategory); + } + + // Create new separator + auto* NewSeparator = Compiler.AddNode(MakeShared(Separator.Node, PinCategories, PinCategories)); + NewSeparator->SourceNodes = Separator.SourceNodes; + + // Forward exec pin + FVoxelGraphCompilerHelpers::MovePin(Separator.GetInputPin(0), NewSeparator->GetInputPin(0)); + FVoxelGraphCompilerHelpers::MovePin(Separator.GetOutputPin(0), NewSeparator->GetOutputPin(0)); + + // Forward pins + int32 PinIndex = 1; + for (auto& PinToForward : PinsToForward) + { + auto& InputPin = NewSeparator->GetInputPin(PinIndex); + auto& OutputPin = NewSeparator->GetOutputPin(PinIndex); + PinIndex++; + + // TRICKY: the separators are at the _start_, so to link the input pin we must look in the parent nodes + if (Parent && Parent->Nodes.Contains(&PinToForward.From->Node)) + { + // This is one of our parent nodes, so directly create the link + InputPin.LinkTo(*PinToForward.From); + } + else + { + // We need to find our parent pin + check(Parent); + InputPin.LinkTo(*Parent->ForwardedPins[PinToForward.To]); + } + + if (Nodes.Contains(&PinToForward.To->Node)) + { + // It's forwarded to one of our nodes, so just link it + PinToForward.From->BreakLinkTo(*PinToForward.To); + OutputPin.LinkTo(*PinToForward.To); + } + else + { + // Queue it for children to link + ForwardedPins.Add(PinToForward.To, &OutputPin); + } + } + + // Delete our old node + if (Compiler.FirstNode == &Separator) + { + Compiler.FirstNode = NewSeparator; + ensure(Compiler.FirstNodePinIndex == 0); + } + Separator.BreakAllLinks(); + Compiler.RemoveNode(&Separator); + + // Call children + for (auto& Child : Children) + { + Child->CreatePins(Compiler); + } + } +}; + +void FVoxelFillFunctionSeparatorsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + auto& FirstSeparator = CastCheckedVoxel(Compiler.FirstNode); + FVoxelFunctionTree Tree(FirstSeparator, nullptr); + + { + TMap Parents; + Tree.Validate(Compiler.ErrorReporter, Parents); + + if (Compiler.ErrorReporter.HasError()) + { + return; + } + } + + Tree.FindUsedNodes({}); + Tree.FindPinsToForward(); + Tree.CreatePins(Compiler); +} + +void FVoxelFindFunctionsPass::Apply(FVoxelGraphCompiler& Compiler, TArray& OutFunctions) +{ + check(Compiler.FirstNode); + check(Compiler.FirstNode->IsA()); + + for (auto& Node : Compiler.GetAllNodes()) + { + if (auto* FunctionCall = CastVoxel(Node)) + { + OutFunctions.Add(FVoxelCompilationFunctionDescriptor(FunctionCall->FunctionId, FunctionCall)); + } + } + + for (auto& Function : OutFunctions) + { + Function.Nodes.Add(Function.FirstNode); + + auto& OutputPin = Function.FirstNode->GetOutputPin(0); + if (OutputPin.NumLinkedTo() == 0) + { + continue; + } + check(OutputPin.NumLinkedTo() == 1); + + FVoxelGraphCompilerHelpers::GetFunctionNodes(&OutputPin.GetLinkedTo(0).Node, Function.Nodes); + } + + TSet FunctionSeparators; + for (auto& Function : OutFunctions) + { + FunctionSeparators.Add(Function.FirstNode); + } + + for (auto& FunctionA : OutFunctions) + { + for (auto& FunctionB : OutFunctions) + { + if (FunctionA.FirstNode != FunctionB.FirstNode) + { + auto IntersectionNodes = FunctionA.Nodes.Intersect(FunctionB.Nodes).Difference(FunctionSeparators); + if (IntersectionNodes.Num() > 0) + { + auto IntersectionNodesHead = FVoxelGraphCompilerHelpers::FilterHeads(IntersectionNodes); + Compiler.ErrorReporter.AddError("INTERNAL ERROR: Nodes outputs are used in different functions! Try moving the function separators somewhere else"); + + for (auto& Node : IntersectionNodesHead) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "Node is used by FunctionA and FunctionB", EVoxelGraphNodeMessageType::Error); + } + + auto NodesAToShow = FunctionA.Nodes.Difference(IntersectionNodes).Difference(FunctionSeparators); + auto NodesBToShow = FunctionB.Nodes.Difference(IntersectionNodes).Difference(FunctionSeparators); + bool bNodesAShown = false; + bool bNodesBShown = false; + for (auto& Node : NodesAToShow) + { + if (Node->IsLinkedToOne(IntersectionNodesHead)) + { + bNodesAShown = true; + Compiler.ErrorReporter.AddMessageToNode(Node, "FunctionA node", EVoxelGraphNodeMessageType::Info); + } + } + for (auto& Node : NodesBToShow) + { + if (Node->IsLinkedToOne(IntersectionNodesHead)) + { + bNodesBShown = true; + Compiler.ErrorReporter.AddMessageToNode(Node, "FunctionB node", EVoxelGraphNodeMessageType::Info); + } + } + if (!bNodesAShown) + { + for (auto& Node : NodesAToShow) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "FunctionA node", EVoxelGraphNodeMessageType::Info); + } + } + if (!bNodesBShown) + { + for (auto& Node : NodesBToShow) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "FunctionB node", EVoxelGraphNodeMessageType::Info); + } + } + auto* Separator = FVoxelGraphCompilerHelpers::IsDataNodeSuccessor(FunctionA.FirstNode, FunctionB.FirstNode) ? FunctionB.FirstNode : FunctionA.FirstNode; + Compiler.ErrorReporter.AddMessageToNode(Separator, "separator", EVoxelGraphNodeMessageType::Info); + return; + } + } + } + } +} + +void FVoxelRemoveNodesOutsideFunction::Apply(FVoxelGraphCompiler& Compiler, TSet& FunctionNodes) +{ + for (auto& Node : Compiler.GetAllNodesCopy()) + { + if (!FunctionNodes.Contains(Node)) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } + } +} + +void FVoxelAddFirstFunctionPass::Apply(FVoxelGraphCompiler& Compiler) +{ + if (Compiler.FirstNode) + { + auto& InputPin = Compiler.FirstNode->GetInputPin(Compiler.FirstNodePinIndex); + InputPin.BreakAllLinks(); + + TSharedRef FunctionNode = MakeShareable( + new FVoxelFunctionSeparatorCompilationNode( + *GetDefault(), + { EVoxelPinCategory::Exec }, + { EVoxelPinCategory::Exec })); + + Compiler.AddNode(FunctionNode); + FunctionNode->GetOutputPin(0).LinkTo(InputPin); + Compiler.FirstNode = &FunctionNode.Get(); + Compiler.FirstNodePinIndex = 0; + } +} + +void FVoxelReplaceFunctionSeparatorsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + check(Compiler.FirstNode); + + // Replace first node + { + auto* OldNode = Compiler.FirstNode; + auto* NewNode = Compiler.AddNode(MakeShared(CastCheckedVoxel(OldNode))); + Compiler.FirstNode = NewNode; + + FVoxelGraphCompilerHelpers::MoveOutputPins(*OldNode, *NewNode); + FVoxelGraphCompilerHelpers::BreakNodeLinks(*OldNode); + OldNode->CheckIsNotLinked(Compiler.ErrorReporter); + Compiler.RemoveNode(OldNode); + } + + // Replace function calls + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (auto* Separator = CastVoxel(Node)) + { + auto* NewNode = Compiler.AddNode(MakeShared(*Separator)); + FVoxelGraphCompilerHelpers::MoveInputPins(*Separator, *NewNode); + FVoxelGraphCompilerHelpers::BreakNodeLinks(*Separator); + Separator->CheckIsNotLinked(Compiler.ErrorReporter); + Compiler.RemoveNode(Separator); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.h new file mode 100644 index 00000000..30335a48 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelFunctionsPass.h @@ -0,0 +1,44 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +class FVoxelCompilationNode; +struct FVoxelCompilationFunctionDescriptor; + +struct FVoxelFillFunctionSeparatorsPass +{ + VOXEL_PASS_BODY(FVoxelFillFunctionSeparatorsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +struct FVoxelFindFunctionsPass +{ + VOXEL_PASS_BODY(FVoxelFindFunctionsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, TArray& OutFunctions); +}; + +struct FVoxelRemoveNodesOutsideFunction +{ + VOXEL_PASS_BODY(FVoxelRemoveNodesOutsideFunction); + + static void Apply(FVoxelGraphCompiler& Compiler, TSet& FunctionNodes); +}; + +struct FVoxelAddFirstFunctionPass +{ + VOXEL_PASS_BODY(FVoxelAddFirstFunctionPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + +struct FVoxelReplaceFunctionSeparatorsPass +{ + VOXEL_PASS_BODY(FVoxelReplaceFunctionSeparatorsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.cpp new file mode 100644 index 00000000..c395c94a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.cpp @@ -0,0 +1,114 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGetRangeAnalysisPass.h" +#include "Compilation/Passes/VoxelSetIdsPass.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelCompilationNodeTree.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelGraphErrorReporter.h" + +void FVoxelGetRangeAnalysisPass::Apply(FVoxelGraphCompiler& Compiler) +{ + TArray GetRangeAnalysisNodes; + for (auto* NodeIt : Compiler.GetAllNodes()) + { + auto* Node = CastVoxel(NodeIt); + if (!Node) + { + continue; + } + + GetRangeAnalysisNodes.Add(Node); + + TSet Predecessors; + FVoxelGraphCompilerHelpers::GetAllPredecessors(Node, Predecessors); + for (auto* It : Predecessors) + { + if (It == Node) + { + continue; + } + if (It->IsExecNode()) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "cannot have an exec node before a GetRangeAnalysis node", EVoxelGraphNodeMessageType::Error); + return; + } + if (It->IsA()) + { + Compiler.ErrorReporter.AddMessageToNode(Node, "cannot have a GetRangeAnalysis node before a GetRangeAnalysis node", EVoxelGraphNodeMessageType::Error); + return; + } + } + } + + for (auto* PreviousGetRangeAnalysisNode : GetRangeAnalysisNodes) + { + TMap OldNodesToNewNodes; + const auto LocalCompiler = Compiler.Clone("GetRangeAnalysis", OldNodesToNewNodes); + + FVoxelCompilationNode* SetValue; + + // Add SetValue node + { + SetValue = LocalCompiler->AddNode(GetMutableDefault()->GetCompilationNode()); + + auto& GetRangeAnalysisNode = *OldNodesToNewNodes[PreviousGetRangeAnalysisNode]; + FVoxelGraphCompilerHelpers::MovePin(GetRangeAnalysisNode.GetInputPin(0), SetValue->GetInputPin(1)); + + // Set the first node now, so we can remove the original one + LocalCompiler->FirstNode = SetValue; + LocalCompiler->FirstNodePinIndex = 0; + } + + TSet Predecessors; + FVoxelGraphCompilerHelpers::GetAllPredecessors(SetValue, Predecessors); + + // Compute GetRange dependencies + uint8 Dependencies = 0; + for (auto* Predecessor : Predecessors) + { + Dependencies |= Predecessor->GetDefaultAxisDependencies(); + } + + // Remove all other nodes + for (auto* NodeIt : LocalCompiler->GetAllNodesCopy()) + { + // Force set dependencies to not have any constant node + NodeIt->Dependencies = EVoxelAxisDependenciesFlags::XYZ; + if (!Predecessors.Contains(NodeIt)) + { + NodeIt->BreakAllLinks(); + LocalCompiler->RemoveNode(NodeIt); + } + } + + // Assign ids + int32 Id = 0; + LocalCompiler->ApplyPass(Id); + + // Create the tree + const TVoxelSharedRef ComputeTree = MakeVoxelShared(); + { + const auto Tree = FVoxelCompilationNodeTree::Create(&*SetValue); + + TArray FunctionCallsToLink; + TSet UsedNodes; + FVoxelCreatedComputeNodes CreatedNodes; + Tree->ConvertToComputeNodeTree(EVoxelFunctionAxisDependencies::XYZWithoutCache, *ComputeTree, FunctionCallsToLink, UsedNodes, CreatedNodes); + ensure(FunctionCallsToLink.Num() == 0); + } + + check(PreviousGetRangeAnalysisNode->VariablesBufferSize == -1 && !PreviousGetRangeAnalysisNode->Tree); + PreviousGetRangeAnalysisNode->VariablesBufferSize = Id; + PreviousGetRangeAnalysisNode->Tree = ComputeTree; + // Make sure we always have a context + PreviousGetRangeAnalysisNode->DefaultDependencies = FMath::Max(Dependencies, EVoxelAxisDependenciesFlags::X); + + // Disconnect the input + PreviousGetRangeAnalysisNode->GetInputPin(0).BreakAllLinks(); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.h new file mode 100644 index 00000000..688bf50c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelGetRangeAnalysisPass.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelGetRangeAnalysisPass +{ + VOXEL_PASS_BODY(FVoxelGetRangeAnalysisPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.cpp new file mode 100644 index 00000000..f910e4ae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.cpp @@ -0,0 +1,166 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelMacrosPass.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/Passes/VoxelReplaceLocalVariablesPass.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphErrorReporter.h" + +inline void ReplaceMacro(FVoxelGraphCompiler& Compiler, FVoxelMacroCompilationNode* MacroCompilationNode, bool bClearMessages) +{ + const UVoxelGraphMacroNode* const MacroNode = CastChecked(&MacroCompilationNode->Node); + UVoxelGraphMacro* const MacroGraph = MacroNode->Macro; + auto& ErrorReporter = Compiler.ErrorReporter; + + check(MacroGraph); + check(MacroGraph->InputNode && MacroGraph->OutputNode); + check(MacroNode->InputPins.Num() == MacroGraph->InputNode->OutputPins.Num() && MacroNode->OutputPins.Num() == MacroGraph->OutputNode->InputPins.Num()); + + static TSet StackMacros; + + if (StackMacros.Contains(MacroNode)) + { + FString Error; + Error += "Recursive macros detected! Macros in stack: "; + for (auto& StackMacro : StackMacros) + { + Error += "\n\t" + StackMacro->Macro->GetName(); + } + ErrorReporter.AddError(Error); + ErrorReporter.AddNodeToSelect(MacroCompilationNode); + ErrorReporter.AddMessageToNode(MacroCompilationNode, "recursive macros detected", EVoxelGraphNodeMessageType::Error); + return; + } + + StackMacros.Add(MacroNode); + { + TMap NodesMap; + + // Merge macro nodes + { + if (bClearMessages) + { + FVoxelGraphErrorReporter::ClearCompilationMessages(MacroGraph); + } + + auto LocalReporter = MakeShared(Compiler.ErrorReporter, MacroGraph->GetName()); + FVoxelGraphCompiler LocalCompiler(LocalReporter); + MacroGraph->AllNodes.RemoveAll([](UVoxelNode* Node) {return !IsValid(Node); }); + NodesMap = LocalCompiler.InitFromNodes(MacroGraph->AllNodes, nullptr, 0); + for (auto& Node : LocalCompiler.GetAllNodes()) + { + Node->SourceNodes.Append(MacroCompilationNode->SourceNodes); + } + LocalCompiler.ApplyPass(); // Apply it here to keep local refs and avoid UVoxelNode* collisions + LocalCompiler.ApplyPass(bClearMessages); // Apply pass AFTER rename/SourceNode + Compiler.AppendAndClear(LocalCompiler); + + if (ErrorReporter.HasError()) + { + ErrorReporter.AddMessageToNode(MacroNode, "errors in macro", EVoxelGraphNodeMessageType::Error); + } + } + if (ErrorReporter.HasError()) + { + StackMacros.Remove(MacroNode); + return; + } + + FVoxelCompilationNode* const InputNode = NodesMap.FindChecked(MacroGraph->InputNode); + FVoxelCompilationNode* const OutputNode = NodesMap.FindChecked(MacroGraph->OutputNode); + + MacroCompilationNode->InputNode = InputNode; + MacroCompilationNode->OutputNode = OutputNode; + + // Link pins + for (auto& MacroNodePin : MacroCompilationNode->IteratePins()) + { + int32 PinIndex = MacroNodePin.Index; + bool bIsInput = MacroNodePin.Direction == EVoxelPinDirection::Input; + auto& InternalNodePin = bIsInput ? InputNode->GetInputPin(PinIndex) : OutputNode->GetOutputPin(PinIndex); + check(MacroNodePin.Direction == InternalNodePin.Direction); + + // Copy the default values the user set + if (bIsInput) + { + InternalNodePin.SetDefaultValue(MacroNodePin.GetDefaultValue()); + } + + // Always destroy all links when inside a macro: we don't want the preview nodes + InternalNodePin.BreakAllLinks(); + for (auto& LinkedToPin : MacroNodePin.IterateLinkedTo()) + { + InternalNodePin.LinkTo(LinkedToPin); + } + MacroNodePin.BreakAllLinks(); + } + + if (Compiler.FirstNode == MacroCompilationNode) + { + Compiler.FirstNode = InputNode; + // Input node pin index is the same + } + + Compiler.RemoveNode(MacroCompilationNode); + } + StackMacros.Remove(MacroNode); +} + + +void FVoxelGraphInlineMacrosPass::Apply(FVoxelGraphCompiler& Compiler, bool bClearMessages) +{ + // The nodes array will be changed by the macro nodes + for (auto& Node : Compiler.GetAllNodesCopy()) + { + if (auto* MacroNode = CastVoxel(Node)) + { + ReplaceMacro(Compiler, MacroNode, bClearMessages); + } + if (Compiler.ErrorReporter.HasError()) + { + return; + } + } + for (auto& Node : Compiler.GetAllNodes()) + { + check(!Node->IsA()); + } +} + +void FVoxelGraphReplaceMacroInputOutputsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto& Node : Compiler.GetAllNodesCopy()) + { + if (auto* MacroInputOutputNode = CastVoxel(Node)) + { + check(Node->GetInputCount() == Node->GetOutputCount()); + for (int32 Index = 0; Index < MacroInputOutputNode->GetInputCount(); Index++) + { + auto& InputPin = MacroInputOutputNode->GetInputPin(Index); + auto& OutputPin = MacroInputOutputNode->GetOutputPin(Index); + auto* Passthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, InputPin); + MacroInputOutputNode->Passthroughs.Add(Passthrough); + + auto& PassthroughOutputPin = Passthrough->GetOutputPin(0); + check(PassthroughOutputPin.NumLinkedTo() == 1); + PassthroughOutputPin.BreakAllLinks(); + for (auto& LinkedToPin : OutputPin.IterateLinkedTo()) + { + PassthroughOutputPin.LinkTo(LinkedToPin); + } + OutputPin.BreakAllLinks(); + } + + if (Compiler.FirstNode == MacroInputOutputNode) + { + Compiler.FirstNode = MacroInputOutputNode->Passthroughs[Compiler.FirstNodePinIndex]; + Compiler.FirstNodePinIndex = 0; // Only one input for passthroughs + } + + MacroInputOutputNode->CheckIsNotLinked(Compiler.ErrorReporter); + Compiler.RemoveNode(MacroInputOutputNode); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.h new file mode 100644 index 00000000..ae2200b4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelMacrosPass.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelGraphInlineMacrosPass +{ + VOXEL_PASS_BODY(FVoxelGraphInlineMacrosPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool bClearMessages); +}; + +struct FVoxelGraphReplaceMacroInputOutputsPass +{ + VOXEL_PASS_BODY(FVoxelGraphReplaceMacroInputOutputsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.cpp new file mode 100644 index 00000000..02dcb4fe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.cpp @@ -0,0 +1,342 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelOptimizationsPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "VoxelNodes/VoxelOptimizationNodes.h" +#include "VoxelNodes/VoxelConstantNodes.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelContext.h" + +void FVoxelOptimizeForPermutationPass::Apply(FVoxelGraphCompiler& Compiler, const FVoxelGraphPermutationArray& Permutation) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + auto* Setter = CastVoxel(Node); + if (Setter && !Permutation.Contains(Setter->OutputIndex)) + { + auto& InputPin = Setter->GetInputPin(0); + auto& OutputPin = Setter->GetOutputPin(0); + check(InputPin.PinCategory == EVoxelPinCategory::Exec && OutputPin.PinCategory == EVoxelPinCategory::Exec); + + if (OutputPin.NumLinkedTo() > 0 && InputPin.NumLinkedTo() > 0) + { + check(OutputPin.NumLinkedTo() == 1); + auto& PinLinkedToOutput = OutputPin.GetLinkedTo(0); + FVoxelGraphCompilerHelpers::MovePin(InputPin, PinLinkedToOutput); + } + + Setter->BreakAllLinks(); + Compiler.RemoveNode(Setter); + } + } +} + +void FVoxelReplaceCompileTimeConstantsPass::Apply(FVoxelGraphCompiler& Compiler, const TMap& Constants) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + auto* Constant = CastVoxel(Node); + if (Constant) + { + auto& OutputPin = Constant->GetOutputPin(0); + + const FString Value = Constants.FindRef(Constant->Name); + + for (auto& LinkedTo : OutputPin.IterateLinkedTo()) + { + LinkedTo.SetDefaultValue(Value); + } + + auto* CompileTimeConstantNode = const_cast(Cast(&Node->Node)); + if (ensure(CompileTimeConstantNode)) + { + CompileTimeConstantNode->Constants.Append(Constants); + CompileTimeConstantNode->Constants.KeySort([](auto& A, auto& B) {return A.FastLess(B); }); + } + + Constant->BreakAllLinks(); + Compiler.RemoveNode(Constant); + } + } +} + +void FVoxelRemoveUnusedExecsPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node != Compiler.FirstNode && Node->IsExecNode() && !FVoxelGraphCompilerHelpers::HasSetterOrFunctionCallSuccessor(Node)) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + bChanged = true; + } + } +} + +void FVoxelRemoveUnusedNodesPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + TSet Nodes; + FVoxelGraphCompilerHelpers::GetAllUsedNodes(Compiler.FirstNode, Nodes); + + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (!Nodes.Contains(Node)) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + bChanged = true; + } + } +} + +void FVoxelDisconnectUnusedFlowMergePinsPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + if (Pin.NumLinkedTo() == 0) + { + for (auto* InputPin : { &Node->GetInputPin(Pin.Index), &Node->GetInputPin(Pin.Index + Node->GetOutputCount()) }) + { + if (InputPin->NumLinkedTo() > 0) + { + InputPin->BreakAllLinks(); + bChanged = true; + } + } + } + } + } + } + } +} + +void FVoxelRemoveFlowMergeWithNoPinsPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + bool bRemove = true; + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec && Pin.NumLinkedTo() != 0) + { + bRemove = false; + break; + } + } + + if (bRemove) + { + bChanged = true; + + auto& ExecOutputPin = Node->GetOutputPin(0); + if (ExecOutputPin.NumLinkedTo() == 0) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } + else + { + auto& PinLinkedToExecOutput = ExecOutputPin.GetLinkedTo(0); + + FVoxelGraphCompilerHelpers::MovePin(Node->GetInputPin(0), PinLinkedToExecOutput); + FVoxelGraphCompilerHelpers::MovePin(Node->GetInputPin(Node->GetOutputCount()), PinLinkedToExecOutput); + + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } + } + } + } +} + +void FVoxelRemoveFlowMergeWithSingleExecPinLinkedPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + const int32 NumPins = Node->GetOutputCount(); + + auto& ExecInputPinA = Node->GetInputPin(0); + auto& ExecInputPinB = Node->GetInputPin(NumPins); + + ensureVoxelGraphExitPass(ExecInputPinA.PinCategory == EVoxelPinCategory::Exec, Node); + ensureVoxelGraphExitPass(ExecInputPinB.PinCategory == EVoxelPinCategory::Exec, Node); + + if (ExecInputPinA.NumLinkedTo() == 0 && ExecInputPinB.NumLinkedTo() == 0) + { + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + bChanged = true; + } + else if (ExecInputPinA.NumLinkedTo() == 0 || ExecInputPinB.NumLinkedTo() == 0) + { + const int32 InputOffset = ExecInputPinA.NumLinkedTo() == 0 ? NumPins : 0; // We want to iterate on the valid pins + for (int32 Index = 0; Index < NumPins; Index++) + { + auto& InputPin = Node->GetInputPin(Index + InputOffset); + auto& OutputPin = Node->GetOutputPin(Index); + ensureVoxelGraphExitPass(InputPin.PinCategory == OutputPin.PinCategory, Node); + FVoxelGraphCompilerHelpers::MovePin(InputPin, OutputPin.GetLinkedToArray()); + } + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + bChanged = true; + } + } + } +} + +void FVoxelRemoveConstantIfsPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + auto& BoolPin = Node->GetInputPin(1); + check(BoolPin.PinCategory == EVoxelPinCategory::Boolean); + if (BoolPin.NumLinkedTo() == 0) + { + const bool bValue = FVoxelPinCategory::ConvertDefaultValue(EVoxelPinCategory::Boolean, BoolPin.GetDefaultValue()).Get(); + + auto& InputPin = Node->GetInputPin(0); + auto& OutputPin = Node->GetOutputPin(bValue ? 0 : 1); + + // Safe because no default value for execs + if (OutputPin.NumLinkedTo() > 0) + { + check(OutputPin.NumLinkedTo() == 1); + auto& LinkedToOutput = OutputPin.GetLinkedTo(0); + FVoxelGraphCompilerHelpers::MovePin(InputPin, LinkedToOutput); + } + + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + + bChanged = true; + } + } + } +} + +void FVoxelRemoveConstantSwitchesPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + auto& BoolPin = Node->GetInputPin(2); + check(BoolPin.PinCategory == EVoxelPinCategory::Boolean); + if (BoolPin.NumLinkedTo() == 0) + { + const bool bValue = FVoxelPinCategory::ConvertDefaultValue(EVoxelPinCategory::Boolean, BoolPin.GetDefaultValue()).Get(); + + auto& InputPin = Node->GetInputPin(bValue ? 0 : 1); + auto& OutputPin = Node->GetOutputPin(0); + + FVoxelGraphCompilerHelpers::MovePin(InputPin, OutputPin.GetLinkedToArray()); + + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + + bChanged = true; + } + } + } +} + +void FVoxelRemoveIfsWithSameTargetPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (Node->IsA()) + { + auto& OutputPinA = Node->GetOutputPin(0); + auto& OutputPinB = Node->GetOutputPin(1); + if (OutputPinA.NumLinkedTo() > 0 && OutputPinB.NumLinkedTo() > 0) + { + check(OutputPinA.NumLinkedTo() == 1 && OutputPinB.NumLinkedTo() == 1); + auto& LinkedToA = OutputPinA.GetLinkedTo(0); + auto& LinkedToB = OutputPinB.GetLinkedTo(0); + if (&LinkedToA == &LinkedToB) + { + auto& InputPin = Node->GetInputPin(0); + for (auto& LinkedToInput : InputPin.IterateLinkedTo()) + { + LinkedToInput.LinkTo(LinkedToA); + } + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + + bChanged = true; + } + } + } + } +} + +void FVoxelReplaceConstantPureNodesPass::Apply(FVoxelGraphCompiler& Compiler, bool& bChanged) +{ + for (auto* Node : Compiler.GetAllNodesCopy()) + { + if (!Node->IsExecNode() && Node->IsPureNode()) + { + bool bRemove = true; + for (auto& Pin : Node->IteratePins()) + { + if (Pin.NumLinkedTo() > 0) + { + bRemove = false; + break; + } + } + if (bRemove) + { + auto ComputeNode = Node->GetComputeNode(); + check(ComputeNode.IsValid()); + if (!ensureVoxelGraphImpl(ComputeNode->Type == EVoxelComputeNodeType::Data, Node, Compiler.ErrorReporter)) + { + return; + } + check(ComputeNode->Type == EVoxelComputeNodeType::Data); + const auto DataComputeNode = StaticCastSharedPtr(ComputeNode); + + FVoxelNodeType InputBuffer[MAX_VOXELNODE_PINS]; + FVoxelNodeType OutputBuffer[MAX_VOXELNODE_PINS]; + for (int32 Index = 0; Index < Node->GetInputCount(); Index++) + { + auto& Pin = Node->GetInputPin(Index); + InputBuffer[Index] = FVoxelPinCategory::ConvertDefaultValue(Pin.PinCategory, Pin.GetDefaultValue()); + } + + DataComputeNode->Compute(InputBuffer, OutputBuffer, FVoxelContext::EmptyContext); + + for (int32 Index = 0; Index < Node->GetOutputCount(); Index++) + { + auto& Pin = Node->GetOutputPin(Index); + FString NewDefaultValue = FVoxelPinCategory::ToString(Pin.PinCategory, OutputBuffer[Index]); + for (auto& LinkedTo : Pin.IterateLinkedTo()) + { + LinkedTo.SetDefaultValue(NewDefaultValue); + } + } + + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + bChanged = true; + } + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.h new file mode 100644 index 00000000..2206db8f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelOptimizationsPass.h @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphOutputs.h" +#include "VoxelCompilationPass.h" + +struct FVoxelOptimizeForPermutationPass +{ + VOXEL_PASS_BODY(FVoxelOptimizeForPermutationPass); + + static void Apply(FVoxelGraphCompiler& Compiler, const FVoxelGraphPermutationArray& Permutation); +}; + +struct FVoxelReplaceCompileTimeConstantsPass +{ + VOXEL_PASS_BODY(FVoxelReplaceCompileTimeConstantsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, const TMap& Constants); +}; + +// Remove all exec nodes with no setter or function call after it +struct FVoxelRemoveUnusedExecsPass +{ + VOXEL_PASS_BODY(FVoxelRemoveUnusedExecsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveUnusedNodesPass +{ + VOXEL_PASS_BODY(FVoxelRemoveUnusedNodesPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelDisconnectUnusedFlowMergePinsPass +{ + VOXEL_PASS_BODY(FVoxelDisconnectUnusedFlowMergePinsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveFlowMergeWithNoPinsPass +{ + VOXEL_PASS_BODY(FVoxelRemoveFlowMergeWithNoPinsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveFlowMergeWithSingleExecPinLinkedPass +{ + VOXEL_PASS_BODY(FVoxelRemoveFlowMergeWithSingleExecPinLinkedPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveConstantIfsPass +{ + VOXEL_PASS_BODY(FVoxelRemoveConstantIfsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveConstantSwitchesPass +{ + VOXEL_PASS_BODY(FVoxelRemoveConstantSwitchesPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelRemoveIfsWithSameTargetPass +{ + VOXEL_PASS_BODY(FVoxelRemoveIfsWithSameTargetPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; + +struct FVoxelReplaceConstantPureNodesPass +{ + VOXEL_PASS_BODY(FVoxelReplaceConstantPureNodesPass); + + static void Apply(FVoxelGraphCompiler& Compiler, bool& bChanged); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.cpp new file mode 100644 index 00000000..ae132328 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelReplaceLocalVariablesPass.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "VoxelNodes/VoxelLocalVariables.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGraphGenerator.h" + +void FVoxelReplaceLocalVariablesPass::Apply(FVoxelGraphCompiler& Compiler) +{ + TMap DeclarationPassthroughs; + TMap UsagePassthroughs; + + for (auto* CompilationNode : Compiler.GetAllNodesCopy()) + { + if (auto* Declaration = CastVoxel(CompilationNode)) + { + auto* Passthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Declaration->GetInputPin(0)); + + auto* VoxelNode = CastChecked(&Declaration->Node); + check(!DeclarationPassthroughs.Contains(VoxelNode)); + DeclarationPassthroughs.Add(VoxelNode, Passthrough); + + Declaration->BreakAllLinks(); + Compiler.RemoveNode(Declaration); + } + else if (auto* Usage = CastVoxel(CompilationNode)) + { + auto* Passthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Usage->GetOutputPin(0)); + Usage->Passthrough = Passthrough; + + auto* UsageDeclaration = CastChecked(&Usage->Node); + check(!UsagePassthroughs.Contains(UsageDeclaration)); + UsagePassthroughs.Add(UsageDeclaration, Passthrough); + + Usage->BreakAllLinks(); + Compiler.RemoveNode(Usage); + } + } + + if (Compiler.ErrorReporter.HasError()) + { + return; + } + + for (auto& It : UsagePassthroughs) + { + auto* Output = It.Key; + auto* OutputPassthrough = It.Value; + auto* Declaration = Output->Declaration; + check(Declaration); + auto* DeclarationPassthrough = DeclarationPassthroughs.FindChecked(Declaration); + + auto& OutputPin = DeclarationPassthrough->GetOutputPin(0); + auto& InputPin = OutputPassthrough->GetInputPin(0); + check(InputPin.NumLinkedTo() == 0); // Only input pin, as output pin can be linked to multiple local variable outputs + OutputPin.LinkTo(InputPin); + } + + if (Compiler.ErrorReporter.HasError()) + { + return; + } + + for (auto* Node : Compiler.GetAllNodes()) + { + check(!CastVoxel(Node) && !CastVoxel(Node)); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.h new file mode 100644 index 00000000..e80b12e2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceLocalVariablesPass.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelReplaceLocalVariablesPass +{ + VOXEL_PASS_BODY(FVoxelReplaceLocalVariablesPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.cpp new file mode 100644 index 00000000..aba48813 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.cpp @@ -0,0 +1,163 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelReplaceSmartMinMaxPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "VoxelNodes/VoxelOptimizationNodes.h" +#include "VoxelNodes/VoxelMathNodes.h" +#include "VoxelNodes/VoxelBinaryNodes.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelNodes/VoxelIfNode.h" + +void FVoxelReplaceSmartMinMaxPass::Apply(FVoxelGraphCompiler& Compiler) +{ + for (auto* NodeIt : Compiler.GetAllNodesCopy()) + { + auto* Node = CastVoxel(NodeIt); + if (!Node) + { + continue; + } + + const int32 NumInputs = Node->GetInputCountWithoutExecs(); + + FVoxelCompilationNode* InputExecPassthrough; + TArray InputPassthroughs; + FVoxelCompilationNode* OutputExecPassthrough; + FVoxelCompilationNode* OutputPassthrough; + + // Create passthroughs + { + InputExecPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Node->GetInputPin(0)); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Float) + { + InputPassthroughs.Add(FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Pin)); + } + } + + OutputExecPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Node->GetOutputPin(0)); + OutputPassthrough = FVoxelGraphCompilerHelpers::AddPassthrough(Compiler, Node->GetOutputPin(1)); + } + + // Add GetRange to each pin + TArray GetRangeNodes; + for (int32 InputIndex = 0; InputIndex < NumInputs; InputIndex++) + { + auto* GetRange = Compiler.AddNode(GetDefault()->GetCompilationNode(), Node); + GetRange->GetInputPin(0).LinkTo(InputPassthroughs[InputIndex]->GetOutputPin(0)); + GetRangeNodes.Add(GetRange); + } + + // Add a Max of all the GetRange.Min + FVoxelCompilationNode* MaxOfRangeMin; + { + UVoxelNode* MaxVoxelNode; + int32 GetRangePinIndex; + if (Node->bIsMin) + { + // We take the min of the max + MaxVoxelNode = NewObject(); + GetRangePinIndex = 1; + } + else + { + // We take the max of the min + MaxVoxelNode = NewObject(); + GetRangePinIndex = 0; + } + MaxVoxelNode->InputPinCount = NumInputs; + + MaxOfRangeMin = Compiler.AddNode(MaxVoxelNode->GetCompilationNode(), Node); + + // Link all the GetRange pins to it + for (int32 InputIndex = 0; InputIndex < NumInputs; InputIndex++) + { + MaxOfRangeMin->GetInputPin(InputIndex).LinkTo(GetRangeNodes[InputIndex]->GetOutputPin(GetRangePinIndex)); + } + } + + // Then for each of the pin, add a if (Range.Max < MaxOfRangeMin) FlowMerge MaxOfRangeMin else FlowMerge Value + auto* LastExecPin = &InputExecPassthrough->GetOutputPin(0); + TArray FlowMergeValueOutputPins; + for (int32 InputIndex = 0; InputIndex < NumInputs; InputIndex++) + { + UVoxelNode* LessThanVoxelNode; + int32 GetRangePinIndex; + if (Node->bIsMin) + { + // We take the min of the max + LessThanVoxelNode = NewObject(); + GetRangePinIndex = 0; + } + else + { + // We take the max of the min + LessThanVoxelNode = NewObject(); + GetRangePinIndex = 1; + } + + auto* LessThan = Compiler.AddNode(LessThanVoxelNode->GetCompilationNode(), Node); + LessThan->GetInputPin(0).LinkTo(GetRangeNodes[InputIndex]->GetOutputPin(GetRangePinIndex)); + LessThan->GetInputPin(1).LinkTo(MaxOfRangeMin->GetOutputPin(0)); + + auto* If = Compiler.AddNode(GetDefault()->GetCompilationNode(), Node); + If->GetInputPin(0).LinkTo(*LastExecPin); + If->GetInputPin(1).LinkTo(LessThan->GetOutputPin(0)); + + auto* FlowMerge = Compiler.AddNode(GetDefault()->GetCompilationNode(), Node); + // if (Range.Max < MaxOfRangeMin) FlowMerge MaxOfRangeMin + FlowMerge->GetInputPin(0).LinkTo(If->GetOutputPin(0)); + FlowMerge->GetInputPin(1).LinkTo(MaxOfRangeMin->GetOutputPin(0)); + // else FlowMerge Value + FlowMerge->GetInputPin(2).LinkTo(If->GetOutputPin(1)); + FlowMerge->GetInputPin(3).LinkTo(InputPassthroughs[InputIndex]->GetOutputPin(0)); + + LastExecPin = &FlowMerge->GetOutputPin(0); + FlowMergeValueOutputPins.Add(&FlowMerge->GetOutputPin(1)); + } + + // Add a function separator (only one at the end, else flow merge are useless) + FVoxelCompilationNode* FunctionSeparator = Compiler.AddNode(GetDefault()->GetCompilationNode(), Node); + LastExecPin->LinkTo(FunctionSeparator->GetInputPin(0)); + LastExecPin = &FunctionSeparator->GetOutputPin(0); + + // Link the last exec pin to the actual exec output + LastExecPin->LinkTo(OutputExecPassthrough->GetInputPin(0)); + + // Do the max of the flow merge values, and link it to the function separator input + { + UVoxelNode* MaxVoxelNode; + if (Node->bIsMin) + { + MaxVoxelNode = NewObject(); + } + else + { + MaxVoxelNode = NewObject(); + } + MaxVoxelNode->InputPinCount = NumInputs; + + auto* Max = Compiler.AddNode(MaxVoxelNode->GetCompilationNode(), Node); + for (int32 InputIndex = 0; InputIndex < NumInputs; InputIndex++) + { + Max->GetInputPin(InputIndex).LinkTo(*FlowMergeValueOutputPins[InputIndex]); + } + Max->GetOutputPin(0).LinkTo(OutputPassthrough->GetInputPin(0)); + } + + // For preview + Node->OutputPassthrough = OutputPassthrough; + + if (Compiler.FirstNode == Node) + { + Compiler.FirstNode = InputExecPassthrough; + ensure(Compiler.FirstNodePinIndex == 0); + } + Node->BreakAllLinks(); + Compiler.RemoveNode(Node); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.h new file mode 100644 index 00000000..472217aa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelReplaceSmartMinMaxPass.h @@ -0,0 +1,14 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelReplaceSmartMinMaxPass +{ + VOXEL_PASS_BODY(FVoxelReplaceSmartMinMaxPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.cpp new file mode 100644 index 00000000..02570b58 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.cpp @@ -0,0 +1,70 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/Passes/VoxelSetIdsPass.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" + +void FVoxelSetPinsIdsPass::Apply(FVoxelGraphCompiler& Compiler, int32& Id) +{ + // First set outputs ids + for (auto* Node : Compiler.GetAllNodes()) + { + int32 OutputIndex = 0; + for (auto& OutputPin : Node->IteratePins()) + { + if (OutputPin.PinCategory != EVoxelPinCategory::Exec) + { + Node->SetOutputId(OutputIndex++, OutputPin.NumLinkedTo() == 0 ? -1 : Id++); // -1 if not used + } + } + } + + // Then set inputs ids + for (auto* Node : Compiler.GetAllNodes()) + { + int32 InputIndex = 0; + for (auto& InputPin : Node->IteratePins()) + { + if (InputPin.PinCategory != EVoxelPinCategory::Exec) + { + int32 InputId; + if (InputPin.NumLinkedTo() == 0) + { + InputId = -1; + } + else + { + check(InputPin.NumLinkedTo() == 1); + auto& OtherOutputPin = InputPin.GetLinkedTo(0); + auto& OtherNode = OtherOutputPin.Node; + int32 RealOutputIndex = 0; + // Skip exec pins + for (int32 OutputIndex = 0; OutputIndex < OtherOutputPin.Index; OutputIndex++) + { + if (OtherNode.GetOutputPin(OutputIndex).PinCategory != EVoxelPinCategory::Exec) + { + RealOutputIndex++; + } + } + InputId = OtherNode.GetOutputId(RealOutputIndex); + } + Node->SetInputId(InputIndex++, InputId); + } + } + } +} + +void FVoxelSetFunctionsIdsPass::Apply(FVoxelGraphCompiler& Compiler) +{ + CastCheckedVoxel(Compiler.FirstNode).FunctionId = 0; + int32 Id = 1; // 0 is for the function corresponding to the first node + for (auto* Node : Compiler.GetAllNodes()) + { + auto* Function = CastVoxel(Node); + if (Function && Function != Compiler.FirstNode) + { + Function->FunctionId = Id++; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.h new file mode 100644 index 00000000..f3c7daae --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/Passes/VoxelSetIdsPass.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelCompilationPass.h" + +struct FVoxelSetPinsIdsPass +{ + VOXEL_PASS_BODY(FVoxelSetPinsIdsPass); + + static void Apply(FVoxelGraphCompiler& Compiler, int32& Id); +}; + +struct FVoxelSetFunctionsIdsPass +{ + VOXEL_PASS_BODY(FVoxelSetFunctionsIdsPass); + + static void Apply(FVoxelGraphCompiler& Compiler); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNode.cpp new file mode 100644 index 00000000..e87ee448 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNode.cpp @@ -0,0 +1,256 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "VoxelNode.h" +#include "VoxelGraphErrorReporter.h" + +FVoxelCompilationPin::FVoxelCompilationPin(FVoxelCompilationNode& Node, int32 Index, EVoxelPinDirection Direction, EVoxelPinCategory PinCategory, const FName& Name) + : Node(Node) + , Index(Index) + , Direction(Direction) + , PinCategory(PinCategory) + , Name(Name) +{ +} + +void FVoxelCompilationPin::Check(FVoxelGraphErrorReporter& ErrorReporter) +{ +#define ensureCheck(Expr) if (!ensure(Expr)) { ErrorReporter.AddMessageToNode(&Node, "Internal pin error: " + FString(#Expr), EVoxelGraphNodeMessageType::Error); } + for (auto& LinkedToPin : LinkedTo) + { + // Only one occurrence + { + const int32 FromStartIndex = LinkedTo.Find(LinkedToPin); + const int32 FromEndIndex = LinkedTo.FindLast(LinkedToPin); + ensureCheck(FromStartIndex == FromEndIndex && FromStartIndex >= 0); + } + { + auto& OtherLinkedTo = LinkedToPin->LinkedTo; + const int32 FromStartIndex = OtherLinkedTo.Find(this); + const int32 FromEndIndex = OtherLinkedTo.FindLast(this); + ensureCheck(FromStartIndex == FromEndIndex && FromStartIndex >= 0); + } + } + + if (PinCategory == EVoxelPinCategory::Exec) + { + if (Direction == EVoxelPinDirection::Output) + { + ensureCheck(NumLinkedTo() <= 1); + } + } + else + { + if (Direction == EVoxelPinDirection::Input) + { + ensureCheck(NumLinkedTo() <= 1); + } + } +#undef ensureCheck +} + +FVoxelCompilationPin FVoxelCompilationPin::Clone(FVoxelCompilationNode& NewNode) const +{ + auto NewPin = FVoxelCompilationPin(NewNode, Index, Direction, PinCategory, Name); + NewPin.DefaultValue = DefaultValue; + NewPin.LinkedTo = LinkedTo; + return NewPin; +} + +FVoxelCompilationNode::FVoxelCompilationNode(EVoxelCompilationNodeType Type, const UVoxelNode& Node) + : Node(Node) + , PrivateType(Type) +{ + SourceNodes.Add(&Node); + + const int32 InputCount = FMath::Clamp(Node.InputPinCount, Node.GetMinInputPins(), Node.GetMaxInputPins()); // To work with default classes too + const int32 OutputCount = Node.GetOutputPinsCount(); + + for (int32 Index = 0; Index < InputCount; Index++) + { + const auto PinCategory = Node.GetInputPinCategory(Index); + const FName PinName = Node.GetInputPinName(Index); + Pins.Emplace(*this, Index, EVoxelPinDirection::Input, PinCategory, PinName); + if (PinCategory != EVoxelPinCategory::Exec) + { + InputIds.Add(-1); + } + } + for (int32 Index = 0; Index < OutputCount; Index++) + { + const auto PinCategory = Node.GetOutputPinCategory(Index); + const FName PinName = Node.GetOutputPinName(Index); + Pins.Emplace(*this, Index, EVoxelPinDirection::Output, PinCategory, PinName); + if (PinCategory != EVoxelPinCategory::Exec) + { + OutputIds.Add(-1); + } + } + RebuildPinsArray(); +} + +FVoxelCompilationNode::FVoxelCompilationNode( + EVoxelCompilationNodeType Type, const UVoxelNode& Node, + const TArray& InputCategories, + const TArray& OutputCategories) + : Node(Node) + , PrivateType(Type) +{ + SourceNodes.Add(&Node); + + const int32 InputCount = InputCategories.Num(); + const int32 OutputCount = OutputCategories.Num(); + + for (int32 Index = 0; Index < InputCount; Index++) + { + const auto PinCategory = InputCategories[Index]; + const FName PinName = "INTERNAL"; + Pins.Emplace(*this, Index, EVoxelPinDirection::Input, PinCategory, PinName); + if (PinCategory != EVoxelPinCategory::Exec) + { + InputIds.Add(-1); + } + } + for (int32 Index = 0; Index < OutputCount; Index++) + { + const auto PinCategory = OutputCategories[Index]; + const FName PinName = "INTERNAL"; + Pins.Emplace(*this, Index, EVoxelPinDirection::Output, PinCategory, PinName); + if (PinCategory != EVoxelPinCategory::Exec) + { + OutputIds.Add(-1); + } + } + RebuildPinsArray(); +} + +TArray FVoxelCompilationNode::GetInputPinCategories() const +{ + TArray Result; + for (auto& Pin : IteratePins()) + { + Result.Add(Pin.PinCategory); + } + return Result; +} + +TArray FVoxelCompilationNode::GetOutputPinCategories() const +{ + TArray Result; + for (auto& Pin : IteratePins()) + { + Result.Add(Pin.PinCategory); + } + return Result; +} + +FString FVoxelCompilationNode::GetPrettyName() const +{ + if (SourceNodes.Num() == 0) + { + return "Class: " + GetClassName(); + } + else + { + FString Result = SourceNodes.Last()->GetTitle().ToString(); + for (int32 Index = SourceNodes.Num() - 2; Index >= 0; Index--) + { + Result += "." + SourceNodes[Index]->GetTitle().ToString(); + } + return Result; + } +} + +bool FVoxelCompilationNode::IsSeedNode() const +{ + for (auto& Pin : IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Seed) + { + return true; + } + } + return false; +} + +void FVoxelCompilationNode::BreakAllLinks() +{ + FVoxelGraphCompilerHelpers::BreakNodeLinks(*this);; +} + +bool FVoxelCompilationNode::IsLinkedTo(const FVoxelCompilationNode* OtherNode) const +{ + for (auto& Pin : IteratePins()) + { + for (auto& LinkedTo : Pin.IterateLinkedTo()) + { + if (&LinkedTo.Node == OtherNode) + { + return true; + } + } + } + return false; +} + +void FVoxelCompilationNode::CheckIsNotLinked(FVoxelGraphErrorReporter& ErrorReporter) const +{ + for (auto& Pin : IteratePins()) + { + ensureVoxelGraph(Pin.NumLinkedTo() == 0, this); + } +} + +TVoxelSharedPtr FVoxelCompilationNode::GetComputeNode() const +{ + return Node.GetComputeNode(*this); +} + +void FVoxelCompilationNode::Check(FVoxelGraphErrorReporter& ErrorReporter) const +{ + +} + +void FVoxelCompilationNode::CopyPropertiesToNewNode(const TSharedRef& NewNode, bool bFixLinks) const +{ + NewNode->Dependencies = Dependencies; + NewNode->SourceNodes = SourceNodes; + ensure(NewNode->Pins.Num() == 0); + for (auto& Pin : Pins) + { + NewNode->Pins.Add(Pin.Clone(*NewNode)); + } + NewNode->RebuildPinsArray(); + NewNode->InputIds = InputIds; + NewNode->OutputIds = OutputIds; + + if (bFixLinks) + { + for (auto& Pin : NewNode->Pins) + { + for (auto& LinkedToPin : Pin.LinkedTo) + { + LinkedToPin->LinkedTo.Add(&Pin); + } + } + } +} + +void FVoxelCompilationNode::RebuildPinsArray() +{ + CachedInputPins.Empty(); + CachedOutputPins.Empty(); + for (auto& Pin : Pins) + { + if (Pin.Direction == EVoxelPinDirection::Input) + { + CachedInputPins.Add(&Pin); + } + else + { + CachedOutputPins.Add(&Pin); + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.cpp new file mode 100644 index 00000000..aba3d49a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.cpp @@ -0,0 +1,245 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelCompilationNodeTree.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelDefaultComputeNodes.h" + +TSharedRef FVoxelCompilationNodeTree::Create(FVoxelCompilationNode* FirstNode) +{ + VOXEL_FUNCTION_COUNTER(); + + TSharedRef Result = MakeShareable(new FVoxelCompilationNodeTree(FirstNode)); + Result->RemoveAlreadyComputedNodes(); + Result->BakeSortedDataNodes(); + return Result; +} + +FVoxelCompilationNodeTree::FVoxelCompilationNodeTree(FVoxelCompilationNode* ExecNode) + : ExecNode(ExecNode) +{ + check(ExecNode); + check(ExecNode->IsExecNode()); + + FVoxelGraphCompilerHelpers::AddPreviousNodesToSet(ExecNode, DataNodesSet); + DataNodesSet.Remove(ExecNode); + + if (!ExecNode->IsA()) + { + for (auto& Pin : ExecNode->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + if (Pin.NumLinkedTo() > 0) + { + check(Pin.NumLinkedTo() == 1); + Children.Emplace(&Pin.GetLinkedTo(0).Node); + } + else + { + // Else the children choice will be wrong + check(ExecNode->GetExecOutputCount() == 1); + } + } + } + // Find the nodes computed by all branches, except if we're an Init as they might use our not computed yet data outputs + if (!ExecNode->IsA()) + { + for (auto* DataNode : FVoxelGraphCompilerHelpers::GetAlwaysComputedNodes(ExecNode)) + { + check(!DataNode->IsExecNode()); + DataNodesSet.Add(DataNode); + } + } + } +} + +void FVoxelCompilationNodeTree::RemoveAlreadyComputedNodes(const TSet& AlreadyComputedNodes) +{ + // Remove all nodes already in parents + DataNodesSet = DataNodesSet.Difference(AlreadyComputedNodes); + + // Propagate to childs + TSet AlreadyComputedNodesCopy = AlreadyComputedNodes; + AlreadyComputedNodesCopy.Append(DataNodesSet); + for (auto& Child : Children) + { + Child.RemoveAlreadyComputedNodes(AlreadyComputedNodesCopy); + } +} + +void FVoxelCompilationNodeTree::BakeSortedDataNodes() +{ + check(SortedDataNodes.Num() == 0); + SortedDataNodes = DataNodesSet.Array(); + FVoxelGraphCompilerHelpers::SortNodes(SortedDataNodes); + + for (auto& Child : Children) + { + Child.BakeSortedDataNodes(); + } +} + +inline bool ShouldComputeDataNode(EVoxelAxisDependencies NodeDependencies, EVoxelFunctionAxisDependencies FunctionDependencies) +{ + switch (NodeDependencies) + { + case EVoxelAxisDependencies::X: + return + FunctionDependencies == EVoxelFunctionAxisDependencies::X || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; + case EVoxelAxisDependencies::XY: + return + FunctionDependencies == EVoxelFunctionAxisDependencies::XYWithCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; + case EVoxelAxisDependencies::XYZ: + return + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; + case EVoxelAxisDependencies::Constant: + default: + check(false); + return false; + } +} + +inline bool AreExecNodeDependenciesInferiorOrEqual(EVoxelAxisDependencies NodeDependencies, EVoxelFunctionAxisDependencies FunctionDependencies) +{ + switch (NodeDependencies) + { + case EVoxelAxisDependencies::Constant: + case EVoxelAxisDependencies::X: + return true; + case EVoxelAxisDependencies::XY: + return + FunctionDependencies == EVoxelFunctionAxisDependencies::XYWithCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; + case EVoxelAxisDependencies::XYZ: + return + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithCache || + FunctionDependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; + default: + check(false); + return false; + } +} + +void FVoxelCompilationNodeTree::ConvertToComputeNodeTree( + EVoxelFunctionAxisDependencies FunctionDependencies, + FVoxelComputeNodeTree& OutTree, + TArray& OutFunctionCallsToLink, + TSet& OutUsedNodes, + FVoxelCreatedComputeNodes& CreatedNodes) const +{ + // Data nodes + for (auto& DataNode : SortedDataNodes) + { + check(!DataNode->IsExecNode()); + if (DataNode->IsSeedNode() || ShouldComputeDataNode(FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(DataNode->Dependencies), FunctionDependencies)) + { + OutUsedNodes.Add(DataNode); + + auto ComputeNode = CreatedNodes.GetComputeNode(*DataNode); + + if (ComputeNode->Type == EVoxelComputeNodeType::Data) + { + check(!DataNode->IsSeedNode()); + + auto DataComputeNode = StaticCastSharedRef(ComputeNode); + OutTree.DataNodes.Add(&DataComputeNode.Get()); + OutTree.DataNodesRefs.Add(DataComputeNode); + } + else + { + check(DataNode->IsSeedNode()); + check(ComputeNode->Type == EVoxelComputeNodeType::Seed); + + auto SeedComputeNode = StaticCastSharedRef(ComputeNode); + OutTree.SeedNodes.Add(&SeedComputeNode.Get()); + OutTree.SeedNodesRefs.Add(SeedComputeNode); + } + } + } + + // Exec node + if (ExecNode) + { + const EVoxelAxisDependencies NodeDependencies = FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(ExecNode->Dependencies); + + bool bCanComputeExecNode; + switch (ExecNode->GetPrivateType()) + { + case EVoxelCompilationNodeType::Passthrough: + case EVoxelCompilationNodeType::FunctionInit: + case EVoxelCompilationNodeType::Setter: + { + bCanComputeExecNode = true; + break; + } + case EVoxelCompilationNodeType::If: + case EVoxelCompilationNodeType::FunctionCall: + { + bCanComputeExecNode = AreExecNodeDependenciesInferiorOrEqual(NodeDependencies, FunctionDependencies); + break; + } + default: + bCanComputeExecNode = false; + check(false); + } + + if (bCanComputeExecNode) + { + OutUsedNodes.Add(ExecNode); + + TVoxelSharedPtr ComputeNode; + if (ExecNode->IsA()) + { + ComputeNode = ExecNode->GetComputeNode(); + } + else if (ExecNode->IsA()) + { + check(Children.Num() == 0); + ComputeNode = CastCheckedVoxel(ExecNode).GetComputeNode(FunctionDependencies); + } + else if (ExecNode->IsA()) + { + if (FunctionDependencies != EVoxelFunctionAxisDependencies::XYZWithCache && + FunctionDependencies != EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + ComputeNode = FVoxelGraphCompilerHelpers::GetPassthroughNode(EVoxelPinCategory::Exec, ExecNode->SourceNodes)->GetComputeNode(); + } + else + { + ComputeNode = CreatedNodes.GetComputeNode(*ExecNode); + } + } + else + { + ComputeNode = CreatedNodes.GetComputeNode(*ExecNode); + } + check(ComputeNode.IsValid()); + check(ComputeNode->Type == EVoxelComputeNodeType::Exec); + + const auto ExecComputeNode = StaticCastSharedPtr(ComputeNode); + OutTree.ExecNode = ExecComputeNode.Get(); + OutTree.ExecNodeRef = ExecComputeNode; + + if (OutTree.ExecNode->ExecType == EVoxelComputeNodeExecType::FunctionCall) + { + OutFunctionCallsToLink.Add(static_cast(OutTree.ExecNode)); + } + + for (auto& Child : Children) + { + Child.ConvertToComputeNodeTree(FunctionDependencies, *new (OutTree.Children) FVoxelComputeNodeTree(), OutFunctionCallsToLink, OutUsedNodes, CreatedNodes); + } + } + } +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.h new file mode 100644 index 00000000..31d27ed4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelCompilationNodeTree.h @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAxisDependencies.h" + +class FVoxelCompilationNode; +class FVoxelComputeNode; +class FVoxelFunctionCallComputeNode; +class FVoxelComputeNodeTree; +class FVoxelCreatedComputeNodes; + +class FVoxelCompilationNodeTree +{ +public: + FVoxelCompilationNodeTree() = default; + static TSharedRef Create(FVoxelCompilationNode* FirstNode); + + void ConvertToComputeNodeTree( + EVoxelFunctionAxisDependencies FunctionDependencies, + FVoxelComputeNodeTree& OutTree, + TArray& OutFunctionCallsToLink, + TSet& OutUsedNodes, + FVoxelCreatedComputeNodes& CreatedNodes) const; + +private: + TArray Children; + + TSet DataNodesSet; + TArray SortedDataNodes; + FVoxelCompilationNode* ExecNode = nullptr; + + FVoxelCompilationNodeTree(FVoxelCompilationNode* ExecNode); + void RemoveAlreadyComputedNodes(const TSet& AlreadyComputedNodes = {}); + void BakeSortedDataNodes(); + + friend class TArray; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelDefaultCompilationNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelDefaultCompilationNodes.cpp new file mode 100644 index 00000000..d2eb4be7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelDefaultCompilationNodes.cpp @@ -0,0 +1,86 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Runtime/VoxelDefaultComputeNodes.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" + +FVoxelFunctionInitCompilationNode::FVoxelFunctionInitCompilationNode(const FVoxelFunctionSeparatorCompilationNode& Separator) + : TVoxelCompilationNode(Separator.Node, TArray(), Separator.GetOutputPinCategories()) +{ + Dependencies = Separator.Dependencies; + SourceNodes = Separator.SourceNodes; + FunctionId = Separator.FunctionId; + + for (int32 Index = 0; Index < GetOutputCountWithoutExecs() ; Index++) + { + SetOutputId(Index, Separator.GetOutputId(Index)); + } +} + +TVoxelSharedPtr FVoxelFunctionInitCompilationNode::GetComputeNode() const +{ + return MakeVoxelShared(Node, *this); +} + +FVoxelFunctionCallCompilationNode::FVoxelFunctionCallCompilationNode(const FVoxelFunctionSeparatorCompilationNode& Separator) + : TVoxelCompilationNode(Separator.Node, Separator.GetInputPinCategories(), TArray()) +{ + Dependencies = Separator.Dependencies; + SourceNodes = Separator.SourceNodes; + FunctionId = Separator.FunctionId; + + for (int32 Index = 0; Index < GetInputCountWithoutExecs(); Index++) + { + SetInputId(Index, Separator.GetInputId(Index)); + } +} + +inline EVoxelFunctionAxisDependencies GetCalledFunctionDependencies(EVoxelAxisDependencies NodeDependencies, EVoxelFunctionAxisDependencies FunctionDependencies) +{ + switch (NodeDependencies) + { + case EVoxelAxisDependencies::Constant: + case EVoxelAxisDependencies::X: + return FunctionDependencies; + case EVoxelAxisDependencies::XY: + switch (FunctionDependencies) + { + case EVoxelFunctionAxisDependencies::XYWithCache: + case EVoxelFunctionAxisDependencies::XYWithoutCache: + return EVoxelFunctionAxisDependencies::XYWithoutCache; + case EVoxelFunctionAxisDependencies::XYZWithCache: + return EVoxelFunctionAxisDependencies::XYZWithCache; + case EVoxelFunctionAxisDependencies::XYZWithoutCache: + return EVoxelFunctionAxisDependencies::XYZWithoutCache; + // Can't have X if we are XY, must fail AreNodeDependenciesInferiorOrEqual + case EVoxelFunctionAxisDependencies::X: + default: + check(false); + return EVoxelFunctionAxisDependencies::X; + } + case EVoxelAxisDependencies::XYZ: + switch (FunctionDependencies) + { + case EVoxelFunctionAxisDependencies::XYZWithCache: + case EVoxelFunctionAxisDependencies::XYZWithoutCache: + return EVoxelFunctionAxisDependencies::XYZWithoutCache; + // Can't have X or XY if we are XYZ, must fail AreNodeDependenciesInferiorOrEqual + case EVoxelFunctionAxisDependencies::X: + case EVoxelFunctionAxisDependencies::XYWithCache: + case EVoxelFunctionAxisDependencies::XYWithoutCache: + default: + check(false); + return EVoxelFunctionAxisDependencies::X; + } + default: + check(false); + return EVoxelFunctionAxisDependencies::X; + } +} + +TVoxelSharedPtr FVoxelFunctionCallCompilationNode::GetComputeNode(EVoxelFunctionAxisDependencies FunctionDependencies) const +{ + EVoxelFunctionAxisDependencies CalledFunctionDependencies = GetCalledFunctionDependencies(FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(Dependencies), FunctionDependencies); + return MakeVoxelShared(FunctionId, CalledFunctionDependencies, Node, *this); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompiler.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompiler.cpp new file mode 100644 index 00000000..b43355f2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompiler.cpp @@ -0,0 +1,261 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGraphGlobals.h" + +FVoxelGraphCompiler::FVoxelGraphCompiler(const TSharedRef& ErrorReporter) + : ErrorReporter(*ErrorReporter) + , ErrorReporterRef(ErrorReporter) +{ +} + +FVoxelGraphCompiler::FVoxelGraphCompiler(UVoxelGraphGenerator* Graph) + : FVoxelGraphCompiler(MakeShared(Graph)) +{ +} + +TSharedRef FVoxelGraphCompiler::Clone(const FString& ErrorPrefix, TMap& OutOldNodesToNewNodes) const +{ + TSharedRef Result = MakeShareable(new FVoxelGraphCompiler(MakeShared(ErrorReporter, ErrorPrefix))); + + TMap OldPinsToNewPins; + FVoxelGraphCompilerHelpers::DuplicateNodes(*Result, Nodes, OldPinsToNewPins, OutOldNodesToNewNodes); + Result->FirstNode = FirstNode ? OutOldNodesToNewNodes[FirstNode] : nullptr; + Result->FirstNodePinIndex = FirstNodePinIndex; + + return Result; +} + +TSharedRef FVoxelGraphCompiler::Clone(const FString& ErrorPrefix) const +{ + TMap OldNodesToNewNodes; + return Clone(ErrorPrefix, OldNodesToNewNodes); +} + +TMap FVoxelGraphCompiler::InitFromNodes(const TArray& InNodes, UVoxelNode* InFirstNode, int32 InFirstNodePinIndex) +{ + TMap Map; + + // Convert nodes + for (auto& Node : InNodes) + { + check(Node); + Map.Add(Node, AddNode(Node)); + } + if (ErrorReporter.HasError()) + { + return {}; + } + + // Set default values + for (auto& Node : InNodes) + { + FVoxelCompilationNode* CompilationNode = Map.FindChecked(Node); + for (int32 I = 0; I < Node->InputPins.Num(); I++) + { + CompilationNode->GetInputPin(I).SetDefaultValue(Node->InputPins[I].DefaultValue); + } + } + + // Fix links + for (auto& Node : InNodes) + { + FVoxelCompilationNode* CompilationNode = Map.FindChecked(Node); + + for (int32 PinIndex = 0; PinIndex < Node->InputPins.Num(); PinIndex++) + { + auto& NodeInputPin = Node->InputPins[PinIndex]; + auto& CompilationNodeInputPin = CompilationNode->GetInputPin(PinIndex); + + check(NodeInputPin.OtherNodes.Num() == NodeInputPin.OtherPinIds.Num()); + for (int32 I = 0; I < NodeInputPin.OtherNodes.Num(); I++) + { + auto OtherNode = NodeInputPin.OtherNodes[I]; + auto OtherPinId = NodeInputPin.OtherPinIds[I]; + if (!OtherNode) + { + ErrorReporter.AddError("Invalid node, please delete the node or use Voxel/Recreate Nodes"); + ErrorReporter.AddMessageToNode(CompilationNode, "An invalid node is linked to this node", EVoxelGraphNodeMessageType::Error); + return {}; + } + + const int32 OtherPinIndex = OtherNode->GetOutputPinIndex(OtherPinId); + check(OtherPinIndex >= 0); + + if (!Map.Contains(OtherNode)) + { + ErrorReporter.AddMessageToNode(OtherNode, "Internal error: Node not found", EVoxelGraphNodeMessageType::Error); + return {}; + } + FVoxelCompilationNode* OtherCompilationNode = Map.FindChecked(OtherNode); + auto& OtherPin = OtherCompilationNode->GetOutputPin(OtherPinIndex); + + if (!CompilationNodeInputPin.IsLinkedTo(OtherPin)) + { + if (CompilationNodeInputPin.PinCategory != OtherPin.PinCategory) + { + ErrorReporter.AddMessageToNode(OtherNode, "Pins with different categories are linked together", EVoxelGraphNodeMessageType::Error); + return {}; + } + CompilationNodeInputPin.LinkTo(OtherPin); + } + } + } + + for (int32 PinIndex = 0; PinIndex < Node->OutputPins.Num(); PinIndex++) + { + auto& NodeOutputPin = Node->OutputPins[PinIndex]; + auto& CompilationNodeOutputPin = CompilationNode->GetOutputPin(PinIndex); + + check(NodeOutputPin.OtherNodes.Num() == NodeOutputPin.OtherPinIds.Num()); + for (int32 I = 0; I < NodeOutputPin.OtherNodes.Num(); I++) + { + auto OtherNode = NodeOutputPin.OtherNodes[I]; + auto OtherPinId = NodeOutputPin.OtherPinIds[I]; + if (!OtherNode) + { + ErrorReporter.AddError("Invalid node, please delete the node or use Voxel/Recreate Nodes"); + ErrorReporter.AddMessageToNode(CompilationNode, "An invalid node is linked to this node", EVoxelGraphNodeMessageType::Error); + return {}; + } + + const int32 OtherPinIndex = OtherNode->GetInputPinIndex(OtherPinId); + check(OtherPinIndex >= 0); + + if (!Map.Contains(OtherNode)) + { + ErrorReporter.AddMessageToNode(OtherNode, "Internal error: Node not found", EVoxelGraphNodeMessageType::Error); + return {}; + } + FVoxelCompilationNode* OtherCompilationNode = Map.FindChecked(OtherNode); + auto& OtherPin = OtherCompilationNode->GetInputPin(OtherPinIndex); + + if (!CompilationNodeOutputPin.IsLinkedTo(OtherPin)) + { + if (CompilationNodeOutputPin.PinCategory != OtherPin.PinCategory) + { + ErrorReporter.AddMessageToNode(OtherNode, "Pins with different categories are linked together", EVoxelGraphNodeMessageType::Error); + return {}; + } + CompilationNodeOutputPin.LinkTo(OtherPin); + } + } + } + } + + // Set first node + FirstNode = InFirstNode ? Map.FindChecked(InFirstNode) : nullptr; + FirstNodePinIndex = InFirstNodePinIndex; + + // Check validity + Check(); + + return Map; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCompilationNode* FVoxelGraphCompiler::AddNode(UVoxelNode* Node) +{ + check(IsValid(Node)); + Node->LogErrors(ErrorReporter); + if (!ErrorReporter.HasError()) + { + return AddNode(Node->GetCompilationNode()); + } + else + { + return nullptr; + } +} + +FVoxelCompilationNode* FVoxelGraphCompiler::AddNode(const TSharedPtr& Ref, FVoxelCompilationNode* SourceNode) +{ + if (SourceNode) + { + Ref->SourceNodes.Append(SourceNode->SourceNodes); + } + + NodesRefs.Add(Ref); + Nodes.Add(Ref.Get()); + return Ref.Get(); +} + +void FVoxelGraphCompiler::RemoveNode(FVoxelCompilationNode* Node) +{ + ensureVoxelGraph(FirstNode != Node, Node); // Can't remove first node + Nodes.Remove(Node); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphCompiler::Check() const +{ + VOXEL_FUNCTION_COUNTER(); + + if (ErrorReporter.HasError()) + { + return; + } + for (auto& Node : Nodes) + { + check(Node->GetInputCount() < MAX_VOXELNODE_PINS); + check(Node->GetOutputCount() < MAX_VOXELNODE_PINS); + Node->Check(ErrorReporter); + if (Node->IsSeedNode() && + !Node->IsA() && + !Node->IsA()) + { + ensureVoxelGraph(FVoxelAxisDependencies::IsConstant(Node->Dependencies), Node); + for (auto& Pin : Node->IteratePins()) + { + ensureVoxelGraph(Pin.PinCategory == EVoxelPinCategory::Seed, Node); + } + } + for (auto& Pin : Node->IteratePins()) + { + Pin.Check(ErrorReporter); + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + ensureVoxelGraph(Nodes.Contains(&LinkedToPin.Node), Node); + } + if (Pin.Direction == EVoxelPinDirection::Output && Node->IsSeedNode() && !Node->IsA() && !Node->IsA()) + { + ensureVoxelGraph(Pin.PinCategory == EVoxelPinCategory::Seed, Node); + } + } + } + ensureVoxelGraph(!FirstNode || Nodes.Contains(FirstNode), nullptr); + ensureVoxelGraph(!FirstNode || FirstNode->IsA() || FirstNode->GetInputPin(FirstNodePinIndex).PinCategory == EVoxelPinCategory::Exec, nullptr); +} + +void FVoxelGraphCompiler::AppendAndClear(FVoxelGraphCompiler& Other) +{ + NodesRefs.Append(Other.NodesRefs); + Nodes.Append(Other.Nodes); + + Other.NodesRefs.Reset(); + Other.Nodes.Reset(); +} + +int32 FVoxelGraphCompiler::CompilationId = 0; + +TVoxelSharedRef FVoxelCreatedComputeNodes::GetComputeNode(const FVoxelCompilationNode& Node) +{ + check(!Node.IsA() && !Node.IsA()); + + auto& Result = Map.FindOrAdd(&Node); + if (!Result.IsValid()) + { + Result = Node.GetComputeNode(); + } + return Result.ToSharedRef(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.cpp new file mode 100644 index 00000000..e4812841 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.cpp @@ -0,0 +1,779 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelGraphCompilerHelpers.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelNodes/VoxelNodeHelperMacros.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelNode.h" + +namespace FVoxelExecPassthroughComputeNode +{ + using FLocalVoxelComputeNode = FVoxelPassthroughComputeNode; +} +namespace FVoxelBoolPassthroughComputeNode +{ + GENERATED_COMPUTENODE + ( + DEFINE_INPUTS(bool), + DEFINE_OUTPUTS(bool), + _O0 = _I0; + ) +} +namespace FVoxelIntPassthroughComputeNode +{ + GENERATED_COMPUTENODE + ( + DEFINE_INPUTS(int32), + DEFINE_OUTPUTS(int32), + _O0 = _I0; + ) +}; +namespace FVoxelFloatPassthroughComputeNode +{ + GENERATED_COMPUTENODE + ( + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = _I0; + ) +}; +namespace FVoxelMaterialPassthroughComputeNode +{ + GENERATED_COMPUTENODE + ( + DEFINE_INPUTS(FVoxelMaterial), + DEFINE_OUTPUTS(FVoxelMaterial), + _O0 = _I0; + ) +}; +namespace FVoxelSeedPassthroughComputeNode +{ + class FLocalVoxelComputeNode : public FVoxelSeedComputeNode + { + public: + using FVoxelSeedComputeNode::FVoxelSeedComputeNode; + + void Init(FVoxelGraphSeed Inputs[], FVoxelGraphSeed Outputs[], const FVoxelGeneratorInit& InitStruct) override + { + Outputs[0] = Inputs[0]; + } + void InitCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Inputs[0] + ";"); + } + }; +}; + +template +class TVoxelPassthroughCompilationNode : public TVoxelCompilationNode, FVoxelPassthroughCompilationNode> +{ +public: + using Parent = TVoxelCompilationNode, FVoxelPassthroughCompilationNode>; + using Parent::Parent; + + TVoxelPassthroughCompilationNode(const TArray& InSourceNodes) + : Parent(*GetDefault(), TArray({ Category }), TArray({ Category })) + { + this->SourceNodes = InSourceNodes; + } + virtual TVoxelSharedPtr GetComputeNode() const override + { + return MakeVoxelShared(this->Node, *this); + } +}; + +using FVoxelExecPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelBoolPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelIntPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelFloatPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelMaterialPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelColorPassthrough = TVoxelPassthroughCompilationNode; +using FVoxelSeedPassthrough = TVoxelPassthroughCompilationNode; + +TSharedRef FVoxelGraphCompilerHelpers::GetPassthroughNode(EVoxelPinCategory Category, const TArray& SourceNodes) +{ + switch (Category) + { + case EVoxelPinCategory::Exec: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Boolean: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Int: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Float: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Material: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Color: + return MakeShared(SourceNodes); + case EVoxelPinCategory::Seed: + return MakeShared(SourceNodes); + default: + check(false); + return TSharedPtr().ToSharedRef(); + } +} + +FVoxelCompilationNode* FVoxelGraphCompilerHelpers::AddPassthrough(FVoxelGraphCompiler& Compiler, FVoxelCompilationPin& Pin, FVoxelCompilationNode* SourceNode) +{ + if (!SourceNode) + { + SourceNode = &Pin.Node; + } + + const auto Passthrough = GetPassthroughNode(Pin.PinCategory, SourceNode->SourceNodes); + Compiler.AddNode(Passthrough); + + auto& PassthroughInputPin = Passthrough->GetInputPin(0); + auto& PassthroughOutputPin = Passthrough->GetOutputPin(0); + if (Pin.Direction == EVoxelPinDirection::Input) + { + PassthroughInputPin.SetDefaultValue(Pin.GetDefaultValue()); + } + + auto& PassthroughPinConnectedToPin = Pin.Direction == EVoxelPinDirection::Input ? PassthroughOutputPin : PassthroughInputPin; + auto& PassthroughPinNotConnectedToPin = Pin.Direction == EVoxelPinDirection::Output ? PassthroughOutputPin : PassthroughInputPin; + + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + LinkedToPin.LinkTo(PassthroughPinNotConnectedToPin); + } + Pin.BreakAllLinks(); + + PassthroughPinConnectedToPin.LinkTo(Pin); + + return &Passthrough.Get(); +} + +struct FVoxelCompilationPinLink +{ + FVoxelCompilationPin* From; + FVoxelCompilationPin* To; + + FVoxelCompilationPinLink(FVoxelCompilationPin* From, FVoxelCompilationPin* To) + : From(From) + , To(To) + { + } +}; + +void DuplicateNodeAndQueueLinks( + FVoxelGraphCompiler& Compiler, + FVoxelCompilationNode* Node, + TArray& OutLinksToCreate, + TMap& OldPinsToNewPins, + TMap& OldNodesToNewNodes) +{ + auto* NewNode = Compiler.AddNode(Node->Clone(true)); + OldNodesToNewNodes.Add(Node, NewNode); + + auto** ParentParent = Compiler.Parents.Find(Node); + auto* Parent = ParentParent ? *ParentParent : Node; + Compiler.Parents.Add(NewNode, Parent); + Compiler.DuplicateCounts.FindOrAdd(Parent)++; + for (auto& It : Compiler.DuplicateCounts) + { + if (It.Key && It.Value > 256) + { + if (!Compiler.ErrorReporter.HasError()) + { + Compiler.ErrorReporter.AddError("You need to add a function separator after your Select, FastLerp or FlowMerge node, and make all the data links go through it"); + } + Compiler.ErrorReporter.AddMessageToNode(It.Key, FString::Printf(TEXT("duplicated %d times: function separator needed"), It.Value), EVoxelGraphNodeMessageType::Error); + } + } + + for (auto& Pin : NewNode->IteratePins()) + { + for (auto& LinkedTo : Pin.IterateLinkedTo()) + { + // From MUST be new pin + OutLinksToCreate.Emplace(&Pin, &LinkedTo); + } + } + + for (int32 Index = 0; Index < NewNode->GetNumPins(); Index++) + { + auto& NewPin = NewNode->GetPin(Index); + auto& OldPin = Node->GetPin(Index); + + OldPinsToNewPins.Add(&OldPin, &NewPin); + } + + NewNode->BreakAllLinks(); +} + +void FVoxelGraphCompilerHelpers::DuplicateNodes( + FVoxelGraphCompiler& Compiler, + const TSet& Nodes, + TMap& OutOldPinsToNewPins, + TMap& OldNodesToNewNodes) +{ + TArray LinksToCreate; + + // Duplicate all nodes + for (auto& Node : Nodes) + { + DuplicateNodeAndQueueLinks(Compiler, Node, LinksToCreate, OutOldPinsToNewPins, OldNodesToNewNodes); + if (Compiler.ErrorReporter.HasError()) + { + return; + } + } + + // Link them back together + for (auto& LinkToCreate : LinksToCreate) + { + auto* From = LinkToCreate.From; + auto* To = LinkToCreate.To; + + check(!OutOldPinsToNewPins.Contains(From)); + + auto** NewTo = OutOldPinsToNewPins.Find(To); + if (NewTo) + { + To = *NewTo; + } + + if (!From->IsLinkedTo(*To)) + { + From->LinkTo(*To); + } + } +} + +inline void GetSortedExecNodesImpl(FVoxelCompilationNode* Node, TArray& Nodes, TSet& VisitedNodes) +{ + if (!VisitedNodes.Contains(Node)) + { + VisitedNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetSortedExecNodesImpl(&LinkedToPin.Node, Nodes, VisitedNodes); + } + } + } + + Nodes.Add(Node); + } +} + +void FVoxelGraphCompilerHelpers::GetSortedExecNodes(FVoxelCompilationNode* FirstNode, TArray& OutNodes) +{ + TSet VisitedNodes; + GetSortedExecNodesImpl(FirstNode, OutNodes, VisitedNodes); +} + +void FVoxelGraphCompilerHelpers::GetAllSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllSuccessors(&LinkedToPin.Node, OutNodes); + } + } +} + +void FVoxelGraphCompilerHelpers::GetAllPredecessors(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllPredecessors(&LinkedToPin.Node, OutNodes); + } + } +} + +void FVoxelGraphCompilerHelpers::GetAllDataSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllDataSuccessors(&LinkedToPin.Node, OutNodes); + } + } + } +} + +void FVoxelGraphCompilerHelpers::GetAllExecSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllExecSuccessors(&LinkedToPin.Node, OutNodes); + } + } + } +} + +inline bool HasSetterOrFunctionCallSuccessorImpl(FVoxelCompilationNode* Node, TSet& VisitedNodes) +{ + if (VisitedNodes.Contains(Node)) + { + return false; + } + VisitedNodes.Add(Node); + + if (Node->IsA() || Node->IsA()) + { + return true; + } + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + if (HasSetterOrFunctionCallSuccessorImpl(&LinkedToPin.Node, VisitedNodes)) + { + return true; + } + } + } + } + + return false; +} + +bool FVoxelGraphCompilerHelpers::HasSetterOrFunctionCallSuccessor(FVoxelCompilationNode* Node) +{ + check(Node); + TSet VisitedNodes; + return HasSetterOrFunctionCallSuccessorImpl(Node, VisitedNodes); +} + +void FVoxelGraphCompilerHelpers::GetAllUsedNodes(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + check(Node); + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllUsedNodes(&LinkedToPin.Node, OutNodes); + } + } + } + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + GetAllUsedNodes(&LinkedToPin.Node, OutNodes); + } + } + } +} + +// Won't add function parameters as dependencies +void GetDataDependencies(FVoxelCompilationNode* Node, TSet& OutNodes) +{ + if (OutNodes.Contains(Node)) + { + return; + } + OutNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + if (!LinkedToPin.Node.IsA() && !LinkedToPin.Node.IsA()) + { + check(!LinkedToPin.Node.IsA()); + GetDataDependencies(&LinkedToPin.Node, OutNodes); + } + } + } + } +} + +inline bool IsDataNodeSuccessorImpl(FVoxelCompilationNode* DataNode, FVoxelCompilationNode* PossibleSuccessor, TSet& VisitedNodes) +{ + if (DataNode == PossibleSuccessor) + { + return true; + } + if (VisitedNodes.Contains(PossibleSuccessor)) + { + return false; + } + VisitedNodes.Add(PossibleSuccessor); + + for (auto& Pin : PossibleSuccessor->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + if (IsDataNodeSuccessorImpl(DataNode, &LinkedToPin.Node, VisitedNodes)) + { + return true; + } + } + } + } + return false; +} + +bool FVoxelGraphCompilerHelpers::IsDataNodeSuccessor(FVoxelCompilationNode* DataNode, FVoxelCompilationNode* PossibleSuccessor) +{ + TSet VisitedNodes; + return IsDataNodeSuccessorImpl(DataNode, PossibleSuccessor, VisitedNodes); +} + +bool FVoxelGraphCompilerHelpers::AreAllNodePredecessorsChildOfStartNodeExecOnly(FVoxelCompilationNode* Node, FVoxelCompilationNode* StartNode, FVoxelCompilationNode*& FaultyNode) +{ + if (Node == StartNode) + { + return true; + } + + bool bIsChild = false; + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + if (AreAllNodePredecessorsChildOfStartNodeExecOnly(&LinkedToPin.Node, StartNode, FaultyNode)) + { + bIsChild = true; + } + else + { + return false; + } + } + } + } + + if (!bIsChild) + { + FaultyNode = Node; + } + + return bIsChild; +} + +void FVoxelGraphCompilerHelpers::GetFunctionNodes(FVoxelCompilationNode* StartNode, TSet& OutNodes) +{ + if (OutNodes.Contains(StartNode)) + { + return; + } + OutNodes.Add(StartNode); + + // Go along data input & exec outputs + + for (auto& Pin : StartNode->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec && Pin.NumLinkedTo() > 0) + { + check(Pin.NumLinkedTo() == 1); + GetFunctionNodes(&Pin.GetLinkedTo(0).Node, OutNodes); + } + } + + // Stop on separators + if (!StartNode->IsA()) + { + check(!StartNode->IsA()); + for (auto& Pin : StartNode->IteratePins()) + { + if (Pin.PinCategory == EVoxelPinCategory::Exec && Pin.NumLinkedTo() > 0) + { + check(Pin.NumLinkedTo() == 1); + GetFunctionNodes(&Pin.GetLinkedTo(0).Node, OutNodes); + } + } + } +} + +inline bool AreDataNodesSorted(const TArray& Nodes) +{ + TMap Indices; + for (int32 Index = 0; Index < Nodes.Num() ; Index++) + { + Indices.Add(Nodes[Index], Index); + } + for (int32 Index = 0; Index < Nodes.Num(); Index++) + { + TSet Dependencies; + GetDataDependencies(Nodes[Index], Dependencies); + for (auto& Dependency : Dependencies) + { + if (int32* DependencyIndex = Indices.Find(Dependency)) + { + if (!ensure(*DependencyIndex <= Index)) + { + return false; + } + } + } + } + return true; +} + +void FVoxelGraphCompilerHelpers::SortNodes(TArray& Nodes) +{ + VOXEL_FUNCTION_COUNTER(); + + TMap Order; + for (auto& Node : Nodes) + { + Order.Add(Node, 0); + } + + bool bChanged = true; + while (bChanged) + { + bChanged = false; + for (auto& Node : Nodes) + { + for (auto& Pin : Node->IteratePins()) + { + for (auto& LinkedTo : Pin.IterateLinkedTo()) + { + auto& OtherNode = LinkedTo.Node; + if (Order.Contains(&OtherNode) && Order[&OtherNode] >= Order[Node]) + { + Order[Node] = Order[&OtherNode] + 1; + bChanged = true; + } + } + } + } + } + Algo::Sort(Nodes, [&](auto* NodeA, auto* NodeB) { return Order[NodeA] < Order[NodeB]; }); + checkVoxelSlow(AreDataNodesSorted(Nodes)); +} + +inline void AddPreviousNodesToSetImpl(FVoxelCompilationNode* ExecNode, FVoxelCompilationNode* Node, TSet& Nodes) +{ + if (Nodes.Contains(Node) || (ExecNode != Node && Node->IsExecNode())) // Ignore exec nodes different from the first one + { + return; + } + Nodes.Add(Node); + + for (int32 I = 0; I < Node->GetInputCount(); I++) + { + auto& Pin = Node->GetInputPin(I); + + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + AddPreviousNodesToSetImpl(ExecNode, &LinkedToPin.Node, Nodes); + } + } + } +} + +void FVoxelGraphCompilerHelpers::AddPreviousNodesToSet(FVoxelCompilationNode* Node, TSet& Nodes) +{ + AddPreviousNodesToSetImpl(Node, Node, Nodes); +} + +inline FVoxelCompilationNode* GetPreviousFlowMergeOrFunctionSeparatorNodeImpl(FVoxelCompilationNode* Node, TSet& VisitedNodes) +{ + check(!Node->IsA() && !Node->IsA()); + if (Node->IsA() || Node->IsA()) + { + return Node; + } + if (VisitedNodes.Contains(Node)) + { + return nullptr; + } + VisitedNodes.Add(Node); + + for (auto& Pin : Node->IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + if (auto* Result = GetPreviousFlowMergeOrFunctionSeparatorNodeImpl(&LinkedToPin.Node, VisitedNodes)) + { + return Result; + } + } + } + } + + return nullptr; +} + +FVoxelCompilationNode* FVoxelGraphCompilerHelpers::GetPreviousFlowMergeOrFunctionSeparatorNode(FVoxelCompilationNode* Node) +{ + TSet Nodes; + return GetPreviousFlowMergeOrFunctionSeparatorNodeImpl(Node, Nodes); +} + +TSet FVoxelGraphCompilerHelpers::GetAllExecSuccessorsAndTheirDataDependencies(FVoxelCompilationNode* FirstNode, bool bAddFirstNodeDataDependencies) +{ + TSet Successors; + FVoxelGraphCompilerHelpers::GetAllExecSuccessors(FirstNode, Successors); + TSet OutNodes = Successors; + for (auto& Successor : Successors) + { + if (bAddFirstNodeDataDependencies || Successor != FirstNode) + { + TSet Nodes; + GetDataDependencies(Successor, Nodes); + OutNodes.Append(Nodes); + } + } + return OutNodes; +} + +TSet FVoxelGraphCompilerHelpers::GetAlwaysComputedNodes(FVoxelCompilationNode* Node) +{ + check(Node->IsExecNode()); + check(!Node->IsA() && !Node->IsA()); + + TArray> SuccessorsNodes; + for (int32 Index = 0; Index < Node->GetOutputCount() ; Index++) + { + auto& OutputPin = Node->GetOutputPin(Index); + if (OutputPin.PinCategory == EVoxelPinCategory::Exec) + { + if (OutputPin.NumLinkedTo() > 0) + { + check(OutputPin.NumLinkedTo() == 1); + SuccessorsNodes.Add(GetAlwaysComputedNodes(&OutputPin.GetLinkedTo(0).Node)); + } + } + } + + // First add children nodes + TSet OutNodes; + if (SuccessorsNodes.Num() > 0) + { + OutNodes = SuccessorsNodes[0]; + for (int32 Index = 1; Index < SuccessorsNodes.Num(); Index++) + { + OutNodes = OutNodes.Intersect(SuccessorsNodes[Index]); + } + } + + // Then our own data dependencies + GetDataDependencies(Node, OutNodes); + OutNodes.Remove(Node); + + return OutNodes; +} + +TSet FVoxelGraphCompilerHelpers::FilterHeads(const TSet& Nodes) +{ + TSet Result; + for (auto* Node : Nodes) + { + TSet Successors; + GetAllDataSuccessors(Node, Successors); + Successors.Remove(Node); + if (Successors.Intersect(Nodes).Num() == 0) + { + Result.Add(Node); + } + } + return Result; +} + +void FVoxelGraphCompilerHelpers::MovePin(FVoxelCompilationPin& From, FVoxelCompilationPin& To) +{ + MovePin(From, { &To }); +} + +void FVoxelGraphCompilerHelpers::MovePin(FVoxelCompilationPin& From, const TArray& To) +{ + for (auto& ToPin : To) + { + check(From.Direction == ToPin->Direction); + for (auto& LinkedTo : From.IterateLinkedTo()) + { + LinkedTo.LinkTo(*ToPin); + } + if (From.Direction == EVoxelPinDirection::Input) + { + ToPin->SetDefaultValue(From.GetDefaultValue()); + } + } + From.BreakAllLinks(); +} + +void FVoxelGraphCompilerHelpers::MoveInputPins(FVoxelCompilationNode& From, FVoxelCompilationNode& To) +{ + check(From.GetInputCount() == To.GetInputCount()); + for (int32 Index = 0; Index < From.GetInputCount() ; Index++) + { + auto& FromPin = From.GetInputPin(Index); + auto& ToPin = To.GetInputPin(Index); + MovePin(FromPin, ToPin); + } +} + +void FVoxelGraphCompilerHelpers::MoveOutputPins(FVoxelCompilationNode& From, FVoxelCompilationNode& To) +{ + check(From.GetOutputCount() == To.GetOutputCount()); + for (int32 Index = 0; Index < From.GetOutputCount() ; Index++) + { + auto& FromPin = From.GetOutputPin(Index); + auto& ToPin = To.GetOutputPin(Index); + MovePin(FromPin, ToPin); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.h new file mode 100644 index 00000000..d5a5aa87 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerHelpers.h @@ -0,0 +1,74 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Compilation/VoxelCompilationEnums.h" +#include "Compilation/VoxelCompilationNode.h" + +class FVoxelGraphCompiler; +class FVoxelCompilationNode; +struct FVoxelCompilationPin; + +namespace FVoxelGraphCompilerHelpers +{ + TSharedRef GetPassthroughNode(EVoxelPinCategory Category, const TArray& SourceNodes = {}); + + FVoxelCompilationNode* AddPassthrough(FVoxelGraphCompiler& Compiler, FVoxelCompilationPin& Pin, FVoxelCompilationNode* SourceNode = nullptr); + + void DuplicateNodes( + FVoxelGraphCompiler& Compiler, + const TSet& Nodes, + TMap& OutOldPinsToNewPins, + TMap& OldNodesToNewNodes); + + void GetSortedExecNodes(FVoxelCompilationNode* FirstNode, TArray& OutNodes); + + void GetAllSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes); + + void GetAllPredecessors(FVoxelCompilationNode* Node, TSet& OutNodes); + + void GetAllDataSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes); + + void GetAllExecSuccessors(FVoxelCompilationNode* Node, TSet& OutNodes); + + bool HasSetterOrFunctionCallSuccessor(FVoxelCompilationNode* Node); + + void GetAllUsedNodes(FVoxelCompilationNode* Node, TSet& OutNodes); + + bool IsDataNodeSuccessor(FVoxelCompilationNode* DataNode, FVoxelCompilationNode* PossibleSuccessor); + + bool AreAllNodePredecessorsChildOfStartNodeExecOnly(FVoxelCompilationNode* Node, FVoxelCompilationNode* StartNode, FVoxelCompilationNode*& FaultyNode); + + void GetFunctionNodes(FVoxelCompilationNode* FunctionStartNode, TSet& OutNodes); + + void SortNodes(TArray& Nodes); + + void AddPreviousNodesToSet(FVoxelCompilationNode* Node, TSet& Nodes); + + FVoxelCompilationNode* GetPreviousFlowMergeOrFunctionSeparatorNode(FVoxelCompilationNode* Node); + + // Won't add function parameters as dependencies + TSet GetAllExecSuccessorsAndTheirDataDependencies(FVoxelCompilationNode* FirstNode, bool bAddFirstNodeDataDependencies); + + // Will find all data nodes children that are always computed, no matter the branch + TSet GetAlwaysComputedNodes(FVoxelCompilationNode* Node); + + // Will remove all nodes that have a successor in the set + TSet FilterHeads(const TSet& Nodes); + + template + void BreakNodeLinks(FVoxelCompilationNode& Node) + { + for (auto& Pin : Node.IteratePins()) + { + Pin.BreakAllLinks(); + } + } + + // Like UE Ctrl Drag & Drop + void MovePin(FVoxelCompilationPin& From, FVoxelCompilationPin& To); + void MovePin(FVoxelCompilationPin& From, const TArray& To); + void MoveInputPins(FVoxelCompilationNode& From, FVoxelCompilationNode& To); + void MoveOutputPins(FVoxelCompilationNode& From, FVoxelCompilationNode& To); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerManager.cpp new file mode 100644 index 00000000..389ac085 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Compilation/VoxelGraphCompilerManager.cpp @@ -0,0 +1,630 @@ +// Copyright 2020 Phyronnaz + +#include "Compilation/VoxelGraphCompilerManager.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelCompilationNodeTree.h" +#include "Compilation/VoxelGraphCompilerHelpers.h" + +#include "Passes/VoxelCompactPassthroughsPass.h" +#include "Passes/VoxelOptimizationsPass.h" +#include "Passes/VoxelReplaceLocalVariablesPass.h" +#include "Passes/VoxelReplaceSmartMinMaxPass.h" +#include "Passes/VoxelGetRangeAnalysisPass.h" +#include "Passes/VoxelFunctionsPass.h" +#include "Passes/VoxelSetIdsPass.h" +#include "Passes/VoxelDependenciesPass.h" +#include "Passes/VoxelMacrosPass.h" +#include "Passes/VoxelFlowMergePass.h" +#include "Passes/ReplaceBiomeMergePass.h" +#include "Passes/RangeAnalysisPass.h" + +#include "Runtime/VoxelDefaultComputeNodes.h" +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelGraphChecker.h" +#include "Runtime/VoxelCompiledGraphs.h" + +#include "VoxelGraphPreviewSettings.h" +#include "VoxelGraphErrorReporter.h" +#include "IVoxelGraphEditor.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphConstants.h" +#include "VoxelNode.h" +#include "VoxelMinimal.h" + +#include "VoxelNodes/VoxelExecNodes.h" + +#include "Misc/MessageDialog.h" +#include "EdGraph/EdGraphPin.h" + +FVoxelGraphCompilerManager::FVoxelGraphCompilerManager( + UVoxelGraphGenerator* Graph, + bool bEnableOptimizations, + bool bPreview, + const UVoxelGraphPreviewSettings* PreviewSettings, + bool bAutomaticPreview, + bool bOnlyShowAxisDependencies) + : Graph(Graph) + , bEnableOptimizations(bEnableOptimizations) + , bPreview(bPreview) + , PreviewSettings(PreviewSettings) + , bAutomaticPreview(bAutomaticPreview) + , bOnlyShowAxisDependencies(bOnlyShowAxisDependencies) +{ + check(Graph); + check(!bPreview || PreviewSettings); + FVoxelGraphCompiler::IncreaseCompilationId(); +} + +FVoxelGraphCompilerManager::~FVoxelGraphCompilerManager() +{ + // UniquePtr forward decl +} + +bool FVoxelGraphCompilerManager::Compile(FVoxelCompiledGraphs& OutGraphs) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelGraphCompiler Compiler{ Graph }; + + if (!bOnlyShowAxisDependencies) + { + FVoxelGraphErrorReporter::ClearCompilationMessages(Graph); + } + + TArray& Nodes = Graph->AllNodes; + Nodes.RemoveAll([](UVoxelNode* Node) { return !IsValid(Node); }); + UVoxelNode* FirstNode = Graph->FirstNode; + const int32 FirstNodePinIndex = FirstNode ? FirstNode->GetInputPinIndex(Graph->FirstNodePinId) : 0; + + NodesMap = Compiler.InitFromNodes(Nodes, FirstNode, FirstNodePinIndex); + + CompileInternal(Compiler, OutGraphs); + + if (Compiler.ErrorReporter.HasError() && bOnlyShowAxisDependencies) + { + // Early exit so that errors are only showed once (and never showed when bOnlyShowAxisDependencies is true) + return false; + } + + Compiler.ErrorReporter.Apply(!bAutomaticPreview); + + if (Compiler.ErrorReporter.HasError()) + { + OutGraphs = {}; + return false; + } + else + { + OutGraphs.Compact(); + return true; + } +} + +#define CHECK_NO_ERRORS() if (Compiler.ErrorReporter.HasError()) return false; + +bool FVoxelGraphCompilerManager::CompileInternal(FVoxelGraphCompiler& Compiler, FVoxelCompiledGraphs& OutGraphs) +{ + VOXEL_FUNCTION_COUNTER(); + + CHECK_NO_ERRORS(); + + // Replace portal in current graph only. To have valid refs, each macro will do it for their own graphs + Compiler.ApplyPass(); + + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::BeforeMacroInlining) + { + DebugCompiler(Compiler); + } + + Compiler.ApplyPass(/*bClearMessages*/ !bOnlyShowAxisDependencies); + Compiler.ApplyPass(); + + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::AfterMacroInlining) + { + DebugCompiler(Compiler); + } + + Compiler.ApplyPass(); + + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::AfterBiomeMergeReplace) + { + DebugCompiler(Compiler); + } + + CHECK_NO_ERRORS(); + + Compiler.ApplyPass(); + + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::AfterSmartMinMaxReplace) + { + DebugCompiler(Compiler); + } + + CHECK_NO_ERRORS(); + + if (bPreview) + { + SetupPreview(Compiler); + } + if (!Compiler.FirstNode) + { + // Create a dummy set value node to avoid spamming "First node not connected" + auto* SetValueNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + SetValueNode->GetInputPin(1).SetDefaultValue("1"); + Compiler.FirstNode = SetValueNode; + } + + CHECK_NO_ERRORS(); + + check(Compiler.FirstNode); + + Compiler.ApplyPass(); + + const auto Outputs = Graph->GetOutputs(); + const auto Permutations = Graph->GetPermutations(); + + for (auto& Permutation : Permutations) + { + const FString OutputName = FVoxelGraphOutputsUtils::GetPermutationName(Permutation, Outputs); + TSharedRef TargetCompiler = Compiler.Clone("target " + OutputName); + if (!CompileOutput(Permutation, OutputName, *TargetCompiler, OutGraphs.Add(Permutation))) + { + return false; + } + } + + return true; +} + +struct FVoxelCompilationFunction +{ + FVoxelGraphCompilerManager& Manager; + const int32 FunctionId; + const TSharedRef Compiler; + FVoxelGraphFunctions CompiledFunctions; + + FVoxelCompilationFunction( + FVoxelGraphCompilerManager& Manager, + int32 FunctionId, + const TSharedRef& Compiler) + : Manager(Manager) + , FunctionId(FunctionId) + , Compiler(Compiler) + { + } + + inline bool HasErrors() const + { + return Compiler->ErrorReporter.HasError(); + } + + void PreCompile() + { + Compiler->ApplyPass(); + Compiler->ApplyPass(); + + Manager.Optimize(*Compiler); + + // Must be done after optimizing + Compiler->ApplyPass(); + + // Make sure to do it before FVoxelMarkDependenciesPass! + Compiler->ApplyPass(); + + Compiler->ApplyPass(); + } + + void AssignPinsIds(int32& Id) + { + Compiler->ApplyPass(Id); + } + + void FindConstants( + FVoxelCreatedComputeNodes& CreatedNodes, + TArray>& OutConstantNodes, + TArray>& OutSeedNodes) + { + // Must be done after MarkDependenciesPass & SetPinsIdsPass + Compiler->ApplyPass(CreatedNodes, OutConstantNodes, OutSeedNodes); + } + + void Compile( + FVoxelCreatedComputeNodes& CreatedNodes, + TArray& OutFunctionCallsToLink, + const bool bDebug) + { + VOXEL_FUNCTION_COUNTER(); + + const auto FunctionInit = CastCheckedVoxel(Compiler->FirstNode).GetComputeNode(); + const auto CompilationTree = FVoxelCompilationNodeTree::Create(Compiler->FirstNode); + + TMap> Functions; + for (EVoxelFunctionAxisDependencies Dependencies : FVoxelAxisDependencies::GetAllFunctionDependencies()) + { + TSet UsedNodes; + auto ComputeTree = MakeVoxelShared(); + CompilationTree->ConvertToComputeNodeTree(Dependencies, *ComputeTree, OutFunctionCallsToLink, UsedNodes, CreatedNodes); + Functions.Add(Dependencies, MakeShareable(new FVoxelGraphFunction(ComputeTree, FunctionInit.ToSharedRef(), FunctionId, Dependencies))); + + // Debug + if (Manager.Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::Axis && + Manager.Graph->FunctionToDebug == FunctionId && + Manager.Graph->AxisDependenciesToDebug == Dependencies && + + bDebug) + { + Manager.DebugNodes(UsedNodes); + } + } + + CompiledFunctions = FVoxelGraphFunctions( + FunctionId, + Functions[EVoxelFunctionAxisDependencies::X], + Functions[EVoxelFunctionAxisDependencies::XYWithCache], + Functions[EVoxelFunctionAxisDependencies::XYWithoutCache], + Functions[EVoxelFunctionAxisDependencies::XYZWithCache], + Functions[EVoxelFunctionAxisDependencies::XYZWithoutCache] + ); + } +}; + +bool FVoxelGraphCompilerManager::CompileOutput(const FVoxelGraphPermutationArray& Permutation, const FString& OutputName, FVoxelGraphCompiler& Compiler, TVoxelSharedPtr& OutGraph) +{ + VOXEL_FUNCTION_COUNTER(); + + Compiler.ApplyPass(Permutation); + + TMap Constants; + Constants.Add("IsRangeAnalysis", LexToString(Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex))); + Constants.Add("IsPreview", LexToString(bPreview)); + Constants.Add(*("Target=" + OutputName), LexToString(true)); + if(bPreview) + { + Constants.Add("Preview.LeftToRight", LexToString(int32(PreviewSettings->LeftToRight))); + Constants.Add("Preview.BottomToTop", LexToString(int32(PreviewSettings->BottomToTop))); + Constants.Add("Preview.Center.X", LexToString(PreviewSettings->Center.X)); + Constants.Add("Preview.Center.Y", LexToString(PreviewSettings->Center.Y)); + Constants.Add("Preview.Center.Z", LexToString(PreviewSettings->Center.Z)); + Constants.Add("Preview.ResolutionScale", LexToString(PreviewSettings->ResolutionMultiplierLog)); + Constants.Add("Preview.PreviewType2D", LexToString(int32(PreviewSettings->PreviewType2D))); + Constants.Add("Preview.MaterialConfig", LexToString(int32(PreviewSettings->MaterialConfig))); + Constants.Add("Preview.HeightBasedColor", LexToString(PreviewSettings->bHeightBasedColor)); + Constants.Add("Preview.EnableWater", LexToString(PreviewSettings->bEnableWater)); + Constants.Add("Preview.MinValue", LexToString(PreviewSettings->NormalizeMinValue)); + Constants.Add("Preview.MaxValue", LexToString(PreviewSettings->NormalizeMaxValue)); + Constants.Add("Preview.Resolution", LexToString(PreviewSettings->Resolution)); + Constants.Add("Preview.LODToPreview", LexToString(PreviewSettings->LODToPreview)); + Constants.Add("Preview.Height", LexToString(PreviewSettings->Height)); + } + Compiler.ApplyPass(Constants); + + CHECK_NO_ERRORS(); + + if (Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + Compiler.ApplyPass(); + Compiler.ApplyPass(); + } + + CHECK_NO_ERRORS(); + + Optimize(Compiler); + + CHECK_NO_ERRORS(); + + const bool bDebug = Graph->TargetToDebug.ToLower() == OutputName.ToLower(); + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::BeforeFillFunctionSeparators && bDebug) + { + DebugCompiler(Compiler); + } + + Compiler.ApplyPass(); + + CHECK_NO_ERRORS(); + + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::Output && bDebug) + { + DebugCompiler(Compiler); + } + + CHECK_NO_ERRORS(); + + Compiler.ApplyPass(); + + TArray FunctionsDescriptors; + Compiler.ApplyPass(FunctionsDescriptors); + + CHECK_NO_ERRORS(); + + if (Graph->bShowFunctions && (bDebug || Graph->bDetailedErrors)) + { + for (auto& FunctionDescriptor : FunctionsDescriptors) + { + check(FunctionDescriptor.FirstNode); + for (auto& Node : FunctionDescriptor.Nodes) + { + Compiler.ErrorReporter.AddMessageToNode(Node, FString::Printf(TEXT("function %d"), FunctionDescriptor.FunctionId), EVoxelGraphNodeMessageType::Info, false); + } + } + } + + TArray Functions; + Functions.Reserve(FunctionsDescriptors.Num()); // Else the array will realloc and the ptrs be invalid + TMap FunctionIdsToFunctions; + for (auto& FunctionDescriptor : FunctionsDescriptors) + { + TMap OldNodesToNewNodes; + auto FunctionCompiler = Compiler.Clone(FString::Printf(TEXT("function %d"), FunctionDescriptor.FunctionId), OldNodesToNewNodes); + + FunctionCompiler->FirstNode = OldNodesToNewNodes[FunctionDescriptor.FirstNode]; + + TSet NodesToKeep; + for (auto* Node : FunctionDescriptor.Nodes) + { + NodesToKeep.Add(OldNodesToNewNodes[Node]); + } + FunctionCompiler->ApplyPass(NodesToKeep); + + if (FunctionCompiler->ErrorReporter.HasError()) + { + return false; + } + + int32 Index = Functions.Emplace(*this, FunctionDescriptor.FunctionId, FunctionCompiler); + check(!FunctionIdsToFunctions.Contains(FunctionDescriptor.FunctionId)); + FunctionIdsToFunctions.Add(FunctionDescriptor.FunctionId, &Functions[Index]); + } + for (auto& Function : Functions) + { + Function.PreCompile(); + if (Function.HasErrors()) + { + return false; + } + } + + int32 Id = 0; + for (auto& Function : Functions) + { + Function.AssignPinsIds(Id); + } + + // Debug here as FindConstants will remove some nodes + if (Graph->DebugLevel == EVoxelGraphGeneratorDebugLevel::Function && bDebug) + { + auto* Function = FunctionIdsToFunctions.Find(Graph->FunctionToDebug); + if (Function) + { + DebugCompiler(*(*Function)->Compiler); + } + } + if (bOnlyShowAxisDependencies && (bDebug || Graph->bDetailedErrors)) + { + for (auto& Function : Functions) + { + Function.Compiler->ApplyPass(); + } + } + + FVoxelCreatedComputeNodes CreatedNodes; + TArray> ConstantNodes; + TArray> SeedNodes; + for (auto& Function : Functions) + { + Function.FindConstants(CreatedNodes, ConstantNodes, SeedNodes); + } + + TArray FunctionCallsToLink; + for (auto& Function : Functions) + { + Function.Compile(CreatedNodes, FunctionCallsToLink, bDebug); + } + + for (auto* FunctionCall : FunctionCallsToLink) + { + FVoxelCompilationFunction* Function = FunctionIdsToFunctions.FindChecked(FunctionCall->FunctionId); + FunctionCall->SetFunctions(Function->CompiledFunctions); + } + + TArray AllCompiledFunctions; + for (auto& Function : Functions) + { + AllCompiledFunctions.Add(Function.CompiledFunctions); + } + AllCompiledFunctions.Sort([](auto& A, auto& B) { return A.FunctionId < B.FunctionId; }); + check(AllCompiledFunctions[0].FunctionId == 0); + + OutGraph = MakeShareable(new FVoxelGraph( + OutputName, + AllCompiledFunctions, + FunctionIdsToFunctions.FindChecked(0)->CompiledFunctions, + ConstantNodes, + SeedNodes, + Id)); + + FVoxelGraphChecker::CheckGraph(Compiler.ErrorReporter, *OutGraph); + + return true; +} + +#undef CHECK_NO_ERRORS + +void FVoxelGraphCompilerManager::SetupPreview(FVoxelGraphCompiler& Compiler) +{ +#if WITH_EDITORONLY_DATA + auto* PreviewedGraphPin = Graph->PreviewedPin.Get(); + if (!PreviewedGraphPin) + { + return; + } + + auto* PreviewedGraphNode = Cast(PreviewedGraphPin->GetOwningNode()); + if (!ensure(PreviewedGraphNode)) + { + return; + } + auto* PreviewedVoxelNode = PreviewedGraphNode->GetVoxelNode(); + if (!ensure(PreviewedVoxelNode)) + { + return; + } + const int32 PreviewedPinIndex = PreviewedVoxelNode->GetOutputPinIndex(PreviewedGraphPin->PinId); + if (!ensure(PreviewedPinIndex != -1)) + { + return; + } + if(!ensure(NodesMap.Contains(PreviewedVoxelNode))) + { + return; + } + + FVoxelCompilationNode* PreviewedCompilationNode = NodesMap.FindChecked(PreviewedVoxelNode); + int32 PinIndex = PreviewedPinIndex; + + if (auto* MacroNode = CastVoxel(PreviewedCompilationNode)) + { + auto* OutputNode = MacroNode->OutputNode; + check(OutputNode->IsA()); + PreviewedCompilationNode = OutputNode; + } + + if (auto* MacroOutputNode = CastVoxel(PreviewedCompilationNode)) + { + auto* Passthrough = MacroOutputNode->Passthroughs[PinIndex]; + check(Passthrough); + + PinIndex = 0; + PreviewedCompilationNode = Passthrough; + } + + if (auto* PortalNode = CastVoxel(PreviewedCompilationNode)) + { + check(PinIndex == 0); + PreviewedCompilationNode = PortalNode->Passthrough; + if (!PreviewedCompilationNode) + { + // Should never happen + Compiler.ErrorReporter.AddError("Invalid local variable!"); + Compiler.ErrorReporter.AddNodeToSelect(PortalNode); + return; + } + } + + if (auto* BiomeNode = CastVoxel(PreviewedCompilationNode)) + { + if(!BiomeNode->OutputPassthroughs.IsValidIndex(PinIndex - 1)) + { + // Should never happen + Compiler.ErrorReporter.AddError("Invalid biome merge node!"); + Compiler.ErrorReporter.AddNodeToSelect(BiomeNode); + return; + } + PreviewedCompilationNode = BiomeNode->OutputPassthroughs[PinIndex - 1]; + check(PreviewedCompilationNode); + PinIndex = 0; + } + + if (auto* SmartNode = CastVoxel(PreviewedCompilationNode)) + { + ensure(PinIndex == 1); + PreviewedCompilationNode = SmartNode->OutputPassthrough; + check(PreviewedCompilationNode); + PinIndex = 0; + } + + auto SetValueNode = Compiler.AddNode(GetDefault()->GetCompilationNode()); + + auto& Pin = PreviewedCompilationNode->GetOutputPin(PinIndex); + check(Pin.PinCategory == EVoxelPinCategory::Float); + SetValueNode->GetInputPin(1).LinkTo(Pin); + + auto* FlowMergeOrFunction = FVoxelGraphCompilerHelpers::GetPreviousFlowMergeOrFunctionSeparatorNode(PreviewedCompilationNode); + if (FlowMergeOrFunction) + { + if (Graph->bShowFlowMergeAndFunctionsWarnings) + { + const auto Result = FMessageDialog::Open(EAppMsgType::YesNo, + VOXEL_LOCTEXT( + "Warning: Flow merge & function node preview might have unexpected results if it's not always executed.\n" + "Hide this warning?")); + if (Result == EAppReturnType::Yes) + { + Graph->bShowFlowMergeAndFunctionsWarnings = false; + } + } + + if (!ensure(Compiler.FirstNode)) + { + Compiler.ErrorReporter.AddError("Start node not connected"); + return; + } + + auto& OutputExecPin = FlowMergeOrFunction->GetOutputPin(0); + check(OutputExecPin.PinCategory == EVoxelPinCategory::Exec); + OutputExecPin.BreakAllLinks(); // Remove existing linked nodes + + auto& SetValueInputExecPin = SetValueNode->GetInputPin(0); + OutputExecPin.LinkTo(SetValueInputExecPin); + } + else + { + Compiler.FirstNode = SetValueNode; + Compiler.FirstNodePinIndex = 0; + } + + Compiler.Check(); +#endif +} + +void FVoxelGraphCompilerManager::DebugCompiler(const FVoxelGraphCompiler& Compiler) const +{ +#if WITH_EDITOR + if (Compiler.FirstNode) Compiler.FirstNode->DebugMessages.Add("First Node"); + DebugNodes(Compiler.GetAllNodes()); + if (Compiler.FirstNode) Compiler.FirstNode->DebugMessages.Pop(); +#endif +} + +void FVoxelGraphCompilerManager::DebugNodes(const TSet& Nodes) const +{ +#if WITH_EDITOR + auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor(); + if (Graph->bEnableDebugGraph && VoxelGraphEditor) + { + VoxelGraphEditor->DebugNodes(Graph->VoxelDebugGraph, Nodes); + } +#endif +} + +void FVoxelGraphCompilerManager::Optimize(FVoxelGraphCompiler& Compiler) const +{ + VOXEL_FUNCTION_COUNTER(); + + Compiler.ApplyPass(); + bool bContinue = true; + while (bContinue) + { + bContinue = false; + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + + if (bEnableOptimizations) + { + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass(bContinue); + Compiler.ApplyPass< FVoxelReplaceConstantPureNodesPass>(bContinue); + } + + if (Compiler.ErrorReporter.HasError()) + { + return; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConfig.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConfig.cpp new file mode 100644 index 00000000..0f028d68 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConfig.cpp @@ -0,0 +1,51 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelNodes/VoxelExposedNodes.h" +#include "VoxelGraphErrorReporter.h" + +FVoxelCppConfig::FVoxelCppConfig(FVoxelGraphErrorReporter& ErrorReporter) + : ErrorReporter(ErrorReporter) +{ +} + +void FVoxelCppConfig::AddExposedVariable(const TSharedRef& Variable) +{ + const FName Name(*Variable->ExposedName); + if (auto* ExistingVariablePtr = ExposedVariables.Find(Name)) + { + const auto& ExistingVariable = *ExistingVariablePtr; + if (ExistingVariable->Node != Variable->Node && !ExistingVariable->IsSameAs(*Variable, false)) + { + ErrorReporter.AddError("Exposed variables from nodes A and B have the same name, but different settings!"); + ErrorReporter.AddMessageToNode(Variable->Node, "node A", EVoxelGraphNodeMessageType::Info); + ErrorReporter.AddMessageToNode(ExistingVariable->Node, "node B", EVoxelGraphNodeMessageType::Info); + } + } + else + { + ExposedVariables.Add(Name, Variable); + } +} + +void FVoxelCppConfig::AddInclude(const FVoxelCppInclude& Include) +{ + Includes.AddUnique(Include); +} + +void FVoxelCppConfig::BuildExposedVariablesArray() +{ + ExposedVariablesArray.Reset(); + ExposedVariables.GenerateValueArray(ExposedVariablesArray); + + // Needs to match FVoxelGeneratorPickerCustomization::CustomizeChildren + ExposedVariablesArray.Sort([](const TSharedRef& A, const TSharedRef& B) + { + if (A->Priority != B->Priority) + { + return A->Priority < B->Priority; + } + return A->ExposedName < B->ExposedName; + }); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructor.cpp new file mode 100644 index 00000000..73787376 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructor.cpp @@ -0,0 +1,209 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "Runtime/VoxelGraphFunction.h" +#include "VoxelGraphConstants.h" +#include "VoxelAxisDependencies.h" +#include "VoxelGraphErrorReporter.h" + +void FVoxelCppConstructor::GetCode(FString& OutCode) const +{ + for (auto& Line : Lines) + { + OutCode.Append(Line); + OutCode.Append("\n"); + } +} + +void FVoxelCppConstructor::AddOtherConstructor(const FVoxelCppConstructor& Other) +{ + for (auto& Line : Other.Lines) + { + AddLineInternal(Line); + } + ensure(Other.CurrentIndent == 0); + ensure(Other.CurrentScope == &Other.MainScope); +} + +void FVoxelCppConstructor::AddFunctionCall(const FVoxelGraphFunctionInfo& Info, const TArray& Args) +{ + FString Line = Info.GetFunctionName(); + + Line += "("; + if (Info.FunctionType == EVoxelFunctionType::Init) + { + Line += FVoxelCppIds::InitStruct; + } + else + { + check(Info.FunctionType == EVoxelFunctionType::Compute); + Line += FVoxelCppIds::Context; + if (Info.Dependencies == EVoxelFunctionAxisDependencies::X || + Info.Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || // Still need to compute X variables + Info.Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::X); + } + if (Info.Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + Info.Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::XY); + } + if (Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache || + Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + Line += ", " + FVoxelCppIds::GraphOutputs; + } + } + for (auto& Arg : Args) + { + Line += ", " + Arg; + } + Line += ");"; + AddLine(Line); +} + +void FVoxelCppConstructor::AddFunctionDeclaration(const FVoxelGraphFunctionInfo& Info, const TArray& Args) +{ + FString Line = "void " + Info.GetFunctionName(); + Line += "("; + if (Info.FunctionType == EVoxelFunctionType::Init) + { + Line += "const FVoxelGeneratorInit& " + FVoxelCppIds::InitStruct; + } + else + { + check(Info.FunctionType == EVoxelFunctionType::Compute); + Line += "const " + GetContextTypeString() + "& " + FVoxelCppIds::Context; + + const auto Dependencies = Info.Dependencies; + + if (Dependencies == EVoxelFunctionAxisDependencies::X || + Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || // Still need to compute X variables + Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", "; + if (Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += "const "; + } + Line += FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::X) + "& " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::X); + } + + if (Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", "; + if (Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += "const "; + } + Line += FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::XY) + "& " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::XY); + } + + if (Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache || Info.Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + Line += ", " + FVoxelCppIds::GraphOutputsType + "& " + FVoxelCppIds::GraphOutputs; + } + } + for (auto& Arg : Args) + { + Line += ", " + Arg; + } + Line += ")"; + if (Info.FunctionType != EVoxelFunctionType::Init) + { + Line += " const"; + } + AddLine(Line); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelCppConstructor::AddVariable(int32 Id, const FString& Value) +{ + check(Id >= 0 && !CurrentScope->Variables.Contains(Id)); + CurrentScope->Variables.Add(Id, Value); +} + +bool FVoxelCppConstructor::HasVariable(int32 Id) +{ + return CurrentScope->GetVariable(Id) != nullptr; +} + +bool FVoxelCppConstructor::CurrentScopeHasVariable(int32 Id) +{ + return CurrentScope->Variables.Contains(Id); +} + +FString FVoxelCppConstructor::GetVariable(int32 Id, const FVoxelComputeNode* Node) +{ + check(Id >= 0); + if (auto* Variable = CurrentScope->GetVariable(Id)) + { + return *Variable; + } + else + { + ErrorReporter.AddInternalError("Invalid Id in GetVariable."); + ErrorReporter.AddMessageToNode(Node, "INTERNAL ERROR: Invalid Id in GetVariable", EVoxelGraphNodeMessageType::Error, true); + return "ERROR" + FString::FromInt(Id); + } +} + +void FVoxelCppConstructor::StartScope() +{ + CurrentScope = CurrentScope->GetChild(); +} + +void FVoxelCppConstructor::EndScope() +{ + CurrentScope = CurrentScope->GetParent(); + CurrentScope->RemoveChild(); +} + +bool FVoxelCppConstructor::IsNodeInit(FVoxelComputeNode* Node) const +{ + return CurrentScope->IsNodeInit(Node); +} + +void FVoxelCppConstructor::SetNodeAsInit(FVoxelComputeNode* Node) +{ + CurrentScope->NodesAlreadyInit.Add(Node); +} + +/////////////////////////////////////////////////////////////////////////////// + +FString FVoxelCppConstructor::GetTypeString(EVoxelPinCategory Category) const +{ + if (Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + return FVoxelPinCategory::GetRangeTypeString(Category); + } + else + { + return FVoxelPinCategory::GetTypeString(Category); + } +} + +FString FVoxelCppConstructor::GetTypeString(EVoxelDataPinCategory Category) const +{ + return GetTypeString(FVoxelPinCategory::DataPinToPin(Category)); +} + +FString FVoxelCppConstructor::GetContextTypeString() const +{ + if (Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + return "FVoxelContextRange"; + } + else + { + return "FVoxelContext"; + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructorManager.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructorManager.cpp new file mode 100644 index 00000000..a48ecf35 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppConstructorManager.cpp @@ -0,0 +1,740 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelCppConstructorManager.h" +#include "VoxelGraphOutputs.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGraphConstants.h" + +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConfig.h" + +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelCompiledGraphs.h" + +struct FVoxelCppStructConfig +{ + const FVoxelGraphPermutationArray Permutation; + const FString Name; + const FString StructName; + const TArray Outputs; + + FVoxelCppStructConfig(const FVoxelGraphPermutationArray& Permutation, const FString& InName, const TArray& Outputs) + : Permutation(Permutation) + , Name(InName) + , StructName("FLocalComputeStruct_" + Name) + , Outputs(Outputs) + { + } + + inline bool IsSingleOutput(EVoxelDataPinCategory Category) const + { + return Outputs.Num() == 1 && Outputs[0].Category == Category; + } + inline bool IsSingleOutputRange(EVoxelDataPinCategory Category) const + { + return + Outputs.Num() == 2 && + Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex) && + GetRangeGraphOutput().Category == Category; + } + // With range analysis there's a dummy output, ignore it + inline const FVoxelGraphOutput& GetRangeGraphOutput() const + { + check(Outputs.Num() == 2 && Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)); + if (Outputs[0].Index == FVoxelGraphOutputsIndices::RangeAnalysisIndex) + { + return Outputs[1]; + } + else + { + check(Outputs[1].Index == FVoxelGraphOutputsIndices::RangeAnalysisIndex); + return Outputs[0]; + } + } +}; + +inline void AddCppStruct(FVoxelCppConstructor& Cpp, FVoxelCppConstructor& GlobalScopeCpp, const FVoxelCppConfig& Config, const FVoxelGraph& Graph, const FVoxelCppStructConfig& StructConfig) +{ + const bool bIsRangeAnalysis = StructConfig.Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex); + + bool bHasMaterial = false; + TArray Outputs; + TArray GraphOutputs; + for (auto& Output : StructConfig.Outputs) + { + if (Output.Index == FVoxelGraphOutputsIndices::RangeAnalysisIndex) + { + continue; + } + + if (uint32(GraphOutputs.Num()) <= Output.Index) + { + GraphOutputs.SetNum(Output.Index + 1); + } + + if (Output.Index == FVoxelGraphOutputsIndices::MaterialIndex) + { + bHasMaterial = true; + // Do not add to Outputs, but do add an entry in GraphOutputs for SetMaterial to work properly + GraphOutputs[Output.Index] = FVoxelCppIds::GraphOutputs + ".MaterialBuilder"; + } + else + { + Outputs.Add(Output); + GraphOutputs[Output.Index] = FVoxelCppIds::GraphOutputs + "." + Output.Name.ToString(); + } + } + + Cpp.AddLine("class " + StructConfig.StructName); + Cpp.EnterNamedScope(StructConfig.StructName); + Cpp.StartBlock(); + Cpp.Public(); + { + FVoxelCppVariableScope Scope(Cpp); + + // GraphOutputs struct + Cpp.AddLine("struct " + FVoxelCppIds::GraphOutputsType); + Cpp.EnterNamedScope(FVoxelCppIds::GraphOutputsType); + Cpp.StartBlock(); + { + Cpp.AddLine(FVoxelCppIds::GraphOutputsType + "() {}"); + Cpp.NewLine(); + Cpp.AddLine("void Init(const FVoxelGraphOutputsInit& Init)"); + Cpp.StartBlock(); + if (bHasMaterial) + { + Cpp.AddLine("MaterialBuilder.SetMaterialConfig(Init.MaterialConfig);"); + } + Cpp.EndBlock(); + + const FString TypeT = bIsRangeAnalysis ? "TVoxelRange" : "T"; + + Cpp.NewLine(); + Cpp.AddLine("template"); + Cpp.AddLine(TypeT + " Get() const;"); + Cpp.AddLine("template"); + Cpp.AddLine("void Set(" + TypeT + " Value);"); + + for (auto& Output : Outputs) + { + const auto Type = FVoxelPinCategory::GetTypeString(Output.Category); + const auto RealType = bIsRangeAnalysis ? "TVoxelRange<" + Type + ">" : Type; + + GlobalScopeCpp.AddLine("template<>"); + GlobalScopeCpp.AddLine("inline " + RealType + " " + Cpp.GetScopeAccessor() + "Get<" + Type + ", " + FString::FromInt(Output.Index) + ">() const"); + GlobalScopeCpp.StartBlock(); + GlobalScopeCpp.AddLine("return " + Output.Name.ToString() + ";"); + GlobalScopeCpp.EndBlock(); + GlobalScopeCpp.AddLine("template<>"); + GlobalScopeCpp.AddLine("inline void " + Cpp.GetScopeAccessor() + "Set<" + Type + ", " + FString::FromInt(Output.Index) + ">(" + RealType + " InValue)"); + GlobalScopeCpp.StartBlock(); + GlobalScopeCpp.AddLine(Output.Name.ToString() + " = InValue;"); + GlobalScopeCpp.EndBlock(); + } + + if (bHasMaterial) + { + GlobalScopeCpp.AddLine("template<>"); + GlobalScopeCpp.AddLine("inline FVoxelMaterial " + Cpp.GetScopeAccessor() + "Get() const"); + GlobalScopeCpp.StartBlock(); + GlobalScopeCpp.AddLine("return MaterialBuilder.Build();"); + GlobalScopeCpp.EndBlock(); + GlobalScopeCpp.AddLine("template<>"); + GlobalScopeCpp.AddLine("inline void " + Cpp.GetScopeAccessor() + "Set(FVoxelMaterial Material)"); + GlobalScopeCpp.StartBlock(); + GlobalScopeCpp.EndBlock(); + } + + Cpp.NewLine(); + + for (auto& Output : Outputs) + { + Cpp.AddLine(Output.GetDeclaration(Cpp) + ";"); + } + if (bHasMaterial) + { + Cpp.AddLine("FVoxelMaterialBuilder MaterialBuilder;"); + } + } + Cpp.EndBlock(true); + Cpp.ExitNamedScope(FVoxelCppIds::GraphOutputsType); + + // Cache structs + for (auto Dependency : { + EVoxelAxisDependencies::Constant, + EVoxelAxisDependencies::X, + EVoxelAxisDependencies::XY }) + { + Cpp.AddLine("struct " + FVoxelCppIds::GetCacheType(Dependency)); + Cpp.StartBlock(); + { + Cpp.AddLine(FVoxelCppIds::GetCacheType(Dependency) + "() {}"); + Cpp.NewLine(); + + if (Dependency == EVoxelAxisDependencies::Constant) + { + TSet ConstantNodes; + Graph.GetConstantNodes(ConstantNodes); + for (auto* Node : ConstantNodes) + { + check(Node->Type == EVoxelComputeNodeType::Data); + Node->DeclareOutputs(Cpp, FVoxelVariableAccessInfo::StructDeclaration(Dependency)); + } + } + else + { + TSet Nodes; + Graph.GetNotConstantNodes(Nodes); + for (auto* Node : Nodes) + { + // We don't want to cache functions or seed outputs + if (Node->Type == EVoxelComputeNodeType::Data) + { + Node->DeclareOutputs(Cpp, FVoxelVariableAccessInfo::StructDeclaration(Dependency)); + } + else + { + check(Node->Type == EVoxelComputeNodeType::Exec || Node->Type == EVoxelComputeNodeType::Seed); + } + } + } + } + Cpp.EndBlock(true); + Cpp.NewLine(); + } + + // Constructor + Cpp.AddLine(StructConfig.StructName + "(const " + FVoxelCppIds::ExposedVariablesStructType + "& In" + FVoxelCppIds::ExposedVariablesStruct + ")"); + Cpp.Indent(); + Cpp.AddLine(": " + FVoxelCppIds::ExposedVariablesStruct + "(In" + FVoxelCppIds::ExposedVariablesStruct + ")"); + Cpp.Unindent(); + Cpp.AddLine("{"); + Cpp.AddLine("}"); + Cpp.NewLine(); + + // Init + Cpp.AddLine("void Init(const FVoxelGeneratorInit& " + FVoxelCppIds::InitStruct + ")"); + Cpp.StartBlock(); + { + Cpp.AddLine("////////////////////////////////////////////////////"); + Cpp.AddLine("//////////////////// Init nodes ////////////////////"); + Cpp.AddLine("////////////////////////////////////////////////////"); + Cpp.StartBlock(); + Graph.Init(Cpp); + Cpp.EndBlock(); + Cpp.NewLine(); + + Cpp.AddLine("////////////////////////////////////////////////////"); + Cpp.AddLine("//////////////// Compute constants /////////////////"); + Cpp.AddLine("////////////////////////////////////////////////////"); + Cpp.StartBlock(); + Graph.ComputeConstants(Cpp); + Cpp.EndBlock(); + } + Cpp.EndBlock(); + + // Computes + for (EVoxelFunctionAxisDependencies Dependencies : FVoxelAxisDependencies::GetAllFunctionDependencies()) + { + if (bIsRangeAnalysis && Dependencies != EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + continue; + } + + FString Line = "void Compute" + FVoxelAxisDependencies::ToString(Dependencies) + "("; + Line += "const " + Cpp.GetContextTypeString() + "& " + FVoxelCppIds::Context; + + if (Dependencies == EVoxelFunctionAxisDependencies::X || + Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || // Still need to compute X variables + Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", "; + if (Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += "const "; + } + Line += FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::X) + "& " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::X); + } + + if (Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache || + Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || + Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += ", "; + if (Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache) + { + Line += "const "; + } + Line += FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::XY) + "& " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::XY); + } + + if (Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache || Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + Line += ", " + FVoxelCppIds::GraphOutputsType + "& " + FVoxelCppIds::GraphOutputs; + } + Line += ") const"; + Cpp.AddLine(Line); + Cpp.StartBlock(); + Graph.Compute(Cpp, Dependencies); + Cpp.EndBlock(); + } + + // Getters + Cpp.NewLine(); + Cpp.AddLinef(TEXT("inline %s GetBufferX() const { return {}; }"), *FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::X)); + Cpp.AddLinef(TEXT("inline %s GetBufferXY() const { return {}; }"), *FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::XY)); + Cpp.AddLinef(TEXT("inline %s GetOutputs() const { return {}; }"), *FVoxelCppIds::GraphOutputsType); + Cpp.NewLine(); + + Cpp.Private(); + + // Constant cache + Cpp.AddLine(FVoxelCppIds::GetCacheType(EVoxelAxisDependencies::Constant) + " " + FVoxelCppIds::GetCacheName(EVoxelAxisDependencies::Constant) + ";"); + + Cpp.NewLine(); + + // Exposed variables ref + Cpp.AddLine("const " + FVoxelCppIds::ExposedVariablesStructType + "& " + FVoxelCppIds::ExposedVariablesStruct + ";"); + + Cpp.NewLine(); + + // Private node variables + { + TSet Nodes; + Graph.GetAllNodes(Nodes); + TArray PrivateVariables; + for (auto* Node : Nodes) + { + Node->GetPrivateVariables(PrivateVariables); + } + for (auto& Variable : PrivateVariables) + { + Cpp.AddLine(Variable.GetDeclaration() + ";"); + } + } + + // Functions + Cpp.NewLine(); + Cpp.AddLine("///////////////////////////////////////////////////////////////////////"); + Cpp.AddLine("//////////////////////////// Init functions ///////////////////////////"); + Cpp.AddLine("///////////////////////////////////////////////////////////////////////"); + Cpp.NewLine(); + Graph.DeclareInitFunctions(Cpp); + Cpp.AddLine("///////////////////////////////////////////////////////////////////////"); + Cpp.AddLine("////////////////////////// Compute functions //////////////////////////"); + Cpp.AddLine("///////////////////////////////////////////////////////////////////////"); + Cpp.NewLine(); + Graph.DeclareComputeFunctions(Cpp, GraphOutputs); + } + Cpp.EndBlock(true); + Cpp.ExitNamedScope(StructConfig.StructName); +} + +FVoxelCppConstructorManager::FVoxelCppConstructorManager(const FString& ClassName, UVoxelGraphGenerator* VoxelGraphGenerator) + : ClassName(ClassName) + , VoxelGraphGenerator(VoxelGraphGenerator) + , Graphs(MakeUnique()) + , ErrorReporter(MakeUnique(VoxelGraphGenerator)) +{ + check(VoxelGraphGenerator); + + if (!VoxelGraphGenerator->CreateGraphs(*Graphs, false, false, false)) + { + ErrorReporter->AddError("Compilation error!"); + } +} + +FVoxelCppConstructorManager::~FVoxelCppConstructorManager() +{ + // UniquePtr forward decl +} + +bool FVoxelCppConstructorManager::Compile(FString& OutHeader, FString& OutCpp) +{ + const bool bResult = CompileInternal(OutHeader, OutCpp); + ErrorReporter->Apply(true); + return bResult; +} + +#define CHECK_FOR_ERRORS() if (ErrorReporter->HasError()) { return false; } + +bool FVoxelCppConstructorManager::CompileInternal(FString& OutHeader, FString& OutCpp) +{ + CHECK_FOR_ERRORS(); + + TArray AllStructConfigs; + TMap PermutationToStructConfigs; + TSet Nodes; + { + auto Outputs = VoxelGraphGenerator->GetOutputs(); + // Check outputs names + { + TSet Names; + for (auto& It : Outputs) + { + auto& Name = It.Value.Name; + if (Name.ToString().IsEmpty()) + { + ErrorReporter->AddError("Empty Output name!"); + } + CHECK_FOR_ERRORS(); + if (Names.Contains(Name)) + { + ErrorReporter->AddError("Multiple Outputs have the same name! (" + Name.ToString() + ")"); + } + CHECK_FOR_ERRORS(); + Names.Add(Name); + } + } + for (const FVoxelGraphPermutationArray& Permutation : VoxelGraphGenerator->GetPermutations()) + { + if (Permutation.Num() == 0) + { + continue; + } + + const FString Name = "Local" + FVoxelGraphOutputsUtils::GetPermutationName(Permutation, Outputs); + TArray PermutationOutputs; + for (uint32 Index : Permutation) + { + PermutationOutputs.Add(Outputs[Index]); + } + + const auto& Graph = Graphs->Get(Permutation); + Graph->GetAllNodes(Nodes); + + FVoxelCppStructConfig StructConfig(Permutation, Name, PermutationOutputs); + AllStructConfigs.Add(StructConfig); + PermutationToStructConfigs.Add(Permutation, StructConfig); + } + } + + PermutationToStructConfigs.KeySort([](const FVoxelGraphPermutationArray& A, const FVoxelGraphPermutationArray& B) + { + if (A.Num() < B.Num()) + { + return true; + } + if (A.Num() > B.Num()) + { + return false; + } + for (int32 Index = 0; Index < A.Num(); Index++) + { + if (A[Index] > B[Index]) + { + return false; + } + } + return true; + }); + + FVoxelCppConfig Config(*ErrorReporter); + Config.AddInclude("CoreMinimal.h"); + Config.AddInclude("VoxelGeneratedWorldGeneratorsIncludes.h"); + for (auto* Node : Nodes) + { + Node->CallSetupCpp(Config); + } + CHECK_FOR_ERRORS(); + Config.AddInclude(ClassName + ".generated.h"); + Config.BuildExposedVariablesArray(); + + const FString InstanceClassName("F" + ClassName + "Instance"); + const FString MainClassName("U" + ClassName); + + FVoxelCppConstructor Header({}, *ErrorReporter); + FVoxelCppConstructor Cpp({}, *ErrorReporter); + + // Header Intro + Header.AddLine("// Copyright 2020 Phyronnaz"); + Header.NewLine(); + Header.AddLine("#pragma once"); + Header.NewLine(); + + // Includes + for (auto& Include : Config.GetIncludes()) + { + Header.AddLine(Include.ToString()); + } + Header.NewLine(); + + // Cpp Intro + Cpp.AddLine("// Copyright 2020 Phyronnaz"); + Cpp.NewLine(); + Cpp.AddLine(FVoxelCppInclude(ClassName + ".h").ToString()); + Cpp.NewLine(); + Cpp.AddLine("PRAGMA_GENERATED_VOXEL_GRAPH_START"); + Cpp.NewLine(); + Cpp.AddLine("using FVoxelGraphSeed = int32;"); + Cpp.NewLine(); + + Cpp.AddLine("#if VOXEL_GRAPH_GENERATED_VERSION == " + FString::FromInt(VOXEL_GRAPH_GENERATED_VERSION)); + + // Instance + { + // For the outputs templates specialization + FVoxelCppConstructor GlobalScopeCpp({}, * ErrorReporter); + + Cpp.AddLine("class " + InstanceClassName + " : public TVoxelGraphGeneratorInstanceHelper<" + InstanceClassName + ", " + MainClassName + ">"); + Cpp.StartBlock(); + Cpp.Public(); + { + // Define FParams + { + Cpp.AddLine("struct " + FVoxelCppIds::ExposedVariablesStructType); + Cpp.StartBlock(); + for (auto& Variable : Config.GetExposedVariables()) + { + Cpp.AddLine(Variable->GetConstDeclaration() + ";"); + } + Cpp.EndBlock(true); + } + Cpp.NewLine(); + + // Define structs + for (auto& StructConfig : AllStructConfigs) + { + const auto& Graph = *Graphs->Get(StructConfig.Permutation); + + FVoxelCppConstructor LocalCpp(StructConfig.Permutation, *ErrorReporter); + LocalCpp.EnterNamedScope(InstanceClassName); + AddCppStruct(LocalCpp, GlobalScopeCpp, Config, Graph, StructConfig); + LocalCpp.ExitNamedScope(InstanceClassName); + CHECK_FOR_ERRORS(); + + Cpp.AddOtherConstructor(LocalCpp); + } + Cpp.NewLine(); + + // Constructor + { + const auto GetMaps = [&](bool bEndComma, EVoxelDataPinCategory Category, auto Lambda) + { + Cpp.AddLine("{"); + Cpp.Indent(); + for (auto& FlagConfig : AllStructConfigs) + { + if (FlagConfig.Outputs.Num() == 1) + { + auto& Output = FlagConfig.Outputs[0]; + if (Output.Category == Category) + { + Cpp.AddLine("{ \"" + Output.Name.ToString() + "\", " + Lambda(Output.Index) + " },"); + } + } + } + Cpp.Unindent(); + Cpp.AddLine(FString("}") + (bEndComma ? "," : "")); + }; + + Cpp.AddLine(InstanceClassName + "(" + MainClassName + "& Object)"); + Cpp.Indent(); + + { + Cpp.Indent(); + Cpp.AddLine(": TVoxelGraphGeneratorInstanceHelper("); + /////////////////////////////////////////////////////////////////////////////// + GetMaps(true, EVoxelDataPinCategory::Float, [](uint32 Index) { return FString::FromInt(Index); }); + GetMaps(true, EVoxelDataPinCategory::Int, [](uint32 Index) { return FString::FromInt(Index); }); + GetMaps(true, EVoxelDataPinCategory::Color, [](uint32 Index) { return FString::FromInt(Index); }); + /////////////////////////////////////////////////////////////////////////////// + Cpp.AddLine("{"); + Cpp.Indent(); + GetMaps(true, EVoxelDataPinCategory::Float, [](uint32 Index) { return FString::Printf(TEXT("NoTransformAccessor::Get<%u, TOutputFunctionPtr>()"), Index); }); + GetMaps(true, EVoxelDataPinCategory::Int, [](uint32 Index) { return FString::Printf(TEXT("NoTransformAccessor::Get<%u, TOutputFunctionPtr>()"), Index); }); + GetMaps(true, EVoxelDataPinCategory::Color, [](uint32 Index) { return FString::Printf(TEXT("NoTransformAccessor::Get<%u, TOutputFunctionPtr>()"), Index); }); + GetMaps(false, EVoxelDataPinCategory::Float, [](uint32 Index) { return FString::Printf(TEXT("NoTransformRangeAccessor::Get<%u, TRangeOutputFunctionPtr>()"), Index); }); + Cpp.Unindent(); + Cpp.AddLine("},"); + /////////////////////////////////////////////////////////////////////////////// + Cpp.AddLine("{"); + Cpp.Indent(); + GetMaps(true, EVoxelDataPinCategory::Float, [](uint32 Index) { return FString::Printf(TEXT("WithTransformAccessor::Get<%u, TOutputFunctionPtr_Transform>()"), Index); }); + GetMaps(true, EVoxelDataPinCategory::Int, [](uint32 Index) { return FString::Printf(TEXT("WithTransformAccessor::Get<%u, TOutputFunctionPtr_Transform>()"), Index); }); + GetMaps(true, EVoxelDataPinCategory::Color, [](uint32 Index) { return FString::Printf(TEXT("WithTransformAccessor::Get<%u, TOutputFunctionPtr_Transform>()"), Index); }); + GetMaps(false, EVoxelDataPinCategory::Float, [](uint32 Index) { return FString::Printf(TEXT("WithTransformRangeAccessor::Get<%u, TRangeOutputFunctionPtr_Transform>()"), Index); }); + Cpp.Unindent(); + Cpp.AddLine("},"); + /////////////////////////////////////////////////////////////////////////////// + Cpp.AddLine("Object)"); + Cpp.Unindent(); + } + + // Init params + auto& Variables = Config.GetExposedVariables(); + if (Variables.Num() > 0) + { + Cpp.AddLine(", Params(FParams"); + Cpp.AddLine("{"); + Cpp.Indent(); + for (int32 Index = 0; Index < Variables.Num(); Index++) + { + auto& Variable = *Variables[Index]; + Cpp.AddLine(Variable.GetLocalVariableFromExposedOne("Object." + Variable.ExposedName) + (Index == Variables.Num() - 1 ? "" : ",")); + } + Cpp.Unindent(); + Cpp.AddLine("})"); + } + + // Pass params to structs + for (auto& FlagConfig : AllStructConfigs) + { + Cpp.AddLine(", " + FlagConfig.Name + "(" + FVoxelCppIds::ExposedVariablesStruct + ")"); + } + + Cpp.Unindent(); + + Cpp.StartBlock(); + Cpp.EndBlock(); + } + Cpp.NewLine(); + + // Init + Cpp.AddLine("virtual void InitGraph(const FVoxelGeneratorInit& " + FVoxelCppIds::InitStruct + ") override final"); + Cpp.StartBlock(); + { + for (auto& FlagConfig : AllStructConfigs) + { + Cpp.AddLine(FlagConfig.Name + ".Init(" + FVoxelCppIds::InitStruct + ");"); + } + } + Cpp.EndBlock(); + + const auto PermutationToString = [](FVoxelGraphPermutationArray Permutation) + { + Permutation.Sort(); + FString String; + for (uint32 Index : Permutation) + { + if (!String.IsEmpty()) + { + String += ", "; + } + String += FString::FromInt(Index); + } + return String; + }; + + for (auto& PermutationIt : PermutationToStructConfigs) + { + const FVoxelGraphPermutationArray& Permutation = PermutationIt.Key; + const FString PermutationString = PermutationToString(Permutation); + const FString ScopeAccessor = InstanceClassName; + + GlobalScopeCpp.AddLine("template<>"); + if (Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + GlobalScopeCpp.AddLinef(TEXT("inline auto& %s::GetRangeTarget<%s>() const"), *ScopeAccessor, *PermutationString); + } + else + { + GlobalScopeCpp.AddLinef(TEXT("inline auto& %s::GetTarget<%s>() const"), *ScopeAccessor, *PermutationString); + } + GlobalScopeCpp.StartBlock(); + GlobalScopeCpp.AddLinef(TEXT("return %s;"), *PermutationIt.Value.Name); + GlobalScopeCpp.EndBlock(); + } + + Cpp.NewLine(); + Cpp.AddLine("template"); + Cpp.AddLine("auto& GetTarget() const;"); + Cpp.NewLine(); + Cpp.AddLine("template"); + Cpp.AddLine("auto& GetRangeTarget() const;"); + Cpp.NewLine(); + Cpp.Private(); + + Cpp.AddLine(FVoxelCppIds::ExposedVariablesStructType + " " + FVoxelCppIds::ExposedVariablesStruct + ";"); + for (auto& StructConfig : AllStructConfigs) + { + Cpp.AddLine(StructConfig.StructName + " " + StructConfig.Name + ";"); + } + Cpp.NewLine(); + } + Cpp.EndBlock(true); + + // Add the specializations + Cpp.NewLine(); + Cpp.AddOtherConstructor(GlobalScopeCpp); + } + + Cpp.AddLine("#endif"); + + Cpp.NewLine(); + Cpp.AddLine("////////////////////////////////////////////////////////////"); + Cpp.AddLine("////////////////////////// UCLASS //////////////////////////"); + Cpp.AddLine("////////////////////////////////////////////////////////////"); + Cpp.NewLine(); + + // UClass + { + Header.AddLine("UCLASS(Blueprintable)"); + Header.AddLine("class " + MainClassName + " : public UVoxelGraphGeneratorHelper"); + Header.StartBlock(); + { + Header.AddLine("GENERATED_BODY()"); + Header.NewLine(); + Header.Public(); + + for (auto& Variable : Config.GetExposedVariables()) + { + Header.AddLinef(TEXT("// %s"), *Variable->Tooltip); + FString MetadataString = Variable->GetMetadataString(); + if (!MetadataString.IsEmpty()) + { + MetadataString = ", meta=(" + MetadataString + ")"; + } + Header.AddLinef(TEXT("UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"%s\"%s)"), *Variable->Category, *MetadataString); + Header.AddLine(Variable->ExposedType + " " + Variable->ExposedName + (!Variable->DefaultValue.IsEmpty() ? " = " + Variable->DefaultValue + ";" : ";")); + } + + Header.NewLine(); + Header.AddLine(MainClassName + "();"); + + Cpp.AddLine(MainClassName + "::" + MainClassName + "()"); + Cpp.StartBlock(); + Cpp.AddLine("bEnableRangeAnalysis = " + LexToString(VoxelGraphGenerator->bEnableRangeAnalysis) + ";"); + Cpp.EndBlock(); + + // GetGenerator + Header.AddLine("virtual TVoxelSharedRef GetTransformableInstance() override;"); + Cpp.NewLine(); + Cpp.AddLine("TVoxelSharedRef " + MainClassName + "::GetTransformableInstance()"); + Cpp.StartBlock(); + Cpp.AddPreprocessorLine("#if VOXEL_GRAPH_GENERATED_VERSION == " + FString::FromInt(VOXEL_GRAPH_GENERATED_VERSION)); + Cpp.AddLine("return MakeVoxelShared<" + InstanceClassName + ">(*this);"); + Cpp.AddPreprocessorLine("#else"); + Cpp.AddPreprocessorLine("#if VOXEL_GRAPH_GENERATED_VERSION > " + FString::FromInt(VOXEL_GRAPH_GENERATED_VERSION)); + { + const FString Error = "\"Outdated generated voxel graph: " + ClassName + ". You need to regenerate it.\""; + Cpp.AddLine("EMIT_CUSTOM_WARNING(" + Error + ");"); + Cpp.AddLine("FVoxelMessages::Warning(" + Error + ");"); + } + Cpp.AddPreprocessorLine("#else"); + { + const FString Error = "\"Generated voxel graph is more recent than the Voxel Plugin version: " + ClassName + ". You need to update the plugin.\""; + Cpp.AddLine("EMIT_CUSTOM_WARNING(" + Error + ");"); + Cpp.AddLine("FVoxelMessages::Warning(" + Error + ");"); + } + Cpp.AddPreprocessorLine("#endif"); + Cpp.AddLine("return MakeVoxelShared();"); + Cpp.AddPreprocessorLine("#endif"); + Cpp.EndBlock(); + } + Header.EndBlock(true); + } + + Cpp.NewLine(); + Cpp.AddLine("PRAGMA_GENERATED_VOXEL_GRAPH_END"); + + Header.GetCode(OutHeader); + Cpp.GetCode(OutCpp); + + return !ErrorReporter->HasError(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppIds.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppIds.cpp new file mode 100644 index 00000000..72f7f59c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelCppIds.cpp @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelCppIds.h" + +const FString FVoxelCppIds::InitStruct = "InitStruct"; +const FString FVoxelCppIds::Context = "Context"; + +const FString FVoxelCppIds::GraphOutputs = "Outputs"; +const FString FVoxelCppIds::GraphOutputsType = "FOutputs"; + +const FString FVoxelCppIds::ExposedVariablesStruct = "Params"; +const FString FVoxelCppIds::ExposedVariablesStructType = "FParams"; + +FString FVoxelCppIds::GetCacheName(EVoxelAxisDependencies Dependencies) +{ + switch (Dependencies) + { + case EVoxelAxisDependencies::Constant: + return "BufferConstant"; + case EVoxelAxisDependencies::X: + return "BufferX"; + case EVoxelAxisDependencies::XY: + return "BufferXY"; + case EVoxelAxisDependencies::XYZ: + default: + check(false); + return "BufferXYZ"; + } +} + +FString FVoxelCppIds::GetCacheType(EVoxelAxisDependencies Dependencies) +{ + switch (Dependencies) + { + case EVoxelAxisDependencies::Constant: + return "FBufferConstant"; + case EVoxelAxisDependencies::X: + return "FBufferX"; + case EVoxelAxisDependencies::XY: + return "FBufferXY"; + case EVoxelAxisDependencies::XYZ: + default: + check(false); + return "FBufferXYZ"; + } +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp new file mode 100644 index 00000000..f59d6270 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp @@ -0,0 +1,154 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelVariables.h" +#include "VoxelNodes/VoxelExposedNodes.h" +#include "CppTranslation/VoxelCppIds.h" + +FString FVoxelVariable::SanitizeName(const FString& InName) +{ + static const FString ValidChars = TEXT("_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + static const FString ValidStartChars = TEXT("_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + + FString SanitizedName; + + for (int32 CharIdx = 0; CharIdx < InName.Len(); ++CharIdx) + { + FString Char = InName.Mid(CharIdx, 1); + + if (!ValidChars.Contains(*Char)) + { + SanitizedName += TEXT("_"); + } + else + { + SanitizedName += Char; + } + } + + if (SanitizedName == "None") + { + return "None1"; + } + + if (SanitizedName.IsEmpty() || SanitizedName == TEXT("_")) + { + return TEXT("Name"); + } + + if (ValidStartChars.Contains(*SanitizedName.Mid(0, 1))) + { + return SanitizedName; + } + else + { + return TEXT("_") + SanitizedName; + } +} + + +FVoxelVariable::FVoxelVariable(const FString& Type, const FString& Name) + : Type(Type) + , ExposedName(SanitizeName(Name)) + , CppName(ExposedName) +{ + +} + +FVoxelVariable::FVoxelVariable(const FString& Type, const FString& Name, const FString& CppPrefix) + : Type(Type) + , ExposedName(SanitizeName(Name)) + , CppName(CppPrefix + ExposedName) +{ +} + +FVoxelExposedVariable::FVoxelExposedVariable( + const UVoxelExposedNode& Node, + const FString& Type, + const FString& ExposedType, + const FString& DefaultValue) + : FVoxelVariable(Type, Node.UniqueName.ToString(), FVoxelCppIds::ExposedVariablesStruct + ".") + , Node(&Node) + , DefaultValue(DefaultValue) + , ExposedType(ExposedType.IsEmpty() ? Type : ExposedType) + , Category(Node.Category) + , Tooltip(Node.Tooltip) + , Priority(Node.Priority) + , CustomMetaData(Node.GetMetaData()) +{ +} + +inline bool operator==(const TMap& A, const TMap& B) +{ + TArray KeysA; + TArray KeysB; + A.GetKeys(KeysA); + B.GetKeys(KeysB); + KeysA.Sort([](auto& InA, auto& InB) {return InA.FastLess(InB); }); + KeysB.Sort([](auto& InA, auto& InB) {return InA.FastLess(InB); }); + + if (KeysA != KeysB) + { + return false; + } + for (auto& Key : KeysA) + { + if (A[Key] != B[Key]) + { + return false; + } + } + return true; +} + +bool FVoxelExposedVariable::IsSameAs(const FVoxelExposedVariable& Other, bool bCheckNode) const +{ + return + Type == Other.Type && + ExposedName == Other.ExposedName && + (!bCheckNode || Node == Other.Node) && + Priority == Other.Priority && + DefaultValue == Other.DefaultValue && + Category == Other.Category && + Tooltip == Other.Tooltip && + ExposedType == Other.ExposedType && + CustomMetaData == Other.CustomMetaData && + GetLocalVariableFromExposedOne(ExposedName) == Other.GetLocalVariableFromExposedOne(ExposedName) && + GetExposedVariableDefaultMetadata() == Other.GetExposedVariableDefaultMetadata(); +} + +FString FVoxelExposedVariable::GetMetadataString() const +{ + auto Metadata = GetExposedVariableDefaultMetadata(); + Metadata.Append(CustomMetaData); + Metadata.KeySort([](auto& A, auto& B) + { + return A.FastLess(B); + }); + + FString Result; + for (auto& It : Metadata) + { + if (It.Key == STATIC_FNAME("UIMin") || + It.Key == STATIC_FNAME("UIMax")) + { + if (It.Value.IsEmpty()) + { + continue; + } + } + if (!Result.IsEmpty()) + { + Result += ", "; + } + Result += It.Key.ToString(); + if (!It.Value.IsEmpty()) + { + Result += "="; + Result += "\""; + Result += It.Value; + Result += "\""; + } + } + return Result; +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.cpp new file mode 100644 index 00000000..b971421c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.cpp @@ -0,0 +1,993 @@ +// Copyright 2020 Phyronnaz + +#include "VDI_Capsule_Graph.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVDI_Capsule_GraphInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Noise_Amplitude; + const int32 Seed; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_14; // Noise Amplitude = 1.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_16; // XYZ.X output 0 + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + v_flt Variable_6; // Data Item Parameters output 6 + v_flt Variable_13; // * output 0 + v_flt Variable_12; // * output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_17; // XYZ.Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 1443 + FVoxelGraphSeed Variable_15; // Seed = 1443 output 0 + Variable_15 = Params.Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Noise Amplitude = 1.0 + BufferConstant.Variable_14 = Params.Noise_Amplitude; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_0_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 1443 + FVoxelGraphSeed Variable_15; // Seed = 1443 output 0 + Variable_15 = Params.Seed; + + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_0_Noise.SetSeed(Variable_15); + _3D_Gradient_Perturb_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // XYZ.X + BufferX.Variable_16 = Context.GetLocalX(); + + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + BufferX.Variable_4 = (*Context.Items.CustomData).GetData()[4]; + BufferX.Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + BufferX.Variable_0 = 57.599049; + BufferX.Variable_1 = 0.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 0.000000; + BufferX.Variable_4 = 100.000000; + BufferX.Variable_5 = 0.000000; + BufferX.Variable_6 = 40.000000; + } + + // 1 / X + v_flt Variable_11; // 1 / X output 0 + Variable_11 = FVoxelNodeFunctions::OneOverX(BufferX.Variable_6); + + // * + BufferX.Variable_13 = BufferX.Variable_6 * BufferConstant.Variable_14; + + // * + BufferX.Variable_12 = Variable_11 * v_flt(0.2f); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // XYZ.Y + BufferXY.Variable_17 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // XYZ.X + BufferX.Variable_16 = Context.GetLocalX(); + + // XYZ.Y + BufferXY.Variable_17 = Context.GetLocalY(); + + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + BufferX.Variable_4 = (*Context.Items.CustomData).GetData()[4]; + BufferX.Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + BufferX.Variable_0 = 57.599049; + BufferX.Variable_1 = 0.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 0.000000; + BufferX.Variable_4 = 100.000000; + BufferX.Variable_5 = 0.000000; + BufferX.Variable_6 = 40.000000; + } + + // 1 / X + v_flt Variable_11; // 1 / X output 0 + Variable_11 = FVoxelNodeFunctions::OneOverX(BufferX.Variable_6); + + // * + BufferX.Variable_13 = BufferX.Variable_6 * BufferConstant.Variable_14; + + // * + BufferX.Variable_12 = Variable_11 * v_flt(0.2f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // XYZ.Z + v_flt Variable_18; // XYZ.Z output 0 + Variable_18 = Context.GetLocalZ(); + + // 3D Gradient Perturb + v_flt Variable_8; // 3D Gradient Perturb output 0 + v_flt Variable_9; // 3D Gradient Perturb output 1 + v_flt Variable_10; // 3D Gradient Perturb output 2 + Variable_8 = BufferX.Variable_16; + Variable_9 = BufferXY.Variable_17; + Variable_10 = Variable_18; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_8, Variable_9, Variable_10, BufferX.Variable_12, BufferX.Variable_13); + + // Capsule SDF + v_flt Variable_7; // Capsule SDF output 0 + Variable_7 = FVoxelSDFNodeFunctions::Capsule(Variable_8, Variable_9, Variable_10, BufferX.Variable_0, BufferX.Variable_1, BufferX.Variable_2, BufferX.Variable_3, BufferX.Variable_4, BufferX.Variable_5, BufferX.Variable_6); + + Outputs.Value = Variable_7; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // XYZ.X + v_flt Variable_16; // XYZ.X output 0 + Variable_16 = Context.GetLocalX(); + + // XYZ.Y + v_flt Variable_17; // XYZ.Y output 0 + Variable_17 = Context.GetLocalY(); + + // XYZ.Z + v_flt Variable_18; // XYZ.Z output 0 + Variable_18 = Context.GetLocalZ(); + + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + v_flt Variable_6; // Data Item Parameters output 6 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 57.599049; + Variable_1 = 0.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 100.000000; + Variable_5 = 0.000000; + Variable_6 = 40.000000; + } + + // 1 / X + v_flt Variable_11; // 1 / X output 0 + Variable_11 = FVoxelNodeFunctions::OneOverX(Variable_6); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = Variable_6 * BufferConstant.Variable_14; + + // * + v_flt Variable_12; // * output 0 + Variable_12 = Variable_11 * v_flt(0.2f); + + // 3D Gradient Perturb + v_flt Variable_8; // 3D Gradient Perturb output 0 + v_flt Variable_9; // 3D Gradient Perturb output 1 + v_flt Variable_10; // 3D Gradient Perturb output 2 + Variable_8 = Variable_16; + Variable_9 = Variable_17; + Variable_10 = Variable_18; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_8, Variable_9, Variable_10, Variable_12, Variable_13); + + // Capsule SDF + v_flt Variable_7; // Capsule SDF output 0 + Variable_7 = FVoxelSDFNodeFunctions::Capsule(Variable_8, Variable_9, Variable_10, Variable_0, Variable_1, Variable_2, Variable_3, Variable_4, Variable_5, Variable_6); + + Outputs.Value = Variable_7; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_14; // Noise Amplitude = 1.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_15; // XYZ.X output 0 + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + TVoxelRange Variable_4; // Data Item Parameters output 4 + TVoxelRange Variable_5; // Data Item Parameters output 5 + TVoxelRange Variable_6; // Data Item Parameters output 6 + TVoxelRange Variable_13; // * output 0 + TVoxelRange Variable_12; // * output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_16; // XYZ.Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Noise Amplitude = 1.0 + BufferConstant.Variable_14 = Params.Noise_Amplitude; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_1_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // XYZ.Z + TVoxelRange Variable_17; // XYZ.Z output 0 + Variable_17 = Context.GetLocalZ(); + + // XYZ.X + TVoxelRange Variable_15; // XYZ.X output 0 + Variable_15 = Context.GetLocalX(); + + // XYZ.Y + TVoxelRange Variable_16; // XYZ.Y output 0 + Variable_16 = Context.GetLocalY(); + + // Data Item Parameters + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + TVoxelRange Variable_4; // Data Item Parameters output 4 + TVoxelRange Variable_5; // Data Item Parameters output 5 + TVoxelRange Variable_6; // Data Item Parameters output 6 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 57.599049; + Variable_1 = 0.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 100.000000; + Variable_5 = 0.000000; + Variable_6 = 40.000000; + } + + // 1 / X + TVoxelRange Variable_11; // 1 / X output 0 + Variable_11 = FVoxelNodeFunctions::OneOverX(Variable_6); + + // * + TVoxelRange Variable_13; // * output 0 + Variable_13 = Variable_6 * BufferConstant.Variable_14; + + // * + TVoxelRange Variable_12; // * output 0 + Variable_12 = Variable_11 * TVoxelRange(0.2f); + + // 3D Gradient Perturb + TVoxelRange Variable_8; // 3D Gradient Perturb output 0 + TVoxelRange Variable_9; // 3D Gradient Perturb output 1 + TVoxelRange Variable_10; // 3D Gradient Perturb output 2 + Variable_8 = TVoxelRange::FromList(Variable_15.Min - 2 * Variable_13.Max, Variable_15.Max + 2 * Variable_13.Max); + Variable_9 = TVoxelRange::FromList(Variable_16.Min - 2 * Variable_13.Max, Variable_16.Max + 2 * Variable_13.Max); + Variable_10 = TVoxelRange::FromList(Variable_17.Min - 2 * Variable_13.Max, Variable_17.Max + 2 * Variable_13.Max); + + // Capsule SDF + TVoxelRange Variable_7; // Capsule SDF output 0 + Variable_7 = FVoxelSDFNodeFunctions::Capsule(Variable_8, Variable_9, Variable_10, Variable_0, Variable_1, Variable_2, Variable_3, Variable_4, Variable_5, Variable_6); + + Outputs.Value = Variable_7; + } + + }; + + FVDI_Capsule_GraphInstance(UVDI_Capsule_Graph& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Noise_Amplitude, + Object.Seed + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Capsule_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVDI_Capsule_GraphInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVDI_Capsule_GraphInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVDI_Capsule_GraphInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVDI_Capsule_GraphInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVDI_Capsule_Graph::UVDI_Capsule_Graph() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVDI_Capsule_Graph::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VDI_Capsule_Graph. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VDI_Capsule_Graph. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Capsule_Graph. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Capsule_Graph. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.h new file mode 100644 index 00000000..c7f96791 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.h @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VDI_Capsule_Graph.generated.h" + +UCLASS(Blueprintable) +class UVDI_Capsule_Graph : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // Relative to the radius + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Amplitude", UIMax="2", UIMin="0")) + float Noise_Amplitude = 1.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 1443; + + UVDI_Capsule_Graph(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.cpp new file mode 100644 index 00000000..337e3fec --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.cpp @@ -0,0 +1,1131 @@ +// Copyright 2020 Phyronnaz + +#include "VDI_Example_Crater_Graph.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVDI_Example_Crater_GraphInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_9; // X output 0 + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + v_flt Variable_6; // Data Item Parameters output 6 + v_flt Variable_7; // Data Item Parameters output 7 + v_flt Variable_8; // Data Item Parameters output 8 + v_flt Variable_16; // Vector Length output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_10; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_9 = Context.GetLocalX(); + + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 9) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + BufferX.Variable_4 = (*Context.Items.CustomData).GetData()[4]; + BufferX.Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + BufferX.Variable_7 = (*Context.Items.CustomData).GetData()[7]; + BufferX.Variable_8 = (*Context.Items.CustomData).GetData()[8]; + } + else + { + BufferX.Variable_0 = 140.000000; + BufferX.Variable_1 = 140.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 50.000000; + BufferX.Variable_4 = 2.000000; + BufferX.Variable_5 = 0.100000; + BufferX.Variable_6 = -0.800000; + BufferX.Variable_7 = 0.200000; + BufferX.Variable_8 = 0.500000; + } + + // Vector Length + BufferX.Variable_16 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferX.Variable_1, BufferX.Variable_2); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_10 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_10 = Context.GetLocalY(); + + // X + BufferX.Variable_9 = Context.GetLocalX(); + + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 9) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + BufferX.Variable_4 = (*Context.Items.CustomData).GetData()[4]; + BufferX.Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + BufferX.Variable_7 = (*Context.Items.CustomData).GetData()[7]; + BufferX.Variable_8 = (*Context.Items.CustomData).GetData()[8]; + } + else + { + BufferX.Variable_0 = 140.000000; + BufferX.Variable_1 = 140.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 50.000000; + BufferX.Variable_4 = 2.000000; + BufferX.Variable_5 = 0.100000; + BufferX.Variable_6 = -0.800000; + BufferX.Variable_7 = 0.200000; + BufferX.Variable_8 = 0.500000; + } + + // Vector Length + BufferX.Variable_16 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferX.Variable_1, BufferX.Variable_2); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // Normalize.Vector Length + v_flt Variable_28; // Normalize.Vector Length output 0 + Variable_28 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_9, BufferXY.Variable_10, Variable_11); + + // Normalize./ + v_flt Variable_30; // Normalize./ output 0 + Variable_30 = BufferXY.Variable_10 / Variable_28; + + // Normalize./ + v_flt Variable_29; // Normalize./ output 0 + Variable_29 = BufferX.Variable_9 / Variable_28; + + // Normalize./ + v_flt Variable_31; // Normalize./ output 0 + Variable_31 = Variable_11 / Variable_28; + + // vector * float.* + v_flt Variable_15; // vector * float.* output 0 + Variable_15 = Variable_31 * BufferX.Variable_16; + + // vector * float.* + v_flt Variable_32; // vector * float.* output 0 + Variable_32 = Variable_29 * BufferX.Variable_16; + + // vector * float.* + v_flt Variable_33; // vector * float.* output 0 + Variable_33 = Variable_30 * BufferX.Variable_16; + + // vector - vector.- + v_flt Variable_25; // vector - vector.- output 0 + Variable_25 = Variable_15 - BufferX.Variable_2; + + // vector - vector.- + v_flt Variable_26; // vector - vector.- output 0 + Variable_26 = Variable_32 - BufferX.Variable_0; + + // vector - vector.- + v_flt Variable_27; // vector - vector.- output 0 + Variable_27 = Variable_33 - BufferX.Variable_1; + + // Vector Length + v_flt Variable_12; // Vector Length output 0 + Variable_12 = FVoxelNodeFunctions::VectorLength(Variable_26, Variable_27, Variable_25); + + // / + v_flt Variable_13; // / output 0 + Variable_13 = Variable_12 / BufferX.Variable_3; + + // * + v_flt Variable_14; // * output 0 + Variable_14 = Variable_13 * Variable_13; + + // - + v_flt Variable_21; // - output 0 + Variable_21 = Variable_14 - v_flt(1.0f); + + // - + v_flt Variable_17; // - output 0 + Variable_17 = Variable_14 - v_flt(1.0f); + + // - + v_flt Variable_18; // - output 0 + Variable_18 = Variable_17 - BufferX.Variable_4; + + // Smooth Intersection + v_flt Variable_23; // Smooth Intersection output 0 + Variable_23 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_21, BufferX.Variable_6, BufferX.Variable_7); + + // Min (float) + v_flt Variable_19; // Min (float) output 0 + Variable_19 = FVoxelNodeFunctions::Min(Variable_18, v_flt(0.0f)); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = Variable_19 * Variable_19 * BufferX.Variable_5; + + // Smooth Union + v_flt Variable_24; // Smooth Union output 0 + Variable_24 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_23, Variable_20, BufferX.Variable_8); + + // * + v_flt Variable_22; // * output 0 + Variable_22 = Variable_24 * BufferX.Variable_3; + + Outputs.Value = Variable_22; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // Y + v_flt Variable_10; // Y output 0 + Variable_10 = Context.GetLocalY(); + + // X + v_flt Variable_9; // X output 0 + Variable_9 = Context.GetLocalX(); + + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + v_flt Variable_6; // Data Item Parameters output 6 + v_flt Variable_7; // Data Item Parameters output 7 + v_flt Variable_8; // Data Item Parameters output 8 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 9) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + Variable_7 = (*Context.Items.CustomData).GetData()[7]; + Variable_8 = (*Context.Items.CustomData).GetData()[8]; + } + else + { + Variable_0 = 140.000000; + Variable_1 = 140.000000; + Variable_2 = 0.000000; + Variable_3 = 50.000000; + Variable_4 = 2.000000; + Variable_5 = 0.100000; + Variable_6 = -0.800000; + Variable_7 = 0.200000; + Variable_8 = 0.500000; + } + + // Vector Length + v_flt Variable_16; // Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // Normalize.Vector Length + v_flt Variable_28; // Normalize.Vector Length output 0 + Variable_28 = FVoxelNodeFunctions::VectorLength(Variable_9, Variable_10, Variable_11); + + // Normalize./ + v_flt Variable_30; // Normalize./ output 0 + Variable_30 = Variable_10 / Variable_28; + + // Normalize./ + v_flt Variable_29; // Normalize./ output 0 + Variable_29 = Variable_9 / Variable_28; + + // Normalize./ + v_flt Variable_31; // Normalize./ output 0 + Variable_31 = Variable_11 / Variable_28; + + // vector * float.* + v_flt Variable_15; // vector * float.* output 0 + Variable_15 = Variable_31 * Variable_16; + + // vector * float.* + v_flt Variable_32; // vector * float.* output 0 + Variable_32 = Variable_29 * Variable_16; + + // vector * float.* + v_flt Variable_33; // vector * float.* output 0 + Variable_33 = Variable_30 * Variable_16; + + // vector - vector.- + v_flt Variable_25; // vector - vector.- output 0 + Variable_25 = Variable_15 - Variable_2; + + // vector - vector.- + v_flt Variable_26; // vector - vector.- output 0 + Variable_26 = Variable_32 - Variable_0; + + // vector - vector.- + v_flt Variable_27; // vector - vector.- output 0 + Variable_27 = Variable_33 - Variable_1; + + // Vector Length + v_flt Variable_12; // Vector Length output 0 + Variable_12 = FVoxelNodeFunctions::VectorLength(Variable_26, Variable_27, Variable_25); + + // / + v_flt Variable_13; // / output 0 + Variable_13 = Variable_12 / Variable_3; + + // * + v_flt Variable_14; // * output 0 + Variable_14 = Variable_13 * Variable_13; + + // - + v_flt Variable_21; // - output 0 + Variable_21 = Variable_14 - v_flt(1.0f); + + // - + v_flt Variable_17; // - output 0 + Variable_17 = Variable_14 - v_flt(1.0f); + + // - + v_flt Variable_18; // - output 0 + Variable_18 = Variable_17 - Variable_4; + + // Smooth Intersection + v_flt Variable_23; // Smooth Intersection output 0 + Variable_23 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_21, Variable_6, Variable_7); + + // Min (float) + v_flt Variable_19; // Min (float) output 0 + Variable_19 = FVoxelNodeFunctions::Min(Variable_18, v_flt(0.0f)); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = Variable_19 * Variable_19 * Variable_5; + + // Smooth Union + v_flt Variable_24; // Smooth Union output 0 + Variable_24 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_23, Variable_20, Variable_8); + + // * + v_flt Variable_22; // * output 0 + Variable_22 = Variable_24 * Variable_3; + + Outputs.Value = Variable_22; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_25; // Normalize.Range Union output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_19; // * output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Normalize.Range Union + BufferConstant.Variable_25 = FVoxelNodeFunctions::Union(TVoxelRange(-1.0f), TVoxelRange(1.0f)); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Data Item Parameters + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + TVoxelRange Variable_4; // Data Item Parameters output 4 + TVoxelRange Variable_5; // Data Item Parameters output 5 + TVoxelRange Variable_6; // Data Item Parameters output 6 + TVoxelRange Variable_7; // Data Item Parameters output 7 + TVoxelRange Variable_8; // Data Item Parameters output 8 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 9) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + Variable_7 = (*Context.Items.CustomData).GetData()[7]; + Variable_8 = (*Context.Items.CustomData).GetData()[8]; + } + else + { + Variable_0 = 140.000000; + Variable_1 = 140.000000; + Variable_2 = 0.000000; + Variable_3 = 50.000000; + Variable_4 = 2.000000; + Variable_5 = 0.100000; + Variable_6 = -0.800000; + Variable_7 = 0.200000; + Variable_8 = 0.500000; + } + + // Vector Length + TVoxelRange Variable_13; // Vector Length output 0 + Variable_13 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // vector * float.* + TVoxelRange Variable_12; // vector * float.* output 0 + Variable_12 = BufferConstant.Variable_25 * Variable_13; + + // vector * float.* + TVoxelRange Variable_26; // vector * float.* output 0 + Variable_26 = BufferConstant.Variable_25 * Variable_13; + + // vector * float.* + TVoxelRange Variable_27; // vector * float.* output 0 + Variable_27 = BufferConstant.Variable_25 * Variable_13; + + // vector - vector.- + TVoxelRange Variable_23; // vector - vector.- output 0 + Variable_23 = Variable_26 - Variable_0; + + // vector - vector.- + TVoxelRange Variable_24; // vector - vector.- output 0 + Variable_24 = Variable_27 - Variable_1; + + // vector - vector.- + TVoxelRange Variable_22; // vector - vector.- output 0 + Variable_22 = Variable_12 - Variable_2; + + // Vector Length + TVoxelRange Variable_9; // Vector Length output 0 + Variable_9 = FVoxelNodeFunctions::VectorLength(Variable_23, Variable_24, Variable_22); + + // / + TVoxelRange Variable_10; // / output 0 + Variable_10 = Variable_9 / Variable_3; + + // * + TVoxelRange Variable_11; // * output 0 + Variable_11 = Variable_10 * Variable_10; + + // - + TVoxelRange Variable_18; // - output 0 + Variable_18 = Variable_11 - TVoxelRange(1.0f); + + // - + TVoxelRange Variable_14; // - output 0 + Variable_14 = Variable_11 - TVoxelRange(1.0f); + + // - + TVoxelRange Variable_15; // - output 0 + Variable_15 = Variable_14 - Variable_4; + + // Smooth Intersection + TVoxelRange Variable_20; // Smooth Intersection output 0 + Variable_20 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_18, Variable_6, Variable_7); + + // Min (float) + TVoxelRange Variable_16; // Min (float) output 0 + Variable_16 = FVoxelNodeFunctions::Min(Variable_15, TVoxelRange(0.0f)); + + // * + TVoxelRange Variable_17; // * output 0 + Variable_17 = Variable_16 * Variable_16 * Variable_5; + + // Smooth Union + TVoxelRange Variable_21; // Smooth Union output 0 + Variable_21 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_20, Variable_17, Variable_8); + + // * + TVoxelRange Variable_19; // * output 0 + Variable_19 = Variable_21 * Variable_3; + + Outputs.Value = Variable_19; + } + + }; + + FVDI_Example_Crater_GraphInstance(UVDI_Example_Crater_Graph& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Example_Crater_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVDI_Example_Crater_GraphInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVDI_Example_Crater_GraphInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVDI_Example_Crater_GraphInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVDI_Example_Crater_GraphInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVDI_Example_Crater_Graph::UVDI_Example_Crater_Graph() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVDI_Example_Crater_Graph::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VDI_Example_Crater_Graph. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VDI_Example_Crater_Graph. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Example_Crater_Graph. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Example_Crater_Graph. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.h new file mode 100644 index 00000000..122df601 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VDI_Example_Crater_Graph.generated.h" + +UCLASS(Blueprintable) +class UVDI_Example_Crater_Graph : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + + UVDI_Example_Crater_Graph(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.cpp new file mode 100644 index 00000000..b794b1e9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.cpp @@ -0,0 +1,1316 @@ +// Copyright 2020 Phyronnaz + +#include "VDI_Ravine_Graph.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVDI_Ravine_GraphInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_6; // Data Item Parameters output 6 + v_flt Variable_7; // X output 0 + v_flt Variable_28; // vector - vector.- output 0 + v_flt Variable_27; // vector - vector.- output 0 + v_flt Variable_21; // vector - vector.- output 0 + v_flt Variable_34; // vector / float./ output 0 + v_flt Variable_16; // vector / float./ output 0 + v_flt Variable_33; // vector / float./ output 0 + v_flt Variable_20; // + output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_8; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_Fractal_0_Noise; + TStaticArray _3D_Gradient_Perturb_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Gradient Perturb Fractal + _3D_Gradient_Perturb_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Gradient_Perturb_Fractal_0_Noise.SetFractalOctavesAndGain(10, 0.5); + _3D_Gradient_Perturb_Fractal_0_Noise.SetFractalLacunarity(2.0); + _3D_Gradient_Perturb_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[0] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[1] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[2] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[3] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[4] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[5] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[6] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[7] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[8] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[9] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[10] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[11] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[12] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[13] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[14] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[15] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[16] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[17] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[18] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[19] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[20] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[21] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[22] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[23] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[24] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[25] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[26] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[27] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[28] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[29] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[30] = 10; + _3D_Gradient_Perturb_Fractal_0_LODToOctaves[31] = 10; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 0.000000; + Variable_1 = 100.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 0.000000; + Variable_5 = 0.000000; + BufferX.Variable_6 = 40.000000; + } + + // X + BufferX.Variable_7 = Context.GetLocalX(); + + // vector - vector.- + BufferX.Variable_28 = Variable_4 - Variable_1; + + // vector - vector.- + v_flt Variable_35; // vector - vector.- output 0 + Variable_35 = Variable_0 - Variable_3; + + // vector - vector.- + BufferX.Variable_27 = Variable_3 - Variable_0; + + // vector - vector.- + BufferX.Variable_21 = Variable_5 - Variable_2; + + // / + v_flt Variable_22; // / output 0 + Variable_22 = BufferX.Variable_6 / v_flt(4.0f); + + // vector + vector.+ + v_flt Variable_15; // vector + vector.+ output 0 + Variable_15 = Variable_2 + Variable_5; + + // vector + vector.+ + v_flt Variable_31; // vector + vector.+ output 0 + Variable_31 = Variable_0 + Variable_3; + + // vector - vector.- + v_flt Variable_17; // vector - vector.- output 0 + Variable_17 = Variable_2 - Variable_5; + + // vector + vector.+ + v_flt Variable_32; // vector + vector.+ output 0 + Variable_32 = Variable_1 + Variable_4; + + // vector - vector.- + v_flt Variable_36; // vector - vector.- output 0 + Variable_36 = Variable_1 - Variable_4; + + // vector / float./ + BufferX.Variable_34 = Variable_32 / v_flt(2.0f); + + // vector / float./ + BufferX.Variable_16 = Variable_15 / v_flt(2.0f); + + // Vector Length + v_flt Variable_18; // Vector Length output 0 + Variable_18 = FVoxelNodeFunctions::VectorLength(Variable_35, Variable_36, Variable_17); + + // vector / float./ + BufferX.Variable_33 = Variable_31 / v_flt(2.0f); + + // / + v_flt Variable_19; // / output 0 + Variable_19 = Variable_18 / v_flt(2.0f); + + // + + BufferX.Variable_20 = Variable_19 + Variable_22; + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_8 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_8 = Context.GetLocalY(); + + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + BufferX.Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 0.000000; + Variable_1 = 100.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 0.000000; + Variable_5 = 0.000000; + BufferX.Variable_6 = 40.000000; + } + + // X + BufferX.Variable_7 = Context.GetLocalX(); + + // vector - vector.- + BufferX.Variable_28 = Variable_4 - Variable_1; + + // vector - vector.- + v_flt Variable_35; // vector - vector.- output 0 + Variable_35 = Variable_0 - Variable_3; + + // vector - vector.- + BufferX.Variable_27 = Variable_3 - Variable_0; + + // vector - vector.- + BufferX.Variable_21 = Variable_5 - Variable_2; + + // / + v_flt Variable_22; // / output 0 + Variable_22 = BufferX.Variable_6 / v_flt(4.0f); + + // vector + vector.+ + v_flt Variable_15; // vector + vector.+ output 0 + Variable_15 = Variable_2 + Variable_5; + + // vector + vector.+ + v_flt Variable_31; // vector + vector.+ output 0 + Variable_31 = Variable_0 + Variable_3; + + // vector - vector.- + v_flt Variable_17; // vector - vector.- output 0 + Variable_17 = Variable_2 - Variable_5; + + // vector + vector.+ + v_flt Variable_32; // vector + vector.+ output 0 + Variable_32 = Variable_1 + Variable_4; + + // vector - vector.- + v_flt Variable_36; // vector - vector.- output 0 + Variable_36 = Variable_1 - Variable_4; + + // vector / float./ + BufferX.Variable_34 = Variable_32 / v_flt(2.0f); + + // vector / float./ + BufferX.Variable_16 = Variable_15 / v_flt(2.0f); + + // Vector Length + v_flt Variable_18; // Vector Length output 0 + Variable_18 = FVoxelNodeFunctions::VectorLength(Variable_35, Variable_36, Variable_17); + + // vector / float./ + BufferX.Variable_33 = Variable_31 / v_flt(2.0f); + + // / + v_flt Variable_19; // / output 0 + Variable_19 = Variable_18 / v_flt(2.0f); + + // + + BufferX.Variable_20 = Variable_19 + Variable_22; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_9; // Z output 0 + Variable_9 = Context.GetLocalZ(); + + // 3D Gradient Perturb Fractal + v_flt Variable_23; // 3D Gradient Perturb Fractal output 0 + v_flt Variable_24; // 3D Gradient Perturb Fractal output 1 + v_flt Variable_25; // 3D Gradient Perturb Fractal output 2 + Variable_23 = BufferX.Variable_7; + Variable_24 = BufferXY.Variable_8; + Variable_25 = Variable_9; + _3D_Gradient_Perturb_Fractal_0_Noise.GradientPerturbFractal_3D(Variable_23, Variable_24, Variable_25, v_flt(0.001f), _3D_Gradient_Perturb_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], v_flt(200.0f)); + + // vector - vector.- + v_flt Variable_29; // vector - vector.- output 0 + Variable_29 = Variable_23 - BufferX.Variable_33; + + // vector - vector.- + v_flt Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_25 - BufferX.Variable_16; + + // vector - vector.- + v_flt Variable_30; // vector - vector.- output 0 + Variable_30 = Variable_24 - BufferX.Variable_34; + + // Inverse Transform Position XZ + v_flt Variable_11; // Inverse Transform Position XZ output 0 + v_flt Variable_12; // Inverse Transform Position XZ output 1 + v_flt Variable_13; // Inverse Transform Position XZ output 2 + FVoxelMathNodeFunctions::InverseTransformPositionXZ(BufferX.Variable_27, BufferX.Variable_28, BufferX.Variable_21, v_flt(0.0f), v_flt(0.0f), v_flt(1.0f), Variable_29, Variable_30, Variable_14, Variable_11, Variable_12, Variable_13); + + // * + v_flt Variable_26; // * output 0 + Variable_26 = Variable_13 * v_flt(2.0f); + + // Triangular Prism SDF + v_flt Variable_10; // Triangular Prism SDF output 0 + Variable_10 = FVoxelSDFNodeFunctions::TriPrism(Variable_12, Variable_11, Variable_26, BufferX.Variable_6, BufferX.Variable_20); + + Outputs.Value = Variable_10; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_8; // Y output 0 + Variable_8 = Context.GetLocalY(); + + // Z + v_flt Variable_9; // Z output 0 + Variable_9 = Context.GetLocalZ(); + + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // Data Item Parameters output 4 + v_flt Variable_5; // Data Item Parameters output 5 + v_flt Variable_6; // Data Item Parameters output 6 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 0.000000; + Variable_1 = 100.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 0.000000; + Variable_5 = 0.000000; + Variable_6 = 40.000000; + } + + // X + v_flt Variable_7; // X output 0 + Variable_7 = Context.GetLocalX(); + + // vector - vector.- + v_flt Variable_28; // vector - vector.- output 0 + Variable_28 = Variable_4 - Variable_1; + + // vector - vector.- + v_flt Variable_35; // vector - vector.- output 0 + Variable_35 = Variable_0 - Variable_3; + + // 3D Gradient Perturb Fractal + v_flt Variable_23; // 3D Gradient Perturb Fractal output 0 + v_flt Variable_24; // 3D Gradient Perturb Fractal output 1 + v_flt Variable_25; // 3D Gradient Perturb Fractal output 2 + Variable_23 = Variable_7; + Variable_24 = Variable_8; + Variable_25 = Variable_9; + _3D_Gradient_Perturb_Fractal_0_Noise.GradientPerturbFractal_3D(Variable_23, Variable_24, Variable_25, v_flt(0.001f), _3D_Gradient_Perturb_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], v_flt(200.0f)); + + // vector - vector.- + v_flt Variable_27; // vector - vector.- output 0 + Variable_27 = Variable_3 - Variable_0; + + // vector - vector.- + v_flt Variable_21; // vector - vector.- output 0 + Variable_21 = Variable_5 - Variable_2; + + // / + v_flt Variable_22; // / output 0 + Variable_22 = Variable_6 / v_flt(4.0f); + + // vector + vector.+ + v_flt Variable_15; // vector + vector.+ output 0 + Variable_15 = Variable_2 + Variable_5; + + // vector + vector.+ + v_flt Variable_31; // vector + vector.+ output 0 + Variable_31 = Variable_0 + Variable_3; + + // vector - vector.- + v_flt Variable_17; // vector - vector.- output 0 + Variable_17 = Variable_2 - Variable_5; + + // vector + vector.+ + v_flt Variable_32; // vector + vector.+ output 0 + Variable_32 = Variable_1 + Variable_4; + + // vector - vector.- + v_flt Variable_36; // vector - vector.- output 0 + Variable_36 = Variable_1 - Variable_4; + + // vector / float./ + v_flt Variable_34; // vector / float./ output 0 + Variable_34 = Variable_32 / v_flt(2.0f); + + // vector / float./ + v_flt Variable_16; // vector / float./ output 0 + Variable_16 = Variable_15 / v_flt(2.0f); + + // Vector Length + v_flt Variable_18; // Vector Length output 0 + Variable_18 = FVoxelNodeFunctions::VectorLength(Variable_35, Variable_36, Variable_17); + + // vector / float./ + v_flt Variable_33; // vector / float./ output 0 + Variable_33 = Variable_31 / v_flt(2.0f); + + // vector - vector.- + v_flt Variable_29; // vector - vector.- output 0 + Variable_29 = Variable_23 - Variable_33; + + // / + v_flt Variable_19; // / output 0 + Variable_19 = Variable_18 / v_flt(2.0f); + + // vector - vector.- + v_flt Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_25 - Variable_16; + + // vector - vector.- + v_flt Variable_30; // vector - vector.- output 0 + Variable_30 = Variable_24 - Variable_34; + + // Inverse Transform Position XZ + v_flt Variable_11; // Inverse Transform Position XZ output 0 + v_flt Variable_12; // Inverse Transform Position XZ output 1 + v_flt Variable_13; // Inverse Transform Position XZ output 2 + FVoxelMathNodeFunctions::InverseTransformPositionXZ(Variable_27, Variable_28, Variable_21, v_flt(0.0f), v_flt(0.0f), v_flt(1.0f), Variable_29, Variable_30, Variable_14, Variable_11, Variable_12, Variable_13); + + // + + v_flt Variable_20; // + output 0 + Variable_20 = Variable_19 + Variable_22; + + // * + v_flt Variable_26; // * output 0 + Variable_26 = Variable_13 * v_flt(2.0f); + + // Triangular Prism SDF + v_flt Variable_10; // Triangular Prism SDF output 0 + Variable_10 = FVoxelSDFNodeFunctions::TriPrism(Variable_12, Variable_11, Variable_26, Variable_6, Variable_20); + + Outputs.Value = Variable_10; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_6; // Data Item Parameters output 6 + TVoxelRange Variable_7; // X output 0 + TVoxelRange Variable_28; // vector - vector.- output 0 + TVoxelRange Variable_27; // vector - vector.- output 0 + TVoxelRange Variable_21; // vector - vector.- output 0 + TVoxelRange Variable_34; // vector / float./ output 0 + TVoxelRange Variable_16; // vector / float./ output 0 + TVoxelRange Variable_33; // vector / float./ output 0 + TVoxelRange Variable_20; // + output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_8; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_Fractal_1_Noise; + TStaticArray _3D_Gradient_Perturb_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Gradient Perturb Fractal + _3D_Gradient_Perturb_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Gradient_Perturb_Fractal_1_Noise.SetFractalOctavesAndGain(10, 0.5); + _3D_Gradient_Perturb_Fractal_1_Noise.SetFractalLacunarity(2.0); + _3D_Gradient_Perturb_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[0] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[1] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[2] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[3] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[4] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[5] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[6] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[7] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[8] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[9] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[10] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[11] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[12] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[13] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[14] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[15] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[16] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[17] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[18] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[19] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[20] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[21] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[22] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[23] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[24] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[25] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[26] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[27] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[28] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[29] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[30] = 10; + _3D_Gradient_Perturb_Fractal_1_LODToOctaves[31] = 10; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Y + TVoxelRange Variable_8; // Y output 0 + Variable_8 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_9; // Z output 0 + Variable_9 = Context.GetLocalZ(); + + // Data Item Parameters + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + TVoxelRange Variable_4; // Data Item Parameters output 4 + TVoxelRange Variable_5; // Data Item Parameters output 5 + TVoxelRange Variable_6; // Data Item Parameters output 6 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 7) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + Variable_4 = (*Context.Items.CustomData).GetData()[4]; + Variable_5 = (*Context.Items.CustomData).GetData()[5]; + Variable_6 = (*Context.Items.CustomData).GetData()[6]; + } + else + { + Variable_0 = 0.000000; + Variable_1 = 100.000000; + Variable_2 = 0.000000; + Variable_3 = 0.000000; + Variable_4 = 0.000000; + Variable_5 = 0.000000; + Variable_6 = 40.000000; + } + + // X + TVoxelRange Variable_7; // X output 0 + Variable_7 = Context.GetLocalX(); + + // vector - vector.- + TVoxelRange Variable_28; // vector - vector.- output 0 + Variable_28 = Variable_4 - Variable_1; + + // vector - vector.- + TVoxelRange Variable_35; // vector - vector.- output 0 + Variable_35 = Variable_0 - Variable_3; + + // 3D Gradient Perturb Fractal + TVoxelRange Variable_23; // 3D Gradient Perturb Fractal output 0 + TVoxelRange Variable_24; // 3D Gradient Perturb Fractal output 1 + TVoxelRange Variable_25; // 3D Gradient Perturb Fractal output 2 + Variable_23 = TVoxelRange::FromList(Variable_7.Min - 2 * TVoxelRange(200.0f).Max, Variable_7.Max + 2 * TVoxelRange(200.0f).Max); + Variable_24 = TVoxelRange::FromList(Variable_8.Min - 2 * TVoxelRange(200.0f).Max, Variable_8.Max + 2 * TVoxelRange(200.0f).Max); + Variable_25 = TVoxelRange::FromList(Variable_9.Min - 2 * TVoxelRange(200.0f).Max, Variable_9.Max + 2 * TVoxelRange(200.0f).Max); + + // vector - vector.- + TVoxelRange Variable_27; // vector - vector.- output 0 + Variable_27 = Variable_3 - Variable_0; + + // vector - vector.- + TVoxelRange Variable_21; // vector - vector.- output 0 + Variable_21 = Variable_5 - Variable_2; + + // / + TVoxelRange Variable_22; // / output 0 + Variable_22 = Variable_6 / TVoxelRange(4.0f); + + // vector + vector.+ + TVoxelRange Variable_15; // vector + vector.+ output 0 + Variable_15 = Variable_2 + Variable_5; + + // vector + vector.+ + TVoxelRange Variable_31; // vector + vector.+ output 0 + Variable_31 = Variable_0 + Variable_3; + + // vector - vector.- + TVoxelRange Variable_17; // vector - vector.- output 0 + Variable_17 = Variable_2 - Variable_5; + + // vector + vector.+ + TVoxelRange Variable_32; // vector + vector.+ output 0 + Variable_32 = Variable_1 + Variable_4; + + // vector - vector.- + TVoxelRange Variable_36; // vector - vector.- output 0 + Variable_36 = Variable_1 - Variable_4; + + // vector / float./ + TVoxelRange Variable_34; // vector / float./ output 0 + Variable_34 = Variable_32 / TVoxelRange(2.0f); + + // vector / float./ + TVoxelRange Variable_16; // vector / float./ output 0 + Variable_16 = Variable_15 / TVoxelRange(2.0f); + + // Vector Length + TVoxelRange Variable_18; // Vector Length output 0 + Variable_18 = FVoxelNodeFunctions::VectorLength(Variable_35, Variable_36, Variable_17); + + // vector / float./ + TVoxelRange Variable_33; // vector / float./ output 0 + Variable_33 = Variable_31 / TVoxelRange(2.0f); + + // vector - vector.- + TVoxelRange Variable_29; // vector - vector.- output 0 + Variable_29 = Variable_23 - Variable_33; + + // / + TVoxelRange Variable_19; // / output 0 + Variable_19 = Variable_18 / TVoxelRange(2.0f); + + // vector - vector.- + TVoxelRange Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_25 - Variable_16; + + // vector - vector.- + TVoxelRange Variable_30; // vector - vector.- output 0 + Variable_30 = Variable_24 - Variable_34; + + // Inverse Transform Position XZ + TVoxelRange Variable_11; // Inverse Transform Position XZ output 0 + TVoxelRange Variable_12; // Inverse Transform Position XZ output 1 + TVoxelRange Variable_13; // Inverse Transform Position XZ output 2 + FVoxelMathNodeFunctions::InverseTransformPositionXZ(Variable_27, Variable_28, Variable_21, TVoxelRange(0.0f), TVoxelRange(0.0f), TVoxelRange(1.0f), Variable_29, Variable_30, Variable_14, Variable_11, Variable_12, Variable_13); + + // + + TVoxelRange Variable_20; // + output 0 + Variable_20 = Variable_19 + Variable_22; + + // * + TVoxelRange Variable_26; // * output 0 + Variable_26 = Variable_13 * TVoxelRange(2.0f); + + // Triangular Prism SDF + TVoxelRange Variable_10; // Triangular Prism SDF output 0 + Variable_10 = FVoxelSDFNodeFunctions::TriPrism(Variable_12, Variable_11, Variable_26, Variable_6, Variable_20); + + Outputs.Value = Variable_10; + } + + }; + + FVDI_Ravine_GraphInstance(UVDI_Ravine_Graph& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Ravine_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVDI_Ravine_GraphInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVDI_Ravine_GraphInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVDI_Ravine_GraphInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVDI_Ravine_GraphInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVDI_Ravine_Graph::UVDI_Ravine_Graph() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVDI_Ravine_Graph::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VDI_Ravine_Graph. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VDI_Ravine_Graph. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Ravine_Graph. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Ravine_Graph. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.h new file mode 100644 index 00000000..9b62de88 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VDI_Ravine_Graph.generated.h" + +UCLASS(Blueprintable) +class UVDI_Ravine_Graph : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + + UVDI_Ravine_Graph(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.cpp new file mode 100644 index 00000000..0c491d3f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.cpp @@ -0,0 +1,954 @@ +// Copyright 2020 Phyronnaz + +#include "VDI_Sphere_Graph.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVDI_Sphere_GraphInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + v_flt Variable_4; // X output 0 + v_flt Variable_11; // * output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_5; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_0_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 4) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + } + else + { + BufferX.Variable_0 = -19.096230; + BufferX.Variable_1 = 0.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 40.000000; + } + + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // 1 / X + v_flt Variable_10; // 1 / X output 0 + Variable_10 = FVoxelNodeFunctions::OneOverX(BufferX.Variable_3); + + // * + BufferX.Variable_11 = Variable_10 * v_flt(0.1f); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_5 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Data Item Parameters + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 4) + { + BufferX.Variable_0 = (*Context.Items.CustomData).GetData()[0]; + BufferX.Variable_1 = (*Context.Items.CustomData).GetData()[1]; + BufferX.Variable_2 = (*Context.Items.CustomData).GetData()[2]; + BufferX.Variable_3 = (*Context.Items.CustomData).GetData()[3]; + } + else + { + BufferX.Variable_0 = -19.096230; + BufferX.Variable_1 = 0.000000; + BufferX.Variable_2 = 0.000000; + BufferX.Variable_3 = 40.000000; + } + + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // Y + BufferXY.Variable_5 = Context.GetLocalY(); + + // 1 / X + v_flt Variable_10; // 1 / X output 0 + Variable_10 = FVoxelNodeFunctions::OneOverX(BufferX.Variable_3); + + // * + BufferX.Variable_11 = Variable_10 * v_flt(0.1f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // 3D Gradient Perturb + v_flt Variable_7; // 3D Gradient Perturb output 0 + v_flt Variable_8; // 3D Gradient Perturb output 1 + v_flt Variable_9; // 3D Gradient Perturb output 2 + Variable_7 = BufferX.Variable_4; + Variable_8 = BufferXY.Variable_5; + Variable_9 = Variable_6; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_7, Variable_8, Variable_9, BufferX.Variable_11, BufferX.Variable_3); + + // vector - vector.- + v_flt Variable_15; // vector - vector.- output 0 + Variable_15 = Variable_9 - BufferX.Variable_2; + + // vector - vector.- + v_flt Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_8 - BufferX.Variable_1; + + // vector - vector.- + v_flt Variable_13; // vector - vector.- output 0 + Variable_13 = Variable_7 - BufferX.Variable_0; + + // Sphere SDF + v_flt Variable_12; // Sphere SDF output 0 + Variable_12 = FVoxelSDFNodeFunctions::Sphere(Variable_13, Variable_14, Variable_15, BufferX.Variable_3); + + Outputs.Value = Variable_12; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Data Item Parameters + v_flt Variable_0; // Data Item Parameters output 0 + v_flt Variable_1; // Data Item Parameters output 1 + v_flt Variable_2; // Data Item Parameters output 2 + v_flt Variable_3; // Data Item Parameters output 3 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 4) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + } + else + { + Variable_0 = -19.096230; + Variable_1 = 0.000000; + Variable_2 = 0.000000; + Variable_3 = 40.000000; + } + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // Y + v_flt Variable_5; // Y output 0 + Variable_5 = Context.GetLocalY(); + + // 1 / X + v_flt Variable_10; // 1 / X output 0 + Variable_10 = FVoxelNodeFunctions::OneOverX(Variable_3); + + // * + v_flt Variable_11; // * output 0 + Variable_11 = Variable_10 * v_flt(0.1f); + + // 3D Gradient Perturb + v_flt Variable_7; // 3D Gradient Perturb output 0 + v_flt Variable_8; // 3D Gradient Perturb output 1 + v_flt Variable_9; // 3D Gradient Perturb output 2 + Variable_7 = Variable_4; + Variable_8 = Variable_5; + Variable_9 = Variable_6; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_7, Variable_8, Variable_9, Variable_11, Variable_3); + + // vector - vector.- + v_flt Variable_15; // vector - vector.- output 0 + Variable_15 = Variable_9 - Variable_2; + + // vector - vector.- + v_flt Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_8 - Variable_1; + + // vector - vector.- + v_flt Variable_13; // vector - vector.- output 0 + Variable_13 = Variable_7 - Variable_0; + + // Sphere SDF + v_flt Variable_12; // Sphere SDF output 0 + Variable_12 = FVoxelSDFNodeFunctions::Sphere(Variable_13, Variable_14, Variable_15, Variable_3); + + Outputs.Value = Variable_12; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + TVoxelRange Variable_4; // X output 0 + TVoxelRange Variable_11; // * output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_5; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_1_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Data Item Parameters + TVoxelRange Variable_0; // Data Item Parameters output 0 + TVoxelRange Variable_1; // Data Item Parameters output 1 + TVoxelRange Variable_2; // Data Item Parameters output 2 + TVoxelRange Variable_3; // Data Item Parameters output 3 + if (Context.Items.CustomData && Context.Items.CustomData->Num() == 4) + { + Variable_0 = (*Context.Items.CustomData).GetData()[0]; + Variable_1 = (*Context.Items.CustomData).GetData()[1]; + Variable_2 = (*Context.Items.CustomData).GetData()[2]; + Variable_3 = (*Context.Items.CustomData).GetData()[3]; + } + else + { + Variable_0 = -19.096230; + Variable_1 = 0.000000; + Variable_2 = 0.000000; + Variable_3 = 40.000000; + } + + // X + TVoxelRange Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // Y + TVoxelRange Variable_5; // Y output 0 + Variable_5 = Context.GetLocalY(); + + // 1 / X + TVoxelRange Variable_10; // 1 / X output 0 + Variable_10 = FVoxelNodeFunctions::OneOverX(Variable_3); + + // * + TVoxelRange Variable_11; // * output 0 + Variable_11 = Variable_10 * TVoxelRange(0.1f); + + // 3D Gradient Perturb + TVoxelRange Variable_7; // 3D Gradient Perturb output 0 + TVoxelRange Variable_8; // 3D Gradient Perturb output 1 + TVoxelRange Variable_9; // 3D Gradient Perturb output 2 + Variable_7 = TVoxelRange::FromList(Variable_4.Min - 2 * Variable_3.Max, Variable_4.Max + 2 * Variable_3.Max); + Variable_8 = TVoxelRange::FromList(Variable_5.Min - 2 * Variable_3.Max, Variable_5.Max + 2 * Variable_3.Max); + Variable_9 = TVoxelRange::FromList(Variable_6.Min - 2 * Variable_3.Max, Variable_6.Max + 2 * Variable_3.Max); + + // vector - vector.- + TVoxelRange Variable_15; // vector - vector.- output 0 + Variable_15 = Variable_9 - Variable_2; + + // vector - vector.- + TVoxelRange Variable_14; // vector - vector.- output 0 + Variable_14 = Variable_8 - Variable_1; + + // vector - vector.- + TVoxelRange Variable_13; // vector - vector.- output 0 + Variable_13 = Variable_7 - Variable_0; + + // Sphere SDF + TVoxelRange Variable_12; // Sphere SDF output 0 + Variable_12 = FVoxelSDFNodeFunctions::Sphere(Variable_13, Variable_14, Variable_15, Variable_3); + + Outputs.Value = Variable_12; + } + + }; + + FVDI_Sphere_GraphInstance(UVDI_Sphere_Graph& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVDI_Sphere_GraphInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVDI_Sphere_GraphInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVDI_Sphere_GraphInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVDI_Sphere_GraphInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVDI_Sphere_GraphInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVDI_Sphere_Graph::UVDI_Sphere_Graph() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVDI_Sphere_Graph::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VDI_Sphere_Graph. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VDI_Sphere_Graph. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Sphere_Graph. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VDI_Sphere_Graph. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.h new file mode 100644 index 00000000..0ff915db --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VDI_Sphere_Graph.generated.h" + +UCLASS(Blueprintable) +class UVDI_Sphere_Graph : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + + UVDI_Sphere_Graph(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.cpp new file mode 100644 index 00000000..72228d2c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.cpp @@ -0,0 +1,1045 @@ +// Copyright 2020 Phyronnaz + +#include "VG_Example_Craters.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVG_Example_CratersInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Radius; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_15; // Radius = 200.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_4; // X output 0 + v_flt Variable_8; // X output 0 + v_flt Variable_0; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_5; // Y output 0 + v_flt Variable_1; // Y output 0 + v_flt Variable_9; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Radius = 200.0 + BufferConstant.Variable_15 = Params.Radius; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(4.0); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // X + BufferX.Variable_8 = Context.GetLocalX(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_5 = Context.GetLocalY(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + BufferXY.Variable_9 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // X + BufferX.Variable_8 = Context.GetLocalX(); + + // Y + BufferXY.Variable_5 = Context.GetLocalY(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + BufferXY.Variable_9 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_10; // Z output 0 + Variable_10 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Normalize.Vector Length + v_flt Variable_17; // Normalize.Vector Length output 0 + Variable_17 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_4, BufferXY.Variable_5, Variable_6); + + // Data Item Sample + v_flt Variable_7; // Data Item Sample output 0 + Variable_7 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, BufferX.Variable_8, BufferXY.Variable_9, Variable_10, v_flt(0.0f), v_flt(0.0f), 1u, EVoxelDataItemCombineMode::Sum); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferXY.Variable_1, Variable_2); + + // Normalize./ + v_flt Variable_18; // Normalize./ output 0 + Variable_18 = BufferX.Variable_4 / Variable_17; + + // Normalize./ + v_flt Variable_19; // Normalize./ output 0 + Variable_19 = BufferXY.Variable_5 / Variable_17; + + // Normalize./ + v_flt Variable_20; // Normalize./ output 0 + Variable_20 = Variable_6 / Variable_17; + + // 3D Perlin Noise Fractal + v_flt Variable_11; // 3D Perlin Noise Fractal output 0 + Variable_11 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(Variable_18, Variable_19, Variable_20, v_flt(1.0f), _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_11 = FMath::Clamp(Variable_11, -0.686521, 0.684919); + + // * + v_flt Variable_12; // * output 0 + Variable_12 = Variable_11 * v_flt(20.0f); + + // + + v_flt Variable_14; // + output 0 + Variable_14 = BufferConstant.Variable_15 + Variable_12 + Variable_7; + + // - + v_flt Variable_13; // - output 0 + Variable_13 = Variable_3 - Variable_14; + + // Set High Quality Value.* + v_flt Variable_16; // Set High Quality Value.* output 0 + Variable_16 = Variable_13 * v_flt(0.2f); + + Outputs.Value = Variable_16; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // X + v_flt Variable_8; // X output 0 + Variable_8 = Context.GetLocalX(); + + // Z + v_flt Variable_10; // Z output 0 + Variable_10 = Context.GetLocalZ(); + + // Y + v_flt Variable_5; // Y output 0 + Variable_5 = Context.GetLocalY(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Y + v_flt Variable_9; // Y output 0 + Variable_9 = Context.GetLocalY(); + + // Normalize.Vector Length + v_flt Variable_17; // Normalize.Vector Length output 0 + Variable_17 = FVoxelNodeFunctions::VectorLength(Variable_4, Variable_5, Variable_6); + + // Data Item Sample + v_flt Variable_7; // Data Item Sample output 0 + Variable_7 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_8, Variable_9, Variable_10, v_flt(0.0f), v_flt(0.0f), 1u, EVoxelDataItemCombineMode::Sum); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // Normalize./ + v_flt Variable_18; // Normalize./ output 0 + Variable_18 = Variable_4 / Variable_17; + + // Normalize./ + v_flt Variable_19; // Normalize./ output 0 + Variable_19 = Variable_5 / Variable_17; + + // Normalize./ + v_flt Variable_20; // Normalize./ output 0 + Variable_20 = Variable_6 / Variable_17; + + // 3D Perlin Noise Fractal + v_flt Variable_11; // 3D Perlin Noise Fractal output 0 + Variable_11 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(Variable_18, Variable_19, Variable_20, v_flt(1.0f), _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_11 = FMath::Clamp(Variable_11, -0.686521, 0.684919); + + // * + v_flt Variable_12; // * output 0 + Variable_12 = Variable_11 * v_flt(20.0f); + + // + + v_flt Variable_14; // + output 0 + Variable_14 = BufferConstant.Variable_15 + Variable_12 + Variable_7; + + // - + v_flt Variable_13; // - output 0 + Variable_13 = Variable_3 - Variable_14; + + // Set High Quality Value.* + v_flt Variable_16; // Set High Quality Value.* output 0 + Variable_16 = Variable_13 * v_flt(0.2f); + + Outputs.Value = Variable_16; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_12; // Radius = 200.0 output 0 + TVoxelRange Variable_9; // * output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_0; // X output 0 + TVoxelRange Variable_5; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_6; // Y output 0 + TVoxelRange Variable_1; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(4.0); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Radius = 200.0 + BufferConstant.Variable_12 = Params.Radius; + + // 3D Perlin Noise Fractal + TVoxelRange Variable_8; // 3D Perlin Noise Fractal output 0 + Variable_8 = { -0.686521f, 0.684919f }; + + // * + BufferConstant.Variable_9 = Variable_8 * TVoxelRange(20.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_6; // Y output 0 + Variable_6 = Context.GetLocalY(); + + // X + TVoxelRange Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Y + TVoxelRange Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_7; // Z output 0 + Variable_7 = Context.GetLocalZ(); + + // X + TVoxelRange Variable_5; // X output 0 + Variable_5 = Context.GetLocalX(); + + // Data Item Sample + TVoxelRange Variable_4; // Data Item Sample output 0 + Variable_4 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_5, Variable_6, Variable_7, TVoxelRange(0.0f), TVoxelRange(0.0f), 1u, EVoxelDataItemCombineMode::Sum); + + // Vector Length + TVoxelRange Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // + + TVoxelRange Variable_11; // + output 0 + Variable_11 = BufferConstant.Variable_12 + BufferConstant.Variable_9 + Variable_4; + + // - + TVoxelRange Variable_10; // - output 0 + Variable_10 = Variable_3 - Variable_11; + + // Set High Quality Value.* + TVoxelRange Variable_13; // Set High Quality Value.* output 0 + Variable_13 = Variable_10 * TVoxelRange(0.2f); + + Outputs.Value = Variable_13; + } + + }; + + FVG_Example_CratersInstance(UVG_Example_Craters& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Radius + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVG_Example_CratersInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVG_Example_CratersInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVG_Example_CratersInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_CratersInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVG_Example_CratersInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVG_Example_CratersInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVG_Example_CratersInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVG_Example_CratersInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVG_Example_Craters::UVG_Example_Craters() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVG_Example_Craters::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VG_Example_Craters. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VG_Example_Craters. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Craters. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Craters. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.h new file mode 100644 index 00000000..04c6000d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Craters.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VG_Example_Craters.generated.h" + +UCLASS(Blueprintable) +class UVG_Example_Craters : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Radius")) + float Radius = 200.0; + + UVG_Example_Craters(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.cpp new file mode 100644 index 00000000..c311913b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.cpp @@ -0,0 +1,1131 @@ +// Copyright 2020 Phyronnaz + +#include "VG_Example_Dunes.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVG_Example_DunesInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Direction_X; + const float Direction_Y; + const float Dune_Frequency; + const float Height; + const float Noise_Frequency; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_17; // Dune Frequency = 0.002 output 0 + v_flt Variable_18; // Noise Frequency = 0.001 output 0 + v_flt Variable_19; // Height = 75.0 output 0 + v_flt Variable_23; // Normalize./ output 0 + v_flt Variable_22; // Normalize./ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_4; // X output 0 + v_flt Variable_27; // vector2 * float.* output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // * output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Direction Y = 1.0 + v_flt Variable_14; // Direction Y = 1.0 output 0 + Variable_14 = Params.Direction_Y; + + // Dune Frequency = 0.002 + BufferConstant.Variable_17 = Params.Dune_Frequency; + + // Noise Frequency = 0.001 + BufferConstant.Variable_18 = Params.Noise_Frequency; + + // Height = 75.0 + BufferConstant.Variable_19 = Params.Height; + + // Direction X = 0.4 + v_flt Variable_13; // Direction X = 0.4 output 0 + Variable_13 = Params.Direction_X; + + // Normalize.Vector Length + v_flt Variable_21; // Normalize.Vector Length output 0 + Variable_21 = FVoxelNodeFunctions::VectorLength(Variable_13, Variable_14, v_flt(0.0f)); + + // Normalize.== + bool Variable_24; // Normalize.== output 0 + Variable_24 = Variable_21 == v_flt(0.0f); + + // Normalize.Switch (float) + v_flt Variable_25; // Normalize.Switch (float) output 0 + Variable_25 = FVoxelNodeFunctions::Switch(v_flt(1.0f), Variable_21, Variable_24); + + // Normalize./ + BufferConstant.Variable_23 = Variable_14 / Variable_25; + + // Normalize./ + BufferConstant.Variable_22 = Variable_13 / Variable_25; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + v_flt Variable_11; // X output 0 + Variable_11 = Context.GetLocalX(); + + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // vector2 * vector2.* + v_flt Variable_26; // vector2 * vector2.* output 0 + Variable_26 = Variable_11 * BufferConstant.Variable_22; + + // vector2 * float.* + BufferX.Variable_27 = Variable_26 * BufferConstant.Variable_17; + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_9; // 2D Perlin Noise Fractal output 0 + Variable_9 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_4, Variable_3, BufferConstant.Variable_18, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_9 = FMath::Clamp(Variable_9, -0.663838, 0.649431); + + // vector2 * vector2.* + v_flt Variable_15; // vector2 * vector2.* output 0 + Variable_15 = Variable_12 * BufferConstant.Variable_23; + + // vector2 * float.* + v_flt Variable_16; // vector2 * float.* output 0 + Variable_16 = Variable_15 * BufferConstant.Variable_17; + + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_9 + BufferX.Variable_27 + Variable_16; + + // * + v_flt Variable_8; // * output 0 + Variable_8 = v_flt(3.141593f) * Variable_10; + + // SIN + v_flt Variable_5; // SIN output 0 + Variable_5 = FVoxelNodeFunctions::Sin(Variable_8); + + // ABS + v_flt Variable_6; // ABS output 0 + Variable_6 = FVoxelNodeFunctions::Abs(Variable_5); + + // * -1 + v_flt Variable_7; // * -1 output 0 + Variable_7 = Variable_6 * -1; + + // * + BufferXY.Variable_2 = Variable_7 * BufferConstant.Variable_19; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // X + v_flt Variable_11; // X output 0 + Variable_11 = Context.GetLocalX(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // X + BufferX.Variable_4 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_9; // 2D Perlin Noise Fractal output 0 + Variable_9 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_4, Variable_3, BufferConstant.Variable_18, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_9 = FMath::Clamp(Variable_9, -0.663838, 0.649431); + + // vector2 * vector2.* + v_flt Variable_15; // vector2 * vector2.* output 0 + Variable_15 = Variable_12 * BufferConstant.Variable_23; + + // vector2 * vector2.* + v_flt Variable_26; // vector2 * vector2.* output 0 + Variable_26 = Variable_11 * BufferConstant.Variable_22; + + // vector2 * float.* + BufferX.Variable_27 = Variable_26 * BufferConstant.Variable_17; + + // vector2 * float.* + v_flt Variable_16; // vector2 * float.* output 0 + Variable_16 = Variable_15 * BufferConstant.Variable_17; + + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_9 + BufferX.Variable_27 + Variable_16; + + // * + v_flt Variable_8; // * output 0 + Variable_8 = v_flt(3.141593f) * Variable_10; + + // SIN + v_flt Variable_5; // SIN output 0 + Variable_5 = FVoxelNodeFunctions::Sin(Variable_8); + + // ABS + v_flt Variable_6; // ABS output 0 + Variable_6 = FVoxelNodeFunctions::Abs(Variable_5); + + // * -1 + v_flt Variable_7; // * -1 output 0 + Variable_7 = Variable_6 * -1; + + // * + BufferXY.Variable_2 = Variable_7 * BufferConstant.Variable_19; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // - + v_flt Variable_1; // - output 0 + Variable_1 = Variable_0 - BufferXY.Variable_2; + + // Set High Quality Value.* + v_flt Variable_20; // Set High Quality Value.* output 0 + Variable_20 = Variable_1 * v_flt(0.2f); + + Outputs.Value = Variable_20; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // X + v_flt Variable_11; // X output 0 + Variable_11 = Context.GetLocalX(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_9; // 2D Perlin Noise Fractal output 0 + Variable_9 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(Variable_4, Variable_3, BufferConstant.Variable_18, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_9 = FMath::Clamp(Variable_9, -0.663838, 0.649431); + + // vector2 * vector2.* + v_flt Variable_15; // vector2 * vector2.* output 0 + Variable_15 = Variable_12 * BufferConstant.Variable_23; + + // vector2 * vector2.* + v_flt Variable_26; // vector2 * vector2.* output 0 + Variable_26 = Variable_11 * BufferConstant.Variable_22; + + // vector2 * float.* + v_flt Variable_27; // vector2 * float.* output 0 + Variable_27 = Variable_26 * BufferConstant.Variable_17; + + // vector2 * float.* + v_flt Variable_16; // vector2 * float.* output 0 + Variable_16 = Variable_15 * BufferConstant.Variable_17; + + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_9 + Variable_27 + Variable_16; + + // * + v_flt Variable_8; // * output 0 + Variable_8 = v_flt(3.141593f) * Variable_10; + + // SIN + v_flt Variable_5; // SIN output 0 + Variable_5 = FVoxelNodeFunctions::Sin(Variable_8); + + // ABS + v_flt Variable_6; // ABS output 0 + Variable_6 = FVoxelNodeFunctions::Abs(Variable_5); + + // * -1 + v_flt Variable_7; // * -1 output 0 + Variable_7 = Variable_6 * -1; + + // * + v_flt Variable_2; // * output 0 + Variable_2 = Variable_7 * BufferConstant.Variable_19; + + // - + v_flt Variable_1; // - output 0 + Variable_1 = Variable_0 - Variable_2; + + // Set High Quality Value.* + v_flt Variable_20; // Set High Quality Value.* output 0 + Variable_20 = Variable_1 * v_flt(0.2f); + + Outputs.Value = Variable_20; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_13; // Dune Frequency = 0.002 output 0 + TVoxelRange Variable_14; // Height = 75.0 output 0 + TVoxelRange Variable_16; // Normalize.Range Union output 0 + TVoxelRange Variable_7; // 2D Perlin Noise Fractal output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_18; // vector2 * float.* output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_2; // * output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Dune Frequency = 0.002 + BufferConstant.Variable_13 = Params.Dune_Frequency; + + // Height = 75.0 + BufferConstant.Variable_14 = Params.Height; + + // Normalize.Range Union + BufferConstant.Variable_16 = FVoxelNodeFunctions::Union(TVoxelRange(-1.0f), TVoxelRange(1.0f)); + + // 2D Perlin Noise Fractal + BufferConstant.Variable_7 = { -0.663838f, 0.649431f }; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Y + TVoxelRange Variable_10; // Y output 0 + Variable_10 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // X + TVoxelRange Variable_9; // X output 0 + Variable_9 = Context.GetLocalX(); + + // vector2 * vector2.* + TVoxelRange Variable_11; // vector2 * vector2.* output 0 + Variable_11 = Variable_10 * BufferConstant.Variable_16; + + // vector2 * vector2.* + TVoxelRange Variable_17; // vector2 * vector2.* output 0 + Variable_17 = Variable_9 * BufferConstant.Variable_16; + + // vector2 * float.* + TVoxelRange Variable_12; // vector2 * float.* output 0 + Variable_12 = Variable_11 * BufferConstant.Variable_13; + + // vector2 * float.* + TVoxelRange Variable_18; // vector2 * float.* output 0 + Variable_18 = Variable_17 * BufferConstant.Variable_13; + + // + + TVoxelRange Variable_8; // + output 0 + Variable_8 = BufferConstant.Variable_7 + Variable_18 + Variable_12; + + // * + TVoxelRange Variable_6; // * output 0 + Variable_6 = TVoxelRange(3.141593f) * Variable_8; + + // SIN + TVoxelRange Variable_3; // SIN output 0 + Variable_3 = FVoxelNodeFunctions::Sin(Variable_6); + + // ABS + TVoxelRange Variable_4; // ABS output 0 + Variable_4 = FVoxelNodeFunctions::Abs(Variable_3); + + // * -1 + TVoxelRange Variable_5; // * -1 output 0 + Variable_5 = Variable_4 * -1; + + // * + TVoxelRange Variable_2; // * output 0 + Variable_2 = Variable_5 * BufferConstant.Variable_14; + + // - + TVoxelRange Variable_1; // - output 0 + Variable_1 = Variable_0 - Variable_2; + + // Set High Quality Value.* + TVoxelRange Variable_15; // Set High Quality Value.* output 0 + Variable_15 = Variable_1 * TVoxelRange(0.2f); + + Outputs.Value = Variable_15; + } + + }; + + FVG_Example_DunesInstance(UVG_Example_Dunes& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Direction_X, + Object.Direction_Y, + Object.Dune_Frequency, + Object.Height, + Object.Noise_Frequency + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVG_Example_DunesInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVG_Example_DunesInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVG_Example_DunesInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_DunesInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVG_Example_DunesInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVG_Example_DunesInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVG_Example_DunesInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVG_Example_DunesInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVG_Example_Dunes::UVG_Example_Dunes() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVG_Example_Dunes::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VG_Example_Dunes. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VG_Example_Dunes. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Dunes. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Dunes. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.h new file mode 100644 index 00000000..74ce4cee --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.h @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VG_Example_Dunes.generated.h" + +UCLASS(Blueprintable) +class UVG_Example_Dunes : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // The direction of the noise. Will be normalized + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Direction X")) + float Direction_X = 0.4; + // The direction of the noise. Will be normalized + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Direction Y")) + float Direction_Y = 1.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Dune Frequency")) + float Dune_Frequency = 0.002; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Height")) + float Height = 75.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Frequency")) + float Noise_Frequency = 0.001; + + UVG_Example_Dunes(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.cpp new file mode 100644 index 00000000..350a1d0e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.cpp @@ -0,0 +1,1484 @@ +// Copyright 2020 Phyronnaz + +#include "VG_Example_Erosion.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVG_Example_ErosionInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Erosion_Material_Offset; + const float Erosion_Material_Strength; + const float Erosion_Strength; + const float Height; + const FName Rocks; + const FName Snow; + const float Valleys_Height; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_10; // Height = 500.0 output 0 + v_flt Variable_13; // Valleys Height = -0.5 output 0 + v_flt Variable_9; // Erosion Strength = 0.008 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_2; // X output 0 + v_flt Variable_11; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_0; // 2D Noise SDF.+ output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Height = 500.0 + BufferConstant.Variable_10 = Params.Height; + + // Valleys Height = -0.5 + BufferConstant.Variable_13 = Params.Valleys_Height; + + // Erosion Strength = 0.008 + BufferConstant.Variable_9 = Params.Erosion_Strength; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_0_LODToOctaves; + FVoxelFastNoise _2D_Erosion_0_Noise; + TStaticArray _2D_Erosion_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + // Init of 2D Erosion + _2D_Erosion_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Erosion_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Erosion_0_Noise.SetFractalOctavesAndGain(5, 0.5); + _2D_Erosion_0_Noise.SetFractalLacunarity(2.0); + _2D_Erosion_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Erosion_0_Noise.SetCellularJitter(0.5); + _2D_Erosion_0_LODToOctaves[0] = 5; + _2D_Erosion_0_LODToOctaves[1] = 5; + _2D_Erosion_0_LODToOctaves[2] = 5; + _2D_Erosion_0_LODToOctaves[3] = 5; + _2D_Erosion_0_LODToOctaves[4] = 5; + _2D_Erosion_0_LODToOctaves[5] = 5; + _2D_Erosion_0_LODToOctaves[6] = 5; + _2D_Erosion_0_LODToOctaves[7] = 5; + _2D_Erosion_0_LODToOctaves[8] = 5; + _2D_Erosion_0_LODToOctaves[9] = 5; + _2D_Erosion_0_LODToOctaves[10] = 5; + _2D_Erosion_0_LODToOctaves[11] = 5; + _2D_Erosion_0_LODToOctaves[12] = 5; + _2D_Erosion_0_LODToOctaves[13] = 5; + _2D_Erosion_0_LODToOctaves[14] = 5; + _2D_Erosion_0_LODToOctaves[15] = 5; + _2D_Erosion_0_LODToOctaves[16] = 5; + _2D_Erosion_0_LODToOctaves[17] = 5; + _2D_Erosion_0_LODToOctaves[18] = 5; + _2D_Erosion_0_LODToOctaves[19] = 5; + _2D_Erosion_0_LODToOctaves[20] = 5; + _2D_Erosion_0_LODToOctaves[21] = 5; + _2D_Erosion_0_LODToOctaves[22] = 5; + _2D_Erosion_0_LODToOctaves[23] = 5; + _2D_Erosion_0_LODToOctaves[24] = 5; + _2D_Erosion_0_LODToOctaves[25] = 5; + _2D_Erosion_0_LODToOctaves[26] = 5; + _2D_Erosion_0_LODToOctaves[27] = 5; + _2D_Erosion_0_LODToOctaves[28] = 5; + _2D_Erosion_0_LODToOctaves[29] = 5; + _2D_Erosion_0_LODToOctaves[30] = 5; + _2D_Erosion_0_LODToOctaves[31] = 5; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_2 = Context.GetLocalX(); + + // X + BufferX.Variable_11 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_14; // 2D Perlin Noise Fractal output 0 + v_flt Variable_15; // 2D Perlin Noise Fractal output 1 + v_flt Variable_16; // 2D Perlin Noise Fractal output 2 + Variable_14 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D_Deriv(BufferX.Variable_2, Variable_3, v_flt(0.001f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_15,Variable_16); + Variable_14 = FMath::Clamp(Variable_14, -0.722935, 0.711631); + Variable_15 = FMath::Clamp(Variable_15, -1.982108, 2.144371); + Variable_16 = FMath::Clamp(Variable_16, -2.105316, 1.997740); + + // Smooth Step + v_flt Variable_7; // Smooth Step output 0 + Variable_7 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_13, v_flt(0.0f), Variable_14); + + // 2D Erosion + v_flt Variable_1; // 2D Erosion output 0 + v_flt _2D_Erosion_0_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_0_Temp_2; // 2D Erosion output 2 + Variable_1 = _2D_Erosion_0_Noise.GetErosion_2D(BufferX.Variable_11, Variable_12, v_flt(0.02f), _2D_Erosion_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_15, Variable_16, _2D_Erosion_0_Temp_1, _2D_Erosion_0_Temp_2); + Variable_1 = FMath::Clamp(Variable_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_1 = FMath::Clamp(_2D_Erosion_0_Temp_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_2 = FMath::Clamp(_2D_Erosion_0_Temp_2, -1.200000, 1.200000); + + // * + v_flt Variable_8; // * output 0 + Variable_8 = Variable_1 * Variable_7; + + // * + v_flt Variable_5; // * output 0 + Variable_5 = BufferConstant.Variable_9 * Variable_8; + + // + + v_flt Variable_4; // + output 0 + Variable_4 = Variable_14 + Variable_5; + + // 2D Noise SDF.* + v_flt Variable_19; // 2D Noise SDF.* output 0 + Variable_19 = Variable_4 * BufferConstant.Variable_10; + + // 2D Noise SDF.+ + BufferXY.Variable_0 = Variable_19 + v_flt(0.0f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_2 = Context.GetLocalX(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // X + BufferX.Variable_11 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_14; // 2D Perlin Noise Fractal output 0 + v_flt Variable_15; // 2D Perlin Noise Fractal output 1 + v_flt Variable_16; // 2D Perlin Noise Fractal output 2 + Variable_14 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D_Deriv(BufferX.Variable_2, Variable_3, v_flt(0.001f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_15,Variable_16); + Variable_14 = FMath::Clamp(Variable_14, -0.722935, 0.711631); + Variable_15 = FMath::Clamp(Variable_15, -1.982108, 2.144371); + Variable_16 = FMath::Clamp(Variable_16, -2.105316, 1.997740); + + // Smooth Step + v_flt Variable_7; // Smooth Step output 0 + Variable_7 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_13, v_flt(0.0f), Variable_14); + + // 2D Erosion + v_flt Variable_1; // 2D Erosion output 0 + v_flt _2D_Erosion_0_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_0_Temp_2; // 2D Erosion output 2 + Variable_1 = _2D_Erosion_0_Noise.GetErosion_2D(BufferX.Variable_11, Variable_12, v_flt(0.02f), _2D_Erosion_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_15, Variable_16, _2D_Erosion_0_Temp_1, _2D_Erosion_0_Temp_2); + Variable_1 = FMath::Clamp(Variable_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_1 = FMath::Clamp(_2D_Erosion_0_Temp_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_2 = FMath::Clamp(_2D_Erosion_0_Temp_2, -1.200000, 1.200000); + + // * + v_flt Variable_8; // * output 0 + Variable_8 = Variable_1 * Variable_7; + + // * + v_flt Variable_5; // * output 0 + Variable_5 = BufferConstant.Variable_9 * Variable_8; + + // + + v_flt Variable_4; // + output 0 + Variable_4 = Variable_14 + Variable_5; + + // 2D Noise SDF.* + v_flt Variable_19; // 2D Noise SDF.* output 0 + Variable_19 = Variable_4 * BufferConstant.Variable_10; + + // 2D Noise SDF.+ + BufferXY.Variable_0 = Variable_19 + v_flt(0.0f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // 2D Noise SDF.- + v_flt Variable_18; // 2D Noise SDF.- output 0 + Variable_18 = Variable_6 - BufferXY.Variable_0; + + // Set High Quality Value.* + v_flt Variable_17; // Set High Quality Value.* output 0 + Variable_17 = Variable_18 * v_flt(0.2f); + + Outputs.Value = Variable_17; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_2; // X output 0 + Variable_2 = Context.GetLocalX(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // X + v_flt Variable_11; // X output 0 + Variable_11 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_14; // 2D Perlin Noise Fractal output 0 + v_flt Variable_15; // 2D Perlin Noise Fractal output 1 + v_flt Variable_16; // 2D Perlin Noise Fractal output 2 + Variable_14 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D_Deriv(Variable_2, Variable_3, v_flt(0.001f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_15,Variable_16); + Variable_14 = FMath::Clamp(Variable_14, -0.722935, 0.711631); + Variable_15 = FMath::Clamp(Variable_15, -1.982108, 2.144371); + Variable_16 = FMath::Clamp(Variable_16, -2.105316, 1.997740); + + // Smooth Step + v_flt Variable_7; // Smooth Step output 0 + Variable_7 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_13, v_flt(0.0f), Variable_14); + + // 2D Erosion + v_flt Variable_1; // 2D Erosion output 0 + v_flt _2D_Erosion_0_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_0_Temp_2; // 2D Erosion output 2 + Variable_1 = _2D_Erosion_0_Noise.GetErosion_2D(Variable_11, Variable_12, v_flt(0.02f), _2D_Erosion_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_15, Variable_16, _2D_Erosion_0_Temp_1, _2D_Erosion_0_Temp_2); + Variable_1 = FMath::Clamp(Variable_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_1 = FMath::Clamp(_2D_Erosion_0_Temp_1, -1.200000, 1.200000); + _2D_Erosion_0_Temp_2 = FMath::Clamp(_2D_Erosion_0_Temp_2, -1.200000, 1.200000); + + // * + v_flt Variable_8; // * output 0 + Variable_8 = Variable_1 * Variable_7; + + // * + v_flt Variable_5; // * output 0 + Variable_5 = BufferConstant.Variable_9 * Variable_8; + + // + + v_flt Variable_4; // + output 0 + Variable_4 = Variable_14 + Variable_5; + + // 2D Noise SDF.* + v_flt Variable_19; // 2D Noise SDF.* output 0 + Variable_19 = Variable_4 * BufferConstant.Variable_10; + + // 2D Noise SDF.+ + v_flt Variable_0; // 2D Noise SDF.+ output 0 + Variable_0 = Variable_19 + v_flt(0.0f); + + // 2D Noise SDF.- + v_flt Variable_18; // 2D Noise SDF.- output 0 + Variable_18 = Variable_6 - Variable_0; + + // Set High Quality Value.* + v_flt Variable_17; // Set High Quality Value.* output 0 + Variable_17 = Variable_18 * v_flt(0.2f); + + Outputs.Value = Variable_17; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + int32 Variable_8; // Get Material Collection Index: Rocks output 0 + v_flt Variable_17; // Valleys Height = -0.5 output 0 + v_flt Variable_16; // Erosion Material Strength = 3.0 output 0 + int32 Variable_4; // Get Material Collection Index: Snow output 0 + v_flt Variable_13; // 1 - X output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_1; // X output 0 + v_flt Variable_14; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_9; // * output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of Get Material Collection Index: Rocks + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Rocks_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Rocks); + } + else + { + Get_Material_Collection_Index__Rocks_0_Index = -1; + } + + // Init of Get Material Collection Index: Snow + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Snow_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Snow); + } + else + { + Get_Material_Collection_Index__Snow_0_Index = -1; + } + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Get Material Collection Index: Rocks + BufferConstant.Variable_8 = Get_Material_Collection_Index__Rocks_0_Index; + + // Erosion Material Offset = 0.65 + v_flt Variable_11; // Erosion Material Offset = 0.65 output 0 + Variable_11 = Params.Erosion_Material_Offset; + + // Valleys Height = -0.5 + BufferConstant.Variable_17 = Params.Valleys_Height; + + // Erosion Material Strength = 3.0 + BufferConstant.Variable_16 = Params.Erosion_Material_Strength; + + // Get Material Collection Index: Snow + BufferConstant.Variable_4 = Get_Material_Collection_Index__Snow_0_Index; + + // 1 - X + BufferConstant.Variable_13 = 1 - Variable_11; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + int32 Get_Material_Collection_Index__Rocks_0_Index; + int32 Get_Material_Collection_Index__Snow_0_Index; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_1_LODToOctaves; + FVoxelFastNoise _2D_Erosion_1_Noise; + TStaticArray _2D_Erosion_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + // Init of 2D Erosion + _2D_Erosion_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Erosion_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Erosion_1_Noise.SetFractalOctavesAndGain(5, 0.5); + _2D_Erosion_1_Noise.SetFractalLacunarity(2.0); + _2D_Erosion_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Erosion_1_Noise.SetCellularJitter(0.5); + _2D_Erosion_1_LODToOctaves[0] = 5; + _2D_Erosion_1_LODToOctaves[1] = 5; + _2D_Erosion_1_LODToOctaves[2] = 5; + _2D_Erosion_1_LODToOctaves[3] = 5; + _2D_Erosion_1_LODToOctaves[4] = 5; + _2D_Erosion_1_LODToOctaves[5] = 5; + _2D_Erosion_1_LODToOctaves[6] = 5; + _2D_Erosion_1_LODToOctaves[7] = 5; + _2D_Erosion_1_LODToOctaves[8] = 5; + _2D_Erosion_1_LODToOctaves[9] = 5; + _2D_Erosion_1_LODToOctaves[10] = 5; + _2D_Erosion_1_LODToOctaves[11] = 5; + _2D_Erosion_1_LODToOctaves[12] = 5; + _2D_Erosion_1_LODToOctaves[13] = 5; + _2D_Erosion_1_LODToOctaves[14] = 5; + _2D_Erosion_1_LODToOctaves[15] = 5; + _2D_Erosion_1_LODToOctaves[16] = 5; + _2D_Erosion_1_LODToOctaves[17] = 5; + _2D_Erosion_1_LODToOctaves[18] = 5; + _2D_Erosion_1_LODToOctaves[19] = 5; + _2D_Erosion_1_LODToOctaves[20] = 5; + _2D_Erosion_1_LODToOctaves[21] = 5; + _2D_Erosion_1_LODToOctaves[22] = 5; + _2D_Erosion_1_LODToOctaves[23] = 5; + _2D_Erosion_1_LODToOctaves[24] = 5; + _2D_Erosion_1_LODToOctaves[25] = 5; + _2D_Erosion_1_LODToOctaves[26] = 5; + _2D_Erosion_1_LODToOctaves[27] = 5; + _2D_Erosion_1_LODToOctaves[28] = 5; + _2D_Erosion_1_LODToOctaves[29] = 5; + _2D_Erosion_1_LODToOctaves[30] = 5; + _2D_Erosion_1_LODToOctaves[31] = 5; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_1 = Context.GetLocalX(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_18; // 2D Perlin Noise Fractal output 0 + v_flt Variable_19; // 2D Perlin Noise Fractal output 1 + v_flt Variable_20; // 2D Perlin Noise Fractal output 2 + Variable_18 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D_Deriv(BufferX.Variable_1, Variable_2, v_flt(0.001f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_19,Variable_20); + Variable_18 = FMath::Clamp(Variable_18, -0.722935, 0.711631); + Variable_19 = FMath::Clamp(Variable_19, -1.982108, 2.144371); + Variable_20 = FMath::Clamp(Variable_20, -2.105316, 1.997740); + + // 2D Erosion + v_flt Variable_0; // 2D Erosion output 0 + v_flt _2D_Erosion_1_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_1_Temp_2; // 2D Erosion output 2 + Variable_0 = _2D_Erosion_1_Noise.GetErosion_2D(BufferX.Variable_14, Variable_15, v_flt(0.02f), _2D_Erosion_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_19, Variable_20, _2D_Erosion_1_Temp_1, _2D_Erosion_1_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -1.200000, 1.200000); + _2D_Erosion_1_Temp_1 = FMath::Clamp(_2D_Erosion_1_Temp_1, -1.200000, 1.200000); + _2D_Erosion_1_Temp_2 = FMath::Clamp(_2D_Erosion_1_Temp_2, -1.200000, 1.200000); + + // Smooth Step + v_flt Variable_3; // Smooth Step output 0 + Variable_3 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_17, v_flt(0.0f), Variable_18); + + // * + v_flt Variable_10; // * output 0 + Variable_10 = Variable_0 * Variable_3; + + // Smooth Step + v_flt Variable_5; // Smooth Step output 0 + Variable_5 = FVoxelMathNodeFunctions::SmoothStep(v_flt(-1.2f), v_flt(1.2f), Variable_10); + + // 1 - X + v_flt Variable_6; // 1 - X output 0 + Variable_6 = 1 - Variable_5; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_6 - BufferConstant.Variable_13; + + // Max (float) + v_flt Variable_12; // Max (float) output 0 + Variable_12 = FVoxelNodeFunctions::Max(Variable_7, v_flt(0.0f)); + + // * + BufferXY.Variable_9 = Variable_12 * BufferConstant.Variable_16; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // X + BufferX.Variable_1 = Context.GetLocalX(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_18; // 2D Perlin Noise Fractal output 0 + v_flt Variable_19; // 2D Perlin Noise Fractal output 1 + v_flt Variable_20; // 2D Perlin Noise Fractal output 2 + Variable_18 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D_Deriv(BufferX.Variable_1, Variable_2, v_flt(0.001f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_19,Variable_20); + Variable_18 = FMath::Clamp(Variable_18, -0.722935, 0.711631); + Variable_19 = FMath::Clamp(Variable_19, -1.982108, 2.144371); + Variable_20 = FMath::Clamp(Variable_20, -2.105316, 1.997740); + + // 2D Erosion + v_flt Variable_0; // 2D Erosion output 0 + v_flt _2D_Erosion_1_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_1_Temp_2; // 2D Erosion output 2 + Variable_0 = _2D_Erosion_1_Noise.GetErosion_2D(BufferX.Variable_14, Variable_15, v_flt(0.02f), _2D_Erosion_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_19, Variable_20, _2D_Erosion_1_Temp_1, _2D_Erosion_1_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -1.200000, 1.200000); + _2D_Erosion_1_Temp_1 = FMath::Clamp(_2D_Erosion_1_Temp_1, -1.200000, 1.200000); + _2D_Erosion_1_Temp_2 = FMath::Clamp(_2D_Erosion_1_Temp_2, -1.200000, 1.200000); + + // Smooth Step + v_flt Variable_3; // Smooth Step output 0 + Variable_3 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_17, v_flt(0.0f), Variable_18); + + // * + v_flt Variable_10; // * output 0 + Variable_10 = Variable_0 * Variable_3; + + // Smooth Step + v_flt Variable_5; // Smooth Step output 0 + Variable_5 = FVoxelMathNodeFunctions::SmoothStep(v_flt(-1.2f), v_flt(1.2f), Variable_10); + + // 1 - X + v_flt Variable_6; // 1 - X output 0 + Variable_6 = 1 - Variable_5; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_6 - BufferConstant.Variable_13; + + // Max (float) + v_flt Variable_12; // Max (float) output 0 + Variable_12 = FVoxelNodeFunctions::Max(Variable_7, v_flt(0.0f)); + + // * + BufferXY.Variable_9 = Variable_12 * BufferConstant.Variable_16; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_4, v_flt(1.0f), bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_8, BufferXY.Variable_9, bool(false)); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // X + v_flt Variable_1; // X output 0 + Variable_1 = Context.GetLocalX(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // X + v_flt Variable_14; // X output 0 + Variable_14 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_18; // 2D Perlin Noise Fractal output 0 + v_flt Variable_19; // 2D Perlin Noise Fractal output 1 + v_flt Variable_20; // 2D Perlin Noise Fractal output 2 + Variable_18 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D_Deriv(Variable_1, Variable_2, v_flt(0.001f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],Variable_19,Variable_20); + Variable_18 = FMath::Clamp(Variable_18, -0.722935, 0.711631); + Variable_19 = FMath::Clamp(Variable_19, -1.982108, 2.144371); + Variable_20 = FMath::Clamp(Variable_20, -2.105316, 1.997740); + + // 2D Erosion + v_flt Variable_0; // 2D Erosion output 0 + v_flt _2D_Erosion_1_Temp_1; // 2D Erosion output 1 + v_flt _2D_Erosion_1_Temp_2; // 2D Erosion output 2 + Variable_0 = _2D_Erosion_1_Noise.GetErosion_2D(Variable_14, Variable_15, v_flt(0.02f), _2D_Erosion_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], Variable_19, Variable_20, _2D_Erosion_1_Temp_1, _2D_Erosion_1_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -1.200000, 1.200000); + _2D_Erosion_1_Temp_1 = FMath::Clamp(_2D_Erosion_1_Temp_1, -1.200000, 1.200000); + _2D_Erosion_1_Temp_2 = FMath::Clamp(_2D_Erosion_1_Temp_2, -1.200000, 1.200000); + + // Smooth Step + v_flt Variable_3; // Smooth Step output 0 + Variable_3 = FVoxelMathNodeFunctions::SmoothStep(BufferConstant.Variable_17, v_flt(0.0f), Variable_18); + + // * + v_flt Variable_10; // * output 0 + Variable_10 = Variable_0 * Variable_3; + + // Smooth Step + v_flt Variable_5; // Smooth Step output 0 + Variable_5 = FVoxelMathNodeFunctions::SmoothStep(v_flt(-1.2f), v_flt(1.2f), Variable_10); + + // 1 - X + v_flt Variable_6; // 1 - X output 0 + Variable_6 = 1 - Variable_5; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_6 - BufferConstant.Variable_13; + + // Max (float) + v_flt Variable_12; // Max (float) output 0 + Variable_12 = FVoxelNodeFunctions::Max(Variable_7, v_flt(0.0f)); + + // * + v_flt Variable_9; // * output 0 + Variable_9 = Variable_12 * BufferConstant.Variable_16; + + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_4, v_flt(1.0f), bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_8, Variable_9, bool(false)); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_0; // 2D Noise SDF.+ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Erosion + _2D_Erosion_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Erosion_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Erosion_2_Noise.SetFractalOctavesAndGain(5, 0.5); + _2D_Erosion_2_Noise.SetFractalLacunarity(2.0); + _2D_Erosion_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Erosion_2_Noise.SetCellularJitter(0.5); + _2D_Erosion_2_LODToOctaves[0] = 5; + _2D_Erosion_2_LODToOctaves[1] = 5; + _2D_Erosion_2_LODToOctaves[2] = 5; + _2D_Erosion_2_LODToOctaves[3] = 5; + _2D_Erosion_2_LODToOctaves[4] = 5; + _2D_Erosion_2_LODToOctaves[5] = 5; + _2D_Erosion_2_LODToOctaves[6] = 5; + _2D_Erosion_2_LODToOctaves[7] = 5; + _2D_Erosion_2_LODToOctaves[8] = 5; + _2D_Erosion_2_LODToOctaves[9] = 5; + _2D_Erosion_2_LODToOctaves[10] = 5; + _2D_Erosion_2_LODToOctaves[11] = 5; + _2D_Erosion_2_LODToOctaves[12] = 5; + _2D_Erosion_2_LODToOctaves[13] = 5; + _2D_Erosion_2_LODToOctaves[14] = 5; + _2D_Erosion_2_LODToOctaves[15] = 5; + _2D_Erosion_2_LODToOctaves[16] = 5; + _2D_Erosion_2_LODToOctaves[17] = 5; + _2D_Erosion_2_LODToOctaves[18] = 5; + _2D_Erosion_2_LODToOctaves[19] = 5; + _2D_Erosion_2_LODToOctaves[20] = 5; + _2D_Erosion_2_LODToOctaves[21] = 5; + _2D_Erosion_2_LODToOctaves[22] = 5; + _2D_Erosion_2_LODToOctaves[23] = 5; + _2D_Erosion_2_LODToOctaves[24] = 5; + _2D_Erosion_2_LODToOctaves[25] = 5; + _2D_Erosion_2_LODToOctaves[26] = 5; + _2D_Erosion_2_LODToOctaves[27] = 5; + _2D_Erosion_2_LODToOctaves[28] = 5; + _2D_Erosion_2_LODToOctaves[29] = 5; + _2D_Erosion_2_LODToOctaves[30] = 5; + _2D_Erosion_2_LODToOctaves[31] = 5; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_2_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[31] = 3; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 2D Erosion + TVoxelRange Variable_1; // 2D Erosion output 0 + TVoxelRange _2D_Erosion_2_Temp_1; // 2D Erosion output 1 + TVoxelRange _2D_Erosion_2_Temp_2; // 2D Erosion output 2 + Variable_1 = { -1.200000f, 1.200000f }; + _2D_Erosion_2_Temp_1 = { -1.200000f, 1.200000f }; + _2D_Erosion_2_Temp_2 = { -1.200000f, 1.200000f }; + + // 2D Perlin Noise Fractal + TVoxelRange Variable_10; // 2D Perlin Noise Fractal output 0 + TVoxelRange _2D_Perlin_Noise_Fractal_2_Temp_1; // 2D Perlin Noise Fractal output 1 + TVoxelRange _2D_Perlin_Noise_Fractal_2_Temp_2; // 2D Perlin Noise Fractal output 2 + Variable_10 = { -0.722935f, 0.711631f }; + _2D_Perlin_Noise_Fractal_2_Temp_1 = { -1.982108f, 2.144371f }; + _2D_Perlin_Noise_Fractal_2_Temp_2 = { -2.105316f, 1.997740f }; + + // Valleys Height = -0.5 + TVoxelRange Variable_9; // Valleys Height = -0.5 output 0 + Variable_9 = Params.Valleys_Height; + + // Height = 500.0 + TVoxelRange Variable_8; // Height = 500.0 output 0 + Variable_8 = Params.Height; + + // Erosion Strength = 0.008 + TVoxelRange Variable_7; // Erosion Strength = 0.008 output 0 + Variable_7 = Params.Erosion_Strength; + + // Smooth Step + TVoxelRange Variable_5; // Smooth Step output 0 + Variable_5 = FVoxelMathNodeFunctions::SmoothStep(Variable_9, TVoxelRange(0.0f), Variable_10); + + // * + TVoxelRange Variable_6; // * output 0 + Variable_6 = Variable_1 * Variable_5; + + // * + TVoxelRange Variable_3; // * output 0 + Variable_3 = Variable_7 * Variable_6; + + // + + TVoxelRange Variable_2; // + output 0 + Variable_2 = Variable_10 + Variable_3; + + // 2D Noise SDF.* + TVoxelRange Variable_13; // 2D Noise SDF.* output 0 + Variable_13 = Variable_2 * Variable_8; + + // 2D Noise SDF.+ + BufferConstant.Variable_0 = Variable_13 + TVoxelRange(0.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Erosion_2_Noise; + TStaticArray _2D_Erosion_2_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_2_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_2_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // 2D Noise SDF.- + TVoxelRange Variable_12; // 2D Noise SDF.- output 0 + Variable_12 = Variable_4 - BufferConstant.Variable_0; + + // Set High Quality Value.* + TVoxelRange Variable_11; // Set High Quality Value.* output 0 + Variable_11 = Variable_12 * TVoxelRange(0.2f); + + Outputs.Value = Variable_11; + } + + }; + + FVG_Example_ErosionInstance(UVG_Example_Erosion& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Erosion_Material_Offset, + Object.Erosion_Material_Strength, + Object.Erosion_Strength, + Object.Height, + *Object.Rocks.GetAssetName(), + *Object.Snow.GetAssetName(), + Object.Valleys_Height + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVG_Example_ErosionInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVG_Example_ErosionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVG_Example_ErosionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_ErosionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVG_Example_ErosionInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVG_Example_ErosionInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVG_Example_ErosionInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVG_Example_ErosionInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVG_Example_Erosion::UVG_Example_Erosion() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVG_Example_Erosion::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VG_Example_Erosion. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VG_Example_Erosion. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Erosion. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_Erosion. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.h new file mode 100644 index 00000000..425ec6aa --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.h @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VG_Example_Erosion.generated.h" + +UCLASS(Blueprintable) +class UVG_Example_Erosion : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Erosion Material Offset")) + float Erosion_Material_Offset = 0.65; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Erosion Material Strength")) + float Erosion_Material_Strength = 3.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Erosion Strength")) + float Erosion_Strength = 0.008; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Height")) + float Height = 500.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Rocks")) + TSoftObjectPtr Rocks = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.MI_BrownMudRocks")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Snow")) + TSoftObjectPtr Snow = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.MI_Snow")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Valleys Height")) + float Valleys_Height = -0.5; + + UVG_Example_Erosion(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.cpp new file mode 100644 index 00000000..eb85fb9c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.cpp @@ -0,0 +1,992 @@ +// Copyright 2020 Phyronnaz + +#include "VG_Example_FastCraters.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVG_Example_FastCratersInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_15; // XYZ.X output 0 + v_flt Variable_7; // XYZ.X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_16; // XYZ.Y output 0 + v_flt Variable_8; // XYZ.Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Crater_Noise_Fractal_0_Noise; + TStaticArray _3D_Crater_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Crater Noise Fractal + _3D_Crater_Noise_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Crater_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Crater_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(5, 0.6); + _3D_Crater_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _3D_Crater_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Crater_Noise_Fractal_0_Noise.SetCellularJitter(0.45); + _3D_Crater_Noise_Fractal_0_Noise.SetCraterFalloffExponent(4.0); + _3D_Crater_Noise_Fractal_0_LODToOctaves[0] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[1] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[2] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[3] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[4] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[5] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[6] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[7] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[8] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[9] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[10] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[11] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[12] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[13] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[14] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[15] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[16] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[17] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[18] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[19] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[20] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[21] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[22] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[23] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[24] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[25] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[26] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[27] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[28] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[29] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[30] = 5; + _3D_Crater_Noise_Fractal_0_LODToOctaves[31] = 5; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // XYZ.X + BufferX.Variable_15 = Context.GetLocalX(); + + // XYZ.X + BufferX.Variable_7 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // XYZ.Y + BufferXY.Variable_16 = Context.GetLocalY(); + + // XYZ.Y + BufferXY.Variable_8 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // XYZ.Y + BufferXY.Variable_16 = Context.GetLocalY(); + + // XYZ.X + BufferX.Variable_15 = Context.GetLocalX(); + + // XYZ.X + BufferX.Variable_7 = Context.GetLocalX(); + + // XYZ.Y + BufferXY.Variable_8 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // XYZ.Z + v_flt Variable_0; // XYZ.Z output 0 + Variable_0 = Context.GetLocalZ(); + + // XYZ.Z + v_flt Variable_4; // XYZ.Z output 0 + Variable_4 = Context.GetLocalZ(); + + // Normalize.Vector Length + v_flt Variable_9; // Normalize.Vector Length output 0 + Variable_9 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_7, BufferXY.Variable_8, Variable_0); + + // Vector Length + v_flt Variable_1; // Vector Length output 0 + Variable_1 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_15, BufferXY.Variable_16, Variable_4); + + // Normalize./ + v_flt Variable_12; // Normalize./ output 0 + Variable_12 = Variable_0 / Variable_9; + + // Normalize./ + v_flt Variable_11; // Normalize./ output 0 + Variable_11 = BufferXY.Variable_8 / Variable_9; + + // Normalize./ + v_flt Variable_10; // Normalize./ output 0 + Variable_10 = BufferX.Variable_7 / Variable_9; + + // 3D Crater Noise Fractal + v_flt Variable_2; // 3D Crater Noise Fractal output 0 + Variable_2 = _3D_Crater_Noise_Fractal_0_Noise.GetCraterFractal_3D(Variable_10, Variable_11, Variable_12, v_flt(2.0f), _3D_Crater_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, 0.008314, 0.543512); + + // * -1 + v_flt Variable_5; // * -1 output 0 + Variable_5 = Variable_2 * -1; + + // 2D Noise SDF.* + v_flt Variable_14; // 2D Noise SDF.* output 0 + Variable_14 = Variable_5 * v_flt(30.0f); + + // 2D Noise SDF.+ + v_flt Variable_3; // 2D Noise SDF.+ output 0 + Variable_3 = Variable_14 + v_flt(500.0f); + + // 2D Noise SDF.- + v_flt Variable_13; // 2D Noise SDF.- output 0 + Variable_13 = Variable_1 - Variable_3; + + // Set High Quality Value.* + v_flt Variable_6; // Set High Quality Value.* output 0 + Variable_6 = Variable_13 * v_flt(0.2f); + + Outputs.Value = Variable_6; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // XYZ.Y + v_flt Variable_16; // XYZ.Y output 0 + Variable_16 = Context.GetLocalY(); + + // XYZ.Z + v_flt Variable_0; // XYZ.Z output 0 + Variable_0 = Context.GetLocalZ(); + + // XYZ.X + v_flt Variable_15; // XYZ.X output 0 + Variable_15 = Context.GetLocalX(); + + // XYZ.X + v_flt Variable_7; // XYZ.X output 0 + Variable_7 = Context.GetLocalX(); + + // XYZ.Z + v_flt Variable_4; // XYZ.Z output 0 + Variable_4 = Context.GetLocalZ(); + + // XYZ.Y + v_flt Variable_8; // XYZ.Y output 0 + Variable_8 = Context.GetLocalY(); + + // Normalize.Vector Length + v_flt Variable_9; // Normalize.Vector Length output 0 + Variable_9 = FVoxelNodeFunctions::VectorLength(Variable_7, Variable_8, Variable_0); + + // Vector Length + v_flt Variable_1; // Vector Length output 0 + Variable_1 = FVoxelNodeFunctions::VectorLength(Variable_15, Variable_16, Variable_4); + + // Normalize./ + v_flt Variable_12; // Normalize./ output 0 + Variable_12 = Variable_0 / Variable_9; + + // Normalize./ + v_flt Variable_11; // Normalize./ output 0 + Variable_11 = Variable_8 / Variable_9; + + // Normalize./ + v_flt Variable_10; // Normalize./ output 0 + Variable_10 = Variable_7 / Variable_9; + + // 3D Crater Noise Fractal + v_flt Variable_2; // 3D Crater Noise Fractal output 0 + Variable_2 = _3D_Crater_Noise_Fractal_0_Noise.GetCraterFractal_3D(Variable_10, Variable_11, Variable_12, v_flt(2.0f), _3D_Crater_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, 0.008314, 0.543512); + + // * -1 + v_flt Variable_5; // * -1 output 0 + Variable_5 = Variable_2 * -1; + + // 2D Noise SDF.* + v_flt Variable_14; // 2D Noise SDF.* output 0 + Variable_14 = Variable_5 * v_flt(30.0f); + + // 2D Noise SDF.+ + v_flt Variable_3; // 2D Noise SDF.+ output 0 + Variable_3 = Variable_14 + v_flt(500.0f); + + // 2D Noise SDF.- + v_flt Variable_13; // 2D Noise SDF.- output 0 + Variable_13 = Variable_1 - Variable_3; + + // Set High Quality Value.* + v_flt Variable_6; // Set High Quality Value.* output 0 + Variable_6 = Variable_13 * v_flt(0.2f); + + Outputs.Value = Variable_6; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_2; // 2D Noise SDF.+ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_8; // XYZ.X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_9; // XYZ.Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D Crater Noise Fractal + _3D_Crater_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Crater_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Crater_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(5, 0.6); + _3D_Crater_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _3D_Crater_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Crater_Noise_Fractal_1_Noise.SetCellularJitter(0.45); + _3D_Crater_Noise_Fractal_1_Noise.SetCraterFalloffExponent(4.0); + _3D_Crater_Noise_Fractal_1_LODToOctaves[0] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[1] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[2] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[3] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[4] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[5] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[6] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[7] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[8] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[9] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[10] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[11] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[12] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[13] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[14] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[15] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[16] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[17] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[18] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[19] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[20] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[21] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[22] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[23] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[24] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[25] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[26] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[27] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[28] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[29] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[30] = 5; + _3D_Crater_Noise_Fractal_1_LODToOctaves[31] = 5; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 3D Crater Noise Fractal + TVoxelRange Variable_1; // 3D Crater Noise Fractal output 0 + Variable_1 = { 0.008314f, 0.543512f }; + + // * -1 + TVoxelRange Variable_4; // * -1 output 0 + Variable_4 = Variable_1 * -1; + + // 2D Noise SDF.* + TVoxelRange Variable_7; // 2D Noise SDF.* output 0 + Variable_7 = Variable_4 * TVoxelRange(30.0f); + + // 2D Noise SDF.+ + BufferConstant.Variable_2 = Variable_7 + TVoxelRange(500.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Crater_Noise_Fractal_1_Noise; + TStaticArray _3D_Crater_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // XYZ.Y + TVoxelRange Variable_9; // XYZ.Y output 0 + Variable_9 = Context.GetLocalY(); + + // XYZ.X + TVoxelRange Variable_8; // XYZ.X output 0 + Variable_8 = Context.GetLocalX(); + + // XYZ.Z + TVoxelRange Variable_3; // XYZ.Z output 0 + Variable_3 = Context.GetLocalZ(); + + // Vector Length + TVoxelRange Variable_0; // Vector Length output 0 + Variable_0 = FVoxelNodeFunctions::VectorLength(Variable_8, Variable_9, Variable_3); + + // 2D Noise SDF.- + TVoxelRange Variable_6; // 2D Noise SDF.- output 0 + Variable_6 = Variable_0 - BufferConstant.Variable_2; + + // Set High Quality Value.* + TVoxelRange Variable_5; // Set High Quality Value.* output 0 + Variable_5 = Variable_6 * TVoxelRange(0.2f); + + Outputs.Value = Variable_5; + } + + }; + + FVG_Example_FastCratersInstance(UVG_Example_FastCraters& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_FastCratersInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVG_Example_FastCratersInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVG_Example_FastCratersInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVG_Example_FastCratersInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVG_Example_FastCratersInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVG_Example_FastCraters::UVG_Example_FastCraters() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVG_Example_FastCraters::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VG_Example_FastCraters. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VG_Example_FastCraters. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_FastCraters. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_FastCraters. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.h new file mode 100644 index 00000000..c758163b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VG_Example_FastCraters.generated.h" + +UCLASS(Blueprintable) +class UVG_Example_FastCraters : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + + UVG_Example_FastCraters(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.cpp new file mode 100644 index 00000000..1c43421b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.cpp @@ -0,0 +1,1156 @@ +// Copyright 2020 Phyronnaz + +#include "VG_Example_MultiIndex.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVG_Example_MultiIndexInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const FName Layer_0; + const FName Layer_1; + const FName Layer_2; + const FName Layer_3; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_2; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_0; // 2D Noise SDF.+ output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Make Seeds + FVoxelGraphSeed Variable_5; // Make Seeds output 0 + FVoxelGraphSeed Make_Seeds_0_Temp_1; // Make Seeds output 1 + Variable_5 = FVoxelUtilities::MurmurHash32(FVoxelGraphSeed(1337)); + Make_Seeds_0_Temp_1 = FVoxelUtilities::MurmurHash32(Variable_5); + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Make Seeds + FVoxelGraphSeed Variable_5; // Make Seeds output 0 + FVoxelGraphSeed Make_Seeds_0_Temp_1; // Make Seeds output 1 + Variable_5 = FVoxelUtilities::MurmurHash32(FVoxelGraphSeed(1337)); + Make_Seeds_0_Temp_1 = FVoxelUtilities::MurmurHash32(Variable_5); + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_0_Noise.SetSeed(Variable_5); + _2D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(7, 0.5); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 7; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 7; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_2 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_1; // 2D Perlin Noise Fractal output 0 + Variable_1 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_2, Variable_3, v_flt(0.002f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_1 = FMath::Clamp(Variable_1, -0.601283, 0.600269); + + // 2D Noise SDF.* + v_flt Variable_8; // 2D Noise SDF.* output 0 + Variable_8 = Variable_1 * v_flt(300.0f); + + // 2D Noise SDF.+ + BufferXY.Variable_0 = Variable_8 + v_flt(0.0f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // X + BufferX.Variable_2 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_1; // 2D Perlin Noise Fractal output 0 + Variable_1 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_2, Variable_3, v_flt(0.002f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_1 = FMath::Clamp(Variable_1, -0.601283, 0.600269); + + // 2D Noise SDF.* + v_flt Variable_8; // 2D Noise SDF.* output 0 + Variable_8 = Variable_1 * v_flt(300.0f); + + // 2D Noise SDF.+ + BufferXY.Variable_0 = Variable_8 + v_flt(0.0f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // 2D Noise SDF.- + v_flt Variable_7; // 2D Noise SDF.- output 0 + Variable_7 = Variable_4 - BufferXY.Variable_0; + + // Set High Quality Value.* + v_flt Variable_6; // Set High Quality Value.* output 0 + Variable_6 = Variable_7 * v_flt(0.2f); + + Outputs.Value = Variable_6; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // Y + v_flt Variable_3; // Y output 0 + Variable_3 = Context.GetLocalY(); + + // X + v_flt Variable_2; // X output 0 + Variable_2 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_1; // 2D Perlin Noise Fractal output 0 + Variable_1 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(Variable_2, Variable_3, v_flt(0.002f), _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_1 = FMath::Clamp(Variable_1, -0.601283, 0.600269); + + // 2D Noise SDF.* + v_flt Variable_8; // 2D Noise SDF.* output 0 + Variable_8 = Variable_1 * v_flt(300.0f); + + // 2D Noise SDF.+ + v_flt Variable_0; // 2D Noise SDF.+ output 0 + Variable_0 = Variable_8 + v_flt(0.0f); + + // 2D Noise SDF.- + v_flt Variable_7; // 2D Noise SDF.- output 0 + Variable_7 = Variable_4 - Variable_0; + + // Set High Quality Value.* + v_flt Variable_6; // Set High Quality Value.* output 0 + Variable_6 = Variable_7 * v_flt(0.2f); + + Outputs.Value = Variable_6; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + int32 Variable_6; // Get Material Collection Index: Layer 1 output 0 + int32 Variable_7; // Get Material Collection Index: Layer 2 output 0 + int32 Variable_8; // Get Material Collection Index: Layer 3 output 0 + int32 Variable_0; // Get Material Collection Index: Layer 0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_11; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_14; // * output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Make Seeds + FVoxelGraphSeed Make_Seeds_1_Temp_0; // Make Seeds output 0 + FVoxelGraphSeed Variable_10; // Make Seeds output 1 + Make_Seeds_1_Temp_0 = FVoxelUtilities::MurmurHash32(FVoxelGraphSeed(1337)); + Variable_10 = FVoxelUtilities::MurmurHash32(Make_Seeds_1_Temp_0); + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of Get Material Collection Index: Layer 1 + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Layer_1_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Layer_1); + } + else + { + Get_Material_Collection_Index__Layer_1_0_Index = -1; + } + + // Init of Get Material Collection Index: Layer 2 + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Layer_2_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Layer_2); + } + else + { + Get_Material_Collection_Index__Layer_2_0_Index = -1; + } + + // Init of Get Material Collection Index: Layer 3 + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Layer_3_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Layer_3); + } + else + { + Get_Material_Collection_Index__Layer_3_0_Index = -1; + } + + // Init of Get Material Collection Index: Layer 0 + if (InitStruct.MaterialCollection) + { + Get_Material_Collection_Index__Layer_0_0_Index = InitStruct.MaterialCollection->GetMaterialIndex(Params.Layer_0); + } + else + { + Get_Material_Collection_Index__Layer_0_0_Index = -1; + } + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Get Material Collection Index: Layer 1 + BufferConstant.Variable_6 = Get_Material_Collection_Index__Layer_1_0_Index; + + // Get Material Collection Index: Layer 2 + BufferConstant.Variable_7 = Get_Material_Collection_Index__Layer_2_0_Index; + + // Get Material Collection Index: Layer 3 + BufferConstant.Variable_8 = Get_Material_Collection_Index__Layer_3_0_Index; + + // Get Material Collection Index: Layer 0 + BufferConstant.Variable_0 = Get_Material_Collection_Index__Layer_0_0_Index; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + int32 Get_Material_Collection_Index__Layer_1_0_Index; + int32 Get_Material_Collection_Index__Layer_2_0_Index; + int32 Get_Material_Collection_Index__Layer_3_0_Index; + int32 Get_Material_Collection_Index__Layer_0_0_Index; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Make Seeds + FVoxelGraphSeed Make_Seeds_1_Temp_0; // Make Seeds output 0 + FVoxelGraphSeed Variable_10; // Make Seeds output 1 + Make_Seeds_1_Temp_0 = FVoxelUtilities::MurmurHash32(FVoxelGraphSeed(1337)); + Variable_10 = FVoxelUtilities::MurmurHash32(Make_Seeds_1_Temp_0); + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_1_Noise.SetSeed(Variable_10); + _2D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(7, 0.5); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 7; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 7; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_11 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_13; // 2D Perlin Noise Fractal output 0 + Variable_13 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_11, Variable_12, v_flt(0.02f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_13 = FMath::Clamp(Variable_13, -0.306139, 0.328394); + + // * + BufferXY.Variable_14 = Variable_13 * v_flt(25.0f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // X + BufferX.Variable_11 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_13; // 2D Perlin Noise Fractal output 0 + Variable_13 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_11, Variable_12, v_flt(0.02f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_13 = FMath::Clamp(Variable_13, -0.306139, 0.328394); + + // * + BufferXY.Variable_14 = Variable_13 * v_flt(25.0f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_5; // Z output 0 + Variable_5 = Context.GetLocalZ(); + + // + + v_flt Variable_9; // + output 0 + Variable_9 = Variable_5 + BufferXY.Variable_14; + + // Height Splitter + v_flt Variable_1; // Height Splitter output 0 + v_flt Variable_2; // Height Splitter output 1 + v_flt Variable_3; // Height Splitter output 2 + v_flt Variable_4; // Height Splitter output 3 + { + TVoxelStaticArray InputsArray; + TVoxelStaticArray OutputsArray; + InputsArray[0] = v_flt(-75.0f); + InputsArray[1] = v_flt(5.0f); + InputsArray[2] = v_flt(-20.0f); + InputsArray[3] = v_flt(5.0f); + InputsArray[4] = v_flt(10.0f); + InputsArray[5] = v_flt(5.0f); + FVoxelMathNodeFunctions::HeightSplit(Variable_9, InputsArray, OutputsArray); + Variable_1 = OutputsArray[0]; + Variable_2 = OutputsArray[1]; + Variable_3 = OutputsArray[2]; + Variable_4 = OutputsArray[3]; + } + + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_0, Variable_1, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_6, Variable_2, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_7, Variable_3, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_8, Variable_4, bool(false)); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_12; // Y output 0 + Variable_12 = Context.GetLocalY(); + + // Z + v_flt Variable_5; // Z output 0 + Variable_5 = Context.GetLocalZ(); + + // X + v_flt Variable_11; // X output 0 + Variable_11 = Context.GetLocalX(); + + // 2D Perlin Noise Fractal + v_flt Variable_13; // 2D Perlin Noise Fractal output 0 + Variable_13 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(Variable_11, Variable_12, v_flt(0.02f), _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_13 = FMath::Clamp(Variable_13, -0.306139, 0.328394); + + // * + v_flt Variable_14; // * output 0 + Variable_14 = Variable_13 * v_flt(25.0f); + + // + + v_flt Variable_9; // + output 0 + Variable_9 = Variable_5 + Variable_14; + + // Height Splitter + v_flt Variable_1; // Height Splitter output 0 + v_flt Variable_2; // Height Splitter output 1 + v_flt Variable_3; // Height Splitter output 2 + v_flt Variable_4; // Height Splitter output 3 + { + TVoxelStaticArray InputsArray; + TVoxelStaticArray OutputsArray; + InputsArray[0] = v_flt(-75.0f); + InputsArray[1] = v_flt(5.0f); + InputsArray[2] = v_flt(-20.0f); + InputsArray[3] = v_flt(5.0f); + InputsArray[4] = v_flt(10.0f); + InputsArray[5] = v_flt(5.0f); + FVoxelMathNodeFunctions::HeightSplit(Variable_9, InputsArray, OutputsArray); + Variable_1 = OutputsArray[0]; + Variable_2 = OutputsArray[1]; + Variable_3 = OutputsArray[2]; + Variable_4 = OutputsArray[3]; + } + + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_0, Variable_1, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_6, Variable_2, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_7, Variable_3, bool(false)); + Outputs.MaterialBuilder.AddMultiIndex(BufferConstant.Variable_8, Variable_4, bool(false)); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_0; // 2D Noise SDF.+ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalOctavesAndGain(7, 0.5); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_2_LODToOctaves[0] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[1] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[2] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[3] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[4] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[5] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[6] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[7] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[8] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[9] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[10] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[11] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[12] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[13] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[14] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[15] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[16] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[17] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[18] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[19] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[20] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[21] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[22] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[23] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[24] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[25] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[26] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[27] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[28] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[29] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[30] = 7; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[31] = 7; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 2D Perlin Noise Fractal + TVoxelRange Variable_1; // 2D Perlin Noise Fractal output 0 + Variable_1 = { -0.601283f, 0.600269f }; + + // 2D Noise SDF.* + TVoxelRange Variable_5; // 2D Noise SDF.* output 0 + Variable_5 = Variable_1 * TVoxelRange(300.0f); + + // 2D Noise SDF.+ + BufferConstant.Variable_0 = Variable_5 + TVoxelRange(0.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_2_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_2_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // 2D Noise SDF.- + TVoxelRange Variable_4; // 2D Noise SDF.- output 0 + Variable_4 = Variable_2 - BufferConstant.Variable_0; + + // Set High Quality Value.* + TVoxelRange Variable_3; // Set High Quality Value.* output 0 + Variable_3 = Variable_4 * TVoxelRange(0.2f); + + Outputs.Value = Variable_3; + } + + }; + + FVG_Example_MultiIndexInstance(UVG_Example_MultiIndex& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + *Object.Layer_0.GetAssetName(), + *Object.Layer_1.GetAssetName(), + *Object.Layer_2.GetAssetName(), + *Object.Layer_3.GetAssetName() + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVG_Example_MultiIndexInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVG_Example_MultiIndexInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVG_Example_MultiIndexInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVG_Example_MultiIndexInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVG_Example_MultiIndexInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVG_Example_MultiIndex::UVG_Example_MultiIndex() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVG_Example_MultiIndex::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VG_Example_MultiIndex. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VG_Example_MultiIndex. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_MultiIndex. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VG_Example_MultiIndex. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.h new file mode 100644 index 00000000..4f0f8b6b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VG_Example_MultiIndex.generated.h" + +UCLASS(Blueprintable) +class UVG_Example_MultiIndex : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Layer 0")) + TSoftObjectPtr Layer_0 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.MI_AerialGrassRock")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Layer 1")) + TSoftObjectPtr Layer_1 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.MI_BrownMudRocks")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Layer 2")) + TSoftObjectPtr Layer_2 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.MI_CoralMud")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Layer 3")) + TSoftObjectPtr Layer_3 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.MI_Snow")); + + UVG_Example_MultiIndex(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.cpp new file mode 100644 index 00000000..f4555abd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.cpp @@ -0,0 +1,1786 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_Cave.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_CaveInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Bottom_Noise_Frequency; + const float Bottom_Noise_Scale; + const int32 Bottom_Noise_Seed; + const int32 Global_Height_Seed; + const int32 Top_Noise_Seed; + const float Top_Noise_Frequency; + const float Top_Noise_Scale; + const float Bottom_Top_Merge_Smoothness; + const float Global_Height_Merge_Smoothness; + const float Global_Height_Noise_Frequency; + const float Global_Height_Noise_Scale; + const float Global_Height_Offset; + const float Cave_Height; + const float Cave_Radius; + const float Cave_Walls_Smoothness; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_31; // Global Height Noise Frequency = 0.005 output 0 + v_flt Variable_24; // Bottom Top Merge Smoothness = 25.0 output 0 + v_flt Variable_25; // Cave Walls Smoothness = 100.0 output 0 + v_flt Variable_26; // Global Height Merge Smoothness = 15.0 output 0 + v_flt Variable_27; // Top Noise Frequency = 0.005 output 0 + v_flt Variable_28; // Bottom Noise Frequency = 0.008 output 0 + v_flt Variable_15; // Cave Radius = 400.0 output 0 + v_flt Variable_33; // Top Noise Scale = 150.0 output 0 + v_flt Variable_34; // Bottom Noise Scale = 150.0 output 0 + v_flt Variable_38; // Global Height Noise Scale = 200.0 output 0 + v_flt Variable_39; // Global Height Offset = 150.0 output 0 + v_flt Variable_37; // / output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_3; // X output 0 + v_flt Variable_19; // X output 0 + v_flt Variable_0; // X output 0 + v_flt Variable_12; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_6; // * output 0 + v_flt Variable_7; // * output 0 + v_flt Variable_14; // - output 0 + v_flt Variable_22; // + output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Top Noise Seed = 3022 + FVoxelGraphSeed Variable_29; // Top Noise Seed = 3022 output 0 + Variable_29 = Params.Top_Noise_Seed; + + // Init of Bottom Noise Seed = 3024 + FVoxelGraphSeed Variable_30; // Bottom Noise Seed = 3024 output 0 + Variable_30 = Params.Bottom_Noise_Seed; + + // Init of Global Height Seed = 1447 + FVoxelGraphSeed Variable_32; // Global Height Seed = 1447 output 0 + Variable_32 = Params.Global_Height_Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Global Height Noise Frequency = 0.005 + BufferConstant.Variable_31 = Params.Global_Height_Noise_Frequency; + + // Bottom Top Merge Smoothness = 25.0 + BufferConstant.Variable_24 = Params.Bottom_Top_Merge_Smoothness; + + // Cave Walls Smoothness = 100.0 + BufferConstant.Variable_25 = Params.Cave_Walls_Smoothness; + + // Global Height Merge Smoothness = 15.0 + BufferConstant.Variable_26 = Params.Global_Height_Merge_Smoothness; + + // Top Noise Frequency = 0.005 + BufferConstant.Variable_27 = Params.Top_Noise_Frequency; + + // Bottom Noise Frequency = 0.008 + BufferConstant.Variable_28 = Params.Bottom_Noise_Frequency; + + // Cave Radius = 400.0 + BufferConstant.Variable_15 = Params.Cave_Radius; + + // Top Noise Scale = 150.0 + BufferConstant.Variable_33 = Params.Top_Noise_Scale; + + // Bottom Noise Scale = 150.0 + BufferConstant.Variable_34 = Params.Bottom_Noise_Scale; + + // Cave Height = 100.0 + v_flt Variable_36; // Cave Height = 100.0 output 0 + Variable_36 = Params.Cave_Height; + + // Global Height Noise Scale = 200.0 + BufferConstant.Variable_38 = Params.Global_Height_Noise_Scale; + + // Global Height Offset = 150.0 + BufferConstant.Variable_39 = Params.Global_Height_Offset; + + // / + BufferConstant.Variable_37 = Variable_36 / v_flt(2.0f); + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_IQ_Noise_0_Noise; + TStaticArray _2D_IQ_Noise_0_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_0_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Bottom Noise Seed = 3024 + FVoxelGraphSeed Variable_30; // Bottom Noise Seed = 3024 output 0 + Variable_30 = Params.Bottom_Noise_Seed; + + // Init of Global Height Seed = 1447 + FVoxelGraphSeed Variable_32; // Global Height Seed = 1447 output 0 + Variable_32 = Params.Global_Height_Seed; + + // Init of Top Noise Seed = 3022 + FVoxelGraphSeed Variable_29; // Top Noise Seed = 3022 output 0 + Variable_29 = Params.Top_Noise_Seed; + + // Init of 2D IQ Noise + _2D_IQ_Noise_0_Noise.SetSeed(Variable_32); + _2D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_0_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_0_LODToOctaves[0] = 15; + _2D_IQ_Noise_0_LODToOctaves[1] = 15; + _2D_IQ_Noise_0_LODToOctaves[2] = 15; + _2D_IQ_Noise_0_LODToOctaves[3] = 15; + _2D_IQ_Noise_0_LODToOctaves[4] = 15; + _2D_IQ_Noise_0_LODToOctaves[5] = 15; + _2D_IQ_Noise_0_LODToOctaves[6] = 15; + _2D_IQ_Noise_0_LODToOctaves[7] = 15; + _2D_IQ_Noise_0_LODToOctaves[8] = 15; + _2D_IQ_Noise_0_LODToOctaves[9] = 15; + _2D_IQ_Noise_0_LODToOctaves[10] = 15; + _2D_IQ_Noise_0_LODToOctaves[11] = 15; + _2D_IQ_Noise_0_LODToOctaves[12] = 15; + _2D_IQ_Noise_0_LODToOctaves[13] = 15; + _2D_IQ_Noise_0_LODToOctaves[14] = 15; + _2D_IQ_Noise_0_LODToOctaves[15] = 15; + _2D_IQ_Noise_0_LODToOctaves[16] = 15; + _2D_IQ_Noise_0_LODToOctaves[17] = 15; + _2D_IQ_Noise_0_LODToOctaves[18] = 15; + _2D_IQ_Noise_0_LODToOctaves[19] = 15; + _2D_IQ_Noise_0_LODToOctaves[20] = 15; + _2D_IQ_Noise_0_LODToOctaves[21] = 15; + _2D_IQ_Noise_0_LODToOctaves[22] = 15; + _2D_IQ_Noise_0_LODToOctaves[23] = 15; + _2D_IQ_Noise_0_LODToOctaves[24] = 15; + _2D_IQ_Noise_0_LODToOctaves[25] = 15; + _2D_IQ_Noise_0_LODToOctaves[26] = 15; + _2D_IQ_Noise_0_LODToOctaves[27] = 15; + _2D_IQ_Noise_0_LODToOctaves[28] = 15; + _2D_IQ_Noise_0_LODToOctaves[29] = 15; + _2D_IQ_Noise_0_LODToOctaves[30] = 15; + _2D_IQ_Noise_0_LODToOctaves[31] = 15; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_0_Noise.SetSeed(Variable_30); + _2D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_1_Noise.SetSeed(Variable_29); + _2D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_3 = Context.GetLocalX(); + + // X + BufferX.Variable_19 = Context.GetLocalX(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_12 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Y + v_flt Variable_13; // Y output 0 + Variable_13 = Context.GetLocalY(); + + // Y + v_flt Variable_4; // Y output 0 + Variable_4 = Context.GetLocalY(); + + // Y + v_flt Variable_20; // Y output 0 + Variable_20 = Context.GetLocalY(); + + // 2D IQ Noise + v_flt Variable_23; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_23 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_19, Variable_20, BufferConstant.Variable_31, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_23 = FMath::Clamp(Variable_23, -0.779186, 0.705623); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.482251, 1.789484); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.568460, 1.481016); + + // 2D Perlin Noise Fractal + v_flt Variable_5; // 2D Perlin Noise Fractal output 0 + Variable_5 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_3, Variable_4, BufferConstant.Variable_28, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_5 = FMath::Clamp(Variable_5, -0.643471, 0.527891); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_0, Variable_1, BufferConstant.Variable_27, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.643471, 0.527891); + + // Vector Length.Vector Length + v_flt Variable_40; // Vector Length.Vector Length output 0 + Variable_40 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_12, Variable_13, v_flt(0.0f)); + + // * + v_flt Variable_21; // * output 0 + Variable_21 = Variable_23 * BufferConstant.Variable_38; + + // * + BufferXY.Variable_6 = Variable_2 * BufferConstant.Variable_33; + + // * + BufferXY.Variable_7 = Variable_5 * BufferConstant.Variable_34; + + // - + BufferXY.Variable_14 = BufferConstant.Variable_15 - Variable_40; + + // + + BufferXY.Variable_22 = Variable_21 + BufferConstant.Variable_39; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_3 = Context.GetLocalX(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Y + v_flt Variable_13; // Y output 0 + Variable_13 = Context.GetLocalY(); + + // Y + v_flt Variable_4; // Y output 0 + Variable_4 = Context.GetLocalY(); + + // Y + v_flt Variable_20; // Y output 0 + Variable_20 = Context.GetLocalY(); + + // X + BufferX.Variable_19 = Context.GetLocalX(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_12 = Context.GetLocalX(); + + // 2D IQ Noise + v_flt Variable_23; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_23 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_19, Variable_20, BufferConstant.Variable_31, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_23 = FMath::Clamp(Variable_23, -0.779186, 0.705623); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.482251, 1.789484); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.568460, 1.481016); + + // 2D Perlin Noise Fractal + v_flt Variable_5; // 2D Perlin Noise Fractal output 0 + Variable_5 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_3, Variable_4, BufferConstant.Variable_28, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_5 = FMath::Clamp(Variable_5, -0.643471, 0.527891); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_0, Variable_1, BufferConstant.Variable_27, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.643471, 0.527891); + + // Vector Length.Vector Length + v_flt Variable_40; // Vector Length.Vector Length output 0 + Variable_40 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_12, Variable_13, v_flt(0.0f)); + + // * + v_flt Variable_21; // * output 0 + Variable_21 = Variable_23 * BufferConstant.Variable_38; + + // * + BufferXY.Variable_6 = Variable_2 * BufferConstant.Variable_33; + + // * + BufferXY.Variable_7 = Variable_5 * BufferConstant.Variable_34; + + // - + BufferXY.Variable_14 = BufferConstant.Variable_15 - Variable_40; + + // + + BufferXY.Variable_22 = Variable_21 + BufferConstant.Variable_39; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_17; // Z output 0 + Variable_17 = Context.GetLocalZ(); + + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // - + v_flt Variable_10; // - output 0 + Variable_10 = BufferXY.Variable_6 - Variable_11; + + // - + v_flt Variable_9; // - output 0 + Variable_9 = Variable_8 - BufferXY.Variable_7; + + // - + v_flt Variable_18; // - output 0 + Variable_18 = Variable_17 - BufferXY.Variable_22; + + // Smooth Union.- + v_flt Variable_58; // Smooth Union.- output 0 + Variable_58 = Variable_10 - Variable_9; + + // Smooth Union./ + v_flt Variable_59; // Smooth Union./ output 0 + Variable_59 = Variable_58 / BufferConstant.Variable_24; + + // Smooth Union.* + v_flt Variable_60; // Smooth Union.* output 0 + Variable_60 = Variable_59 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_61; // Smooth Union.+ output 0 + Variable_61 = Variable_60 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_62; // Smooth Union.Clamp output 0 + Variable_62 = FVoxelNodeFunctions::Clamp(Variable_61, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.Lerp + v_flt Variable_63; // Smooth Union.Lerp output 0 + Variable_63 = FVoxelNodeFunctions::Lerp(Variable_10, Variable_9, Variable_62); + + // Smooth Union.1 - X + v_flt Variable_66; // Smooth Union.1 - X output 0 + Variable_66 = 1 - Variable_62; + + // Smooth Union.* + v_flt Variable_65; // Smooth Union.* output 0 + Variable_65 = BufferConstant.Variable_24 * Variable_62 * Variable_66; + + // Smooth Union.- + v_flt Variable_64; // Smooth Union.- output 0 + Variable_64 = Variable_63 - Variable_65; + + // + + v_flt Variable_35; // + output 0 + Variable_35 = Variable_64 + BufferConstant.Variable_37; + + // Smooth Union.- + v_flt Variable_41; // Smooth Union.- output 0 + Variable_41 = BufferXY.Variable_14 - Variable_35; + + // Smooth Union./ + v_flt Variable_42; // Smooth Union./ output 0 + Variable_42 = Variable_41 / BufferConstant.Variable_25; + + // Smooth Union.* + v_flt Variable_43; // Smooth Union.* output 0 + Variable_43 = Variable_42 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_44; // Smooth Union.+ output 0 + Variable_44 = Variable_43 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_45; // Smooth Union.Clamp output 0 + Variable_45 = FVoxelNodeFunctions::Clamp(Variable_44, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.1 - X + v_flt Variable_49; // Smooth Union.1 - X output 0 + Variable_49 = 1 - Variable_45; + + // Smooth Union.Lerp + v_flt Variable_46; // Smooth Union.Lerp output 0 + Variable_46 = FVoxelNodeFunctions::Lerp(BufferXY.Variable_14, Variable_35, Variable_45); + + // Smooth Union.* + v_flt Variable_48; // Smooth Union.* output 0 + Variable_48 = BufferConstant.Variable_25 * Variable_45 * Variable_49; + + // Smooth Union.- + v_flt Variable_47; // Smooth Union.- output 0 + Variable_47 = Variable_46 - Variable_48; + + // Smooth Intersection.- + v_flt Variable_57; // Smooth Intersection.- output 0 + Variable_57 = Variable_18 - Variable_47; + + // Smooth Intersection./ + v_flt Variable_50; // Smooth Intersection./ output 0 + Variable_50 = Variable_57 / BufferConstant.Variable_26; + + // Smooth Intersection.* + v_flt Variable_51; // Smooth Intersection.* output 0 + Variable_51 = Variable_50 * v_flt(0.5f); + + // Smooth Intersection.- + v_flt Variable_16; // Smooth Intersection.- output 0 + Variable_16 = v_flt(0.5f) - Variable_51; + + // Smooth Intersection.Clamp + v_flt Variable_52; // Smooth Intersection.Clamp output 0 + Variable_52 = FVoxelNodeFunctions::Clamp(Variable_16, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Intersection.1 - X + v_flt Variable_55; // Smooth Intersection.1 - X output 0 + Variable_55 = 1 - Variable_52; + + // Smooth Intersection.Lerp + v_flt Variable_53; // Smooth Intersection.Lerp output 0 + Variable_53 = FVoxelNodeFunctions::Lerp(Variable_18, Variable_47, Variable_52); + + // Smooth Intersection.* + v_flt Variable_54; // Smooth Intersection.* output 0 + Variable_54 = BufferConstant.Variable_26 * Variable_52 * Variable_55; + + // Smooth Intersection.+ + v_flt Variable_56; // Smooth Intersection.+ output 0 + Variable_56 = Variable_53 + Variable_54; + + Outputs.Value = Variable_56; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // X + v_flt Variable_3; // X output 0 + Variable_3 = Context.GetLocalX(); + + // Z + v_flt Variable_17; // Z output 0 + Variable_17 = Context.GetLocalZ(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Y + v_flt Variable_13; // Y output 0 + Variable_13 = Context.GetLocalY(); + + // Y + v_flt Variable_4; // Y output 0 + Variable_4 = Context.GetLocalY(); + + // Y + v_flt Variable_20; // Y output 0 + Variable_20 = Context.GetLocalY(); + + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // X + v_flt Variable_19; // X output 0 + Variable_19 = Context.GetLocalX(); + + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // X + v_flt Variable_12; // X output 0 + Variable_12 = Context.GetLocalX(); + + // 2D IQ Noise + v_flt Variable_23; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_23 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(Variable_19, Variable_20, BufferConstant.Variable_31, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_23 = FMath::Clamp(Variable_23, -0.779186, 0.705623); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.482251, 1.789484); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.568460, 1.481016); + + // 2D Perlin Noise Fractal + v_flt Variable_5; // 2D Perlin Noise Fractal output 0 + Variable_5 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(Variable_3, Variable_4, BufferConstant.Variable_28, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_5 = FMath::Clamp(Variable_5, -0.643471, 0.527891); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(Variable_0, Variable_1, BufferConstant.Variable_27, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.643471, 0.527891); + + // Vector Length.Vector Length + v_flt Variable_40; // Vector Length.Vector Length output 0 + Variable_40 = FVoxelNodeFunctions::VectorLength(Variable_12, Variable_13, v_flt(0.0f)); + + // * + v_flt Variable_21; // * output 0 + Variable_21 = Variable_23 * BufferConstant.Variable_38; + + // * + v_flt Variable_6; // * output 0 + Variable_6 = Variable_2 * BufferConstant.Variable_33; + + // * + v_flt Variable_7; // * output 0 + Variable_7 = Variable_5 * BufferConstant.Variable_34; + + // - + v_flt Variable_14; // - output 0 + Variable_14 = BufferConstant.Variable_15 - Variable_40; + + // + + v_flt Variable_22; // + output 0 + Variable_22 = Variable_21 + BufferConstant.Variable_39; + + // - + v_flt Variable_10; // - output 0 + Variable_10 = Variable_6 - Variable_11; + + // - + v_flt Variable_9; // - output 0 + Variable_9 = Variable_8 - Variable_7; + + // - + v_flt Variable_18; // - output 0 + Variable_18 = Variable_17 - Variable_22; + + // Smooth Union.- + v_flt Variable_58; // Smooth Union.- output 0 + Variable_58 = Variable_10 - Variable_9; + + // Smooth Union./ + v_flt Variable_59; // Smooth Union./ output 0 + Variable_59 = Variable_58 / BufferConstant.Variable_24; + + // Smooth Union.* + v_flt Variable_60; // Smooth Union.* output 0 + Variable_60 = Variable_59 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_61; // Smooth Union.+ output 0 + Variable_61 = Variable_60 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_62; // Smooth Union.Clamp output 0 + Variable_62 = FVoxelNodeFunctions::Clamp(Variable_61, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.Lerp + v_flt Variable_63; // Smooth Union.Lerp output 0 + Variable_63 = FVoxelNodeFunctions::Lerp(Variable_10, Variable_9, Variable_62); + + // Smooth Union.1 - X + v_flt Variable_66; // Smooth Union.1 - X output 0 + Variable_66 = 1 - Variable_62; + + // Smooth Union.* + v_flt Variable_65; // Smooth Union.* output 0 + Variable_65 = BufferConstant.Variable_24 * Variable_62 * Variable_66; + + // Smooth Union.- + v_flt Variable_64; // Smooth Union.- output 0 + Variable_64 = Variable_63 - Variable_65; + + // + + v_flt Variable_35; // + output 0 + Variable_35 = Variable_64 + BufferConstant.Variable_37; + + // Smooth Union.- + v_flt Variable_41; // Smooth Union.- output 0 + Variable_41 = Variable_14 - Variable_35; + + // Smooth Union./ + v_flt Variable_42; // Smooth Union./ output 0 + Variable_42 = Variable_41 / BufferConstant.Variable_25; + + // Smooth Union.* + v_flt Variable_43; // Smooth Union.* output 0 + Variable_43 = Variable_42 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_44; // Smooth Union.+ output 0 + Variable_44 = Variable_43 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_45; // Smooth Union.Clamp output 0 + Variable_45 = FVoxelNodeFunctions::Clamp(Variable_44, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.1 - X + v_flt Variable_49; // Smooth Union.1 - X output 0 + Variable_49 = 1 - Variable_45; + + // Smooth Union.Lerp + v_flt Variable_46; // Smooth Union.Lerp output 0 + Variable_46 = FVoxelNodeFunctions::Lerp(Variable_14, Variable_35, Variable_45); + + // Smooth Union.* + v_flt Variable_48; // Smooth Union.* output 0 + Variable_48 = BufferConstant.Variable_25 * Variable_45 * Variable_49; + + // Smooth Union.- + v_flt Variable_47; // Smooth Union.- output 0 + Variable_47 = Variable_46 - Variable_48; + + // Smooth Intersection.- + v_flt Variable_57; // Smooth Intersection.- output 0 + Variable_57 = Variable_18 - Variable_47; + + // Smooth Intersection./ + v_flt Variable_50; // Smooth Intersection./ output 0 + Variable_50 = Variable_57 / BufferConstant.Variable_26; + + // Smooth Intersection.* + v_flt Variable_51; // Smooth Intersection.* output 0 + Variable_51 = Variable_50 * v_flt(0.5f); + + // Smooth Intersection.- + v_flt Variable_16; // Smooth Intersection.- output 0 + Variable_16 = v_flt(0.5f) - Variable_51; + + // Smooth Intersection.Clamp + v_flt Variable_52; // Smooth Intersection.Clamp output 0 + Variable_52 = FVoxelNodeFunctions::Clamp(Variable_16, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Intersection.1 - X + v_flt Variable_55; // Smooth Intersection.1 - X output 0 + Variable_55 = 1 - Variable_52; + + // Smooth Intersection.Lerp + v_flt Variable_53; // Smooth Intersection.Lerp output 0 + Variable_53 = FVoxelNodeFunctions::Lerp(Variable_18, Variable_47, Variable_52); + + // Smooth Intersection.* + v_flt Variable_54; // Smooth Intersection.* output 0 + Variable_54 = BufferConstant.Variable_26 * Variable_52 * Variable_55; + + // Smooth Intersection.+ + v_flt Variable_56; // Smooth Intersection.+ output 0 + Variable_56 = Variable_53 + Variable_54; + + Outputs.Value = Variable_56; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_18; // Bottom Top Merge Smoothness = 25.0 output 0 + TVoxelRange Variable_19; // Cave Walls Smoothness = 100.0 output 0 + TVoxelRange Variable_20; // Global Height Merge Smoothness = 15.0 output 0 + TVoxelRange Variable_11; // Cave Radius = 400.0 output 0 + TVoxelRange Variable_25; // / output 0 + TVoxelRange Variable_2; // * output 0 + TVoxelRange Variable_3; // * output 0 + TVoxelRange Variable_16; // + output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_8; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_10; // - output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_2_Noise.SetSeed(FVoxelGraphSeed(1338)); + _2D_Perlin_Noise_Fractal_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_2_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[31] = 3; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_3_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_3_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[31] = 3; + + // Init of 2D IQ Noise + _2D_IQ_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1339)); + _2D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_1_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_1_LODToOctaves[0] = 15; + _2D_IQ_Noise_1_LODToOctaves[1] = 15; + _2D_IQ_Noise_1_LODToOctaves[2] = 15; + _2D_IQ_Noise_1_LODToOctaves[3] = 15; + _2D_IQ_Noise_1_LODToOctaves[4] = 15; + _2D_IQ_Noise_1_LODToOctaves[5] = 15; + _2D_IQ_Noise_1_LODToOctaves[6] = 15; + _2D_IQ_Noise_1_LODToOctaves[7] = 15; + _2D_IQ_Noise_1_LODToOctaves[8] = 15; + _2D_IQ_Noise_1_LODToOctaves[9] = 15; + _2D_IQ_Noise_1_LODToOctaves[10] = 15; + _2D_IQ_Noise_1_LODToOctaves[11] = 15; + _2D_IQ_Noise_1_LODToOctaves[12] = 15; + _2D_IQ_Noise_1_LODToOctaves[13] = 15; + _2D_IQ_Noise_1_LODToOctaves[14] = 15; + _2D_IQ_Noise_1_LODToOctaves[15] = 15; + _2D_IQ_Noise_1_LODToOctaves[16] = 15; + _2D_IQ_Noise_1_LODToOctaves[17] = 15; + _2D_IQ_Noise_1_LODToOctaves[18] = 15; + _2D_IQ_Noise_1_LODToOctaves[19] = 15; + _2D_IQ_Noise_1_LODToOctaves[20] = 15; + _2D_IQ_Noise_1_LODToOctaves[21] = 15; + _2D_IQ_Noise_1_LODToOctaves[22] = 15; + _2D_IQ_Noise_1_LODToOctaves[23] = 15; + _2D_IQ_Noise_1_LODToOctaves[24] = 15; + _2D_IQ_Noise_1_LODToOctaves[25] = 15; + _2D_IQ_Noise_1_LODToOctaves[26] = 15; + _2D_IQ_Noise_1_LODToOctaves[27] = 15; + _2D_IQ_Noise_1_LODToOctaves[28] = 15; + _2D_IQ_Noise_1_LODToOctaves[29] = 15; + _2D_IQ_Noise_1_LODToOctaves[30] = 15; + _2D_IQ_Noise_1_LODToOctaves[31] = 15; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Bottom Top Merge Smoothness = 25.0 + BufferConstant.Variable_18 = Params.Bottom_Top_Merge_Smoothness; + + // 2D Perlin Noise Fractal + TVoxelRange Variable_1; // 2D Perlin Noise Fractal output 0 + Variable_1 = { -0.643471f, 0.527891f }; + + // Cave Walls Smoothness = 100.0 + BufferConstant.Variable_19 = Params.Cave_Walls_Smoothness; + + // Global Height Merge Smoothness = 15.0 + BufferConstant.Variable_20 = Params.Global_Height_Merge_Smoothness; + + // Cave Radius = 400.0 + BufferConstant.Variable_11 = Params.Cave_Radius; + + // Top Noise Scale = 150.0 + TVoxelRange Variable_21; // Top Noise Scale = 150.0 output 0 + Variable_21 = Params.Top_Noise_Scale; + + // Cave Height = 100.0 + TVoxelRange Variable_24; // Cave Height = 100.0 output 0 + Variable_24 = Params.Cave_Height; + + // 2D Perlin Noise Fractal + TVoxelRange Variable_0; // 2D Perlin Noise Fractal output 0 + Variable_0 = { -0.643471f, 0.527891f }; + + // Global Height Noise Scale = 200.0 + TVoxelRange Variable_26; // Global Height Noise Scale = 200.0 output 0 + Variable_26 = Params.Global_Height_Noise_Scale; + + // 2D IQ Noise + TVoxelRange Variable_17; // 2D IQ Noise output 0 + TVoxelRange _2D_IQ_Noise_1_Temp_1; // 2D IQ Noise output 1 + TVoxelRange _2D_IQ_Noise_1_Temp_2; // 2D IQ Noise output 2 + Variable_17 = { -0.779186f, 0.705623f }; + _2D_IQ_Noise_1_Temp_1 = { -1.482251f, 1.789484f }; + _2D_IQ_Noise_1_Temp_2 = { -1.568460f, 1.481016f }; + + // Global Height Offset = 150.0 + TVoxelRange Variable_27; // Global Height Offset = 150.0 output 0 + Variable_27 = Params.Global_Height_Offset; + + // Bottom Noise Scale = 150.0 + TVoxelRange Variable_22; // Bottom Noise Scale = 150.0 output 0 + Variable_22 = Params.Bottom_Noise_Scale; + + // / + BufferConstant.Variable_25 = Variable_24 / TVoxelRange(2.0f); + + // * + BufferConstant.Variable_2 = Variable_0 * Variable_21; + + // * + BufferConstant.Variable_3 = Variable_1 * Variable_22; + + // * + TVoxelRange Variable_15; // * output 0 + Variable_15 = Variable_17 * Variable_26; + + // + + BufferConstant.Variable_16 = Variable_15 + Variable_27; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_2_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_2_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_3_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_3_LODToOctaves; + FVoxelFastNoise _2D_IQ_Noise_1_Noise; + TStaticArray _2D_IQ_Noise_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_9; // Y output 0 + Variable_9 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_13; // Z output 0 + Variable_13 = Context.GetLocalZ(); + + // Z + TVoxelRange Variable_7; // Z output 0 + Variable_7 = Context.GetLocalZ(); + + // X + TVoxelRange Variable_8; // X output 0 + Variable_8 = Context.GetLocalX(); + + // - + TVoxelRange Variable_6; // - output 0 + Variable_6 = BufferConstant.Variable_2 - Variable_7; + + // Vector Length.Vector Length + TVoxelRange Variable_28; // Vector Length.Vector Length output 0 + Variable_28 = FVoxelNodeFunctions::VectorLength(Variable_8, Variable_9, TVoxelRange(0.0f)); + + // - + TVoxelRange Variable_14; // - output 0 + Variable_14 = Variable_13 - BufferConstant.Variable_16; + + // - + TVoxelRange Variable_5; // - output 0 + Variable_5 = Variable_4 - BufferConstant.Variable_3; + + // Smooth Union.- + TVoxelRange Variable_46; // Smooth Union.- output 0 + Variable_46 = Variable_6 - Variable_5; + + // - + TVoxelRange Variable_10; // - output 0 + Variable_10 = BufferConstant.Variable_11 - Variable_28; + + // Smooth Union./ + TVoxelRange Variable_47; // Smooth Union./ output 0 + Variable_47 = Variable_46 / BufferConstant.Variable_18; + + // Smooth Union.* + TVoxelRange Variable_48; // Smooth Union.* output 0 + Variable_48 = Variable_47 * TVoxelRange(0.5f); + + // Smooth Union.+ + TVoxelRange Variable_49; // Smooth Union.+ output 0 + Variable_49 = Variable_48 + TVoxelRange(0.5f); + + // Smooth Union.Clamp + TVoxelRange Variable_50; // Smooth Union.Clamp output 0 + Variable_50 = FVoxelNodeFunctions::Clamp(Variable_49, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // Smooth Union.1 - X + TVoxelRange Variable_54; // Smooth Union.1 - X output 0 + Variable_54 = 1 - Variable_50; + + // Smooth Union.Lerp + TVoxelRange Variable_51; // Smooth Union.Lerp output 0 + Variable_51 = FVoxelNodeFunctions::Lerp(Variable_6, Variable_5, Variable_50); + + // Smooth Union.* + TVoxelRange Variable_53; // Smooth Union.* output 0 + Variable_53 = BufferConstant.Variable_18 * Variable_50 * Variable_54; + + // Smooth Union.- + TVoxelRange Variable_52; // Smooth Union.- output 0 + Variable_52 = Variable_51 - Variable_53; + + // + + TVoxelRange Variable_23; // + output 0 + Variable_23 = Variable_52 + BufferConstant.Variable_25; + + // Smooth Union.- + TVoxelRange Variable_29; // Smooth Union.- output 0 + Variable_29 = Variable_10 - Variable_23; + + // Smooth Union./ + TVoxelRange Variable_30; // Smooth Union./ output 0 + Variable_30 = Variable_29 / BufferConstant.Variable_19; + + // Smooth Union.* + TVoxelRange Variable_31; // Smooth Union.* output 0 + Variable_31 = Variable_30 * TVoxelRange(0.5f); + + // Smooth Union.+ + TVoxelRange Variable_32; // Smooth Union.+ output 0 + Variable_32 = Variable_31 + TVoxelRange(0.5f); + + // Smooth Union.Clamp + TVoxelRange Variable_33; // Smooth Union.Clamp output 0 + Variable_33 = FVoxelNodeFunctions::Clamp(Variable_32, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // Smooth Union.Lerp + TVoxelRange Variable_34; // Smooth Union.Lerp output 0 + Variable_34 = FVoxelNodeFunctions::Lerp(Variable_10, Variable_23, Variable_33); + + // Smooth Union.1 - X + TVoxelRange Variable_37; // Smooth Union.1 - X output 0 + Variable_37 = 1 - Variable_33; + + // Smooth Union.* + TVoxelRange Variable_36; // Smooth Union.* output 0 + Variable_36 = BufferConstant.Variable_19 * Variable_33 * Variable_37; + + // Smooth Union.- + TVoxelRange Variable_35; // Smooth Union.- output 0 + Variable_35 = Variable_34 - Variable_36; + + // Smooth Intersection.- + TVoxelRange Variable_45; // Smooth Intersection.- output 0 + Variable_45 = Variable_14 - Variable_35; + + // Smooth Intersection./ + TVoxelRange Variable_38; // Smooth Intersection./ output 0 + Variable_38 = Variable_45 / BufferConstant.Variable_20; + + // Smooth Intersection.* + TVoxelRange Variable_39; // Smooth Intersection.* output 0 + Variable_39 = Variable_38 * TVoxelRange(0.5f); + + // Smooth Intersection.- + TVoxelRange Variable_12; // Smooth Intersection.- output 0 + Variable_12 = TVoxelRange(0.5f) - Variable_39; + + // Smooth Intersection.Clamp + TVoxelRange Variable_40; // Smooth Intersection.Clamp output 0 + Variable_40 = FVoxelNodeFunctions::Clamp(Variable_12, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // Smooth Intersection.Lerp + TVoxelRange Variable_41; // Smooth Intersection.Lerp output 0 + Variable_41 = FVoxelNodeFunctions::Lerp(Variable_14, Variable_35, Variable_40); + + // Smooth Intersection.1 - X + TVoxelRange Variable_43; // Smooth Intersection.1 - X output 0 + Variable_43 = 1 - Variable_40; + + // Smooth Intersection.* + TVoxelRange Variable_42; // Smooth Intersection.* output 0 + Variable_42 = BufferConstant.Variable_20 * Variable_40 * Variable_43; + + // Smooth Intersection.+ + TVoxelRange Variable_44; // Smooth Intersection.+ output 0 + Variable_44 = Variable_41 + Variable_42; + + Outputs.Value = Variable_44; + } + + }; + + FVoxelExample_CaveInstance(UVoxelExample_Cave& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Bottom_Noise_Frequency, + Object.Bottom_Noise_Scale, + Object.Bottom_Noise_Seed, + Object.Global_Height_Seed, + Object.Top_Noise_Seed, + Object.Top_Noise_Frequency, + Object.Top_Noise_Scale, + Object.Bottom_Top_Merge_Smoothness, + Object.Global_Height_Merge_Smoothness, + Object.Global_Height_Noise_Frequency, + Object.Global_Height_Noise_Scale, + Object.Global_Height_Offset, + Object.Cave_Height, + Object.Cave_Radius, + Object.Cave_Walls_Smoothness + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_CaveInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_CaveInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_CaveInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_CaveInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_CaveInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_CaveInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_CaveInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_CaveInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_Cave::UVoxelExample_Cave() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_Cave::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_Cave. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_Cave. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Cave. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Cave. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.h new file mode 100644 index 00000000..ddb170cf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.h @@ -0,0 +1,63 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_Cave.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_Cave : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bottom Noise", meta=(DisplayName="Bottom Noise Frequency")) + float Bottom_Noise_Frequency = 0.008; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bottom Noise", meta=(DisplayName="Bottom Noise Scale")) + float Bottom_Noise_Scale = 150.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Bottom Noise Seed")) + int32 Bottom_Noise_Seed = 3024; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Global Height Seed")) + int32 Global_Height_Seed = 1447; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Top Noise Seed")) + int32 Top_Noise_Seed = 3022; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Top Noise", meta=(DisplayName="Top Noise Frequency")) + float Top_Noise_Frequency = 0.005; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Top Noise", meta=(DisplayName="Top Noise Scale")) + float Top_Noise_Scale = 150.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Bottom & Top Merge", meta=(DisplayName="Bottom Top Merge Smoothness")) + float Bottom_Top_Merge_Smoothness = 25.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Global Height", meta=(DisplayName="Global Height Merge Smoothness")) + float Global_Height_Merge_Smoothness = 15.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Global Height", meta=(DisplayName="Global Height Noise Frequency")) + float Global_Height_Noise_Frequency = 0.005; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Global Height", meta=(DisplayName="Global Height Noise Scale")) + float Global_Height_Noise_Scale = 200.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Global Height", meta=(DisplayName="Global Height Offset")) + float Global_Height_Offset = 150.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cave Settings", meta=(DisplayName="Cave Height")) + float Cave_Height = 100.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cave Settings", meta=(DisplayName="Cave Radius")) + float Cave_Radius = 400.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Cave Settings", meta=(DisplayName="Cave Walls Smoothness")) + float Cave_Walls_Smoothness = 100.0; + + UVoxelExample_Cave(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.cpp new file mode 100644 index 00000000..dea42431 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.cpp @@ -0,0 +1,1424 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_Cliffs.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_CliffsInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Cliffs_Slope; + const float Height; + const float Overhangs; + const float Base_Shape_Frequency; + const float Base_Shape_Offset; + const int32 Base_Shape_Seed; + const int32 Sides_Noise_Seed; + const int32 Top_Noise_Seed; + const float Sides_Noise_Amplitude; + const float Sides_Noise_Frequency; + const float Top_Noise_Frequency; + const float Top_Noise_Scale; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_21; // Cliffs Slope = 10.0 output 0 + v_flt Variable_27; // Sides Noise Amplitude = 0.2 output 0 + v_flt Variable_28; // Height = 50.0 output 0 + v_flt Variable_29; // Overhangs = 0.2 output 0 + v_flt Variable_30; // Base Shape Frequency = 0.005 output 0 + v_flt Variable_33; // Sides Noise Frequency = 0.1 output 0 + v_flt Variable_35; // Top Noise Frequency = 0.01 output 0 + v_flt Variable_37; // Top Noise Scale = 25.0 output 0 + v_flt Variable_32; // / output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // X output 0 + v_flt Variable_14; // X output 0 + v_flt Variable_24; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_19; // + output 0 + v_flt Variable_17; // * output 0 + v_flt Variable_26; // * output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Base Shape Seed = 3323 + FVoxelGraphSeed Variable_31; // Base Shape Seed = 3323 output 0 + Variable_31 = Params.Base_Shape_Seed; + + // Init of Sides Noise Seed = 2647 + FVoxelGraphSeed Variable_34; // Sides Noise Seed = 2647 output 0 + Variable_34 = Params.Sides_Noise_Seed; + + // Init of Top Noise Seed = 12932 + FVoxelGraphSeed Variable_36; // Top Noise Seed = 12932 output 0 + Variable_36 = Params.Top_Noise_Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Cliffs Slope = 10.0 + BufferConstant.Variable_21 = Params.Cliffs_Slope; + + // Sides Noise Amplitude = 0.2 + BufferConstant.Variable_27 = Params.Sides_Noise_Amplitude; + + // Height = 50.0 + BufferConstant.Variable_28 = Params.Height; + + // Overhangs = 0.2 + BufferConstant.Variable_29 = Params.Overhangs; + + // Base Shape Frequency = 0.005 + BufferConstant.Variable_30 = Params.Base_Shape_Frequency; + + // Base Shape Offset = 0.0 + v_flt Variable_20; // Base Shape Offset = 0.0 output 0 + Variable_20 = Params.Base_Shape_Offset; + + // Sides Noise Frequency = 0.1 + BufferConstant.Variable_33 = Params.Sides_Noise_Frequency; + + // Top Noise Frequency = 0.01 + BufferConstant.Variable_35 = Params.Top_Noise_Frequency; + + // Top Noise Scale = 25.0 + BufferConstant.Variable_37 = Params.Top_Noise_Scale; + + // / + BufferConstant.Variable_32 = Variable_20 / v_flt(10.0f); + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_0_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_1_LODToOctaves; + FVoxelFastNoise _2D_IQ_Noise_0_Noise; + TStaticArray _2D_IQ_Noise_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Top Noise Seed = 12932 + FVoxelGraphSeed Variable_36; // Top Noise Seed = 12932 output 0 + Variable_36 = Params.Top_Noise_Seed; + + // Init of Base Shape Seed = 3323 + FVoxelGraphSeed Variable_31; // Base Shape Seed = 3323 output 0 + Variable_31 = Params.Base_Shape_Seed; + + // Init of Sides Noise Seed = 2647 + FVoxelGraphSeed Variable_34; // Sides Noise Seed = 2647 output 0 + Variable_34 = Params.Sides_Noise_Seed; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_0_Noise.SetSeed(Variable_31); + _2D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_1_Noise.SetSeed(Variable_34); + _2D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + // Init of 2D IQ Noise + _2D_IQ_Noise_0_Noise.SetSeed(Variable_36); + _2D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_0_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_0_LODToOctaves[0] = 15; + _2D_IQ_Noise_0_LODToOctaves[1] = 15; + _2D_IQ_Noise_0_LODToOctaves[2] = 15; + _2D_IQ_Noise_0_LODToOctaves[3] = 15; + _2D_IQ_Noise_0_LODToOctaves[4] = 15; + _2D_IQ_Noise_0_LODToOctaves[5] = 15; + _2D_IQ_Noise_0_LODToOctaves[6] = 15; + _2D_IQ_Noise_0_LODToOctaves[7] = 15; + _2D_IQ_Noise_0_LODToOctaves[8] = 15; + _2D_IQ_Noise_0_LODToOctaves[9] = 15; + _2D_IQ_Noise_0_LODToOctaves[10] = 15; + _2D_IQ_Noise_0_LODToOctaves[11] = 15; + _2D_IQ_Noise_0_LODToOctaves[12] = 15; + _2D_IQ_Noise_0_LODToOctaves[13] = 15; + _2D_IQ_Noise_0_LODToOctaves[14] = 15; + _2D_IQ_Noise_0_LODToOctaves[15] = 15; + _2D_IQ_Noise_0_LODToOctaves[16] = 15; + _2D_IQ_Noise_0_LODToOctaves[17] = 15; + _2D_IQ_Noise_0_LODToOctaves[18] = 15; + _2D_IQ_Noise_0_LODToOctaves[19] = 15; + _2D_IQ_Noise_0_LODToOctaves[20] = 15; + _2D_IQ_Noise_0_LODToOctaves[21] = 15; + _2D_IQ_Noise_0_LODToOctaves[22] = 15; + _2D_IQ_Noise_0_LODToOctaves[23] = 15; + _2D_IQ_Noise_0_LODToOctaves[24] = 15; + _2D_IQ_Noise_0_LODToOctaves[25] = 15; + _2D_IQ_Noise_0_LODToOctaves[26] = 15; + _2D_IQ_Noise_0_LODToOctaves[27] = 15; + _2D_IQ_Noise_0_LODToOctaves[28] = 15; + _2D_IQ_Noise_0_LODToOctaves[29] = 15; + _2D_IQ_Noise_0_LODToOctaves[30] = 15; + _2D_IQ_Noise_0_LODToOctaves[31] = 15; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + // X + BufferX.Variable_24 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // Y + v_flt Variable_25; // Y output 0 + Variable_25 = Context.GetLocalY(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_0, Variable_1, BufferConstant.Variable_30, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.576700, 0.658489); + + // 2D Perlin Noise Fractal + v_flt Variable_23; // 2D Perlin Noise Fractal output 0 + Variable_23 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_24, Variable_25, BufferConstant.Variable_33, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_23 = FMath::Clamp(Variable_23, -0.594076, 0.593483); + + // 2D IQ Noise + v_flt Variable_16; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_16 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_14, Variable_15, BufferConstant.Variable_35, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_16 = FMath::Clamp(Variable_16, -0.648057, 0.611352); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.300998, 1.397865); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.723871, 1.259353); + + // + + BufferXY.Variable_19 = Variable_2 + BufferConstant.Variable_32; + + // * + BufferXY.Variable_17 = Variable_16 * BufferConstant.Variable_37; + + // * + BufferXY.Variable_26 = Variable_23 * BufferConstant.Variable_27; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + // X + BufferX.Variable_24 = Context.GetLocalX(); + + // Y + v_flt Variable_25; // Y output 0 + Variable_25 = Context.GetLocalY(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(BufferX.Variable_0, Variable_1, BufferConstant.Variable_30, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.576700, 0.658489); + + // 2D Perlin Noise Fractal + v_flt Variable_23; // 2D Perlin Noise Fractal output 0 + Variable_23 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(BufferX.Variable_24, Variable_25, BufferConstant.Variable_33, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_23 = FMath::Clamp(Variable_23, -0.594076, 0.593483); + + // 2D IQ Noise + v_flt Variable_16; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_16 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_14, Variable_15, BufferConstant.Variable_35, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_16 = FMath::Clamp(Variable_16, -0.648057, 0.611352); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.300998, 1.397865); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.723871, 1.259353); + + // + + BufferXY.Variable_19 = Variable_2 + BufferConstant.Variable_32; + + // * + BufferXY.Variable_17 = Variable_16 * BufferConstant.Variable_37; + + // * + BufferXY.Variable_26 = Variable_23 * BufferConstant.Variable_27; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_7; // Z output 0 + Variable_7 = Context.GetLocalZ(); + + // Z + v_flt Variable_3; // Z output 0 + Variable_3 = Context.GetLocalZ(); + + // / + v_flt Variable_4; // / output 0 + Variable_4 = Variable_3 / BufferConstant.Variable_28; + + // + + v_flt Variable_22; // + output 0 + Variable_22 = Variable_4 + BufferXY.Variable_26; + + // Clamp + v_flt Variable_5; // Clamp output 0 + Variable_5 = FVoxelNodeFunctions::Clamp(Variable_22, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_12; // * output 0 + Variable_12 = Variable_5 * BufferConstant.Variable_29; + + // + + v_flt Variable_6; // + output 0 + Variable_6 = BufferXY.Variable_19 + Variable_12; + + // * + v_flt Variable_10; // * output 0 + Variable_10 = Variable_6 * BufferConstant.Variable_21; + + // Clamp + v_flt Variable_11; // Clamp output 0 + Variable_11 = FVoxelNodeFunctions::Clamp(Variable_10, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_9; // * output 0 + Variable_9 = Variable_11 * BufferConstant.Variable_28; + + // Lerp + v_flt Variable_18; // Lerp output 0 + Variable_18 = FVoxelNodeFunctions::Lerp(v_flt(0.0f), BufferXY.Variable_17, Variable_11); + + // + + v_flt Variable_13; // + output 0 + Variable_13 = Variable_9 + Variable_18; + + // - + v_flt Variable_8; // - output 0 + Variable_8 = Variable_7 - Variable_13; + + Outputs.Value = Variable_8; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Z + v_flt Variable_7; // Z output 0 + Variable_7 = Context.GetLocalZ(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // X + v_flt Variable_14; // X output 0 + Variable_14 = Context.GetLocalX(); + + // X + v_flt Variable_24; // X output 0 + Variable_24 = Context.GetLocalX(); + + // Y + v_flt Variable_25; // Y output 0 + Variable_25 = Context.GetLocalY(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + v_flt Variable_3; // Z output 0 + Variable_3 = Context.GetLocalZ(); + + // 2D Perlin Noise Fractal + v_flt Variable_2; // 2D Perlin Noise Fractal output 0 + Variable_2 = _2D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_2D(Variable_0, Variable_1, BufferConstant.Variable_30, _2D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_2 = FMath::Clamp(Variable_2, -0.576700, 0.658489); + + // 2D Perlin Noise Fractal + v_flt Variable_23; // 2D Perlin Noise Fractal output 0 + Variable_23 = _2D_Perlin_Noise_Fractal_1_Noise.GetPerlinFractal_2D(Variable_24, Variable_25, BufferConstant.Variable_33, _2D_Perlin_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_23 = FMath::Clamp(Variable_23, -0.594076, 0.593483); + + // 2D IQ Noise + v_flt Variable_16; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_16 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(Variable_14, Variable_15, BufferConstant.Variable_35, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_16 = FMath::Clamp(Variable_16, -0.648057, 0.611352); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.300998, 1.397865); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.723871, 1.259353); + + // / + v_flt Variable_4; // / output 0 + Variable_4 = Variable_3 / BufferConstant.Variable_28; + + // + + v_flt Variable_19; // + output 0 + Variable_19 = Variable_2 + BufferConstant.Variable_32; + + // * + v_flt Variable_17; // * output 0 + Variable_17 = Variable_16 * BufferConstant.Variable_37; + + // * + v_flt Variable_26; // * output 0 + Variable_26 = Variable_23 * BufferConstant.Variable_27; + + // + + v_flt Variable_22; // + output 0 + Variable_22 = Variable_4 + Variable_26; + + // Clamp + v_flt Variable_5; // Clamp output 0 + Variable_5 = FVoxelNodeFunctions::Clamp(Variable_22, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_12; // * output 0 + Variable_12 = Variable_5 * BufferConstant.Variable_29; + + // + + v_flt Variable_6; // + output 0 + Variable_6 = Variable_19 + Variable_12; + + // * + v_flt Variable_10; // * output 0 + Variable_10 = Variable_6 * BufferConstant.Variable_21; + + // Clamp + v_flt Variable_11; // Clamp output 0 + Variable_11 = FVoxelNodeFunctions::Clamp(Variable_10, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_9; // * output 0 + Variable_9 = Variable_11 * BufferConstant.Variable_28; + + // Lerp + v_flt Variable_18; // Lerp output 0 + Variable_18 = FVoxelNodeFunctions::Lerp(v_flt(0.0f), Variable_17, Variable_11); + + // + + v_flt Variable_13; // + output 0 + Variable_13 = Variable_9 + Variable_18; + + // - + v_flt Variable_8; // - output 0 + Variable_8 = Variable_7 - Variable_13; + + Outputs.Value = Variable_8; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_17; // Cliffs Slope = 10.0 output 0 + TVoxelRange Variable_22; // Height = 50.0 output 0 + TVoxelRange Variable_23; // Overhangs = 0.2 output 0 + TVoxelRange Variable_13; // * output 0 + TVoxelRange Variable_20; // * output 0 + TVoxelRange Variable_15; // + output 0 + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_2_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_2_LODToOctaves[31] = 3; + + // Init of 2D IQ Noise + _2D_IQ_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_1_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_1_LODToOctaves[0] = 15; + _2D_IQ_Noise_1_LODToOctaves[1] = 15; + _2D_IQ_Noise_1_LODToOctaves[2] = 15; + _2D_IQ_Noise_1_LODToOctaves[3] = 15; + _2D_IQ_Noise_1_LODToOctaves[4] = 15; + _2D_IQ_Noise_1_LODToOctaves[5] = 15; + _2D_IQ_Noise_1_LODToOctaves[6] = 15; + _2D_IQ_Noise_1_LODToOctaves[7] = 15; + _2D_IQ_Noise_1_LODToOctaves[8] = 15; + _2D_IQ_Noise_1_LODToOctaves[9] = 15; + _2D_IQ_Noise_1_LODToOctaves[10] = 15; + _2D_IQ_Noise_1_LODToOctaves[11] = 15; + _2D_IQ_Noise_1_LODToOctaves[12] = 15; + _2D_IQ_Noise_1_LODToOctaves[13] = 15; + _2D_IQ_Noise_1_LODToOctaves[14] = 15; + _2D_IQ_Noise_1_LODToOctaves[15] = 15; + _2D_IQ_Noise_1_LODToOctaves[16] = 15; + _2D_IQ_Noise_1_LODToOctaves[17] = 15; + _2D_IQ_Noise_1_LODToOctaves[18] = 15; + _2D_IQ_Noise_1_LODToOctaves[19] = 15; + _2D_IQ_Noise_1_LODToOctaves[20] = 15; + _2D_IQ_Noise_1_LODToOctaves[21] = 15; + _2D_IQ_Noise_1_LODToOctaves[22] = 15; + _2D_IQ_Noise_1_LODToOctaves[23] = 15; + _2D_IQ_Noise_1_LODToOctaves[24] = 15; + _2D_IQ_Noise_1_LODToOctaves[25] = 15; + _2D_IQ_Noise_1_LODToOctaves[26] = 15; + _2D_IQ_Noise_1_LODToOctaves[27] = 15; + _2D_IQ_Noise_1_LODToOctaves[28] = 15; + _2D_IQ_Noise_1_LODToOctaves[29] = 15; + _2D_IQ_Noise_1_LODToOctaves[30] = 15; + _2D_IQ_Noise_1_LODToOctaves[31] = 15; + + // Init of 2D Perlin Noise Fractal + _2D_Perlin_Noise_Fractal_3_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_Fractal_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalOctavesAndGain(3, 0.5); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalLacunarity(2.0); + _2D_Perlin_Noise_Fractal_3_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Perlin_Noise_Fractal_3_LODToOctaves[0] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[1] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[2] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[3] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[4] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[5] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[6] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[7] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[8] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[9] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[10] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[11] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[12] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[13] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[14] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[15] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[16] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[17] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[18] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[19] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[20] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[21] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[22] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[23] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[24] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[25] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[26] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[27] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[28] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[29] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[30] = 3; + _2D_Perlin_Noise_Fractal_3_LODToOctaves[31] = 3; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 2D Perlin Noise Fractal + TVoxelRange Variable_19; // 2D Perlin Noise Fractal output 0 + Variable_19 = { -0.594076f, 0.593483f }; + + // 2D IQ Noise + TVoxelRange Variable_12; // 2D IQ Noise output 0 + TVoxelRange _2D_IQ_Noise_1_Temp_1; // 2D IQ Noise output 1 + TVoxelRange _2D_IQ_Noise_1_Temp_2; // 2D IQ Noise output 2 + Variable_12 = { -0.648057f, 0.611352f }; + _2D_IQ_Noise_1_Temp_1 = { -1.300998f, 1.397865f }; + _2D_IQ_Noise_1_Temp_2 = { -1.723871f, 1.259353f }; + + // Top Noise Scale = 25.0 + TVoxelRange Variable_25; // Top Noise Scale = 25.0 output 0 + Variable_25 = Params.Top_Noise_Scale; + + // Base Shape Offset = 0.0 + TVoxelRange Variable_16; // Base Shape Offset = 0.0 output 0 + Variable_16 = Params.Base_Shape_Offset; + + // Cliffs Slope = 10.0 + BufferConstant.Variable_17 = Params.Cliffs_Slope; + + // 2D Perlin Noise Fractal + TVoxelRange Variable_0; // 2D Perlin Noise Fractal output 0 + Variable_0 = { -0.576700f, 0.658489f }; + + // Height = 50.0 + BufferConstant.Variable_22 = Params.Height; + + // Sides Noise Amplitude = 0.2 + TVoxelRange Variable_21; // Sides Noise Amplitude = 0.2 output 0 + Variable_21 = Params.Sides_Noise_Amplitude; + + // Overhangs = 0.2 + BufferConstant.Variable_23 = Params.Overhangs; + + // * + BufferConstant.Variable_13 = Variable_12 * Variable_25; + + // * + BufferConstant.Variable_20 = Variable_19 * Variable_21; + + // / + TVoxelRange Variable_24; // / output 0 + Variable_24 = Variable_16 / TVoxelRange(10.0f); + + // + + BufferConstant.Variable_15 = Variable_0 + Variable_24; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_Fractal_2_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_2_LODToOctaves; + FVoxelFastNoise _2D_IQ_Noise_1_Noise; + TStaticArray _2D_IQ_Noise_1_LODToOctaves; + FVoxelFastNoise _2D_Perlin_Noise_Fractal_3_Noise; + TStaticArray _2D_Perlin_Noise_Fractal_3_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_5; // Z output 0 + Variable_5 = Context.GetLocalZ(); + + // Z + TVoxelRange Variable_1; // Z output 0 + Variable_1 = Context.GetLocalZ(); + + // / + TVoxelRange Variable_2; // / output 0 + Variable_2 = Variable_1 / BufferConstant.Variable_22; + + // + + TVoxelRange Variable_18; // + output 0 + Variable_18 = Variable_2 + BufferConstant.Variable_20; + + // Clamp + TVoxelRange Variable_3; // Clamp output 0 + Variable_3 = FVoxelNodeFunctions::Clamp(Variable_18, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // * + TVoxelRange Variable_10; // * output 0 + Variable_10 = Variable_3 * BufferConstant.Variable_23; + + // + + TVoxelRange Variable_4; // + output 0 + Variable_4 = BufferConstant.Variable_15 + Variable_10; + + // * + TVoxelRange Variable_8; // * output 0 + Variable_8 = Variable_4 * BufferConstant.Variable_17; + + // Clamp + TVoxelRange Variable_9; // Clamp output 0 + Variable_9 = FVoxelNodeFunctions::Clamp(Variable_8, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // * + TVoxelRange Variable_7; // * output 0 + Variable_7 = Variable_9 * BufferConstant.Variable_22; + + // Lerp + TVoxelRange Variable_14; // Lerp output 0 + Variable_14 = FVoxelNodeFunctions::Lerp(TVoxelRange(0.0f), BufferConstant.Variable_13, Variable_9); + + // + + TVoxelRange Variable_11; // + output 0 + Variable_11 = Variable_7 + Variable_14; + + // - + TVoxelRange Variable_6; // - output 0 + Variable_6 = Variable_5 - Variable_11; + + Outputs.Value = Variable_6; + } + + }; + + FVoxelExample_CliffsInstance(UVoxelExample_Cliffs& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Cliffs_Slope, + Object.Height, + Object.Overhangs, + Object.Base_Shape_Frequency, + Object.Base_Shape_Offset, + Object.Base_Shape_Seed, + Object.Sides_Noise_Seed, + Object.Top_Noise_Seed, + Object.Sides_Noise_Amplitude, + Object.Sides_Noise_Frequency, + Object.Top_Noise_Frequency, + Object.Top_Noise_Scale + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_CliffsInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_CliffsInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_CliffsInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_CliffsInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_CliffsInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_Cliffs::UVoxelExample_Cliffs() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_Cliffs::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_Cliffs. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_Cliffs. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Cliffs. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Cliffs. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.h new file mode 100644 index 00000000..1be2f72a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.h @@ -0,0 +1,54 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_Cliffs.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_Cliffs : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta=(DisplayName="Cliffs Slope")) + float Cliffs_Slope = 10.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta=(DisplayName="Height")) + float Height = 50.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta=(DisplayName="Overhangs", UIMax="1", UIMin="0")) + float Overhangs = 0.2; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Base Shape", meta=(DisplayName="Base Shape Frequency", UIMax="1", UIMin="0")) + float Base_Shape_Frequency = 0.005; + // < 0 : more holes; > 0: less holes + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Base Shape", meta=(DisplayName="Base Shape Offset", UIMax="10", UIMin="-10")) + float Base_Shape_Offset = 0.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Base Shape Seed")) + int32 Base_Shape_Seed = 3323; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Sides Noise Seed")) + int32 Sides_Noise_Seed = 2647; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Top Noise Seed")) + int32 Top_Noise_Seed = 12932; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Side Noise", meta=(DisplayName="Sides Noise Amplitude", UIMax="1", UIMin="0")) + float Sides_Noise_Amplitude = 0.2; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Side Noise", meta=(DisplayName="Sides Noise Frequency", UIMax="1", UIMin="0")) + float Sides_Noise_Frequency = 0.1; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Top Noise", meta=(DisplayName="Top Noise Frequency", UIMax="1", UIMin="0")) + float Top_Noise_Frequency = 0.01; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Top Noise", meta=(DisplayName="Top Noise Scale")) + float Top_Noise_Scale = 25.0; + + UVoxelExample_Cliffs(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.cpp new file mode 100644 index 00000000..872914d7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.cpp @@ -0,0 +1,1311 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_FloatingIslandOnion.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_FloatingIslandOnionInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Height; + const float Perturb_Amplitude; + const float Perturb_Frequency; + const int32 Seed; + const float Top_Noise_Frequency; + const float Top_Noise_Height; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_22; // Top Noise Frequency = 0.002 output 0 + v_flt Variable_21; // Perturb Frequency = 0.01 output 0 + v_flt Variable_20; // Perturb Amplitude = 50.0 output 0 + v_flt Variable_3; // Top Noise Height = 500.0 output 0 + v_flt Variable_12; // * -1 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_6; // X output 0 + v_flt Variable_1; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_25; // 2D Noise SDF.+ output 0 + v_flt Variable_27; // Elongate.vector - vector.- output 0 + v_flt Variable_26; // Elongate.vector - vector.- output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 1337 + FVoxelGraphSeed Variable_18; // Seed = 1337 output 0 + Variable_18 = Params.Seed; + + // Init of HASH + FVoxelGraphSeed Variable_19; // HASH output 0 + Variable_19 = FVoxelUtilities::MurmurHash32(Variable_18); + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Height = 200.0 + v_flt Variable_9; // Height = 200.0 output 0 + Variable_9 = Params.Height; + + // Top Noise Frequency = 0.002 + BufferConstant.Variable_22 = Params.Top_Noise_Frequency; + + // Perturb Frequency = 0.01 + BufferConstant.Variable_21 = Params.Perturb_Frequency; + + // Perturb Amplitude = 50.0 + BufferConstant.Variable_20 = Params.Perturb_Amplitude; + + // Top Noise Height = 500.0 + BufferConstant.Variable_3 = Params.Top_Noise_Height; + + // * -1 + BufferConstant.Variable_12 = Variable_9 * -1; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_IQ_Noise_0_Noise; + TStaticArray _2D_IQ_Noise_0_LODToOctaves; + FVoxelFastNoise _2D_Gradient_Perturb_Fractal_0_Noise; + TStaticArray _2D_Gradient_Perturb_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 1337 + FVoxelGraphSeed Variable_18; // Seed = 1337 output 0 + Variable_18 = Params.Seed; + + // Init of HASH + FVoxelGraphSeed Variable_19; // HASH output 0 + Variable_19 = FVoxelUtilities::MurmurHash32(Variable_18); + + // Init of 2D IQ Noise + _2D_IQ_Noise_0_Noise.SetSeed(Variable_18); + _2D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_0_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_0_LODToOctaves[0] = 15; + _2D_IQ_Noise_0_LODToOctaves[1] = 15; + _2D_IQ_Noise_0_LODToOctaves[2] = 15; + _2D_IQ_Noise_0_LODToOctaves[3] = 15; + _2D_IQ_Noise_0_LODToOctaves[4] = 15; + _2D_IQ_Noise_0_LODToOctaves[5] = 15; + _2D_IQ_Noise_0_LODToOctaves[6] = 15; + _2D_IQ_Noise_0_LODToOctaves[7] = 15; + _2D_IQ_Noise_0_LODToOctaves[8] = 15; + _2D_IQ_Noise_0_LODToOctaves[9] = 15; + _2D_IQ_Noise_0_LODToOctaves[10] = 15; + _2D_IQ_Noise_0_LODToOctaves[11] = 15; + _2D_IQ_Noise_0_LODToOctaves[12] = 15; + _2D_IQ_Noise_0_LODToOctaves[13] = 15; + _2D_IQ_Noise_0_LODToOctaves[14] = 15; + _2D_IQ_Noise_0_LODToOctaves[15] = 15; + _2D_IQ_Noise_0_LODToOctaves[16] = 15; + _2D_IQ_Noise_0_LODToOctaves[17] = 15; + _2D_IQ_Noise_0_LODToOctaves[18] = 15; + _2D_IQ_Noise_0_LODToOctaves[19] = 15; + _2D_IQ_Noise_0_LODToOctaves[20] = 15; + _2D_IQ_Noise_0_LODToOctaves[21] = 15; + _2D_IQ_Noise_0_LODToOctaves[22] = 15; + _2D_IQ_Noise_0_LODToOctaves[23] = 15; + _2D_IQ_Noise_0_LODToOctaves[24] = 15; + _2D_IQ_Noise_0_LODToOctaves[25] = 15; + _2D_IQ_Noise_0_LODToOctaves[26] = 15; + _2D_IQ_Noise_0_LODToOctaves[27] = 15; + _2D_IQ_Noise_0_LODToOctaves[28] = 15; + _2D_IQ_Noise_0_LODToOctaves[29] = 15; + _2D_IQ_Noise_0_LODToOctaves[30] = 15; + _2D_IQ_Noise_0_LODToOctaves[31] = 15; + + // Init of 2D Gradient Perturb Fractal + _2D_Gradient_Perturb_Fractal_0_Noise.SetSeed(Variable_19); + _2D_Gradient_Perturb_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Gradient_Perturb_Fractal_0_Noise.SetFractalOctavesAndGain(7, 0.5); + _2D_Gradient_Perturb_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Gradient_Perturb_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[0] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[1] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[2] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[3] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[4] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[5] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[6] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[7] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[8] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[9] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[10] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[11] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[12] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[13] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[14] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[15] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[16] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[17] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[18] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[19] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[20] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[21] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[22] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[23] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[24] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[25] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[26] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[27] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[28] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[29] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[30] = 7; + _2D_Gradient_Perturb_Fractal_0_LODToOctaves[31] = 7; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_6 = Context.GetLocalX(); + + // X + BufferX.Variable_1 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_7; // Y output 0 + Variable_7 = Context.GetLocalY(); + + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // 2D IQ Noise + v_flt Variable_0; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_0 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_1, Variable_2, BufferConstant.Variable_22, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -0.631134, 0.652134); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.224071, 1.771704); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.220247, 1.219364); + + // 2D Gradient Perturb Fractal + v_flt Variable_13; // 2D Gradient Perturb Fractal output 0 + v_flt Variable_14; // 2D Gradient Perturb Fractal output 1 + Variable_13 = BufferX.Variable_6; + Variable_14 = Variable_7; + _2D_Gradient_Perturb_Fractal_0_Noise.GradientPerturbFractal_2D(Variable_13, Variable_14, BufferConstant.Variable_21, _2D_Gradient_Perturb_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], BufferConstant.Variable_20); + + // 2D Noise SDF.* + v_flt Variable_24; // 2D Noise SDF.* output 0 + Variable_24 = Variable_0 * BufferConstant.Variable_3; + + // Elongate.Clamp Vector.Clamp + v_flt Variable_28; // Elongate.Clamp Vector.Clamp output 0 + Variable_28 = FVoxelNodeFunctions::Clamp(Variable_13, v_flt(-1.0f), v_flt(1.0f)); + + // 2D Noise SDF.+ + BufferXY.Variable_25 = Variable_24 + v_flt(0.0f); + + // Elongate.Clamp Vector.Clamp + v_flt Variable_5; // Elongate.Clamp Vector.Clamp output 0 + Variable_5 = FVoxelNodeFunctions::Clamp(Variable_14, v_flt(-100.0f), v_flt(100.0f)); + + // Elongate.vector - vector.- + BufferXY.Variable_27 = Variable_14 - Variable_5; + + // Elongate.vector - vector.- + BufferXY.Variable_26 = Variable_13 - Variable_28; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_6 = Context.GetLocalX(); + + // Y + v_flt Variable_7; // Y output 0 + Variable_7 = Context.GetLocalY(); + + // X + BufferX.Variable_1 = Context.GetLocalX(); + + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // 2D IQ Noise + v_flt Variable_0; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_0 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_1, Variable_2, BufferConstant.Variable_22, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -0.631134, 0.652134); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.224071, 1.771704); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.220247, 1.219364); + + // 2D Gradient Perturb Fractal + v_flt Variable_13; // 2D Gradient Perturb Fractal output 0 + v_flt Variable_14; // 2D Gradient Perturb Fractal output 1 + Variable_13 = BufferX.Variable_6; + Variable_14 = Variable_7; + _2D_Gradient_Perturb_Fractal_0_Noise.GradientPerturbFractal_2D(Variable_13, Variable_14, BufferConstant.Variable_21, _2D_Gradient_Perturb_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], BufferConstant.Variable_20); + + // 2D Noise SDF.* + v_flt Variable_24; // 2D Noise SDF.* output 0 + Variable_24 = Variable_0 * BufferConstant.Variable_3; + + // Elongate.Clamp Vector.Clamp + v_flt Variable_28; // Elongate.Clamp Vector.Clamp output 0 + Variable_28 = FVoxelNodeFunctions::Clamp(Variable_13, v_flt(-1.0f), v_flt(1.0f)); + + // 2D Noise SDF.+ + BufferXY.Variable_25 = Variable_24 + v_flt(0.0f); + + // Elongate.Clamp Vector.Clamp + v_flt Variable_5; // Elongate.Clamp Vector.Clamp output 0 + Variable_5 = FVoxelNodeFunctions::Clamp(Variable_14, v_flt(-100.0f), v_flt(100.0f)); + + // Elongate.vector - vector.- + BufferXY.Variable_27 = Variable_14 - Variable_5; + + // Elongate.vector - vector.- + BufferXY.Variable_26 = Variable_13 - Variable_28; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // 2D Noise SDF.- + v_flt Variable_23; // 2D Noise SDF.- output 0 + Variable_23 = Variable_4 - BufferXY.Variable_25; + + // Round Cone SDF + v_flt Variable_11; // Round Cone SDF output 0 + Variable_11 = FVoxelSDFNodeFunctions::RoundCone(BufferXY.Variable_26, BufferXY.Variable_27, Variable_8, v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), BufferConstant.Variable_12, v_flt(150.0f), v_flt(50.0f)); + + // Shell.ABS + v_flt Variable_29; // Shell.ABS output 0 + Variable_29 = FVoxelNodeFunctions::Abs(Variable_11); + + // Shell.- + v_flt Variable_15; // Shell.- output 0 + Variable_15 = Variable_29 - v_flt(150.0f); + + // Shell.ABS + v_flt Variable_30; // Shell.ABS output 0 + Variable_30 = FVoxelNodeFunctions::Abs(Variable_15); + + // Shell.- + v_flt Variable_16; // Shell.- output 0 + Variable_16 = Variable_30 - v_flt(75.0f); + + // Shell.ABS + v_flt Variable_31; // Shell.ABS output 0 + Variable_31 = FVoxelNodeFunctions::Abs(Variable_16); + + // Shell.- + v_flt Variable_17; // Shell.- output 0 + Variable_17 = Variable_31 - v_flt(37.0f); + + // Smooth Intersection + v_flt Variable_10; // Smooth Intersection output 0 + Variable_10 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_23, Variable_17, v_flt(20.0f)); + + // Set High Quality Value.* + v_flt Variable_32; // Set High Quality Value.* output 0 + Variable_32 = Variable_10 * v_flt(0.2f); + + Outputs.Value = Variable_32; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // X + v_flt Variable_6; // X output 0 + Variable_6 = Context.GetLocalX(); + + // Z + v_flt Variable_4; // Z output 0 + Variable_4 = Context.GetLocalZ(); + + // Y + v_flt Variable_7; // Y output 0 + Variable_7 = Context.GetLocalY(); + + // X + v_flt Variable_1; // X output 0 + Variable_1 = Context.GetLocalX(); + + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // 2D IQ Noise + v_flt Variable_0; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_0 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(Variable_1, Variable_2, BufferConstant.Variable_22, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_0 = FMath::Clamp(Variable_0, -0.631134, 0.652134); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.224071, 1.771704); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.220247, 1.219364); + + // 2D Gradient Perturb Fractal + v_flt Variable_13; // 2D Gradient Perturb Fractal output 0 + v_flt Variable_14; // 2D Gradient Perturb Fractal output 1 + Variable_13 = Variable_6; + Variable_14 = Variable_7; + _2D_Gradient_Perturb_Fractal_0_Noise.GradientPerturbFractal_2D(Variable_13, Variable_14, BufferConstant.Variable_21, _2D_Gradient_Perturb_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)], BufferConstant.Variable_20); + + // 2D Noise SDF.* + v_flt Variable_24; // 2D Noise SDF.* output 0 + Variable_24 = Variable_0 * BufferConstant.Variable_3; + + // Elongate.Clamp Vector.Clamp + v_flt Variable_28; // Elongate.Clamp Vector.Clamp output 0 + Variable_28 = FVoxelNodeFunctions::Clamp(Variable_13, v_flt(-1.0f), v_flt(1.0f)); + + // 2D Noise SDF.+ + v_flt Variable_25; // 2D Noise SDF.+ output 0 + Variable_25 = Variable_24 + v_flt(0.0f); + + // Elongate.Clamp Vector.Clamp + v_flt Variable_5; // Elongate.Clamp Vector.Clamp output 0 + Variable_5 = FVoxelNodeFunctions::Clamp(Variable_14, v_flt(-100.0f), v_flt(100.0f)); + + // Elongate.vector - vector.- + v_flt Variable_27; // Elongate.vector - vector.- output 0 + Variable_27 = Variable_14 - Variable_5; + + // Elongate.vector - vector.- + v_flt Variable_26; // Elongate.vector - vector.- output 0 + Variable_26 = Variable_13 - Variable_28; + + // 2D Noise SDF.- + v_flt Variable_23; // 2D Noise SDF.- output 0 + Variable_23 = Variable_4 - Variable_25; + + // Round Cone SDF + v_flt Variable_11; // Round Cone SDF output 0 + Variable_11 = FVoxelSDFNodeFunctions::RoundCone(Variable_26, Variable_27, Variable_8, v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), v_flt(0.0f), BufferConstant.Variable_12, v_flt(150.0f), v_flt(50.0f)); + + // Shell.ABS + v_flt Variable_29; // Shell.ABS output 0 + Variable_29 = FVoxelNodeFunctions::Abs(Variable_11); + + // Shell.- + v_flt Variable_15; // Shell.- output 0 + Variable_15 = Variable_29 - v_flt(150.0f); + + // Shell.ABS + v_flt Variable_30; // Shell.ABS output 0 + Variable_30 = FVoxelNodeFunctions::Abs(Variable_15); + + // Shell.- + v_flt Variable_16; // Shell.- output 0 + Variable_16 = Variable_30 - v_flt(75.0f); + + // Shell.ABS + v_flt Variable_31; // Shell.ABS output 0 + Variable_31 = FVoxelNodeFunctions::Abs(Variable_16); + + // Shell.- + v_flt Variable_17; // Shell.- output 0 + Variable_17 = Variable_31 - v_flt(37.0f); + + // Smooth Intersection + v_flt Variable_10; // Smooth Intersection output 0 + Variable_10 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_23, Variable_17, v_flt(20.0f)); + + // Set High Quality Value.* + v_flt Variable_32; // Set High Quality Value.* output 0 + Variable_32 = Variable_10 * v_flt(0.2f); + + Outputs.Value = Variable_32; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_17; // Perturb Frequency = 0.01 output 0 + TVoxelRange Variable_16; // Perturb Amplitude = 50.0 output 0 + TVoxelRange Variable_10; // * -1 output 0 + TVoxelRange Variable_20; // 2D Noise SDF.+ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_4; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_21; // Elongate.vector - vector.- output 0 + TVoxelRange Variable_22; // Elongate.vector - vector.- output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D IQ Noise + _2D_IQ_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_1_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_1_LODToOctaves[0] = 15; + _2D_IQ_Noise_1_LODToOctaves[1] = 15; + _2D_IQ_Noise_1_LODToOctaves[2] = 15; + _2D_IQ_Noise_1_LODToOctaves[3] = 15; + _2D_IQ_Noise_1_LODToOctaves[4] = 15; + _2D_IQ_Noise_1_LODToOctaves[5] = 15; + _2D_IQ_Noise_1_LODToOctaves[6] = 15; + _2D_IQ_Noise_1_LODToOctaves[7] = 15; + _2D_IQ_Noise_1_LODToOctaves[8] = 15; + _2D_IQ_Noise_1_LODToOctaves[9] = 15; + _2D_IQ_Noise_1_LODToOctaves[10] = 15; + _2D_IQ_Noise_1_LODToOctaves[11] = 15; + _2D_IQ_Noise_1_LODToOctaves[12] = 15; + _2D_IQ_Noise_1_LODToOctaves[13] = 15; + _2D_IQ_Noise_1_LODToOctaves[14] = 15; + _2D_IQ_Noise_1_LODToOctaves[15] = 15; + _2D_IQ_Noise_1_LODToOctaves[16] = 15; + _2D_IQ_Noise_1_LODToOctaves[17] = 15; + _2D_IQ_Noise_1_LODToOctaves[18] = 15; + _2D_IQ_Noise_1_LODToOctaves[19] = 15; + _2D_IQ_Noise_1_LODToOctaves[20] = 15; + _2D_IQ_Noise_1_LODToOctaves[21] = 15; + _2D_IQ_Noise_1_LODToOctaves[22] = 15; + _2D_IQ_Noise_1_LODToOctaves[23] = 15; + _2D_IQ_Noise_1_LODToOctaves[24] = 15; + _2D_IQ_Noise_1_LODToOctaves[25] = 15; + _2D_IQ_Noise_1_LODToOctaves[26] = 15; + _2D_IQ_Noise_1_LODToOctaves[27] = 15; + _2D_IQ_Noise_1_LODToOctaves[28] = 15; + _2D_IQ_Noise_1_LODToOctaves[29] = 15; + _2D_IQ_Noise_1_LODToOctaves[30] = 15; + _2D_IQ_Noise_1_LODToOctaves[31] = 15; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Top Noise Height = 500.0 + TVoxelRange Variable_1; // Top Noise Height = 500.0 output 0 + Variable_1 = Params.Top_Noise_Height; + + // Height = 200.0 + TVoxelRange Variable_7; // Height = 200.0 output 0 + Variable_7 = Params.Height; + + // Perturb Frequency = 0.01 + BufferConstant.Variable_17 = Params.Perturb_Frequency; + + // Perturb Amplitude = 50.0 + BufferConstant.Variable_16 = Params.Perturb_Amplitude; + + // 2D IQ Noise + TVoxelRange Variable_0; // 2D IQ Noise output 0 + TVoxelRange _2D_IQ_Noise_1_Temp_1; // 2D IQ Noise output 1 + TVoxelRange _2D_IQ_Noise_1_Temp_2; // 2D IQ Noise output 2 + Variable_0 = { -0.631134f, 0.652134f }; + _2D_IQ_Noise_1_Temp_1 = { -1.224071f, 1.771704f }; + _2D_IQ_Noise_1_Temp_2 = { -1.220247f, 1.219364f }; + + // 2D Noise SDF.* + TVoxelRange Variable_19; // 2D Noise SDF.* output 0 + Variable_19 = Variable_0 * Variable_1; + + // * -1 + BufferConstant.Variable_10 = Variable_7 * -1; + + // 2D Noise SDF.+ + BufferConstant.Variable_20 = Variable_19 + TVoxelRange(0.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_IQ_Noise_1_Noise; + TStaticArray _2D_IQ_Noise_1_LODToOctaves; + FVoxelFastNoise _2D_Gradient_Perturb_Fractal_1_Noise; + TStaticArray _2D_Gradient_Perturb_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Gradient Perturb Fractal + _2D_Gradient_Perturb_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Gradient_Perturb_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Gradient_Perturb_Fractal_1_Noise.SetFractalOctavesAndGain(7, 0.5); + _2D_Gradient_Perturb_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Gradient_Perturb_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[0] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[1] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[2] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[3] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[4] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[5] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[6] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[7] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[8] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[9] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[10] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[11] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[12] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[13] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[14] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[15] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[16] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[17] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[18] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[19] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[20] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[21] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[22] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[23] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[24] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[25] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[26] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[27] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[28] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[29] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[30] = 7; + _2D_Gradient_Perturb_Fractal_1_LODToOctaves[31] = 7; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // X + TVoxelRange Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // Z + TVoxelRange Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_5; // Y output 0 + Variable_5 = Context.GetLocalY(); + + // 2D Gradient Perturb Fractal + TVoxelRange Variable_11; // 2D Gradient Perturb Fractal output 0 + TVoxelRange Variable_12; // 2D Gradient Perturb Fractal output 1 + Variable_11 = TVoxelRange::FromList(Variable_4.Min - 2 * BufferConstant.Variable_16.Max, Variable_4.Max + 2 * BufferConstant.Variable_16.Max); + Variable_12 = TVoxelRange::FromList(Variable_5.Min - 2 * BufferConstant.Variable_16.Max, Variable_5.Max + 2 * BufferConstant.Variable_16.Max); + + // 2D Noise SDF.- + TVoxelRange Variable_18; // 2D Noise SDF.- output 0 + Variable_18 = Variable_2 - BufferConstant.Variable_20; + + // Elongate.Clamp Vector.Clamp + TVoxelRange Variable_23; // Elongate.Clamp Vector.Clamp output 0 + Variable_23 = FVoxelNodeFunctions::Clamp(Variable_11, TVoxelRange(-1.0f), TVoxelRange(1.0f)); + + // Elongate.Clamp Vector.Clamp + TVoxelRange Variable_3; // Elongate.Clamp Vector.Clamp output 0 + Variable_3 = FVoxelNodeFunctions::Clamp(Variable_12, TVoxelRange(-100.0f), TVoxelRange(100.0f)); + + // Elongate.vector - vector.- + TVoxelRange Variable_21; // Elongate.vector - vector.- output 0 + Variable_21 = Variable_11 - Variable_23; + + // Elongate.vector - vector.- + TVoxelRange Variable_22; // Elongate.vector - vector.- output 0 + Variable_22 = Variable_12 - Variable_3; + + // Round Cone SDF + TVoxelRange Variable_9; // Round Cone SDF output 0 + Variable_9 = FVoxelSDFNodeFunctions::RoundCone(Variable_21, Variable_22, Variable_6, TVoxelRange(0.0f), TVoxelRange(0.0f), TVoxelRange(0.0f), TVoxelRange(0.0f), TVoxelRange(0.0f), BufferConstant.Variable_10, TVoxelRange(150.0f), TVoxelRange(50.0f)); + + // Shell.ABS + TVoxelRange Variable_24; // Shell.ABS output 0 + Variable_24 = FVoxelNodeFunctions::Abs(Variable_9); + + // Shell.- + TVoxelRange Variable_13; // Shell.- output 0 + Variable_13 = Variable_24 - TVoxelRange(150.0f); + + // Shell.ABS + TVoxelRange Variable_25; // Shell.ABS output 0 + Variable_25 = FVoxelNodeFunctions::Abs(Variable_13); + + // Shell.- + TVoxelRange Variable_14; // Shell.- output 0 + Variable_14 = Variable_25 - TVoxelRange(75.0f); + + // Shell.ABS + TVoxelRange Variable_26; // Shell.ABS output 0 + Variable_26 = FVoxelNodeFunctions::Abs(Variable_14); + + // Shell.- + TVoxelRange Variable_15; // Shell.- output 0 + Variable_15 = Variable_26 - TVoxelRange(37.0f); + + // Smooth Intersection + TVoxelRange Variable_8; // Smooth Intersection output 0 + Variable_8 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_18, Variable_15, TVoxelRange(20.0f)); + + // Set High Quality Value.* + TVoxelRange Variable_27; // Set High Quality Value.* output 0 + Variable_27 = Variable_8 * TVoxelRange(0.2f); + + Outputs.Value = Variable_27; + } + + }; + + FVoxelExample_FloatingIslandOnionInstance(UVoxelExample_FloatingIslandOnion& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Height, + Object.Perturb_Amplitude, + Object.Perturb_Frequency, + Object.Seed, + Object.Top_Noise_Frequency, + Object.Top_Noise_Height + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_FloatingIslandOnionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_FloatingIslandOnionInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_FloatingIslandOnionInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_FloatingIslandOnionInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_FloatingIslandOnionInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_FloatingIslandOnion::UVoxelExample_FloatingIslandOnion() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_FloatingIslandOnion::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_FloatingIslandOnion. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_FloatingIslandOnion. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_FloatingIslandOnion. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_FloatingIslandOnion. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.h new file mode 100644 index 00000000..3e4d863c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_FloatingIslandOnion.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_FloatingIslandOnion : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Height")) + float Height = 200.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Perturb Amplitude")) + float Perturb_Amplitude = 50.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Perturb Frequency")) + float Perturb_Frequency = 0.01; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 1337; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Top Noise Frequency")) + float Top_Noise_Frequency = 0.002; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Top Noise Height")) + float Top_Noise_Height = 500.0; + + UVoxelExample_FloatingIslandOnion(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.cpp new file mode 100644 index 00000000..2dc68d5d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.cpp @@ -0,0 +1,1879 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_HeightmapComposition.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_HeightmapCompositionInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Depth; + const bool Flip_X; + const bool Flip_Y; + const TVoxelHeightmapAssetSamplerWrapper heightmap_x0_y0; + const TVoxelHeightmapAssetSamplerWrapper heightmap_x0_y1; + const TVoxelHeightmapAssetSamplerWrapper heightmap_x1_y0; + const TVoxelHeightmapAssetSamplerWrapper heightmap_x1_y1; + const float Size_X; + const float Size_Y; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_6; // Size Y = 512.0 output 0 + bool Variable_11; // Flip X = False output 0 + bool Variable_12; // Flip Y = True output 0 + v_flt Variable_5; // Size X = 512.0 output 0 + v_flt Variable_26; // Depth = 300.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_13; // Switch (float) output 0 + bool Variable_7; // < output 0 + v_flt Variable_9; // + output 0 + v_flt Variable_21; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + bool Variable_8; // < output 0 + v_flt Variable_0; // Heightmap: heightmap x0 y0 output 0 + v_flt Variable_1; // Heightmap: heightmap x0 y1 output 0 + v_flt Variable_2; // Heightmap: heightmap x1 y0 output 0 + v_flt Variable_3; // Heightmap: heightmap x1 y1 output 0 + v_flt Variable_20; // Box SDF output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + Function1_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Size Y = 512.0 + BufferConstant.Variable_6 = Params.Size_Y; + + // Flip X = False + BufferConstant.Variable_11 = Params.Flip_X; + + // Flip Y = True + BufferConstant.Variable_12 = Params.Flip_Y; + + // Size X = 512.0 + BufferConstant.Variable_5 = Params.Size_X; + + // Depth = 300.0 + BufferConstant.Variable_26 = Params.Depth; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + void Function1_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // Switch (float) + BufferX.Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // < + BufferX.Variable_7 = BufferX.Variable_13 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + // + + BufferX.Variable_9 = BufferX.Variable_13 + BufferConstant.Variable_5; + + } + else + { + } + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + BufferXY.Variable_8 = Variable_15 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + FVoxelMaterial Heightmap__heightmap_x0_y0_0_Temp_1; // Heightmap: heightmap x0 y0 output 1 + v_flt Heightmap__heightmap_x0_y0_0_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_0_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_0_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_0_Temp_5; // Heightmap: heightmap x0 y0 output 5 + BufferXY.Variable_0 = Params.heightmap_x0_y0.GetHeight(BufferX.Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_0, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x0 y1 + FVoxelMaterial Heightmap__heightmap_x0_y1_0_Temp_1; // Heightmap: heightmap x0 y1 output 1 + v_flt Heightmap__heightmap_x0_y1_0_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_0_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_0_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_0_Temp_5; // Heightmap: heightmap x0 y1 output 5 + BufferXY.Variable_1 = Params.heightmap_x0_y1.GetHeight(BufferX.Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_1, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + else + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + FVoxelMaterial Heightmap__heightmap_x1_y0_0_Temp_1; // Heightmap: heightmap x1 y0 output 1 + v_flt Heightmap__heightmap_x1_y0_0_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_0_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_0_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_0_Temp_5; // Heightmap: heightmap x1 y0 output 5 + BufferXY.Variable_2 = Params.heightmap_x1_y0.GetHeight(BufferX.Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_2, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x1 y1 + FVoxelMaterial Heightmap__heightmap_x1_y1_0_Temp_1; // Heightmap: heightmap x1 y1 output 1 + v_flt Heightmap__heightmap_x1_y1_0_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_0_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_0_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_0_Temp_5; // Heightmap: heightmap x1 y1 output 5 + BufferXY.Variable_3 = Params.heightmap_x1_y1.GetHeight(BufferX.Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_3, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + BufferX.Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + BufferXY.Variable_8 = Variable_15 < v_flt(0.0f); + + // < + BufferX.Variable_7 = BufferX.Variable_13 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + // + + BufferX.Variable_9 = BufferX.Variable_13 + BufferConstant.Variable_5; + + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + FVoxelMaterial Heightmap__heightmap_x0_y0_0_Temp_1; // Heightmap: heightmap x0 y0 output 1 + v_flt Heightmap__heightmap_x0_y0_0_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_0_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_0_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_0_Temp_5; // Heightmap: heightmap x0 y0 output 5 + BufferXY.Variable_0 = Params.heightmap_x0_y0.GetHeight(BufferX.Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_0, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x0 y1 + FVoxelMaterial Heightmap__heightmap_x0_y1_0_Temp_1; // Heightmap: heightmap x0 y1 output 1 + v_flt Heightmap__heightmap_x0_y1_0_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_0_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_0_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_0_Temp_5; // Heightmap: heightmap x0 y1 output 5 + BufferXY.Variable_1 = Params.heightmap_x0_y1.GetHeight(BufferX.Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_1, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + else + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + FVoxelMaterial Heightmap__heightmap_x1_y0_0_Temp_1; // Heightmap: heightmap x1 y0 output 1 + v_flt Heightmap__heightmap_x1_y0_0_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_0_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_0_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_0_Temp_5; // Heightmap: heightmap x1 y0 output 5 + BufferXY.Variable_2 = Params.heightmap_x1_y0.GetHeight(BufferX.Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_2, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x1 y1 + FVoxelMaterial Heightmap__heightmap_x1_y1_0_Temp_1; // Heightmap: heightmap x1 y1 output 1 + v_flt Heightmap__heightmap_x1_y1_0_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_0_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_0_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_0_Temp_5; // Heightmap: heightmap x1 y1 output 5 + BufferXY.Variable_3 = Params.heightmap_x1_y1.GetHeight(BufferX.Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_3, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + if (BufferX.Variable_7) + { + if (BufferXY.Variable_8) + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_0, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_1, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + else + { + if (BufferXY.Variable_8) + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_2, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_3, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + v_flt Variable_13; // Switch (float) output 0 + Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + bool Variable_8; // < output 0 + Variable_8 = Variable_15 < v_flt(0.0f); + + // < + bool Variable_7; // < output 0 + Variable_7 = Variable_13 < v_flt(0.0f); + + if (Variable_7) + { + // + + v_flt Variable_9; // + output 0 + Variable_9 = Variable_13 + BufferConstant.Variable_5; + + if (Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + v_flt Variable_0; // Heightmap: heightmap x0 y0 output 0 + FVoxelMaterial Heightmap__heightmap_x0_y0_0_Temp_1; // Heightmap: heightmap x0 y0 output 1 + v_flt Heightmap__heightmap_x0_y0_0_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_0_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_0_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_0_Temp_5; // Heightmap: heightmap x0 y0 output 5 + Variable_0 = Params.heightmap_x0_y0.GetHeight(Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_0, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x0 y1 + v_flt Variable_1; // Heightmap: heightmap x0 y1 output 0 + FVoxelMaterial Heightmap__heightmap_x0_y1_0_Temp_1; // Heightmap: heightmap x0 y1 output 1 + v_flt Heightmap__heightmap_x0_y1_0_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_0_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_0_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_0_Temp_5; // Heightmap: heightmap x0 y1 output 5 + Variable_1 = Params.heightmap_x0_y1.GetHeight(Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_1, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + else + { + if (Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + v_flt Variable_2; // Heightmap: heightmap x1 y0 output 0 + FVoxelMaterial Heightmap__heightmap_x1_y0_0_Temp_1; // Heightmap: heightmap x1 y0 output 1 + v_flt Heightmap__heightmap_x1_y0_0_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_0_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_0_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_0_Temp_5; // Heightmap: heightmap x1 y0 output 5 + Variable_2 = Params.heightmap_x1_y0.GetHeight(Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_2, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x1 y1 + v_flt Variable_3; // Heightmap: heightmap x1 y1 output 0 + FVoxelMaterial Heightmap__heightmap_x1_y1_0_Temp_1; // Heightmap: heightmap x1 y1 output 1 + v_flt Heightmap__heightmap_x1_y1_0_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_0_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_0_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_0_Temp_5; // Heightmap: heightmap x1 y1 output 5 + Variable_3 = Params.heightmap_x1_y1.GetHeight(Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_3, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + + void Function1_X_Compute(const FVoxelContext& Context, FBufferX& BufferX, v_flt Variable_29, v_flt Variable_30, v_flt Variable_31) const + { + // X + BufferX.Variable_21 = Context.GetLocalX(); + + } + + void Function1_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY, v_flt Variable_29, v_flt Variable_30, v_flt Variable_31) const + { + // Y + v_flt Variable_22; // Y output 0 + Variable_22 = Context.GetLocalY(); + + // Box SDF + BufferXY.Variable_20 = FVoxelSDFNodeFunctions::Box(BufferX.Variable_21, Variable_22, v_flt(0.0f), Variable_30, Variable_31, v_flt(1000000000.0f)); + + } + + void Function1_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY, v_flt Variable_29, v_flt Variable_30, v_flt Variable_31) const + { + // X + BufferX.Variable_21 = Context.GetLocalX(); + + // Y + v_flt Variable_22; // Y output 0 + Variable_22 = Context.GetLocalY(); + + // Box SDF + BufferXY.Variable_20 = FVoxelSDFNodeFunctions::Box(BufferX.Variable_21, Variable_22, v_flt(0.0f), Variable_30, Variable_31, v_flt(1000000000.0f)); + + } + + void Function1_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs, v_flt Variable_29, v_flt Variable_30, v_flt Variable_31) const + { + // Z + v_flt Variable_18; // Z output 0 + Variable_18 = Context.GetLocalZ(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // + + v_flt Variable_25; // + output 0 + Variable_25 = Variable_24 + BufferConstant.Variable_26; + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_18 - Variable_29; + + // * -1 + v_flt Variable_27; // * -1 output 0 + Variable_27 = Variable_25 * -1; + + // Max (float) + v_flt Variable_23; // Max (float) output 0 + Variable_23 = FVoxelNodeFunctions::Max(Variable_19, FVoxelNodeFunctions::Max(BufferXY.Variable_20, Variable_27)); + + // Set High Quality Value.* + v_flt Variable_28; // Set High Quality Value.* output 0 + Variable_28 = Variable_23 * v_flt(0.2f); + + Outputs.Value = Variable_28; + } + + void Function1_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs, v_flt Variable_29, v_flt Variable_30, v_flt Variable_31) const + { + // X + v_flt Variable_21; // X output 0 + Variable_21 = Context.GetLocalX(); + + // Y + v_flt Variable_22; // Y output 0 + Variable_22 = Context.GetLocalY(); + + // Z + v_flt Variable_18; // Z output 0 + Variable_18 = Context.GetLocalZ(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // + + v_flt Variable_25; // + output 0 + Variable_25 = Variable_24 + BufferConstant.Variable_26; + + // Box SDF + v_flt Variable_20; // Box SDF output 0 + Variable_20 = FVoxelSDFNodeFunctions::Box(Variable_21, Variable_22, v_flt(0.0f), Variable_30, Variable_31, v_flt(1000000000.0f)); + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_18 - Variable_29; + + // * -1 + v_flt Variable_27; // * -1 output 0 + Variable_27 = Variable_25 * -1; + + // Max (float) + v_flt Variable_23; // Max (float) output 0 + Variable_23 = FVoxelNodeFunctions::Max(Variable_19, FVoxelNodeFunctions::Max(Variable_20, Variable_27)); + + // Set High Quality Value.* + v_flt Variable_28; // Set High Quality Value.* output 0 + Variable_28 = Variable_23 * v_flt(0.2f); + + Outputs.Value = Variable_28; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_6; // Size Y = 512.0 output 0 + bool Variable_11; // Flip X = False output 0 + bool Variable_12; // Flip Y = True output 0 + v_flt Variable_5; // Size X = 512.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_13; // Switch (float) output 0 + bool Variable_7; // < output 0 + v_flt Variable_9; // + output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + bool Variable_8; // < output 0 + FVoxelMaterial Variable_0; // Heightmap: heightmap x0 y0 output 1 + FVoxelMaterial Variable_1; // Heightmap: heightmap x0 y1 output 1 + FVoxelMaterial Variable_2; // Heightmap: heightmap x1 y0 output 1 + FVoxelMaterial Variable_3; // Heightmap: heightmap x1 y1 output 1 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + Function1_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Size Y = 512.0 + BufferConstant.Variable_6 = Params.Size_Y; + + // Flip X = False + BufferConstant.Variable_11 = Params.Flip_X; + + // Flip Y = True + BufferConstant.Variable_12 = Params.Flip_Y; + + // Size X = 512.0 + BufferConstant.Variable_5 = Params.Size_X; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + void Function1_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // Switch (float) + BufferX.Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // < + BufferX.Variable_7 = BufferX.Variable_13 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + // + + BufferX.Variable_9 = BufferX.Variable_13 + BufferConstant.Variable_5; + + } + else + { + } + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + BufferXY.Variable_8 = Variable_15 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + v_flt Heightmap__heightmap_x0_y0_1_Temp_0; // Heightmap: heightmap x0 y0 output 0 + v_flt Heightmap__heightmap_x0_y0_1_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_1_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_1_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_1_Temp_5; // Heightmap: heightmap x0 y0 output 5 + BufferXY.Variable_0 = Params.heightmap_x0_y0.GetMaterial(BufferX.Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_0); + } + else + { + // Heightmap: heightmap x0 y1 + v_flt Heightmap__heightmap_x0_y1_1_Temp_0; // Heightmap: heightmap x0 y1 output 0 + v_flt Heightmap__heightmap_x0_y1_1_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_1_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_1_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_1_Temp_5; // Heightmap: heightmap x0 y1 output 5 + BufferXY.Variable_1 = Params.heightmap_x0_y1.GetMaterial(BufferX.Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_1); + } + } + else + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + v_flt Heightmap__heightmap_x1_y0_1_Temp_0; // Heightmap: heightmap x1 y0 output 0 + v_flt Heightmap__heightmap_x1_y0_1_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_1_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_1_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_1_Temp_5; // Heightmap: heightmap x1 y0 output 5 + BufferXY.Variable_2 = Params.heightmap_x1_y0.GetMaterial(BufferX.Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_2); + } + else + { + // Heightmap: heightmap x1 y1 + v_flt Heightmap__heightmap_x1_y1_1_Temp_0; // Heightmap: heightmap x1 y1 output 0 + v_flt Heightmap__heightmap_x1_y1_1_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_1_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_1_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_1_Temp_5; // Heightmap: heightmap x1 y1 output 5 + BufferXY.Variable_3 = Params.heightmap_x1_y1.GetMaterial(BufferX.Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_3); + } + } + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + BufferX.Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + BufferXY.Variable_8 = Variable_15 < v_flt(0.0f); + + // < + BufferX.Variable_7 = BufferX.Variable_13 < v_flt(0.0f); + + if (BufferX.Variable_7) + { + // + + BufferX.Variable_9 = BufferX.Variable_13 + BufferConstant.Variable_5; + + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + v_flt Heightmap__heightmap_x0_y0_1_Temp_0; // Heightmap: heightmap x0 y0 output 0 + v_flt Heightmap__heightmap_x0_y0_1_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_1_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_1_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_1_Temp_5; // Heightmap: heightmap x0 y0 output 5 + BufferXY.Variable_0 = Params.heightmap_x0_y0.GetMaterial(BufferX.Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_0); + } + else + { + // Heightmap: heightmap x0 y1 + v_flt Heightmap__heightmap_x0_y1_1_Temp_0; // Heightmap: heightmap x0 y1 output 0 + v_flt Heightmap__heightmap_x0_y1_1_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_1_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_1_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_1_Temp_5; // Heightmap: heightmap x0 y1 output 5 + BufferXY.Variable_1 = Params.heightmap_x0_y1.GetMaterial(BufferX.Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_1); + } + } + else + { + if (BufferXY.Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + v_flt Heightmap__heightmap_x1_y0_1_Temp_0; // Heightmap: heightmap x1 y0 output 0 + v_flt Heightmap__heightmap_x1_y0_1_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_1_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_1_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_1_Temp_5; // Heightmap: heightmap x1 y0 output 5 + BufferXY.Variable_2 = Params.heightmap_x1_y0.GetMaterial(BufferX.Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_2); + } + else + { + // Heightmap: heightmap x1 y1 + v_flt Heightmap__heightmap_x1_y1_1_Temp_0; // Heightmap: heightmap x1 y1 output 0 + v_flt Heightmap__heightmap_x1_y1_1_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_1_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_1_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_1_Temp_5; // Heightmap: heightmap x1 y1 output 5 + BufferXY.Variable_3 = Params.heightmap_x1_y1.GetMaterial(BufferX.Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYWithoutCache_Compute(Context, BufferX, BufferXY, BufferXY.Variable_3); + } + } + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + if (BufferX.Variable_7) + { + if (BufferXY.Variable_8) + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_0); + } + else + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_1); + } + } + else + { + if (BufferXY.Variable_8) + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_2); + } + else + { + Function1_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs, BufferXY.Variable_3); + } + } + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // X + v_flt Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + v_flt Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // * -1 + v_flt Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + v_flt Variable_13; // Switch (float) output 0 + Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // Switch (float) + v_flt Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + bool Variable_8; // < output 0 + Variable_8 = Variable_15 < v_flt(0.0f); + + // < + bool Variable_7; // < output 0 + Variable_7 = Variable_13 < v_flt(0.0f); + + if (Variable_7) + { + // + + v_flt Variable_9; // + output 0 + Variable_9 = Variable_13 + BufferConstant.Variable_5; + + if (Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + v_flt Heightmap__heightmap_x0_y0_1_Temp_0; // Heightmap: heightmap x0 y0 output 0 + FVoxelMaterial Variable_0; // Heightmap: heightmap x0 y0 output 1 + v_flt Heightmap__heightmap_x0_y0_1_Temp_2; // Heightmap: heightmap x0 y0 output 2 + v_flt Heightmap__heightmap_x0_y0_1_Temp_3; // Heightmap: heightmap x0 y0 output 3 + v_flt Heightmap__heightmap_x0_y0_1_Temp_4; // Heightmap: heightmap x0 y0 output 4 + v_flt Heightmap__heightmap_x0_y0_1_Temp_5; // Heightmap: heightmap x0 y0 output 5 + Variable_0 = Params.heightmap_x0_y0.GetMaterial(Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_0); + } + else + { + // Heightmap: heightmap x0 y1 + v_flt Heightmap__heightmap_x0_y1_1_Temp_0; // Heightmap: heightmap x0 y1 output 0 + FVoxelMaterial Variable_1; // Heightmap: heightmap x0 y1 output 1 + v_flt Heightmap__heightmap_x0_y1_1_Temp_2; // Heightmap: heightmap x0 y1 output 2 + v_flt Heightmap__heightmap_x0_y1_1_Temp_3; // Heightmap: heightmap x0 y1 output 3 + v_flt Heightmap__heightmap_x0_y1_1_Temp_4; // Heightmap: heightmap x0 y1 output 4 + v_flt Heightmap__heightmap_x0_y1_1_Temp_5; // Heightmap: heightmap x0 y1 output 5 + Variable_1 = Params.heightmap_x0_y1.GetMaterial(Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_1); + } + } + else + { + if (Variable_8) + { + // + + v_flt Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + v_flt Heightmap__heightmap_x1_y0_1_Temp_0; // Heightmap: heightmap x1 y0 output 0 + FVoxelMaterial Variable_2; // Heightmap: heightmap x1 y0 output 1 + v_flt Heightmap__heightmap_x1_y0_1_Temp_2; // Heightmap: heightmap x1 y0 output 2 + v_flt Heightmap__heightmap_x1_y0_1_Temp_3; // Heightmap: heightmap x1 y0 output 3 + v_flt Heightmap__heightmap_x1_y0_1_Temp_4; // Heightmap: heightmap x1 y0 output 4 + v_flt Heightmap__heightmap_x1_y0_1_Temp_5; // Heightmap: heightmap x1 y0 output 5 + Variable_2 = Params.heightmap_x1_y0.GetMaterial(Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_2); + } + else + { + // Heightmap: heightmap x1 y1 + v_flt Heightmap__heightmap_x1_y1_1_Temp_0; // Heightmap: heightmap x1 y1 output 0 + FVoxelMaterial Variable_3; // Heightmap: heightmap x1 y1 output 1 + v_flt Heightmap__heightmap_x1_y1_1_Temp_2; // Heightmap: heightmap x1 y1 output 2 + v_flt Heightmap__heightmap_x1_y1_1_Temp_3; // Heightmap: heightmap x1 y1 output 3 + v_flt Heightmap__heightmap_x1_y1_1_Temp_4; // Heightmap: heightmap x1 y1 output 4 + v_flt Heightmap__heightmap_x1_y1_1_Temp_5; // Heightmap: heightmap x1 y1 output 5 + Variable_3 = Params.heightmap_x1_y1.GetMaterial(Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_3); + } + } + } + + void Function1_X_Compute(const FVoxelContext& Context, FBufferX& BufferX, FVoxelMaterial Variable_18) const + { + } + + void Function1_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY, FVoxelMaterial Variable_18) const + { + } + + void Function1_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY, FVoxelMaterial Variable_18) const + { + } + + void Function1_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs, FVoxelMaterial Variable_18) const + { + Outputs.MaterialBuilder = Variable_18; + } + + void Function1_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs, FVoxelMaterial Variable_18) const + { + Outputs.MaterialBuilder = Variable_18; + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_6; // Size Y = 512.0 output 0 + FVoxelBoolRange Variable_11; // Flip X = False output 0 + FVoxelBoolRange Variable_12; // Flip Y = True output 0 + TVoxelRange Variable_5; // Size X = 512.0 output 0 + TVoxelRange Variable_31; // Depth = 300.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_13; // Switch (float) output 0 + FVoxelBoolRange Variable_7; // < output 0 + FVoxelBoolRange Variable_18; // 4x Flow Merge.Is Single bool output 0 + TVoxelRange Variable_26; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + FVoxelBoolRange Variable_8; // < output 0 + FVoxelBoolRange Variable_19; // 4x Flow Merge.Is Single bool output 0 + TVoxelRange Variable_0; // Heightmap: heightmap x0 y0 output 0 + TVoxelRange Variable_1; // Heightmap: heightmap x0 y1 output 0 + TVoxelRange Variable_2; // Heightmap: heightmap x1 y0 output 0 + TVoxelRange Variable_3; // Heightmap: heightmap x1 y1 output 0 + TVoxelRange Variable_21; // 4x Flow Merge.Range Union output 0 + TVoxelRange Variable_22; // 4x Flow Merge.Range Union output 0 + TVoxelRange Variable_20; // 4x Flow Merge.Range Union output 0 + TVoxelRange Variable_25; // Box SDF output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + Function1_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Size Y = 512.0 + BufferConstant.Variable_6 = Params.Size_Y; + + // Flip X = False + BufferConstant.Variable_11 = Params.Flip_X; + + // Flip Y = True + BufferConstant.Variable_12 = Params.Flip_Y; + + // Size X = 512.0 + BufferConstant.Variable_5 = Params.Size_X; + + // Depth = 300.0 + BufferConstant.Variable_31 = Params.Depth; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + void Function1_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Y + TVoxelRange Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // X + TVoxelRange Variable_4; // X output 0 + Variable_4 = Context.GetLocalX(); + + // * -1 + TVoxelRange Variable_14; // * -1 output 0 + Variable_14 = Variable_4 * -1; + + // * -1 + TVoxelRange Variable_17; // * -1 output 0 + Variable_17 = Variable_16 * -1; + + // Switch (float) + TVoxelRange Variable_13; // Switch (float) output 0 + Variable_13 = FVoxelNodeFunctions::Switch(Variable_14, Variable_4, BufferConstant.Variable_11); + + // Switch (float) + TVoxelRange Variable_15; // Switch (float) output 0 + Variable_15 = FVoxelNodeFunctions::Switch(Variable_17, Variable_16, BufferConstant.Variable_12); + + // < + FVoxelBoolRange Variable_7; // < output 0 + Variable_7 = Variable_13 < TVoxelRange(0.0f); + + // 4x Flow Merge.Is Single bool + FVoxelBoolRange Variable_18; // 4x Flow Merge.Is Single bool output 0 + Variable_18 = FVoxelNodeFunctions::IsSingleBool(Variable_7); + + if (Variable_18) + { + // < + FVoxelBoolRange Variable_8; // < output 0 + Variable_8 = Variable_15 < TVoxelRange(0.0f); + + // 4x Flow Merge.Is Single bool + FVoxelBoolRange Variable_19; // 4x Flow Merge.Is Single bool output 0 + Variable_19 = FVoxelNodeFunctions::IsSingleBool(Variable_8); + + if (Variable_19) + { + if (Variable_7) + { + // + + TVoxelRange Variable_9; // + output 0 + Variable_9 = Variable_13 + BufferConstant.Variable_5; + + if (Variable_8) + { + // + + TVoxelRange Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x0 y0 + TVoxelRange Variable_0; // Heightmap: heightmap x0 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y0_2_Temp_1; // Heightmap: heightmap x0 y0 output 1 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_2; // Heightmap: heightmap x0 y0 output 2 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_3; // Heightmap: heightmap x0 y0 output 3 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_4; // Heightmap: heightmap x0 y0 output 4 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_5; // Heightmap: heightmap x0 y0 output 5 + Variable_0 = Params.heightmap_x0_y0.GetHeightRange(Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_0, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x0 y1 + TVoxelRange Variable_1; // Heightmap: heightmap x0 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y1_2_Temp_1; // Heightmap: heightmap x0 y1 output 1 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_2; // Heightmap: heightmap x0 y1 output 2 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_3; // Heightmap: heightmap x0 y1 output 3 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_4; // Heightmap: heightmap x0 y1 output 4 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_5; // Heightmap: heightmap x0 y1 output 5 + Variable_1 = Params.heightmap_x0_y1.GetHeightRange(Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_1, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + else + { + if (Variable_8) + { + // + + TVoxelRange Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // Heightmap: heightmap x1 y0 + TVoxelRange Variable_2; // Heightmap: heightmap x1 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y0_2_Temp_1; // Heightmap: heightmap x1 y0 output 1 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_2; // Heightmap: heightmap x1 y0 output 2 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_3; // Heightmap: heightmap x1 y0 output 3 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_4; // Heightmap: heightmap x1 y0 output 4 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_5; // Heightmap: heightmap x1 y0 output 5 + Variable_2 = Params.heightmap_x1_y0.GetHeightRange(Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_2, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x1 y1 + TVoxelRange Variable_3; // Heightmap: heightmap x1 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y1_2_Temp_1; // Heightmap: heightmap x1 y1 output 1 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_2; // Heightmap: heightmap x1 y1 output 2 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_3; // Heightmap: heightmap x1 y1 output 3 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_4; // Heightmap: heightmap x1 y1 output 4 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_5; // Heightmap: heightmap x1 y1 output 5 + Variable_3 = Params.heightmap_x1_y1.GetHeightRange(Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_3, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + else + { + // + + TVoxelRange Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + if (Variable_7) + { + // + + TVoxelRange Variable_9; // + output 0 + Variable_9 = Variable_13 + BufferConstant.Variable_5; + + // Heightmap: heightmap x0 y0 + TVoxelRange Variable_0; // Heightmap: heightmap x0 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y0_2_Temp_1; // Heightmap: heightmap x0 y0 output 1 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_2; // Heightmap: heightmap x0 y0 output 2 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_3; // Heightmap: heightmap x0 y0 output 3 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_4; // Heightmap: heightmap x0 y0 output 4 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_5; // Heightmap: heightmap x0 y0 output 5 + Variable_0 = Params.heightmap_x0_y0.GetHeightRange(Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + // Heightmap: heightmap x0 y1 + TVoxelRange Variable_1; // Heightmap: heightmap x0 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y1_2_Temp_1; // Heightmap: heightmap x0 y1 output 1 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_2; // Heightmap: heightmap x0 y1 output 2 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_3; // Heightmap: heightmap x0 y1 output 3 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_4; // Heightmap: heightmap x0 y1 output 4 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_5; // Heightmap: heightmap x0 y1 output 5 + Variable_1 = Params.heightmap_x0_y1.GetHeightRange(Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + // 4x Flow Merge.Range Union + TVoxelRange Variable_21; // 4x Flow Merge.Range Union output 0 + Variable_21 = FVoxelNodeFunctions::Union(Variable_0, Variable_1); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_21, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + else + { + // Heightmap: heightmap x1 y0 + TVoxelRange Variable_2; // Heightmap: heightmap x1 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y0_2_Temp_1; // Heightmap: heightmap x1 y0 output 1 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_2; // Heightmap: heightmap x1 y0 output 2 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_3; // Heightmap: heightmap x1 y0 output 3 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_4; // Heightmap: heightmap x1 y0 output 4 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_5; // Heightmap: heightmap x1 y0 output 5 + Variable_2 = Params.heightmap_x1_y0.GetHeightRange(Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + // Heightmap: heightmap x1 y1 + TVoxelRange Variable_3; // Heightmap: heightmap x1 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y1_2_Temp_1; // Heightmap: heightmap x1 y1 output 1 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_2; // Heightmap: heightmap x1 y1 output 2 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_3; // Heightmap: heightmap x1 y1 output 3 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_4; // Heightmap: heightmap x1 y1 output 4 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_5; // Heightmap: heightmap x1 y1 output 5 + Variable_3 = Params.heightmap_x1_y1.GetHeightRange(Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + // 4x Flow Merge.Range Union + TVoxelRange Variable_22; // 4x Flow Merge.Range Union output 0 + Variable_22 = FVoxelNodeFunctions::Union(Variable_2, Variable_3); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_22, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + } + else + { + // + + TVoxelRange Variable_10; // + output 0 + Variable_10 = Variable_15 + BufferConstant.Variable_6; + + // + + TVoxelRange Variable_9; // + output 0 + Variable_9 = Variable_13 + BufferConstant.Variable_5; + + // Heightmap: heightmap x1 y1 + TVoxelRange Variable_3; // Heightmap: heightmap x1 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y1_2_Temp_1; // Heightmap: heightmap x1 y1 output 1 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_2; // Heightmap: heightmap x1 y1 output 2 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_3; // Heightmap: heightmap x1 y1 output 3 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_4; // Heightmap: heightmap x1 y1 output 4 + TVoxelRange Heightmap__heightmap_x1_y1_2_Temp_5; // Heightmap: heightmap x1 y1 output 5 + Variable_3 = Params.heightmap_x1_y1.GetHeightRange(Variable_13, Variable_15, EVoxelSamplerMode::Clamp); + + // Heightmap: heightmap x0 y1 + TVoxelRange Variable_1; // Heightmap: heightmap x0 y1 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y1_2_Temp_1; // Heightmap: heightmap x0 y1 output 1 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_2; // Heightmap: heightmap x0 y1 output 2 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_3; // Heightmap: heightmap x0 y1 output 3 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_4; // Heightmap: heightmap x0 y1 output 4 + TVoxelRange Heightmap__heightmap_x0_y1_2_Temp_5; // Heightmap: heightmap x0 y1 output 5 + Variable_1 = Params.heightmap_x0_y1.GetHeightRange(Variable_9, Variable_15, EVoxelSamplerMode::Clamp); + + // Heightmap: heightmap x1 y0 + TVoxelRange Variable_2; // Heightmap: heightmap x1 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x1_y0_2_Temp_1; // Heightmap: heightmap x1 y0 output 1 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_2; // Heightmap: heightmap x1 y0 output 2 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_3; // Heightmap: heightmap x1 y0 output 3 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_4; // Heightmap: heightmap x1 y0 output 4 + TVoxelRange Heightmap__heightmap_x1_y0_2_Temp_5; // Heightmap: heightmap x1 y0 output 5 + Variable_2 = Params.heightmap_x1_y0.GetHeightRange(Variable_13, Variable_10, EVoxelSamplerMode::Clamp); + + // Heightmap: heightmap x0 y0 + TVoxelRange Variable_0; // Heightmap: heightmap x0 y0 output 0 + FVoxelMaterialRange Heightmap__heightmap_x0_y0_2_Temp_1; // Heightmap: heightmap x0 y0 output 1 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_2; // Heightmap: heightmap x0 y0 output 2 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_3; // Heightmap: heightmap x0 y0 output 3 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_4; // Heightmap: heightmap x0 y0 output 4 + TVoxelRange Heightmap__heightmap_x0_y0_2_Temp_5; // Heightmap: heightmap x0 y0 output 5 + Variable_0 = Params.heightmap_x0_y0.GetHeightRange(Variable_9, Variable_10, EVoxelSamplerMode::Clamp); + + // 4x Flow Merge.Range Union + TVoxelRange Variable_20; // 4x Flow Merge.Range Union output 0 + Variable_20 = FVoxelNodeFunctions::Union(Variable_0, FVoxelNodeFunctions::Union(Variable_1, FVoxelNodeFunctions::Union(Variable_2, Variable_3))); + + Function1_XYZWithoutCache_Compute(Context, Outputs, Variable_20, BufferConstant.Variable_5, BufferConstant.Variable_6); + } + } + + void Function1_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs, TVoxelRange Variable_34, TVoxelRange Variable_35, TVoxelRange Variable_36) const + { + // X + TVoxelRange Variable_26; // X output 0 + Variable_26 = Context.GetLocalX(); + + // Y + TVoxelRange Variable_27; // Y output 0 + Variable_27 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_23; // Z output 0 + Variable_23 = Context.GetLocalZ(); + + // Z + TVoxelRange Variable_29; // Z output 0 + Variable_29 = Context.GetLocalZ(); + + // + + TVoxelRange Variable_30; // + output 0 + Variable_30 = Variable_29 + BufferConstant.Variable_31; + + // Box SDF + TVoxelRange Variable_25; // Box SDF output 0 + Variable_25 = FVoxelSDFNodeFunctions::Box(Variable_26, Variable_27, TVoxelRange(0.0f), Variable_35, Variable_36, TVoxelRange(1000000000.0f)); + + // - + TVoxelRange Variable_24; // - output 0 + Variable_24 = Variable_23 - Variable_34; + + // * -1 + TVoxelRange Variable_32; // * -1 output 0 + Variable_32 = Variable_30 * -1; + + // Max (float) + TVoxelRange Variable_28; // Max (float) output 0 + Variable_28 = FVoxelNodeFunctions::Max(Variable_24, FVoxelNodeFunctions::Max(Variable_25, Variable_32)); + + // Set High Quality Value.* + TVoxelRange Variable_33; // Set High Quality Value.* output 0 + Variable_33 = Variable_28 * TVoxelRange(0.2f); + + Outputs.Value = Variable_33; + } + + }; + + FVoxelExample_HeightmapCompositionInstance(UVoxelExample_HeightmapComposition& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Depth, + Object.Flip_X, + Object.Flip_Y, + TVoxelHeightmapAssetSamplerWrapper(Object.heightmap_x0_y0.LoadSynchronous()), + TVoxelHeightmapAssetSamplerWrapper(Object.heightmap_x0_y1.LoadSynchronous()), + TVoxelHeightmapAssetSamplerWrapper(Object.heightmap_x1_y0.LoadSynchronous()), + TVoxelHeightmapAssetSamplerWrapper(Object.heightmap_x1_y1.LoadSynchronous()), + Object.Size_X, + Object.Size_Y + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_HeightmapCompositionInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_HeightmapCompositionInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_HeightmapCompositionInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_HeightmapCompositionInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_HeightmapCompositionInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_HeightmapComposition::UVoxelExample_HeightmapComposition() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_HeightmapComposition::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_HeightmapComposition. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_HeightmapComposition. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_HeightmapComposition. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_HeightmapComposition. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.h new file mode 100644 index 00000000..29d85cc3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.h @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_HeightmapComposition.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_HeightmapComposition : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // Depth below the heightmap + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Depth")) + float Depth = 300.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Flip X")) + bool Flip_X = false; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Flip Y")) + bool Flip_Y = true; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="heightmap x0 y0")) + TSoftObjectPtr heightmap_x0_y0 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.heightmap_x0_y0")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="heightmap x0 y1")) + TSoftObjectPtr heightmap_x0_y1 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.heightmap_x0_y1")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="heightmap x1 y0")) + TSoftObjectPtr heightmap_x1_y0 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.heightmap_x1_y0")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="heightmap x1 y1")) + TSoftObjectPtr heightmap_x1_y1 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.heightmap_x1_y1")); + // Size of the heightmaps + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Size X")) + float Size_X = 512.0; + // Size of the heightmaps + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Size Y")) + float Size_Y = 512.0; + + UVoxelExample_HeightmapComposition(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.cpp new file mode 100644 index 00000000..12f1537a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.cpp @@ -0,0 +1,1253 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_HollowPlanet.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_HollowPlanetInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Intersection_Smoothness; + const float Noise_Bias; + const float Noise_Frequency; + const float Noise_Scale; + const float Radius; + const int32 Seed; + const bool Use_IQ_Noise; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_11; // Radius = 250.0 output 0 + bool Variable_22; // Use IQ Noise = True output 0 + v_flt Variable_16; // Noise Bias = 0.2 output 0 + v_flt Variable_17; // Intersection Smoothness = 10.0 output 0 + v_flt Variable_19; // Noise Frequency = 4.0 output 0 + v_flt Variable_10; // Noise Scale = 20.0 output 0 + v_flt Variable_12; // * -1 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_6; // X output 0 + v_flt Variable_0; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_7; // Y output 0 + v_flt Variable_1; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 1443 + FVoxelGraphSeed Variable_20; // Seed = 1443 output 0 + Variable_20 = Params.Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Radius = 250.0 + BufferConstant.Variable_11 = Params.Radius; + + // Use IQ Noise = True + BufferConstant.Variable_22 = Params.Use_IQ_Noise; + + // Noise Bias = 0.2 + BufferConstant.Variable_16 = Params.Noise_Bias; + + // Intersection Smoothness = 10.0 + BufferConstant.Variable_17 = Params.Intersection_Smoothness; + + // Noise Frequency = 4.0 + BufferConstant.Variable_19 = Params.Noise_Frequency; + + // Noise Scale = 20.0 + BufferConstant.Variable_10 = Params.Noise_Scale; + + // * -1 + BufferConstant.Variable_12 = BufferConstant.Variable_10 * -1; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_IQ_Noise_0_Noise; + TStaticArray _3D_IQ_Noise_0_LODToOctaves; + FVoxelFastNoise _3D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 1443 + FVoxelGraphSeed Variable_20; // Seed = 1443 output 0 + Variable_20 = Params.Seed; + + // Init of 3D IQ Noise + _3D_IQ_Noise_0_Noise.SetSeed(Variable_20); + _3D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.5); + _3D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _3D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_IQ_Noise_0_Noise.SetMatrixFromRotation_3D(FRotator(40.000000, 45.000000, 50.000000)); + _3D_IQ_Noise_0_LODToOctaves[0] = 15; + _3D_IQ_Noise_0_LODToOctaves[1] = 15; + _3D_IQ_Noise_0_LODToOctaves[2] = 15; + _3D_IQ_Noise_0_LODToOctaves[3] = 15; + _3D_IQ_Noise_0_LODToOctaves[4] = 15; + _3D_IQ_Noise_0_LODToOctaves[5] = 15; + _3D_IQ_Noise_0_LODToOctaves[6] = 15; + _3D_IQ_Noise_0_LODToOctaves[7] = 15; + _3D_IQ_Noise_0_LODToOctaves[8] = 15; + _3D_IQ_Noise_0_LODToOctaves[9] = 15; + _3D_IQ_Noise_0_LODToOctaves[10] = 15; + _3D_IQ_Noise_0_LODToOctaves[11] = 15; + _3D_IQ_Noise_0_LODToOctaves[12] = 15; + _3D_IQ_Noise_0_LODToOctaves[13] = 15; + _3D_IQ_Noise_0_LODToOctaves[14] = 15; + _3D_IQ_Noise_0_LODToOctaves[15] = 15; + _3D_IQ_Noise_0_LODToOctaves[16] = 15; + _3D_IQ_Noise_0_LODToOctaves[17] = 15; + _3D_IQ_Noise_0_LODToOctaves[18] = 15; + _3D_IQ_Noise_0_LODToOctaves[19] = 15; + _3D_IQ_Noise_0_LODToOctaves[20] = 15; + _3D_IQ_Noise_0_LODToOctaves[21] = 15; + _3D_IQ_Noise_0_LODToOctaves[22] = 15; + _3D_IQ_Noise_0_LODToOctaves[23] = 15; + _3D_IQ_Noise_0_LODToOctaves[24] = 15; + _3D_IQ_Noise_0_LODToOctaves[25] = 15; + _3D_IQ_Noise_0_LODToOctaves[26] = 15; + _3D_IQ_Noise_0_LODToOctaves[27] = 15; + _3D_IQ_Noise_0_LODToOctaves[28] = 15; + _3D_IQ_Noise_0_LODToOctaves[29] = 15; + _3D_IQ_Noise_0_LODToOctaves[30] = 15; + _3D_IQ_Noise_0_LODToOctaves[31] = 15; + + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_0_Noise.SetSeed(Variable_20); + _3D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(3, 0.5); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 3; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 3; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_6 = Context.GetLocalX(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_7 = Context.GetLocalY(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_6 = Context.GetLocalX(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // Y + BufferXY.Variable_7 = Context.GetLocalY(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Normalize.Vector Length + v_flt Variable_24; // Normalize.Vector Length output 0 + Variable_24 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferXY.Variable_1, Variable_2); + + // Vector Length + v_flt Variable_5; // Vector Length output 0 + Variable_5 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_6, BufferXY.Variable_7, Variable_8); + + // Normalize./ + v_flt Variable_25; // Normalize./ output 0 + Variable_25 = BufferX.Variable_0 / Variable_24; + + // Normalize./ + v_flt Variable_26; // Normalize./ output 0 + Variable_26 = BufferXY.Variable_1 / Variable_24; + + // Normalize./ + v_flt Variable_27; // Normalize./ output 0 + Variable_27 = Variable_2 / Variable_24; + + // 3D IQ Noise + v_flt Variable_18; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_0_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_0_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_0_Temp_3; // 3D IQ Noise output 3 + Variable_18 = _3D_IQ_Noise_0_Noise.IQNoise_3D_Deriv(Variable_25, Variable_26, Variable_27, BufferConstant.Variable_19, _3D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_0_Temp_1,_3D_IQ_Noise_0_Temp_2,_3D_IQ_Noise_0_Temp_3); + Variable_18 = FMath::Clamp(Variable_18, -0.732619, 0.767129); + _3D_IQ_Noise_0_Temp_1 = FMath::Clamp(_3D_IQ_Noise_0_Temp_1, -1.577728, 1.771872); + _3D_IQ_Noise_0_Temp_2 = FMath::Clamp(_3D_IQ_Noise_0_Temp_2, -1.779903, 1.388687); + _3D_IQ_Noise_0_Temp_3 = FMath::Clamp(_3D_IQ_Noise_0_Temp_3, -1.667376, 1.695596); + + // 3D Perlin Noise Fractal + v_flt Variable_3; // 3D Perlin Noise Fractal output 0 + Variable_3 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(Variable_25, Variable_26, Variable_27, BufferConstant.Variable_19, _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_3 = FMath::Clamp(Variable_3, -0.419158, 0.458317); + + // Switch (float) + v_flt Variable_21; // Switch (float) output 0 + Variable_21 = FVoxelNodeFunctions::Switch(Variable_18, Variable_3, BufferConstant.Variable_22); + + // + + v_flt Variable_14; // + output 0 + Variable_14 = Variable_21 + BufferConstant.Variable_16; + + // 2D Noise SDF.* + v_flt Variable_29; // 2D Noise SDF.* output 0 + Variable_29 = Variable_14 * BufferConstant.Variable_10; + + // 2D Noise SDF.* + v_flt Variable_31; // 2D Noise SDF.* output 0 + Variable_31 = Variable_14 * BufferConstant.Variable_12; + + // 2D Noise SDF.+ + v_flt Variable_9; // 2D Noise SDF.+ output 0 + Variable_9 = Variable_31 + BufferConstant.Variable_11; + + // 2D Noise SDF.+ + v_flt Variable_4; // 2D Noise SDF.+ output 0 + Variable_4 = Variable_29 + BufferConstant.Variable_11; + + // 2D Noise SDF.- + v_flt Variable_28; // 2D Noise SDF.- output 0 + Variable_28 = Variable_5 - Variable_4; + + // 2D Noise SDF.- + v_flt Variable_30; // 2D Noise SDF.- output 0 + Variable_30 = Variable_5 - Variable_9; + + // * -1 + v_flt Variable_13; // * -1 output 0 + Variable_13 = Variable_30 * -1; + + // Smooth Intersection + v_flt Variable_15; // Smooth Intersection output 0 + Variable_15 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_28, Variable_13, BufferConstant.Variable_17); + + // Set High Quality Value.* + v_flt Variable_23; // Set High Quality Value.* output 0 + Variable_23 = Variable_15 * v_flt(0.2f); + + Outputs.Value = Variable_23; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_6; // X output 0 + Variable_6 = Context.GetLocalX(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Y + v_flt Variable_7; // Y output 0 + Variable_7 = Context.GetLocalY(); + + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Normalize.Vector Length + v_flt Variable_24; // Normalize.Vector Length output 0 + Variable_24 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // Vector Length + v_flt Variable_5; // Vector Length output 0 + Variable_5 = FVoxelNodeFunctions::VectorLength(Variable_6, Variable_7, Variable_8); + + // Normalize./ + v_flt Variable_25; // Normalize./ output 0 + Variable_25 = Variable_0 / Variable_24; + + // Normalize./ + v_flt Variable_26; // Normalize./ output 0 + Variable_26 = Variable_1 / Variable_24; + + // Normalize./ + v_flt Variable_27; // Normalize./ output 0 + Variable_27 = Variable_2 / Variable_24; + + // 3D IQ Noise + v_flt Variable_18; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_0_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_0_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_0_Temp_3; // 3D IQ Noise output 3 + Variable_18 = _3D_IQ_Noise_0_Noise.IQNoise_3D_Deriv(Variable_25, Variable_26, Variable_27, BufferConstant.Variable_19, _3D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_0_Temp_1,_3D_IQ_Noise_0_Temp_2,_3D_IQ_Noise_0_Temp_3); + Variable_18 = FMath::Clamp(Variable_18, -0.732619, 0.767129); + _3D_IQ_Noise_0_Temp_1 = FMath::Clamp(_3D_IQ_Noise_0_Temp_1, -1.577728, 1.771872); + _3D_IQ_Noise_0_Temp_2 = FMath::Clamp(_3D_IQ_Noise_0_Temp_2, -1.779903, 1.388687); + _3D_IQ_Noise_0_Temp_3 = FMath::Clamp(_3D_IQ_Noise_0_Temp_3, -1.667376, 1.695596); + + // 3D Perlin Noise Fractal + v_flt Variable_3; // 3D Perlin Noise Fractal output 0 + Variable_3 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(Variable_25, Variable_26, Variable_27, BufferConstant.Variable_19, _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_3 = FMath::Clamp(Variable_3, -0.419158, 0.458317); + + // Switch (float) + v_flt Variable_21; // Switch (float) output 0 + Variable_21 = FVoxelNodeFunctions::Switch(Variable_18, Variable_3, BufferConstant.Variable_22); + + // + + v_flt Variable_14; // + output 0 + Variable_14 = Variable_21 + BufferConstant.Variable_16; + + // 2D Noise SDF.* + v_flt Variable_29; // 2D Noise SDF.* output 0 + Variable_29 = Variable_14 * BufferConstant.Variable_10; + + // 2D Noise SDF.* + v_flt Variable_31; // 2D Noise SDF.* output 0 + Variable_31 = Variable_14 * BufferConstant.Variable_12; + + // 2D Noise SDF.+ + v_flt Variable_9; // 2D Noise SDF.+ output 0 + Variable_9 = Variable_31 + BufferConstant.Variable_11; + + // 2D Noise SDF.+ + v_flt Variable_4; // 2D Noise SDF.+ output 0 + Variable_4 = Variable_29 + BufferConstant.Variable_11; + + // 2D Noise SDF.- + v_flt Variable_28; // 2D Noise SDF.- output 0 + Variable_28 = Variable_5 - Variable_4; + + // 2D Noise SDF.- + v_flt Variable_30; // 2D Noise SDF.- output 0 + Variable_30 = Variable_5 - Variable_9; + + // * -1 + v_flt Variable_13; // * -1 output 0 + Variable_13 = Variable_30 * -1; + + // Smooth Intersection + v_flt Variable_15; // Smooth Intersection output 0 + Variable_15 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_28, Variable_13, BufferConstant.Variable_17); + + // Set High Quality Value.* + v_flt Variable_23; // Set High Quality Value.* output 0 + Variable_23 = Variable_15 * v_flt(0.2f); + + Outputs.Value = Variable_23; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_14; // Intersection Smoothness = 10.0 output 0 + TVoxelRange Variable_6; // 2D Noise SDF.+ output 0 + TVoxelRange Variable_1; // 2D Noise SDF.+ output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_3; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_4; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(3, 0.5); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 3; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 3; + + // Init of 3D IQ Noise + _3D_IQ_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.5); + _3D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _3D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_IQ_Noise_1_Noise.SetMatrixFromRotation_3D(FRotator(40.000000, 45.000000, 50.000000)); + _3D_IQ_Noise_1_LODToOctaves[0] = 15; + _3D_IQ_Noise_1_LODToOctaves[1] = 15; + _3D_IQ_Noise_1_LODToOctaves[2] = 15; + _3D_IQ_Noise_1_LODToOctaves[3] = 15; + _3D_IQ_Noise_1_LODToOctaves[4] = 15; + _3D_IQ_Noise_1_LODToOctaves[5] = 15; + _3D_IQ_Noise_1_LODToOctaves[6] = 15; + _3D_IQ_Noise_1_LODToOctaves[7] = 15; + _3D_IQ_Noise_1_LODToOctaves[8] = 15; + _3D_IQ_Noise_1_LODToOctaves[9] = 15; + _3D_IQ_Noise_1_LODToOctaves[10] = 15; + _3D_IQ_Noise_1_LODToOctaves[11] = 15; + _3D_IQ_Noise_1_LODToOctaves[12] = 15; + _3D_IQ_Noise_1_LODToOctaves[13] = 15; + _3D_IQ_Noise_1_LODToOctaves[14] = 15; + _3D_IQ_Noise_1_LODToOctaves[15] = 15; + _3D_IQ_Noise_1_LODToOctaves[16] = 15; + _3D_IQ_Noise_1_LODToOctaves[17] = 15; + _3D_IQ_Noise_1_LODToOctaves[18] = 15; + _3D_IQ_Noise_1_LODToOctaves[19] = 15; + _3D_IQ_Noise_1_LODToOctaves[20] = 15; + _3D_IQ_Noise_1_LODToOctaves[21] = 15; + _3D_IQ_Noise_1_LODToOctaves[22] = 15; + _3D_IQ_Noise_1_LODToOctaves[23] = 15; + _3D_IQ_Noise_1_LODToOctaves[24] = 15; + _3D_IQ_Noise_1_LODToOctaves[25] = 15; + _3D_IQ_Noise_1_LODToOctaves[26] = 15; + _3D_IQ_Noise_1_LODToOctaves[27] = 15; + _3D_IQ_Noise_1_LODToOctaves[28] = 15; + _3D_IQ_Noise_1_LODToOctaves[29] = 15; + _3D_IQ_Noise_1_LODToOctaves[30] = 15; + _3D_IQ_Noise_1_LODToOctaves[31] = 15; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Noise Bias = 0.2 + TVoxelRange Variable_13; // Noise Bias = 0.2 output 0 + Variable_13 = Params.Noise_Bias; + + // 3D Perlin Noise Fractal + TVoxelRange Variable_0; // 3D Perlin Noise Fractal output 0 + Variable_0 = { -0.419158f, 0.458317f }; + + // Intersection Smoothness = 10.0 + BufferConstant.Variable_14 = Params.Intersection_Smoothness; + + // Noise Scale = 20.0 + TVoxelRange Variable_7; // Noise Scale = 20.0 output 0 + Variable_7 = Params.Noise_Scale; + + // Radius = 250.0 + TVoxelRange Variable_8; // Radius = 250.0 output 0 + Variable_8 = Params.Radius; + + // 3D IQ Noise + TVoxelRange Variable_15; // 3D IQ Noise output 0 + TVoxelRange _3D_IQ_Noise_1_Temp_1; // 3D IQ Noise output 1 + TVoxelRange _3D_IQ_Noise_1_Temp_2; // 3D IQ Noise output 2 + TVoxelRange _3D_IQ_Noise_1_Temp_3; // 3D IQ Noise output 3 + Variable_15 = { -0.732619f, 0.767129f }; + _3D_IQ_Noise_1_Temp_1 = { -1.577728f, 1.771872f }; + _3D_IQ_Noise_1_Temp_2 = { -1.779903f, 1.388687f }; + _3D_IQ_Noise_1_Temp_3 = { -1.667376f, 1.695596f }; + + // Use IQ Noise = True + FVoxelBoolRange Variable_17; // Use IQ Noise = True output 0 + Variable_17 = Params.Use_IQ_Noise; + + // Switch (float) + TVoxelRange Variable_16; // Switch (float) output 0 + Variable_16 = FVoxelNodeFunctions::Switch(Variable_15, Variable_0, Variable_17); + + // * -1 + TVoxelRange Variable_9; // * -1 output 0 + Variable_9 = Variable_7 * -1; + + // + + TVoxelRange Variable_11; // + output 0 + Variable_11 = Variable_16 + Variable_13; + + // 2D Noise SDF.* + TVoxelRange Variable_20; // 2D Noise SDF.* output 0 + Variable_20 = Variable_11 * Variable_7; + + // 2D Noise SDF.* + TVoxelRange Variable_22; // 2D Noise SDF.* output 0 + Variable_22 = Variable_11 * Variable_9; + + // 2D Noise SDF.+ + BufferConstant.Variable_6 = Variable_22 + Variable_8; + + // 2D Noise SDF.+ + BufferConstant.Variable_1 = Variable_20 + Variable_8; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_1_LODToOctaves; + FVoxelFastNoise _3D_IQ_Noise_1_Noise; + TStaticArray _3D_IQ_Noise_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // X + TVoxelRange Variable_3; // X output 0 + Variable_3 = Context.GetLocalX(); + + // Y + TVoxelRange Variable_4; // Y output 0 + Variable_4 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_5; // Z output 0 + Variable_5 = Context.GetLocalZ(); + + // Vector Length + TVoxelRange Variable_2; // Vector Length output 0 + Variable_2 = FVoxelNodeFunctions::VectorLength(Variable_3, Variable_4, Variable_5); + + // 2D Noise SDF.- + TVoxelRange Variable_21; // 2D Noise SDF.- output 0 + Variable_21 = Variable_2 - BufferConstant.Variable_6; + + // 2D Noise SDF.- + TVoxelRange Variable_19; // 2D Noise SDF.- output 0 + Variable_19 = Variable_2 - BufferConstant.Variable_1; + + // * -1 + TVoxelRange Variable_10; // * -1 output 0 + Variable_10 = Variable_21 * -1; + + // Smooth Intersection + TVoxelRange Variable_12; // Smooth Intersection output 0 + Variable_12 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_19, Variable_10, BufferConstant.Variable_14); + + // Set High Quality Value.* + TVoxelRange Variable_18; // Set High Quality Value.* output 0 + Variable_18 = Variable_12 * TVoxelRange(0.2f); + + Outputs.Value = Variable_18; + } + + }; + + FVoxelExample_HollowPlanetInstance(UVoxelExample_HollowPlanet& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Intersection_Smoothness, + Object.Noise_Bias, + Object.Noise_Frequency, + Object.Noise_Scale, + Object.Radius, + Object.Seed, + Object.Use_IQ_Noise + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_HollowPlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_HollowPlanetInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_HollowPlanetInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_HollowPlanetInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_HollowPlanetInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_HollowPlanet::UVoxelExample_HollowPlanet() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_HollowPlanet::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_HollowPlanet. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_HollowPlanet. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_HollowPlanet. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_HollowPlanet. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.h new file mode 100644 index 00000000..b07ca369 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.h @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_HollowPlanet.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_HollowPlanet : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Intersection Smoothness")) + float Intersection_Smoothness = 10.0; + // Above 0: More ground. Below zero: less ground + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Bias")) + float Noise_Bias = 0.2; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Frequency")) + float Noise_Frequency = 4.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Scale")) + float Noise_Scale = 20.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Radius")) + float Radius = 250.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 1443; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Use IQ Noise")) + bool Use_IQ_Noise = true; + + UVoxelExample_HollowPlanet(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.cpp new file mode 100644 index 00000000..a603f1b8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.cpp @@ -0,0 +1,924 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_IQNoise.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_IQNoiseInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Frequency; + const float Height; + const int32 Seed; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_7; // Frequency = 0.001 output 0 + v_flt Variable_3; // Height = 500.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_5; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // * output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 1443 + FVoxelGraphSeed Variable_8; // Seed = 1443 output 0 + Variable_8 = Params.Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Frequency = 0.001 + BufferConstant.Variable_7 = Params.Frequency; + + // Height = 500.0 + BufferConstant.Variable_3 = Params.Height; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_IQ_Noise_0_Noise; + TStaticArray _2D_IQ_Noise_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 1443 + FVoxelGraphSeed Variable_8; // Seed = 1443 output 0 + Variable_8 = Params.Seed; + + // Init of 2D IQ Noise + _2D_IQ_Noise_0_Noise.SetSeed(Variable_8); + _2D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_0_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_0_LODToOctaves[0] = 15; + _2D_IQ_Noise_0_LODToOctaves[1] = 15; + _2D_IQ_Noise_0_LODToOctaves[2] = 15; + _2D_IQ_Noise_0_LODToOctaves[3] = 15; + _2D_IQ_Noise_0_LODToOctaves[4] = 15; + _2D_IQ_Noise_0_LODToOctaves[5] = 15; + _2D_IQ_Noise_0_LODToOctaves[6] = 15; + _2D_IQ_Noise_0_LODToOctaves[7] = 15; + _2D_IQ_Noise_0_LODToOctaves[8] = 15; + _2D_IQ_Noise_0_LODToOctaves[9] = 15; + _2D_IQ_Noise_0_LODToOctaves[10] = 15; + _2D_IQ_Noise_0_LODToOctaves[11] = 15; + _2D_IQ_Noise_0_LODToOctaves[12] = 15; + _2D_IQ_Noise_0_LODToOctaves[13] = 15; + _2D_IQ_Noise_0_LODToOctaves[14] = 15; + _2D_IQ_Noise_0_LODToOctaves[15] = 15; + _2D_IQ_Noise_0_LODToOctaves[16] = 15; + _2D_IQ_Noise_0_LODToOctaves[17] = 15; + _2D_IQ_Noise_0_LODToOctaves[18] = 15; + _2D_IQ_Noise_0_LODToOctaves[19] = 15; + _2D_IQ_Noise_0_LODToOctaves[20] = 15; + _2D_IQ_Noise_0_LODToOctaves[21] = 15; + _2D_IQ_Noise_0_LODToOctaves[22] = 15; + _2D_IQ_Noise_0_LODToOctaves[23] = 15; + _2D_IQ_Noise_0_LODToOctaves[24] = 15; + _2D_IQ_Noise_0_LODToOctaves[25] = 15; + _2D_IQ_Noise_0_LODToOctaves[26] = 15; + _2D_IQ_Noise_0_LODToOctaves[27] = 15; + _2D_IQ_Noise_0_LODToOctaves[28] = 15; + _2D_IQ_Noise_0_LODToOctaves[29] = 15; + _2D_IQ_Noise_0_LODToOctaves[30] = 15; + _2D_IQ_Noise_0_LODToOctaves[31] = 15; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_5 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_6; // Y output 0 + Variable_6 = Context.GetLocalY(); + + // 2D IQ Noise + v_flt Variable_4; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_4 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_5, Variable_6, BufferConstant.Variable_7, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_4 = FMath::Clamp(Variable_4, -0.722945, 0.798964); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.342896, 1.704131); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.209649, 1.295183); + + // * + BufferXY.Variable_2 = Variable_4 * BufferConstant.Variable_3; + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + v_flt Variable_6; // Y output 0 + Variable_6 = Context.GetLocalY(); + + // X + BufferX.Variable_5 = Context.GetLocalX(); + + // 2D IQ Noise + v_flt Variable_4; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_4 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(BufferX.Variable_5, Variable_6, BufferConstant.Variable_7, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_4 = FMath::Clamp(Variable_4, -0.722945, 0.798964); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.342896, 1.704131); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.209649, 1.295183); + + // * + BufferXY.Variable_2 = Variable_4 * BufferConstant.Variable_3; + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // - + v_flt Variable_1; // - output 0 + Variable_1 = Variable_0 - BufferXY.Variable_2; + + Outputs.Value = Variable_1; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Y + v_flt Variable_6; // Y output 0 + Variable_6 = Context.GetLocalY(); + + // X + v_flt Variable_5; // X output 0 + Variable_5 = Context.GetLocalX(); + + // 2D IQ Noise + v_flt Variable_4; // 2D IQ Noise output 0 + v_flt _2D_IQ_Noise_0_Temp_1; // 2D IQ Noise output 1 + v_flt _2D_IQ_Noise_0_Temp_2; // 2D IQ Noise output 2 + Variable_4 = _2D_IQ_Noise_0_Noise.IQNoise_2D_Deriv(Variable_5, Variable_6, BufferConstant.Variable_7, _2D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_2D_IQ_Noise_0_Temp_1,_2D_IQ_Noise_0_Temp_2); + Variable_4 = FMath::Clamp(Variable_4, -0.722945, 0.798964); + _2D_IQ_Noise_0_Temp_1 = FMath::Clamp(_2D_IQ_Noise_0_Temp_1, -1.342896, 1.704131); + _2D_IQ_Noise_0_Temp_2 = FMath::Clamp(_2D_IQ_Noise_0_Temp_2, -1.209649, 1.295183); + + // * + v_flt Variable_2; // * output 0 + Variable_2 = Variable_4 * BufferConstant.Variable_3; + + // - + v_flt Variable_1; // - output 0 + Variable_1 = Variable_0 - Variable_2; + + Outputs.Value = Variable_1; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_2; // * output 0 + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D IQ Noise + _2D_IQ_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.5); + _2D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _2D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_IQ_Noise_1_Noise.SetMatrixFromRotation_2D(40.0); + _2D_IQ_Noise_1_LODToOctaves[0] = 15; + _2D_IQ_Noise_1_LODToOctaves[1] = 15; + _2D_IQ_Noise_1_LODToOctaves[2] = 15; + _2D_IQ_Noise_1_LODToOctaves[3] = 15; + _2D_IQ_Noise_1_LODToOctaves[4] = 15; + _2D_IQ_Noise_1_LODToOctaves[5] = 15; + _2D_IQ_Noise_1_LODToOctaves[6] = 15; + _2D_IQ_Noise_1_LODToOctaves[7] = 15; + _2D_IQ_Noise_1_LODToOctaves[8] = 15; + _2D_IQ_Noise_1_LODToOctaves[9] = 15; + _2D_IQ_Noise_1_LODToOctaves[10] = 15; + _2D_IQ_Noise_1_LODToOctaves[11] = 15; + _2D_IQ_Noise_1_LODToOctaves[12] = 15; + _2D_IQ_Noise_1_LODToOctaves[13] = 15; + _2D_IQ_Noise_1_LODToOctaves[14] = 15; + _2D_IQ_Noise_1_LODToOctaves[15] = 15; + _2D_IQ_Noise_1_LODToOctaves[16] = 15; + _2D_IQ_Noise_1_LODToOctaves[17] = 15; + _2D_IQ_Noise_1_LODToOctaves[18] = 15; + _2D_IQ_Noise_1_LODToOctaves[19] = 15; + _2D_IQ_Noise_1_LODToOctaves[20] = 15; + _2D_IQ_Noise_1_LODToOctaves[21] = 15; + _2D_IQ_Noise_1_LODToOctaves[22] = 15; + _2D_IQ_Noise_1_LODToOctaves[23] = 15; + _2D_IQ_Noise_1_LODToOctaves[24] = 15; + _2D_IQ_Noise_1_LODToOctaves[25] = 15; + _2D_IQ_Noise_1_LODToOctaves[26] = 15; + _2D_IQ_Noise_1_LODToOctaves[27] = 15; + _2D_IQ_Noise_1_LODToOctaves[28] = 15; + _2D_IQ_Noise_1_LODToOctaves[29] = 15; + _2D_IQ_Noise_1_LODToOctaves[30] = 15; + _2D_IQ_Noise_1_LODToOctaves[31] = 15; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Height = 500.0 + TVoxelRange Variable_3; // Height = 500.0 output 0 + Variable_3 = Params.Height; + + // 2D IQ Noise + TVoxelRange Variable_4; // 2D IQ Noise output 0 + TVoxelRange _2D_IQ_Noise_1_Temp_1; // 2D IQ Noise output 1 + TVoxelRange _2D_IQ_Noise_1_Temp_2; // 2D IQ Noise output 2 + Variable_4 = { -0.722945f, 0.798964f }; + _2D_IQ_Noise_1_Temp_1 = { -1.342896f, 1.704131f }; + _2D_IQ_Noise_1_Temp_2 = { -1.209649f, 1.295183f }; + + // * + BufferConstant.Variable_2 = Variable_4 * Variable_3; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_IQ_Noise_1_Noise; + TStaticArray _2D_IQ_Noise_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // - + TVoxelRange Variable_1; // - output 0 + Variable_1 = Variable_0 - BufferConstant.Variable_2; + + Outputs.Value = Variable_1; + } + + }; + + FVoxelExample_IQNoiseInstance(UVoxelExample_IQNoise& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Frequency, + Object.Height, + Object.Seed + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_IQNoiseInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_IQNoiseInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_IQNoiseInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_IQNoiseInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_IQNoiseInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_IQNoise::UVoxelExample_IQNoise() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_IQNoise::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_IQNoise. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_IQNoise. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_IQNoise. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_IQNoise. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.h new file mode 100644 index 00000000..f3a8e893 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_IQNoise.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_IQNoise : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Frequency")) + float Frequency = 0.001; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Height")) + float Height = 500.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 1443; + + UVoxelExample_IQNoise(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.cpp new file mode 100644 index 00000000..9d2fd1d5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.cpp @@ -0,0 +1,1799 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_LayeredPlanet.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_LayeredPlanetInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Frequency; + const FVoxelRichCurve None1; + const int32 Seed; + const bool Slice_Mode; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + bool Variable_51; // Slice Mode = False output 0 + v_flt Variable_43; // Frequency = 0.005 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_22; // X output 0 + v_flt Variable_35; // X output 0 + v_flt Variable_8; // X output 0 + v_flt Variable_26; // X output 0 + v_flt Variable_30; // X output 0 + v_flt Variable_18; // X output 0 + v_flt Variable_14; // X output 0 + v_flt Variable_39; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_9; // Y output 0 + v_flt Variable_31; // Y output 0 + v_flt Variable_19; // Y output 0 + v_flt Variable_27; // Y output 0 + v_flt Variable_40; // Y output 0 + v_flt Variable_23; // Y output 0 + v_flt Variable_15; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 4761 + FVoxelGraphSeed Variable_49; // Seed = 4761 output 0 + Variable_49 = Params.Seed; + + // Init of Make Seeds + FVoxelGraphSeed Variable_44; // Make Seeds output 0 + FVoxelGraphSeed Variable_45; // Make Seeds output 1 + FVoxelGraphSeed Variable_46; // Make Seeds output 2 + FVoxelGraphSeed Variable_47; // Make Seeds output 3 + FVoxelGraphSeed Variable_48; // Make Seeds output 4 + Variable_44 = FVoxelUtilities::MurmurHash32(Variable_49); + Variable_45 = FVoxelUtilities::MurmurHash32(Variable_44); + Variable_46 = FVoxelUtilities::MurmurHash32(Variable_45); + Variable_47 = FVoxelUtilities::MurmurHash32(Variable_46); + Variable_48 = FVoxelUtilities::MurmurHash32(Variable_47); + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Slice Mode = False + BufferConstant.Variable_51 = Params.Slice_Mode; + + // Frequency = 0.005 + BufferConstant.Variable_43 = Params.Frequency; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_0_Noise; + FVoxelFastNoise _3D_Perlin_Noise_1_Noise; + FVoxelFastNoise _3D_Perlin_Noise_2_Noise; + FVoxelFastNoise _3D_Perlin_Noise_3_Noise; + FVoxelFastNoise _3D_Perlin_Noise_4_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 4761 + FVoxelGraphSeed Variable_49; // Seed = 4761 output 0 + Variable_49 = Params.Seed; + + // Init of Make Seeds + FVoxelGraphSeed Variable_44; // Make Seeds output 0 + FVoxelGraphSeed Variable_45; // Make Seeds output 1 + FVoxelGraphSeed Variable_46; // Make Seeds output 2 + FVoxelGraphSeed Variable_47; // Make Seeds output 3 + FVoxelGraphSeed Variable_48; // Make Seeds output 4 + Variable_44 = FVoxelUtilities::MurmurHash32(Variable_49); + Variable_45 = FVoxelUtilities::MurmurHash32(Variable_44); + Variable_46 = FVoxelUtilities::MurmurHash32(Variable_45); + Variable_47 = FVoxelUtilities::MurmurHash32(Variable_46); + Variable_48 = FVoxelUtilities::MurmurHash32(Variable_47); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_0_Noise.SetSeed(Variable_45); + _3D_Perlin_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_1_Noise.SetSeed(Variable_48); + _3D_Perlin_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_2_Noise.SetSeed(Variable_47); + _3D_Perlin_Noise_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_3_Noise.SetSeed(Variable_46); + _3D_Perlin_Noise_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_4_Noise.SetSeed(Variable_44); + _3D_Perlin_Noise_4_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_22 = Context.GetLocalX(); + + // X + BufferX.Variable_35 = Context.GetLocalX(); + + // X + BufferX.Variable_8 = Context.GetLocalX(); + + // X + BufferX.Variable_26 = Context.GetLocalX(); + + // X + BufferX.Variable_30 = Context.GetLocalX(); + + // X + BufferX.Variable_18 = Context.GetLocalX(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + // X + BufferX.Variable_39 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_9 = Context.GetLocalY(); + + // Y + BufferXY.Variable_31 = Context.GetLocalY(); + + // Y + BufferXY.Variable_19 = Context.GetLocalY(); + + // Y + BufferXY.Variable_27 = Context.GetLocalY(); + + // Y + BufferXY.Variable_40 = Context.GetLocalY(); + + // Y + BufferXY.Variable_23 = Context.GetLocalY(); + + // Y + BufferXY.Variable_15 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_22 = Context.GetLocalX(); + + // Y + BufferXY.Variable_9 = Context.GetLocalY(); + + // X + BufferX.Variable_35 = Context.GetLocalX(); + + // X + BufferX.Variable_8 = Context.GetLocalX(); + + // Y + BufferXY.Variable_31 = Context.GetLocalY(); + + // Y + BufferXY.Variable_19 = Context.GetLocalY(); + + // Y + BufferXY.Variable_27 = Context.GetLocalY(); + + // X + BufferX.Variable_26 = Context.GetLocalX(); + + // X + BufferX.Variable_30 = Context.GetLocalX(); + + // X + BufferX.Variable_18 = Context.GetLocalX(); + + // X + BufferX.Variable_14 = Context.GetLocalX(); + + // Y + BufferXY.Variable_40 = Context.GetLocalY(); + + // Y + BufferXY.Variable_23 = Context.GetLocalY(); + + // X + BufferX.Variable_39 = Context.GetLocalX(); + + // Y + BufferXY.Variable_15 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_10; // Z output 0 + Variable_10 = Context.GetLocalZ(); + + // Z + v_flt Variable_20; // Z output 0 + Variable_20 = Context.GetLocalZ(); + + // Z + v_flt Variable_32; // Z output 0 + Variable_32 = Context.GetLocalZ(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Z + v_flt Variable_28; // Z output 0 + Variable_28 = Context.GetLocalZ(); + + // Z + v_flt Variable_41; // Z output 0 + Variable_41 = Context.GetLocalZ(); + + // Data Item Sample + v_flt Variable_13; // Data Item Sample output 0 + Variable_13 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, BufferX.Variable_8, BufferXY.Variable_9, Variable_10, v_flt(5.0f), v_flt(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // Normalize.Vector Length + v_flt Variable_74; // Normalize.Vector Length output 0 + Variable_74 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_18, BufferXY.Variable_19, Variable_20); + + // Vector Length + v_flt Variable_16; // Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_14, BufferXY.Variable_15, Variable_0); + + // Normalize./ + v_flt Variable_75; // Normalize./ output 0 + Variable_75 = BufferX.Variable_18 / Variable_74; + + // 3D Perlin Noise + v_flt Variable_21; // 3D Perlin Noise output 0 + Variable_21 = _3D_Perlin_Noise_0_Noise.GetPerlin_3D(BufferX.Variable_22, BufferXY.Variable_23, Variable_24, BufferConstant.Variable_43); + Variable_21 = FMath::Clamp(Variable_21, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_38; // 3D Perlin Noise output 0 + Variable_38 = _3D_Perlin_Noise_1_Noise.GetPerlin_3D(BufferX.Variable_39, BufferXY.Variable_40, Variable_41, BufferConstant.Variable_43); + Variable_38 = FMath::Clamp(Variable_38, -0.790554, 0.781474); + + // - + v_flt Variable_33; // - output 0 + Variable_33 = Variable_16 - v_flt(500.0f); + + // Normalize./ + v_flt Variable_76; // Normalize./ output 0 + Variable_76 = BufferXY.Variable_19 / Variable_74; + + // Normalize./ + v_flt Variable_77; // Normalize./ output 0 + Variable_77 = Variable_20 / Variable_74; + + // * -1 + v_flt Variable_12; // * -1 output 0 + Variable_12 = Variable_13 * -1; + + // 3D Perlin Noise + v_flt Variable_29; // 3D Perlin Noise output 0 + Variable_29 = _3D_Perlin_Noise_2_Noise.GetPerlin_3D(BufferX.Variable_30, BufferXY.Variable_31, Variable_32, BufferConstant.Variable_43); + Variable_29 = FMath::Clamp(Variable_29, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_25; // 3D Perlin Noise output 0 + Variable_25 = _3D_Perlin_Noise_3_Noise.GetPerlin_3D(BufferX.Variable_26, BufferXY.Variable_27, Variable_28, BufferConstant.Variable_43); + Variable_25 = FMath::Clamp(Variable_25, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_17; // 3D Perlin Noise output 0 + Variable_17 = _3D_Perlin_Noise_4_Noise.GetPerlin_3D(Variable_75, Variable_76, Variable_77, v_flt(5.0f)); + Variable_17 = FMath::Clamp(Variable_17, -0.911908, 0.945800); + + // Cave Layer.* + v_flt Variable_56; // Cave Layer.* output 0 + Variable_56 = Variable_25 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_63; // Cave Layer.* output 0 + Variable_63 = Variable_21 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_71; // Cave Layer.* output 0 + Variable_71 = Variable_29 * v_flt(50.0f); + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_33 - v_flt(75.0f); + + // + + v_flt Variable_37; // + output 0 + Variable_37 = v_flt(300.0f) + Variable_33; + + // 2D Noise SDF.* + v_flt Variable_79; // 2D Noise SDF.* output 0 + Variable_79 = Variable_38 * v_flt(50.0f); + + // + + v_flt Variable_1; // + output 0 + Variable_1 = v_flt(100.0f) + Variable_33; + + // + + v_flt Variable_3; // + output 0 + Variable_3 = v_flt(200.0f) + Variable_33; + + // Cave Layer.1 - X + v_flt Variable_60; // Cave Layer.1 - X output 0 + Variable_60 = 1 - Variable_63; + + // Cave Layer.- + v_flt Variable_55; // Cave Layer.- output 0 + Variable_55 = Variable_56 - Variable_1; + + // Float Curve: + v_flt Variable_7; // Float Curve: output 0 + Variable_7 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_17); + + // Cave Layer.1 - X + v_flt Variable_53; // Cave Layer.1 - X output 0 + Variable_53 = 1 - Variable_56; + + // Cave Layer.- + v_flt Variable_62; // Cave Layer.- output 0 + Variable_62 = Variable_63 - Variable_33; + + // 2D Noise SDF.+ + v_flt Variable_34; // 2D Noise SDF.+ output 0 + Variable_34 = Variable_79 + v_flt(0.0f); + + // Cave Layer.1 - X + v_flt Variable_68; // Cave Layer.1 - X output 0 + Variable_68 = 1 - Variable_71; + + // Cave Layer.- + v_flt Variable_70; // Cave Layer.- output 0 + Variable_70 = Variable_71 - Variable_3; + + // Cave Layer.* + v_flt Variable_73; // Cave Layer.* output 0 + Variable_73 = v_flt(0.2f) * Variable_68; + + // Cave Layer.* + v_flt Variable_58; // Cave Layer.* output 0 + Variable_58 = v_flt(0.2f) * Variable_53; + + // 2D Noise SDF.- + v_flt Variable_78; // 2D Noise SDF.- output 0 + Variable_78 = Variable_37 - Variable_34; + + // Cave Layer.* + v_flt Variable_65; // Cave Layer.* output 0 + Variable_65 = v_flt(0.2f) * Variable_60; + + // * + v_flt Variable_5; // * output 0 + Variable_5 = Variable_7 * v_flt(100.0f); + + // Cave Layer.- + v_flt Variable_69; // Cave Layer.- output 0 + Variable_69 = Variable_3 - Variable_73; + + // Cave Layer.- + v_flt Variable_54; // Cave Layer.- output 0 + Variable_54 = Variable_1 - Variable_58; + + // * -1 + v_flt Variable_42; // * -1 output 0 + Variable_42 = Variable_78 * -1; + + // Cave Layer.- + v_flt Variable_61; // Cave Layer.- output 0 + Variable_61 = Variable_33 - Variable_65; + + // - + v_flt Variable_6; // - output 0 + Variable_6 = Variable_4 - Variable_5; + + // Cave Layer.Smooth Union + v_flt Variable_67; // Cave Layer.Smooth Union output 0 + Variable_67 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_69, Variable_70, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_59; // Cave Layer.Smooth Union output 0 + Variable_59 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_61, Variable_62, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_52; // Cave Layer.Smooth Union output 0 + Variable_52 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_54, Variable_55, v_flt(100.0f)); + + // Cave Layer.+ + v_flt Variable_64; // Cave Layer.+ output 0 + Variable_64 = Variable_59 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_57; // Cave Layer.+ output 0 + Variable_57 = Variable_52 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_72; // Cave Layer.+ output 0 + Variable_72 = Variable_67 + v_flt(25.0f); + + // Max (float) + v_flt Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_6, FVoxelNodeFunctions::Max(Variable_64, FVoxelNodeFunctions::Max(Variable_57, FVoxelNodeFunctions::Max(Variable_72, Variable_42)))); + + // Smooth Intersection + v_flt Variable_11; // Smooth Intersection output 0 + Variable_11 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_12, v_flt(5.0f)); + + // Max (float) + v_flt Variable_36; // Max (float) output 0 + Variable_36 = FVoxelNodeFunctions::Max(BufferX.Variable_35, Variable_11); + + // Switch (float) + v_flt Variable_50; // Switch (float) output 0 + Variable_50 = FVoxelNodeFunctions::Switch(Variable_36, Variable_11, BufferConstant.Variable_51); + + // Set High Quality Value.* + v_flt Variable_66; // Set High Quality Value.* output 0 + Variable_66 = Variable_50 * v_flt(0.2f); + + Outputs.Value = Variable_66; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_22; // X output 0 + Variable_22 = Context.GetLocalX(); + + // Z + v_flt Variable_10; // Z output 0 + Variable_10 = Context.GetLocalZ(); + + // Y + v_flt Variable_9; // Y output 0 + Variable_9 = Context.GetLocalY(); + + // X + v_flt Variable_35; // X output 0 + Variable_35 = Context.GetLocalX(); + + // X + v_flt Variable_8; // X output 0 + Variable_8 = Context.GetLocalX(); + + // Y + v_flt Variable_31; // Y output 0 + Variable_31 = Context.GetLocalY(); + + // Y + v_flt Variable_19; // Y output 0 + Variable_19 = Context.GetLocalY(); + + // Y + v_flt Variable_27; // Y output 0 + Variable_27 = Context.GetLocalY(); + + // Z + v_flt Variable_20; // Z output 0 + Variable_20 = Context.GetLocalZ(); + + // Z + v_flt Variable_32; // Z output 0 + Variable_32 = Context.GetLocalZ(); + + // X + v_flt Variable_26; // X output 0 + Variable_26 = Context.GetLocalX(); + + // X + v_flt Variable_30; // X output 0 + Variable_30 = Context.GetLocalX(); + + // X + v_flt Variable_18; // X output 0 + Variable_18 = Context.GetLocalX(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Z + v_flt Variable_28; // Z output 0 + Variable_28 = Context.GetLocalZ(); + + // X + v_flt Variable_14; // X output 0 + Variable_14 = Context.GetLocalX(); + + // Y + v_flt Variable_40; // Y output 0 + Variable_40 = Context.GetLocalY(); + + // Y + v_flt Variable_23; // Y output 0 + Variable_23 = Context.GetLocalY(); + + // X + v_flt Variable_39; // X output 0 + Variable_39 = Context.GetLocalX(); + + // Z + v_flt Variable_41; // Z output 0 + Variable_41 = Context.GetLocalZ(); + + // Y + v_flt Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // Data Item Sample + v_flt Variable_13; // Data Item Sample output 0 + Variable_13 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_8, Variable_9, Variable_10, v_flt(5.0f), v_flt(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // Normalize.Vector Length + v_flt Variable_74; // Normalize.Vector Length output 0 + Variable_74 = FVoxelNodeFunctions::VectorLength(Variable_18, Variable_19, Variable_20); + + // Vector Length + v_flt Variable_16; // Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(Variable_14, Variable_15, Variable_0); + + // Normalize./ + v_flt Variable_75; // Normalize./ output 0 + Variable_75 = Variable_18 / Variable_74; + + // 3D Perlin Noise + v_flt Variable_21; // 3D Perlin Noise output 0 + Variable_21 = _3D_Perlin_Noise_0_Noise.GetPerlin_3D(Variable_22, Variable_23, Variable_24, BufferConstant.Variable_43); + Variable_21 = FMath::Clamp(Variable_21, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_38; // 3D Perlin Noise output 0 + Variable_38 = _3D_Perlin_Noise_1_Noise.GetPerlin_3D(Variable_39, Variable_40, Variable_41, BufferConstant.Variable_43); + Variable_38 = FMath::Clamp(Variable_38, -0.790554, 0.781474); + + // - + v_flt Variable_33; // - output 0 + Variable_33 = Variable_16 - v_flt(500.0f); + + // Normalize./ + v_flt Variable_76; // Normalize./ output 0 + Variable_76 = Variable_19 / Variable_74; + + // Normalize./ + v_flt Variable_77; // Normalize./ output 0 + Variable_77 = Variable_20 / Variable_74; + + // * -1 + v_flt Variable_12; // * -1 output 0 + Variable_12 = Variable_13 * -1; + + // 3D Perlin Noise + v_flt Variable_29; // 3D Perlin Noise output 0 + Variable_29 = _3D_Perlin_Noise_2_Noise.GetPerlin_3D(Variable_30, Variable_31, Variable_32, BufferConstant.Variable_43); + Variable_29 = FMath::Clamp(Variable_29, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_25; // 3D Perlin Noise output 0 + Variable_25 = _3D_Perlin_Noise_3_Noise.GetPerlin_3D(Variable_26, Variable_27, Variable_28, BufferConstant.Variable_43); + Variable_25 = FMath::Clamp(Variable_25, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_17; // 3D Perlin Noise output 0 + Variable_17 = _3D_Perlin_Noise_4_Noise.GetPerlin_3D(Variable_75, Variable_76, Variable_77, v_flt(5.0f)); + Variable_17 = FMath::Clamp(Variable_17, -0.911908, 0.945800); + + // Cave Layer.* + v_flt Variable_56; // Cave Layer.* output 0 + Variable_56 = Variable_25 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_63; // Cave Layer.* output 0 + Variable_63 = Variable_21 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_71; // Cave Layer.* output 0 + Variable_71 = Variable_29 * v_flt(50.0f); + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_33 - v_flt(75.0f); + + // + + v_flt Variable_37; // + output 0 + Variable_37 = v_flt(300.0f) + Variable_33; + + // 2D Noise SDF.* + v_flt Variable_79; // 2D Noise SDF.* output 0 + Variable_79 = Variable_38 * v_flt(50.0f); + + // + + v_flt Variable_1; // + output 0 + Variable_1 = v_flt(100.0f) + Variable_33; + + // + + v_flt Variable_3; // + output 0 + Variable_3 = v_flt(200.0f) + Variable_33; + + // Cave Layer.1 - X + v_flt Variable_60; // Cave Layer.1 - X output 0 + Variable_60 = 1 - Variable_63; + + // Cave Layer.- + v_flt Variable_55; // Cave Layer.- output 0 + Variable_55 = Variable_56 - Variable_1; + + // Float Curve: + v_flt Variable_7; // Float Curve: output 0 + Variable_7 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_17); + + // Cave Layer.1 - X + v_flt Variable_53; // Cave Layer.1 - X output 0 + Variable_53 = 1 - Variable_56; + + // Cave Layer.- + v_flt Variable_62; // Cave Layer.- output 0 + Variable_62 = Variable_63 - Variable_33; + + // 2D Noise SDF.+ + v_flt Variable_34; // 2D Noise SDF.+ output 0 + Variable_34 = Variable_79 + v_flt(0.0f); + + // Cave Layer.1 - X + v_flt Variable_68; // Cave Layer.1 - X output 0 + Variable_68 = 1 - Variable_71; + + // Cave Layer.- + v_flt Variable_70; // Cave Layer.- output 0 + Variable_70 = Variable_71 - Variable_3; + + // Cave Layer.* + v_flt Variable_73; // Cave Layer.* output 0 + Variable_73 = v_flt(0.2f) * Variable_68; + + // Cave Layer.* + v_flt Variable_58; // Cave Layer.* output 0 + Variable_58 = v_flt(0.2f) * Variable_53; + + // 2D Noise SDF.- + v_flt Variable_78; // 2D Noise SDF.- output 0 + Variable_78 = Variable_37 - Variable_34; + + // Cave Layer.* + v_flt Variable_65; // Cave Layer.* output 0 + Variable_65 = v_flt(0.2f) * Variable_60; + + // * + v_flt Variable_5; // * output 0 + Variable_5 = Variable_7 * v_flt(100.0f); + + // Cave Layer.- + v_flt Variable_69; // Cave Layer.- output 0 + Variable_69 = Variable_3 - Variable_73; + + // Cave Layer.- + v_flt Variable_54; // Cave Layer.- output 0 + Variable_54 = Variable_1 - Variable_58; + + // * -1 + v_flt Variable_42; // * -1 output 0 + Variable_42 = Variable_78 * -1; + + // Cave Layer.- + v_flt Variable_61; // Cave Layer.- output 0 + Variable_61 = Variable_33 - Variable_65; + + // - + v_flt Variable_6; // - output 0 + Variable_6 = Variable_4 - Variable_5; + + // Cave Layer.Smooth Union + v_flt Variable_67; // Cave Layer.Smooth Union output 0 + Variable_67 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_69, Variable_70, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_59; // Cave Layer.Smooth Union output 0 + Variable_59 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_61, Variable_62, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_52; // Cave Layer.Smooth Union output 0 + Variable_52 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_54, Variable_55, v_flt(100.0f)); + + // Cave Layer.+ + v_flt Variable_64; // Cave Layer.+ output 0 + Variable_64 = Variable_59 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_57; // Cave Layer.+ output 0 + Variable_57 = Variable_52 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_72; // Cave Layer.+ output 0 + Variable_72 = Variable_67 + v_flt(25.0f); + + // Max (float) + v_flt Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_6, FVoxelNodeFunctions::Max(Variable_64, FVoxelNodeFunctions::Max(Variable_57, FVoxelNodeFunctions::Max(Variable_72, Variable_42)))); + + // Smooth Intersection + v_flt Variable_11; // Smooth Intersection output 0 + Variable_11 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_12, v_flt(5.0f)); + + // Max (float) + v_flt Variable_36; // Max (float) output 0 + Variable_36 = FVoxelNodeFunctions::Max(Variable_35, Variable_11); + + // Switch (float) + v_flt Variable_50; // Switch (float) output 0 + Variable_50 = FVoxelNodeFunctions::Switch(Variable_36, Variable_11, BufferConstant.Variable_51); + + // Set High Quality Value.* + v_flt Variable_66; // Set High Quality Value.* output 0 + Variable_66 = Variable_50 * v_flt(0.2f); + + Outputs.Value = Variable_66; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_1; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // Y output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_1 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_2 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_2 = Context.GetLocalY(); + + // X + BufferX.Variable_1 = Context.GetLocalX(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_1, BufferXY.Variable_2, Variable_0); + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_3 - v_flt(500.0f); + + // <= + bool Variable_7; // <= output 0 + Variable_7 = Variable_4 <= v_flt(50.0f); + + // <= + bool Variable_8; // <= output 0 + Variable_8 = Variable_4 <= v_flt(-40.0f); + + // <= + bool Variable_10; // <= output 0 + Variable_10 = Variable_4 <= v_flt(-250.0f); + + // Switch (color) + FColor Variable_5; // Switch (color) output 0 + Variable_5 = FVoxelNodeFunctions::Switch(FColor(FColor(14, 71, 255, 255)), FColor(FColor(229, 0, 255, 255)), Variable_7); + + // Switch (color) + FColor Variable_6; // Switch (color) output 0 + Variable_6 = FVoxelNodeFunctions::Switch(FColor(FColor(255, 142, 44, 255)), Variable_5, Variable_8); + + // Switch (color) + FColor Variable_9; // Switch (color) output 0 + Variable_9 = FVoxelNodeFunctions::Switch(FColor(FColor(3, 143, 0, 255)), Variable_6, Variable_10); + + Outputs.MaterialBuilder.SetColor(Variable_9); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Y + v_flt Variable_2; // Y output 0 + Variable_2 = Context.GetLocalY(); + + // X + v_flt Variable_1; // X output 0 + Variable_1 = Context.GetLocalX(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_1, Variable_2, Variable_0); + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_3 - v_flt(500.0f); + + // <= + bool Variable_7; // <= output 0 + Variable_7 = Variable_4 <= v_flt(50.0f); + + // <= + bool Variable_8; // <= output 0 + Variable_8 = Variable_4 <= v_flt(-40.0f); + + // <= + bool Variable_10; // <= output 0 + Variable_10 = Variable_4 <= v_flt(-250.0f); + + // Switch (color) + FColor Variable_5; // Switch (color) output 0 + Variable_5 = FVoxelNodeFunctions::Switch(FColor(FColor(14, 71, 255, 255)), FColor(FColor(229, 0, 255, 255)), Variable_7); + + // Switch (color) + FColor Variable_6; // Switch (color) output 0 + Variable_6 = FVoxelNodeFunctions::Switch(FColor(FColor(255, 142, 44, 255)), Variable_5, Variable_8); + + // Switch (color) + FColor Variable_9; // Switch (color) output 0 + Variable_9 = FVoxelNodeFunctions::Switch(FColor(FColor(3, 143, 0, 255)), Variable_6, Variable_10); + + Outputs.MaterialBuilder.SetColor(Variable_9); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_1; // Set Sphere Up Vector.X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // Set Sphere Up Vector.Y output 0 + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Set Sphere Up Vector.X + BufferX.Variable_1 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Set Sphere Up Vector.Y + BufferXY.Variable_2 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Set Sphere Up Vector.X + BufferX.Variable_1 = Context.GetLocalX(); + + // Set Sphere Up Vector.Y + BufferXY.Variable_2 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Set Sphere Up Vector.Z + v_flt Variable_0; // Set Sphere Up Vector.Z output 0 + Variable_0 = Context.GetLocalZ(); + + Outputs.UpVectorX = BufferX.Variable_1; + Outputs.UpVectorY = BufferXY.Variable_2; + Outputs.UpVectorZ = Variable_0; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Set Sphere Up Vector.X + v_flt Variable_1; // Set Sphere Up Vector.X output 0 + Variable_1 = Context.GetLocalX(); + + // Set Sphere Up Vector.Y + v_flt Variable_2; // Set Sphere Up Vector.Y output 0 + Variable_2 = Context.GetLocalY(); + + // Set Sphere Up Vector.Z + v_flt Variable_0; // Set Sphere Up Vector.Z output 0 + Variable_0 = Context.GetLocalZ(); + + Outputs.UpVectorX = Variable_1; + Outputs.UpVectorY = Variable_2; + Outputs.UpVectorZ = Variable_0; + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + FVoxelBoolRange Variable_29; // Slice Mode = False output 0 + TVoxelRange Variable_49; // Cave Layer.* output 0 + TVoxelRange Variable_34; // Cave Layer.* output 0 + TVoxelRange Variable_41; // Cave Layer.* output 0 + TVoxelRange Variable_5; // * output 0 + TVoxelRange Variable_22; // 2D Noise SDF.+ output 0 + TVoxelRange Variable_51; // Cave Layer.* output 0 + TVoxelRange Variable_36; // Cave Layer.* output 0 + TVoxelRange Variable_43; // Cave Layer.* output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_23; // X output 0 + TVoxelRange Variable_8; // X output 0 + TVoxelRange Variable_14; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_9; // Y output 0 + TVoxelRange Variable_15; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_5_Noise.SetSeed(FVoxelGraphSeed(13339)); + _3D_Perlin_Noise_5_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_6_Noise.SetSeed(FVoxelGraphSeed(1340)); + _3D_Perlin_Noise_6_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_7_Noise.SetSeed(FVoxelGraphSeed(1330)); + _3D_Perlin_Noise_7_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_8_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_8_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_9_Noise.SetSeed(FVoxelGraphSeed(1339)); + _3D_Perlin_Noise_9_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 3D Perlin Noise + TVoxelRange Variable_26; // 3D Perlin Noise output 0 + Variable_26 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_17; // 3D Perlin Noise output 0 + Variable_17 = { -0.911908f, 0.945800f }; + + // 3D Perlin Noise + TVoxelRange Variable_18; // 3D Perlin Noise output 0 + Variable_18 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_19; // 3D Perlin Noise output 0 + Variable_19 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_20; // 3D Perlin Noise output 0 + Variable_20 = { -0.790554f, 0.781474f }; + + // Slice Mode = False + BufferConstant.Variable_29 = Params.Slice_Mode; + + // Cave Layer.* + BufferConstant.Variable_49 = Variable_20 * TVoxelRange(50.0f); + + // Float Curve: + TVoxelRange Variable_7; // Float Curve: output 0 + Variable_7 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_17); + + // 2D Noise SDF.* + TVoxelRange Variable_53; // 2D Noise SDF.* output 0 + Variable_53 = Variable_26 * TVoxelRange(50.0f); + + // Cave Layer.* + BufferConstant.Variable_34 = Variable_19 * TVoxelRange(50.0f); + + // Cave Layer.* + BufferConstant.Variable_41 = Variable_18 * TVoxelRange(50.0f); + + // Cave Layer.1 - X + TVoxelRange Variable_38; // Cave Layer.1 - X output 0 + Variable_38 = 1 - BufferConstant.Variable_41; + + // * + BufferConstant.Variable_5 = Variable_7 * TVoxelRange(100.0f); + + // 2D Noise SDF.+ + BufferConstant.Variable_22 = Variable_53 + TVoxelRange(0.0f); + + // Cave Layer.1 - X + TVoxelRange Variable_31; // Cave Layer.1 - X output 0 + Variable_31 = 1 - BufferConstant.Variable_34; + + // Cave Layer.1 - X + TVoxelRange Variable_46; // Cave Layer.1 - X output 0 + Variable_46 = 1 - BufferConstant.Variable_49; + + // Cave Layer.* + BufferConstant.Variable_51 = TVoxelRange(0.2f) * Variable_46; + + // Cave Layer.* + BufferConstant.Variable_36 = TVoxelRange(0.2f) * Variable_31; + + // Cave Layer.* + BufferConstant.Variable_43 = TVoxelRange(0.2f) * Variable_38; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_5_Noise; + FVoxelFastNoise _3D_Perlin_Noise_6_Noise; + FVoxelFastNoise _3D_Perlin_Noise_7_Noise; + FVoxelFastNoise _3D_Perlin_Noise_8_Noise; + FVoxelFastNoise _3D_Perlin_Noise_9_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_10; // Z output 0 + Variable_10 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_9; // Y output 0 + Variable_9 = Context.GetLocalY(); + + // X + TVoxelRange Variable_23; // X output 0 + Variable_23 = Context.GetLocalX(); + + // X + TVoxelRange Variable_8; // X output 0 + Variable_8 = Context.GetLocalX(); + + // Y + TVoxelRange Variable_15; // Y output 0 + Variable_15 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // X + TVoxelRange Variable_14; // X output 0 + Variable_14 = Context.GetLocalX(); + + // Data Item Sample + TVoxelRange Variable_13; // Data Item Sample output 0 + Variable_13 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_8, Variable_9, Variable_10, TVoxelRange(5.0f), TVoxelRange(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // Vector Length + TVoxelRange Variable_16; // Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(Variable_14, Variable_15, Variable_0); + + // - + TVoxelRange Variable_21; // - output 0 + Variable_21 = Variable_16 - TVoxelRange(500.0f); + + // * -1 + TVoxelRange Variable_12; // * -1 output 0 + Variable_12 = Variable_13 * -1; + + // - + TVoxelRange Variable_4; // - output 0 + Variable_4 = Variable_21 - TVoxelRange(75.0f); + + // + + TVoxelRange Variable_3; // + output 0 + Variable_3 = TVoxelRange(200.0f) + Variable_21; + + // + + TVoxelRange Variable_1; // + output 0 + Variable_1 = TVoxelRange(100.0f) + Variable_21; + + // + + TVoxelRange Variable_25; // + output 0 + Variable_25 = TVoxelRange(300.0f) + Variable_21; + + // Cave Layer.- + TVoxelRange Variable_39; // Cave Layer.- output 0 + Variable_39 = Variable_21 - BufferConstant.Variable_43; + + // Cave Layer.- + TVoxelRange Variable_40; // Cave Layer.- output 0 + Variable_40 = BufferConstant.Variable_41 - Variable_21; + + // - + TVoxelRange Variable_6; // - output 0 + Variable_6 = Variable_4 - BufferConstant.Variable_5; + + // Cave Layer.Smooth Union + TVoxelRange Variable_37; // Cave Layer.Smooth Union output 0 + Variable_37 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_39, Variable_40, TVoxelRange(100.0f)); + + // Cave Layer.- + TVoxelRange Variable_32; // Cave Layer.- output 0 + Variable_32 = Variable_1 - BufferConstant.Variable_36; + + // Cave Layer.- + TVoxelRange Variable_47; // Cave Layer.- output 0 + Variable_47 = Variable_3 - BufferConstant.Variable_51; + + // Cave Layer.- + TVoxelRange Variable_33; // Cave Layer.- output 0 + Variable_33 = BufferConstant.Variable_34 - Variable_1; + + // Cave Layer.- + TVoxelRange Variable_48; // Cave Layer.- output 0 + Variable_48 = BufferConstant.Variable_49 - Variable_3; + + // 2D Noise SDF.- + TVoxelRange Variable_52; // 2D Noise SDF.- output 0 + Variable_52 = Variable_25 - BufferConstant.Variable_22; + + // Cave Layer.+ + TVoxelRange Variable_42; // Cave Layer.+ output 0 + Variable_42 = Variable_37 + TVoxelRange(25.0f); + + // Cave Layer.Smooth Union + TVoxelRange Variable_30; // Cave Layer.Smooth Union output 0 + Variable_30 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_32, Variable_33, TVoxelRange(100.0f)); + + // * -1 + TVoxelRange Variable_27; // * -1 output 0 + Variable_27 = Variable_52 * -1; + + // Cave Layer.Smooth Union + TVoxelRange Variable_45; // Cave Layer.Smooth Union output 0 + Variable_45 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_47, Variable_48, TVoxelRange(100.0f)); + + // Cave Layer.+ + TVoxelRange Variable_50; // Cave Layer.+ output 0 + Variable_50 = Variable_45 + TVoxelRange(25.0f); + + // Cave Layer.+ + TVoxelRange Variable_35; // Cave Layer.+ output 0 + Variable_35 = Variable_30 + TVoxelRange(25.0f); + + // Max (float) + TVoxelRange Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_6, FVoxelNodeFunctions::Max(Variable_42, FVoxelNodeFunctions::Max(Variable_35, FVoxelNodeFunctions::Max(Variable_50, Variable_27)))); + + // Smooth Intersection + TVoxelRange Variable_11; // Smooth Intersection output 0 + Variable_11 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_12, TVoxelRange(5.0f)); + + // Max (float) + TVoxelRange Variable_24; // Max (float) output 0 + Variable_24 = FVoxelNodeFunctions::Max(Variable_23, Variable_11); + + // Switch (float) + TVoxelRange Variable_28; // Switch (float) output 0 + Variable_28 = FVoxelNodeFunctions::Switch(Variable_24, Variable_11, BufferConstant.Variable_29); + + // Set High Quality Value.* + TVoxelRange Variable_44; // Set High Quality Value.* output 0 + Variable_44 = Variable_28 * TVoxelRange(0.2f); + + Outputs.Value = Variable_44; + } + + }; + + FVoxelExample_LayeredPlanetInstance(UVoxelExample_LayeredPlanet& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Frequency, + FVoxelRichCurve(Object.None1.LoadSynchronous()), + Object.Seed, + Object.Slice_Mode + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_LayeredPlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_LayeredPlanetInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_LayeredPlanetInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_LayeredPlanetInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_LayeredPlanetInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_LayeredPlanet::UVoxelExample_LayeredPlanet() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_LayeredPlanet::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_LayeredPlanet. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_LayeredPlanet. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_LayeredPlanet. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_LayeredPlanet. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.h new file mode 100644 index 00000000..167738a9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.h @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_LayeredPlanet.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_LayeredPlanet : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Frequency")) + float Frequency = 0.005; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName)) + TSoftObjectPtr None1 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.VoxelExample_LayeredWorld_Curve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 4761; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Slice Mode")) + bool Slice_Mode = false; + + UVoxelExample_LayeredPlanet(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.cpp new file mode 100644 index 00000000..3ec83299 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.cpp @@ -0,0 +1,1592 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_LayeredWorld.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_LayeredWorldInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Frequency; + const FVoxelRichCurve None1; + const int32 Seed; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_35; // Frequency = 0.005 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_9; // X output 0 + v_flt Variable_31; // X output 0 + v_flt Variable_22; // X output 0 + v_flt Variable_15; // X output 0 + v_flt Variable_18; // X output 0 + v_flt Variable_26; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_32; // Y output 0 + v_flt Variable_10; // Y output 0 + v_flt Variable_27; // Y output 0 + v_flt Variable_19; // Y output 0 + v_flt Variable_23; // Y output 0 + v_flt Variable_6; // * output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Seed = 4761 + FVoxelGraphSeed Variable_42; // Seed = 4761 output 0 + Variable_42 = Params.Seed; + + // Init of Make Seeds + FVoxelGraphSeed Variable_37; // Make Seeds output 0 + FVoxelGraphSeed Variable_38; // Make Seeds output 1 + FVoxelGraphSeed Variable_39; // Make Seeds output 2 + FVoxelGraphSeed Variable_40; // Make Seeds output 3 + FVoxelGraphSeed Variable_41; // Make Seeds output 4 + Variable_37 = FVoxelUtilities::MurmurHash32(Variable_42); + Variable_38 = FVoxelUtilities::MurmurHash32(Variable_37); + Variable_39 = FVoxelUtilities::MurmurHash32(Variable_38); + Variable_40 = FVoxelUtilities::MurmurHash32(Variable_39); + Variable_41 = FVoxelUtilities::MurmurHash32(Variable_40); + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Frequency = 0.005 + BufferConstant.Variable_35 = Params.Frequency; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_0_Noise; + FVoxelFastNoise _3D_Perlin_Noise_0_Noise; + FVoxelFastNoise _3D_Perlin_Noise_1_Noise; + FVoxelFastNoise _3D_Perlin_Noise_2_Noise; + FVoxelFastNoise _3D_Perlin_Noise_3_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Seed = 4761 + FVoxelGraphSeed Variable_42; // Seed = 4761 output 0 + Variable_42 = Params.Seed; + + // Init of Make Seeds + FVoxelGraphSeed Variable_37; // Make Seeds output 0 + FVoxelGraphSeed Variable_38; // Make Seeds output 1 + FVoxelGraphSeed Variable_39; // Make Seeds output 2 + FVoxelGraphSeed Variable_40; // Make Seeds output 3 + FVoxelGraphSeed Variable_41; // Make Seeds output 4 + Variable_37 = FVoxelUtilities::MurmurHash32(Variable_42); + Variable_38 = FVoxelUtilities::MurmurHash32(Variable_37); + Variable_39 = FVoxelUtilities::MurmurHash32(Variable_38); + Variable_40 = FVoxelUtilities::MurmurHash32(Variable_39); + Variable_41 = FVoxelUtilities::MurmurHash32(Variable_40); + + // Init of 2D Perlin Noise + _2D_Perlin_Noise_0_Noise.SetSeed(Variable_37); + _2D_Perlin_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_0_Noise.SetSeed(Variable_38); + _3D_Perlin_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_1_Noise.SetSeed(Variable_39); + _3D_Perlin_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_2_Noise.SetSeed(Variable_41); + _3D_Perlin_Noise_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_3_Noise.SetSeed(Variable_40); + _3D_Perlin_Noise_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_9 = Context.GetLocalX(); + + // X + BufferX.Variable_31 = Context.GetLocalX(); + + // X + BufferX.Variable_22 = Context.GetLocalX(); + + // X + BufferX.Variable_15 = Context.GetLocalX(); + + // X + BufferX.Variable_18 = Context.GetLocalX(); + + // X + BufferX.Variable_26 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_32 = Context.GetLocalY(); + + // Y + BufferXY.Variable_10 = Context.GetLocalY(); + + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // Y + BufferXY.Variable_27 = Context.GetLocalY(); + + // Y + BufferXY.Variable_19 = Context.GetLocalY(); + + // Y + BufferXY.Variable_23 = Context.GetLocalY(); + + // 2D Perlin Noise + v_flt Variable_36; // 2D Perlin Noise output 0 + Variable_36 = _2D_Perlin_Noise_0_Noise.GetPerlin_2D(BufferX.Variable_15, Variable_16, v_flt(0.01f)); + Variable_36 = FMath::Clamp(Variable_36, -0.767493, 0.745772); + + // Float Curve: + v_flt Variable_8; // Float Curve: output 0 + Variable_8 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_36); + + // * + BufferXY.Variable_6 = Variable_8 * v_flt(100.0f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_32 = Context.GetLocalY(); + + // Y + BufferXY.Variable_10 = Context.GetLocalY(); + + // X + BufferX.Variable_9 = Context.GetLocalX(); + + // X + BufferX.Variable_31 = Context.GetLocalX(); + + // X + BufferX.Variable_22 = Context.GetLocalX(); + + // X + BufferX.Variable_15 = Context.GetLocalX(); + + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // Y + BufferXY.Variable_27 = Context.GetLocalY(); + + // X + BufferX.Variable_18 = Context.GetLocalX(); + + // Y + BufferXY.Variable_19 = Context.GetLocalY(); + + // X + BufferX.Variable_26 = Context.GetLocalX(); + + // Y + BufferXY.Variable_23 = Context.GetLocalY(); + + // 2D Perlin Noise + v_flt Variable_36; // 2D Perlin Noise output 0 + Variable_36 = _2D_Perlin_Noise_0_Noise.GetPerlin_2D(BufferX.Variable_15, Variable_16, v_flt(0.01f)); + Variable_36 = FMath::Clamp(Variable_36, -0.767493, 0.745772); + + // Float Curve: + v_flt Variable_8; // Float Curve: output 0 + Variable_8 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_36); + + // * + BufferXY.Variable_6 = Variable_8 * v_flt(100.0f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // Z + v_flt Variable_33; // Z output 0 + Variable_33 = Context.GetLocalZ(); + + // Z + v_flt Variable_28; // Z output 0 + Variable_28 = Context.GetLocalZ(); + + // Z + v_flt Variable_20; // Z output 0 + Variable_20 = Context.GetLocalZ(); + + // + + v_flt Variable_1; // + output 0 + Variable_1 = v_flt(100.0f) + Variable_0; + + // Data Item Sample + v_flt Variable_14; // Data Item Sample output 0 + Variable_14 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, BufferX.Variable_9, BufferXY.Variable_10, Variable_11, v_flt(5.0f), v_flt(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // - + v_flt Variable_5; // - output 0 + Variable_5 = Variable_0 - v_flt(75.0f); + + // + + v_flt Variable_4; // + output 0 + Variable_4 = v_flt(200.0f) + Variable_0; + + // + + v_flt Variable_29; // + output 0 + Variable_29 = v_flt(300.0f) + Variable_0; + + // 3D Perlin Noise + v_flt Variable_17; // 3D Perlin Noise output 0 + Variable_17 = _3D_Perlin_Noise_0_Noise.GetPerlin_3D(BufferX.Variable_18, BufferXY.Variable_19, Variable_20, BufferConstant.Variable_35); + Variable_17 = FMath::Clamp(Variable_17, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_21; // 3D Perlin Noise output 0 + Variable_21 = _3D_Perlin_Noise_1_Noise.GetPerlin_3D(BufferX.Variable_22, BufferXY.Variable_23, Variable_24, BufferConstant.Variable_35); + Variable_21 = FMath::Clamp(Variable_21, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_30; // 3D Perlin Noise output 0 + Variable_30 = _3D_Perlin_Noise_2_Noise.GetPerlin_3D(BufferX.Variable_31, BufferXY.Variable_32, Variable_33, BufferConstant.Variable_35); + Variable_30 = FMath::Clamp(Variable_30, -0.790554, 0.781474); + + // * -1 + v_flt Variable_13; // * -1 output 0 + Variable_13 = Variable_14 * -1; + + // 3D Perlin Noise + v_flt Variable_25; // 3D Perlin Noise output 0 + Variable_25 = _3D_Perlin_Noise_3_Noise.GetPerlin_3D(BufferX.Variable_26, BufferXY.Variable_27, Variable_28, BufferConstant.Variable_35); + Variable_25 = FMath::Clamp(Variable_25, -0.790554, 0.781474); + + // Cave Layer.* + v_flt Variable_47; // Cave Layer.* output 0 + Variable_47 = Variable_21 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_62; // Cave Layer.* output 0 + Variable_62 = Variable_25 * v_flt(50.0f); + + // 2D Noise SDF.* + v_flt Variable_66; // 2D Noise SDF.* output 0 + Variable_66 = Variable_30 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_54; // Cave Layer.* output 0 + Variable_54 = Variable_17 * v_flt(50.0f); + + // Cave Layer.1 - X + v_flt Variable_44; // Cave Layer.1 - X output 0 + Variable_44 = 1 - Variable_47; + + // Cave Layer.- + v_flt Variable_46; // Cave Layer.- output 0 + Variable_46 = Variable_47 - Variable_1; + + // Cave Layer.1 - X + v_flt Variable_51; // Cave Layer.1 - X output 0 + Variable_51 = 1 - Variable_54; + + // Cave Layer.- + v_flt Variable_53; // Cave Layer.- output 0 + Variable_53 = Variable_54 - Variable_0; + + // 2D Noise SDF.+ + v_flt Variable_3; // 2D Noise SDF.+ output 0 + Variable_3 = Variable_66 + v_flt(0.0f); + + // Cave Layer.1 - X + v_flt Variable_59; // Cave Layer.1 - X output 0 + Variable_59 = 1 - Variable_62; + + // Cave Layer.- + v_flt Variable_61; // Cave Layer.- output 0 + Variable_61 = Variable_62 - Variable_4; + + // Cave Layer.* + v_flt Variable_49; // Cave Layer.* output 0 + Variable_49 = v_flt(0.2f) * Variable_44; + + // Cave Layer.* + v_flt Variable_56; // Cave Layer.* output 0 + Variable_56 = v_flt(0.2f) * Variable_51; + + // Cave Layer.* + v_flt Variable_64; // Cave Layer.* output 0 + Variable_64 = v_flt(0.2f) * Variable_59; + + // 2D Noise SDF.- + v_flt Variable_65; // 2D Noise SDF.- output 0 + Variable_65 = Variable_29 - Variable_3; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_5 - BufferXY.Variable_6; + + // * -1 + v_flt Variable_34; // * -1 output 0 + Variable_34 = Variable_65 * -1; + + // Cave Layer.- + v_flt Variable_52; // Cave Layer.- output 0 + Variable_52 = Variable_0 - Variable_56; + + // Cave Layer.- + v_flt Variable_60; // Cave Layer.- output 0 + Variable_60 = Variable_4 - Variable_64; + + // Cave Layer.- + v_flt Variable_45; // Cave Layer.- output 0 + Variable_45 = Variable_1 - Variable_49; + + // Cave Layer.Smooth Union + v_flt Variable_50; // Cave Layer.Smooth Union output 0 + Variable_50 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_52, Variable_53, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_43; // Cave Layer.Smooth Union output 0 + Variable_43 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_45, Variable_46, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_58; // Cave Layer.Smooth Union output 0 + Variable_58 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_60, Variable_61, v_flt(100.0f)); + + // Cave Layer.+ + v_flt Variable_55; // Cave Layer.+ output 0 + Variable_55 = Variable_50 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_63; // Cave Layer.+ output 0 + Variable_63 = Variable_58 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_48; // Cave Layer.+ output 0 + Variable_48 = Variable_43 + v_flt(25.0f); + + // Max (float) + v_flt Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_7, FVoxelNodeFunctions::Max(Variable_55, FVoxelNodeFunctions::Max(Variable_48, FVoxelNodeFunctions::Max(Variable_63, Variable_34)))); + + // Smooth Intersection + v_flt Variable_12; // Smooth Intersection output 0 + Variable_12 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_13, v_flt(5.0f)); + + // Set High Quality Value.* + v_flt Variable_57; // Set High Quality Value.* output 0 + Variable_57 = Variable_12 * v_flt(0.2f); + + Outputs.Value = Variable_57; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Y + v_flt Variable_32; // Y output 0 + Variable_32 = Context.GetLocalY(); + + // Z + v_flt Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // Y + v_flt Variable_10; // Y output 0 + Variable_10 = Context.GetLocalY(); + + // X + v_flt Variable_9; // X output 0 + Variable_9 = Context.GetLocalX(); + + // X + v_flt Variable_31; // X output 0 + Variable_31 = Context.GetLocalX(); + + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // Z + v_flt Variable_24; // Z output 0 + Variable_24 = Context.GetLocalZ(); + + // Z + v_flt Variable_33; // Z output 0 + Variable_33 = Context.GetLocalZ(); + + // X + v_flt Variable_22; // X output 0 + Variable_22 = Context.GetLocalX(); + + // X + v_flt Variable_15; // X output 0 + Variable_15 = Context.GetLocalX(); + + // Y + v_flt Variable_16; // Y output 0 + Variable_16 = Context.GetLocalY(); + + // Z + v_flt Variable_28; // Z output 0 + Variable_28 = Context.GetLocalZ(); + + // Y + v_flt Variable_27; // Y output 0 + Variable_27 = Context.GetLocalY(); + + // Z + v_flt Variable_20; // Z output 0 + Variable_20 = Context.GetLocalZ(); + + // X + v_flt Variable_18; // X output 0 + Variable_18 = Context.GetLocalX(); + + // Y + v_flt Variable_19; // Y output 0 + Variable_19 = Context.GetLocalY(); + + // X + v_flt Variable_26; // X output 0 + Variable_26 = Context.GetLocalX(); + + // Y + v_flt Variable_23; // Y output 0 + Variable_23 = Context.GetLocalY(); + + // + + v_flt Variable_1; // + output 0 + Variable_1 = v_flt(100.0f) + Variable_0; + + // Data Item Sample + v_flt Variable_14; // Data Item Sample output 0 + Variable_14 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_9, Variable_10, Variable_11, v_flt(5.0f), v_flt(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // - + v_flt Variable_5; // - output 0 + Variable_5 = Variable_0 - v_flt(75.0f); + + // + + v_flt Variable_4; // + output 0 + Variable_4 = v_flt(200.0f) + Variable_0; + + // + + v_flt Variable_29; // + output 0 + Variable_29 = v_flt(300.0f) + Variable_0; + + // 2D Perlin Noise + v_flt Variable_36; // 2D Perlin Noise output 0 + Variable_36 = _2D_Perlin_Noise_0_Noise.GetPerlin_2D(Variable_15, Variable_16, v_flt(0.01f)); + Variable_36 = FMath::Clamp(Variable_36, -0.767493, 0.745772); + + // 3D Perlin Noise + v_flt Variable_17; // 3D Perlin Noise output 0 + Variable_17 = _3D_Perlin_Noise_0_Noise.GetPerlin_3D(Variable_18, Variable_19, Variable_20, BufferConstant.Variable_35); + Variable_17 = FMath::Clamp(Variable_17, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_21; // 3D Perlin Noise output 0 + Variable_21 = _3D_Perlin_Noise_1_Noise.GetPerlin_3D(Variable_22, Variable_23, Variable_24, BufferConstant.Variable_35); + Variable_21 = FMath::Clamp(Variable_21, -0.790554, 0.781474); + + // 3D Perlin Noise + v_flt Variable_30; // 3D Perlin Noise output 0 + Variable_30 = _3D_Perlin_Noise_2_Noise.GetPerlin_3D(Variable_31, Variable_32, Variable_33, BufferConstant.Variable_35); + Variable_30 = FMath::Clamp(Variable_30, -0.790554, 0.781474); + + // * -1 + v_flt Variable_13; // * -1 output 0 + Variable_13 = Variable_14 * -1; + + // 3D Perlin Noise + v_flt Variable_25; // 3D Perlin Noise output 0 + Variable_25 = _3D_Perlin_Noise_3_Noise.GetPerlin_3D(Variable_26, Variable_27, Variable_28, BufferConstant.Variable_35); + Variable_25 = FMath::Clamp(Variable_25, -0.790554, 0.781474); + + // Float Curve: + v_flt Variable_8; // Float Curve: output 0 + Variable_8 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_36); + + // Cave Layer.* + v_flt Variable_47; // Cave Layer.* output 0 + Variable_47 = Variable_21 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_62; // Cave Layer.* output 0 + Variable_62 = Variable_25 * v_flt(50.0f); + + // 2D Noise SDF.* + v_flt Variable_66; // 2D Noise SDF.* output 0 + Variable_66 = Variable_30 * v_flt(50.0f); + + // Cave Layer.* + v_flt Variable_54; // Cave Layer.* output 0 + Variable_54 = Variable_17 * v_flt(50.0f); + + // Cave Layer.1 - X + v_flt Variable_44; // Cave Layer.1 - X output 0 + Variable_44 = 1 - Variable_47; + + // Cave Layer.- + v_flt Variable_46; // Cave Layer.- output 0 + Variable_46 = Variable_47 - Variable_1; + + // Cave Layer.1 - X + v_flt Variable_51; // Cave Layer.1 - X output 0 + Variable_51 = 1 - Variable_54; + + // Cave Layer.- + v_flt Variable_53; // Cave Layer.- output 0 + Variable_53 = Variable_54 - Variable_0; + + // 2D Noise SDF.+ + v_flt Variable_3; // 2D Noise SDF.+ output 0 + Variable_3 = Variable_66 + v_flt(0.0f); + + // Cave Layer.1 - X + v_flt Variable_59; // Cave Layer.1 - X output 0 + Variable_59 = 1 - Variable_62; + + // Cave Layer.- + v_flt Variable_61; // Cave Layer.- output 0 + Variable_61 = Variable_62 - Variable_4; + + // * + v_flt Variable_6; // * output 0 + Variable_6 = Variable_8 * v_flt(100.0f); + + // Cave Layer.* + v_flt Variable_49; // Cave Layer.* output 0 + Variable_49 = v_flt(0.2f) * Variable_44; + + // Cave Layer.* + v_flt Variable_56; // Cave Layer.* output 0 + Variable_56 = v_flt(0.2f) * Variable_51; + + // Cave Layer.* + v_flt Variable_64; // Cave Layer.* output 0 + Variable_64 = v_flt(0.2f) * Variable_59; + + // 2D Noise SDF.- + v_flt Variable_65; // 2D Noise SDF.- output 0 + Variable_65 = Variable_29 - Variable_3; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_5 - Variable_6; + + // * -1 + v_flt Variable_34; // * -1 output 0 + Variable_34 = Variable_65 * -1; + + // Cave Layer.- + v_flt Variable_52; // Cave Layer.- output 0 + Variable_52 = Variable_0 - Variable_56; + + // Cave Layer.- + v_flt Variable_60; // Cave Layer.- output 0 + Variable_60 = Variable_4 - Variable_64; + + // Cave Layer.- + v_flt Variable_45; // Cave Layer.- output 0 + Variable_45 = Variable_1 - Variable_49; + + // Cave Layer.Smooth Union + v_flt Variable_50; // Cave Layer.Smooth Union output 0 + Variable_50 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_52, Variable_53, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_43; // Cave Layer.Smooth Union output 0 + Variable_43 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_45, Variable_46, v_flt(100.0f)); + + // Cave Layer.Smooth Union + v_flt Variable_58; // Cave Layer.Smooth Union output 0 + Variable_58 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_60, Variable_61, v_flt(100.0f)); + + // Cave Layer.+ + v_flt Variable_55; // Cave Layer.+ output 0 + Variable_55 = Variable_50 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_63; // Cave Layer.+ output 0 + Variable_63 = Variable_58 + v_flt(25.0f); + + // Cave Layer.+ + v_flt Variable_48; // Cave Layer.+ output 0 + Variable_48 = Variable_43 + v_flt(25.0f); + + // Max (float) + v_flt Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_7, FVoxelNodeFunctions::Max(Variable_55, FVoxelNodeFunctions::Max(Variable_48, FVoxelNodeFunctions::Max(Variable_63, Variable_34)))); + + // Smooth Intersection + v_flt Variable_12; // Smooth Intersection output 0 + Variable_12 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_13, v_flt(5.0f)); + + // Set High Quality Value.* + v_flt Variable_57; // Set High Quality Value.* output 0 + Variable_57 = Variable_12 * v_flt(0.2f); + + Outputs.Value = Variable_57; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // <= + bool Variable_4; // <= output 0 + Variable_4 = Variable_0 <= v_flt(-40.0f); + + // <= + bool Variable_3; // <= output 0 + Variable_3 = Variable_0 <= v_flt(50.0f); + + // <= + bool Variable_6; // <= output 0 + Variable_6 = Variable_0 <= v_flt(-250.0f); + + // Switch (color) + FColor Variable_1; // Switch (color) output 0 + Variable_1 = FVoxelNodeFunctions::Switch(FColor(FColor(14, 71, 255, 255)), FColor(FColor(229, 0, 255, 255)), Variable_3); + + // Switch (color) + FColor Variable_2; // Switch (color) output 0 + Variable_2 = FVoxelNodeFunctions::Switch(FColor(FColor(255, 142, 44, 255)), Variable_1, Variable_4); + + // Switch (color) + FColor Variable_5; // Switch (color) output 0 + Variable_5 = FVoxelNodeFunctions::Switch(FColor(FColor(3, 143, 0, 255)), Variable_2, Variable_6); + + Outputs.MaterialBuilder.SetColor(Variable_5); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // <= + bool Variable_4; // <= output 0 + Variable_4 = Variable_0 <= v_flt(-40.0f); + + // <= + bool Variable_3; // <= output 0 + Variable_3 = Variable_0 <= v_flt(50.0f); + + // <= + bool Variable_6; // <= output 0 + Variable_6 = Variable_0 <= v_flt(-250.0f); + + // Switch (color) + FColor Variable_1; // Switch (color) output 0 + Variable_1 = FVoxelNodeFunctions::Switch(FColor(FColor(14, 71, 255, 255)), FColor(FColor(229, 0, 255, 255)), Variable_3); + + // Switch (color) + FColor Variable_2; // Switch (color) output 0 + Variable_2 = FVoxelNodeFunctions::Switch(FColor(FColor(255, 142, 44, 255)), Variable_1, Variable_4); + + // Switch (color) + FColor Variable_5; // Switch (color) output 0 + Variable_5 = FVoxelNodeFunctions::Switch(FColor(FColor(3, 143, 0, 255)), Variable_2, Variable_6); + + Outputs.MaterialBuilder.SetColor(Variable_5); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_26; // Cave Layer.* output 0 + TVoxelRange Variable_33; // Cave Layer.* output 0 + TVoxelRange Variable_41; // Cave Layer.* output 0 + TVoxelRange Variable_3; // 2D Noise SDF.+ output 0 + TVoxelRange Variable_6; // * output 0 + TVoxelRange Variable_43; // Cave Layer.* output 0 + TVoxelRange Variable_28; // Cave Layer.* output 0 + TVoxelRange Variable_35; // Cave Layer.* output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_9; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_10; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise + _2D_Perlin_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Perlin_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_4_Noise.SetSeed(FVoxelGraphSeed(1339)); + _3D_Perlin_Noise_4_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_5_Noise.SetSeed(FVoxelGraphSeed(1330)); + _3D_Perlin_Noise_5_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_6_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_6_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D Perlin Noise + _3D_Perlin_Noise_7_Noise.SetSeed(FVoxelGraphSeed(13339)); + _3D_Perlin_Noise_7_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 2D Perlin Noise + TVoxelRange Variable_21; // 2D Perlin Noise output 0 + Variable_21 = { -0.767493f, 0.745772f }; + + // 3D Perlin Noise + TVoxelRange Variable_17; // 3D Perlin Noise output 0 + Variable_17 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_15; // 3D Perlin Noise output 0 + Variable_15 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_16; // 3D Perlin Noise output 0 + Variable_16 = { -0.790554f, 0.781474f }; + + // 3D Perlin Noise + TVoxelRange Variable_19; // 3D Perlin Noise output 0 + Variable_19 = { -0.790554f, 0.781474f }; + + // Float Curve: + TVoxelRange Variable_8; // Float Curve: output 0 + Variable_8 = FVoxelNodeFunctions::GetCurveValue(Params.None1, Variable_21); + + // Cave Layer.* + BufferConstant.Variable_26 = Variable_16 * TVoxelRange(50.0f); + + // Cave Layer.* + BufferConstant.Variable_33 = Variable_15 * TVoxelRange(50.0f); + + // Cave Layer.* + BufferConstant.Variable_41 = Variable_17 * TVoxelRange(50.0f); + + // 2D Noise SDF.* + TVoxelRange Variable_45; // 2D Noise SDF.* output 0 + Variable_45 = Variable_19 * TVoxelRange(50.0f); + + // Cave Layer.1 - X + TVoxelRange Variable_30; // Cave Layer.1 - X output 0 + Variable_30 = 1 - BufferConstant.Variable_33; + + // 2D Noise SDF.+ + BufferConstant.Variable_3 = Variable_45 + TVoxelRange(0.0f); + + // * + BufferConstant.Variable_6 = Variable_8 * TVoxelRange(100.0f); + + // Cave Layer.1 - X + TVoxelRange Variable_23; // Cave Layer.1 - X output 0 + Variable_23 = 1 - BufferConstant.Variable_26; + + // Cave Layer.1 - X + TVoxelRange Variable_38; // Cave Layer.1 - X output 0 + Variable_38 = 1 - BufferConstant.Variable_41; + + // Cave Layer.* + BufferConstant.Variable_43 = TVoxelRange(0.2f) * Variable_38; + + // Cave Layer.* + BufferConstant.Variable_28 = TVoxelRange(0.2f) * Variable_23; + + // Cave Layer.* + BufferConstant.Variable_35 = TVoxelRange(0.2f) * Variable_30; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_1_Noise; + FVoxelFastNoise _3D_Perlin_Noise_4_Noise; + FVoxelFastNoise _3D_Perlin_Noise_5_Noise; + FVoxelFastNoise _3D_Perlin_Noise_6_Noise; + FVoxelFastNoise _3D_Perlin_Noise_7_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // X + TVoxelRange Variable_9; // X output 0 + Variable_9 = Context.GetLocalX(); + + // Z + TVoxelRange Variable_11; // Z output 0 + Variable_11 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_10; // Y output 0 + Variable_10 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_0; // Z output 0 + Variable_0 = Context.GetLocalZ(); + + // - + TVoxelRange Variable_5; // - output 0 + Variable_5 = Variable_0 - TVoxelRange(75.0f); + + // Cave Layer.- + TVoxelRange Variable_31; // Cave Layer.- output 0 + Variable_31 = Variable_0 - BufferConstant.Variable_35; + + // + + TVoxelRange Variable_1; // + output 0 + Variable_1 = TVoxelRange(100.0f) + Variable_0; + + // + + TVoxelRange Variable_4; // + output 0 + Variable_4 = TVoxelRange(200.0f) + Variable_0; + + // Cave Layer.- + TVoxelRange Variable_32; // Cave Layer.- output 0 + Variable_32 = BufferConstant.Variable_33 - Variable_0; + + // Data Item Sample + TVoxelRange Variable_14; // Data Item Sample output 0 + Variable_14 = FVoxelNodeFunctions::GetDataItemDistance(Context.Items.ItemHolder, Variable_9, Variable_10, Variable_11, TVoxelRange(5.0f), TVoxelRange(1000.0f), 1u, EVoxelDataItemCombineMode::Min); + + // + + TVoxelRange Variable_18; // + output 0 + Variable_18 = TVoxelRange(300.0f) + Variable_0; + + // Cave Layer.- + TVoxelRange Variable_24; // Cave Layer.- output 0 + Variable_24 = Variable_1 - BufferConstant.Variable_28; + + // - + TVoxelRange Variable_7; // - output 0 + Variable_7 = Variable_5 - BufferConstant.Variable_6; + + // Cave Layer.Smooth Union + TVoxelRange Variable_29; // Cave Layer.Smooth Union output 0 + Variable_29 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_31, Variable_32, TVoxelRange(100.0f)); + + // Cave Layer.- + TVoxelRange Variable_25; // Cave Layer.- output 0 + Variable_25 = BufferConstant.Variable_26 - Variable_1; + + // 2D Noise SDF.- + TVoxelRange Variable_44; // 2D Noise SDF.- output 0 + Variable_44 = Variable_18 - BufferConstant.Variable_3; + + // Cave Layer.- + TVoxelRange Variable_39; // Cave Layer.- output 0 + Variable_39 = Variable_4 - BufferConstant.Variable_43; + + // * -1 + TVoxelRange Variable_13; // * -1 output 0 + Variable_13 = Variable_14 * -1; + + // Cave Layer.- + TVoxelRange Variable_40; // Cave Layer.- output 0 + Variable_40 = BufferConstant.Variable_41 - Variable_4; + + // Cave Layer.Smooth Union + TVoxelRange Variable_22; // Cave Layer.Smooth Union output 0 + Variable_22 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_24, Variable_25, TVoxelRange(100.0f)); + + // * -1 + TVoxelRange Variable_20; // * -1 output 0 + Variable_20 = Variable_44 * -1; + + // Cave Layer.Smooth Union + TVoxelRange Variable_37; // Cave Layer.Smooth Union output 0 + Variable_37 = FVoxelSDFNodeFunctions::SmoothUnion(Variable_39, Variable_40, TVoxelRange(100.0f)); + + // Cave Layer.+ + TVoxelRange Variable_34; // Cave Layer.+ output 0 + Variable_34 = Variable_29 + TVoxelRange(25.0f); + + // Cave Layer.+ + TVoxelRange Variable_27; // Cave Layer.+ output 0 + Variable_27 = Variable_22 + TVoxelRange(25.0f); + + // Cave Layer.+ + TVoxelRange Variable_42; // Cave Layer.+ output 0 + Variable_42 = Variable_37 + TVoxelRange(25.0f); + + // Max (float) + TVoxelRange Variable_2; // Max (float) output 0 + Variable_2 = FVoxelNodeFunctions::Max(Variable_7, FVoxelNodeFunctions::Max(Variable_34, FVoxelNodeFunctions::Max(Variable_27, FVoxelNodeFunctions::Max(Variable_42, Variable_20)))); + + // Smooth Intersection + TVoxelRange Variable_12; // Smooth Intersection output 0 + Variable_12 = FVoxelSDFNodeFunctions::SmoothIntersection(Variable_2, Variable_13, TVoxelRange(5.0f)); + + // Set High Quality Value.* + TVoxelRange Variable_36; // Set High Quality Value.* output 0 + Variable_36 = Variable_12 * TVoxelRange(0.2f); + + Outputs.Value = Variable_36; + } + + }; + + FVoxelExample_LayeredWorldInstance(UVoxelExample_LayeredWorld& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Frequency, + FVoxelRichCurve(Object.None1.LoadSynchronous()), + Object.Seed + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_LayeredWorldInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_LayeredWorldInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_LayeredWorldInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_LayeredWorldInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_LayeredWorldInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_LayeredWorld::UVoxelExample_LayeredWorld() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_LayeredWorld::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_LayeredWorld. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_LayeredWorld. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_LayeredWorld. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_LayeredWorld. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.h new file mode 100644 index 00000000..ad41c3d7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_LayeredWorld.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_LayeredWorld : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Frequency")) + float Frequency = 0.005; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName)) + TSoftObjectPtr None1 = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.VoxelExample_LayeredWorld_Curve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Seed")) + int32 Seed = 4761; + + UVoxelExample_LayeredWorld(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.cpp new file mode 100644 index 00000000..e8922c44 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.cpp @@ -0,0 +1,1329 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_Planet.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_PlanetInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Frequency; + const int32 Noise_Seed; + const float Noise_Strength; + const FVoxelColorRichCurve PlanetColorCurve; + const FVoxelRichCurve PlanetCurve; + const float Radius; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_8; // Noise Strength = 0.02 output 0 + v_flt Variable_10; // Frequency = 2.0 output 0 + v_flt Variable_5; // Radius = 1000.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // X output 0 + v_flt Variable_16; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_1; // Y output 0 + v_flt Variable_17; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Noise Seed = 1443 + FVoxelGraphSeed Variable_20; // Noise Seed = 1443 output 0 + Variable_20 = Params.Noise_Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Noise Strength = 0.02 + BufferConstant.Variable_8 = Params.Noise_Strength; + + // Frequency = 2.0 + BufferConstant.Variable_10 = Params.Frequency; + + // Radius = 1000.0 + BufferConstant.Variable_5 = Params.Radius; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_0_Noise; + FVoxelFastNoise _3D_IQ_Noise_0_Noise; + TStaticArray _3D_IQ_Noise_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Noise Seed = 1443 + FVoxelGraphSeed Variable_20; // Noise Seed = 1443 output 0 + Variable_20 = Params.Noise_Seed; + + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D IQ Noise + _3D_IQ_Noise_0_Noise.SetSeed(Variable_20); + _3D_IQ_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_IQ_Noise_0_Noise.SetFractalOctavesAndGain(15, 0.6); + _3D_IQ_Noise_0_Noise.SetFractalLacunarity(2.0); + _3D_IQ_Noise_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_IQ_Noise_0_Noise.SetMatrixFromRotation_3D(FRotator(40.000000, 45.000000, 50.000000)); + _3D_IQ_Noise_0_LODToOctaves[0] = 15; + _3D_IQ_Noise_0_LODToOctaves[1] = 15; + _3D_IQ_Noise_0_LODToOctaves[2] = 15; + _3D_IQ_Noise_0_LODToOctaves[3] = 15; + _3D_IQ_Noise_0_LODToOctaves[4] = 15; + _3D_IQ_Noise_0_LODToOctaves[5] = 15; + _3D_IQ_Noise_0_LODToOctaves[6] = 15; + _3D_IQ_Noise_0_LODToOctaves[7] = 15; + _3D_IQ_Noise_0_LODToOctaves[8] = 15; + _3D_IQ_Noise_0_LODToOctaves[9] = 15; + _3D_IQ_Noise_0_LODToOctaves[10] = 15; + _3D_IQ_Noise_0_LODToOctaves[11] = 15; + _3D_IQ_Noise_0_LODToOctaves[12] = 15; + _3D_IQ_Noise_0_LODToOctaves[13] = 15; + _3D_IQ_Noise_0_LODToOctaves[14] = 15; + _3D_IQ_Noise_0_LODToOctaves[15] = 15; + _3D_IQ_Noise_0_LODToOctaves[16] = 15; + _3D_IQ_Noise_0_LODToOctaves[17] = 15; + _3D_IQ_Noise_0_LODToOctaves[18] = 15; + _3D_IQ_Noise_0_LODToOctaves[19] = 15; + _3D_IQ_Noise_0_LODToOctaves[20] = 15; + _3D_IQ_Noise_0_LODToOctaves[21] = 15; + _3D_IQ_Noise_0_LODToOctaves[22] = 15; + _3D_IQ_Noise_0_LODToOctaves[23] = 15; + _3D_IQ_Noise_0_LODToOctaves[24] = 15; + _3D_IQ_Noise_0_LODToOctaves[25] = 15; + _3D_IQ_Noise_0_LODToOctaves[26] = 15; + _3D_IQ_Noise_0_LODToOctaves[27] = 15; + _3D_IQ_Noise_0_LODToOctaves[28] = 15; + _3D_IQ_Noise_0_LODToOctaves[29] = 15; + _3D_IQ_Noise_0_LODToOctaves[30] = 15; + _3D_IQ_Noise_0_LODToOctaves[31] = 15; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_16 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + BufferXY.Variable_17 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + BufferXY.Variable_17 = Context.GetLocalY(); + + // X + BufferX.Variable_16 = Context.GetLocalX(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Z + v_flt Variable_18; // Z output 0 + Variable_18 = Context.GetLocalZ(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferXY.Variable_1, Variable_2); + + // Sphere Normalize with Preview.Normalize.Vector Length + v_flt Variable_21; // Sphere Normalize with Preview.Normalize.Vector Length output 0 + Variable_21 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_16, BufferXY.Variable_17, Variable_18); + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_23; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_23 = BufferXY.Variable_17 / Variable_21; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_24; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_24 = Variable_18 / Variable_21; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_22; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_22 = BufferX.Variable_16 / Variable_21; + + // 3D Gradient Perturb + v_flt Variable_13; // 3D Gradient Perturb output 0 + v_flt Variable_14; // 3D Gradient Perturb output 1 + v_flt Variable_15; // 3D Gradient Perturb output 2 + Variable_13 = Variable_22; + Variable_14 = Variable_23; + Variable_15 = Variable_24; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_13, Variable_14, Variable_15, v_flt(0.02f), v_flt(0.01f)); + + // 3D IQ Noise + v_flt Variable_6; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_0_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_0_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_0_Temp_3; // 3D IQ Noise output 3 + Variable_6 = _3D_IQ_Noise_0_Noise.IQNoise_3D_Deriv(Variable_13, Variable_14, Variable_15, BufferConstant.Variable_10, _3D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_0_Temp_1,_3D_IQ_Noise_0_Temp_2,_3D_IQ_Noise_0_Temp_3); + Variable_6 = FMath::Clamp(Variable_6, -0.653693, 0.750231); + _3D_IQ_Noise_0_Temp_1 = FMath::Clamp(_3D_IQ_Noise_0_Temp_1, -1.536367, 1.653675); + _3D_IQ_Noise_0_Temp_2 = FMath::Clamp(_3D_IQ_Noise_0_Temp_2, -1.654880, 1.681203); + _3D_IQ_Noise_0_Temp_3 = FMath::Clamp(_3D_IQ_Noise_0_Temp_3, -1.580102, 1.625968); + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_6 - v_flt(0.1f); + + // / + v_flt Variable_12; // / output 0 + Variable_12 = Variable_19 / v_flt(0.5f); + + // Float Curve: PlanetCurve + v_flt Variable_11; // Float Curve: PlanetCurve output 0 + Variable_11 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetCurve, Variable_12); + + // * + v_flt Variable_7; // * output 0 + Variable_7 = BufferConstant.Variable_5 * Variable_11 * BufferConstant.Variable_8; + + // + + v_flt Variable_9; // + output 0 + Variable_9 = BufferConstant.Variable_5 + Variable_7; + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_3 - Variable_9; + + Outputs.Value = Variable_4; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Y + v_flt Variable_17; // Y output 0 + Variable_17 = Context.GetLocalY(); + + // X + v_flt Variable_16; // X output 0 + Variable_16 = Context.GetLocalX(); + + // Z + v_flt Variable_18; // Z output 0 + Variable_18 = Context.GetLocalZ(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // Sphere Normalize with Preview.Normalize.Vector Length + v_flt Variable_21; // Sphere Normalize with Preview.Normalize.Vector Length output 0 + Variable_21 = FVoxelNodeFunctions::VectorLength(Variable_16, Variable_17, Variable_18); + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_23; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_23 = Variable_17 / Variable_21; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_24; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_24 = Variable_18 / Variable_21; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_22; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_22 = Variable_16 / Variable_21; + + // 3D Gradient Perturb + v_flt Variable_13; // 3D Gradient Perturb output 0 + v_flt Variable_14; // 3D Gradient Perturb output 1 + v_flt Variable_15; // 3D Gradient Perturb output 2 + Variable_13 = Variable_22; + Variable_14 = Variable_23; + Variable_15 = Variable_24; + _3D_Gradient_Perturb_0_Noise.GradientPerturb_3D(Variable_13, Variable_14, Variable_15, v_flt(0.02f), v_flt(0.01f)); + + // 3D IQ Noise + v_flt Variable_6; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_0_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_0_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_0_Temp_3; // 3D IQ Noise output 3 + Variable_6 = _3D_IQ_Noise_0_Noise.IQNoise_3D_Deriv(Variable_13, Variable_14, Variable_15, BufferConstant.Variable_10, _3D_IQ_Noise_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_0_Temp_1,_3D_IQ_Noise_0_Temp_2,_3D_IQ_Noise_0_Temp_3); + Variable_6 = FMath::Clamp(Variable_6, -0.653693, 0.750231); + _3D_IQ_Noise_0_Temp_1 = FMath::Clamp(_3D_IQ_Noise_0_Temp_1, -1.536367, 1.653675); + _3D_IQ_Noise_0_Temp_2 = FMath::Clamp(_3D_IQ_Noise_0_Temp_2, -1.654880, 1.681203); + _3D_IQ_Noise_0_Temp_3 = FMath::Clamp(_3D_IQ_Noise_0_Temp_3, -1.580102, 1.625968); + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_6 - v_flt(0.1f); + + // / + v_flt Variable_12; // / output 0 + Variable_12 = Variable_19 / v_flt(0.5f); + + // Float Curve: PlanetCurve + v_flt Variable_11; // Float Curve: PlanetCurve output 0 + Variable_11 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetCurve, Variable_12); + + // * + v_flt Variable_7; // * output 0 + Variable_7 = BufferConstant.Variable_5 * Variable_11 * BufferConstant.Variable_8; + + // + + v_flt Variable_9; // + output 0 + Variable_9 = BufferConstant.Variable_5 + Variable_7; + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_3 - Variable_9; + + Outputs.Value = Variable_4; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_1; // Frequency = 2.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_10; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_11; // Y output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of Noise Seed = 1443 + FVoxelGraphSeed Variable_15; // Noise Seed = 1443 output 0 + Variable_15 = Params.Noise_Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Frequency = 2.0 + BufferConstant.Variable_1 = Params.Frequency; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Gradient_Perturb_1_Noise; + FVoxelFastNoise _3D_IQ_Noise_1_Noise; + TStaticArray _3D_IQ_Noise_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of Noise Seed = 1443 + FVoxelGraphSeed Variable_15; // Noise Seed = 1443 output 0 + Variable_15 = Params.Noise_Seed; + + // Init of 3D Gradient Perturb + _3D_Gradient_Perturb_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Gradient_Perturb_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 3D IQ Noise + _3D_IQ_Noise_1_Noise.SetSeed(Variable_15); + _3D_IQ_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_IQ_Noise_1_Noise.SetFractalOctavesAndGain(15, 0.6); + _3D_IQ_Noise_1_Noise.SetFractalLacunarity(2.0); + _3D_IQ_Noise_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_IQ_Noise_1_Noise.SetMatrixFromRotation_3D(FRotator(40.000000, 45.000000, 50.000000)); + _3D_IQ_Noise_1_LODToOctaves[0] = 15; + _3D_IQ_Noise_1_LODToOctaves[1] = 15; + _3D_IQ_Noise_1_LODToOctaves[2] = 15; + _3D_IQ_Noise_1_LODToOctaves[3] = 15; + _3D_IQ_Noise_1_LODToOctaves[4] = 15; + _3D_IQ_Noise_1_LODToOctaves[5] = 15; + _3D_IQ_Noise_1_LODToOctaves[6] = 15; + _3D_IQ_Noise_1_LODToOctaves[7] = 15; + _3D_IQ_Noise_1_LODToOctaves[8] = 15; + _3D_IQ_Noise_1_LODToOctaves[9] = 15; + _3D_IQ_Noise_1_LODToOctaves[10] = 15; + _3D_IQ_Noise_1_LODToOctaves[11] = 15; + _3D_IQ_Noise_1_LODToOctaves[12] = 15; + _3D_IQ_Noise_1_LODToOctaves[13] = 15; + _3D_IQ_Noise_1_LODToOctaves[14] = 15; + _3D_IQ_Noise_1_LODToOctaves[15] = 15; + _3D_IQ_Noise_1_LODToOctaves[16] = 15; + _3D_IQ_Noise_1_LODToOctaves[17] = 15; + _3D_IQ_Noise_1_LODToOctaves[18] = 15; + _3D_IQ_Noise_1_LODToOctaves[19] = 15; + _3D_IQ_Noise_1_LODToOctaves[20] = 15; + _3D_IQ_Noise_1_LODToOctaves[21] = 15; + _3D_IQ_Noise_1_LODToOctaves[22] = 15; + _3D_IQ_Noise_1_LODToOctaves[23] = 15; + _3D_IQ_Noise_1_LODToOctaves[24] = 15; + _3D_IQ_Noise_1_LODToOctaves[25] = 15; + _3D_IQ_Noise_1_LODToOctaves[26] = 15; + _3D_IQ_Noise_1_LODToOctaves[27] = 15; + _3D_IQ_Noise_1_LODToOctaves[28] = 15; + _3D_IQ_Noise_1_LODToOctaves[29] = 15; + _3D_IQ_Noise_1_LODToOctaves[30] = 15; + _3D_IQ_Noise_1_LODToOctaves[31] = 15; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_10 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_11 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_10 = Context.GetLocalX(); + + // Y + BufferXY.Variable_11 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_12; // Z output 0 + Variable_12 = Context.GetLocalZ(); + + // Sphere Normalize with Preview.Normalize.Vector Length + v_flt Variable_16; // Sphere Normalize with Preview.Normalize.Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_10, BufferXY.Variable_11, Variable_12); + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_17; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_17 = BufferX.Variable_10 / Variable_16; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_19; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_19 = Variable_12 / Variable_16; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_18; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_18 = BufferXY.Variable_11 / Variable_16; + + // 3D Gradient Perturb + v_flt Variable_7; // 3D Gradient Perturb output 0 + v_flt Variable_8; // 3D Gradient Perturb output 1 + v_flt Variable_9; // 3D Gradient Perturb output 2 + Variable_7 = Variable_17; + Variable_8 = Variable_18; + Variable_9 = Variable_19; + _3D_Gradient_Perturb_1_Noise.GradientPerturb_3D(Variable_7, Variable_8, Variable_9, v_flt(0.02f), v_flt(0.01f)); + + // 3D IQ Noise + v_flt Variable_0; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_1_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_1_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_1_Temp_3; // 3D IQ Noise output 3 + Variable_0 = _3D_IQ_Noise_1_Noise.IQNoise_3D_Deriv(Variable_7, Variable_8, Variable_9, BufferConstant.Variable_1, _3D_IQ_Noise_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_1_Temp_1,_3D_IQ_Noise_1_Temp_2,_3D_IQ_Noise_1_Temp_3); + Variable_0 = FMath::Clamp(Variable_0, -0.653693, 0.750231); + _3D_IQ_Noise_1_Temp_1 = FMath::Clamp(_3D_IQ_Noise_1_Temp_1, -1.536367, 1.653675); + _3D_IQ_Noise_1_Temp_2 = FMath::Clamp(_3D_IQ_Noise_1_Temp_2, -1.654880, 1.681203); + _3D_IQ_Noise_1_Temp_3 = FMath::Clamp(_3D_IQ_Noise_1_Temp_3, -1.580102, 1.625968); + + // - + v_flt Variable_13; // - output 0 + Variable_13 = Variable_0 - v_flt(0.1f); + + // / + v_flt Variable_2; // / output 0 + Variable_2 = Variable_13 / v_flt(0.5f); + + // Color Curve: PlanetColorCurve + v_flt Variable_3; // Color Curve: PlanetColorCurve output 0 + v_flt Variable_4; // Color Curve: PlanetColorCurve output 1 + v_flt Variable_5; // Color Curve: PlanetColorCurve output 2 + v_flt Variable_6; // Color Curve: PlanetColorCurve output 3 + Variable_3 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[0], Variable_2); + Variable_4 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[1], Variable_2); + Variable_5 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[2], Variable_2); + Variable_6 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[3], Variable_2); + + // Make Color + FColor Variable_14; // Make Color output 0 + Variable_14 = FVoxelNodeFunctions::MakeColorFloat(Variable_3, Variable_4, Variable_5, Variable_6); + + Outputs.MaterialBuilder.SetColor(Variable_14); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_10; // X output 0 + Variable_10 = Context.GetLocalX(); + + // Z + v_flt Variable_12; // Z output 0 + Variable_12 = Context.GetLocalZ(); + + // Y + v_flt Variable_11; // Y output 0 + Variable_11 = Context.GetLocalY(); + + // Sphere Normalize with Preview.Normalize.Vector Length + v_flt Variable_16; // Sphere Normalize with Preview.Normalize.Vector Length output 0 + Variable_16 = FVoxelNodeFunctions::VectorLength(Variable_10, Variable_11, Variable_12); + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_17; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_17 = Variable_10 / Variable_16; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_19; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_19 = Variable_12 / Variable_16; + + // Sphere Normalize with Preview.Normalize./ + v_flt Variable_18; // Sphere Normalize with Preview.Normalize./ output 0 + Variable_18 = Variable_11 / Variable_16; + + // 3D Gradient Perturb + v_flt Variable_7; // 3D Gradient Perturb output 0 + v_flt Variable_8; // 3D Gradient Perturb output 1 + v_flt Variable_9; // 3D Gradient Perturb output 2 + Variable_7 = Variable_17; + Variable_8 = Variable_18; + Variable_9 = Variable_19; + _3D_Gradient_Perturb_1_Noise.GradientPerturb_3D(Variable_7, Variable_8, Variable_9, v_flt(0.02f), v_flt(0.01f)); + + // 3D IQ Noise + v_flt Variable_0; // 3D IQ Noise output 0 + v_flt _3D_IQ_Noise_1_Temp_1; // 3D IQ Noise output 1 + v_flt _3D_IQ_Noise_1_Temp_2; // 3D IQ Noise output 2 + v_flt _3D_IQ_Noise_1_Temp_3; // 3D IQ Noise output 3 + Variable_0 = _3D_IQ_Noise_1_Noise.IQNoise_3D_Deriv(Variable_7, Variable_8, Variable_9, BufferConstant.Variable_1, _3D_IQ_Noise_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)],_3D_IQ_Noise_1_Temp_1,_3D_IQ_Noise_1_Temp_2,_3D_IQ_Noise_1_Temp_3); + Variable_0 = FMath::Clamp(Variable_0, -0.653693, 0.750231); + _3D_IQ_Noise_1_Temp_1 = FMath::Clamp(_3D_IQ_Noise_1_Temp_1, -1.536367, 1.653675); + _3D_IQ_Noise_1_Temp_2 = FMath::Clamp(_3D_IQ_Noise_1_Temp_2, -1.654880, 1.681203); + _3D_IQ_Noise_1_Temp_3 = FMath::Clamp(_3D_IQ_Noise_1_Temp_3, -1.580102, 1.625968); + + // - + v_flt Variable_13; // - output 0 + Variable_13 = Variable_0 - v_flt(0.1f); + + // / + v_flt Variable_2; // / output 0 + Variable_2 = Variable_13 / v_flt(0.5f); + + // Color Curve: PlanetColorCurve + v_flt Variable_3; // Color Curve: PlanetColorCurve output 0 + v_flt Variable_4; // Color Curve: PlanetColorCurve output 1 + v_flt Variable_5; // Color Curve: PlanetColorCurve output 2 + v_flt Variable_6; // Color Curve: PlanetColorCurve output 3 + Variable_3 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[0], Variable_2); + Variable_4 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[1], Variable_2); + Variable_5 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[2], Variable_2); + Variable_6 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetColorCurve.Curves[3], Variable_2); + + // Make Color + FColor Variable_14; // Make Color output 0 + Variable_14 = FVoxelNodeFunctions::MakeColorFloat(Variable_3, Variable_4, Variable_5, Variable_6); + + Outputs.MaterialBuilder.SetColor(Variable_14); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_1; // Set Sphere Up Vector.X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // Set Sphere Up Vector.Y output 0 + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Set Sphere Up Vector.X + BufferX.Variable_1 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Set Sphere Up Vector.Y + BufferXY.Variable_2 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Set Sphere Up Vector.X + BufferX.Variable_1 = Context.GetLocalX(); + + // Set Sphere Up Vector.Y + BufferXY.Variable_2 = Context.GetLocalY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Set Sphere Up Vector.Z + v_flt Variable_0; // Set Sphere Up Vector.Z output 0 + Variable_0 = Context.GetLocalZ(); + + Outputs.UpVectorX = BufferX.Variable_1; + Outputs.UpVectorY = BufferXY.Variable_2; + Outputs.UpVectorZ = Variable_0; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Set Sphere Up Vector.X + v_flt Variable_1; // Set Sphere Up Vector.X output 0 + Variable_1 = Context.GetLocalX(); + + // Set Sphere Up Vector.Y + v_flt Variable_2; // Set Sphere Up Vector.Y output 0 + Variable_2 = Context.GetLocalY(); + + // Set Sphere Up Vector.Z + v_flt Variable_0; // Set Sphere Up Vector.Z output 0 + Variable_0 = Context.GetLocalZ(); + + Outputs.UpVectorX = Variable_1; + Outputs.UpVectorY = Variable_2; + Outputs.UpVectorZ = Variable_0; + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_9; // + output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_0; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_1; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D IQ Noise + _3D_IQ_Noise_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_IQ_Noise_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_IQ_Noise_2_Noise.SetFractalOctavesAndGain(15, 0.6); + _3D_IQ_Noise_2_Noise.SetFractalLacunarity(2.0); + _3D_IQ_Noise_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_IQ_Noise_2_Noise.SetMatrixFromRotation_3D(FRotator(40.000000, 45.000000, 50.000000)); + _3D_IQ_Noise_2_LODToOctaves[0] = 15; + _3D_IQ_Noise_2_LODToOctaves[1] = 15; + _3D_IQ_Noise_2_LODToOctaves[2] = 15; + _3D_IQ_Noise_2_LODToOctaves[3] = 15; + _3D_IQ_Noise_2_LODToOctaves[4] = 15; + _3D_IQ_Noise_2_LODToOctaves[5] = 15; + _3D_IQ_Noise_2_LODToOctaves[6] = 15; + _3D_IQ_Noise_2_LODToOctaves[7] = 15; + _3D_IQ_Noise_2_LODToOctaves[8] = 15; + _3D_IQ_Noise_2_LODToOctaves[9] = 15; + _3D_IQ_Noise_2_LODToOctaves[10] = 15; + _3D_IQ_Noise_2_LODToOctaves[11] = 15; + _3D_IQ_Noise_2_LODToOctaves[12] = 15; + _3D_IQ_Noise_2_LODToOctaves[13] = 15; + _3D_IQ_Noise_2_LODToOctaves[14] = 15; + _3D_IQ_Noise_2_LODToOctaves[15] = 15; + _3D_IQ_Noise_2_LODToOctaves[16] = 15; + _3D_IQ_Noise_2_LODToOctaves[17] = 15; + _3D_IQ_Noise_2_LODToOctaves[18] = 15; + _3D_IQ_Noise_2_LODToOctaves[19] = 15; + _3D_IQ_Noise_2_LODToOctaves[20] = 15; + _3D_IQ_Noise_2_LODToOctaves[21] = 15; + _3D_IQ_Noise_2_LODToOctaves[22] = 15; + _3D_IQ_Noise_2_LODToOctaves[23] = 15; + _3D_IQ_Noise_2_LODToOctaves[24] = 15; + _3D_IQ_Noise_2_LODToOctaves[25] = 15; + _3D_IQ_Noise_2_LODToOctaves[26] = 15; + _3D_IQ_Noise_2_LODToOctaves[27] = 15; + _3D_IQ_Noise_2_LODToOctaves[28] = 15; + _3D_IQ_Noise_2_LODToOctaves[29] = 15; + _3D_IQ_Noise_2_LODToOctaves[30] = 15; + _3D_IQ_Noise_2_LODToOctaves[31] = 15; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // 3D IQ Noise + TVoxelRange Variable_6; // 3D IQ Noise output 0 + TVoxelRange _3D_IQ_Noise_2_Temp_1; // 3D IQ Noise output 1 + TVoxelRange _3D_IQ_Noise_2_Temp_2; // 3D IQ Noise output 2 + TVoxelRange _3D_IQ_Noise_2_Temp_3; // 3D IQ Noise output 3 + Variable_6 = { -0.653693f, 0.750231f }; + _3D_IQ_Noise_2_Temp_1 = { -1.536367f, 1.653675f }; + _3D_IQ_Noise_2_Temp_2 = { -1.654880f, 1.681203f }; + _3D_IQ_Noise_2_Temp_3 = { -1.580102f, 1.625968f }; + + // Noise Strength = 0.02 + TVoxelRange Variable_8; // Noise Strength = 0.02 output 0 + Variable_8 = Params.Noise_Strength; + + // Radius = 1000.0 + TVoxelRange Variable_5; // Radius = 1000.0 output 0 + Variable_5 = Params.Radius; + + // - + TVoxelRange Variable_12; // - output 0 + Variable_12 = Variable_6 - TVoxelRange(0.1f); + + // / + TVoxelRange Variable_11; // / output 0 + Variable_11 = Variable_12 / TVoxelRange(0.5f); + + // Float Curve: PlanetCurve + TVoxelRange Variable_10; // Float Curve: PlanetCurve output 0 + Variable_10 = FVoxelNodeFunctions::GetCurveValue(Params.PlanetCurve, Variable_11); + + // * + TVoxelRange Variable_7; // * output 0 + Variable_7 = Variable_5 * Variable_10 * Variable_8; + + // + + BufferConstant.Variable_9 = Variable_5 + Variable_7; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_IQ_Noise_2_Noise; + TStaticArray _3D_IQ_Noise_2_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Y + TVoxelRange Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // X + TVoxelRange Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Z + TVoxelRange Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Vector Length + TVoxelRange Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // - + TVoxelRange Variable_4; // - output 0 + Variable_4 = Variable_3 - BufferConstant.Variable_9; + + Outputs.Value = Variable_4; + } + + }; + + FVoxelExample_PlanetInstance(UVoxelExample_Planet& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Frequency, + Object.Noise_Seed, + Object.Noise_Strength, + FVoxelColorRichCurve(Object.PlanetColorCurve.LoadSynchronous()), + FVoxelRichCurve(Object.PlanetCurve.LoadSynchronous()), + Object.Radius + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_PlanetInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_PlanetInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_PlanetInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_PlanetInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_PlanetInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_Planet::UVoxelExample_Planet() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_Planet::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_Planet. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_Planet. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Planet. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Planet. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.h new file mode 100644 index 00000000..d5e96598 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_Planet.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_Planet : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Frequency")) + float Frequency = 2.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Seed")) + int32 Noise_Seed = 1443; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Noise Strength")) + float Noise_Strength = 0.02; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="PlanetColorCurve")) + TSoftObjectPtr PlanetColorCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.VoxelExample_Planet_ColorCurve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="PlanetCurve")) + TSoftObjectPtr PlanetCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.VoxelExample_Planet_Curve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Radius")) + float Radius = 1000.0; + + UVoxelExample_Planet(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.cpp new file mode 100644 index 00000000..234eca1d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.cpp @@ -0,0 +1,1153 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_Ravines.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_RavinesInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float _3D_Noise_Frequency; + const int32 _3D_Noise_Seed; + const float Bottom_Transition_Smoothness; + const float Height; + const float Top_Transition_Smoothness; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_9; // Top Transition Smoothness = 5.0 output 0 + v_flt Variable_10; // Bottom Transition Smoothness = 5.0 output 0 + v_flt Variable_11; // 3D Noise Frequency = 0.02 output 0 + v_flt Variable_5; // Height = 50.0 output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_1; // Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + // Init of 3D Noise Seed = 1443 + FVoxelGraphSeed Variable_12; // 3D Noise Seed = 1443 output 0 + Variable_12 = Params._3D_Noise_Seed; + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Top Transition Smoothness = 5.0 + BufferConstant.Variable_9 = Params.Top_Transition_Smoothness; + + // Bottom Transition Smoothness = 5.0 + BufferConstant.Variable_10 = Params.Bottom_Transition_Smoothness; + + // 3D Noise Frequency = 0.02 + BufferConstant.Variable_11 = Params._3D_Noise_Frequency; + + // Height = 50.0 + BufferConstant.Variable_5 = Params.Height; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_Fractal_0_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_0_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 3D Noise Seed = 1443 + FVoxelGraphSeed Variable_12; // 3D Noise Seed = 1443 output 0 + Variable_12 = Params._3D_Noise_Seed; + + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_0_Noise.SetSeed(Variable_12); + _3D_Perlin_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(1, 0.5); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _3D_Perlin_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_0_LODToOctaves[0] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[1] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[2] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[3] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[4] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[5] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[6] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[7] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[8] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[9] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[10] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[11] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[12] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[13] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[14] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[15] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[16] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[17] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[18] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[19] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[20] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[21] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[22] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[23] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[24] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[25] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[26] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[27] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[28] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[29] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[30] = 1; + _3D_Perlin_Noise_Fractal_0_LODToOctaves[31] = 1; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // + + v_flt Variable_7; // + output 0 + Variable_7 = Variable_8 + BufferConstant.Variable_5; + + // 3D Perlin Noise Fractal + v_flt Variable_3; // 3D Perlin Noise Fractal output 0 + Variable_3 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(BufferX.Variable_0, BufferXY.Variable_1, Variable_2, BufferConstant.Variable_11, _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_3 = FMath::Clamp(Variable_3, -0.852398, 0.865098); + + // * + v_flt Variable_4; // * output 0 + Variable_4 = Variable_3 * v_flt(5.0f); + + // Smooth Intersection.- + v_flt Variable_20; // Smooth Intersection.- output 0 + Variable_20 = Variable_6 - Variable_4; + + // Smooth Intersection./ + v_flt Variable_13; // Smooth Intersection./ output 0 + Variable_13 = Variable_20 / BufferConstant.Variable_9; + + // Smooth Intersection.* + v_flt Variable_14; // Smooth Intersection.* output 0 + Variable_14 = Variable_13 * v_flt(0.5f); + + // Smooth Intersection.- + v_flt Variable_21; // Smooth Intersection.- output 0 + Variable_21 = v_flt(0.5f) - Variable_14; + + // Smooth Intersection.Clamp + v_flt Variable_15; // Smooth Intersection.Clamp output 0 + Variable_15 = FVoxelNodeFunctions::Clamp(Variable_21, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Intersection.Lerp + v_flt Variable_16; // Smooth Intersection.Lerp output 0 + Variable_16 = FVoxelNodeFunctions::Lerp(Variable_6, Variable_4, Variable_15); + + // Smooth Intersection.1 - X + v_flt Variable_18; // Smooth Intersection.1 - X output 0 + Variable_18 = 1 - Variable_15; + + // Smooth Intersection.* + v_flt Variable_17; // Smooth Intersection.* output 0 + Variable_17 = BufferConstant.Variable_9 * Variable_15 * Variable_18; + + // Smooth Intersection.+ + v_flt Variable_19; // Smooth Intersection.+ output 0 + Variable_19 = Variable_16 + Variable_17; + + // Smooth Union.- + v_flt Variable_22; // Smooth Union.- output 0 + Variable_22 = Variable_7 - Variable_19; + + // Smooth Union./ + v_flt Variable_23; // Smooth Union./ output 0 + Variable_23 = Variable_22 / BufferConstant.Variable_10; + + // Smooth Union.* + v_flt Variable_24; // Smooth Union.* output 0 + Variable_24 = Variable_23 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_25; // Smooth Union.+ output 0 + Variable_25 = Variable_24 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_26; // Smooth Union.Clamp output 0 + Variable_26 = FVoxelNodeFunctions::Clamp(Variable_25, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.Lerp + v_flt Variable_27; // Smooth Union.Lerp output 0 + Variable_27 = FVoxelNodeFunctions::Lerp(Variable_7, Variable_19, Variable_26); + + // Smooth Union.1 - X + v_flt Variable_30; // Smooth Union.1 - X output 0 + Variable_30 = 1 - Variable_26; + + // Smooth Union.* + v_flt Variable_29; // Smooth Union.* output 0 + Variable_29 = BufferConstant.Variable_10 * Variable_26 * Variable_30; + + // Smooth Union.- + v_flt Variable_28; // Smooth Union.- output 0 + Variable_28 = Variable_27 - Variable_29; + + Outputs.Value = Variable_28; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_6; // Z output 0 + Variable_6 = Context.GetLocalZ(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // + + v_flt Variable_7; // + output 0 + Variable_7 = Variable_8 + BufferConstant.Variable_5; + + // 3D Perlin Noise Fractal + v_flt Variable_3; // 3D Perlin Noise Fractal output 0 + Variable_3 = _3D_Perlin_Noise_Fractal_0_Noise.GetPerlinFractal_3D(Variable_0, Variable_1, Variable_2, BufferConstant.Variable_11, _3D_Perlin_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_3 = FMath::Clamp(Variable_3, -0.852398, 0.865098); + + // * + v_flt Variable_4; // * output 0 + Variable_4 = Variable_3 * v_flt(5.0f); + + // Smooth Intersection.- + v_flt Variable_20; // Smooth Intersection.- output 0 + Variable_20 = Variable_6 - Variable_4; + + // Smooth Intersection./ + v_flt Variable_13; // Smooth Intersection./ output 0 + Variable_13 = Variable_20 / BufferConstant.Variable_9; + + // Smooth Intersection.* + v_flt Variable_14; // Smooth Intersection.* output 0 + Variable_14 = Variable_13 * v_flt(0.5f); + + // Smooth Intersection.- + v_flt Variable_21; // Smooth Intersection.- output 0 + Variable_21 = v_flt(0.5f) - Variable_14; + + // Smooth Intersection.Clamp + v_flt Variable_15; // Smooth Intersection.Clamp output 0 + Variable_15 = FVoxelNodeFunctions::Clamp(Variable_21, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Intersection.Lerp + v_flt Variable_16; // Smooth Intersection.Lerp output 0 + Variable_16 = FVoxelNodeFunctions::Lerp(Variable_6, Variable_4, Variable_15); + + // Smooth Intersection.1 - X + v_flt Variable_18; // Smooth Intersection.1 - X output 0 + Variable_18 = 1 - Variable_15; + + // Smooth Intersection.* + v_flt Variable_17; // Smooth Intersection.* output 0 + Variable_17 = BufferConstant.Variable_9 * Variable_15 * Variable_18; + + // Smooth Intersection.+ + v_flt Variable_19; // Smooth Intersection.+ output 0 + Variable_19 = Variable_16 + Variable_17; + + // Smooth Union.- + v_flt Variable_22; // Smooth Union.- output 0 + Variable_22 = Variable_7 - Variable_19; + + // Smooth Union./ + v_flt Variable_23; // Smooth Union./ output 0 + Variable_23 = Variable_22 / BufferConstant.Variable_10; + + // Smooth Union.* + v_flt Variable_24; // Smooth Union.* output 0 + Variable_24 = Variable_23 * v_flt(0.5f); + + // Smooth Union.+ + v_flt Variable_25; // Smooth Union.+ output 0 + Variable_25 = Variable_24 + v_flt(0.5f); + + // Smooth Union.Clamp + v_flt Variable_26; // Smooth Union.Clamp output 0 + Variable_26 = FVoxelNodeFunctions::Clamp(Variable_25, v_flt(0.0f), v_flt(1.0f)); + + // Smooth Union.Lerp + v_flt Variable_27; // Smooth Union.Lerp output 0 + Variable_27 = FVoxelNodeFunctions::Lerp(Variable_7, Variable_19, Variable_26); + + // Smooth Union.1 - X + v_flt Variable_30; // Smooth Union.1 - X output 0 + Variable_30 = 1 - Variable_26; + + // Smooth Union.* + v_flt Variable_29; // Smooth Union.* output 0 + Variable_29 = BufferConstant.Variable_10 * Variable_26 * Variable_30; + + // Smooth Union.- + v_flt Variable_28; // Smooth Union.- output 0 + Variable_28 = Variable_27 - Variable_29; + + Outputs.Value = Variable_28; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_7; // Bottom Transition Smoothness = 5.0 output 0 + TVoxelRange Variable_2; // Height = 50.0 output 0 + TVoxelRange Variable_6; // Top Transition Smoothness = 5.0 output 0 + TVoxelRange Variable_1; // * output 0 + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 3D Perlin Noise Fractal + _3D_Perlin_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _3D_Perlin_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(1, 0.5); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _3D_Perlin_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _3D_Perlin_Noise_Fractal_1_LODToOctaves[0] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[1] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[2] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[3] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[4] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[5] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[6] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[7] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[8] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[9] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[10] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[11] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[12] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[13] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[14] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[15] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[16] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[17] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[18] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[19] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[20] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[21] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[22] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[23] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[24] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[25] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[26] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[27] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[28] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[29] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[30] = 1; + _3D_Perlin_Noise_Fractal_1_LODToOctaves[31] = 1; + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Bottom Transition Smoothness = 5.0 + BufferConstant.Variable_7 = Params.Bottom_Transition_Smoothness; + + // Height = 50.0 + BufferConstant.Variable_2 = Params.Height; + + // Top Transition Smoothness = 5.0 + BufferConstant.Variable_6 = Params.Top_Transition_Smoothness; + + // 3D Perlin Noise Fractal + TVoxelRange Variable_0; // 3D Perlin Noise Fractal output 0 + Variable_0 = { -0.852398f, 0.865098f }; + + // * + BufferConstant.Variable_1 = Variable_0 * TVoxelRange(5.0f); + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _3D_Perlin_Noise_Fractal_1_Noise; + TStaticArray _3D_Perlin_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Z + TVoxelRange Variable_5; // Z output 0 + Variable_5 = Context.GetLocalZ(); + + // Z + TVoxelRange Variable_3; // Z output 0 + Variable_3 = Context.GetLocalZ(); + + // + + TVoxelRange Variable_4; // + output 0 + Variable_4 = Variable_5 + BufferConstant.Variable_2; + + // Smooth Intersection.- + TVoxelRange Variable_15; // Smooth Intersection.- output 0 + Variable_15 = Variable_3 - BufferConstant.Variable_1; + + // Smooth Intersection./ + TVoxelRange Variable_8; // Smooth Intersection./ output 0 + Variable_8 = Variable_15 / BufferConstant.Variable_6; + + // Smooth Intersection.* + TVoxelRange Variable_9; // Smooth Intersection.* output 0 + Variable_9 = Variable_8 * TVoxelRange(0.5f); + + // Smooth Intersection.- + TVoxelRange Variable_16; // Smooth Intersection.- output 0 + Variable_16 = TVoxelRange(0.5f) - Variable_9; + + // Smooth Intersection.Clamp + TVoxelRange Variable_10; // Smooth Intersection.Clamp output 0 + Variable_10 = FVoxelNodeFunctions::Clamp(Variable_16, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // Smooth Intersection.1 - X + TVoxelRange Variable_13; // Smooth Intersection.1 - X output 0 + Variable_13 = 1 - Variable_10; + + // Smooth Intersection.Lerp + TVoxelRange Variable_11; // Smooth Intersection.Lerp output 0 + Variable_11 = FVoxelNodeFunctions::Lerp(Variable_3, BufferConstant.Variable_1, Variable_10); + + // Smooth Intersection.* + TVoxelRange Variable_12; // Smooth Intersection.* output 0 + Variable_12 = BufferConstant.Variable_6 * Variable_10 * Variable_13; + + // Smooth Intersection.+ + TVoxelRange Variable_14; // Smooth Intersection.+ output 0 + Variable_14 = Variable_11 + Variable_12; + + // Smooth Union.- + TVoxelRange Variable_17; // Smooth Union.- output 0 + Variable_17 = Variable_4 - Variable_14; + + // Smooth Union./ + TVoxelRange Variable_18; // Smooth Union./ output 0 + Variable_18 = Variable_17 / BufferConstant.Variable_7; + + // Smooth Union.* + TVoxelRange Variable_19; // Smooth Union.* output 0 + Variable_19 = Variable_18 * TVoxelRange(0.5f); + + // Smooth Union.+ + TVoxelRange Variable_20; // Smooth Union.+ output 0 + Variable_20 = Variable_19 + TVoxelRange(0.5f); + + // Smooth Union.Clamp + TVoxelRange Variable_21; // Smooth Union.Clamp output 0 + Variable_21 = FVoxelNodeFunctions::Clamp(Variable_20, TVoxelRange(0.0f), TVoxelRange(1.0f)); + + // Smooth Union.1 - X + TVoxelRange Variable_25; // Smooth Union.1 - X output 0 + Variable_25 = 1 - Variable_21; + + // Smooth Union.Lerp + TVoxelRange Variable_22; // Smooth Union.Lerp output 0 + Variable_22 = FVoxelNodeFunctions::Lerp(Variable_4, Variable_14, Variable_21); + + // Smooth Union.* + TVoxelRange Variable_24; // Smooth Union.* output 0 + Variable_24 = BufferConstant.Variable_7 * Variable_21 * Variable_25; + + // Smooth Union.- + TVoxelRange Variable_23; // Smooth Union.- output 0 + Variable_23 = Variable_22 - Variable_24; + + Outputs.Value = Variable_23; + } + + }; + + FVoxelExample_RavinesInstance(UVoxelExample_Ravines& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object._3D_Noise_Frequency, + Object._3D_Noise_Seed, + Object.Bottom_Transition_Smoothness, + Object.Height, + Object.Top_Transition_Smoothness + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_RavinesInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_RavinesInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_RavinesInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_RavinesInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_RavinesInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_Ravines::UVoxelExample_Ravines() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_Ravines::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_Ravines. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_Ravines. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Ravines. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Ravines. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.h new file mode 100644 index 00000000..ef3e5d1c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.h @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_Ravines.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_Ravines : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName=" 3D Noise Frequency", UIMax="1", UIMin="0")) + float _3D_Noise_Frequency = 0.02; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="3D Noise Seed")) + int32 _3D_Noise_Seed = 1443; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Bottom Transition Smoothness")) + float Bottom_Transition_Smoothness = 5.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Height")) + float Height = 50.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Top Transition Smoothness")) + float Top_Transition_Smoothness = 5.0; + + UVoxelExample_Ravines(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.cpp new file mode 100644 index 00000000..0b06ed5c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.cpp @@ -0,0 +1,3110 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_RingWorld.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_RingWorldInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const float Radius; + const float RingEdgesHardness; + const float Scale; + const float Thickness; + const float Width_in_Degrees; + const float RiverDepth; + const float RiverWidth; + const FColor BeachColor; + const FColor MountainsColorHigh; + const FColor MountainsColorLowHigh; + const FColor MountainsColorLowLow; + const FVoxelRichCurve MoutainsMaskCurve; + const FColor PlainsColorHigh; + const FColor PlainsColorLow; + const float PlainsNoiseFrequency; + const float PlainsNoiseHeight; + const FVoxelRichCurve PlainsNoiseStrengthCurve; + const FVoxelRichCurve RingMainShapeCurve; + const FColor RingOuterColor; + const FColor RiverColor; + const FVoxelRichCurve RiverDepthCurve; + const float MountainsNoiseFrequency; + const float MountainsNoiseHeight; + const float BaseNoiseFrquency; + const float BaseNoiseHeight; + const float BaseHeight; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_51; // MountainsNoiseFrequency = 0.2 output 0 + v_flt Variable_22; // RingEdgesHardness = 10.0 output 0 + v_flt Variable_60; // PlainsNoiseFrequency = 0.2 output 0 + v_flt Variable_48; // MountainsNoiseHeight = 500.0 output 0 + v_flt Variable_42; // BaseNoiseHeight = 250.0 output 0 + v_flt Variable_89; // RiverWidth = 1.0 output 0 + v_flt Variable_39; // BaseHeight = 1000.0 output 0 + v_flt Variable_57; // PlainsNoiseHeight = 250.0 output 0 + v_flt Variable_45; // BaseNoiseFrquency = 0.005 output 0 + v_flt Variable_65; // RiverDepth = 100.0 output 0 + v_flt Variable_84; // Scale = 10.0 output 0 + v_flt Variable_85; // * output 0 + v_flt Variable_36; // / output 0 + v_flt Variable_6; // + output 0 + v_flt Variable_86; // / output 0 + v_flt Variable_35; // - output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // X output 0 + v_flt Variable_24; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_1; // Y output 0 + v_flt Variable_44; // * output 0 + v_flt Variable_59; // * output 0 + v_flt Variable_50; // * output 0 + v_flt Variable_74; // * output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // MountainsNoiseFrequency = 0.2 + BufferConstant.Variable_51 = Params.MountainsNoiseFrequency; + + // Radius = 7000.0 + v_flt Variable_5; // Radius = 7000.0 output 0 + Variable_5 = Params.Radius; + + // RingEdgesHardness = 10.0 + BufferConstant.Variable_22 = Params.RingEdgesHardness; + + // Width in Degrees = 50.0 + v_flt Variable_30; // Width in Degrees = 50.0 output 0 + Variable_30 = Params.Width_in_Degrees; + + // PlainsNoiseFrequency = 0.2 + BufferConstant.Variable_60 = Params.PlainsNoiseFrequency; + + // MountainsNoiseHeight = 500.0 + BufferConstant.Variable_48 = Params.MountainsNoiseHeight; + + // BaseNoiseHeight = 250.0 + BufferConstant.Variable_42 = Params.BaseNoiseHeight; + + // RiverWidth = 1.0 + BufferConstant.Variable_89 = Params.RiverWidth; + + // BaseHeight = 1000.0 + BufferConstant.Variable_39 = Params.BaseHeight; + + // Thickness = 500.0 + v_flt Variable_90; // Thickness = 500.0 output 0 + Variable_90 = Params.Thickness; + + // PlainsNoiseHeight = 250.0 + BufferConstant.Variable_57 = Params.PlainsNoiseHeight; + + // BaseNoiseFrquency = 0.005 + BufferConstant.Variable_45 = Params.BaseNoiseFrquency; + + // RiverDepth = 100.0 + BufferConstant.Variable_65 = Params.RiverDepth; + + // Scale = 10.0 + BufferConstant.Variable_84 = Params.Scale; + + // * + v_flt Variable_91; // * output 0 + Variable_91 = Variable_90 * BufferConstant.Variable_84; + + // * + BufferConstant.Variable_85 = Variable_5 * BufferConstant.Variable_84; + + // / + BufferConstant.Variable_36 = Variable_30 / v_flt(360.0f); + + // + + BufferConstant.Variable_6 = BufferConstant.Variable_85 + Variable_91; + + // / + BufferConstant.Variable_86 = BufferConstant.Variable_85 / BufferConstant.Variable_84; + + // - + BufferConstant.Variable_35 = v_flt(0.5f) - BufferConstant.Variable_36; + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Simplex_Noise_0_Noise; + FVoxelFastNoise _2D_Simplex_Noise_Fractal_0_Noise; + TStaticArray _2D_Simplex_Noise_Fractal_0_LODToOctaves; + FVoxelFastNoise _2D_Simplex_Noise_1_Noise; + FVoxelFastNoise _2D_Perlin_Noise_0_Noise; + FVoxelFastNoise _2D_Simplex_Noise_Fractal_1_Noise; + TStaticArray _2D_Simplex_Noise_Fractal_1_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Simplex Noise + _2D_Simplex_Noise_0_Noise.SetSeed(FVoxelGraphSeed(1000)); + _2D_Simplex_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise Fractal + _2D_Simplex_Noise_Fractal_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_Fractal_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Simplex_Noise_Fractal_0_Noise.SetFractalOctavesAndGain(5, 0.5); + _2D_Simplex_Noise_Fractal_0_Noise.SetFractalLacunarity(2.0); + _2D_Simplex_Noise_Fractal_0_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Simplex_Noise_Fractal_0_LODToOctaves[0] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[1] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[2] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[3] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[4] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[5] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[6] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[7] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[8] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[9] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[10] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[11] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[12] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[13] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[14] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[15] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[16] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[17] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[18] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[19] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[20] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[21] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[22] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[23] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[24] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[25] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[26] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[27] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[28] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[29] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[30] = 5; + _2D_Simplex_Noise_Fractal_0_LODToOctaves[31] = 5; + + // Init of 2D Simplex Noise + _2D_Simplex_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Perlin Noise + _2D_Perlin_Noise_0_Noise.SetSeed(FVoxelGraphSeed(1338)); + _2D_Perlin_Noise_0_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise Fractal + _2D_Simplex_Noise_Fractal_1_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_Fractal_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Simplex_Noise_Fractal_1_Noise.SetFractalOctavesAndGain(8, 0.5); + _2D_Simplex_Noise_Fractal_1_Noise.SetFractalLacunarity(2.0); + _2D_Simplex_Noise_Fractal_1_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Simplex_Noise_Fractal_1_LODToOctaves[0] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[1] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[2] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[3] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[4] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[5] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[6] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[7] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[8] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[9] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[10] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[11] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[12] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[13] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[14] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[15] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[16] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[17] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[18] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[19] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[20] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[21] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[22] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[23] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[24] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[25] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[26] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[27] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[28] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[29] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[30] = 8; + _2D_Simplex_Noise_Fractal_1_LODToOctaves[31] = 8; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_24 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + v_flt Variable_28; // Y output 0 + Variable_28 = Context.GetLocalY(); + + // Atan2 + v_flt Variable_23; // Atan2 output 0 + Variable_23 = FVoxelNodeFunctions::Atan2(Variable_28, BufferX.Variable_24); + + // / + v_flt Variable_25; // / output 0 + Variable_25 = Variable_23 / v_flt(3.141593f); + + // * + v_flt Variable_27; // * output 0 + Variable_27 = BufferConstant.Variable_86 * Variable_25; + + // * + BufferXY.Variable_44 = Variable_27 * BufferConstant.Variable_45; + + // * + BufferXY.Variable_59 = Variable_27 * BufferConstant.Variable_60; + + // * + BufferXY.Variable_50 = Variable_27 * BufferConstant.Variable_51; + + // 2D Simplex Noise + v_flt Variable_77; // 2D Simplex Noise output 0 + Variable_77 = _2D_Simplex_Noise_0_Noise.GetSimplex_2D(Variable_27, Variable_27, v_flt(0.001f)); + Variable_77 = FMath::Clamp(Variable_77, -0.997888, 0.997883); + + // * + BufferXY.Variable_74 = Variable_77 * v_flt(0.1f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_24 = Context.GetLocalX(); + + // Y + v_flt Variable_28; // Y output 0 + Variable_28 = Context.GetLocalY(); + + // Atan2 + v_flt Variable_23; // Atan2 output 0 + Variable_23 = FVoxelNodeFunctions::Atan2(Variable_28, BufferX.Variable_24); + + // / + v_flt Variable_25; // / output 0 + Variable_25 = Variable_23 / v_flt(3.141593f); + + // * + v_flt Variable_27; // * output 0 + Variable_27 = BufferConstant.Variable_86 * Variable_25; + + // * + BufferXY.Variable_44 = Variable_27 * BufferConstant.Variable_45; + + // * + BufferXY.Variable_59 = Variable_27 * BufferConstant.Variable_60; + + // * + BufferXY.Variable_50 = Variable_27 * BufferConstant.Variable_51; + + // 2D Simplex Noise + v_flt Variable_77; // 2D Simplex Noise output 0 + Variable_77 = _2D_Simplex_Noise_0_Noise.GetSimplex_2D(Variable_27, Variable_27, v_flt(0.001f)); + Variable_77 = FMath::Clamp(Variable_77, -0.997888, 0.997883); + + // * + BufferXY.Variable_74 = Variable_77 * v_flt(0.1f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_70; // Z output 0 + Variable_70 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Z + v_flt Variable_12; // Z output 0 + Variable_12 = Context.GetLocalZ(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferXY.Variable_1, Variable_2); + + // + + v_flt Variable_29; // + output 0 + Variable_29 = Variable_3 + v_flt(0.001f); + + // / + v_flt Variable_13; // / output 0 + Variable_13 = Variable_12 / Variable_29; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_29 - BufferConstant.Variable_6; + + // / + v_flt Variable_71; // / output 0 + Variable_71 = Variable_70 / Variable_29; + + // ACOS + v_flt Variable_14; // ACOS output 0 + Variable_14 = FVoxelNodeFunctions::Acos(Variable_13); + + // / + v_flt Variable_10; // / output 0 + Variable_10 = Variable_14 / v_flt(3.141593f); + + // 1 - X + v_flt Variable_11; // 1 - X output 0 + Variable_11 = 1 - Variable_10; + + // * + v_flt Variable_26; // * output 0 + Variable_26 = BufferConstant.Variable_86 * Variable_10; + + // + + v_flt Variable_73; // + output 0 + Variable_73 = Variable_71 + BufferXY.Variable_74; + + // Min (float) + v_flt Variable_9; // Min (float) output 0 + Variable_9 = FVoxelNodeFunctions::Min(Variable_10, Variable_11); + + // * + v_flt Variable_43; // * output 0 + Variable_43 = Variable_26 * BufferConstant.Variable_45; + + // * + v_flt Variable_49; // * output 0 + Variable_49 = Variable_26 * BufferConstant.Variable_51; + + // ACOS + v_flt Variable_72; // ACOS output 0 + Variable_72 = FVoxelNodeFunctions::Acos(Variable_73); + + // * + v_flt Variable_58; // * output 0 + Variable_58 = Variable_26 * BufferConstant.Variable_60; + + // - + v_flt Variable_15; // - output 0 + Variable_15 = Variable_9 - BufferConstant.Variable_35; + + // 2D Simplex Noise Fractal + v_flt Variable_40; // 2D Simplex Noise Fractal output 0 + Variable_40 = _2D_Simplex_Noise_Fractal_0_Noise.GetSimplexFractal_2D(Variable_43, BufferXY.Variable_44, v_flt(0.02f), _2D_Simplex_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_40 = FMath::Clamp(Variable_40, -0.802678, 0.846347); + + // 2D Simplex Noise + v_flt Variable_62; // 2D Simplex Noise output 0 + Variable_62 = _2D_Simplex_Noise_1_Noise.GetSimplex_2D(Variable_58, BufferXY.Variable_59, v_flt(0.02f)); + Variable_62 = FMath::Clamp(Variable_62, -0.997652, 0.996970); + + // 2D Perlin Noise + v_flt Variable_53; // 2D Perlin Noise output 0 + Variable_53 = _2D_Perlin_Noise_0_Noise.GetPerlin_2D(Variable_49, BufferXY.Variable_50, v_flt(0.1f)); + Variable_53 = FMath::Clamp(Variable_53, -0.888317, 0.811183); + + // 2D Simplex Noise Fractal + v_flt Variable_46; // 2D Simplex Noise Fractal output 0 + Variable_46 = _2D_Simplex_Noise_Fractal_1_Noise.GetSimplexFractal_2D(Variable_49, BufferXY.Variable_50, v_flt(0.02f), _2D_Simplex_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_46 = FMath::Clamp(Variable_46, -0.780446, 0.760988); + + // / + v_flt Variable_69; // / output 0 + Variable_69 = Variable_72 / v_flt(3.141593f); + + // / + v_flt Variable_34; // / output 0 + Variable_34 = Variable_15 / BufferConstant.Variable_36; + + // 1 - X + v_flt Variable_67; // 1 - X output 0 + Variable_67 = 1 - Variable_69; + + // * + v_flt Variable_41; // * output 0 + Variable_41 = Variable_40 * BufferConstant.Variable_42; + + // 1 - X + v_flt Variable_33; // 1 - X output 0 + Variable_33 = 1 - Variable_34; + + // Max (float) + v_flt Variable_18; // Max (float) output 0 + Variable_18 = FVoxelNodeFunctions::Max(Variable_34, v_flt(0.0f)); + + // Min (float) + v_flt Variable_66; // Min (float) output 0 + Variable_66 = FVoxelNodeFunctions::Min(Variable_69, Variable_67); + + // Float Curve: PlainsNoiseStrengthCurve + v_flt Variable_61; // Float Curve: PlainsNoiseStrengthCurve output 0 + Variable_61 = FVoxelNodeFunctions::GetCurveValue(Params.PlainsNoiseStrengthCurve, Variable_33); + + // Float Curve: RingMainShapeCurve + v_flt Variable_37; // Float Curve: RingMainShapeCurve output 0 + Variable_37 = FVoxelNodeFunctions::GetCurveValue(Params.RingMainShapeCurve, Variable_33); + + // Float Curve: MoutainsMaskCurve + v_flt Variable_52; // Float Curve: MoutainsMaskCurve output 0 + Variable_52 = FVoxelNodeFunctions::GetCurveValue(Params.MoutainsMaskCurve, Variable_33); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = Variable_18 * BufferConstant.Variable_22; + + // / + v_flt Variable_68; // / output 0 + Variable_68 = Variable_66 / v_flt(0.5f); + + // * + v_flt Variable_54; // * output 0 + Variable_54 = BufferConstant.Variable_48 * Variable_52 * Variable_53 * v_flt(0.1f); + + // * + v_flt Variable_38; // * output 0 + Variable_38 = Variable_37 * BufferConstant.Variable_39; + + // * + v_flt Variable_47; // * output 0 + Variable_47 = Variable_46 * BufferConstant.Variable_48 * Variable_52; + + // Min (float) + v_flt Variable_21; // Min (float) output 0 + Variable_21 = FVoxelNodeFunctions::Min(Variable_20, v_flt(1.0f)); + + // 1 - X + v_flt Variable_75; // 1 - X output 0 + Variable_75 = 1 - Variable_68; + + // 1 - X + v_flt Variable_55; // 1 - X output 0 + Variable_55 = 1 - Variable_52; + + // * + v_flt Variable_76; // * output 0 + Variable_76 = Variable_75 * v_flt(20.0f); + + // / + v_flt Variable_88; // / output 0 + Variable_88 = Variable_76 / BufferConstant.Variable_89; + + // Float Curve: RiverDepthCurve + v_flt Variable_63; // Float Curve: RiverDepthCurve output 0 + Variable_63 = FVoxelNodeFunctions::GetCurveValue(Params.RiverDepthCurve, Variable_88); + + // Max (float) + v_flt Variable_78; // Max (float) output 0 + Variable_78 = FVoxelNodeFunctions::Max(Variable_63, v_flt(0.0f)); + + // Min (float) + v_flt Variable_79; // Min (float) output 0 + Variable_79 = FVoxelNodeFunctions::Min(Variable_63, v_flt(0.0f)); + + // * + v_flt Variable_64; // * output 0 + Variable_64 = Variable_78 * v_flt(-1.0f) * BufferConstant.Variable_65; + + // * + v_flt Variable_80; // * output 0 + Variable_80 = Variable_79 * v_flt(-1.0f); + + // 1 - X + v_flt Variable_82; // 1 - X output 0 + Variable_82 = 1 - Variable_80; + + // * + v_flt Variable_81; // * output 0 + Variable_81 = Variable_62 * Variable_80; + + // - + v_flt Variable_83; // - output 0 + Variable_83 = Variable_81 - Variable_82; + + // * + v_flt Variable_56; // * output 0 + Variable_56 = Variable_83 * BufferConstant.Variable_57 * Variable_55 * Variable_61; + + // + + v_flt Variable_32; // + output 0 + Variable_32 = Variable_38 + Variable_41 + Variable_47 + Variable_54 + Variable_56 + Variable_64; + + // * + v_flt Variable_87; // * output 0 + Variable_87 = Variable_32 * BufferConstant.Variable_84; + + // - + v_flt Variable_31; // - output 0 + Variable_31 = BufferConstant.Variable_85 - Variable_87; + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_31 - Variable_29; + + // Max (float) + v_flt Variable_8; // Max (float) output 0 + Variable_8 = FVoxelNodeFunctions::Max(Variable_4, Variable_7); + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_8 - v_flt(1.0f); + + // * + v_flt Variable_16; // * output 0 + Variable_16 = Variable_19 * Variable_21; + + // + + v_flt Variable_17; // + output 0 + Variable_17 = Variable_16 + v_flt(1.0f); + + Outputs.Value = Variable_17; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Z + v_flt Variable_70; // Z output 0 + Variable_70 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // X + v_flt Variable_24; // X output 0 + Variable_24 = Context.GetLocalX(); + + // Z + v_flt Variable_12; // Z output 0 + Variable_12 = Context.GetLocalZ(); + + // Y + v_flt Variable_28; // Y output 0 + Variable_28 = Context.GetLocalY(); + + // Atan2 + v_flt Variable_23; // Atan2 output 0 + Variable_23 = FVoxelNodeFunctions::Atan2(Variable_28, Variable_24); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // / + v_flt Variable_25; // / output 0 + Variable_25 = Variable_23 / v_flt(3.141593f); + + // + + v_flt Variable_29; // + output 0 + Variable_29 = Variable_3 + v_flt(0.001f); + + // / + v_flt Variable_13; // / output 0 + Variable_13 = Variable_12 / Variable_29; + + // - + v_flt Variable_7; // - output 0 + Variable_7 = Variable_29 - BufferConstant.Variable_6; + + // / + v_flt Variable_71; // / output 0 + Variable_71 = Variable_70 / Variable_29; + + // * + v_flt Variable_27; // * output 0 + Variable_27 = BufferConstant.Variable_86 * Variable_25; + + // ACOS + v_flt Variable_14; // ACOS output 0 + Variable_14 = FVoxelNodeFunctions::Acos(Variable_13); + + // * + v_flt Variable_44; // * output 0 + Variable_44 = Variable_27 * BufferConstant.Variable_45; + + // * + v_flt Variable_59; // * output 0 + Variable_59 = Variable_27 * BufferConstant.Variable_60; + + // * + v_flt Variable_50; // * output 0 + Variable_50 = Variable_27 * BufferConstant.Variable_51; + + // 2D Simplex Noise + v_flt Variable_77; // 2D Simplex Noise output 0 + Variable_77 = _2D_Simplex_Noise_0_Noise.GetSimplex_2D(Variable_27, Variable_27, v_flt(0.001f)); + Variable_77 = FMath::Clamp(Variable_77, -0.997888, 0.997883); + + // * + v_flt Variable_74; // * output 0 + Variable_74 = Variable_77 * v_flt(0.1f); + + // / + v_flt Variable_10; // / output 0 + Variable_10 = Variable_14 / v_flt(3.141593f); + + // 1 - X + v_flt Variable_11; // 1 - X output 0 + Variable_11 = 1 - Variable_10; + + // * + v_flt Variable_26; // * output 0 + Variable_26 = BufferConstant.Variable_86 * Variable_10; + + // + + v_flt Variable_73; // + output 0 + Variable_73 = Variable_71 + Variable_74; + + // Min (float) + v_flt Variable_9; // Min (float) output 0 + Variable_9 = FVoxelNodeFunctions::Min(Variable_10, Variable_11); + + // * + v_flt Variable_43; // * output 0 + Variable_43 = Variable_26 * BufferConstant.Variable_45; + + // * + v_flt Variable_49; // * output 0 + Variable_49 = Variable_26 * BufferConstant.Variable_51; + + // ACOS + v_flt Variable_72; // ACOS output 0 + Variable_72 = FVoxelNodeFunctions::Acos(Variable_73); + + // * + v_flt Variable_58; // * output 0 + Variable_58 = Variable_26 * BufferConstant.Variable_60; + + // - + v_flt Variable_15; // - output 0 + Variable_15 = Variable_9 - BufferConstant.Variable_35; + + // 2D Simplex Noise Fractal + v_flt Variable_40; // 2D Simplex Noise Fractal output 0 + Variable_40 = _2D_Simplex_Noise_Fractal_0_Noise.GetSimplexFractal_2D(Variable_43, Variable_44, v_flt(0.02f), _2D_Simplex_Noise_Fractal_0_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_40 = FMath::Clamp(Variable_40, -0.802678, 0.846347); + + // 2D Simplex Noise + v_flt Variable_62; // 2D Simplex Noise output 0 + Variable_62 = _2D_Simplex_Noise_1_Noise.GetSimplex_2D(Variable_58, Variable_59, v_flt(0.02f)); + Variable_62 = FMath::Clamp(Variable_62, -0.997652, 0.996970); + + // 2D Perlin Noise + v_flt Variable_53; // 2D Perlin Noise output 0 + Variable_53 = _2D_Perlin_Noise_0_Noise.GetPerlin_2D(Variable_49, Variable_50, v_flt(0.1f)); + Variable_53 = FMath::Clamp(Variable_53, -0.888317, 0.811183); + + // 2D Simplex Noise Fractal + v_flt Variable_46; // 2D Simplex Noise Fractal output 0 + Variable_46 = _2D_Simplex_Noise_Fractal_1_Noise.GetSimplexFractal_2D(Variable_49, Variable_50, v_flt(0.02f), _2D_Simplex_Noise_Fractal_1_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_46 = FMath::Clamp(Variable_46, -0.780446, 0.760988); + + // / + v_flt Variable_69; // / output 0 + Variable_69 = Variable_72 / v_flt(3.141593f); + + // / + v_flt Variable_34; // / output 0 + Variable_34 = Variable_15 / BufferConstant.Variable_36; + + // 1 - X + v_flt Variable_67; // 1 - X output 0 + Variable_67 = 1 - Variable_69; + + // * + v_flt Variable_41; // * output 0 + Variable_41 = Variable_40 * BufferConstant.Variable_42; + + // 1 - X + v_flt Variable_33; // 1 - X output 0 + Variable_33 = 1 - Variable_34; + + // Max (float) + v_flt Variable_18; // Max (float) output 0 + Variable_18 = FVoxelNodeFunctions::Max(Variable_34, v_flt(0.0f)); + + // Min (float) + v_flt Variable_66; // Min (float) output 0 + Variable_66 = FVoxelNodeFunctions::Min(Variable_69, Variable_67); + + // Float Curve: PlainsNoiseStrengthCurve + v_flt Variable_61; // Float Curve: PlainsNoiseStrengthCurve output 0 + Variable_61 = FVoxelNodeFunctions::GetCurveValue(Params.PlainsNoiseStrengthCurve, Variable_33); + + // Float Curve: RingMainShapeCurve + v_flt Variable_37; // Float Curve: RingMainShapeCurve output 0 + Variable_37 = FVoxelNodeFunctions::GetCurveValue(Params.RingMainShapeCurve, Variable_33); + + // Float Curve: MoutainsMaskCurve + v_flt Variable_52; // Float Curve: MoutainsMaskCurve output 0 + Variable_52 = FVoxelNodeFunctions::GetCurveValue(Params.MoutainsMaskCurve, Variable_33); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = Variable_18 * BufferConstant.Variable_22; + + // / + v_flt Variable_68; // / output 0 + Variable_68 = Variable_66 / v_flt(0.5f); + + // * + v_flt Variable_54; // * output 0 + Variable_54 = BufferConstant.Variable_48 * Variable_52 * Variable_53 * v_flt(0.1f); + + // * + v_flt Variable_38; // * output 0 + Variable_38 = Variable_37 * BufferConstant.Variable_39; + + // * + v_flt Variable_47; // * output 0 + Variable_47 = Variable_46 * BufferConstant.Variable_48 * Variable_52; + + // Min (float) + v_flt Variable_21; // Min (float) output 0 + Variable_21 = FVoxelNodeFunctions::Min(Variable_20, v_flt(1.0f)); + + // 1 - X + v_flt Variable_75; // 1 - X output 0 + Variable_75 = 1 - Variable_68; + + // 1 - X + v_flt Variable_55; // 1 - X output 0 + Variable_55 = 1 - Variable_52; + + // * + v_flt Variable_76; // * output 0 + Variable_76 = Variable_75 * v_flt(20.0f); + + // / + v_flt Variable_88; // / output 0 + Variable_88 = Variable_76 / BufferConstant.Variable_89; + + // Float Curve: RiverDepthCurve + v_flt Variable_63; // Float Curve: RiverDepthCurve output 0 + Variable_63 = FVoxelNodeFunctions::GetCurveValue(Params.RiverDepthCurve, Variable_88); + + // Max (float) + v_flt Variable_78; // Max (float) output 0 + Variable_78 = FVoxelNodeFunctions::Max(Variable_63, v_flt(0.0f)); + + // Min (float) + v_flt Variable_79; // Min (float) output 0 + Variable_79 = FVoxelNodeFunctions::Min(Variable_63, v_flt(0.0f)); + + // * + v_flt Variable_64; // * output 0 + Variable_64 = Variable_78 * v_flt(-1.0f) * BufferConstant.Variable_65; + + // * + v_flt Variable_80; // * output 0 + Variable_80 = Variable_79 * v_flt(-1.0f); + + // 1 - X + v_flt Variable_82; // 1 - X output 0 + Variable_82 = 1 - Variable_80; + + // * + v_flt Variable_81; // * output 0 + Variable_81 = Variable_62 * Variable_80; + + // - + v_flt Variable_83; // - output 0 + Variable_83 = Variable_81 - Variable_82; + + // * + v_flt Variable_56; // * output 0 + Variable_56 = Variable_83 * BufferConstant.Variable_57 * Variable_55 * Variable_61; + + // + + v_flt Variable_32; // + output 0 + Variable_32 = Variable_38 + Variable_41 + Variable_47 + Variable_54 + Variable_56 + Variable_64; + + // * + v_flt Variable_87; // * output 0 + Variable_87 = Variable_32 * BufferConstant.Variable_84; + + // - + v_flt Variable_31; // - output 0 + Variable_31 = BufferConstant.Variable_85 - Variable_87; + + // - + v_flt Variable_4; // - output 0 + Variable_4 = Variable_31 - Variable_29; + + // Max (float) + v_flt Variable_8; // Max (float) output 0 + Variable_8 = FVoxelNodeFunctions::Max(Variable_4, Variable_7); + + // - + v_flt Variable_19; // - output 0 + Variable_19 = Variable_8 - v_flt(1.0f); + + // * + v_flt Variable_16; // * output 0 + Variable_16 = Variable_19 * Variable_21; + + // + + v_flt Variable_17; // + output 0 + Variable_17 = Variable_16 + v_flt(1.0f); + + Outputs.Value = Variable_17; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_15; // RingEdgesHardness = 10.0 output 0 + v_flt Variable_31; // MountainsNoiseFrequency = 0.2 output 0 + v_flt Variable_72; // RiverWidth = 1.0 output 0 + v_flt Variable_39; // PlainsNoiseFrequency = 0.2 output 0 + v_flt Variable_27; // / output 0 + v_flt Variable_117; // Lerp Colors.Break Color output 0 + v_flt Variable_118; // Lerp Colors.Break Color output 1 + v_flt Variable_119; // Lerp Colors.Break Color output 2 + v_flt Variable_120; // Lerp Colors.Break Color output 3 + v_flt Variable_95; // Lerp Colors.Break Color output 0 + v_flt Variable_96; // Lerp Colors.Break Color output 1 + v_flt Variable_97; // Lerp Colors.Break Color output 2 + v_flt Variable_98; // Lerp Colors.Break Color output 3 + v_flt Variable_91; // Lerp Colors.Break Color output 0 + v_flt Variable_92; // Lerp Colors.Break Color output 1 + v_flt Variable_93; // Lerp Colors.Break Color output 2 + v_flt Variable_94; // Lerp Colors.Break Color output 3 + v_flt Variable_147; // Lerp Colors.Break Color output 0 + v_flt Variable_148; // Lerp Colors.Break Color output 1 + v_flt Variable_149; // Lerp Colors.Break Color output 2 + v_flt Variable_150; // Lerp Colors.Break Color output 3 + v_flt Variable_169; // Lerp Colors.Break Color output 0 + v_flt Variable_170; // Lerp Colors.Break Color output 1 + v_flt Variable_171; // Lerp Colors.Break Color output 2 + v_flt Variable_172; // Lerp Colors.Break Color output 3 + v_flt Variable_121; // Lerp Colors.Break Color output 0 + v_flt Variable_122; // Lerp Colors.Break Color output 1 + v_flt Variable_123; // Lerp Colors.Break Color output 2 + v_flt Variable_124; // Lerp Colors.Break Color output 3 + v_flt Variable_130; // Lerp Colors.Break Color output 0 + v_flt Variable_131; // Lerp Colors.Break Color output 1 + v_flt Variable_132; // Lerp Colors.Break Color output 2 + v_flt Variable_133; // Lerp Colors.Break Color output 3 + v_flt Variable_134; // Lerp Colors.Break Color output 0 + v_flt Variable_135; // Lerp Colors.Break Color output 1 + v_flt Variable_136; // Lerp Colors.Break Color output 2 + v_flt Variable_137; // Lerp Colors.Break Color output 3 + v_flt Variable_26; // - output 0 + v_flt Variable_70; // / output 0 + v_flt Variable_75; // + output 0 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_0; // X output 0 + v_flt Variable_17; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_1; // Y output 0 + v_flt Variable_30; // * output 0 + v_flt Variable_38; // * output 0 + v_flt Variable_56; // * output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // MountainsColorLowHigh + FColor Variable_36; // MountainsColorLowHigh output 0 + Variable_36 = Params.MountainsColorLowHigh; + + // RingEdgesHardness = 10.0 + BufferConstant.Variable_15 = Params.RingEdgesHardness; + + // Width in Degrees = 50.0 + v_flt Variable_23; // Width in Degrees = 50.0 output 0 + Variable_23 = Params.Width_in_Degrees; + + // RingOuterColor + FColor Variable_80; // RingOuterColor output 0 + Variable_80 = Params.RingOuterColor; + + // PlainsColorHigh + FColor Variable_40; // PlainsColorHigh output 0 + Variable_40 = Params.PlainsColorHigh; + + // MountainsNoiseFrequency = 0.2 + BufferConstant.Variable_31 = Params.MountainsNoiseFrequency; + + // MountainsColorHigh + FColor Variable_33; // MountainsColorHigh output 0 + Variable_33 = Params.MountainsColorHigh; + + // MountainsColorLowLow + FColor Variable_34; // MountainsColorLowLow output 0 + Variable_34 = Params.MountainsColorLowLow; + + // RiverWidth = 1.0 + BufferConstant.Variable_72 = Params.RiverWidth; + + // PlainsNoiseFrequency = 0.2 + BufferConstant.Variable_39 = Params.PlainsNoiseFrequency; + + // PlainsColorLow + FColor Variable_43; // PlainsColorLow output 0 + Variable_43 = Params.PlainsColorLow; + + // RiverColor + FColor Variable_47; // RiverColor output 0 + Variable_47 = Params.RiverColor; + + // BeachColor + FColor Variable_66; // BeachColor output 0 + Variable_66 = Params.BeachColor; + + // Scale = 10.0 + v_flt Variable_68; // Scale = 10.0 output 0 + Variable_68 = Params.Scale; + + // Radius = 7000.0 + v_flt Variable_4; // Radius = 7000.0 output 0 + Variable_4 = Params.Radius; + + // Thickness = 500.0 + v_flt Variable_76; // Thickness = 500.0 output 0 + Variable_76 = Params.Thickness; + + // / + BufferConstant.Variable_27 = Variable_23 / v_flt(360.0f); + + // * + v_flt Variable_69; // * output 0 + Variable_69 = Variable_4 * Variable_68; + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_66, BufferConstant.Variable_117, BufferConstant.Variable_118, BufferConstant.Variable_119, BufferConstant.Variable_120); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_40, BufferConstant.Variable_95, BufferConstant.Variable_96, BufferConstant.Variable_97, BufferConstant.Variable_98); + + // * + v_flt Variable_77; // * output 0 + Variable_77 = Variable_76 * Variable_68; + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_43, BufferConstant.Variable_91, BufferConstant.Variable_92, BufferConstant.Variable_93, BufferConstant.Variable_94); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_33, BufferConstant.Variable_147, BufferConstant.Variable_148, BufferConstant.Variable_149, BufferConstant.Variable_150); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_80, BufferConstant.Variable_169, BufferConstant.Variable_170, BufferConstant.Variable_171, BufferConstant.Variable_172); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_47, BufferConstant.Variable_121, BufferConstant.Variable_122, BufferConstant.Variable_123, BufferConstant.Variable_124); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_34, BufferConstant.Variable_130, BufferConstant.Variable_131, BufferConstant.Variable_132, BufferConstant.Variable_133); + + // Lerp Colors.Break Color + FVoxelNodeFunctions::BreakColorFloat(Variable_36, BufferConstant.Variable_134, BufferConstant.Variable_135, BufferConstant.Variable_136, BufferConstant.Variable_137); + + // - + BufferConstant.Variable_26 = v_flt(0.5f) - BufferConstant.Variable_27; + + // / + BufferConstant.Variable_70 = Variable_69 / Variable_68; + + // + + BufferConstant.Variable_75 = Variable_69 + Variable_77 + v_flt(-5.0f); + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Simplex_Noise_2_Noise; + FVoxelFastNoise _2D_Perlin_Noise_1_Noise; + FVoxelFastNoise _2D_Simplex_Noise_3_Noise; + FVoxelFastNoise _2D_Simplex_Noise_Fractal_2_Noise; + TStaticArray _2D_Simplex_Noise_Fractal_2_LODToOctaves; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D Simplex Noise + _2D_Simplex_Noise_2_Noise.SetSeed(FVoxelGraphSeed(1000)); + _2D_Simplex_Noise_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Perlin Noise + _2D_Perlin_Noise_1_Noise.SetSeed(FVoxelGraphSeed(1338)); + _2D_Perlin_Noise_1_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise + _2D_Simplex_Noise_3_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise Fractal + _2D_Simplex_Noise_Fractal_2_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_Fractal_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Simplex_Noise_Fractal_2_Noise.SetFractalOctavesAndGain(8, 0.5); + _2D_Simplex_Noise_Fractal_2_Noise.SetFractalLacunarity(2.0); + _2D_Simplex_Noise_Fractal_2_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Simplex_Noise_Fractal_2_LODToOctaves[0] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[1] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[2] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[3] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[4] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[5] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[6] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[7] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[8] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[9] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[10] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[11] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[12] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[13] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[14] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[15] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[16] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[17] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[18] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[19] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[20] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[21] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[22] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[23] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[24] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[25] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[26] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[27] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[28] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[29] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[30] = 8; + _2D_Simplex_Noise_Fractal_2_LODToOctaves[31] = 8; + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // X + BufferX.Variable_17 = Context.GetLocalX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // Y + v_flt Variable_21; // Y output 0 + Variable_21 = Context.GetLocalY(); + + // Atan2 + v_flt Variable_16; // Atan2 output 0 + Variable_16 = FVoxelNodeFunctions::Atan2(Variable_21, BufferX.Variable_17); + + // / + v_flt Variable_18; // / output 0 + Variable_18 = Variable_16 / v_flt(3.141593f); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = BufferConstant.Variable_70 * Variable_18; + + // * + BufferXY.Variable_30 = Variable_20 * BufferConstant.Variable_31; + + // 2D Simplex Noise + v_flt Variable_59; // 2D Simplex Noise output 0 + Variable_59 = _2D_Simplex_Noise_2_Noise.GetSimplex_2D(Variable_20, Variable_20, v_flt(0.001f)); + Variable_59 = FMath::Clamp(Variable_59, -0.997888, 0.997883); + + // * + BufferXY.Variable_38 = Variable_20 * BufferConstant.Variable_39; + + // * + BufferXY.Variable_56 = Variable_59 * v_flt(0.1f); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // X + BufferX.Variable_0 = Context.GetLocalX(); + + // Y + BufferXY.Variable_1 = Context.GetLocalY(); + + // X + BufferX.Variable_17 = Context.GetLocalX(); + + // Y + v_flt Variable_21; // Y output 0 + Variable_21 = Context.GetLocalY(); + + // Atan2 + v_flt Variable_16; // Atan2 output 0 + Variable_16 = FVoxelNodeFunctions::Atan2(Variable_21, BufferX.Variable_17); + + // / + v_flt Variable_18; // / output 0 + Variable_18 = Variable_16 / v_flt(3.141593f); + + // * + v_flt Variable_20; // * output 0 + Variable_20 = BufferConstant.Variable_70 * Variable_18; + + // * + BufferXY.Variable_30 = Variable_20 * BufferConstant.Variable_31; + + // 2D Simplex Noise + v_flt Variable_59; // 2D Simplex Noise output 0 + Variable_59 = _2D_Simplex_Noise_2_Noise.GetSimplex_2D(Variable_20, Variable_20, v_flt(0.001f)); + Variable_59 = FMath::Clamp(Variable_59, -0.997888, 0.997883); + + // * + BufferXY.Variable_38 = Variable_20 * BufferConstant.Variable_39; + + // * + BufferXY.Variable_56 = Variable_59 * v_flt(0.1f); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_52; // Z output 0 + Variable_52 = Context.GetLocalZ(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(BufferX.Variable_0, BufferXY.Variable_1, Variable_2); + + // + + v_flt Variable_22; // + output 0 + Variable_22 = Variable_3 + v_flt(0.001f); + + // / + v_flt Variable_9; // / output 0 + Variable_9 = Variable_8 / Variable_22; + + // - + v_flt Variable_79; // - output 0 + Variable_79 = BufferConstant.Variable_75 - Variable_22; + + // / + v_flt Variable_53; // / output 0 + Variable_53 = Variable_52 / Variable_22; + + // Clamp + v_flt Variable_73; // Clamp output 0 + Variable_73 = FVoxelNodeFunctions::Clamp(Variable_79, v_flt(0.0f), v_flt(1.0f)); + + // ACOS + v_flt Variable_10; // ACOS output 0 + Variable_10 = FVoxelNodeFunctions::Acos(Variable_9); + + // / + v_flt Variable_6; // / output 0 + Variable_6 = Variable_10 / v_flt(3.141593f); + + // + + v_flt Variable_55; // + output 0 + Variable_55 = Variable_53 + BufferXY.Variable_56; + + // * + v_flt Variable_19; // * output 0 + Variable_19 = BufferConstant.Variable_70 * Variable_6; + + // 1 - X + v_flt Variable_7; // 1 - X output 0 + Variable_7 = 1 - Variable_6; + + // * + v_flt Variable_29; // * output 0 + Variable_29 = Variable_19 * BufferConstant.Variable_31; + + // * + v_flt Variable_37; // * output 0 + Variable_37 = Variable_19 * BufferConstant.Variable_39; + + // ACOS + v_flt Variable_54; // ACOS output 0 + Variable_54 = FVoxelNodeFunctions::Acos(Variable_55); + + // Min (float) + v_flt Variable_5; // Min (float) output 0 + Variable_5 = FVoxelNodeFunctions::Min(Variable_6, Variable_7); + + // 2D Perlin Noise + v_flt Variable_35; // 2D Perlin Noise output 0 + Variable_35 = _2D_Perlin_Noise_1_Noise.GetPerlin_2D(Variable_29, BufferXY.Variable_30, v_flt(0.1f)); + Variable_35 = FMath::Clamp(Variable_35, -0.888317, 0.811183); + + // 2D Simplex Noise + v_flt Variable_41; // 2D Simplex Noise output 0 + Variable_41 = _2D_Simplex_Noise_3_Noise.GetSimplex_2D(Variable_37, BufferXY.Variable_38, v_flt(0.02f)); + Variable_41 = FMath::Clamp(Variable_41, -0.997652, 0.996970); + + // / + v_flt Variable_51; // / output 0 + Variable_51 = Variable_54 / v_flt(3.141593f); + + // - + v_flt Variable_11; // - output 0 + Variable_11 = Variable_5 - BufferConstant.Variable_26; + + // 2D Simplex Noise Fractal + v_flt Variable_28; // 2D Simplex Noise Fractal output 0 + Variable_28 = _2D_Simplex_Noise_Fractal_2_Noise.GetSimplexFractal_2D(Variable_29, BufferXY.Variable_30, v_flt(0.02f), _2D_Simplex_Noise_Fractal_2_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_28 = FMath::Clamp(Variable_28, -0.780446, 0.760988); + + // Lerp Colors.Clamp + v_flt Variable_84; // Lerp Colors.Clamp output 0 + Variable_84 = FVoxelNodeFunctions::Clamp(Variable_28, v_flt(0.0f), v_flt(1.0f)); + + // / + v_flt Variable_25; // / output 0 + Variable_25 = Variable_11 / BufferConstant.Variable_27; + + // Lerp Colors.Clamp + v_flt Variable_83; // Lerp Colors.Clamp output 0 + Variable_83 = FVoxelNodeFunctions::Clamp(Variable_35, v_flt(0.0f), v_flt(1.0f)); + + // 1 - X + v_flt Variable_49; // 1 - X output 0 + Variable_49 = 1 - Variable_51; + + // Min (float) + v_flt Variable_48; // Min (float) output 0 + Variable_48 = FVoxelNodeFunctions::Min(Variable_51, Variable_49); + + // 1 - X + v_flt Variable_24; // 1 - X output 0 + Variable_24 = 1 - Variable_25; + + // Lerp Colors.Lerp + v_flt Variable_126; // Lerp Colors.Lerp output 0 + Variable_126 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_130, BufferConstant.Variable_134, Variable_83); + + // Lerp Colors.Lerp + v_flt Variable_128; // Lerp Colors.Lerp output 0 + Variable_128 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_132, BufferConstant.Variable_136, Variable_83); + + // Lerp Colors.Lerp + v_flt Variable_127; // Lerp Colors.Lerp output 0 + Variable_127 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_131, BufferConstant.Variable_135, Variable_83); + + // Max (float) + v_flt Variable_12; // Max (float) output 0 + Variable_12 = FVoxelNodeFunctions::Max(Variable_25, v_flt(0.0f)); + + // Lerp Colors.Lerp + v_flt Variable_129; // Lerp Colors.Lerp output 0 + Variable_129 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_133, BufferConstant.Variable_137, Variable_83); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = Variable_12 * BufferConstant.Variable_15; + + // / + v_flt Variable_50; // / output 0 + Variable_50 = Variable_48 / v_flt(0.5f); + + // Lerp Colors.Make Color + FColor Variable_138; // Lerp Colors.Make Color output 0 + Variable_138 = FVoxelNodeFunctions::MakeColorFloat(Variable_126, Variable_127, Variable_128, Variable_129); + + // Float Curve: MoutainsMaskCurve + v_flt Variable_32; // Float Curve: MoutainsMaskCurve output 0 + Variable_32 = FVoxelNodeFunctions::GetCurveValue(Params.MoutainsMaskCurve, Variable_24); + + // Min (float) + v_flt Variable_14; // Min (float) output 0 + Variable_14 = FVoxelNodeFunctions::Min(Variable_13, v_flt(1.0f)); + + // * + v_flt Variable_42; // * output 0 + Variable_42 = Variable_32 * v_flt(3.0f); + + // 1 - X + v_flt Variable_57; // 1 - X output 0 + Variable_57 = 1 - Variable_50; + + // Lerp Colors.Break Color + v_flt Variable_143; // Lerp Colors.Break Color output 0 + v_flt Variable_144; // Lerp Colors.Break Color output 1 + v_flt Variable_145; // Lerp Colors.Break Color output 2 + v_flt Variable_146; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_138, Variable_143, Variable_144, Variable_145, Variable_146); + + // * + v_flt Variable_58; // * output 0 + Variable_58 = Variable_57 * v_flt(20.0f); + + // Lerp Colors.Lerp + v_flt Variable_141; // Lerp Colors.Lerp output 0 + Variable_141 = FVoxelNodeFunctions::Lerp(Variable_145, BufferConstant.Variable_149, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_142; // Lerp Colors.Lerp output 0 + Variable_142 = FVoxelNodeFunctions::Lerp(Variable_146, BufferConstant.Variable_150, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_140; // Lerp Colors.Lerp output 0 + Variable_140 = FVoxelNodeFunctions::Lerp(Variable_144, BufferConstant.Variable_148, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_139; // Lerp Colors.Lerp output 0 + Variable_139 = FVoxelNodeFunctions::Lerp(Variable_143, BufferConstant.Variable_147, Variable_84); + + // Min (float) + v_flt Variable_74; // Min (float) output 0 + Variable_74 = FVoxelNodeFunctions::Min(Variable_14, Variable_73); + + // Lerp Colors.Make Color + FColor Variable_151; // Lerp Colors.Make Color output 0 + Variable_151 = FVoxelNodeFunctions::MakeColorFloat(Variable_139, Variable_140, Variable_141, Variable_142); + + // Lerp Colors.Clamp + v_flt Variable_86; // Lerp Colors.Clamp output 0 + Variable_86 = FVoxelNodeFunctions::Clamp(Variable_74, v_flt(0.0f), v_flt(1.0f)); + + // / + v_flt Variable_71; // / output 0 + Variable_71 = Variable_58 / BufferConstant.Variable_72; + + // Lerp Colors.Break Color + v_flt Variable_160; // Lerp Colors.Break Color output 0 + v_flt Variable_161; // Lerp Colors.Break Color output 1 + v_flt Variable_162; // Lerp Colors.Break Color output 2 + v_flt Variable_163; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_151, Variable_160, Variable_161, Variable_162, Variable_163); + + // Float Curve: RiverDepthCurve + v_flt Variable_46; // Float Curve: RiverDepthCurve output 0 + Variable_46 = FVoxelNodeFunctions::GetCurveValue(Params.RiverDepthCurve, Variable_71); + + // Min (float) + v_flt Variable_60; // Min (float) output 0 + Variable_60 = FVoxelNodeFunctions::Min(Variable_46, v_flt(0.0f)); + + // + + v_flt Variable_65; // + output 0 + Variable_65 = Variable_46 + v_flt(0.3f); + + // * + v_flt Variable_67; // * output 0 + Variable_67 = Variable_46 * v_flt(10.0f); + + // Lerp Colors.Clamp + v_flt Variable_82; // Lerp Colors.Clamp output 0 + Variable_82 = FVoxelNodeFunctions::Clamp(Variable_67, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Clamp + v_flt Variable_81; // Lerp Colors.Clamp output 0 + Variable_81 = FVoxelNodeFunctions::Clamp(Variable_65, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_61; // * output 0 + Variable_61 = Variable_60 * v_flt(-1.0f); + + // Lerp Colors.Lerp + v_flt Variable_113; // Lerp Colors.Lerp output 0 + Variable_113 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_117, BufferConstant.Variable_121, Variable_82); + + // Lerp Colors.Lerp + v_flt Variable_115; // Lerp Colors.Lerp output 0 + Variable_115 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_119, BufferConstant.Variable_123, Variable_82); + + // * + v_flt Variable_62; // * output 0 + Variable_62 = Variable_41 * Variable_61; + + // Lerp Colors.Lerp + v_flt Variable_116; // Lerp Colors.Lerp output 0 + Variable_116 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_120, BufferConstant.Variable_124, Variable_82); + + // Lerp Colors.Lerp + v_flt Variable_114; // Lerp Colors.Lerp output 0 + Variable_114 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_118, BufferConstant.Variable_122, Variable_82); + + // 1 - X + v_flt Variable_63; // 1 - X output 0 + Variable_63 = 1 - Variable_61; + + // - + v_flt Variable_64; // - output 0 + Variable_64 = Variable_62 - Variable_63; + + // Lerp Colors.Make Color + FColor Variable_125; // Lerp Colors.Make Color output 0 + Variable_125 = FVoxelNodeFunctions::MakeColorFloat(Variable_113, Variable_114, Variable_115, Variable_116); + + // Max (float) + v_flt Variable_44; // Max (float) output 0 + Variable_44 = FVoxelNodeFunctions::Max(Variable_64, v_flt(0.0f)); + + // Lerp Colors.Clamp + v_flt Variable_78; // Lerp Colors.Clamp output 0 + Variable_78 = FVoxelNodeFunctions::Clamp(Variable_64, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Break Color + v_flt Variable_108; // Lerp Colors.Break Color output 0 + v_flt Variable_109; // Lerp Colors.Break Color output 1 + v_flt Variable_110; // Lerp Colors.Break Color output 2 + v_flt Variable_111; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_125, Variable_108, Variable_109, Variable_110, Variable_111); + + // - + v_flt Variable_45; // - output 0 + Variable_45 = Variable_42 - Variable_44; + + // Lerp Colors.Lerp + v_flt Variable_88; // Lerp Colors.Lerp output 0 + Variable_88 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_92, BufferConstant.Variable_96, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_89; // Lerp Colors.Lerp output 0 + Variable_89 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_93, BufferConstant.Variable_97, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_90; // Lerp Colors.Lerp output 0 + Variable_90 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_94, BufferConstant.Variable_98, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_87; // Lerp Colors.Lerp output 0 + Variable_87 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_91, BufferConstant.Variable_95, Variable_78); + + // Lerp Colors.Make Color + FColor Variable_99; // Lerp Colors.Make Color output 0 + Variable_99 = FVoxelNodeFunctions::MakeColorFloat(Variable_87, Variable_88, Variable_89, Variable_90); + + // Lerp Colors.Clamp + v_flt Variable_85; // Lerp Colors.Clamp output 0 + Variable_85 = FVoxelNodeFunctions::Clamp(Variable_45, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Break Color + v_flt Variable_104; // Lerp Colors.Break Color output 0 + v_flt Variable_105; // Lerp Colors.Break Color output 1 + v_flt Variable_106; // Lerp Colors.Break Color output 2 + v_flt Variable_107; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_99, Variable_104, Variable_105, Variable_106, Variable_107); + + // Lerp Colors.Lerp + v_flt Variable_101; // Lerp Colors.Lerp output 0 + Variable_101 = FVoxelNodeFunctions::Lerp(Variable_105, Variable_109, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_103; // Lerp Colors.Lerp output 0 + Variable_103 = FVoxelNodeFunctions::Lerp(Variable_107, Variable_111, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_102; // Lerp Colors.Lerp output 0 + Variable_102 = FVoxelNodeFunctions::Lerp(Variable_106, Variable_110, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_100; // Lerp Colors.Lerp output 0 + Variable_100 = FVoxelNodeFunctions::Lerp(Variable_104, Variable_108, Variable_81); + + // Lerp Colors.Make Color + FColor Variable_112; // Lerp Colors.Make Color output 0 + Variable_112 = FVoxelNodeFunctions::MakeColorFloat(Variable_100, Variable_101, Variable_102, Variable_103); + + // Lerp Colors.Break Color + v_flt Variable_156; // Lerp Colors.Break Color output 0 + v_flt Variable_157; // Lerp Colors.Break Color output 1 + v_flt Variable_158; // Lerp Colors.Break Color output 2 + v_flt Variable_159; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_112, Variable_156, Variable_157, Variable_158, Variable_159); + + // Lerp Colors.Lerp + v_flt Variable_154; // Lerp Colors.Lerp output 0 + Variable_154 = FVoxelNodeFunctions::Lerp(Variable_158, Variable_162, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_153; // Lerp Colors.Lerp output 0 + Variable_153 = FVoxelNodeFunctions::Lerp(Variable_157, Variable_161, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_155; // Lerp Colors.Lerp output 0 + Variable_155 = FVoxelNodeFunctions::Lerp(Variable_159, Variable_163, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_152; // Lerp Colors.Lerp output 0 + Variable_152 = FVoxelNodeFunctions::Lerp(Variable_156, Variable_160, Variable_85); + + // Lerp Colors.Make Color + FColor Variable_164; // Lerp Colors.Make Color output 0 + Variable_164 = FVoxelNodeFunctions::MakeColorFloat(Variable_152, Variable_153, Variable_154, Variable_155); + + // Lerp Colors.Break Color + v_flt Variable_173; // Lerp Colors.Break Color output 0 + v_flt Variable_174; // Lerp Colors.Break Color output 1 + v_flt Variable_175; // Lerp Colors.Break Color output 2 + v_flt Variable_176; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_164, Variable_173, Variable_174, Variable_175, Variable_176); + + // Lerp Colors.Lerp + v_flt Variable_165; // Lerp Colors.Lerp output 0 + Variable_165 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_169, Variable_173, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_166; // Lerp Colors.Lerp output 0 + Variable_166 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_170, Variable_174, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_167; // Lerp Colors.Lerp output 0 + Variable_167 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_171, Variable_175, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_168; // Lerp Colors.Lerp output 0 + Variable_168 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_172, Variable_176, Variable_86); + + // Lerp Colors.Make Color + FColor Variable_177; // Lerp Colors.Make Color output 0 + Variable_177 = FVoxelNodeFunctions::MakeColorFloat(Variable_165, Variable_166, Variable_167, Variable_168); + + Outputs.MaterialBuilder.SetColor(Variable_177); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // X + v_flt Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Z + v_flt Variable_8; // Z output 0 + Variable_8 = Context.GetLocalZ(); + + // Z + v_flt Variable_52; // Z output 0 + Variable_52 = Context.GetLocalZ(); + + // Y + v_flt Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // X + v_flt Variable_17; // X output 0 + Variable_17 = Context.GetLocalX(); + + // Y + v_flt Variable_21; // Y output 0 + Variable_21 = Context.GetLocalY(); + + // Z + v_flt Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Atan2 + v_flt Variable_16; // Atan2 output 0 + Variable_16 = FVoxelNodeFunctions::Atan2(Variable_21, Variable_17); + + // Vector Length + v_flt Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // / + v_flt Variable_18; // / output 0 + Variable_18 = Variable_16 / v_flt(3.141593f); + + // + + v_flt Variable_22; // + output 0 + Variable_22 = Variable_3 + v_flt(0.001f); + + // / + v_flt Variable_9; // / output 0 + Variable_9 = Variable_8 / Variable_22; + + // - + v_flt Variable_79; // - output 0 + Variable_79 = BufferConstant.Variable_75 - Variable_22; + + // / + v_flt Variable_53; // / output 0 + Variable_53 = Variable_52 / Variable_22; + + // * + v_flt Variable_20; // * output 0 + Variable_20 = BufferConstant.Variable_70 * Variable_18; + + // Clamp + v_flt Variable_73; // Clamp output 0 + Variable_73 = FVoxelNodeFunctions::Clamp(Variable_79, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_30; // * output 0 + Variable_30 = Variable_20 * BufferConstant.Variable_31; + + // 2D Simplex Noise + v_flt Variable_59; // 2D Simplex Noise output 0 + Variable_59 = _2D_Simplex_Noise_2_Noise.GetSimplex_2D(Variable_20, Variable_20, v_flt(0.001f)); + Variable_59 = FMath::Clamp(Variable_59, -0.997888, 0.997883); + + // ACOS + v_flt Variable_10; // ACOS output 0 + Variable_10 = FVoxelNodeFunctions::Acos(Variable_9); + + // * + v_flt Variable_38; // * output 0 + Variable_38 = Variable_20 * BufferConstant.Variable_39; + + // * + v_flt Variable_56; // * output 0 + Variable_56 = Variable_59 * v_flt(0.1f); + + // / + v_flt Variable_6; // / output 0 + Variable_6 = Variable_10 / v_flt(3.141593f); + + // + + v_flt Variable_55; // + output 0 + Variable_55 = Variable_53 + Variable_56; + + // * + v_flt Variable_19; // * output 0 + Variable_19 = BufferConstant.Variable_70 * Variable_6; + + // 1 - X + v_flt Variable_7; // 1 - X output 0 + Variable_7 = 1 - Variable_6; + + // * + v_flt Variable_29; // * output 0 + Variable_29 = Variable_19 * BufferConstant.Variable_31; + + // * + v_flt Variable_37; // * output 0 + Variable_37 = Variable_19 * BufferConstant.Variable_39; + + // ACOS + v_flt Variable_54; // ACOS output 0 + Variable_54 = FVoxelNodeFunctions::Acos(Variable_55); + + // Min (float) + v_flt Variable_5; // Min (float) output 0 + Variable_5 = FVoxelNodeFunctions::Min(Variable_6, Variable_7); + + // 2D Perlin Noise + v_flt Variable_35; // 2D Perlin Noise output 0 + Variable_35 = _2D_Perlin_Noise_1_Noise.GetPerlin_2D(Variable_29, Variable_30, v_flt(0.1f)); + Variable_35 = FMath::Clamp(Variable_35, -0.888317, 0.811183); + + // 2D Simplex Noise + v_flt Variable_41; // 2D Simplex Noise output 0 + Variable_41 = _2D_Simplex_Noise_3_Noise.GetSimplex_2D(Variable_37, Variable_38, v_flt(0.02f)); + Variable_41 = FMath::Clamp(Variable_41, -0.997652, 0.996970); + + // / + v_flt Variable_51; // / output 0 + Variable_51 = Variable_54 / v_flt(3.141593f); + + // - + v_flt Variable_11; // - output 0 + Variable_11 = Variable_5 - BufferConstant.Variable_26; + + // 2D Simplex Noise Fractal + v_flt Variable_28; // 2D Simplex Noise Fractal output 0 + Variable_28 = _2D_Simplex_Noise_Fractal_2_Noise.GetSimplexFractal_2D(Variable_29, Variable_30, v_flt(0.02f), _2D_Simplex_Noise_Fractal_2_LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)]); + Variable_28 = FMath::Clamp(Variable_28, -0.780446, 0.760988); + + // Lerp Colors.Clamp + v_flt Variable_84; // Lerp Colors.Clamp output 0 + Variable_84 = FVoxelNodeFunctions::Clamp(Variable_28, v_flt(0.0f), v_flt(1.0f)); + + // / + v_flt Variable_25; // / output 0 + Variable_25 = Variable_11 / BufferConstant.Variable_27; + + // Lerp Colors.Clamp + v_flt Variable_83; // Lerp Colors.Clamp output 0 + Variable_83 = FVoxelNodeFunctions::Clamp(Variable_35, v_flt(0.0f), v_flt(1.0f)); + + // 1 - X + v_flt Variable_49; // 1 - X output 0 + Variable_49 = 1 - Variable_51; + + // Min (float) + v_flt Variable_48; // Min (float) output 0 + Variable_48 = FVoxelNodeFunctions::Min(Variable_51, Variable_49); + + // 1 - X + v_flt Variable_24; // 1 - X output 0 + Variable_24 = 1 - Variable_25; + + // Lerp Colors.Lerp + v_flt Variable_126; // Lerp Colors.Lerp output 0 + Variable_126 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_130, BufferConstant.Variable_134, Variable_83); + + // Lerp Colors.Lerp + v_flt Variable_128; // Lerp Colors.Lerp output 0 + Variable_128 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_132, BufferConstant.Variable_136, Variable_83); + + // Lerp Colors.Lerp + v_flt Variable_127; // Lerp Colors.Lerp output 0 + Variable_127 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_131, BufferConstant.Variable_135, Variable_83); + + // Max (float) + v_flt Variable_12; // Max (float) output 0 + Variable_12 = FVoxelNodeFunctions::Max(Variable_25, v_flt(0.0f)); + + // Lerp Colors.Lerp + v_flt Variable_129; // Lerp Colors.Lerp output 0 + Variable_129 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_133, BufferConstant.Variable_137, Variable_83); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = Variable_12 * BufferConstant.Variable_15; + + // / + v_flt Variable_50; // / output 0 + Variable_50 = Variable_48 / v_flt(0.5f); + + // Lerp Colors.Make Color + FColor Variable_138; // Lerp Colors.Make Color output 0 + Variable_138 = FVoxelNodeFunctions::MakeColorFloat(Variable_126, Variable_127, Variable_128, Variable_129); + + // Float Curve: MoutainsMaskCurve + v_flt Variable_32; // Float Curve: MoutainsMaskCurve output 0 + Variable_32 = FVoxelNodeFunctions::GetCurveValue(Params.MoutainsMaskCurve, Variable_24); + + // Min (float) + v_flt Variable_14; // Min (float) output 0 + Variable_14 = FVoxelNodeFunctions::Min(Variable_13, v_flt(1.0f)); + + // * + v_flt Variable_42; // * output 0 + Variable_42 = Variable_32 * v_flt(3.0f); + + // 1 - X + v_flt Variable_57; // 1 - X output 0 + Variable_57 = 1 - Variable_50; + + // Lerp Colors.Break Color + v_flt Variable_143; // Lerp Colors.Break Color output 0 + v_flt Variable_144; // Lerp Colors.Break Color output 1 + v_flt Variable_145; // Lerp Colors.Break Color output 2 + v_flt Variable_146; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_138, Variable_143, Variable_144, Variable_145, Variable_146); + + // * + v_flt Variable_58; // * output 0 + Variable_58 = Variable_57 * v_flt(20.0f); + + // Lerp Colors.Lerp + v_flt Variable_141; // Lerp Colors.Lerp output 0 + Variable_141 = FVoxelNodeFunctions::Lerp(Variable_145, BufferConstant.Variable_149, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_142; // Lerp Colors.Lerp output 0 + Variable_142 = FVoxelNodeFunctions::Lerp(Variable_146, BufferConstant.Variable_150, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_140; // Lerp Colors.Lerp output 0 + Variable_140 = FVoxelNodeFunctions::Lerp(Variable_144, BufferConstant.Variable_148, Variable_84); + + // Lerp Colors.Lerp + v_flt Variable_139; // Lerp Colors.Lerp output 0 + Variable_139 = FVoxelNodeFunctions::Lerp(Variable_143, BufferConstant.Variable_147, Variable_84); + + // Min (float) + v_flt Variable_74; // Min (float) output 0 + Variable_74 = FVoxelNodeFunctions::Min(Variable_14, Variable_73); + + // Lerp Colors.Make Color + FColor Variable_151; // Lerp Colors.Make Color output 0 + Variable_151 = FVoxelNodeFunctions::MakeColorFloat(Variable_139, Variable_140, Variable_141, Variable_142); + + // Lerp Colors.Clamp + v_flt Variable_86; // Lerp Colors.Clamp output 0 + Variable_86 = FVoxelNodeFunctions::Clamp(Variable_74, v_flt(0.0f), v_flt(1.0f)); + + // / + v_flt Variable_71; // / output 0 + Variable_71 = Variable_58 / BufferConstant.Variable_72; + + // Lerp Colors.Break Color + v_flt Variable_160; // Lerp Colors.Break Color output 0 + v_flt Variable_161; // Lerp Colors.Break Color output 1 + v_flt Variable_162; // Lerp Colors.Break Color output 2 + v_flt Variable_163; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_151, Variable_160, Variable_161, Variable_162, Variable_163); + + // Float Curve: RiverDepthCurve + v_flt Variable_46; // Float Curve: RiverDepthCurve output 0 + Variable_46 = FVoxelNodeFunctions::GetCurveValue(Params.RiverDepthCurve, Variable_71); + + // Min (float) + v_flt Variable_60; // Min (float) output 0 + Variable_60 = FVoxelNodeFunctions::Min(Variable_46, v_flt(0.0f)); + + // + + v_flt Variable_65; // + output 0 + Variable_65 = Variable_46 + v_flt(0.3f); + + // * + v_flt Variable_67; // * output 0 + Variable_67 = Variable_46 * v_flt(10.0f); + + // Lerp Colors.Clamp + v_flt Variable_82; // Lerp Colors.Clamp output 0 + Variable_82 = FVoxelNodeFunctions::Clamp(Variable_67, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Clamp + v_flt Variable_81; // Lerp Colors.Clamp output 0 + Variable_81 = FVoxelNodeFunctions::Clamp(Variable_65, v_flt(0.0f), v_flt(1.0f)); + + // * + v_flt Variable_61; // * output 0 + Variable_61 = Variable_60 * v_flt(-1.0f); + + // Lerp Colors.Lerp + v_flt Variable_113; // Lerp Colors.Lerp output 0 + Variable_113 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_117, BufferConstant.Variable_121, Variable_82); + + // Lerp Colors.Lerp + v_flt Variable_115; // Lerp Colors.Lerp output 0 + Variable_115 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_119, BufferConstant.Variable_123, Variable_82); + + // * + v_flt Variable_62; // * output 0 + Variable_62 = Variable_41 * Variable_61; + + // Lerp Colors.Lerp + v_flt Variable_116; // Lerp Colors.Lerp output 0 + Variable_116 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_120, BufferConstant.Variable_124, Variable_82); + + // Lerp Colors.Lerp + v_flt Variable_114; // Lerp Colors.Lerp output 0 + Variable_114 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_118, BufferConstant.Variable_122, Variable_82); + + // 1 - X + v_flt Variable_63; // 1 - X output 0 + Variable_63 = 1 - Variable_61; + + // - + v_flt Variable_64; // - output 0 + Variable_64 = Variable_62 - Variable_63; + + // Lerp Colors.Make Color + FColor Variable_125; // Lerp Colors.Make Color output 0 + Variable_125 = FVoxelNodeFunctions::MakeColorFloat(Variable_113, Variable_114, Variable_115, Variable_116); + + // Max (float) + v_flt Variable_44; // Max (float) output 0 + Variable_44 = FVoxelNodeFunctions::Max(Variable_64, v_flt(0.0f)); + + // Lerp Colors.Clamp + v_flt Variable_78; // Lerp Colors.Clamp output 0 + Variable_78 = FVoxelNodeFunctions::Clamp(Variable_64, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Break Color + v_flt Variable_108; // Lerp Colors.Break Color output 0 + v_flt Variable_109; // Lerp Colors.Break Color output 1 + v_flt Variable_110; // Lerp Colors.Break Color output 2 + v_flt Variable_111; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_125, Variable_108, Variable_109, Variable_110, Variable_111); + + // - + v_flt Variable_45; // - output 0 + Variable_45 = Variable_42 - Variable_44; + + // Lerp Colors.Lerp + v_flt Variable_88; // Lerp Colors.Lerp output 0 + Variable_88 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_92, BufferConstant.Variable_96, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_89; // Lerp Colors.Lerp output 0 + Variable_89 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_93, BufferConstant.Variable_97, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_90; // Lerp Colors.Lerp output 0 + Variable_90 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_94, BufferConstant.Variable_98, Variable_78); + + // Lerp Colors.Lerp + v_flt Variable_87; // Lerp Colors.Lerp output 0 + Variable_87 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_91, BufferConstant.Variable_95, Variable_78); + + // Lerp Colors.Make Color + FColor Variable_99; // Lerp Colors.Make Color output 0 + Variable_99 = FVoxelNodeFunctions::MakeColorFloat(Variable_87, Variable_88, Variable_89, Variable_90); + + // Lerp Colors.Clamp + v_flt Variable_85; // Lerp Colors.Clamp output 0 + Variable_85 = FVoxelNodeFunctions::Clamp(Variable_45, v_flt(0.0f), v_flt(1.0f)); + + // Lerp Colors.Break Color + v_flt Variable_104; // Lerp Colors.Break Color output 0 + v_flt Variable_105; // Lerp Colors.Break Color output 1 + v_flt Variable_106; // Lerp Colors.Break Color output 2 + v_flt Variable_107; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_99, Variable_104, Variable_105, Variable_106, Variable_107); + + // Lerp Colors.Lerp + v_flt Variable_101; // Lerp Colors.Lerp output 0 + Variable_101 = FVoxelNodeFunctions::Lerp(Variable_105, Variable_109, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_103; // Lerp Colors.Lerp output 0 + Variable_103 = FVoxelNodeFunctions::Lerp(Variable_107, Variable_111, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_102; // Lerp Colors.Lerp output 0 + Variable_102 = FVoxelNodeFunctions::Lerp(Variable_106, Variable_110, Variable_81); + + // Lerp Colors.Lerp + v_flt Variable_100; // Lerp Colors.Lerp output 0 + Variable_100 = FVoxelNodeFunctions::Lerp(Variable_104, Variable_108, Variable_81); + + // Lerp Colors.Make Color + FColor Variable_112; // Lerp Colors.Make Color output 0 + Variable_112 = FVoxelNodeFunctions::MakeColorFloat(Variable_100, Variable_101, Variable_102, Variable_103); + + // Lerp Colors.Break Color + v_flt Variable_156; // Lerp Colors.Break Color output 0 + v_flt Variable_157; // Lerp Colors.Break Color output 1 + v_flt Variable_158; // Lerp Colors.Break Color output 2 + v_flt Variable_159; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_112, Variable_156, Variable_157, Variable_158, Variable_159); + + // Lerp Colors.Lerp + v_flt Variable_154; // Lerp Colors.Lerp output 0 + Variable_154 = FVoxelNodeFunctions::Lerp(Variable_158, Variable_162, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_153; // Lerp Colors.Lerp output 0 + Variable_153 = FVoxelNodeFunctions::Lerp(Variable_157, Variable_161, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_155; // Lerp Colors.Lerp output 0 + Variable_155 = FVoxelNodeFunctions::Lerp(Variable_159, Variable_163, Variable_85); + + // Lerp Colors.Lerp + v_flt Variable_152; // Lerp Colors.Lerp output 0 + Variable_152 = FVoxelNodeFunctions::Lerp(Variable_156, Variable_160, Variable_85); + + // Lerp Colors.Make Color + FColor Variable_164; // Lerp Colors.Make Color output 0 + Variable_164 = FVoxelNodeFunctions::MakeColorFloat(Variable_152, Variable_153, Variable_154, Variable_155); + + // Lerp Colors.Break Color + v_flt Variable_173; // Lerp Colors.Break Color output 0 + v_flt Variable_174; // Lerp Colors.Break Color output 1 + v_flt Variable_175; // Lerp Colors.Break Color output 2 + v_flt Variable_176; // Lerp Colors.Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_164, Variable_173, Variable_174, Variable_175, Variable_176); + + // Lerp Colors.Lerp + v_flt Variable_165; // Lerp Colors.Lerp output 0 + Variable_165 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_169, Variable_173, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_166; // Lerp Colors.Lerp output 0 + Variable_166 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_170, Variable_174, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_167; // Lerp Colors.Lerp output 0 + Variable_167 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_171, Variable_175, Variable_86); + + // Lerp Colors.Lerp + v_flt Variable_168; // Lerp Colors.Lerp output 0 + Variable_168 = FVoxelNodeFunctions::Lerp(BufferConstant.Variable_172, Variable_176, Variable_86); + + // Lerp Colors.Make Color + FColor Variable_177; // Lerp Colors.Make Color output 0 + Variable_177 = FVoxelNodeFunctions::MakeColorFloat(Variable_165, Variable_166, Variable_167, Variable_168); + + Outputs.MaterialBuilder.SetColor(Variable_177); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + TVoxelRange Variable_39; // MountainsNoiseHeight = 500.0 output 0 + TVoxelRange Variable_41; // 2D Perlin Noise output 0 + TVoxelRange Variable_22; // RingEdgesHardness = 10.0 output 0 + TVoxelRange Variable_45; // PlainsNoiseHeight = 250.0 output 0 + TVoxelRange Variable_47; // 2D Simplex Noise output 0 + TVoxelRange Variable_33; // BaseHeight = 1000.0 output 0 + TVoxelRange Variable_50; // RiverDepth = 100.0 output 0 + TVoxelRange Variable_37; // 2D Simplex Noise Fractal output 0 + TVoxelRange Variable_73; // RiverWidth = 1.0 output 0 + TVoxelRange Variable_69; // Scale = 10.0 output 0 + TVoxelRange Variable_59; // * output 0 + TVoxelRange Variable_30; // / output 0 + TVoxelRange Variable_70; // * output 0 + TVoxelRange Variable_35; // * output 0 + TVoxelRange Variable_6; // + output 0 + TVoxelRange Variable_29; // - output 0 + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_0; // X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_1; // Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + // Init of 2D Perlin Noise + _2D_Perlin_Noise_2_Noise.SetSeed(FVoxelGraphSeed(1338)); + _2D_Perlin_Noise_2_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise + _2D_Simplex_Noise_4_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_4_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + // Init of 2D Simplex Noise Fractal + _2D_Simplex_Noise_Fractal_3_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_Fractal_3_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Simplex_Noise_Fractal_3_Noise.SetFractalOctavesAndGain(5, 0.5); + _2D_Simplex_Noise_Fractal_3_Noise.SetFractalLacunarity(2.0); + _2D_Simplex_Noise_Fractal_3_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Simplex_Noise_Fractal_3_LODToOctaves[0] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[1] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[2] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[3] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[4] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[5] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[6] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[7] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[8] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[9] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[10] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[11] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[12] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[13] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[14] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[15] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[16] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[17] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[18] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[19] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[20] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[21] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[22] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[23] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[24] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[25] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[26] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[27] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[28] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[29] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[30] = 5; + _2D_Simplex_Noise_Fractal_3_LODToOctaves[31] = 5; + + // Init of 2D Simplex Noise Fractal + _2D_Simplex_Noise_Fractal_4_Noise.SetSeed(FVoxelGraphSeed(1337)); + _2D_Simplex_Noise_Fractal_4_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + _2D_Simplex_Noise_Fractal_4_Noise.SetFractalOctavesAndGain(8, 0.5); + _2D_Simplex_Noise_Fractal_4_Noise.SetFractalLacunarity(2.0); + _2D_Simplex_Noise_Fractal_4_Noise.SetFractalType(EVoxelNoiseFractalType::FBM); + _2D_Simplex_Noise_Fractal_4_LODToOctaves[0] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[1] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[2] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[3] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[4] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[5] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[6] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[7] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[8] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[9] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[10] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[11] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[12] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[13] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[14] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[15] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[16] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[17] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[18] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[19] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[20] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[21] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[22] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[23] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[24] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[25] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[26] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[27] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[28] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[29] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[30] = 8; + _2D_Simplex_Noise_Fractal_4_LODToOctaves[31] = 8; + + // Init of 2D Simplex Noise + _2D_Simplex_Noise_5_Noise.SetSeed(FVoxelGraphSeed(1000)); + _2D_Simplex_Noise_5_Noise.SetInterpolation(EVoxelNoiseInterpolation::Quintic); + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // MountainsNoiseHeight = 500.0 + BufferConstant.Variable_39 = Params.MountainsNoiseHeight; + + // 2D Perlin Noise + BufferConstant.Variable_41 = { -0.888317f, 0.811183f }; + + // RingEdgesHardness = 10.0 + BufferConstant.Variable_22 = Params.RingEdgesHardness; + + // Width in Degrees = 50.0 + TVoxelRange Variable_24; // Width in Degrees = 50.0 output 0 + Variable_24 = Params.Width_in_Degrees; + + // PlainsNoiseHeight = 250.0 + BufferConstant.Variable_45 = Params.PlainsNoiseHeight; + + // 2D Simplex Noise + BufferConstant.Variable_47 = { -0.997652f, 0.996970f }; + + // BaseHeight = 1000.0 + BufferConstant.Variable_33 = Params.BaseHeight; + + // 2D Simplex Noise Fractal + TVoxelRange Variable_34; // 2D Simplex Noise Fractal output 0 + Variable_34 = { -0.802678f, 0.846347f }; + + // RiverDepth = 100.0 + BufferConstant.Variable_50 = Params.RiverDepth; + + // BaseNoiseHeight = 250.0 + TVoxelRange Variable_36; // BaseNoiseHeight = 250.0 output 0 + Variable_36 = Params.BaseNoiseHeight; + + // 2D Simplex Noise Fractal + BufferConstant.Variable_37 = { -0.780446f, 0.760988f }; + + // Radius = 7000.0 + TVoxelRange Variable_5; // Radius = 7000.0 output 0 + Variable_5 = Params.Radius; + + // 2D Simplex Noise + TVoxelRange Variable_62; // 2D Simplex Noise output 0 + Variable_62 = { -0.997888f, 0.997883f }; + + // Thickness = 500.0 + TVoxelRange Variable_74; // Thickness = 500.0 output 0 + Variable_74 = Params.Thickness; + + // RiverWidth = 1.0 + BufferConstant.Variable_73 = Params.RiverWidth; + + // Scale = 10.0 + BufferConstant.Variable_69 = Params.Scale; + + // * + BufferConstant.Variable_59 = Variable_62 * TVoxelRange(0.1f); + + // / + BufferConstant.Variable_30 = Variable_24 / TVoxelRange(360.0f); + + // * + TVoxelRange Variable_75; // * output 0 + Variable_75 = Variable_74 * BufferConstant.Variable_69; + + // * + BufferConstant.Variable_70 = Variable_5 * BufferConstant.Variable_69; + + // * + BufferConstant.Variable_35 = Variable_34 * Variable_36; + + // + + BufferConstant.Variable_6 = BufferConstant.Variable_70 + Variable_75; + + // - + BufferConstant.Variable_29 = TVoxelRange(0.5f) - BufferConstant.Variable_30; + + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_Perlin_Noise_2_Noise; + FVoxelFastNoise _2D_Simplex_Noise_4_Noise; + FVoxelFastNoise _2D_Simplex_Noise_Fractal_3_Noise; + TStaticArray _2D_Simplex_Noise_Fractal_3_LODToOctaves; + FVoxelFastNoise _2D_Simplex_Noise_Fractal_4_Noise; + TStaticArray _2D_Simplex_Noise_Fractal_4_LODToOctaves; + FVoxelFastNoise _2D_Simplex_Noise_5_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // X + TVoxelRange Variable_0; // X output 0 + Variable_0 = Context.GetLocalX(); + + // Z + TVoxelRange Variable_2; // Z output 0 + Variable_2 = Context.GetLocalZ(); + + // Y + TVoxelRange Variable_1; // Y output 0 + Variable_1 = Context.GetLocalY(); + + // Z + TVoxelRange Variable_12; // Z output 0 + Variable_12 = Context.GetLocalZ(); + + // Z + TVoxelRange Variable_55; // Z output 0 + Variable_55 = Context.GetLocalZ(); + + // Vector Length + TVoxelRange Variable_3; // Vector Length output 0 + Variable_3 = FVoxelNodeFunctions::VectorLength(Variable_0, Variable_1, Variable_2); + + // + + TVoxelRange Variable_23; // + output 0 + Variable_23 = Variable_3 + TVoxelRange(0.001f); + + // / + TVoxelRange Variable_56; // / output 0 + Variable_56 = Variable_55 / Variable_23; + + // - + TVoxelRange Variable_7; // - output 0 + Variable_7 = Variable_23 - BufferConstant.Variable_6; + + // / + TVoxelRange Variable_13; // / output 0 + Variable_13 = Variable_12 / Variable_23; + + // + + TVoxelRange Variable_58; // + output 0 + Variable_58 = Variable_56 + BufferConstant.Variable_59; + + // ACOS + TVoxelRange Variable_14; // ACOS output 0 + Variable_14 = FVoxelNodeFunctions::Acos(Variable_13); + + // ACOS + TVoxelRange Variable_57; // ACOS output 0 + Variable_57 = FVoxelNodeFunctions::Acos(Variable_58); + + // / + TVoxelRange Variable_10; // / output 0 + Variable_10 = Variable_14 / TVoxelRange(3.141593f); + + // / + TVoxelRange Variable_54; // / output 0 + Variable_54 = Variable_57 / TVoxelRange(3.141593f); + + // 1 - X + TVoxelRange Variable_11; // 1 - X output 0 + Variable_11 = 1 - Variable_10; + + // Min (float) + TVoxelRange Variable_9; // Min (float) output 0 + Variable_9 = FVoxelNodeFunctions::Min(Variable_10, Variable_11); + + // 1 - X + TVoxelRange Variable_52; // 1 - X output 0 + Variable_52 = 1 - Variable_54; + + // Min (float) + TVoxelRange Variable_51; // Min (float) output 0 + Variable_51 = FVoxelNodeFunctions::Min(Variable_54, Variable_52); + + // - + TVoxelRange Variable_15; // - output 0 + Variable_15 = Variable_9 - BufferConstant.Variable_29; + + // / + TVoxelRange Variable_28; // / output 0 + Variable_28 = Variable_15 / BufferConstant.Variable_30; + + // / + TVoxelRange Variable_53; // / output 0 + Variable_53 = Variable_51 / TVoxelRange(0.5f); + + // Max (float) + TVoxelRange Variable_18; // Max (float) output 0 + Variable_18 = FVoxelNodeFunctions::Max(Variable_28, TVoxelRange(0.0f)); + + // 1 - X + TVoxelRange Variable_27; // 1 - X output 0 + Variable_27 = 1 - Variable_28; + + // 1 - X + TVoxelRange Variable_60; // 1 - X output 0 + Variable_60 = 1 - Variable_53; + + // * + TVoxelRange Variable_61; // * output 0 + Variable_61 = Variable_60 * TVoxelRange(20.0f); + + // Float Curve: PlainsNoiseStrengthCurve + TVoxelRange Variable_46; // Float Curve: PlainsNoiseStrengthCurve output 0 + Variable_46 = FVoxelNodeFunctions::GetCurveValue(Params.PlainsNoiseStrengthCurve, Variable_27); + + // Float Curve: MoutainsMaskCurve + TVoxelRange Variable_40; // Float Curve: MoutainsMaskCurve output 0 + Variable_40 = FVoxelNodeFunctions::GetCurveValue(Params.MoutainsMaskCurve, Variable_27); + + // Float Curve: RingMainShapeCurve + TVoxelRange Variable_31; // Float Curve: RingMainShapeCurve output 0 + Variable_31 = FVoxelNodeFunctions::GetCurveValue(Params.RingMainShapeCurve, Variable_27); + + // * + TVoxelRange Variable_20; // * output 0 + Variable_20 = Variable_18 * BufferConstant.Variable_22; + + // * + TVoxelRange Variable_32; // * output 0 + Variable_32 = Variable_31 * BufferConstant.Variable_33; + + // * + TVoxelRange Variable_38; // * output 0 + Variable_38 = BufferConstant.Variable_37 * BufferConstant.Variable_39 * Variable_40; + + // * + TVoxelRange Variable_42; // * output 0 + Variable_42 = BufferConstant.Variable_39 * Variable_40 * BufferConstant.Variable_41 * TVoxelRange(0.1f); + + // / + TVoxelRange Variable_72; // / output 0 + Variable_72 = Variable_61 / BufferConstant.Variable_73; + + // Min (float) + TVoxelRange Variable_21; // Min (float) output 0 + Variable_21 = FVoxelNodeFunctions::Min(Variable_20, TVoxelRange(1.0f)); + + // 1 - X + TVoxelRange Variable_43; // 1 - X output 0 + Variable_43 = 1 - Variable_40; + + // Float Curve: RiverDepthCurve + TVoxelRange Variable_48; // Float Curve: RiverDepthCurve output 0 + Variable_48 = FVoxelNodeFunctions::GetCurveValue(Params.RiverDepthCurve, Variable_72); + + // Max (float) + TVoxelRange Variable_63; // Max (float) output 0 + Variable_63 = FVoxelNodeFunctions::Max(Variable_48, TVoxelRange(0.0f)); + + // Min (float) + TVoxelRange Variable_64; // Min (float) output 0 + Variable_64 = FVoxelNodeFunctions::Min(Variable_48, TVoxelRange(0.0f)); + + // * + TVoxelRange Variable_65; // * output 0 + Variable_65 = Variable_64 * TVoxelRange(-1.0f); + + // * + TVoxelRange Variable_49; // * output 0 + Variable_49 = Variable_63 * TVoxelRange(-1.0f) * BufferConstant.Variable_50; + + // * + TVoxelRange Variable_66; // * output 0 + Variable_66 = BufferConstant.Variable_47 * Variable_65; + + // 1 - X + TVoxelRange Variable_67; // 1 - X output 0 + Variable_67 = 1 - Variable_65; + + // - + TVoxelRange Variable_68; // - output 0 + Variable_68 = Variable_66 - Variable_67; + + // * + TVoxelRange Variable_44; // * output 0 + Variable_44 = Variable_68 * BufferConstant.Variable_45 * Variable_43 * Variable_46; + + // + + TVoxelRange Variable_26; // + output 0 + Variable_26 = Variable_32 + BufferConstant.Variable_35 + Variable_38 + Variable_42 + Variable_44 + Variable_49; + + // * + TVoxelRange Variable_71; // * output 0 + Variable_71 = Variable_26 * BufferConstant.Variable_69; + + // - + TVoxelRange Variable_25; // - output 0 + Variable_25 = BufferConstant.Variable_70 - Variable_71; + + // - + TVoxelRange Variable_4; // - output 0 + Variable_4 = Variable_25 - Variable_23; + + // Max (float) + TVoxelRange Variable_8; // Max (float) output 0 + Variable_8 = FVoxelNodeFunctions::Max(Variable_4, Variable_7); + + // - + TVoxelRange Variable_19; // - output 0 + Variable_19 = Variable_8 - TVoxelRange(1.0f); + + // * + TVoxelRange Variable_16; // * output 0 + Variable_16 = Variable_19 * Variable_21; + + // + + TVoxelRange Variable_17; // + output 0 + Variable_17 = Variable_16 + TVoxelRange(1.0f); + + Outputs.Value = Variable_17; + } + + }; + + FVoxelExample_RingWorldInstance(UVoxelExample_RingWorld& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Radius, + Object.RingEdgesHardness, + Object.Scale, + Object.Thickness, + Object.Width_in_Degrees, + Object.RiverDepth, + Object.RiverWidth, + Object.BeachColor, + Object.MountainsColorHigh, + Object.MountainsColorLowHigh, + Object.MountainsColorLowLow, + FVoxelRichCurve(Object.MoutainsMaskCurve.LoadSynchronous()), + Object.PlainsColorHigh, + Object.PlainsColorLow, + Object.PlainsNoiseFrequency, + Object.PlainsNoiseHeight, + FVoxelRichCurve(Object.PlainsNoiseStrengthCurve.LoadSynchronous()), + FVoxelRichCurve(Object.RingMainShapeCurve.LoadSynchronous()), + Object.RingOuterColor, + Object.RiverColor, + FVoxelRichCurve(Object.RiverDepthCurve.LoadSynchronous()), + Object.MountainsNoiseFrequency, + Object.MountainsNoiseHeight, + Object.BaseNoiseFrquency, + Object.BaseNoiseHeight, + Object.BaseHeight + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_RingWorldInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_RingWorldInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_RingWorldInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_RingWorldInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_RingWorldInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_RingWorld::UVoxelExample_RingWorld() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_RingWorld::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_RingWorld. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_RingWorld. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_RingWorld. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_RingWorld. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.h new file mode 100644 index 00000000..6229c501 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.h @@ -0,0 +1,96 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_RingWorld.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_RingWorld : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Radius")) + float Radius = 7000.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="RingEdgesHardness")) + float RingEdgesHardness = 10.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Scale")) + float Scale = 10.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Thickness")) + float Thickness = 500.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Width in Degrees")) + float Width_in_Degrees = 50.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="RiverDepth")) + float RiverDepth = 100.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="RiverWidth")) + float RiverWidth = 1.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="BeachColor")) + FColor BeachColor = FColor(253, 213, 72, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="MountainsColorHigh")) + FColor MountainsColorHigh = FColor(255, 255, 255, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="MountainsColorLowHigh")) + FColor MountainsColorLowHigh = FColor(33, 34, 35, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="MountainsColorLowLow")) + FColor MountainsColorLowLow = FColor(9, 5, 4, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Curves", meta=(DisplayName="MoutainsMaskCurve")) + TSoftObjectPtr MoutainsMaskCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.MoutainsMaskCurve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="PlainsColorHigh")) + FColor PlainsColorHigh = FColor(26, 47, 10, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="PlainsColorLow")) + FColor PlainsColorLow = FColor(10, 18, 4, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="PlainsNoiseFrequency")) + float PlainsNoiseFrequency = 0.2; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="PlainsNoiseHeight")) + float PlainsNoiseHeight = 250.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Curves", meta=(DisplayName="PlainsNoiseStrengthCurve")) + TSoftObjectPtr PlainsNoiseStrengthCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.PlainsNoiseStrengthCurve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Curves", meta=(DisplayName="RingMainShapeCurve")) + TSoftObjectPtr RingMainShapeCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.RingMainShapeCurve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="RingOuterColor")) + FColor RingOuterColor = FColor(1, 0, 0, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Colors", meta=(DisplayName="RiverColor")) + FColor RiverColor = FColor(0, 0, 255, 255); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Curves", meta=(DisplayName="RiverDepthCurve")) + TSoftObjectPtr RiverDepthCurve = TSoftObjectPtr(FSoftObjectPath("/Voxel/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.RiverDepthCurve")); + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="MountainsNoiseFrequency")) + float MountainsNoiseFrequency = 0.2; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="MountainsNoiseHeight")) + float MountainsNoiseHeight = 500.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="BaseNoiseFrquency")) + float BaseNoiseFrquency = 0.005; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="BaseNoiseHeight")) + float BaseNoiseHeight = 250.0; + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="BaseHeight")) + float BaseHeight = 1000.0; + + UVoxelExample_RingWorld(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.cpp new file mode 100644 index 00000000..1bd4cef1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.cpp @@ -0,0 +1,922 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelExample_Tool_NoisyColors.h" + +PRAGMA_GENERATED_VOXEL_GRAPH_START + +using FVoxelGraphSeed = int32; + +#if VOXEL_GRAPH_GENERATED_VERSION == 1 +class FVoxelExample_Tool_NoisyColorsInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + struct FParams + { + const FColor Color; + }; + + class FLocalComputeStruct_LocalValue + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_1; // Previous Generator Value Material.Global X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + v_flt Variable_2; // Previous Generator Value Material.Global Y output 0 + }; + + FLocalComputeStruct_LocalValue(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Previous Generator Value Material.Global X + BufferX.Variable_1 = Context.GetWorldX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Previous Generator Value Material.Global Y + BufferXY.Variable_2 = Context.GetWorldY(); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Previous Generator Value Material.Global X + BufferX.Variable_1 = Context.GetWorldX(); + + // Previous Generator Value Material.Global Y + BufferXY.Variable_2 = Context.GetWorldY(); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + // Previous Generator Value Material.Global Z + v_flt Variable_3; // Previous Generator Value Material.Global Z output 0 + Variable_3 = Context.GetWorldZ(); + + // Previous Generator Value Material.Get Previous Generator Value + v_flt Variable_0; // Previous Generator Value Material.Get Previous Generator Value output 0 + Variable_0 = FVoxelNodeFunctions::GetPreviousGeneratorValue(BufferX.Variable_1, BufferXY.Variable_2, Variable_3, Context, nullptr); + + Outputs.Value = Variable_0; + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Previous Generator Value Material.Global X + v_flt Variable_1; // Previous Generator Value Material.Global X output 0 + Variable_1 = Context.GetWorldX(); + + // Previous Generator Value Material.Global Y + v_flt Variable_2; // Previous Generator Value Material.Global Y output 0 + Variable_2 = Context.GetWorldY(); + + // Previous Generator Value Material.Global Z + v_flt Variable_3; // Previous Generator Value Material.Global Z output 0 + Variable_3 = Context.GetWorldZ(); + + // Previous Generator Value Material.Get Previous Generator Value + v_flt Variable_0; // Previous Generator Value Material.Get Previous Generator Value output 0 + Variable_0 = FVoxelNodeFunctions::GetPreviousGeneratorValue(Variable_1, Variable_2, Variable_3, Context, nullptr); + + Outputs.Value = Variable_0; + } + + }; + class FLocalComputeStruct_LocalMaterial + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const; + template + void Set(T Value); + + FVoxelMaterialBuilder MaterialBuilder; + }; + struct FBufferConstant + { + FBufferConstant() {} + + v_flt Variable_2; // RGB to HSV output 0 + v_flt Variable_3; // RGB to HSV output 1 + v_flt Variable_4; // RGB to HSV output 2 + }; + + struct FBufferX + { + FBufferX() {} + + v_flt Variable_14; // Global X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + FColor Variable_11; // Make Color output 0 + }; + + FLocalComputeStruct_LocalMaterial(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + // Color + FColor Variable_1; // Color output 0 + Variable_1 = Params.Color; + + // Break Color + v_flt Variable_5; // Break Color output 0 + v_flt Variable_6; // Break Color output 1 + v_flt Variable_7; // Break Color output 2 + v_flt Break_Color_0_Temp_3; // Break Color output 3 + FVoxelNodeFunctions::BreakColorFloat(Variable_1, Variable_5, Variable_6, Variable_7, Break_Color_0_Temp_3); + + // RGB to HSV + FVoxelNodeFunctions::RGBToHSV(Variable_5, Variable_6, Variable_7, BufferConstant.Variable_2, BufferConstant.Variable_3, BufferConstant.Variable_4); + + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + FVoxelFastNoise _2D_White_Noise_0_Noise; + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + // Init of 2D White Noise + _2D_White_Noise_0_Noise.SetSeed(FVoxelGraphSeed(1337)); + + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + // Global X + BufferX.Variable_14 = Context.GetWorldX(); + + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Global Y + v_flt Variable_15; // Global Y output 0 + Variable_15 = Context.GetWorldY(); + + // 2D White Noise + v_flt Variable_12; // 2D White Noise output 0 + Variable_12 = _2D_White_Noise_0_Noise.GetWhiteNoise_2D(BufferX.Variable_14, Variable_15); + + // Map.- + v_flt Variable_16; // Map.- output 0 + Variable_16 = Variable_12 - v_flt(-1.0f); + + // Map./ + v_flt Variable_17; // Map./ output 0 + Variable_17 = Variable_16 / v_flt(2.0f); + + // Map.* + v_flt Variable_18; // Map.* output 0 + Variable_18 = Variable_17 * v_flt(0.65f); + + // Map.+ + v_flt Variable_0; // Map.+ output 0 + Variable_0 = Variable_18 + v_flt(0.35f); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = BufferConstant.Variable_4 * Variable_0; + + // HSV to RGB + v_flt Variable_8; // HSV to RGB output 0 + v_flt Variable_9; // HSV to RGB output 1 + v_flt Variable_10; // HSV to RGB output 2 + FVoxelNodeFunctions::HSVToRGB(BufferConstant.Variable_2, BufferConstant.Variable_3, Variable_13, Variable_8, Variable_9, Variable_10); + + // Make Color + BufferXY.Variable_11 = FVoxelNodeFunctions::MakeColorFloat(Variable_8, Variable_9, Variable_10, v_flt(1.0f)); + + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + // Global Y + v_flt Variable_15; // Global Y output 0 + Variable_15 = Context.GetWorldY(); + + // Global X + BufferX.Variable_14 = Context.GetWorldX(); + + // 2D White Noise + v_flt Variable_12; // 2D White Noise output 0 + Variable_12 = _2D_White_Noise_0_Noise.GetWhiteNoise_2D(BufferX.Variable_14, Variable_15); + + // Map.- + v_flt Variable_16; // Map.- output 0 + Variable_16 = Variable_12 - v_flt(-1.0f); + + // Map./ + v_flt Variable_17; // Map./ output 0 + Variable_17 = Variable_16 / v_flt(2.0f); + + // Map.* + v_flt Variable_18; // Map.* output 0 + Variable_18 = Variable_17 * v_flt(0.65f); + + // Map.+ + v_flt Variable_0; // Map.+ output 0 + Variable_0 = Variable_18 + v_flt(0.35f); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = BufferConstant.Variable_4 * Variable_0; + + // HSV to RGB + v_flt Variable_8; // HSV to RGB output 0 + v_flt Variable_9; // HSV to RGB output 1 + v_flt Variable_10; // HSV to RGB output 2 + FVoxelNodeFunctions::HSVToRGB(BufferConstant.Variable_2, BufferConstant.Variable_3, Variable_13, Variable_8, Variable_9, Variable_10); + + // Make Color + BufferXY.Variable_11 = FVoxelNodeFunctions::MakeColorFloat(Variable_8, Variable_9, Variable_10, v_flt(1.0f)); + + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Outputs.MaterialBuilder.SetColor(BufferXY.Variable_11); + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + // Global Y + v_flt Variable_15; // Global Y output 0 + Variable_15 = Context.GetWorldY(); + + // Global X + v_flt Variable_14; // Global X output 0 + Variable_14 = Context.GetWorldX(); + + // 2D White Noise + v_flt Variable_12; // 2D White Noise output 0 + Variable_12 = _2D_White_Noise_0_Noise.GetWhiteNoise_2D(Variable_14, Variable_15); + + // Map.- + v_flt Variable_16; // Map.- output 0 + Variable_16 = Variable_12 - v_flt(-1.0f); + + // Map./ + v_flt Variable_17; // Map./ output 0 + Variable_17 = Variable_16 / v_flt(2.0f); + + // Map.* + v_flt Variable_18; // Map.* output 0 + Variable_18 = Variable_17 * v_flt(0.65f); + + // Map.+ + v_flt Variable_0; // Map.+ output 0 + Variable_0 = Variable_18 + v_flt(0.35f); + + // * + v_flt Variable_13; // * output 0 + Variable_13 = BufferConstant.Variable_4 * Variable_0; + + // HSV to RGB + v_flt Variable_8; // HSV to RGB output 0 + v_flt Variable_9; // HSV to RGB output 1 + v_flt Variable_10; // HSV to RGB output 2 + FVoxelNodeFunctions::HSVToRGB(BufferConstant.Variable_2, BufferConstant.Variable_3, Variable_13, Variable_8, Variable_9, Variable_10); + + // Make Color + FColor Variable_11; // Make Color output 0 + Variable_11 = FVoxelNodeFunctions::MakeColorFloat(Variable_8, Variable_9, Variable_10, v_flt(1.0f)); + + Outputs.MaterialBuilder.SetColor(Variable_11); + } + + }; + class FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + T Get() const; + template + void Set(T Value); + + v_flt UpVectorX; + v_flt UpVectorY; + v_flt UpVectorZ; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + }; + + struct FBufferXY + { + FBufferXY() {} + + }; + + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Function0_X_Compute(Context, BufferX); + } + void ComputeXYWithCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYWithoutCache(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + Function0_XYWithoutCache_Compute(Context, BufferX, BufferXY); + } + void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + Function0_XYZWithCache_Compute(Context, BufferX, BufferXY, Outputs); + } + void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_X_Compute(const FVoxelContext& Context, FBufferX& BufferX) const + { + } + + void Function0_XYWithCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYWithoutCache_Compute(const FVoxelContext& Context, FBufferX& BufferX, FBufferXY& BufferXY) const + { + } + + void Function0_XYZWithCache_Compute(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutputs& Outputs) const + { + } + + void Function0_XYZWithoutCache_Compute(const FVoxelContext& Context, FOutputs& Outputs) const + { + } + + }; + class FLocalComputeStruct_LocalValueRangeAnalysis + { + public: + struct FOutputs + { + FOutputs() {} + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const; + template + void Set(TVoxelRange Value); + + TVoxelRange Value; + }; + struct FBufferConstant + { + FBufferConstant() {} + + }; + + struct FBufferX + { + FBufferX() {} + + TVoxelRange Variable_1; // Previous Generator Value Material.Global X output 0 + }; + + struct FBufferXY + { + FBufferXY() {} + + TVoxelRange Variable_2; // Previous Generator Value Material.Global Y output 0 + }; + + FLocalComputeStruct_LocalValueRangeAnalysis(const FParams& InParams) + : Params(InParams) + { + } + + void Init(const FVoxelGeneratorInit& InitStruct) + { + //////////////////////////////////////////////////// + //////////////////// Init nodes //////////////////// + //////////////////////////////////////////////////// + { + //////////////////////////////////////////////////// + /////////////// Constant nodes init //////////////// + //////////////////////////////////////////////////// + { + ///////////////////////////////////////////////////////////////////////////////// + //////// First compute all seeds in case they are used by constant nodes //////// + ///////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////// + ///////////// Then init constant nodes ///////////// + //////////////////////////////////////////////////// + + } + + //////////////////////////////////////////////////// + //////////////////// Other inits /////////////////// + //////////////////////////////////////////////////// + Function0_XYZWithoutCache_Init(InitStruct); + } + + //////////////////////////////////////////////////// + //////////////// Compute constants ///////////////// + //////////////////////////////////////////////////// + { + } + } + void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + Function0_XYZWithoutCache_Compute(Context, Outputs); + } + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutputs GetOutputs() const { return {}; } + + private: + FBufferConstant BufferConstant; + + const FParams& Params; + + + /////////////////////////////////////////////////////////////////////// + //////////////////////////// Init functions /////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Init(const FVoxelGeneratorInit& InitStruct) + { + } + + /////////////////////////////////////////////////////////////////////// + ////////////////////////// Compute functions ////////////////////////// + /////////////////////////////////////////////////////////////////////// + + void Function0_XYZWithoutCache_Compute(const FVoxelContextRange& Context, FOutputs& Outputs) const + { + // Previous Generator Value Material.Global X + TVoxelRange Variable_1; // Previous Generator Value Material.Global X output 0 + Variable_1 = Context.GetWorldX(); + + // Previous Generator Value Material.Global Y + TVoxelRange Variable_2; // Previous Generator Value Material.Global Y output 0 + Variable_2 = Context.GetWorldY(); + + // Previous Generator Value Material.Global Z + TVoxelRange Variable_3; // Previous Generator Value Material.Global Z output 0 + Variable_3 = Context.GetWorldZ(); + + // Previous Generator Value Material.Get Previous Generator Value + TVoxelRange Variable_0; // Previous Generator Value Material.Get Previous Generator Value output 0 + Variable_0 = FVoxelNodeFunctions::GetPreviousGeneratorValue(Variable_1, Variable_2, Variable_3, Context, nullptr); + + Outputs.Value = Variable_0; + } + + }; + + FVoxelExample_Tool_NoisyColorsInstance(UVoxelExample_Tool_NoisyColors& Object) + : TVoxelGraphGeneratorInstanceHelper( + { + { "Value", 1 }, + }, + { + }, + { + }, + { + { + { "Value", NoTransformAccessor::Get<1, TOutputFunctionPtr>() }, + }, + { + }, + { + }, + { + { "Value", NoTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr>() }, + } + }, + { + { + { "Value", WithTransformAccessor::Get<1, TOutputFunctionPtr_Transform>() }, + }, + { + }, + { + }, + { + { "Value", WithTransformRangeAccessor::Get<1, TRangeOutputFunctionPtr_Transform>() }, + } + }, + Object) + , Params(FParams + { + Object.Color.ToFColor(false) + }) + , LocalValue(Params) + , LocalMaterial(Params) + , LocalUpVectorXUpVectorYUpVectorZ(Params) + , LocalValueRangeAnalysis(Params) + { + } + + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) override final + { + LocalValue.Init(InitStruct); + LocalMaterial.Init(InitStruct); + LocalUpVectorXUpVectorYUpVectorZ.Init(InitStruct); + LocalValueRangeAnalysis.Init(InitStruct); + } + + template + auto& GetTarget() const; + + template + auto& GetRangeTarget() const; + +private: + FParams Params; + FLocalComputeStruct_LocalValue LocalValue; + FLocalComputeStruct_LocalMaterial LocalMaterial; + FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ LocalUpVectorXUpVectorYUpVectorZ; + FLocalComputeStruct_LocalValueRangeAnalysis LocalValueRangeAnalysis; + +}; + +template<> +inline v_flt FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalValue::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalValue::FOutputs::Set(v_flt InValue) +{ + Value = InValue; +} +template<> +inline FVoxelMaterial FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Get() const +{ + return MaterialBuilder.Build(); +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalMaterial::FOutputs::Set(FVoxelMaterial Material) +{ +} +template<> +inline v_flt FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorX; +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorX = InValue; +} +template<> +inline v_flt FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorY; +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorY = InValue; +} +template<> +inline v_flt FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Get() const +{ + return UpVectorZ; +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalUpVectorXUpVectorYUpVectorZ::FOutputs::Set(v_flt InValue) +{ + UpVectorZ = InValue; +} +template<> +inline TVoxelRange FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Get() const +{ + return Value; +} +template<> +inline void FVoxelExample_Tool_NoisyColorsInstance::FLocalComputeStruct_LocalValueRangeAnalysis::FOutputs::Set(TVoxelRange InValue) +{ + Value = InValue; +} +template<> +inline auto& FVoxelExample_Tool_NoisyColorsInstance::GetTarget<1>() const +{ + return LocalValue; +} +template<> +inline auto& FVoxelExample_Tool_NoisyColorsInstance::GetTarget<2>() const +{ + return LocalMaterial; +} +template<> +inline auto& FVoxelExample_Tool_NoisyColorsInstance::GetRangeTarget<0, 1>() const +{ + return LocalValueRangeAnalysis; +} +template<> +inline auto& FVoxelExample_Tool_NoisyColorsInstance::GetTarget<3, 4, 5>() const +{ + return LocalUpVectorXUpVectorYUpVectorZ; +} +#endif + +//////////////////////////////////////////////////////////// +////////////////////////// UCLASS ////////////////////////// +//////////////////////////////////////////////////////////// + +UVoxelExample_Tool_NoisyColors::UVoxelExample_Tool_NoisyColors() +{ + bEnableRangeAnalysis = true; +} + +TVoxelSharedRef UVoxelExample_Tool_NoisyColors::GetTransformableInstance() +{ +#if VOXEL_GRAPH_GENERATED_VERSION == 1 + return MakeVoxelShared(*this); +#else +#if VOXEL_GRAPH_GENERATED_VERSION > 1 + EMIT_CUSTOM_WARNING("Outdated generated voxel graph: VoxelExample_Tool_NoisyColors. You need to regenerate it."); + FVoxelMessages::Warning("Outdated generated voxel graph: VoxelExample_Tool_NoisyColors. You need to regenerate it."); +#else + EMIT_CUSTOM_WARNING("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Tool_NoisyColors. You need to update the plugin."); + FVoxelMessages::Warning("Generated voxel graph is more recent than the Voxel Plugin version: VoxelExample_Tool_NoisyColors. You need to update the plugin."); +#endif + return MakeVoxelShared(); +#endif +} + +PRAGMA_GENERATED_VOXEL_GRAPH_END diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.h new file mode 100644 index 00000000..47c20145 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" +#include "VoxelExample_Tool_NoisyColors.generated.h" + +UCLASS(Blueprintable) +class UVoxelExample_Tool_NoisyColors : public UVoxelGraphGeneratorHelper +{ + GENERATED_BODY() + +public: + // + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="", meta=(DisplayName="Color")) + FLinearColor Color = FLinearColor(0.056128, 0.109462, 0.052861, 1.000000); + + UVoxelExample_Tool_NoisyColors(); + virtual TVoxelSharedRef GetTransformableInstance() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/NodeFunctions/VoxelNodeFunctions.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/NodeFunctions/VoxelNodeFunctions.cpp new file mode 100644 index 00000000..40bccd31 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/NodeFunctions/VoxelNodeFunctions.cpp @@ -0,0 +1,714 @@ +// Copyright 2020 Phyronnaz + +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelMessages.h" +#include "Curves/CurveFloat.h" +#include "Curves/CurveLinearColor.h" +#include "Async/Async.h" + +/** Util to find float value on bezier defined by 4 control points */ +static TVoxelRange BezierInterp(v_flt P0, v_flt P1, v_flt P2, v_flt P3, const TVoxelRange& Alpha) +{ + const TVoxelRange P01 = FVoxelNodeFunctions::SafeLerp(P0, P1, Alpha); + const TVoxelRange P12 = FVoxelNodeFunctions::SafeLerp(P1, P2, Alpha); + const TVoxelRange P23 = FVoxelNodeFunctions::SafeLerp(P2, P3, Alpha); + const TVoxelRange P012 = FVoxelNodeFunctions::SafeLerp(P01, P12, Alpha); + const TVoxelRange P123 = FVoxelNodeFunctions::SafeLerp(P12, P23, Alpha); + const TVoxelRange P0123 = FVoxelNodeFunctions::SafeLerp(P012, P123, Alpha); + + return P0123; +} + +TVoxelRange EvalForTwoKeys(const FRichCurveKey& Key1, const FRichCurveKey& Key2, const TVoxelRange& InTime) +{ + const v_flt Diff = Key2.Time - Key1.Time; + + if (Diff > 0.f && Key1.InterpMode != RCIM_Constant) + { + const TVoxelRange Alpha = (InTime - Key1.Time) / Diff; + const v_flt P0 = Key1.Value; + const v_flt P3 = Key2.Value; + + if (Key1.InterpMode == RCIM_Linear) + { + return FVoxelNodeFunctions::SafeLerp(P0, P3, Alpha); + } + else + { + const v_flt OneThird = 1.0f / 3.0f; + const v_flt P1 = P0 + (Key1.LeaveTangent * Diff * OneThird); + const v_flt P2 = P3 - (Key2.ArriveTangent * Diff * OneThird); + + return BezierInterp(P0, P1, P2, P3, Alpha); + } + } + else + { + return Key1.Value; + } +} + +inline void AddKeysBounds(const TArray& Keys, const TVoxelRange& Time, TArray& Bounds) +{ + for (int32 Index = 0; Index < Keys.Num() - 1; Index++) + { + auto& KeyA = Keys[Index]; + auto& KeyB = Keys[Index + 1]; + if (TVoxelRange(KeyA.Time, KeyB.Time).Intersects(Time)) + { + auto Range = EvalForTwoKeys(KeyA, KeyB, FVoxelNodeFunctions::Clamp(Time, KeyA.Time, KeyB.Time)); + Bounds.Add(Range.Min); + Bounds.Add(Range.Max); + } + } +} + +inline v_flt GetInferiorInfinityValue(const FRichCurve& Curve, v_flt InTime) +{ + const auto& Keys = Curve.Keys; + if (Curve.PreInfinityExtrap == RCCE_Linear) + { + const v_flt DT = Keys[1].Time - Keys[0].Time; + + if (FMath::IsNearlyZero(DT)) + { + return Keys[0].Value; + } + else + { + const v_flt DV = Keys[1].Value - Keys[0].Value; + const v_flt Slope = DV / DT; + + return Slope * (InTime - Keys[0].Time) + Keys[0].Value; + } + } + else + { + // Otherwise if constant or in a cycle or oscillate, always use the first key value + return Keys[0].Value; + } +} + +inline v_flt GetSuperiorInfinityValue(const FRichCurve& Curve, v_flt InTime) +{ + const auto& Keys = Curve.Keys; + const int32 NumKeys = Keys.Num(); + if (Curve.PostInfinityExtrap == RCCE_Linear) + { + const v_flt DT = Keys[NumKeys - 2].Time - Keys[NumKeys - 1].Time; + + if (FMath::IsNearlyZero(DT)) + { + return Keys[NumKeys - 1].Value; + } + else + { + const v_flt DV = Keys[NumKeys - 2].Value - Keys[NumKeys - 1].Value; + const v_flt Slope = DV / DT; + + return Slope * (InTime - Keys[NumKeys - 1].Time) + Keys[NumKeys - 1].Value; + } + } + else + { + // Otherwise if constant or in a cycle or oscillate, always use the last key value + return Keys[NumKeys - 1].Value; + } +} + +TVoxelRange FVoxelNodeFunctions::GetCurveValue(const FVoxelRichCurve& VoxelCurve, const TVoxelRange& Time) +{ + auto& Curve = VoxelCurve.Curve; + if (Time.IsSingleValue()) + { + return FVoxelRichCurveUtilities::Eval(Curve, Time.GetSingleValue()); + } + else + { + auto& Keys = Curve.GetConstRefOfKeys(); + const int32 NumKeys = Keys.Num(); + + if (NumKeys == 0) + { + // If no keys in curve, return the Default value. + return 0; + } + else if (NumKeys == 1) + { + return Keys[0].Value; + } + else + { + TArray Bounds; + const bool bMinBelow = Time.Min <= Keys[0].Time; + const bool bMaxBelow = Time.Max <= Keys[0].Time; + const bool bMinAbove = Keys[NumKeys - 1].Time <= Time.Min; + const bool bMaxAbove = Keys[NumKeys - 1].Time <= Time.Max; + if (bMaxBelow) + { + ensure(bMinBelow); + Bounds.Add(GetInferiorInfinityValue(Curve, Time.Min)); + Bounds.Add(GetInferiorInfinityValue(Curve, Time.Max)); + } + else if (bMinBelow) + { + Bounds.Add(GetInferiorInfinityValue(Curve, Time.Min)); + AddKeysBounds(Keys, Time, Bounds); + } + else if (bMinAbove) + { + ensure(bMaxAbove); + Bounds.Add(GetSuperiorInfinityValue(Curve, Time.Min)); + Bounds.Add(GetSuperiorInfinityValue(Curve, Time.Max)); + } + else if (bMaxAbove) + { + Bounds.Add(GetSuperiorInfinityValue(Curve, Time.Max)); + AddKeysBounds(Keys, Time, Bounds); + } + else + { + ensure(!bMinBelow && !bMinAbove && !bMaxBelow && !bMaxAbove); + AddKeysBounds(Keys, Time, Bounds); + } + + v_flt Min = Bounds[0]; + v_flt Max = Bounds[0]; + for (int32 Index = 1; Index < Bounds.Num(); Index++) + { + Min = FMath::Min(Bounds[Index], Min); + Max = FMath::Max(Bounds[Index], Max); + } + return + { + FMath::Clamp(Min, VoxelCurve.GetMin(), VoxelCurve.GetMax()), + FMath::Clamp(Max, VoxelCurve.GetMin(), VoxelCurve.GetMax()) + }; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +v_flt FVoxelNodeFunctions::GetPreviousGeneratorValue( + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator) +{ + if (Context.Items.IsEmpty()) + { + if (DefaultGenerator) + { + return DefaultGenerator->GetValue(X, Y, Z, Context.LOD, Context.Items); + } + else + { + return 1; + } + } + else + { + const auto NextStack = Context.Items.GetNextStack(Context.GetWorldX(), Context.GetWorldY(), Context.GetWorldZ()); + return NextStack.Get(X, Y, Z, Context.LOD); + } +} + +TVoxelRange FVoxelNodeFunctions::GetPreviousGeneratorValue( + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + const FVoxelContextRange& Context, + const FVoxelGeneratorInstance* DefaultGenerator) +{ + const FVoxelIntBox Bounds = FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + if (Context.Items.IsEmpty()) + { + if (DefaultGenerator) + { + return DefaultGenerator->GetValueRange(Bounds, Context.LOD, Context.Items); + } + else + { + return 1; + } + } + else + { + const auto NextStack = Context.Items.GetNextStack(Context.WorldBounds); + if (NextStack.IsValid()) + { + return NextStack.GetValueRange(Bounds, Context.LOD); + } + else + { + return {-1, 1}; + } + } +} + +FVoxelMaterial FVoxelNodeFunctions::GetPreviousGeneratorMaterial( + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator) +{ + if (Context.Items.IsEmpty()) + { + if (DefaultGenerator) + { + return DefaultGenerator->GetMaterial(X, Y, Z, Context.LOD, Context.Items); + } + else + { + return FVoxelMaterial::Default(); + } + } + else + { + const auto NextStack = Context.Items.GetNextStack(Context.GetWorldX(), Context.GetWorldY(), Context.GetWorldZ()); + return NextStack.Get(X, Y, Z, Context.LOD); + } +} + +v_flt FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput( + const FName& Name, + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator) +{ + if (Context.Items.IsEmpty()) + { + if (DefaultGenerator) + { + if (const auto Ptr = DefaultGenerator->CustomPtrs.Float.FindRef(Name)) + { + return (DefaultGenerator->*Ptr)(X, Y, Z, Context.LOD, Context.Items); + } + else + { + return 0; + } + } + else + { + return 0; + } + } + else + { + const auto NextStack = Context.Items.GetNextStack(Context.GetWorldX(), Context.GetWorldY(), Context.GetWorldZ()); + return NextStack.GetCustomOutput(0, Name, X, Y, Z, Context.LOD); + } +} + +TVoxelRange FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput( + const FName& Name, + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + const FVoxelContextRange& Context, + const FVoxelGeneratorInstance* DefaultGenerator) +{ + const FVoxelIntBox Bounds = FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + if (Context.Items.IsEmpty()) + { + if (DefaultGenerator) + { + if (const auto Ptr = DefaultGenerator->CustomPtrs.FloatRange.FindRef(Name)) + { + return TVoxelRange((DefaultGenerator->*Ptr)(Bounds, Context.LOD, Context.Items)); + } + else + { + return 0; + } + } + else + { + return 0; + } + } + else + { + const auto NextStack = Context.Items.GetNextStack(Context.WorldBounds); + if (NextStack.IsValid()) + { + return NextStack.GetCustomOutputRange(0, Name, Bounds, Context.LOD); + } + else + { + return TVoxelRange::Infinite(); + } + } +} + +v_flt FVoxelNodeFunctions::GetGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context) +{ + if (const auto Ptr = Generator.CustomPtrs.Float.FindRef(Name)) + { + return (Generator.*Ptr)(X, Y, Z, Context.LOD, FVoxelItemStack(Context.Items.ItemHolder)); + } + else + { + return 0; + } +} + +TVoxelRange FVoxelNodeFunctions::GetGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + const FVoxelContextRange& Context) +{ + const FVoxelIntBox Bounds = FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + if (const auto Ptr = Generator.CustomPtrs.FloatRange.FindRef(Name)) + { + return TVoxelRange((Generator.*Ptr)(Bounds, Context.LOD, FVoxelItemStack(Context.Items.ItemHolder))); + } + else + { + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline void ShowGeneratorMergeError() +{ + const auto Show = []() + { + FVoxelMessages::Error("More than 4 recursive calls to Generator Merge, exiting. Make sure you don't have recursive Generator Merge nodes."); + }; + + if (IsInGameThread()) + { + Show(); + } + else + { + AsyncTask(ENamedThreads::GameThread, [=]() + { + Show(); + }); + } +} + +TArray> FVoxelNodeFunctions::CreateGeneratorArray(const TArray& Generators) +{ + thread_local int32 RecursionDepth = 0; + struct FDepthGuard { FDepthGuard() { RecursionDepth++; } ~FDepthGuard() { RecursionDepth--; } } DepthGuard; + + check(RecursionDepth > 0); + if (RecursionDepth > 4) + { + ShowGeneratorMergeError(); + return { MakeVoxelShared() }; + } + + TArray> Result; + for (auto& Picker : Generators) + { + Result.Add(Picker.GetInstance(true)); + } + if (Result.Num() == 0) + { + Result.Add(MakeVoxelShared()); + } + return Result; +} + +void FVoxelNodeFunctions::ComputeGeneratorsMerge( + const EVoxelMaterialConfig MaterialConfig, + const float Tolerance, + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContext& Context, + v_flt X, v_flt Y, v_flt Z, + int32 Index0, float Alpha0, + int32 Index1, float Alpha1, + int32 Index2, float Alpha2, + int32 Index3, float Alpha3, + bool bComputeValue, bool bComputeMaterial, const TArray& ComputeFloatOutputs, + v_flt& OutValue, + FVoxelMaterial& OutMaterial, + TArray>& OutFloatOutputs, + int32& NumGeneratorsQueried) +{ + thread_local int32 RecursionDepth = 0; + struct FDepthGuard { FDepthGuard() { RecursionDepth++; } ~FDepthGuard() { RecursionDepth--; } } DepthGuard; + + NumGeneratorsQueried = 0; + + check(RecursionDepth > 0); + if (RecursionDepth > 4) + { + static TSet> StaticInstances; + if (!StaticInstances.Contains(InInstances[0])) + { + StaticInstances.Add(InInstances[0]); + ShowGeneratorMergeError(); + } + OutValue = 0; + OutMaterial = FVoxelMaterial::Default(); + OutFloatOutputs.SetNum(FloatOutputsNames.Num()); + return; + } + + check(InInstances.Num() > 0); + + const auto Items = Context.Items; + + Index0 = FMath::Clamp(Index0, 0, InInstances.Num() - 1); + Index1 = FMath::Clamp(Index1, 0, InInstances.Num() - 1); + Index2 = FMath::Clamp(Index2, 0, InInstances.Num() - 1); + Index3 = FMath::Clamp(Index3, 0, InInstances.Num() - 1); + + if (Index0 == Index1) Alpha1 = 0; + if (Index0 == Index2 || Index1 == Index2) Alpha2 = 0; + if (Index0 == Index3 || Index1 == Index3 || Index2 == Index3) Alpha3 = 0; + + const float AlphaSum = + FMath::Max(0.f, Alpha0) + + FMath::Max(0.f, Alpha1) + + FMath::Max(0.f, Alpha2) + + FMath::Max(0.f, Alpha3); + if (AlphaSum > 0) + { + Alpha0 /= AlphaSum; + Alpha1 /= AlphaSum; + Alpha2 /= AlphaSum; + Alpha3 /= AlphaSum; + } + + TArray> Instances; + TArray> Alphas; + + int32 BestIndex = 0; + float BestAlpha = 0; + + const auto AddInput = [&](float Alpha, int32 Index) + { + if (Alpha >= Tolerance) + { + NumGeneratorsQueried++; + Instances.Add(InInstances[Index].Get()); + Alphas.Add(Alpha); + + if (Alpha > BestAlpha) + { + BestIndex = Instances.Num() - 1; + BestAlpha = Alpha; + } + } + }; + AddInput(Alpha0, Index0); + AddInput(Alpha1, Index1); + AddInput(Alpha2, Index2); + AddInput(Alpha3, Index3); + + if (Instances.Num() == 0) + { + ensure(Alpha0 < Tolerance && Alpha1 < Tolerance && Alpha2 < Tolerance && Alpha3 < Tolerance); + Alpha0 = 1; + AddInput(Alpha0, Index0); + } + + const auto GetFloatOutput = [&](auto Lambda) + { + float Result = 0; + for (int32 Index = 0; Index < Instances.Num(); Index++) + { + Result += Lambda(*Instances[Index]) * Alphas[Index]; + } + return Result; + }; + const auto GetMaterialOutput = [&]() + { + switch (MaterialConfig) + { + case EVoxelMaterialConfig::RGB: + { + FLinearColor Result(0, 0, 0, 0); + for (int32 Index = 0; Index < Instances.Num(); Index++) + { + Result += Instances[Index]->GetMaterial(X, Y, Z, Context.LOD, Items).GetLinearColor() * Alphas[Index]; + } + return FVoxelMaterial::CreateFromColor(Result); + } + case EVoxelMaterialConfig::SingleIndex: + { + return Instances[BestIndex]->GetMaterial(X, Y, Z, Context.LOD, Items); + } +// TODO PLACEABLE ITEMS +#if 0 + case EVoxelMaterialConfig::DoubleIndex: + { + TArray> NewIndices; + TArray> NewAlphas; + float BestData = 0; + for (int32 Index = 0; Index < Instances.Num(); Index++) + { + const FVoxelMaterial InstanceMaterial = Instances[Index]->GetMaterial(X, Y, Z, Context.LOD, Items); + const uint8 IndexA = InstanceMaterial.GetDoubleIndex_IndexA(); + const uint8 IndexB = InstanceMaterial.GetDoubleIndex_IndexB(); + const float Blend = InstanceMaterial.GetDoubleIndex_Blend_AsFloat(); + const float Data = InstanceMaterial.GetDoubleIndex_Data_AsFloat(); + + if (Index == BestIndex) + { + BestData = Data; + } + NewIndices.Add(IndexA); + NewIndices.Add(IndexB); + NewAlphas.Add(Alphas[Index] * (1 - Blend)); + NewAlphas.Add(Alphas[Index] * Blend); + } + return CreateDoubleIndexMaterial(NewIndices, NewAlphas, BestData); + } +#endif + default: + ensure(false); + return FVoxelMaterial::Default(); + } + }; + + if (bComputeValue) + { + OutValue = GetFloatOutput([&](const FVoxelGeneratorInstance& Instance) { return Instance.GetValue(X, Y, Z, Context.LOD, Items); }); + } + if (bComputeMaterial) + { + OutMaterial = GetMaterialOutput(); + } + + OutFloatOutputs.SetNumUninitialized(FloatOutputsNames.Num()); + for (int32 Index = 0; Index < FloatOutputsNames.Num(); Index++) + { + if (ComputeFloatOutputs[Index]) + { + OutFloatOutputs[Index] = GetFloatOutput([&](const FVoxelGeneratorInstance& Instance) + { + const auto Ptr = Instance.CustomPtrs.Float.FindRef(FloatOutputsNames[Index]); + if (Ptr) + { + return (Instance.*Ptr)(X, Y, Z, Context.LOD, Items); + } + else + { + return v_flt(0); + } + }); + } + } +} + +void FVoxelNodeFunctions::ComputeGeneratorsMergeRange( + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContextRange& Context, + const TVoxelRange X, + const TVoxelRange Y, + const TVoxelRange Z, + bool bComputeValue, const TArray& ComputeFloatOutputs, + TVoxelRange& OutValue, + TArray, TInlineAllocator<128>>& OutFloatOutputs, + TVoxelRange& NumGeneratorsQueried) +{ + thread_local int32 RecursionDepth = 0; + struct FDepthGuard { FDepthGuard() { RecursionDepth++; } ~FDepthGuard() { RecursionDepth--; } } DepthGuard; + + NumGeneratorsQueried = { 0, 4 }; + + check(RecursionDepth > 0); + if (RecursionDepth > 4) + { + static TSet> StaticInstances; + if (!StaticInstances.Contains(InInstances[0])) + { + StaticInstances.Add(InInstances[0]); + ShowGeneratorMergeError(); + } + OutValue = 0; + OutFloatOutputs.SetNum(FloatOutputsNames.Num()); + return; + } + + const auto Items = Context.Items; + + const FVoxelIntBox Bounds = FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + OutFloatOutputs.SetNumUninitialized(FloatOutputsNames.Num()); + + const auto ComputeFloatOutputsLambda = [&](auto& Instance, bool bUnion) + { + for (int32 OutputIndex = 0; OutputIndex < FloatOutputsNames.Num(); OutputIndex++) + { + if (ComputeFloatOutputs[OutputIndex]) + { + TVoxelRange Result; + const auto Ptr = Instance.CustomPtrs.FloatRange.FindRef(FloatOutputsNames[OutputIndex]); + if (Ptr) + { + Result = (Instance.*Ptr)(Bounds, Context.LOD, Items); + } + else + { + Result = 0; + } + OutFloatOutputs[OutputIndex] = bUnion ? TVoxelRange::Union(OutFloatOutputs[OutputIndex], Result) : Result; + } + } + }; + + if (bComputeValue) + { + OutValue = InInstances[0]->GetValueRange(Bounds, Context.LOD, Items); + } + ComputeFloatOutputsLambda(*InInstances[0], false); + + for (int32 Index = 1; Index < InInstances.Num(); Index++) + { + if (bComputeValue) + { + OutValue = TVoxelRange::Union(OutValue, InInstances[Index]->GetValueRange(Bounds, Context.LOD, Items)); + } + ComputeFloatOutputsLambda(*InInstances[Index], true); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelRichCurve::FVoxelRichCurve(const FRichCurve& Curve) + : Curve(Curve) +{ + Curve.GetValueRange(Min, Max); +} + +FVoxelRichCurve::FVoxelRichCurve(const UCurveFloat* Curve) + : FVoxelRichCurve(Curve ? Curve->FloatCurve : FRichCurve()) +{ +} + +FVoxelColorRichCurve::FVoxelColorRichCurve(const UCurveLinearColor* Curve) +{ + if (Curve) + { + Curves[0] = FVoxelRichCurve(Curve->FloatCurves[0]); + Curves[1] = FVoxelRichCurve(Curve->FloatCurves[1]); + Curves[2] = FVoxelRichCurve(Curve->FloatCurves[2]); + Curves[3] = FVoxelRichCurve(Curve->FloatCurves[3]); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNode.cpp new file mode 100644 index 00000000..b3b67ec9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNode.cpp @@ -0,0 +1,312 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelComputeNode.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelGraphGlobals.h" +#include "VoxelNode.h" + +inline FString MakeUniqueName(const FString& InName) +{ + const auto Name = FVoxelVariable::SanitizeName(InName); + + static TMap Names; + static int32 CompilationId; + check(CompilationId <= FVoxelGraphCompiler::GetCompilationId()); + if (CompilationId != FVoxelGraphCompiler::GetCompilationId()) + { + CompilationId = FVoxelGraphCompiler::GetCompilationId(); + Names.Empty(); + } + + int32& Id = Names.FindOrAdd(*Name); + return Name + "_" + FString::FromInt(Id++); +} + +inline TArray GetCacheOutputs(const FVoxelCompilationNode& CompilationNode) +{ + const auto NodeDependencies = FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(CompilationNode.Dependencies); + TArray Result; + for(auto& OutputPin : CompilationNode.IteratePins()) + { + bool bCache = false; + // Iterate linked to pin, and see if at least one has different dependencies (and hence we need to be stored in the cache) + for (auto& OtherPin : OutputPin.IterateLinkedTo()) + { + const auto OtherNodeDependencies = FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(OtherPin.Node.Dependencies); + const bool bOtherIsExecNode = OtherPin.Node.IsExecNode(); + ensure(int32(NodeDependencies) <= int32(OtherNodeDependencies) || bOtherIsExecNode); + + // If other is an exec node, always cache unless we are in XYZ + if (NodeDependencies != OtherNodeDependencies || (bOtherIsExecNode && NodeDependencies != EVoxelAxisDependencies::XYZ)) + { + bCache = true; + break; + } + } + Result.Add(bCache); + } + return Result; +} + +FVoxelComputeNode::FVoxelComputeNode(EVoxelComputeNodeType Type, const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : Type(Type) + , InputCount(CompilationNode.GetInputCountWithoutExecs()) + , OutputCount(CompilationNode.GetOutputCountWithoutExecs()) + , InputIds(CompilationNode.GetInputIds()) + , OutputIds(CompilationNode.GetOutputIds()) + , CacheOutputs(GetCacheOutputs(CompilationNode)) + , PrettyName(CompilationNode.GetPrettyName()) + , UniqueName(*MakeUniqueName(PrettyName)) + , Node(&Node) + , SourceNodes(CompilationNode.SourceNodes) + , Dependencies(FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(CompilationNode.Dependencies)) +{ + check(InputCount < MAX_VOXELNODE_PINS); + check(OutputCount < MAX_VOXELNODE_PINS); + + check(InputIds.Num() == InputCount); + check(OutputIds.Num() == OutputCount); + + for (auto& InputPin : CompilationNode.IteratePins()) + { + if (InputPin.PinCategory != EVoxelPinCategory::Exec) + { + InputsCategories.Add(InputPin.PinCategory); + } + } + for (auto& OutputPin : CompilationNode.IteratePins()) + { + if (OutputPin.PinCategory != EVoxelPinCategory::Exec) + { + OutputsCategories.Add(OutputPin.PinCategory); + } + } + check(InputsCategories.Num() == InputCount); + check(OutputsCategories.Num() == OutputCount); + + check(&CompilationNode.Node == &Node); + + for (auto& Pin : CompilationNode.IteratePins()) + { + if (Pin.PinCategory != EVoxelPinCategory::Exec) + { + const FString& DefaultValue = Pin.GetDefaultValue(); + DefaultValues.Add(FVoxelPinCategory::ConvertDefaultValue(Pin.PinCategory, DefaultValue)); + DefaultRangeValues.Add(FVoxelPinCategory::ConvertRangeDefaultValue(Pin.PinCategory, DefaultValue)); + DefaultValueStrings.Add(FVoxelPinCategory::ConvertStringDefaultValue(Pin.PinCategory, DefaultValue)); + } + } +} + +UVoxelGraphGenerator* FVoxelComputeNode::GetGraph() const +{ + if (SourceNodes.Num() > 0) + { + if (SourceNodes.Last().IsValid()) + { + return SourceNodes.Last()->Graph; + } + } + return nullptr; +} + +void FVoxelComputeNode::DeclareOutput(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, int32 OutputIndex) const +{ + const EVoxelPinCategory Category = GetOutputCategory(OutputIndex); + if (VariableInfo.IsInit() != (Category == EVoxelPinCategory::Seed)) + { + // Only declare seeds when init + return; + } + + const int32 Id = GetOutputId(OutputIndex); + const FString VariableName = Id == -1 ? GetUnusedOutputName(OutputIndex) : FVoxelCppIds::GetVariableName(Id); + const FString Declaration = + Constructor.GetTypeString(Category) + " " + + VariableName + "; " + + FString::Printf(TEXT("// %s output %d"), *PrettyName, OutputIndex); + + if (Id == -1) + { + ensure(!VariableInfo.IsFunctionParameter()); + if (!VariableInfo.IsStructDeclaration()) + { + Constructor.AddLine(Declaration); + } + } + else if (VariableInfo.IsInit()) + { + if (!Constructor.CurrentScopeHasVariable(Id)) + { + ensureAlways(!Constructor.HasVariable(Id)); + Constructor.AddLine(Declaration); + Constructor.AddVariable(Id, VariableName); + } + } + else if (VariableInfo.IsStructDeclaration()) + { + if (CacheOutputs[OutputIndex]) + { + const auto StructDependencies = VariableInfo.GetStructDeclarationDependencies(); + check(StructDependencies != EVoxelAxisDependencies::XYZ); + if (StructDependencies == Dependencies) + { + Constructor.AddLine(Declaration); + Constructor.AddVariable(Id, FVoxelCppIds::GetCacheName(Dependencies) + "." + VariableName); + } + } + } + else if (VariableInfo.IsFunctionParameter()) + { + Constructor.AddVariable(Id, VariableName); + } + else + { + // Hack to not have buffers in XYZWithoutCache + // check IsConstant as else GetFunctionDependencies would crash + if (!VariableInfo.IsConstant() && VariableInfo.GetFunctionDependencies() == EVoxelFunctionAxisDependencies::XYZWithoutCache) + { + if (!Constructor.CurrentScopeHasVariable(Id)) + { + Constructor.AddLine(Declaration); + Constructor.AddVariable(Id, VariableName); + } + } + else + { + if (CacheOutputs[OutputIndex]) + { + ensureAlways(Constructor.HasVariable(Id)); + ensureAlways(Constructor.HasVariable(Id) && Constructor.GetVariable(Id, nullptr) == FVoxelCppIds::GetCacheName(Dependencies) + "." + VariableName); + } + else + { + ensureAlways(!Constructor.HasVariable(Id)); + Constructor.AddLine(Declaration); + Constructor.AddVariable(Id, VariableName); + } + } + } +} + +void FVoxelComputeNode::DeclareOutputs(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const +{ + for (int32 OutputIndex = 0; OutputIndex < OutputCount; OutputIndex++) + { + DeclareOutput(Constructor, VariableInfo, OutputIndex); + } +} + +TArray FVoxelComputeNode::GetInputsNamesCppImpl(FVoxelCppConstructor& Constructor, bool bOnlySeeds) const +{ + TArray Inputs; + Inputs.SetNum(InputCount); + for (int32 InputIndex = 0; InputIndex < InputCount; InputIndex++) + { + const bool bIsSeed = GetInputCategory(InputIndex) == EVoxelPinCategory::Seed; + if ((bOnlySeeds && !bIsSeed) || (!bOnlySeeds && bIsSeed)) + { + continue; + } + + const int32 Id = GetInputId(InputIndex); + if (Id == -1) + { + Inputs[InputIndex] = Constructor.GetTypeString(GetInputCategory(InputIndex)) + "(" + GetDefaultValueString(InputIndex) + ")"; + } + else + { + Inputs[InputIndex] = Constructor.GetVariable(Id, this); + } + } + return Inputs; +} + +TArray FVoxelComputeNode::GetOutputsNamesCpp(FVoxelCppConstructor& Constructor) const +{ + TArray Outputs; + Outputs.SetNum(OutputCount); + for (int32 OutputIndex = 0; OutputIndex < OutputCount; OutputIndex++) + { + const int32 Id = GetOutputId(OutputIndex); + if (Id == -1) + { + Outputs[OutputIndex] = GetUnusedOutputName(OutputIndex); + } + else + { + Outputs[OutputIndex] = Constructor.GetVariable(Id, this); + } + } + return Outputs; +} + +void FVoxelComputeNode::GetPrivateVariables(TArray& PrivateVariables) const +{ + // Needed for forward decl +} + +FString FVoxelComputeNode::GetUnusedOutputName(int32 OutputIndex) const +{ + check(GetOutputId(OutputIndex) == -1); + return UniqueName.ToString() + "_Temp_" + FString::FromInt(OutputIndex); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelDataComputeNode::CallInitCpp(FVoxelCppConstructor& Constructor) +{ + if (!Constructor.IsNodeInit(this)) + { + DeclareOutputs(Constructor, FVoxelVariableAccessInfo::Init()); + InitCpp(GetSeedInputsNamesCpp(Constructor), Constructor); + Constructor.SetNodeAsInit(this); + } +} + +void FVoxelDataComputeNode::CallComputeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const +{ + DeclareOutputs(Constructor, VariableInfo); + ComputeCpp(GetInputsNamesCpp(Constructor), GetOutputsNamesCpp(Constructor), Constructor); +} + +void FVoxelDataComputeNode::CallComputeRangeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const +{ + DeclareOutputs(Constructor, VariableInfo); + ComputeRangeCpp(GetInputsNamesCpp(Constructor), GetOutputsNamesCpp(Constructor), Constructor); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSeedComputeNode::CallInitCpp(FVoxelCppConstructor& Constructor) +{ + if (!Constructor.IsNodeInit(this)) + { + DeclareOutputs(Constructor, FVoxelVariableAccessInfo::Init()); + InitCpp(GetSeedInputsNamesCpp(Constructor), GetOutputsNamesCpp(Constructor), Constructor); + Constructor.SetNodeAsInit(this); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelSetterComputeNode::CallComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) const +{ + check( + VariableInfo.GetFunctionDependencies() == EVoxelFunctionAxisDependencies::XYZWithoutCache || + VariableInfo.GetFunctionDependencies() == EVoxelFunctionAxisDependencies::XYZWithCache); + ComputeSetterNodeCpp(Constructor, GetInputsNamesCpp(Constructor), GraphOutputs); +} + +void FVoxelSetterComputeNode::CallComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) const +{ + check( + VariableInfo.GetFunctionDependencies() == EVoxelFunctionAxisDependencies::XYZWithoutCache || + VariableInfo.GetFunctionDependencies() == EVoxelFunctionAxisDependencies::XYZWithCache); + ComputeRangeSetterNodeCpp(Constructor, GetInputsNamesCpp(Constructor), GraphOutputs); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNodeTree.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNodeTree.cpp new file mode 100644 index 00000000..6fa62483 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelComputeNodeTree.cpp @@ -0,0 +1,233 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/VoxelDefaultComputeNodes.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelNodes/VoxelIfNode.h" +#include "VoxelGraphGenerator.h" + +void FVoxelComputeNodeTree::Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const +{ + for (auto& Node : SeedNodes) + { + FVoxelGraphSeed NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelGraphSeed NodeOutputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->Init(NodeInputBuffer, NodeOutputBuffer, InitStruct); + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + } + + for (auto& Node : DataNodes) + { + FVoxelGraphSeed NodeInputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->Init(NodeInputBuffer, InitStruct); + Node->CacheFunctionPtr(); + } + + if (ExecNode) + { + // Used only for materials node to check that the right config is used + ExecNode->Init(InitStruct); + } + + for (auto& Child : Children) + { + Child.Init(InitStruct, Buffers); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelComputeNodeTree::GetNodes(TSet& Nodes) const +{ + for (auto& Node : DataNodes) + { + Nodes.Add(Node); + } + + if (ExecNode) + { + Nodes.Add(ExecNode); + } + + for (auto& Child : Children) + { + Child.GetNodes(Nodes); + } +} + +void FVoxelComputeNodeTree::InitCpp(FVoxelCppConstructor& Constructor) const +{ + for (auto& Node : SeedNodes) + { + Constructor.QueueComment("// Init of " + Node->PrettyName); + Node->CallInitCpp(Constructor); + Constructor.EndComment(); + } + + for (auto& Node : DataNodes) + { + Constructor.QueueComment("// Init of " + Node->PrettyName); + Node->CallInitCpp(Constructor); + Constructor.EndComment(); + } + + for (auto& Child : Children) + { + Child.InitCpp(Constructor); + } +} + +void FVoxelComputeNodeTree::ComputeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) +{ + for (auto& Node : DataNodes) + { + Constructor.QueueComment(FString::Printf(TEXT("// %s"), *Node->PrettyName)); + Node->CallComputeCpp(Constructor, VariableInfo); + Constructor.EndComment(); + } + + if (!ExecNode) + { + return; + } + + switch (ExecNode->ExecType) + { + case EVoxelComputeNodeExecType::FunctionInit: + case EVoxelComputeNodeExecType::Passthrough: + { + if (Children.Num() > 0) + { + Children[0].ComputeCpp(Constructor, VariableInfo, GraphOutputs); + } + break; + } + case EVoxelComputeNodeExecType::If: + { + const int32 InputId = ExecNode->GetInputId(0); + const FString Condition = InputId == -1 ? ExecNode->GetDefaultValueString(0) : Constructor.GetVariable(InputId, ExecNode); + Constructor.AddLine("if (" + Condition + ")"); + Constructor.StartBlock(); + { + FVoxelCppVariableScope Scope(Constructor); + Children[0].ComputeCpp(Constructor, VariableInfo, GraphOutputs); + } + Constructor.EndBlock(); + Constructor.AddLine("else"); + Constructor.StartBlock(); + { + FVoxelCppVariableScope Scope(Constructor); + Children[1].ComputeCpp(Constructor, VariableInfo, GraphOutputs); + } + Constructor.EndBlock(); + break; + } + case EVoxelComputeNodeExecType::Setter: + { + static_cast(ExecNode)->CallComputeSetterNodeCpp(Constructor, VariableInfo, GraphOutputs); + if (Children.Num() > 0) // Only a setter can have no children (cf FVoxelRemoveUnusedExecsPass) + { + Children[0].ComputeCpp(Constructor, VariableInfo, GraphOutputs); + } + break; + } + case EVoxelComputeNodeExecType::FunctionCall: + { + check(Children.Num() == 0); + auto* Function = static_cast(ExecNode)->GetFunction(); + Function->Call(Constructor, ExecNode->GetInputsNamesCpp(Constructor), EVoxelFunctionType::Compute); + break; + } + default: + { + check(false); + break; + } + } +} + +void FVoxelComputeNodeTree::ComputeRangeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) +{ + for (auto& Node : DataNodes) + { + Constructor.QueueComment(FString::Printf(TEXT("// %s"), *Node->PrettyName)); + Node->CallComputeRangeCpp(Constructor, VariableInfo); + Constructor.EndComment(); + } + + if (!ExecNode) + { + return; + } + + switch (ExecNode->ExecType) + { + case EVoxelComputeNodeExecType::FunctionInit: + case EVoxelComputeNodeExecType::Passthrough: + { + if (Children.Num() > 0) + { + Children[0].ComputeRangeCpp(Constructor, VariableInfo, GraphOutputs); + } + break; + } + case EVoxelComputeNodeExecType::If: + { + const int32 InputId = ExecNode->GetInputId(0); + const FString Condition = InputId == -1 ? ExecNode->GetDefaultValueString(0) : Constructor.GetVariable(InputId, ExecNode); + auto* IfNode = static_cast(ExecNode); + if (IfNode->BranchToUseForRangeAnalysis != EVoxelNodeIfBranchToUseForRangeAnalysis::None) + { + const bool bCondition = IfNode->BranchToUseForRangeAnalysis == EVoxelNodeIfBranchToUseForRangeAnalysis::UseTrue; + check(bCondition || IfNode->BranchToUseForRangeAnalysis == EVoxelNodeIfBranchToUseForRangeAnalysis::UseFalse); + Constructor.AddLine("if (FVoxelBoolRange::If(" + Condition + ", " + FString(bCondition ? "true" : "false") + "))"); + } + else + { + Constructor.AddLine("if (" + Condition + ")"); + } + Constructor.StartBlock(); + { + FVoxelCppVariableScope Scope(Constructor); + Children[0].ComputeRangeCpp(Constructor, VariableInfo, GraphOutputs); + } + Constructor.EndBlock(); + Constructor.AddLine("else"); + Constructor.StartBlock(); + { + FVoxelCppVariableScope Scope(Constructor); + Children[1].ComputeRangeCpp(Constructor, VariableInfo, GraphOutputs); + } + Constructor.EndBlock(); + break; + } + case EVoxelComputeNodeExecType::Setter: + { + static_cast(ExecNode)->CallComputeRangeSetterNodeCpp(Constructor, VariableInfo, GraphOutputs); + if (Children.Num() > 0) // Only a setter can have no children (cf FVoxelRemoveUnusedExecsPass) + { + Children[0].ComputeRangeCpp(Constructor, VariableInfo, GraphOutputs); + } + break; + } + case EVoxelComputeNodeExecType::FunctionCall: + { + check(Children.Num() == 0); + auto* Function = static_cast(ExecNode)->GetFunction(); + Function->Call(Constructor, ExecNode->GetInputsNamesCpp(Constructor), EVoxelFunctionType::Compute); + break; + } + default: + { + check(false); + break; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraph.cpp new file mode 100644 index 00000000..3ef64e16 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraph.cpp @@ -0,0 +1,251 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelGraphConstants.h" +#include "VoxelContext.h" + +FVoxelGraphFunctions::FVoxelGraphFunctions( + int32 FunctionId, + const TVoxelSharedPtr& FunctionX, + const TVoxelSharedPtr& FunctionXYWithCache, + const TVoxelSharedPtr& FunctionXYWithoutCache, + const TVoxelSharedPtr& FunctionXYZWithCache, + const TVoxelSharedPtr& FunctionXYZWithoutCache) + : FunctionId(FunctionId) + , FunctionX(FunctionX) + , FunctionXYWithCache(FunctionXYWithCache) + , FunctionXYWithoutCache(FunctionXYWithoutCache) + , FunctionXYZWithCache(FunctionXYZWithCache) + , FunctionXYZWithoutCache(FunctionXYZWithoutCache) +{ + check(FunctionX); + check(FunctionXYWithCache); + check(FunctionXYWithoutCache); + check(FunctionXYZWithCache); + check(FunctionXYZWithoutCache); +} + +FVoxelGraph::FVoxelGraph( + const FString& Name, + const TArray& AllFunctions, + const FVoxelGraphFunctions& FirstFunctions, + const TArray>& ConstantComputeNodes, + const TArray>& SeedComputeNodes, + int32 VariablesBufferSize) + : Name(Name) + , AllFunctions(AllFunctions) + , FirstFunctions(FirstFunctions) + , ConstantComputeNodes(ConstantComputeNodes) + , SeedComputeNodes(SeedComputeNodes) + , VariablesBufferSize(VariablesBufferSize) +{ + for (auto& Functions : AllFunctions) + { + check(Functions.IsValid()); + } +} + +void FVoxelGraph::Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const +{ + // First init seeds + for (auto& Node : SeedComputeNodes) + { + FVoxelGraphSeed NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelGraphSeed NodeOutputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->Init(NodeInputBuffer, NodeOutputBuffer, InitStruct); + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + } + + // Then constant nodes + for (auto& Node : ConstantComputeNodes) + { + FVoxelGraphSeed NodeInputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->Init(NodeInputBuffer, InitStruct); + } + + // And then other nodes + for (auto& Functions : AllFunctions) + { + for (auto& Function : Functions.Iterate()) + { + if (Function->IsUsedForInit()) + { + Function->Init(InitStruct, Buffers); + } + } + } +} + +void FVoxelGraph::ComputeConstants(FVoxelGraphVMComputeBuffers& Buffers) const +{ + for (auto& Node : ConstantComputeNodes) + { + FVoxelNodeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelNodeType NodeOutputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->Compute(NodeInputBuffer, NodeOutputBuffer, FVoxelContext::EmptyContext); + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + } +} + +void FVoxelGraph::ComputeRangeConstants(FVoxelGraphVMComputeRangeBuffers& Buffers) const +{ + FVoxelRangeFailStatus::Get().Reset(); + for (auto& Node : ConstantComputeNodes) + { + FVoxelNodeRangeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelNodeRangeType NodeOutputBuffer[MAX_VOXELNODE_PINS]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + Node->ComputeRange(NodeInputBuffer, NodeOutputBuffer, FVoxelContextRange::EmptyContext); + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + } + ensureAlwaysMsgf(!FVoxelRangeFailStatus::Get().HasFailed(), TEXT("A constant node has failed range analysis. This isn't supported!")); +} + +void FVoxelGraph::GetSeedNodes(TSet& Nodes) const +{ + for (auto& Node : SeedComputeNodes) + { + Nodes.Add(&Node.Get()); + } +} + +void FVoxelGraph::GetConstantNodes(TSet& Nodes) const +{ + for (auto& Node : ConstantComputeNodes) + { + Nodes.Add(&Node.Get()); + } +} + +void FVoxelGraph::GetNotConstantNodes(TSet& Nodes) const +{ + for (auto& Functions : AllFunctions) + { + for (auto& Function : Functions.Iterate()) + { + Function->GetNodes(Nodes); + } + } +} + +void FVoxelGraph::GetAllNodes(TSet& Nodes) const +{ + GetSeedNodes(Nodes); + GetConstantNodes(Nodes); + GetNotConstantNodes(Nodes); +} + +void FVoxelGraph::Init(FVoxelCppConstructor& Constructor) const +{ + Constructor.AddLine("////////////////////////////////////////////////////"); + Constructor.AddLine("/////////////// Constant nodes init ////////////////"); + Constructor.AddLine("////////////////////////////////////////////////////"); + Constructor.StartBlock(); + { + FVoxelCppVariableScope Scope(Constructor); + + Constructor.AddLine("/////////////////////////////////////////////////////////////////////////////////"); + Constructor.AddLine("//////// First compute all seeds in case they are used by constant nodes ////////"); + Constructor.AddLine("/////////////////////////////////////////////////////////////////////////////////"); + Constructor.NewLine(); + + // First init seeds + for (auto& Node : SeedComputeNodes) + { + Constructor.QueueComment("// Init of " + Node->PrettyName); + Node->CallInitCpp(Constructor); + Constructor.EndComment(); + } + + Constructor.NewLine(); + Constructor.AddLine("////////////////////////////////////////////////////"); + Constructor.AddLine("///////////// Then init constant nodes /////////////"); + Constructor.AddLine("////////////////////////////////////////////////////"); + Constructor.NewLine(); + + // Then constant nodes + for (auto& Node : ConstantComputeNodes) + { + Constructor.QueueComment("// Init of " + Node->PrettyName); + Node->CallInitCpp(Constructor); + Constructor.EndComment(); + } + } + Constructor.EndBlock(); + Constructor.NewLine(); + Constructor.AddLine("////////////////////////////////////////////////////"); + Constructor.AddLine("//////////////////// Other inits ///////////////////"); + Constructor.AddLine("////////////////////////////////////////////////////"); + + // And then other nodes + for (auto& Functions : AllFunctions) + { + for (auto* Function : Functions.Iterate()) + { + if (Function->IsUsedForInit()) + { + Function->Call(Constructor, {}, EVoxelFunctionType::Init); + } + } + } +} + +void FVoxelGraph::ComputeConstants(FVoxelCppConstructor& Constructor) const +{ + FVoxelCppVariableScope Scope(Constructor); + for (auto& Node : ConstantComputeNodes) + { + Constructor.QueueComment("// " + Node->PrettyName); + if (Constructor.Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + Node->CallComputeRangeCpp(Constructor, FVoxelVariableAccessInfo::Constant()); + } + else + { + Node->CallComputeCpp(Constructor, FVoxelVariableAccessInfo::Constant()); + } + Constructor.EndComment(); + } +} + +void FVoxelGraph::Compute(FVoxelCppConstructor& Constructor, EVoxelFunctionAxisDependencies Dependencies) const +{ + FirstFunctions.Get(Dependencies).Call(Constructor, {}, EVoxelFunctionType::Compute); +} + +void FVoxelGraph::DeclareInitFunctions(FVoxelCppConstructor& Constructor) const +{ + for (auto& Functions : AllFunctions) + { + for (auto& Function : Functions.Iterate()) + { + if (Function->IsUsedForInit()) + { + Function->DeclareInitFunction(Constructor); + Constructor.NewLine(); + } + } + } +} + +void FVoxelGraph::DeclareComputeFunctions(FVoxelCppConstructor& Constructor, const TArray& GraphOutputs) const +{ + for (auto& Functions : AllFunctions) + { + for (auto& Function : Functions.Iterate()) + { + if (Function->IsUsedForCompute(Constructor)) + { + Function->DeclareComputeFunction(Constructor, GraphOutputs); + Constructor.NewLine(); + } + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.cpp new file mode 100644 index 00000000..55ea9b4b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.cpp @@ -0,0 +1,333 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelGraphChecker.h" +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelDefaultComputeNodes.h" +#include "VoxelGraphErrorReporter.h" + +struct FVoxelDefinition +{ + const FVoxelComputeNode* Node = nullptr; + int32 PinIndex = 0; + + FVoxelDefinition() = default; + FVoxelDefinition(const FVoxelComputeNode* Node, int32 PinIndex) + : Node(Node) + , PinIndex(PinIndex) + { + } + + inline bool IsValid() const + { + return Node != nullptr; + } +}; + +struct FVoxelFrames +{ + FVoxelFrames() = default; + + inline void Push() + { + Definitions.Emplace(); + } + inline void Pop() + { + Definitions.Pop(); + } + + void AddDefinition(int32 Id, const FVoxelDefinition& Definition) + { + ensure(Definition.IsValid()); + auto& CurrentDefinitions = Definitions.Last(); + if (Id >= CurrentDefinitions.Num()) + { + CurrentDefinitions.SetNum(Id + 1); + } + check(!FindDefinition(Id).IsValid()); + CurrentDefinitions[Id] = Definition; + } + + FVoxelDefinition FindDefinition(int32 Id) const + { + for (int32 Index = Definitions.Num() - 1; Index >= 0; Index--) + { + if (Definitions[Index].IsValidIndex(Id)) + { + const FVoxelDefinition& Definition = Definitions[Index][Id]; + if (Definition.IsValid()) + { + return Definition; + } + } + } + return {}; + } + +private: + TArray> Definitions; +}; + +inline void CheckNode(const FVoxelComputeNode& Node, FVoxelGraphErrorReporter& ErrorReporter, FVoxelFrames& Frames, bool bIsInit) +{ + for (int32 Index = 0; Index < Node.InputCount; Index++) + { + if ((Node.GetInputCategory(Index) == EVoxelPinCategory::Seed) == bIsInit) + { + const int32 InputId = Node.GetInputId(Index); + if (InputId != -1 && !Frames.FindDefinition(InputId).IsValid()) + { + ErrorReporter.AddMessageToNode(&Node, FString::Printf(TEXT("input pin %d is using an undefined variable!"), Index), EVoxelGraphNodeMessageType::Error); + } + } + } + for (int32 Index = 0; Index < Node.OutputCount; Index++) + { + if ((Node.GetOutputCategory(Index) == EVoxelPinCategory::Seed) == bIsInit) + { + const int32 OutputId = Node.GetOutputId(Index); + if (OutputId < 0) + { + check(OutputId == -1); + // unused output + } + else + { + auto PreviousDefinition = Frames.FindDefinition(OutputId); + if (PreviousDefinition.IsValid()) + { + // Functions init are allowed to redefine variables, and seed too as long as it's the same node + if (!(Node.Type == EVoxelComputeNodeType::Exec && static_cast(Node).ExecType == EVoxelComputeNodeExecType::FunctionInit) && + !(Node.Type == EVoxelComputeNodeType::Seed && PreviousDefinition.Node == &Node)) + { + ErrorReporter.AddMessageToNode(&Node, FString::Printf(TEXT("output pin %d is redefining a variable! definition id: %d"), Index, OutputId), EVoxelGraphNodeMessageType::Error); + ErrorReporter.AddMessageToNode(PreviousDefinition.Node, FString::Printf(TEXT("output pin %d definition id: %d"), PreviousDefinition.PinIndex, OutputId), EVoxelGraphNodeMessageType::Info, false); + } + } + else + { + Frames.AddDefinition(OutputId, FVoxelDefinition(&Node, Index)); + } + } + } + } +} + +inline void CheckTree( + const FVoxelComputeNodeTree& Tree, + FVoxelGraphErrorReporter& InitErrorReporter, + FVoxelGraphErrorReporter& ComputeErrorReporter, + FVoxelFrames& InitFrames, + FVoxelFrames& ComputeFrames, + const TArray& Branches, + int32 Depth, + TSet& OutFunctionsCalled) +{ + InitFrames.Push(); + ComputeFrames.Push(); + + // Init + for (auto* SeedNode : Tree.GetSeedNodes()) + { + check(SeedNode); + CheckNode(*SeedNode, InitErrorReporter, InitFrames, true); + } + for (auto* DataNode : Tree.GetDataNodes()) + { + check(DataNode); + CheckNode(*DataNode, InitErrorReporter, InitFrames, true); + } + + // Compute + for (auto* DataNode : Tree.GetDataNodes()) + { + check(DataNode); + CheckNode(*DataNode, ComputeErrorReporter, ComputeFrames, false); + } + if (auto* ExecNode = Tree.GetExecNode()) + { + CheckNode(*ExecNode, ComputeErrorReporter, ComputeFrames, false); + } + + // Children + auto& Children = Tree.GetChildren(); + check(Children.Num() <= 2); + if (Children.Num() > 0) + { + auto& Child = Children.Num() == 1 ? Children[0] : Children[Branches[Depth]]; + CheckTree(Child, InitErrorReporter, ComputeErrorReporter, InitFrames, ComputeFrames, Branches, Children.Num() == 1 ? Depth : (Depth + 1), OutFunctionsCalled); + } + + // Find functions + if (auto* ExecNode = Tree.GetExecNode()) + { + if (ExecNode->ExecType == EVoxelComputeNodeExecType::FunctionCall) + { + auto* FunctionCall = static_cast(ExecNode); + OutFunctionsCalled.Add(FunctionCall->GetFunction()); + check(Tree.GetChildren().Num() == 0); + } + } +} + +inline void CheckFunction( + const FVoxelGraphFunction& Function, + FVoxelGraphErrorReporter& ErrorReporter, + FVoxelFrames& InitFrames, + FVoxelFrames& ComputeFrames, + const TArray& Branches) +{ + TSet FunctionsCalled; + { + FVoxelGraphErrorReporter FunctionErrorReporter(ErrorReporter, "function " + FString::FromInt(Function.FunctionId)); + FVoxelGraphErrorReporter InitErrorReporter(FunctionErrorReporter, "init"); + FVoxelGraphErrorReporter ComputeErrorReporter(FunctionErrorReporter, "compute"); + CheckTree(Function.GetTree(), InitErrorReporter, ComputeErrorReporter, InitFrames, ComputeFrames, Branches, 0, FunctionsCalled); + } + check(!FunctionsCalled.Contains(&Function)); + for (auto* FunctionCalled : FunctionsCalled) + { + EVoxelFunctionAxisDependencies Dependencies = FunctionCalled->Dependencies; + switch (Function.Dependencies) + { + case EVoxelFunctionAxisDependencies::X: + check(Dependencies == EVoxelFunctionAxisDependencies::X); + break; + case EVoxelFunctionAxisDependencies::XYWithCache: + check(Dependencies == EVoxelFunctionAxisDependencies::XYWithCache || Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache); + break; + case EVoxelFunctionAxisDependencies::XYWithoutCache: + check(Dependencies == EVoxelFunctionAxisDependencies::XYWithoutCache); + break; + case EVoxelFunctionAxisDependencies::XYZWithCache: + check(Dependencies == EVoxelFunctionAxisDependencies::XYZWithCache || Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache); + break; + case EVoxelFunctionAxisDependencies::XYZWithoutCache: + check(Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache); + break; + default: + check(false); + } + } +} + +void InitVoxelFrames( + FVoxelGraphErrorReporter& ErrorReporter, + FVoxelFrames& InitFrames, + FVoxelFrames& ComputeFrames, + const FVoxelGraph& Graph) +{ + InitFrames.Push(); + ComputeFrames.Push(); + + // Init + FVoxelGraphErrorReporter InitErrorReporter(ErrorReporter, "constant init"); + for (auto& SeedNode : Graph.SeedComputeNodes) + { + CheckNode(*SeedNode, InitErrorReporter, InitFrames, true); + } + for (auto& DataNode : Graph.ConstantComputeNodes) + { + CheckNode(*DataNode, InitErrorReporter, InitFrames, true); + } + + // Compute + FVoxelGraphErrorReporter ComputeErrorReporter(ErrorReporter, "constant compute"); + for (auto& DataNode : Graph.ConstantComputeNodes) + { + CheckNode(*DataNode, ComputeErrorReporter, ComputeFrames, false); + } +} +inline int32 GetNumBranches(const FVoxelComputeNodeTree& Tree) +{ + int32 Num = 0; + auto& Children = Tree.GetChildren(); + if (Children.Num() > 1) + { + check(Children.Num() == 2); + Num++; + } + int32 MaxChild = 0; + for (auto& Child : Children) + { + MaxChild = FMath::Max(MaxChild, GetNumBranches(Child)); + } + return Num + MaxChild; +} + +inline bool IncreaseBranches(TArray& Branches) +{ + for (int32 Index = 0; Index < Branches.Num() ; Index++) + { + if (!Branches[Index]) + { + Branches[Index] = true; + return true; + } + else + { + Branches[Index] = false; + } + } + return false; +} + +void FVoxelGraphChecker::CheckGraph(FVoxelGraphErrorReporter& ErrorReporter, const FVoxelGraph& Graph) +{ + for (auto& Functions : Graph.AllFunctions) + { + TArray Branches; + Branches.SetNum(GetNumBranches(Functions.FunctionXYZWithoutCache->GetTree())); + int32 Iterations = 0; + do + { + Iterations++; + { + FVoxelFrames InitFrames; + FVoxelFrames ComputeFrames; + InitVoxelFrames(ErrorReporter, InitFrames, ComputeFrames, Graph); + TSet FunctionsAlreadyCalled; + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "X"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::X), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "XYWithCache"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::XYWithCache), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "XYZWithCache"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::XYZWithCache), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + } + { + FVoxelFrames InitFrames; + FVoxelFrames ComputeFrames; + InitVoxelFrames(ErrorReporter, InitFrames, ComputeFrames, Graph); + TSet FunctionsAlreadyCalled; + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "XYWithoutCache"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::XYWithoutCache), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "XYZWithCache"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::XYZWithCache), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + } + { + FVoxelFrames InitFrames; + FVoxelFrames ComputeFrames; + InitVoxelFrames(ErrorReporter, InitFrames, ComputeFrames, Graph); + TSet FunctionsAlreadyCalled; + { + FVoxelGraphErrorReporter LocalErrorReporter(ErrorReporter, "XYZWithoutCache"); + CheckFunction(Functions.Get(EVoxelFunctionAxisDependencies::XYZWithoutCache), LocalErrorReporter, InitFrames, ComputeFrames, Branches); + } + } + } while (IncreaseBranches(Branches)); + check(Iterations == 1 << Branches.Num()); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.h new file mode 100644 index 00000000..78443abe --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphChecker.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class FVoxelGraphErrorReporter; +class FVoxelGraph; + +namespace FVoxelGraphChecker +{ + void CheckGraph(FVoxelGraphErrorReporter& ErrorReporter, const FVoxelGraph& Graph); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphFunction.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphFunction.cpp new file mode 100644 index 00000000..cc676d99 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/Runtime/VoxelGraphFunction.cpp @@ -0,0 +1,117 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "VoxelGraphConstants.h" + +FString FVoxelGraphFunctionInfo::GetFunctionName() const +{ + FString Name = "Function" + FString::FromInt(FunctionId) + "_" + FVoxelAxisDependencies::ToString(Dependencies); + if (FunctionType == EVoxelFunctionType::Init) + { + Name += "_Init"; + } + else + { + check(FunctionType == EVoxelFunctionType::Compute); + Name += "_Compute"; + } + return Name; +} + +FVoxelGraphFunction::FVoxelGraphFunction( + const TVoxelSharedRef& Tree, + const TVoxelSharedRef& FunctionInit, + int32 FunctionId, + EVoxelFunctionAxisDependencies Dependencies) + : FunctionId(FunctionId) + , Dependencies(Dependencies) + , Tree(Tree) + , FunctionInit(FunctionInit) +{ + check(FunctionId >= 0); + check(FunctionInit->Type == EVoxelComputeNodeType::Exec); + check(static_cast(FunctionInit.Get()).ExecType == EVoxelComputeNodeExecType::FunctionInit); +} + +void FVoxelGraphFunction::Call(FVoxelCppConstructor& Constructor, const TArray& Args, EVoxelFunctionType FunctionType) const +{ + Constructor.AddFunctionCall(FVoxelGraphFunctionInfo(FunctionId, FunctionType, Dependencies), Args); +} + +bool FVoxelGraphFunction::IsUsedForInit() const +{ + // For init we only call the full function + return Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; +} + +bool FVoxelGraphFunction::IsUsedForCompute(FVoxelCppConstructor& Constructor) const +{ + return !Constructor.Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex) + || Dependencies == EVoxelFunctionAxisDependencies::XYZWithoutCache; +} + +void FVoxelGraphFunction::Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const +{ + check(IsUsedForInit()); + + Tree->Init(InitStruct, Buffers); +} + +void FVoxelGraphFunction::GetNodes(TSet& Nodes) const +{ + Tree->GetNodes(Nodes); +} + +void FVoxelGraphFunction::DeclareInitFunction(FVoxelCppConstructor& Constructor) const +{ + check(IsUsedForInit()); + + DeclareFunction(Constructor, EVoxelFunctionType::Init); + Constructor.StartBlock(); + + FVoxelCppVariableScope Scope(Constructor); + FunctionInit->DeclareOutputs(Constructor, FVoxelVariableAccessInfo::FunctionParameter()); + Tree->InitCpp(Constructor); + + Constructor.EndBlock(); +} + +void FVoxelGraphFunction::DeclareComputeFunction(FVoxelCppConstructor& Constructor, const TArray& GraphOutputs) const +{ + check(IsUsedForCompute(Constructor)); + + DeclareFunction(Constructor, EVoxelFunctionType::Compute); + Constructor.StartBlock(); + + FVoxelCppVariableScope Scope(Constructor); + FunctionInit->DeclareOutputs(Constructor, FVoxelVariableAccessInfo::FunctionParameter()); + if (Constructor.Permutation.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + Tree->ComputeRangeCpp(Constructor, FVoxelVariableAccessInfo::Compute(Dependencies), GraphOutputs); + } + else + { + Tree->ComputeCpp(Constructor, FVoxelVariableAccessInfo::Compute(Dependencies), GraphOutputs); + } + + Constructor.EndBlock(); +} + +void FVoxelGraphFunction::DeclareFunction(FVoxelCppConstructor& Constructor, EVoxelFunctionType Type) const +{ + TArray Args; + for (int32 Index = 0; Index < FunctionInit->OutputCount; Index++) + { + const auto Category = FunctionInit->GetOutputCategory(Index); + if (Type != EVoxelFunctionType::Init || Category == EVoxelPinCategory::Seed) + { + Args.Add(Constructor.GetTypeString(Category) + " " + FVoxelCppIds::GetVariableName(FunctionInit->GetOutputId(Index))); + } + } + Constructor.AddFunctionDeclaration(FVoxelGraphFunctionInfo(FunctionId, Type, Dependencies), Args); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelAxisDependencies.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelAxisDependencies.cpp new file mode 100644 index 00000000..4a6a760a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelAxisDependencies.cpp @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelAxisDependencies.h" + +FString FVoxelAxisDependencies::ToString(EVoxelAxisDependencies Dependencies) +{ + switch (Dependencies) + { + case EVoxelAxisDependencies::Constant: + return "Constant"; + case EVoxelAxisDependencies::X: + return "X"; + case EVoxelAxisDependencies::XY: + return "XY"; + case EVoxelAxisDependencies::XYZ: + return "XYZ"; + default: + check(false); + return ""; + } +} + +FString FVoxelAxisDependencies::ToString(EVoxelFunctionAxisDependencies Dependencies) +{ + switch (Dependencies) + { + case EVoxelFunctionAxisDependencies::X: + return "X"; + case EVoxelFunctionAxisDependencies::XYWithCache: + return "XYWithCache"; + case EVoxelFunctionAxisDependencies::XYWithoutCache: + return "XYWithoutCache"; + case EVoxelFunctionAxisDependencies::XYZWithCache: + return "XYZWithCache"; + case EVoxelFunctionAxisDependencies::XYZWithoutCache: + return "XYZWithoutCache"; + default: + check(false); + return ""; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelContext.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelContext.cpp new file mode 100644 index 00000000..12f4b2e7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelContext.cpp @@ -0,0 +1,16 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelContext.h" + +const FVoxelContext FVoxelContext::EmptyContext = FVoxelContext( + 0, + FVoxelItemStack::Empty, + FTransform::Identity, + false); + +const FVoxelContextRange FVoxelContextRange::EmptyContext = FVoxelContextRange( + 0, + FVoxelItemStack::Empty, + FTransform::Identity, + false, + FVoxelIntBox()); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp new file mode 100644 index 00000000..41ca788b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp @@ -0,0 +1,316 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphErrorReporter.h" +#include "VoxelNode.h" +#include "VoxelGraphGenerator.h" +#include "IVoxelGraphEditor.h" +#include "Runtime/VoxelComputeNode.h" +#include "Compilation/VoxelCompilationNode.h" +#include "VoxelNodes/VoxelGraphMacro.h" + +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphNode.h" +#include "Modules/ModuleManager.h" + +FVoxelGraphErrorReporter::FVoxelGraphErrorReporter(const UVoxelGraphGenerator* VoxelGraphGenerator) + : VoxelGraphGenerator(VoxelGraphGenerator) + , Parent(nullptr) + , ErrorPrefix("") +{ + ensure(VoxelGraphGenerator); +} + +FVoxelGraphErrorReporter::FVoxelGraphErrorReporter(FVoxelGraphErrorReporter& Parent, const FString& ErrorPrefix) + : VoxelGraphGenerator(Parent.VoxelGraphGenerator) + , Parent(&Parent) + , ErrorPrefix(Parent.ErrorPrefix + ErrorPrefix + ": ") +{ + ensure(VoxelGraphGenerator); +} + +FVoxelGraphErrorReporter::~FVoxelGraphErrorReporter() +{ + if (Parent) + { + Parent->CopyFrom(*this); + } +} + +void FVoxelGraphErrorReporter::AddError(const FString& Error) +{ + if (!Error.IsEmpty()) + { + const FString ErrorWithPrefix = AddPrefixToError(Error); + Messages.Add(FVoxelGraphMessage{ nullptr, Error, EVoxelGraphNodeMessageType::Error }); + bHasError = true; + } +} + +void FVoxelGraphErrorReporter::AddInternalError(const FString Error) +{ + ensureMsgf(false, TEXT("Internal voxel graph compiler error: %s"), *Error); + + const bool bOldHasErrors = bHasError; + AddError("Internal error: " + Error + + "\nPlease create a bug report here: https://gitlab.com/Phyronnaz/VoxelPluginIssues/issues \n" + "Don't forget to attach the generated header file"); + bHasError = bOldHasErrors; +} + +inline FString& GetErrorString(UVoxelGraphNodeInterface* Node, EVoxelGraphNodeMessageType Type) +{ + switch (Type) + { + default: ensure(false); + case EVoxelGraphNodeMessageType::Info: + return Node->InfoMsg; + case EVoxelGraphNodeMessageType::Warning: + return Node->WarningMsg; + case EVoxelGraphNodeMessageType::Error: + return Node->ErrorMsg; + } +} + +void FVoxelGraphErrorReporter::AddMessageToNode( + const UVoxelNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode, + bool bShowInList) +{ + check(Node); + const FString MessageWithPrefix = AddPrefixToError(Message); + + if (Severity == EVoxelGraphNodeMessageType::Error) + { + bHasError = true; + } + + if (bShowInList) + { + FVoxelGraphMessage NewMessage; + NewMessage.Node = Node; + NewMessage.Message = MessageWithPrefix; + NewMessage.Type = Severity; + Messages.Add(NewMessage); + } + + if (bSelectNode) + { + AddNodeToSelect(Node); + } + +#if WITH_EDITORONLY_DATA + if (auto* GraphNode = Node->GraphNode) + { + AddMessageToNodeInternal(Node, MessageWithPrefix, Severity); + GraphsToRefresh.Add(GraphNode->GetGraph()); + } +#endif +} +void FVoxelGraphErrorReporter::AddMessageToNode( + const FVoxelCompilationNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode, + bool bShowInList) +{ + check(Node); + + for (auto* SourceNode : Node->SourceNodes) + { + AddMessageToNode(SourceNode, Message, Severity, bSelectNode, bShowInList); + } +} + +void FVoxelGraphErrorReporter::AddMessageToNode( + const FVoxelComputeNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode, + bool bShowInList) +{ + check(Node); + + for (auto& SourceNode : Node->SourceNodes) + { + if (SourceNode.IsValid()) + { + AddMessageToNode(SourceNode.Get(), Message, Severity, bSelectNode, bShowInList); + } + } +} + +void FVoxelGraphErrorReporter::AddNodeToSelect(const UVoxelNode* Node) +{ +#if WITH_EDITORONLY_DATA + if (Node && Node->GraphNode) + { + NodesToSelect.Add(Node->GraphNode); + } +#endif +} +void FVoxelGraphErrorReporter::AddNodeToSelect(const FVoxelCompilationNode* Node) +{ + if (Node->SourceNodes.Num() > 0) + { + AddNodeToSelect(Node->SourceNodes.Last()); + } +} + +void FVoxelGraphErrorReporter::Apply(bool bSelectNodes) +{ +#if WITH_EDITOR + if (VoxelGraphGenerator && VoxelGraphGenerator->VoxelGraph) + { + GraphsToRefresh.Add(VoxelGraphGenerator->VoxelGraph); + if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) + { + for (auto* GraphToRefresh : GraphsToRefresh) + { + VoxelGraphEditor->RefreshNodesMessages(GraphToRefresh); + } + if (NodesToSelect.Num() > 0 && bSelectNodes) + { + VoxelGraphEditor->SelectNodesAndZoomToFit(VoxelGraphGenerator->VoxelGraph, NodesToSelect.Array()); + } + VoxelGraphEditor->AddMessages(VoxelGraphGenerator, Messages); + } + } + else +#endif + { + for (auto& Message : Messages) + { + if (Message.Type == EVoxelGraphNodeMessageType::Error) + { + LOG_VOXEL(Warning, TEXT("%s failed to compile: %s"), VoxelGraphGenerator ? *VoxelGraphGenerator->GetName() : TEXT(""), *Message.Message); + } + } + } +} + +void FVoxelGraphErrorReporter::CopyFrom(FVoxelGraphErrorReporter& Other) +{ + check(VoxelGraphGenerator == Other.VoxelGraphGenerator); + + bHasError |= Other.bHasError; + Messages.Append(Other.Messages); + NodesToSelect.Append(Other.NodesToSelect); + GraphsToRefresh.Append(Other.GraphsToRefresh); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphErrorReporter::ClearMessages(const UVoxelGraphGenerator* Graph, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) +{ +#if WITH_EDITOR + if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) + { + VoxelGraphEditor->ClearMessages(Graph, bClearAll, MessagesToClear); + } +#endif +} + +void FVoxelGraphErrorReporter::ClearNodesMessages(const UVoxelGraphGenerator* Graph, bool bRecursive, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) +{ +#if WITH_EDITOR + if (!Graph->VoxelGraph) + { + return; + } + + static TSet Stack; + if (Stack.Contains(Graph)) + { + return; + } + Stack.Add(Graph); + + TSet Macros; + for (auto* Node : Graph->VoxelGraph->Nodes) + { + if (auto* Interface = Cast(Node)) + { + for (auto Type : TEnumRange()) + + { + if (bClearAll || MessagesToClear == Type) + { + GetErrorString(Interface, Type).Empty(); + } + } + if (bRecursive) + { + if (auto* MacroNode = Cast(Interface->GetVoxelNode())) + { + auto* Macro = MacroNode->Macro; + if (!Macros.Contains(Macro) && Macro) + { + Macros.Add(Macro); + ClearNodesMessages(Macro, bRecursive, bClearAll, MessagesToClear); + } + } + } + } + } + if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) + { + VoxelGraphEditor->RefreshNodesMessages(Graph->VoxelGraph); + } + + Stack.Remove(Graph); +#endif +} + +void FVoxelGraphErrorReporter::ClearCompilationMessages(const UVoxelGraphGenerator* Graph) +{ + for (auto Type : TEnumRange()) + { + ClearMessages(Graph, false, Type); + ClearNodesMessages(Graph, true, false, Type); + } +} + +void FVoxelGraphErrorReporter::AddMessageToNodeInternal( + const UVoxelNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity) +{ +#if WITH_EDITOR + if (auto* GraphNode = Node->GraphNode) + { + FString& Text = GetErrorString(GraphNode, Severity); + if (!Text.IsEmpty()) + { + Text += "\n"; + } + Text += Message; + } +#endif +} + +FString FVoxelGraphErrorReporter::AddPrefixToError(const FString& Error) const +{ + if (!VoxelGraphGenerator || VoxelGraphGenerator->bDetailedErrors) + { + return ErrorPrefix + Error; + } + else + { + return Error; + } +} + +bool FEnsureVoxelGraphHelper::Check(FVoxelGraphErrorReporter& ErrorReporter, const FString& Expr, const FString& File, int32 Line, const FVoxelCompilationNode* Node) +{ + const FString Message = "Internal error: " + Expr + " (" + File + ":" + FString::FromInt(Line) + ")"; + ErrorReporter.AddError(Message); + if (Node) + { + ErrorReporter.AddMessageToNode(Node, Message, EVoxelGraphNodeMessageType::Error); + } + return true; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp new file mode 100644 index 00000000..d7fee862 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp @@ -0,0 +1,445 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphGenerator.h" +#include "IVoxelGraphEditor.h" +#include "VoxelGraphGlobals.h" +#include "VoxelGraphOutputs.h" +#include "VoxelGraphOutputsConfig.h" +#include "VoxelGraphConstants.h" +#include "VoxelGraphErrorReporter.h" + +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelGraphGeneratorInstance.h" +#include "CppTranslation/VoxelCppConstructorManager.h" +#include "Compilation/VoxelGraphCompilerManager.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelNodes/VoxelSeedNodes.h" + +#include "VoxelMessages.h" +#include "VoxelNode.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" + +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphSchema.h" +#include "Engine/Texture2D.h" +#include "Misc/ScopeExit.h" +#include "Misc/MessageDialog.h" + +#define VOXEL_GRAPH_THUMBNAIL_RES 128 + +#if WITH_EDITOR +void IVoxelGraphEditor::SetVoxelGraphEditor(TSharedPtr InVoxelGraphEditor) +{ + ensure(!VoxelGraphEditor.IsValid()); + VoxelGraphEditor = InVoxelGraphEditor; +} + +TSharedPtr IVoxelGraphEditor::VoxelGraphEditor = nullptr; +#endif + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +TMap UVoxelGraphGenerator::GetOutputs() const +{ + TMap Result; + for (int32 Index = 0; Index < FVoxelGraphOutput::DefaultOutputs.Num(); Index++) + { + Result.Add(Index, FVoxelGraphOutput::DefaultOutputs[Index]); + } + if (Outputs) + { + const auto& AdditionalOutputs = Outputs->Outputs; + for (int32 Index = 0; Index < AdditionalOutputs.Num(); Index++) + { + Result.Add(FVoxelGraphOutputsIndices::DefaultOutputsMax + Index, AdditionalOutputs[Index]); + } + } + for (auto& It : Result) + { + It.Value.Index = It.Key; + } + return Result; +} + +TArray UVoxelGraphGenerator::GetPermutations() const +{ + TArray Result; + Result.Append(FVoxelGraphOutput::DefaultOutputsPermutations); + if (Outputs) + { + const auto& GraphOutputs = Outputs->Outputs; + for (int32 Index = 0; Index < GraphOutputs.Num(); Index++) + { + FVoxelGraphPermutationArray NewElement; + NewElement.Add(FVoxelGraphOutputsIndices::DefaultOutputsMax + Index); + Result.Add(NewElement); + + if (GraphOutputs[Index].Category == EVoxelDataPinCategory::Float) + { + FVoxelGraphPermutationArray NewRangeElement; + NewRangeElement.Add(FVoxelGraphOutputsIndices::DefaultOutputsMax + Index); + NewRangeElement.Add(FVoxelGraphOutputsIndices::RangeAnalysisIndex); + Result.Add(NewRangeElement); + } + } + } + return Result; +} + +///////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITORONLY_DATA +UTexture2D* UVoxelGraphGenerator::GetPreviewTexture() +{ + if (!PreviewTexture) + { + PreviewTexture = UTexture2D::CreateTransient(VOXEL_GRAPH_THUMBNAIL_RES, VOXEL_GRAPH_THUMBNAIL_RES); + PreviewTexture->CompressionSettings = TC_HDR; + PreviewTexture->SRGB = false; + + PreviewTextureSave.SetNumZeroed(VOXEL_GRAPH_THUMBNAIL_RES * VOXEL_GRAPH_THUMBNAIL_RES); + + FTexture2DMipMap& Mip = PreviewTexture->PlatformData->Mips[0]; + + void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(Data, PreviewTextureSave.GetData(), PreviewTextureSave.Num() * sizeof(FColor)); + + Mip.BulkData.Unlock(); + PreviewTexture->UpdateResource(); + } + + return PreviewTexture; +} + +void UVoxelGraphGenerator::SetPreviewTexture(const TArray& Colors, int32 Size) +{ + // Do not save thumbnails in the free version, as they can't be right since we can't run graphs + check(Colors.Num() == Size * Size); + + Modify(); + + PreviewTextureSave.SetNum(VOXEL_GRAPH_THUMBNAIL_RES * VOXEL_GRAPH_THUMBNAIL_RES); + + for (int32 X = 0; X < VOXEL_GRAPH_THUMBNAIL_RES; X++) + { + for (int32 Y = 0; Y < VOXEL_GRAPH_THUMBNAIL_RES; Y++) + { + FColor Color = Colors[X * Size / VOXEL_GRAPH_THUMBNAIL_RES + (Y * Size / VOXEL_GRAPH_THUMBNAIL_RES) * Size]; + Color.A = 255; + PreviewTextureSave[X + Y * VOXEL_GRAPH_THUMBNAIL_RES] = Color; + } + } + + PreviewTexture = nullptr; +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +bool UVoxelGraphGenerator::CompileToCpp(FString& OutHeader, FString& OutCpp, const FString& Filename) +{ + FVoxelCppConstructorManager Constructor(Filename, this); + return Constructor.Compile(OutHeader, OutCpp); +} + +bool UVoxelGraphGenerator::CreateGraphs( + FVoxelCompiledGraphs& OutGraphs, + bool bPreview, + bool bInAutomaticPreview, + bool bOnlyShowAxisDependencies) +{ + VOXEL_FUNCTION_COUNTER(); + + if (bEnableDebugGraph && bPreview) + { + auto TmpOutputs = GetOutputs(); + TArray Targets; + for (auto& Permutation : GetPermutations()) + { + Targets.Add(FVoxelGraphOutputsUtils::GetPermutationName(Permutation, TmpOutputs)); + } + if (!Targets.Contains(TargetToDebug)) + { + FString Error = "Invalid TargetToDebug! Valid targets:"; + for (auto& Target : Targets) + { + Error += "\n\t" + Target; + } + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Error)); + return false; + } + } + + bool bResult; + + auto Before = FPlatformTime::Seconds(); + { + FVoxelGraphCompilerManager Compiler(this, true, bPreview, PreviewSettings, bInAutomaticPreview, bOnlyShowAxisDependencies); + bResult = Compiler.Compile(OutGraphs); + } + if (!bResult) + { + FVoxelGraphCompilerManager Compiler(this, false, bPreview, PreviewSettings, bInAutomaticPreview, bOnlyShowAxisDependencies); + bResult = Compiler.Compile(OutGraphs); + if (bResult) + { + FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT( + "Voxel", + "InternalErrorCompilation", + "Internal error: graph failed to compile with optimizations, but succeeded without. " + "Please report this to the developer.")); + } + } + auto After = FPlatformTime::Seconds(); + + float Duration = (After - Before) * 1000.f; + LOG_VOXEL(VeryVerbose, TEXT("Graph %s took %fms to compile."), *GetName(), Duration); + + if (bResult) + { + for (auto& It : OutGraphs.GetGraphsMap()) + { + int32 NumVariables = It.Value->VariablesBufferSize; + LOG_VOXEL( + VeryVerbose, + TEXT("\tTarget %s: %d variables (%.2f kB)"), + *It.Value->Name, + NumVariables, + NumVariables * sizeof(FVoxelNodeRangeType) / 1.e3); + } + } + + return bResult; +} + +bool UVoxelGraphGenerator::GetGraphInstance( + TVoxelSharedPtr& OutGenerator, + bool bPreview, + bool bInAutomaticPreview) +{ + auto Graphs = MakeVoxelShared(); + if (!CreateGraphs(*Graphs, bPreview, bInAutomaticPreview, false)) + { + return false; + } + else + { + const auto FinalPermutations = GetPermutations(); + const auto FinalOutputs = GetOutputs(); + OutGenerator = MakeVoxelShared( + Graphs, + *this, + FVoxelGraphOutputsUtils::GetSingleOutputsNamesMap(FinalPermutations, FinalOutputs, EVoxelDataPinCategory::Float), + FVoxelGraphOutputsUtils::GetSingleOutputsNamesMap(FinalPermutations, FinalOutputs, EVoxelDataPinCategory::Int), + FVoxelGraphOutputsUtils::GetSingleOutputsNamesMap(FinalPermutations, FinalOutputs, EVoxelDataPinCategory::Color)); + return true; + } +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +void UVoxelGraphGenerator::ApplyParameters(const TMap& Parameters) +{ + for (auto* Node : AllNodes) + { + Node->ApplyParameters(Parameters); + } +} + +void UVoxelGraphGenerator::GetParameters(TArray& OutParameters) const +{ + for (auto* Node : AllNodes) + { + Node->GetParameters(OutParameters); + } + + TMap NamesToParameters; + for (auto& Parameter : OutParameters) + { + auto* ExistingParameter = NamesToParameters.Find(Parameter.Id); + if (!ExistingParameter) + { + NamesToParameters.Add(Parameter.Id, Parameter); + continue; + } + + if (ExistingParameter->Name != Parameter.Name) + { + FVoxelMessages::Error(FString::Printf( + TEXT("Parameters with same Unique Name but different Display Name: %s vs %s for %s"), + *Parameter.Name, + *ExistingParameter->Name, + *Parameter.Id.ToString())); + } + if (ExistingParameter->Type != Parameter.Type) + { + FVoxelMessages::Error(FString::Printf( + TEXT("Parameters with same Unique Name but different type: %s vs %s for %s"), + *Parameter.Type.ToString(), + *ExistingParameter->Type.ToString(), + *Parameter.Id.ToString())); + } + } +} + +TVoxelSharedRef UVoxelGraphGenerator::GetTransformableInstance() +{ + return GetTransformableInstance({}); +} + +TVoxelSharedRef UVoxelGraphGenerator::GetTransformableInstance(const TMap& Parameters) +{ + TransientParameters = Parameters; + ON_SCOPE_EXIT + { + TransientParameters.Reset(); + }; + + TVoxelSharedPtr GraphGeneratorInstance; + if (GetGraphInstance(GraphGeneratorInstance, false, false)) + { + return GraphGeneratorInstance.ToSharedRef(); + } + else + { + FVoxelMessages::Error("Failed to compile voxel graph", this); + return MakeVoxelShared(); + } +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelGraphGenerator::PostInitProperties() +{ + Super::PostInitProperties(); + if (!HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad)) + { + CreateGraphs(); + } +} + +void UVoxelGraphGenerator::PostLoad() +{ + Super::PostLoad(); + + CreateGraphs(); + BindUpdateSetterNodes(); +} + +void UVoxelGraphGenerator::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + if (PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelGraphGenerator, SaveLocation)) + { + if (!SaveLocation.FilePath.IsEmpty()) + { + SaveLocation.FilePath = FPaths::ConvertRelativePathToFull(SaveLocation.FilePath); + FPaths::MakePathRelativeTo(SaveLocation.FilePath, *FPaths::ProjectDir()); + } + } + if (PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelGraphGenerator, Outputs)) + { + BindUpdateSetterNodes(); + UpdateSetterNodes(); + } + if (PropertyChangedEvent.MemberProperty->HasMetaData("Refresh")) + { + FVoxelCompiledGraphs Graphs; + CreateGraphs(Graphs, true, true, false); + } + } +} + +///////////////////////////////////////////////////////////////////////////////// + +UVoxelNode* UVoxelGraphGenerator::ConstructNewNode(UClass* NewNodeClass, const FVector2D& Position, bool bSelectNewNode) +{ + Modify(); + VoxelGraph->Modify(); + + UVoxelNode* VoxelNode = NewObject(this, NewNodeClass, NAME_None, RF_Transactional); + AllNodes.Add(VoxelNode); // To have valid list even without compiling + MarkPackageDirty(); + +#if WITH_EDITOR + VoxelNode->Graph = this; + + // Create the graph node + check(!VoxelNode->GraphNode); + IVoxelGraphEditor::GetVoxelGraphEditor()->CreateVoxelGraphNode(VoxelGraph, VoxelNode, bSelectNewNode); + VoxelNode->GraphNode->NodePosX = Position.X; + VoxelNode->GraphNode->NodePosY = Position.Y; +#endif // WITH_EDITOR + + return VoxelNode; +} + +void UVoxelGraphGenerator::CreateGraphs() +{ + if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) + { + if (!VoxelGraph) + { + VoxelGraph = VoxelGraphEditor->CreateNewVoxelGraph(this); + VoxelGraph->bAllowDeletion = false; + + // Give the schema a chance to fill out any required nodes (like the results node) + const UEdGraphSchema* Schema = VoxelGraph->GetSchema(); + Schema->CreateDefaultNodesForGraph(*VoxelGraph); + } + if (!VoxelDebugGraph) + { + VoxelDebugGraph = VoxelGraphEditor->CreateNewVoxelGraph(this); + VoxelDebugGraph->bAllowDeletion = false; + } + } +} + +void UVoxelGraphGenerator::CompileVoxelNodesFromGraphNodes() +{ + if (!ensure(this)) + { + return; + } + if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) + { + VoxelGraphEditor->CompileVoxelNodesFromGraphNodes(this); + } +} + +void UVoxelGraphGenerator::UpdateSetterNodes() +{ + for (auto& Node : AllNodes) + { + if (IsValid(Node)) + { + if (auto* SetNode = Cast(Node)) + { + SetNode->UpdateSetterNode(); + } + } + } +} + +void UVoxelGraphGenerator::BindUpdateSetterNodes() +{ + if (Outputs && !Outputs->OnPropertyChanged.IsBoundToObject(this)) + { + Outputs->OnPropertyChanged.AddUObject(this, &UVoxelGraphGenerator::UpdateSetterNodes); + } +} + +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGeneratorInstance.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGeneratorInstance.cpp new file mode 100644 index 00000000..f6498778 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphGeneratorInstance.cpp @@ -0,0 +1,200 @@ +// Copyright 2020 Phyronnaz + +#include "Runtime/VoxelGraphGeneratorInstance.h" +#include "Runtime/Recorders/VoxelGraphEmptyRecorder.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelGraph.inl" + +#include "VoxelGraphGenerator.h" +#include "VoxelGraphConstants.h" +#include "VoxelContext.h" +#include "VoxelMessages.h" + +#include "Async/Async.h" + +void FVoxelCompiledGraphs::Compact() +{ + ensure(FastAccess.Num() == 0); + FastAccess.Reset(); + + Graphs.Compact(); + for (auto& It : Graphs) + { + const uint32 Key = FVoxelGraphPermutation::Hash(It.Key); + ensure(!FastAccess.Contains(Key)); + FastAccess.Add(Key, It.Value); + } + FastAccess.Compact(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphGeneratorInstance::FTarget::Compute(EVoxelFunctionAxisDependencies Dependencies, const FVoxelContext& Context) const +{ + FVoxelGraphEmptyRecorder EmptyRecorder; + Graph.Compute(Context, Buffers, Dependencies, EmptyRecorder); +} + +void FVoxelGraphGeneratorInstance::FRangeTarget::ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutput& Outputs) const +{ + FVoxelGraphEmptyRangeRecorder EmptyRecorder; + Graph.ComputeRange(Context, Buffers, EmptyRecorder); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +inline ReturnType StaticSwitch(uint32 Index) +{ + switch (Index) + { +#if !INTELLISENSE_PARSER + static_assert(MAX_VOXELGRAPH_OUTPUTS == 256, "Need to update switch"); +#define C0(I) case (I): return T::template Get(); +#define C1(I) C0(2 * (I)) C0(2 * (I) + 1) +#define C2(I) C1(2 * (I)) C1(2 * (I) + 1) +#define C3(I) C2(2 * (I)) C2(2 * (I) + 1) +#define C4(I) C3(2 * (I)) C3(2 * (I) + 1) +#define C5(I) C4(2 * (I)) C4(2 * (I) + 1) +#define C6(I) C5(2 * (I)) C5(2 * (I) + 1) +#define C7(I) C6(2 * (I)) C6(2 * (I) + 1) +#define C8(I) C7(2 * (I)) C7(2 * (I) + 1) +#define C9(I) C8(2 * (I)) C8(2 * (I) + 1) +#define C10(I) C9(2 * (I)) C9(2 * (I) + 1) + C8(0); +#undef C0 +#undef C1 +#undef C2 +#undef C3 +#undef C4 +#undef C5 +#undef C6 +#undef C7 +#undef C8 +#undef C9 +#undef C10 +#endif + default: check(false); return nullptr; + } +} + +template +inline TMap GetCustomOutputsPtrMap(const TMap& Map) +{ + TMap Result; + for (auto& It : Map) + { + Result.Emplace(It.Key, StaticSwitch(It.Value)); + } + return Result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +FVoxelGraphGeneratorInstance::FVoxelGraphGeneratorInstance( + const TVoxelSharedRef& Graphs, + UVoxelGraphGenerator& Generator, + const TMap& FloatOutputs, + const TMap& Int32Outputs, + const TMap& ColorOutputs) + : TVoxelGraphGeneratorInstanceHelper( + FloatOutputs, + Int32Outputs, + ColorOutputs, + { + GetCustomOutputsPtrMap, TOutputFunctionPtr>(FloatOutputs), + GetCustomOutputsPtrMap, TOutputFunctionPtr>(Int32Outputs), + GetCustomOutputsPtrMap, TOutputFunctionPtr>(ColorOutputs), + GetCustomOutputsPtrMap, TRangeOutputFunctionPtr>(FloatOutputs), + }, + { + GetCustomOutputsPtrMap, TOutputFunctionPtr_Transform>(FloatOutputs), + GetCustomOutputsPtrMap, TOutputFunctionPtr_Transform>(Int32Outputs), + GetCustomOutputsPtrMap, TOutputFunctionPtr_Transform>(ColorOutputs), + GetCustomOutputsPtrMap, TRangeOutputFunctionPtr_Transform>(FloatOutputs), + }, + Generator) + , Generator(&Generator) + , Graphs(Graphs) +{ + +} + +FVoxelGraphGeneratorInstance::~FVoxelGraphGeneratorInstance() +{ +} + +void FVoxelGraphGeneratorInstance::InitGraph(const FVoxelGeneratorInit& InitStruct) +{ + TArray SeedVariables; + for (auto& It : Graphs->GetGraphsMap()) + { + auto& Graph = It.Value; + SeedVariables.SetNumUninitialized(Graph->VariablesBufferSize, false); + FVoxelGraphVMInitBuffers Buffers(SeedVariables.GetData()); + Graph->Init(InitStruct, Buffers); + } + + // Compute constants + for (auto& It : Graphs->GetGraphsMap()) + { + auto& Graph = It.Value; + if (It.Key.Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex)) + { + auto& CurrentVariables = RangeVariables.FindOrAdd(Graph); + CurrentVariables.SetNumUninitialized(Graph->VariablesBufferSize); + FVoxelGraphVMComputeRangeBuffers Buffers(CurrentVariables.GetData()); + Graph->ComputeRangeConstants(Buffers); + } + else + { + auto& CurrentVariables = Variables.FindOrAdd(Graph); + CurrentVariables.SetNumUninitialized(Graph->VariablesBufferSize); + FVoxelGraphVMComputeBuffers Buffers(CurrentVariables.GetData()); + Graph->ComputeConstants(Buffers); + } + } +} + +FVoxelNodeType* FVoxelGraphGeneratorInstance::GetVariablesBuffer(const TVoxelWeakPtr& Graph) const +{ + auto& BuffersMap = FThreadVariables::Get(); + + auto* BufferPtr = BuffersMap.Find(Graph); + if (BufferPtr) + { + // Fast path + return BufferPtr->GetData(); + } + + // Cleanup + BuffersMap.Remove(nullptr); + // Add new one + auto& Buffer = BuffersMap.Add(Graph); + // Copy data + Buffer = Variables.FindChecked(Graph); + return Buffer.GetData(); +} + +FVoxelNodeRangeType* FVoxelGraphGeneratorInstance::GetRangeVariablesBuffer(const TVoxelWeakPtr& Graph) const +{ + auto& BuffersMap = FThreadRangeVariables::Get(); + + auto* BufferPtr = BuffersMap.Find(Graph); + if (BufferPtr) + { + // Fast path + return BufferPtr->GetData(); + } + + // Cleanup + BuffersMap.Remove(nullptr); + // Add new one + auto& Buffer = BuffersMap.Add(Graph); + // Copy data + Buffer = RangeVariables.FindChecked(Graph); + return Buffer.GetData(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphModule.cpp new file mode 100644 index 00000000..37f0b90a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphModule.cpp @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphModule.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FVoxelGraphModule, VoxelGraph) diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp new file mode 100644 index 00000000..5a65bd61 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp @@ -0,0 +1,82 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphOutputs.h" +#include "VoxelGraphConstants.h" +#include "CppTranslation/VoxelCppConstructor.h" + +FString FVoxelGraphOutput::GetDeclaration(FVoxelCppConstructor& Constructor) const +{ + return Constructor.GetTypeString(Category) + " " + Name.ToString(); +} + +FString FVoxelGraphOutput::GetRefDeclaration(FVoxelCppConstructor& Constructor) const +{ + return Constructor.GetTypeString(Category) + "& " + Name.ToString(); +} + +const TArray FVoxelGraphOutput::DefaultOutputs( + { + {"RangeAnalysis", EVoxelDataPinCategory::Float , FGuid(0x956428b9, 0xff921b6c, 0xabb6b187, 0x7d47075b), FVoxelGraphOutputsIndices::RangeAnalysisIndex}, + {"Value" , EVoxelDataPinCategory::Float , FGuid(0x3fd2558e, 0x99b0d175, 0x14562d0a, 0xc140e1a6), FVoxelGraphOutputsIndices::ValueIndex}, + {"Material" , EVoxelDataPinCategory::Material, FGuid(0xe639695e, 0xecc58da5, 0x0f7be8f7, 0x29d173f3), FVoxelGraphOutputsIndices::MaterialIndex}, + {"UpVectorX" , EVoxelDataPinCategory::Float , FGuid(0xc9b67a6b, 0x592eb713, 0x18cc0ed0, 0x128e47cb), FVoxelGraphOutputsIndices::UpVectorXIndex}, + {"UpVectorY" , EVoxelDataPinCategory::Float , FGuid(0x0bde596a, 0xc3e9fc9c, 0x72b27fc0, 0x1bc6112d), FVoxelGraphOutputsIndices::UpVectorYIndex}, + {"UpVectorZ" , EVoxelDataPinCategory::Float , FGuid(0x0f9e0ef6, 0x2076c764, 0xd7fb80b4, 0xd135530e), FVoxelGraphOutputsIndices::UpVectorZIndex}, + } +); + +const TArray FVoxelGraphOutput::DefaultOutputsPermutations( + { + { + FVoxelGraphOutputsIndices::ValueIndex + }, + { + FVoxelGraphOutputsIndices::MaterialIndex + }, + { + FVoxelGraphOutputsIndices::UpVectorXIndex, + FVoxelGraphOutputsIndices::UpVectorYIndex, + FVoxelGraphOutputsIndices::UpVectorZIndex + }, + { + FVoxelGraphOutputsIndices::ValueIndex, + FVoxelGraphOutputsIndices::RangeAnalysisIndex + } + } +); + +FString FVoxelGraphOutputsUtils::GetPermutationName(const FVoxelGraphPermutationArray& Permutation, const TMap& Outputs) +{ + FString Name = ""; + for (uint32 I : Permutation) + { + Name += Outputs[I].Name.ToString(); + } + return Name; +} + +TMap FVoxelGraphOutputsUtils::GetSingleOutputsNamesMap( + const TArray& Permutations, + const TMap& Outputs, + EVoxelDataPinCategory CategoryFilter) +{ + TMap Result; + for (auto& Permutation : Permutations) + { + if (Permutation.Num() == 1) + { + uint32 Index = Permutation[0]; + auto& Output = Outputs[Index]; + if (Output.Category == CategoryFilter) + { + Result.Add(Output.Name, Index); + } + } + } + return Result; +} + +bool FVoxelGraphOutputsUtils::IsVoxelGraphOutputHidden(int32 Index) +{ + return Index == FVoxelGraphOutputsIndices::RangeAnalysisIndex; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputsConfig.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputsConfig.cpp new file mode 100644 index 00000000..882c6509 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphOutputsConfig.cpp @@ -0,0 +1,101 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphOutputsConfig.h" +#include "CppTranslation/VoxelVariables.h" + +TArray UVoxelGraphOutputsConfig::GetFloatOutputs() const +{ + TArray Result; + for (auto& Output : Outputs) + { + if (Output.Category == EVoxelDataPinCategory::Float) + { + Result.Add(Output.Name); + } + } + return Result; +} + +#if WITH_EDITOR +void UVoxelGraphOutputsConfig::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + // Iterate reverse so that last properties are changed + for (int32 OutputIndex = Outputs.Num() - 1; OutputIndex >= 0 ; OutputIndex--) + { + auto& Output = Outputs[OutputIndex]; + + if (!Output.GUID.IsValid()) + { + Output.GUID = FGuid::NewGuid(); + } + + if (Output.Name.IsNone()) + { + Output.Name = "CustomOutput"; + } + + Output.Name = *FVoxelVariable::SanitizeName(Output.Name.ToString()); + + // Make sure the name is unique + { + int32 NameIndex = 1; + bool bResultNameIndexValid; + FName PotentialName; + + do + { + PotentialName = Output.Name; + if (NameIndex != 1) + { + PotentialName.SetNumber(NameIndex); + } + + bResultNameIndexValid = true; + for (auto& OtherOutput : Outputs) + { + if (&OtherOutput != &Output && OtherOutput.Name == PotentialName) + { + bResultNameIndexValid = false; + break; + } + } + if (bResultNameIndexValid) + { + for (auto& OtherOutput : FVoxelGraphOutput::DefaultOutputs) + { + if (OtherOutput.Name == PotentialName) + { + bResultNameIndexValid = false; + break; + } + } + } + + NameIndex++; + } while (!bResultNameIndexValid); + + Output.Name = PotentialName; + } + } + + OnPropertyChanged.Broadcast(); + } +} +#endif + +void UVoxelGraphOutputsConfig::PostLoad() +{ + Super::PostLoad(); + + for (auto& Output : Outputs) + { + if (!Output.GUID.IsValid()) + { + Output.GUID = FGuid::NewGuid(); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphPreviewSettings.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphPreviewSettings.cpp new file mode 100644 index 00000000..e219265a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelGraphPreviewSettings.cpp @@ -0,0 +1,116 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphPreviewSettings.h" +#include "IVoxelGraphEditor.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +#include "Engine/StaticMesh.h" +#include "Materials/MaterialInterface.h" +#include "UObject/ConstructorHelpers.h" + +UVoxelGraphPreviewSettings::UVoxelGraphPreviewSettings() +{ + static ConstructorHelpers::FObjectFinderOptional MeshObject(TEXT("/Voxel/Preview/SM_Plane")); + static ConstructorHelpers::FObjectFinderOptional HeightmapMaterialObject(TEXT("/Voxel/Preview/M_PreviewMaterial")); + static ConstructorHelpers::FObjectFinderOptional SliceMaterialObject(TEXT("/Voxel/Preview/M_2DPreviewMaterial")); + + Mesh = MeshObject.Get(); + HeightmapMaterial = HeightmapMaterialObject.Get(); + SliceMaterial = SliceMaterialObject.Get(); + + IndexColors.Add(FColorList::Red); + IndexColors.Add(FColorList::Green); + IndexColors.Add(FColorList::Blue); + IndexColors.Add(FColorList::Yellow); + IndexColors.Add(FColorList::Magenta); + IndexColors.Add(FColorList::Cyan); + IndexColors.Add(FColorList::Orange); + IndexColors.Add(FColorList::Aquamarine); + IndexColors.Add(FColorList::BakerChocolate); + IndexColors.Add(FColorList::Brass); + IndexColors.Add(FColorList::Gold); +} + +#if WITH_EDITOR +void UVoxelGraphPreviewSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (LeftToRight == BottomToTop) + { + // Else crash + if (PropertyChangedEvent.MemberProperty && + PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelGraphPreviewSettings, BottomToTop)) + { + LeftToRight = EVoxelGraphPreviewAxes((int32(BottomToTop) + 1) % 3); + } + else + { + BottomToTop = EVoxelGraphPreviewAxes((int32(LeftToRight) + 1) % 3); + } + } + + NumRangeAnalysisChunksPerAxis = FMath::Clamp(NumRangeAnalysisChunksPerAxis, 1, Resolution); + + const FVoxelGraphPreviewSettingsWrapper Wrapper(*this); + ResolutionMultiplierLog = Wrapper.LOD; + PreviewedBounds = Wrapper.Bounds; + + // Don't let the previewed voxel go out of the bounds + PreviewedVoxel = Wrapper.Bounds.Clamp(PreviewedVoxel); + + if (Graph && PropertyChangedEvent.MemberProperty) + { + const bool bAutomatic = PropertyChangedEvent.MemberProperty->HasMetaData(STATIC_FNAME("Automatic")); + const bool bUpdateItems = PropertyChangedEvent.MemberProperty->HasMetaData(STATIC_FNAME("UpdateItems")); + const bool bMeshOnly = PropertyChangedEvent.MemberProperty->HasMetaData(STATIC_FNAME("MeshOnly")); + + if (!bAutomatic || PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + EVoxelGraphPreviewFlags Flags = EVoxelGraphPreviewFlags::None; + if (!bAutomatic) + { + Flags |= EVoxelGraphPreviewFlags::ManualPreview; + } + Flags |= EVoxelGraphPreviewFlags::UpdateMeshSettings; + if (!bMeshOnly) + { + Flags |= EVoxelGraphPreviewFlags::UpdateTextures; + } + if (bUpdateItems) + { + Flags |= EVoxelGraphPreviewFlags::UpdatePlaceableItems; + } + + IVoxelGraphEditor::GetVoxelGraphEditor()->UpdatePreview(Graph, Flags); + } + } +} +#endif + +FVoxelGraphPreviewSettingsWrapper::FVoxelGraphPreviewSettingsWrapper(const UVoxelGraphPreviewSettings& Settings) +{ + LOD = FMath::Clamp(Settings.ResolutionMultiplierLog, 0, 20); + Step = 1 << LOD; + Resolution = Settings.Resolution; + + LeftToRight = Settings.LeftToRight; + BottomToTop = Settings.BottomToTop; + + Center = FVoxelUtilities::DivideRound(Settings.Center, Step) * Step; + + { + Start = Center; + const int32 Offset = Resolution / 2 * Step; + GetAxis(Start, LeftToRight) -= Offset; + GetAxis(Start, BottomToTop) -= Offset; + } + + { + Size = FIntVector(1, 1, 1); + GetAxis(Size, LeftToRight) = Resolution; + GetAxis(Size, BottomToTop) = Resolution; + } + + Bounds = FVoxelIntBox(Start, Start + Size * Step); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNode.cpp new file mode 100644 index 00000000..77a3b615 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNode.cpp @@ -0,0 +1,204 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNode.h" +#include "IVoxelGraphEditor.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "Compilation/VoxelCompilationNode.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "EdGraph/EdGraphNode.h" + +#if WITH_EDITOR +void UVoxelGraphNodeInterface::PostLoad() +{ + Super::PostLoad(); + ErrorMsg.Empty(); +} + +void UVoxelGraphNodeInterface::ReconstructNode() +{ + Super::ReconstructNode(); + + InfoMsg.Empty(); + WarningMsg.Empty(); + ErrorMsg.Empty(); +} +#endif + +int32 UVoxelNode::GetInputPinIndex(const FGuid& PinId) +{ + for (int32 I = 0; I < InputPins.Num(); I++) + { + if (InputPins[I].PinId == PinId) + { + return I; + } + } + return -1; +} + +int32 UVoxelNode::GetOutputPinIndex(const FGuid& PinId) +{ + for (int32 I = 0; I < OutputPins.Num(); I++) + { + if (OutputPins[I].PinId == PinId) + { + return I; + } + } + return -1; +} + +bool UVoxelNode::HasInputPinWithCategory(EVoxelPinCategory Category) const +{ + for (int32 i = 0; i < GetMinInputPins(); i++) + { + if (GetInputPinCategory(i) == Category) + { + return true; + } + } + return false; +} + +bool UVoxelNode::HasOutputPinWithCategory(EVoxelPinCategory Category) const +{ + for (int32 i = 0; i < GetOutputPinsCount(); i++) + { + if (GetOutputPinCategory(i) == Category) + { + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FText UVoxelNode::GetTitle() const +{ +#if WITH_EDITOR + return GetClass()->GetDisplayNameText(); +#else + return FText::GetEmpty(); +#endif +} + +FText UVoxelNode::GetTooltip() const +{ +#if WITH_EDITOR + return GetClass()->GetToolTipText(); +#else + return FText::GetEmpty(); +#endif +} + +TVoxelSharedPtr UVoxelNode::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + check(false); + return TVoxelSharedPtr(); +} + + +TSharedPtr UVoxelNode::GetCompilationNode() const +{ + return MakeShared(*this); +} + +void UVoxelNode::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + if (IsOutdated()) + { + ErrorReporter.AddMessageToNode(this, "outdated node, please right click and press Reconstruct Node", EVoxelGraphNodeMessageType::Error); + } +#if WITH_EDITOR + for (TFieldIterator It(GetClass()); It; ++It) + { + auto* Property = *It; + if (Property->HasMetaData(STATIC_FNAME("NonNull"))) + { + auto* ObjectProperty = UE_25_SWITCH(Cast, CastField)(Property); + if (ensure(ObjectProperty)) + { + if (!*ObjectProperty->ContainerPtrToValuePtr(this)) + { + ErrorReporter.AddMessageToNode(this, FString::Printf(TEXT("%s is null"), *Property->GetDisplayNameText().ToString()), EVoxelGraphNodeMessageType::Error); + } + } + } + } +#endif +} + +#if WITH_EDITOR +void UVoxelNode::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) +{ + Super::PostEditChangeChainProperty(PropertyChangedEvent); + + if (Graph && GraphNode && PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + bool bReconstructNode = false; + for (auto* Property : PropertyChangedEvent.PropertyChain) + { + if (Property && Property->HasMetaData(STATIC_FNAME("ReconstructNode"))) + { + bReconstructNode = true; + break; + } + } + UpdatePreview(bReconstructNode); + } + + MarkPackageDirty(); +} + +void UVoxelNode::PostLoad() +{ + Super::PostLoad(); + // Make sure voxel nodes are transactional (so they work with undo system) + SetFlags(RF_Transactional); + + InputPinCount = FMath::Clamp(InputPinCount, GetMinInputPins(), GetMaxInputPins()); + + if (IsOutdated()) + { + FVoxelGraphErrorReporter::AddMessageToNodeInternal(this, "outdated node, please right click and press Reconstruct Node", EVoxelGraphNodeMessageType::Error); + } +} +#endif + +void UVoxelNode::UpdatePreview(bool bReconstructNode) const +{ +#if WITH_EDITOR + if (bReconstructNode) + { + // Reconstruct before updating preview to have the right output count + GraphNode->ReconstructNode(); + Graph->CompileVoxelNodesFromGraphNodes(); + } + + IVoxelGraphEditor::GetVoxelGraphEditor()->UpdatePreview(Graph, EVoxelGraphPreviewFlags::UpdateTextures); +#endif +} + +bool UVoxelNode::IsOutdated() const +{ + if (InputPins.Num() < GetMinInputPins() || + InputPins.Num() > GetMaxInputPins() || + InputPins.Num() != InputPinCount || + OutputPins.Num() != GetOutputPinsCount()) + { + return true; + } + +#if WITH_EDITORONLY_DATA + if (GraphNode && GraphNode->IsOutdated()) + { + return true; + } +#endif + + return false; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp new file mode 100644 index 00000000..4ba44f34 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp @@ -0,0 +1,23 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBinaryNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FLess, <) +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FLessEqual, <=) +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FGreater, >) +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FGreaterEqual, >=) +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FEqual, ==) +GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(UVoxelNode_FNotEqual, !=) + +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_ILess, <) +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_ILessEqual, <=) +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_IGreater, >) +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_IGreaterEqual, >=) +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_IEqual, ==) +GENERATED_VOXELNODE_IMPL_BINARY_INT(UVoxelNode_INotEqual, !=) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp new file mode 100644 index 00000000..b6fa8676 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp @@ -0,0 +1,193 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBiomeMapNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelTextureSamplerNode.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGraphGenerator.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +#include "Engine/Texture2D.h" + +UVoxelNode_BiomeMapSampler::UVoxelNode_BiomeMapSampler() +{ + SetInputs( + { "U", EC::Float, "Coordinate between 0 and texture width" }, + { "V", EC::Float, "Coordinate between 0 and texture height" }); +} + +int32 UVoxelNode_BiomeMapSampler::GetOutputPinsCount() const +{ + return Biomes.Num(); +} + +FName UVoxelNode_BiomeMapSampler::GetOutputPinName(int32 PinIndex) const +{ + return Biomes.IsValidIndex(PinIndex) ? *Biomes[PinIndex].Name : TEXT("Error"); +} + +EVoxelPinCategory UVoxelNode_BiomeMapSampler::GetOutputPinCategory(int32 PinIndex) const +{ + return EC::Float; +} + +FText UVoxelNode_BiomeMapSampler::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Biome Map: {0}"), Super::GetTitle()); +} + +TVoxelSharedPtr UVoxelNode_BiomeMapSampler::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_BiomeMapSampler& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Threshold(Node.Threshold) + , Colors(Node.GetColors()) + , Texture(FVoxelTextureUtilities::CreateFromTexture_Color(Node.Texture)) + , ColorsVariable("TArray", UniqueName.ToString() + "_Colors") + , TextureVariable(MakeShared(Node, Node.Texture)) + { + } + + void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(ColorsVariable); + } + + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(ColorsVariable.CppName + " = "); + Constructor.Indent(); + Constructor.AddLine("{"); + Constructor.Indent(); + for (int32 Index = 0; Index < Colors.Num(); Index++) + { + auto& C = Colors[Index]; + FString ColorString = FString::Printf(TEXT("FColor(%d, %d, %d, %d)"), C.R, C.G, C.B, C.A); + if (Index == Colors.Num() - 1) + { + Constructor.AddLine(ColorString); + } + else + { + Constructor.AddLine(ColorString + ","); + } + } + Constructor.Unindent(); + Constructor.AddLine("};"); + Constructor.Unindent(); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + const v_flt X = Inputs[0].Get(); + const v_flt Y = Inputs[1].Get(); + + FVoxelNodeFunctions::FindColorsLerpedAlphas( + Threshold, + Colors, + Texture, + X, + Y, + [&](int32 Index, v_flt Alpha) { Outputs[Index].Get() = Alpha; }); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + for (int32 Index = 0; Index < Colors.Num(); Index++) + { + Outputs[Index].Get() = { 0, 1 }; + } + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + FString SwitchString; + SwitchString += "switch (Index) { "; + for (int32 Index = 0; Index < Outputs.Num(); Index++) + { + SwitchString += FString::Printf(TEXT("case %d: %s = Alpha; break; "), Index, *Outputs[Index]); + } + SwitchString += "}"; + + Constructor.AddLinef( + TEXT("FVoxelNodeFunctions::FindColorsLerpedAlphas(%d, %s, %s, %s, %s, [&](int32 Index, v_flt Alpha) { %s; });"), + Threshold, + *ColorsVariable.CppName, + *TextureVariable->CppName, + *Inputs[0], + *Inputs[1], + *SwitchString); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + for (auto& Output : Outputs) + { + Constructor.AddLinef(TEXT("%s = { 0, 1 };"), *Output); + } + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(TextureVariable); + } + + private: + const int Threshold; + const TArray Colors; + const TVoxelTexture Texture; + FVoxelVariable const ColorsVariable; + const TSharedRef TextureVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +TArray UVoxelNode_BiomeMapSampler::GetColors() const +{ + TArray Colors; + for (auto& Biome : Biomes) + { + Colors.Add(Biome.Color); + } + return Colors; +} + +void UVoxelNode_BiomeMapSampler::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + + FString Error; + if (!FVoxelTextureUtilities::CanCreateFromTexture(Texture, Error)) + { + ErrorReporter.AddMessageToNode(this, Error, EVoxelGraphNodeMessageType::Error); + } +} + +#if WITH_EDITOR +void UVoxelNode_BiomeMapSampler::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + if (Biomes.Num() >= 256) + { + Biomes.SetNum(256); + } + for (int32 Index = 0; Index < Biomes.Num(); Index++) + { + auto& Biome = Biomes[Index]; + if (Biome.Name.IsEmpty()) + { + Biome.Name = FString::Printf(TEXT("Biome %d"), Index); + } + } + } + + Super::PostEditChangeProperty(PropertyChangedEvent); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp new file mode 100644 index 00000000..04f6954e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp @@ -0,0 +1,112 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBiomeMergeNode.h" +#include "VoxelNodes/VoxelNodeHelperMacros.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphGenerator.h" + +int32 UVoxelNode_BiomeMerge::GetMinInputPins() const +{ + return 1 + Biomes.Num() * 2; +} + +int32 UVoxelNode_BiomeMerge::GetMaxInputPins() const +{ + return GetMinInputPins(); +} + +int32 UVoxelNode_BiomeMerge::GetOutputPinsCount() const +{ + return 2; +} + +FName UVoxelNode_BiomeMerge::GetInputPinName(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return ""; + } + PinIndex--; + if (PinIndex < 2 * Biomes.Num()) + { + if (PinIndex % 2 == 0) + { + return *(Biomes[PinIndex / 2] + " Value"); + } + else + { + return *(Biomes[PinIndex / 2] + " Alpha"); + } + } + return "Error"; +} + +FName UVoxelNode_BiomeMerge::GetOutputPinName(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return ""; + } + if (PinIndex == 1) + { + return "Result"; + } + return "Error"; +} + +EVoxelPinCategory UVoxelNode_BiomeMerge::GetInputPinCategory(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return EC::Exec; + } + PinIndex--; + if (PinIndex < 2 * Biomes.Num()) + { + return EC::Float; + } + return EC::Float; +} + +EVoxelPinCategory UVoxelNode_BiomeMerge::GetOutputPinCategory(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return EC::Exec; + } + if (PinIndex == 1) + { + return EC::Float; + } + return EC::Float; +} + +TSharedPtr UVoxelNode_BiomeMerge::GetCompilationNode() const +{ + auto* BiomeMerge = new FVoxelBiomeMergeCompilationNode(*this); + BiomeMerge->Tolerance = Tolerance; + return MakeShareable(BiomeMerge); +} + +#if WITH_EDITOR +void UVoxelNode_BiomeMerge::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + if (Biomes.Num() >= 256) + { + Biomes.SetNum(256); + } + for (int32 Index = 0; Index < Biomes.Num(); Index++) + { + auto& Biome = Biomes[Index]; + if (Biome.IsEmpty()) + { + Biome = FString::Printf(TEXT("Biome %d"), Index); + } + } + } + + Super::PostEditChangeProperty(PropertyChangedEvent); +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp new file mode 100644 index 00000000..5d6c99fd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp @@ -0,0 +1,170 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelConstantNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" + +UVoxelNode_LOD::UVoxelNode_LOD() +{ + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_LOD, + NO_INPUTS, + DEFINE_OUTPUTS(int32), + _O0 = _C0.LOD; +) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VoxelSize::UVoxelNode_VoxelSize() +{ + SetOutputs(EC::Float); +} +TVoxelSharedPtr UVoxelNode_VoxelSize::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_VoxelSize& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , VoxelSizeVariable("float", UniqueName.ToString() + "_VoxelSize") + { + } + + void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(VoxelSizeVariable); + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + VoxelSize = InitStruct.VoxelSize; + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(VoxelSizeVariable.CppName + " = " + FVoxelCppIds::InitStruct + ".VoxelSize;"); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = VoxelSize; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = VoxelSize; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *VoxelSizeVariable.CppName); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + + private: + float VoxelSize = 0; + FVoxelVariable const VoxelSizeVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_WorldSize::UVoxelNode_WorldSize() +{ + SetOutputs(EC::Int); +} + +TVoxelSharedPtr UVoxelNode_WorldSize::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_WorldSize& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , WorldSizeVariable("uint32", UniqueName.ToString() + "_WorldSize") + { + } + + void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(WorldSizeVariable); + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + WorldSize = InitStruct.WorldSize; + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(WorldSizeVariable.CppName + " = " + FVoxelCppIds::InitStruct + ".WorldSize;"); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = WorldSize; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = WorldSize; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *WorldSizeVariable.CppName); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + + private: + uint32 WorldSize = 0; + FVoxelVariable const WorldSizeVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CompileTimeConstant::UVoxelNode_CompileTimeConstant() +{ + SetOutputs(EC::Boolean); +} + +FText UVoxelNode_CompileTimeConstant::GetTitle() const +{ + return FText::FromName(Name); +} + +EVoxelPinCategory UVoxelNode_CompileTimeConstant::GetOutputPinCategory(int32 PinIndex) const +{ + return Type; +} + +TSharedPtr UVoxelNode_CompileTimeConstant::GetCompilationNode() const +{ + auto Result = MakeShared(*this); + Result->Name = Name; + return Result; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp new file mode 100644 index 00000000..0a39508b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp @@ -0,0 +1,202 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelCoordinatesNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "VoxelAxisDependencies.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +UVoxelCoordinateNode::UVoxelCoordinateNode() +{ + SetColor(FVoxelNodeColors::FloatNode); +} + +////////////////////////////////////////////////////////////////////////////////////// + +// Note: Both local and global coordinates are depending on their corresponding axis. +// If the transform has a rotation, the graph generator helper won't use the axis dependencies + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_XF::UVoxelNode_XF() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_XF::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::X; +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_XF, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetLocalX(); +) + +UVoxelNode_YF::UVoxelNode_YF() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_YF::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::Y; +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_YF, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetLocalY(); +) + +UVoxelNode_ZF::UVoxelNode_ZF() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_ZF::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::Z; +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_ZF, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetLocalZ(); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GlobalX::UVoxelNode_GlobalX() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_GlobalX::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::X; +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GlobalX, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetWorldX(); +) + +UVoxelNode_GlobalY::UVoxelNode_GlobalY() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_GlobalY::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::Y; +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GlobalY, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetWorldY(); +) + +UVoxelNode_GlobalZ::UVoxelNode_GlobalZ() +{ + SetOutputs(EC::Float); +} +uint8 UVoxelNode_GlobalZ::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::Z; +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GlobalZ, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = _C0.GetWorldZ(); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_LocalToGlobal::UVoxelNode_LocalToGlobal() +{ + SetInputs( + { "X", EC::Float, "X in local space" }, + { "Y", EC::Float, "Y in local space" }, + { "Z", EC::Float, "Z in local space" }); + SetOutputs( + { "X", EC::Float, "X in global space" }, + { "Y", EC::Float, "Y in global space" }, + { "Z", EC::Float, "Z in global space" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_LocalToGlobal, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::LocalToGlobal(_I0, _I1, _I2, _O0, _O1, _O2, _C0); +) + +UVoxelNode_GlobalToLocal::UVoxelNode_GlobalToLocal() +{ + SetInputs( + { "X", EC::Float, "X in global space" }, + { "Y", EC::Float, "Y in global space" }, + { "Z", EC::Float, "Z in global space" }); + SetOutputs( + { "X", EC::Float, "X in local space" }, + { "Y", EC::Float, "Y in local space" }, + { "Z", EC::Float, "Z in local space" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GlobalToLocal, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::GlobalToLocal(_I0, _I1, _I2, _O0, _O1, _O2, _C0); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_TransformVector::UVoxelNode_TransformVector() +{ + SetInputs( + { "X", EC::Float, "X in local space" }, + { "Y", EC::Float, "Y in local space" }, + { "Z", EC::Float, "Z in local space" }); + SetOutputs( + { "X", EC::Float, "X in global space" }, + { "Y", EC::Float, "Y in global space" }, + { "Z", EC::Float, "Z in global space" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_TransformVector, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::TransformVector(_I0, _I1, _I2, _O0, _O1, _O2, _C0); +) + +UVoxelNode_InverseTransformVector::UVoxelNode_InverseTransformVector() +{ + SetInputs( + { "X", EC::Float, "X in global space" }, + { "Y", EC::Float, "Y in global space" }, + { "Z", EC::Float, "Z in global space" }); + SetOutputs( + { "X", EC::Float, "X in local space" }, + { "Y", EC::Float, "Y in local space" }, + { "Z", EC::Float, "Z in local space" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_InverseTransformVector, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::InverseTransformVector(_I0, _I1, _I2, _O0, _O1, _O2, _C0); +) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp new file mode 100644 index 00000000..a4e76226 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp @@ -0,0 +1,137 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelCurveNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +#include "Curves/CurveFloat.h" +#include "Curves/CurveLinearColor.h" + +UVoxelNode_Curve::UVoxelNode_Curve() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + +TVoxelSharedPtr UVoxelNode_Curve::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_Curve& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Curve(Node.Curve) + , Variable(MakeShared(Node, Node.Curve)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetCurveValue(Curve, Inputs[0].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetCurveValue(Curve, Inputs[0].Get()); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = FVoxelNodeFunctions::GetCurveValue(" + Variable->CppName + ", " + Inputs[0] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const FVoxelRichCurve Curve; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +FText UVoxelNode_Curve::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Float Curve: {0}"), Super::GetTitle()); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CurveColor::UVoxelNode_CurveColor() +{ + SetInputs(EC::Float); + SetOutputs( + { "R", EC::Float, "Red between 0 and 1" }, + { "G", EC::Float, "Green between 0 and 1" }, + { "B", EC::Float, "Blue between 0 and 1" }, + { "A", EC::Float, "Alpha between 0 and 1" }); +} + +TVoxelSharedPtr UVoxelNode_CurveColor::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_CurveColor& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Curve(Node.Curve) + , Variable(MakeShared(Node, Node.Curve)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[0], Inputs[0].Get()); + Outputs[1].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[1], Inputs[0].Get()); + Outputs[2].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[2], Inputs[0].Get()); + Outputs[3].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[3], Inputs[0].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[0], Inputs[0].Get()); + Outputs[1].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[1], Inputs[0].Get()); + Outputs[2].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[2], Inputs[0].Get()); + Outputs[3].Get() = FVoxelNodeFunctions::GetCurveValue(Curve.Curves[3], Inputs[0].Get()); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = FVoxelNodeFunctions::GetCurveValue(" + Variable->CppName + ".Curves[0], " + Inputs[0] + ");"); + Constructor.AddLine(Outputs[1] + " = FVoxelNodeFunctions::GetCurveValue(" + Variable->CppName + ".Curves[1], " + Inputs[0] + ");"); + Constructor.AddLine(Outputs[2] + " = FVoxelNodeFunctions::GetCurveValue(" + Variable->CppName + ".Curves[2], " + Inputs[0] + ");"); + Constructor.AddLine(Outputs[3] + " = FVoxelNodeFunctions::GetCurveValue(" + Variable->CppName + ".Curves[3], " + Inputs[0] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const FVoxelColorRichCurve Curve; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +FText UVoxelNode_CurveColor::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Color Curve: {0}"), Super::GetTitle()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp new file mode 100644 index 00000000..8c590720 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp @@ -0,0 +1,110 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelDataAssetSamplerNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" + +UVoxelNode_DataAssetSampler::UVoxelNode_DataAssetSampler() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }); + SetOutputs( + { "Value", EC::Float, "Data asset value at position X Y Z. Between -1 and 1." }, + { "Material", EC::Material, "Data asset material at position X Y Z" }, + { "Size X", EC::Int, "Size of the data asset" }, + { "Size Y", EC::Int, "Size of the data asset" }, + { "Size Z", EC::Int, "Size of the data asset" }); +} + +TVoxelSharedPtr UVoxelNode_DataAssetSampler::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_DataAssetSampler& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , bBilinearInterpolation(Node.bBilinearInterpolation) + , Data(Node.Asset->GetData()) + , Variable(MakeShared(Node, Node.Asset)) + { + } + + void Compute(FVoxelNodeInputBuffer I, FVoxelNodeOutputBuffer O, const FVoxelContext& Context) const override + { + if (bBilinearInterpolation) + { + if (IsOutputUsed(0)) O[0].Get() = Data->GetInterpolatedValue(I[0].Get(), I[1].Get(), I[2].Get(), FVoxelValue::Empty()); + if (IsOutputUsed(1)) O[1].Get() = Data->GetInterpolatedMaterial(I[0].Get(), I[1].Get(), I[2].Get()); + } + else + { + if (IsOutputUsed(0)) O[0].Get() = Data->GetValue(I[0].Get(), I[1].Get(), I[2].Get(), FVoxelValue::Empty()).ToFloat(); + if (IsOutputUsed(1)) O[1].Get() = Data->GetMaterial(I[0].Get(), I[1].Get(), I[2].Get()); + } + if (IsOutputUsed(2)) O[2].Get() = Data->GetSize().X; + if (IsOutputUsed(3)) O[3].Get() = Data->GetSize().Y; + if (IsOutputUsed(4)) O[4].Get() = Data->GetSize().Z; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer O, const FVoxelContextRange& Context) const override + { + O[0].Get() = { -1.f,1.f }; + O[2].Get() = Data->GetSize().X; + O[3].Get() = Data->GetSize().Y; + O[4].Get() = Data->GetSize().Z; + } + void ComputeCpp(const TArray& I, const TArray& O, FVoxelCppConstructor& Constructor) const override + { + if (bBilinearInterpolation) + { + if (IsOutputUsed(0)) Constructor.AddLinef(TEXT("%s = %s->GetInterpolatedValue(%s, %s, %s, FVoxelValue::Empty());"), *O[0], *Variable->CppName, *I[0], *I[1], *I[2]); + if (IsOutputUsed(1)) Constructor.AddLinef(TEXT("%s = %s->GetInterpolatedMaterial(%s, %s, %s);"), *O[1], *Variable->CppName, *I[0], *I[1], *I[2]); + } + else + { + if (IsOutputUsed(0)) Constructor.AddLinef(TEXT("%s = %s->GetValue(%s, %s, %s, FVoxelValue::Empty());"), *O[0], *Variable->CppName, *I[0], *I[1], *I[2]); + if (IsOutputUsed(1)) Constructor.AddLinef(TEXT("%s = %s->GetMaterial(%s, %s, %s);"), *O[1], *Variable->CppName, *I[0], *I[1], *I[2]); + } + if (IsOutputUsed(2)) Constructor.AddLinef(TEXT("%s = %s->GetSize().X;"), *O[2], *Variable->CppName); + if (IsOutputUsed(3)) Constructor.AddLinef(TEXT("%s = %s->GetSize().Y;"), *O[3], *Variable->CppName); + if (IsOutputUsed(4)) Constructor.AddLinef(TEXT("%s = %s->GetSize().Z;"), *O[4], *Variable->CppName); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + if (IsOutputUsed(0)) Constructor.AddLinef(TEXT("%s = { -1.f, 1.f };"), *Outputs[0]); + if (IsOutputUsed(2)) Constructor.AddLinef(TEXT("%s = %s->GetSize().X;"), *Outputs[2], *Variable->CppName); + if (IsOutputUsed(3)) Constructor.AddLinef(TEXT("%s = %s->GetSize().Y;"), *Outputs[3], *Variable->CppName); + if (IsOutputUsed(4)) Constructor.AddLinef(TEXT("%s = %s->GetSize().Z;"), *Outputs[4], *Variable->CppName); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const bool bBilinearInterpolation; + const TVoxelSharedRef Data; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +EVoxelPinCategory UVoxelNode_DataAssetSampler::GetInputPinCategory(int32 PinIndex) const +{ + return bBilinearInterpolation ? EC::Float : EC::Int; +} + +FText UVoxelNode_DataAssetSampler::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Data Asset: {0}"), Super::GetTitle()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp new file mode 100644 index 00000000..e01279ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp @@ -0,0 +1,456 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelExecNodes.h" + +#include "VoxelNodes/VoxelNodeColors.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphConstants.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" + +int32 UVoxelNode_MaterialSetter::GetOutputIndex() const +{ + return FVoxelGraphOutputsIndices::MaterialIndex; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetColor::UVoxelNode_SetColor() +{ + SetInputs( + EC::Exec, + { "Color", EC::Color, "Color" }); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_SetColor::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + using FVoxelSetterComputeNode::FVoxelSetterComputeNode; + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + GraphOutputs.MaterialBuilder.SetColor(Inputs[0].Get()); + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.SetColor(" + Inputs[0] + ");"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetSingleIndex::UVoxelNode_SetSingleIndex() +{ + SetInputs( + EC::Exec, + { "Index", EC::Int, "Index between 0 and 255", "", {0, 255} }); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_SetSingleIndex::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + using FVoxelSetterComputeNode::FVoxelSetterComputeNode; + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + GraphOutputs.MaterialBuilder.SetSingleIndex(Inputs[0].Get()); + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.SetSingleIndex(" + Inputs[0] + ");"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetMultiIndexWetness::UVoxelNode_SetMultiIndexWetness() +{ + SetInputs( + EC::Exec, + { "Wetness", EC::Float, "Wetness between 0 and 1", "", {0, 1 } }); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_SetMultiIndexWetness::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + using FVoxelSetterComputeNode::FVoxelSetterComputeNode; + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + GraphOutputs.MaterialBuilder.SetWetness(Inputs[0].Get()); + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.SetWetness(" + Inputs[0] + ");"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_AddMultiIndex::UVoxelNode_AddMultiIndex() +{ + SetInputs( + EC::Exec, + { "Index", EC::Int, "Material index between 0 and 255", "", {0, 255 } }, + { "Strength", EC::Float, "Strength, usually between 0 and 1", "1" }, + { "Lock Strength", EC::Boolean, "If true, the strength won't be normalized. For example, if you want small rocks with the same density everywhere." }); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_AddMultiIndex::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + using FVoxelSetterComputeNode::FVoxelSetterComputeNode; + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + GraphOutputs.MaterialBuilder.AddMultiIndex(Inputs[0].Get(), Inputs[1].Get(), Inputs[2].Get()); + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.AddMultiIndex(" + Inputs[0] + ", " + Inputs[1] + ", " + Inputs[2] + ");"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetUVs::UVoxelNode_SetUVs() +{ + SetInputs( + EC::Exec, + { "Channel", EC::Int, "Channel, should be 0 or 1", "", {0, 255 } }, + { "U", EC::Float, "U coordinate between 0 and 1", "", {0, 1} }, + { "V", EC::Float, "V coordinate between 0 and 1", "", {0, 1} }); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_SetUVs::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + const bool bSetU; + const bool bSetV; + + FLocalVoxelComputeNode(const UVoxelNode_SetUVs& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelSetterComputeNode(Node, CompilationNode) + , bSetU(Node.bSetU) + , bSetV(Node.bSetV) + { + } + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + if (bSetU) GraphOutputs.MaterialBuilder.SetU(Inputs[0].Get(), Inputs[1].Get()); + if (bSetV) GraphOutputs.MaterialBuilder.SetV(Inputs[0].Get(), Inputs[2].Get()); + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + if (bSetU) Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.SetU(" + Inputs[0] + ", " + Inputs[1] + ");"); + if (bSetV) Constructor.AddLine(FVoxelCppIds::GraphOutputs + ".MaterialBuilder.SetV(" + Inputs[0] + ", " + Inputs[2] + ");"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetNode::UVoxelNode_SetNode() +{ + SetInputs(EC::Exec, EC::Exec); + SetOutputs(EC::Exec); +} + +TVoxelSharedPtr UVoxelNode_SetNode::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSetterComputeNode + { + public: + const uint32 Index; + + FLocalVoxelComputeNode(const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelSetterComputeNode(Node, CompilationNode) + , Index(CastCheckedVoxel(CompilationNode).OutputIndex) + { + } + + void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const override + { + if (Index == FVoxelGraphOutputsIndices::MaterialIndex) + { + GraphOutputs.MaterialBuilder = Inputs[0].template Get(); + } + else + { + GraphOutputs.Buffer[Index] = Inputs[0]; + } + } + void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const override + { + // No material in range + GraphOutputs.Buffer[Index] = Inputs[0]; + } + void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(GraphOutputs[Index] + " = " + Inputs[0] + ";"); + } + void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const override + { + Constructor.AddLine(GraphOutputs[Index] + " = " + Inputs[0] + ";"); + } + }; + return MakeVoxelShared(*this, InCompilationNode); +} + +FText UVoxelNode_SetNode::GetTitle() const +{ + return FText::FromString("Set " + CachedOutput.Name.ToString()); +} + +EVoxelPinCategory UVoxelNode_SetNode::GetInputPinCategory(int32 PinIndex) const +{ + return PinIndex == 0 + ? EVoxelPinCategory::Exec + : FVoxelPinCategory::DataPinToPin(CachedOutput.Category); +} + +FName UVoxelNode_SetNode::GetInputPinName(int32 PinIndex) const +{ + return PinIndex == 0 ? FName() : CachedOutput.Name; +} + +void UVoxelNode_SetNode::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + +#if WITH_EDITOR + if (!UpdateSetterNode()) + { + ErrorReporter.AddMessageToNode(this, "invalid output", EVoxelGraphNodeMessageType::Error); + } +#endif +} + +int32 UVoxelNode_SetNode::GetOutputIndex() const +{ + return Index; +} + +#if WITH_EDITOR +bool UVoxelNode_SetNode::UpdateSetterNode() +{ + if (Graph) + { + auto Outputs = Graph->GetOutputs(); + FVoxelGraphOutput NewOutput; + if (Outputs.Contains(Index) && !FVoxelGraphOutputsUtils::IsVoxelGraphOutputHidden(Index)) + { + NewOutput = Outputs[Index]; + } + if (CachedOutput.GUID.IsValid() && NewOutput.GUID != CachedOutput.GUID) + { + // Try to find it by GUID and name + TArray OutputsArray; + Outputs.GenerateValueArray(OutputsArray); + auto* NewOutputPtr = OutputsArray.FindByPredicate([&](auto& Output) {return Output.GUID == CachedOutput.GUID; }); + if (!NewOutputPtr) + { + NewOutputPtr = OutputsArray.FindByPredicate([&](auto& Output) {return Output.Name == CachedOutput.Name; }); + } + if (NewOutputPtr) + { + NewOutput = *NewOutputPtr; + Index = NewOutputPtr->Index; + } + else + { + return false; + } + } + const bool bDiffCategory = CachedOutput.Category != NewOutput.Category; + const bool bDiffName = CachedOutput.Name != NewOutput.Name; + if (GraphNode && (bDiffCategory || bDiffName)) + { + CachedOutput = NewOutput; + GraphNode->ReconstructNode(); + if (bDiffCategory) + { + Graph->CompileVoxelNodesFromGraphNodes(); + } + } + } + return CachedOutput.GUID.IsValid(); +} + +void UVoxelNode_SetNode::SetIndex(uint32 NewIndex) +{ + Index = NewIndex; + UpdateSetterNode(); +} + +void UVoxelNode_SetNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + UpdateSetterNode(); + } +} + +void UVoxelNode_SetNode::PostLoad() +{ + Super::PostLoad(); + UpdateSetterNode(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_FunctionSeparator::UVoxelNode_FunctionSeparator() +{ + SetColor(FVoxelNodeColors::ExecNode); + AddInput("", "", EC::Exec); + AddOutput("", "", EC::Exec); +} + +TSharedPtr UVoxelNode_FunctionSeparator::GetCompilationNode() const +{ + return MakeShared(*this); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FLinearColor UVoxelNode_FlowMerge::GetColor() const +{ + return FColor::White; +} + +EVoxelPinCategory UVoxelNode_FlowMerge::GetInputPinCategory(int32 PinIndex) const +{ + PinIndex = PinIndex % (Types.Num() + 1); + return PinIndex == 0 ? EVoxelPinCategory::Exec : FVoxelPinCategory::DataPinToPin(Types[PinIndex - 1].Type); +} + +EVoxelPinCategory UVoxelNode_FlowMerge::GetOutputPinCategory(int32 PinIndex) const +{ + return PinIndex == 0 ? EVoxelPinCategory::Exec : FVoxelPinCategory::DataPinToPin(Types[PinIndex - 1].Type); +} + +FName UVoxelNode_FlowMerge::GetInputPinName(int32 PinIndex) const +{ + const bool bIsA = PinIndex <= Types.Num(); + PinIndex = PinIndex % (Types.Num() + 1); + if (PinIndex == 0) + { + return bIsA ? FName("Exec A") : FName("Exec B"); + } + else + { + return FName(*(Types[PinIndex - 1].Name + (bIsA ? " A" : " B"))); + } +} + +FName UVoxelNode_FlowMerge::GetOutputPinName(int32 PinIndex) const +{ + return PinIndex == 0 ? FName("Exec") : FName(*Types[PinIndex - 1].Name); +} + +int32 UVoxelNode_FlowMerge::GetMinInputPins() const +{ + return 2 + 2 * Types.Num(); +} + +int32 UVoxelNode_FlowMerge::GetMaxInputPins() const +{ + return GetMinInputPins(); +} + +int32 UVoxelNode_FlowMerge::GetOutputPinsCount() const +{ + return 1 + Types.Num(); +} + +TSharedPtr UVoxelNode_FlowMerge::GetCompilationNode() const +{ + return MakeShared(*this); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp new file mode 100644 index 00000000..4a4bfcd7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp @@ -0,0 +1,239 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelExposedNodes.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "CppTranslation/VoxelVariables.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGenerators/VoxelGeneratorParameters.h" + +#include "EdGraph/EdGraphNode.h" +#include "UObject/Package.h" +#include "UObject/PropertyPortFlags.h" + +TMap UVoxelExposedNode::GetMetaData() const +{ + auto Result = CustomMetaData; + Result.Add("DisplayName", DisplayName); + + if (!UIMin.IsEmpty()) + { + Result.Add("UIMin", UIMin); + } + if (!UIMax.IsEmpty()) + { + Result.Add("UIMax", UIMax); + } + + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FLinearColor UVoxelExposedNode::GetColor() const +{ + return FVoxelNodeColors::ExposedNode; +} + +FText UVoxelExposedNode::GetTitle() const +{ + const FName PropertyName = GetParameterPropertyName(); + FProperty* Property = GetClass()->FindPropertyByName(PropertyName); + if (!ensure(Property)) + { + return FText::FromString(DisplayName); + } + + if (Property->IsA() || + Property->IsA() || + Property->IsA()) + { + FString Value; + Property->ExportTextItem(Value, Property->ContainerPtrToValuePtr(this), nullptr, nullptr, PPF_None); + + if (Property->IsA()) + { + Value = FString::SanitizeFloat(FCString::Atof(*Value)); + } + + return FText::FromString(FString::Printf(TEXT("%s = %s"), *DisplayName, *Value)); + } + + return FText::FromString(DisplayName); +} + +bool UVoxelExposedNode::CanRenameNode() const +{ + return bCanBeRenamed; +} + +FString UVoxelExposedNode::GetEditableName() const +{ + return DisplayName; +} + +void UVoxelExposedNode::SetEditableName(const FString& NewName) +{ + bCanBeRenamed = false; + DisplayName = *NewName; + MakeNameUnique(); + MarkPackageDirty(); +#if WITH_EDITOR + if (GraphNode) + { + GraphNode->bCanRenameNode = bCanBeRenamed; + GraphNode->ReconstructNode(); + } +#endif +} + +void UVoxelExposedNode::ApplyParameters(const TMap& Parameters) +{ + auto* NewValuePtr = Parameters.Find(UniqueName); + if (!NewValuePtr) + { + return; + } + + const FName PropertyName = GetParameterPropertyName(); + FProperty* Property = GetClass()->FindPropertyByName(PropertyName); + if (!ensure(Property)) + { + return; + } + + Modify(); + + if (!ensure(Property->ImportText(**NewValuePtr, Property->ContainerPtrToValuePtr(this), PPF_None, this))) + { + return; + } + +#if WITH_EDITOR + if (GraphNode) + { + GraphNode->ReconstructNode(); + } +#endif +} + +void UVoxelExposedNode::GetParameters(TArray& OutParameters) const +{ + const FName PropertyName = GetParameterPropertyName(); + FProperty* Property = GetClass()->FindPropertyByName(PropertyName); + if (!ensure(Property)) + { + return; + } + + FString DefaultValue; + Property->ExportTextItem(DefaultValue, Property->ContainerPtrToValuePtr(this), nullptr, nullptr, PPF_None); + + OutParameters.Add(FVoxelGeneratorParameter( + UniqueName, + FVoxelGeneratorParameterType(*Property), + DisplayName, + Category, + Tooltip, + Priority, + GetMetaData(), + DefaultValue)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelExposedNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + MakeNameUnique(); + } +} +#endif + +void UVoxelExposedNode::PostEditImport() +{ + Super::PostEditImport(); + + MakeNameUnique(); +} + +void UVoxelExposedNode::PostLoad() +{ + Super::PostLoad(); + + if (DisplayName.IsEmpty() && !UniqueName.IsNone()) + { + // Fixup old versions that only had UniqueName + DisplayName = UniqueName.ToString(); + DisplayName = DisplayName.Replace(TEXT("_"), TEXT(" ")); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelExposedNode::MakeNameUnique() +{ + if (!Graph) + { + return; + } + + const auto OriginalUniqueName = UniqueName; + + UniqueName = *FVoxelVariable::SanitizeName(DisplayName); + + TSet Names; + for (auto* Node : Graph->AllNodes) + { + auto* ExposedNode = Cast(Node); + if (ExposedNode && ExposedNode != this && ExposedNode->GetClass() != GetClass()) + { + Names.Add(ExposedNode->UniqueName); + } + } + + int32 Number = UniqueName.GetNumber(); + while (Names.Contains(UniqueName)) + { + UniqueName.SetNumber(++Number); + } + + if (OriginalUniqueName != UniqueName) + { + MarkPackageDirty(); + } +} + +const void* UVoxelExposedNode::GetParameterInternal(void* Temp, UScriptStruct* Struct) const +{ + const FName PropertyName = GetParameterPropertyName(); + FProperty* Property = GetClass()->FindPropertyByName(PropertyName); + if (!ensure(Property)) return Temp; + + if (auto* StructProperty = UE_25_SWITCH(Cast, CastField)(Property)) + { + ensure(StructProperty->Struct == Struct); + } + + const void* Default = Property->ContainerPtrToValuePtr(this); + + auto* Parameter = Graph->TransientParameters.Find(UniqueName); + if (!Parameter) return Default; + + if (!ensure(Property->ImportText(**Parameter, Temp, PPF_None, GetTransientPackage()))) + { + return Default; + } + + return Temp; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp new file mode 100644 index 00000000..42f1ba36 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp @@ -0,0 +1,259 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGavoronoiNoiseNode.h" +#include "VoxelNodes/VoxelNoiseNodeHelper.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConstructor.h" + +inline void AddGavoronoiPins(FVoxelPinsHelper& Pins) +{ + Pins.InputPins.Add(FVoxelHelperPin("Direction X", EVoxelPinCategory::Float, "Direction of the noise", "0.5")); + Pins.InputPins.Add(FVoxelHelperPin("Direction Y", EVoxelPinCategory::Float, "Direction of the noise", "0.5")); + Pins.InputPins.Add(FVoxelHelperPin("Direction Variation", EVoxelPinCategory::Float, "Strength of the noise added to the direction, between 0 and 1", "0.4")); +} + +UVoxelNode_2DGavoronoiNoise::UVoxelNode_2DGavoronoiNoise() +{ + AddGavoronoiPins(CustomNoisePins); +} + +UVoxelNode_2DGavoronoiNoiseFractal::UVoxelNode_2DGavoronoiNoiseFractal() +{ + AddGavoronoiPins(CustomNoisePins); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +class TVoxelGavoronoiNoiseComputeNode : public TParent +{ +public: + template + TVoxelGavoronoiNoiseComputeNode(const TNode& Node, const FVoxelCompilationNode& CompilationNode) + : TParent(Node, CompilationNode) + , Jitter(Node.Jitter) + { + } + + virtual void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override + { + FVoxelNoiseComputeNode::InitNoise(Noise); + Noise.SetCellularJitter(Jitter); + } + +protected: + const float Jitter; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelNode_2DGavoronoiNoise::SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const +{ + Super::SetupInputsForComputeOutputRanges(Inputs); + + // Direction X + Inputs[GetBaseInputPinsCount() + 0].Get() = 0.5f; + // Direction Y + Inputs[GetBaseInputPinsCount() + 1].Get() = 0.5f; + // Direction Variation + Inputs[GetBaseInputPinsCount() + 2].Get() = 0.5f; + + return GetBaseInputPinsCount() + 3; +} + +TVoxelSharedPtr UVoxelNode_2DGavoronoiNoise::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public TVoxelGavoronoiNoiseComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + using TVoxelGavoronoiNoiseComputeNode::TVoxelGavoronoiNoiseComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = GetNoise().GetGavoronoi_2D( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[4].Get(), + Inputs[5].Get(), + Inputs[6].Get()); + + Clamp(Outputs, 0); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s.GetGavoronoi_2D(%s, %s, %s, %s, %s, %s);"), + *Outputs[0], + *GetNoiseName(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *Inputs[4], + *Inputs[5], + *Inputs[6]); + + Clamp(Constructor, Outputs, 0); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelNode_2DGavoronoiNoiseFractal::SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const +{ + Super::SetupInputsForComputeOutputRanges(Inputs); + + // Direction X + Inputs[GetBaseInputPinsCount() + 0].Get() = 0.5f; + // Direction Y + Inputs[GetBaseInputPinsCount() + 1].Get() = 0.5f; + // Direction Variation + Inputs[GetBaseInputPinsCount() + 2].Get() = 0.5f; + + return GetBaseInputPinsCount() + 3; +} + +TVoxelSharedPtr UVoxelNode_2DGavoronoiNoiseFractal::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public TVoxelGavoronoiNoiseComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + using TVoxelGavoronoiNoiseComputeNode::TVoxelGavoronoiNoiseComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = GetNoise().GetGavoronoiFractal_2D( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + GET_OCTAVES, + Inputs[4].Get(), + Inputs[5].Get(), + Inputs[6].Get()); + + Clamp(Outputs, 0); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s.GetGavoronoiFractal_2D(%s, %s, %s, %s, %s, %s, %s);"), + *Outputs[0], + *GetNoiseName(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *GET_OCTAVES_CPP, + *Inputs[4], + *Inputs[5], + *Inputs[6]); + + Clamp(Constructor, Outputs, 0); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_2DErosion::UVoxelNode_2DErosion() +{ + bComputeDerivative = true; + + const FString ToolTip = "The derivative of the noise to erode. You can get them by ticking Compute Derivatives in the node details"; + + CustomNoisePins.InputPins.Add(FVoxelHelperPin("Noise DX", EVoxelPinCategory::Float, ToolTip)); + CustomNoisePins.InputPins.Add(FVoxelHelperPin("Noise DY", EVoxelPinCategory::Float, ToolTip)); +} + +void UVoxelNode_2DErosion::ComputeOutputRanges() +{ + // Since the values depend on the input, we cannot sample + + OutputRanges.Reset(); + OutputRanges.Add({ -1.f, 1.f }); + OutputRanges.Add({ -1.f, 1.f }); + OutputRanges.Add({ -1.f, 1.f }); + FVoxelNoiseComputeNode::ExpandRanges(OutputRanges, Tolerance); +} + +#if WITH_EDITOR +bool UVoxelNode_2DErosion::CanEditChange(const FProperty* InProperty) const +{ + return + Super::CanEditChange(InProperty) && + InProperty->GetFName() != GET_MEMBER_NAME_STATIC(UVoxelNode_2DErosion, bComputeDerivative) && + InProperty->GetFName() != GET_MEMBER_NAME_STATIC(UVoxelNode_2DErosion, FractalType); +} +#endif + +TVoxelSharedPtr UVoxelNode_2DErosion::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelNoiseFractalComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_2DErosion& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseFractalComputeNode(Node, CompilationNode) + , Jitter(Node.Jitter) + { + } + + virtual void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override + { + FVoxelNoiseFractalComputeNode::InitNoise(Noise); + Noise.SetCellularJitter(Jitter); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = GetNoise().GetErosion_2D( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + GET_OCTAVES, + Inputs[4].Get(), + Inputs[5].Get(), + Outputs[1].Get(), + Outputs[2].Get()); + + Clamp(Outputs, 0); + Clamp(Outputs, 1); + Clamp(Outputs, 2); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s.GetErosion_2D(%s, %s, %s, %s, %s, %s, %s, %s);"), + *Outputs[0], + *GetNoiseName(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *GET_OCTAVES_CPP, + *Inputs[4], + *Inputs[5], + *Outputs[1], + *Outputs[2]); + + Clamp(Constructor, Outputs, 0); + Clamp(Constructor, Outputs, 1); + Clamp(Constructor, Outputs, 2); + } + + private: + const float Jitter; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp new file mode 100644 index 00000000..6cfe9832 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp @@ -0,0 +1,311 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGeneratorMergeNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGraphOutputsConfig.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "Runtime/VoxelComputeNode.h" + +constexpr int32 NumDefaultInputPins_WGMN = 3 + 2 * 4; + +inline TArray GetFloatOutputs(UVoxelGraphOutputsConfig* Config) +{ + TArray Result; + if (Config) + { + for (auto& Output : Config->Outputs) + { + if (Output.Category == EVoxelDataPinCategory::Float) + { + Result.Add(Output.Name); + } + } + } + return Result; +} + +inline TArray GetComputeFloatOutputs(const TArray& FloatOutputs, const FVoxelComputeNode& Node) +{ + check(Node.OutputCount == 2 + FloatOutputs.Num() + 1); + + TArray Result; + for (int32 Index = 0; Index < FloatOutputs.Num(); Index++) + { + Result.Add(Node.IsOutputUsed(2 + Index)); + } + return Result; +} + +TVoxelSharedPtr UVoxelNode_GeneratorMerge::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_GeneratorMerge& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Tolerance(Node.Tolerance) + , MaterialConfig(Node.MaterialConfig) + , Variable(MakeShared(Node, Node.Generators)) + , Instances(FVoxelNodeFunctions::CreateGeneratorArray(Node.Generators)) + , FloatOutputs(GetFloatOutputs(Node.Outputs)) + , bComputeValue(IsOutputUsed(0)) + , bComputeMaterial(IsOutputUsed(1)) + , ComputeFloatOutputs(GetComputeFloatOutputs(FloatOutputs, *this)) + { + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + for (auto& Instance : Instances) + { + Instance->Init(InitStruct); + } + } + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("for (auto& Instance : %s)"), *Variable->CppName); + Constructor.StartBlock(); + Constructor.AddLinef(TEXT("Instance->Init(%s);"), *FVoxelCppIds::InitStruct); + Constructor.EndBlock(); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer InOutputs, const FVoxelContext& Context) const override + { + TArray> OutFloatOutputs; + FVoxelNodeFunctions::ComputeGeneratorsMerge( + MaterialConfig, + Tolerance, + Instances, + FloatOutputs, + Context, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[3].Get(), Inputs[4].Get(), + Inputs[5].Get(), Inputs[6].Get(), + Inputs[7].Get(), Inputs[8].Get(), + Inputs[9].Get(), Inputs[10].Get(), + bComputeValue, bComputeMaterial, ComputeFloatOutputs, + InOutputs[0].Get(), + InOutputs[1].Get(), + OutFloatOutputs, + InOutputs[OutputCount - 1].Get()); + + for (int32 Index = 0; Index < OutFloatOutputs.Num(); Index++) + { + InOutputs[2 + Index].Get() = OutFloatOutputs[Index]; + } + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer InOutputs, const FVoxelContextRange& Context) const override + { + InOutputs[0].Get() = 0; + + TArray, TInlineAllocator<128>> OutFloatOutputs; + FVoxelNodeFunctions::ComputeGeneratorsMergeRange( + Instances, + FloatOutputs, + Context, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + bComputeValue, ComputeFloatOutputs, + InOutputs[0].Get(), + OutFloatOutputs, + InOutputs[OutputCount - 1].Get()); + + for (int32 Index = 0; Index < OutFloatOutputs.Num(); Index++) + { + InOutputs[2 + Index].Get() = OutFloatOutputs[Index]; + } + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& InOutputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.StartBlock(); + + Constructor.NewLine(); + Constructor.AddLinef(TEXT("static TArray StaticFloatOutputs = %s;"), *FVoxelCppUtils::ArrayToString(FloatOutputs)); + Constructor.AddLinef(TEXT("static TArray StaticComputeFloatOutputs = %s;"), *FVoxelCppUtils::ArrayToString(ComputeFloatOutputs)); + Constructor.NewLine(); + + const FString MaterialConfigString = + MaterialConfig == EVoxelMaterialConfig::RGB + ? "EVoxelMaterialConfig::RGB" + : MaterialConfig == EVoxelMaterialConfig::SingleIndex + ? "EVoxelMaterialConfig::SingleIndex" + : "EVoxelMaterialConfig::DoubleIndex"; // TODO PLACEABLE ITEMS + + Constructor.AddLine("TArray> OutFloatOutputs;"); + Constructor.AddLine("FVoxelNodeFunctions::ComputeGeneratorsMerge("); + Constructor.Indent(); + Constructor.AddLinef(TEXT("%s,"), *MaterialConfigString); + Constructor.AddLinef(TEXT("%f,"), Tolerance); + Constructor.AddLinef(TEXT("%s,"), *Variable->CppName); + Constructor.AddLinef(TEXT("StaticFloatOutputs,")); + Constructor.AddLinef(TEXT("%s,"), *FVoxelCppIds::Context); + Constructor.AddLinef(TEXT("%s,"), *Inputs[0]); + Constructor.AddLinef(TEXT("%s,"), *Inputs[1]); + Constructor.AddLinef(TEXT("%s,"), *Inputs[2]); + Constructor.AddLinef(TEXT("%s, %s,"), *Inputs[3], *Inputs[4]); + Constructor.AddLinef(TEXT("%s, %s,"), *Inputs[5], *Inputs[6]); + Constructor.AddLinef(TEXT("%s, %s,"), *Inputs[7], *Inputs[8]); + Constructor.AddLinef(TEXT("%s, %s,"), *Inputs[9], *Inputs[10]); + Constructor.AddLinef(TEXT("%s, %s,"), *LexToString(bComputeValue), *LexToString(bComputeMaterial)); + Constructor.AddLinef(TEXT("StaticComputeFloatOutputs,")); + Constructor.AddLinef(TEXT("%s,"), *InOutputs[0]); + Constructor.AddLinef(TEXT("%s,"), *InOutputs[1]); + Constructor.AddLinef(TEXT("OutFloatOutputs,")); + Constructor.AddLinef(TEXT("%s);"), *InOutputs[OutputCount - 1]); + Constructor.Unindent(); + + for (int32 Index = 0; Index < FloatOutputs.Num(); Index++) + { + if (ComputeFloatOutputs[Index]) + { + Constructor.AddLinef(TEXT("%s = OutFloatOutputs[%d];"), *InOutputs[2 + Index], Index); + } + } + Constructor.EndBlock(); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& InOutputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.StartBlock(); + + Constructor.NewLine(); + Constructor.AddLinef(TEXT("static TArray StaticFloatOutputs = %s;"), *FVoxelCppUtils::ArrayToString(FloatOutputs)); + Constructor.AddLinef(TEXT("static TArray StaticComputeFloatOutputs = %s;"), *FVoxelCppUtils::ArrayToString(ComputeFloatOutputs)); + Constructor.NewLine(); + + Constructor.AddLine("TArray, TInlineAllocator<128>> OutFloatOutputs;"); + Constructor.AddLine("FVoxelNodeFunctions::ComputeGeneratorsMergeRange("); + Constructor.Indent(); + Constructor.AddLinef(TEXT("%s,"), *Variable->CppName); + Constructor.AddLinef(TEXT("StaticFloatOutputs,")); + Constructor.AddLinef(TEXT("%s,"), *FVoxelCppIds::Context); + Constructor.AddLinef(TEXT("%s,"), *Inputs[0]); + Constructor.AddLinef(TEXT("%s,"), *Inputs[1]); + Constructor.AddLinef(TEXT("%s,"), *Inputs[2]); + Constructor.AddLinef(TEXT("%s,"), *LexToString(bComputeValue)); + Constructor.AddLinef(TEXT("StaticComputeFloatOutputs,")); + Constructor.AddLinef(TEXT("%s,"), *InOutputs[0]); + Constructor.AddLinef(TEXT("OutFloatOutputs,")); + Constructor.AddLinef(TEXT("%s);"), *InOutputs[OutputCount - 1]); + Constructor.Unindent(); + + for (int32 Index = 0; Index < FloatOutputs.Num(); Index++) + { + if (ComputeFloatOutputs[Index]) + { + Constructor.AddLinef(TEXT("%s = OutFloatOutputs[%d];"), *InOutputs[2 + Index], Index); + } + } + Constructor.EndBlock(); + } + + virtual void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const float Tolerance; + const EVoxelMaterialConfig MaterialConfig; + const TSharedRef Variable; + const TArray> Instances; + const TArray FloatOutputs; + + const bool bComputeValue; + const bool bComputeMaterial; + const TArray ComputeFloatOutputs; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +UVoxelNode_GeneratorMerge::UVoxelNode_GeneratorMerge() +{ + SetInputs({ + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }, + { "Index 0", EC::Int, "First generator index" }, + { "Alpha 0", EC::Float, "First generator alpha" }, + { "Index 1", EC::Int, "Second generator index" }, + { "Alpha 1", EC::Float, "Second generator alpha" }, + { "Index 2", EC::Int, "Third generator index" }, + { "Alpha 2", EC::Float, "Third generator alpha" }, + { "Index 3", EC::Int, "Fourth generator index" }, + { "Alpha 3", EC::Float, "Fourth generator alpha" } }); + check(UVoxelNodeHelper::GetMinInputPins() == NumDefaultInputPins_WGMN); +} + +FText UVoxelNode_GeneratorMerge::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Generator Merge: {0}"), Super::GetTitle()); +} + +int32 UVoxelNode_GeneratorMerge::GetOutputPinsCount() const +{ + return 2 + GetFloatOutputs(Outputs).Num() + 1; +} + +FName UVoxelNode_GeneratorMerge::GetOutputPinName(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return "Value"; + } + if (PinIndex == 1) + { + return "Material"; + } + if (PinIndex == GetOutputPinsCount() - 1) + { + return "Num Queried Generators"; + } + PinIndex -= 2; + auto FloatOutputs = GetFloatOutputs(Outputs); + if (FloatOutputs.IsValidIndex(PinIndex)) + { + return FloatOutputs[PinIndex]; + } + return "Error"; +} + +EVoxelPinCategory UVoxelNode_GeneratorMerge::GetOutputPinCategory(int32 PinIndex) const +{ + if (PinIndex == 1) + { + return EC::Material; + } + else if (PinIndex == GetOutputPinsCount() - 1) + { + return EC::Int; + } + else + { + return EC::Float; + } +} + +void UVoxelNode_GeneratorMerge::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + + for (auto& Generator : Generators) + { + if (!Generator.IsValid()) + { + ErrorReporter.AddMessageToNode(this, "invalid generator", EVoxelGraphNodeMessageType::Error); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp new file mode 100644 index 00000000..afc9faca --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp @@ -0,0 +1,275 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGeneratorSamplerNodes.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "VoxelGenerators/VoxelFlatGenerator.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "NodeFunctions/VoxelNodeFunctions.h" + +TSharedPtr UVoxelNode_GeneratorSamplerBase::GetCompilationNode() const +{ + auto Result = MakeShared(*this); + Result->DefaultDependencies = EVoxelAxisDependenciesFlags::X; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SingleGeneratorSamplerBase::UVoxelNode_SingleGeneratorSamplerBase() +{ + Generator = UVoxelFlatGenerator::StaticClass(); + + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }); +} + +FText UVoxelNode_SingleGeneratorSamplerBase::GetTitle() const +{ + return FText::Format( + VOXEL_LOCTEXT("Generator: {0}"), + FText::FromString(UniqueName.ToString())); +} + +void UVoxelNode_SingleGeneratorSamplerBase::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + + if (!Generator.IsValid()) + { + ErrorReporter.AddMessageToNode(this, "invalid generator", EVoxelGraphNodeMessageType::Error); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelGeneratorSamplerComputeNode : public FVoxelDataComputeNode +{ +public: + const int32 NumDefaultInputPins = 3; + + FVoxelGeneratorSamplerComputeNode(const UVoxelNode_SingleGeneratorSamplerBase& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Generator(Node.Generator.GetInstance(false)) + , Variable(MakeShared(Node, Node.Generator)) + { + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + Generator->Init(InitStruct); + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Variable->CppName + "->Init(" + FVoxelCppIds::InitStruct + ");"); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + +protected: + const TVoxelSharedRef Generator; + const TSharedRef Variable; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorValue::UVoxelNode_GetGeneratorValue() +{ + SetOutputs({"", EC::Float, "Value"}); +} + +TVoxelSharedPtr UVoxelNode_GetGeneratorValue::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelGeneratorSamplerComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + using FVoxelGeneratorSamplerComputeNode::FVoxelGeneratorSamplerComputeNode; + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Generator->GetValue( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context.LOD, + FVoxelItemStack(Context.Items.ItemHolder)); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange & Context) const override + { + Outputs[0].Get() = Generator->GetValueRange( + FVoxelRangeUtilities::BoundsFromRanges( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get()), + Context.LOD, + FVoxelItemStack(Context.Items.ItemHolder)); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s->GetValue(%s, %s, %s, %s.LOD, FVoxelItemStack(%s.Items.ItemHolder));"), + *Outputs[0], + *Variable->CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context, + *FVoxelCppIds::Context); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s->GetValueRange(FVoxelRangeUtilities::BoundsFromRanges(%s, %s, %s), %s.LOD, FVoxelItemStack(%s.Items.ItemHolder));"), + *Outputs[0], + *Variable->CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context, + *FVoxelCppIds::Context); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorMaterial::UVoxelNode_GetGeneratorMaterial() +{ + SetOutputs({ "", EC::Material, "Material" }); +} + +TVoxelSharedPtr UVoxelNode_GetGeneratorMaterial::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelGeneratorSamplerComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + using FVoxelGeneratorSamplerComputeNode::FVoxelGeneratorSamplerComputeNode; + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Generator->GetMaterial( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context.LOD, + FVoxelItemStack(Context.Items.ItemHolder)); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s->GetMaterial(%s, %s, %s, %s.LOD, FVoxelItemStack(%s.Items.ItemHolder));"), + *Outputs[0], + *Variable->CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context, + *FVoxelCppIds::Context); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorCustomOutput::UVoxelNode_GetGeneratorCustomOutput() +{ + SetOutputs({ "", EC::Float, "Custom Output Value" }); +} + +FText UVoxelNode_GetGeneratorCustomOutput::GetTitle() const +{ + return FText::FromString("Get Generator Custom Output: " + OutputName.ToString()); +} + +TVoxelSharedPtr UVoxelNode_GetGeneratorCustomOutput::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelGeneratorSamplerComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_GetGeneratorCustomOutput& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelGeneratorSamplerComputeNode(Node, CompilationNode) + , OutputName(Node.OutputName) + { + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetGeneratorCustomOutput( + *Generator, + OutputName, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetGeneratorCustomOutput( + *Generator, + OutputName, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = FVoxelNodeFunctions::GetGeneratorCustomOutput(*%s, STATIC_FNAME(\"%s\"), %s, %s, %s, %s);"), + *Outputs[0], + *Variable->CppName, + *OutputName.ToString(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = FVoxelNodeFunctions::GetGeneratorCustomOutput(*%s, STATIC_FNAME(\"%s\"), %s, %s, %s, %s);"), + *Outputs[0], + *Variable->CppName, + *OutputName.ToString(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + + private: + const FName OutputName; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp new file mode 100644 index 00000000..a6be1ce8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGetMaterialCollectionIndexNode.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" +#include "VoxelGraphGenerator.h" + +#include "Logging/MessageLog.h" +#include "Misc/UObjectToken.h" +#include "UObject/Package.h" +#include "AssetData.h" +#include "VoxelGraphPreviewSettings.h" +#include "Materials/MaterialFunction.h" +#include "Materials/MaterialInstanceConstant.h" + +UVoxelNode_GetMaterialCollectionIndex::UVoxelNode_GetMaterialCollectionIndex() +{ + SetOutputs(EVoxelPinCategory::Int); +} + +FText UVoxelNode_GetMaterialCollectionIndex::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Get Material Collection Index: {0}"), Super::GetTitle()); +} + +UObject* UVoxelNode_GetMaterialCollectionIndex::GetAsset() const +{ + return Material; +} + +UClass* UVoxelNode_GetMaterialCollectionIndex::GetAssetClass() const +{ + return UObject::StaticClass(); +} + +void UVoxelNode_GetMaterialCollectionIndex::SetAsset(UObject* Object) +{ + Material = Cast(Object); + + UpdatePreview(false); +} + +bool UVoxelNode_GetMaterialCollectionIndex::ShouldFilterAsset(const FAssetData& Asset) const +{ + if (!ensure(Graph) || !Graph->PreviewSettings->MaterialCollection) + { + return true; + } + + return Graph->PreviewSettings->MaterialCollection->GetMaterialIndex(Asset.AssetName) == -1; +} + +TVoxelSharedPtr UVoxelNode_GetMaterialCollectionIndex::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_GetMaterialCollectionIndex& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Name(Node.GetParameter() ? Node.GetParameter() ->GetFName() : "") + , IndexVariable("int32", UniqueName.ToString() + "_Index") + , ExposedVariable(MakeShared(Node, Node.Material)) + { + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + if (InitStruct.MaterialCollection) + { + Index = InitStruct.MaterialCollection->GetMaterialIndex(Name); + if (Index == -1) + { + TSharedRef Message = FTokenizedMessage::Create(EMessageSeverity::Warning); + Message->AddToken(FTextToken::Create(FText::Format(VOXEL_LOCTEXT("GetMaterialCollectionIndex: Material {0} not found in "), FText::FromName(Name)))); + Message->AddToken(FUObjectToken::Create(InitStruct.MaterialCollection)); + + Message->AddToken(FTextToken::Create(VOXEL_LOCTEXT("Graph:"))); + + for (auto& SourceNode : SourceNodes) + { + if (SourceNode.IsValid()) + { + Message->AddToken(FUObjectToken::Create(SourceNode->Graph)); + } + } + if (Node.IsValid()) + { + FVoxelGraphErrorReporter ErrorReporter(Node->Graph); + ErrorReporter.AddMessageToNode(Node.Get(), "material index not found", EVoxelGraphNodeMessageType::Warning); + ErrorReporter.Apply(false); + } + FMessageLog("PIE").AddMessage(Message); + } + } + else + { + if (Node.IsValid()) + { + FVoxelGraphErrorReporter ErrorReporter(Node->Graph); + ErrorReporter.AddMessageToNode(Node.Get(), "material collection is null\nYou can set in the Preview Settings", EVoxelGraphNodeMessageType::Warning); + ErrorReporter.Apply(false); + } + } + } + virtual void InitCpp(const TArray & Inputs, FVoxelCppConstructor & Constructor) const override + { + Constructor.AddLinef(TEXT("if (%s.MaterialCollection)"), *FVoxelCppIds::InitStruct); + Constructor.StartBlock(); + Constructor.AddLinef(TEXT("%s = %s.MaterialCollection->GetMaterialIndex(%s);"), *IndexVariable.CppName, *FVoxelCppIds::InitStruct, *ExposedVariable->CppName); + Constructor.EndBlock(); + Constructor.AddLine("else"); + Constructor.StartBlock(); + Constructor.AddLinef(TEXT("%s = -1;"), *IndexVariable.CppName); + Constructor.EndBlock(); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Index; + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = Index; + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *IndexVariable.CppName); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + + virtual void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(ExposedVariable); + } + virtual void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(IndexVariable); + } + + private: + int32 Index = -1; + const FName Name; + const FVoxelVariable IndexVariable; + const TSharedRef ExposedVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp new file mode 100644 index 00000000..21493ba7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp @@ -0,0 +1,220 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGradientPerturbNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "VoxelContext.h" +#include "FastNoise/VoxelFastNoise.inl" + +UVoxelNode_2DGradientPerturb::UVoxelNode_2DGradientPerturb() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Frequency", EC::Float, "The frequency of the noise" }, + { "Amplitude", EC::Float, "The amplitude of the perturbation, in the same unit as the input" }, + { "Seed", EC::Seed, "Seed" }); + SetOutputs( + { "X", EC::Float, "X with perturbation" }, + { "Y", EC::Float, "Y with perturbation" }); +} + +UVoxelNode_2DGradientPerturbFractal::UVoxelNode_2DGradientPerturbFractal() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Frequency", EC::Float, "The frequency of the noise" }, + { "Amplitude", EC::Float, "The amplitude of the perturbation, in the same unit as the input" }, + { "Seed", EC::Seed, "Seed" }); + SetOutputs( + { "X", EC::Float, "X with perturbation" }, + { "Y", EC::Float, "Y with perturbation" }); +} + +UVoxelNode_3DGradientPerturb::UVoxelNode_3DGradientPerturb() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }, + { "Frequency", EC::Float, "The frequency of the noise" }, + { "Amplitude", EC::Float, "The amplitude of the perturbation, in the same unit as the input" }, + { "Seed", EC::Seed, "Seed" }); + SetOutputs( + { "X", EC::Float, "X with perturbation" }, + { "Y", EC::Float, "Y with perturbation" }, + { "Z", EC::Float, "Z with perturbation" }); +} + +UVoxelNode_3DGradientPerturbFractal::UVoxelNode_3DGradientPerturbFractal() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }, + { "Frequency", EC::Float, "The frequency of the noise" }, + { "Amplitude", EC::Float, "The amplitude of the perturbation, in the same unit as the input" }, + { "Seed", EC::Seed, "Seed" }); + SetOutputs( + { "X", EC::Float, "X with perturbation" }, + { "Y", EC::Float, "Y with perturbation" }, + { "Z", EC::Float, "Z with perturbation" }); +} + +TVoxelSharedPtr UVoxelNode_2DGradientPerturb::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelNoiseComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + using FVoxelNoiseComputeNode::FVoxelNoiseComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + Outputs[1].Get() = Inputs[1].Get(); + GetNoise().GradientPerturb_2D(Outputs[0].Get(), Outputs[1].Get(), Inputs[2].Get(), Inputs[3].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = TVoxelRange::FromList(Inputs[0].Get().Min - 2 * Inputs[3].Get().Max, Inputs[0].Get().Max + 2 * Inputs[3].Get().Max); + Outputs[1].Get() = TVoxelRange::FromList(Inputs[1].Get().Min - 2 * Inputs[3].Get().Max, Inputs[1].Get().Max + 2 * Inputs[3].Get().Max); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Inputs[0] + ";"); + Constructor.AddLine(Outputs[1] + " = " + Inputs[1] + ";"); + Constructor.AddLine(GetNoiseName() + ".GradientPerturb_2D(" + Outputs[0] + ", " + Outputs[1] + ", " + Inputs[2] + ", " + Inputs[3] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[0], *Inputs[0], *Inputs[3], *Inputs[0], *Inputs[3]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[1], *Inputs[1], *Inputs[3], *Inputs[1], *Inputs[3]); + } + + virtual int32 GetSeedInputIndex() const override { return 4; } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +TVoxelSharedPtr UVoxelNode_2DGradientPerturbFractal::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelNoiseFractalComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + using FVoxelNoiseFractalComputeNode::FVoxelNoiseFractalComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + Outputs[1].Get() = Inputs[1].Get(); + GetNoise().GradientPerturbFractal_2D(Outputs[0].Get(), Outputs[1].Get(), Inputs[2].Get(), GET_OCTAVES, Inputs[3].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = TVoxelRange::FromList(Inputs[0].Get().Min - 2 * Inputs[3].Get().Max, Inputs[0].Get().Max + 2 * Inputs[3].Get().Max); + Outputs[1].Get() = TVoxelRange::FromList(Inputs[1].Get().Min - 2 * Inputs[3].Get().Max, Inputs[1].Get().Max + 2 * Inputs[3].Get().Max); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Inputs[0] + ";"); + Constructor.AddLine(Outputs[1] + " = " + Inputs[1] + ";"); + Constructor.AddLine(GetNoiseName() + ".GradientPerturbFractal_2D(" + Outputs[0] + ", " + Outputs[1] + ", " + Inputs[2] + ", " + GET_OCTAVES_CPP + ", " + Inputs[3] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[0], *Inputs[0], *Inputs[3], *Inputs[0], *Inputs[3]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[1], *Inputs[1], *Inputs[3], *Inputs[1], *Inputs[3]); + } + + virtual int32 GetSeedInputIndex() const override { return 4; } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +TVoxelSharedPtr UVoxelNode_3DGradientPerturb::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelNoiseComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + using FVoxelNoiseComputeNode::FVoxelNoiseComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + Outputs[1].Get() = Inputs[1].Get(); + Outputs[2].Get() = Inputs[2].Get(); + GetNoise().GradientPerturb_3D(Outputs[0].Get(), Outputs[1].Get(), Outputs[2].Get(), Inputs[3].Get(), Inputs[4].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = TVoxelRange::FromList(Inputs[0].Get().Min - 2 * Inputs[4].Get().Max, Inputs[0].Get().Max + 2 * Inputs[4].Get().Max); + Outputs[1].Get() = TVoxelRange::FromList(Inputs[1].Get().Min - 2 * Inputs[4].Get().Max, Inputs[1].Get().Max + 2 * Inputs[4].Get().Max); + Outputs[2].Get() = TVoxelRange::FromList(Inputs[2].Get().Min - 2 * Inputs[4].Get().Max, Inputs[2].Get().Max + 2 * Inputs[4].Get().Max); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Inputs[0] + ";"); + Constructor.AddLine(Outputs[1] + " = " + Inputs[1] + ";"); + Constructor.AddLine(Outputs[2] + " = " + Inputs[2] + ";"); + Constructor.AddLine(GetNoiseName() + ".GradientPerturb_3D(" + Outputs[0] + ", " + Outputs[1] + ", " + Outputs[2] + ", " + Inputs[3] + ", " + Inputs[4] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[0], *Inputs[0], *Inputs[4], *Inputs[0], *Inputs[4]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[1], *Inputs[1], *Inputs[4], *Inputs[1], *Inputs[4]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[2], *Inputs[2], *Inputs[4], *Inputs[2], *Inputs[4]); + } + + virtual int32 GetSeedInputIndex() const override { return 5; } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +TVoxelSharedPtr UVoxelNode_3DGradientPerturbFractal::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelNoiseFractalComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + using FVoxelNoiseFractalComputeNode::FVoxelNoiseFractalComputeNode; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + Outputs[1].Get() = Inputs[1].Get(); + Outputs[2].Get() = Inputs[2].Get(); + GetNoise().GradientPerturbFractal_3D(Outputs[0].Get(), Outputs[1].Get(), Outputs[2].Get(), Inputs[3].Get(), GET_OCTAVES, Inputs[4].Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = TVoxelRange::FromList(Inputs[0].Get().Min - 2 * Inputs[4].Get().Max, Inputs[0].Get().Max + 2 * Inputs[4].Get().Max); + Outputs[1].Get() = TVoxelRange::FromList(Inputs[1].Get().Min - 2 * Inputs[4].Get().Max, Inputs[1].Get().Max + 2 * Inputs[4].Get().Max); + Outputs[2].Get() = TVoxelRange::FromList(Inputs[2].Get().Min - 2 * Inputs[4].Get().Max, Inputs[2].Get().Max + 2 * Inputs[4].Get().Max); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Inputs[0] + ";"); + Constructor.AddLine(Outputs[1] + " = " + Inputs[1] + ";"); + Constructor.AddLine(Outputs[2] + " = " + Inputs[2] + ";"); + Constructor.AddLine(GetNoiseName() + ".GradientPerturbFractal_3D(" + Outputs[0] + ", " + Outputs[1] + ", " + Outputs[2] + ", " + Inputs[3] + ", " + GET_OCTAVES_CPP + ", " + Inputs[4] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[0], *Inputs[0], *Inputs[4], *Inputs[0], *Inputs[4]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[1], *Inputs[1], *Inputs[4], *Inputs[1], *Inputs[4]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::FromList(%s.Min - 2 * %s.Max, %s.Max + 2 * %s.Max);"), *Outputs[2], *Inputs[2], *Inputs[4], *Inputs[2], *Inputs[4]); + } + + virtual int32 GetSeedInputIndex() const override { return 5; } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp new file mode 100644 index 00000000..c78e4834 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp @@ -0,0 +1,336 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGraphAssetNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelGeneratorSamplerNodes.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "VoxelTools/VoxelHardnessHandler.h" +#include "VoxelGraphGenerator.h" + +int32 UVoxelGraphAssetNode::GetMaxInputPins() const +{ + return GetMinInputPins(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelEditBaseComputeNode : public FVoxelDataComputeNode +{ +public: + FVoxelEditBaseComputeNode(const UVoxelGraphAssetNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , DefaultGenerator( + Node.DefaultGenerator.IsValid() + ? Node.DefaultGenerator.GetInstance(true) + : TVoxelSharedPtr()) + { + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + if (DefaultGenerator.IsValid()) + { + DefaultGenerator->Init(InitStruct); + } + } + +protected: + TVoxelSharedPtr DefaultGenerator; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EditGetValue::UVoxelNode_EditGetValue() +{ + SetInputs( + { "X", EC::Float, "X in global space. Use Global X" }, + { "Y", EC::Float, "Y in global space. Use Global Y" }, + { "Z", EC::Float, "Z in global space. Use Global Z" } + ); + SetOutputs( + EC::Float + ); +} + +TVoxelSharedPtr UVoxelNode_EditGetValue::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelEditBaseComputeNode + { + public: + using FVoxelEditBaseComputeNode::FVoxelEditBaseComputeNode; + + GENERATED_DATA_COMPUTE_NODE_BODY(); + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetPreviousGeneratorValue( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context, + DefaultGenerator.Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetPreviousGeneratorValue( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context, + DefaultGenerator.Get()); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = FVoxelNodeFunctions::GetPreviousGeneratorValue(%s, %s, %s, %s, nullptr);"), + *Outputs[0], + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = FVoxelNodeFunctions::GetPreviousGeneratorValue(%s, %s, %s, %s, nullptr);"), + *Outputs[0], + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EditGetMaterial::UVoxelNode_EditGetMaterial() +{ + SetInputs( + { "X", EC::Float, "X in global space. Use Global X" }, + { "Y", EC::Float, "Y in global space. Use Global Y" }, + { "Z", EC::Float, "Z in global space. Use Global Z" } + ); + SetOutputs( + EC::Material + ); +} + +TVoxelSharedPtr UVoxelNode_EditGetMaterial::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelEditBaseComputeNode + { + public: + using FVoxelEditBaseComputeNode::FVoxelEditBaseComputeNode; + + GENERATED_DATA_COMPUTE_NODE_BODY(); + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetPreviousGeneratorMaterial( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context, + DefaultGenerator.Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = FVoxelNodeFunctions::GetPreviousGeneratorMaterial(%s, %s, %s, %s, nullptr);"), + *Outputs[0], + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EditGetCustomOutput::UVoxelNode_EditGetCustomOutput() +{ + SetInputs( + { "X", EC::Float, "X in global space. Use Global X" }, + { "Y", EC::Float, "Y in global space. Use Global Y" }, + { "Z", EC::Float, "Z in global space. Use Global Z" } + ); + SetOutputs( + EC::Float + ); +} + +FText UVoxelNode_EditGetCustomOutput::GetTitle() const +{ + return FText::FromString("Get Previous Generator Custom Output: " + OutputName.ToString()); +} + +TVoxelSharedPtr UVoxelNode_EditGetCustomOutput::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelEditBaseComputeNode + { + public: + FLocalVoxelComputeNode(const UVoxelNode_EditGetCustomOutput& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelEditBaseComputeNode(Node, CompilationNode) + , OutputName(Node.OutputName) + { + } + + GENERATED_DATA_COMPUTE_NODE_BODY(); + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput( + OutputName, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context, + DefaultGenerator.Get()); + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput( + OutputName, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Context, + DefaultGenerator.Get()); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput(STATIC_FNAME(\"%s\"), %s, %s, %s, %s, nullptr);"), + *Outputs[0], + *OutputName.ToString(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = FVoxelNodeFunctions::GetPreviousGeneratorCustomOutput(STATIC_FNAME(\"%s\"), %s, %s, %s, %s, nullptr);"), + *Outputs[0], + *OutputName.ToString(), + *Inputs[0], + *Inputs[1], + *Inputs[2], + *FVoxelCppIds::Context); + } + + private: + const FName OutputName; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EditGetHardness::UVoxelNode_EditGetHardness() +{ + SetInputs( + EC::Material + ); + SetOutputs( + EC::Float + ); +} + +TVoxelSharedPtr UVoxelNode_EditGetHardness::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_EditGetHardness& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , HardnessHandlerVariable("TUniquePtr", UniqueName.ToString() + "_HardnessHandler") + { + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + if (InitStruct.World) + { + HardnessHandler = MakeUnique(*InitStruct.World); + } + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine("if (" + FVoxelCppIds::InitStruct + ".World)"); + Constructor.StartBlock(); + Constructor.AddLine( + HardnessHandlerVariable.CppName + + " = MakeUnique(*" + + FVoxelCppIds::InitStruct + + ".World);"); + Constructor.EndBlock(); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = + HardnessHandler.IsValid() + ? HardnessHandler->GetHardness(Inputs[0].Get()) + : 1.f; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = + HardnessHandler.IsValid() + ? TVoxelRange(0.f, 1000000.f) + : TVoxelRange(1.f, 1.f); + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = %s.IsValid() ? %s->GetHardness(%s) : 1.f;"), + *Outputs[0], + *HardnessHandlerVariable.CppName, + *Inputs[0]); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef( + TEXT("%s = %s.IsValid() ? TVoxelRange(0.f, 1000000.f) : TVoxelRange(1.f, 1.f);"), + *Outputs[0], + *HardnessHandlerVariable.CppName); + } + + private: + TUniquePtr HardnessHandler; + const FVoxelVariable HardnessHandlerVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp new file mode 100644 index 00000000..e9de089d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp @@ -0,0 +1,255 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "VoxelNodes/VoxelLocalVariables.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "Compilation/VoxelCompilationEnums.h" +#include "VoxelGraphErrorReporter.h" + +FLinearColor UVoxelGraphMacroInputOutputNode::GetColor() const +{ + return FVoxelNodeColors::ExecNode; +} + +bool UVoxelGraphMacroInputOutputNode::CanUserDeleteNode() const +{ + return false; +} + +bool UVoxelGraphMacroInputOutputNode::CanDuplicateNode() const +{ + return false; +} + +int32 UVoxelGraphMacroInputOutputNode::GetMaxInputPins() const +{ + return Pins.Num(); +} + +int32 UVoxelGraphMacroInputOutputNode::GetMinInputPins() const +{ + return Pins.Num(); +} + +EVoxelPinCategory UVoxelGraphMacroInputOutputNode::GetInputPinCategory(int32 PinIndex) const +{ + return Pins[PinIndex].Category; +} + +int32 UVoxelGraphMacroInputOutputNode::GetOutputPinsCount() const +{ + return Pins.Num(); +} + +EVoxelPinCategory UVoxelGraphMacroInputOutputNode::GetOutputPinCategory(int32 PinIndex) const +{ + return Pins[PinIndex].Category; +} + +TSharedPtr UVoxelGraphMacroInputOutputNode::GetCompilationNode() const +{ + return MakeShared(*this); +} + +#if WITH_EDITOR +void UVoxelGraphMacroInputOutputNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (IsA()) + { + for (auto& Pin : Pins) + { + Pin.bCustomDefaultValue = false; + } + } + + if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd) + { + const int32 EditedIndex = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_STRING_CHECKED(UVoxelGraphMacroInputOutputNode, Pins)); + if (Pins.IsValidIndex(EditedIndex)) + { + if (EditedIndex == 0) + { + // Most macros are using floats + Pins[EditedIndex].Category = EVoxelPinCategory::Float; + } + else + { + // Nicer when adding many pins + Pins[EditedIndex].Category = Pins[EditedIndex - 1].Category; + } + } + } + + Super::PostEditChangeProperty(PropertyChangedEvent); +} + +void UVoxelGraphMacroInputOutputNode::PostLoad() +{ + Super::PostLoad(); + + if (GraphNode && InputPins.Num() != OutputPins.Num()) + { + GraphNode->ReconstructNode(); + Graph->CompileVoxelNodesFromGraphNodes(); + } +} +#endif // WITH_EDITOR + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +inline FVoxelGraphMacroPin GetPin(UVoxelGraphMacro* Macro, EVoxelPinDirection Direction, int32 PinIndex) +{ + if (Macro) + { + if (auto* Node = Direction == EVoxelPinDirection::Input ? + static_cast(Macro->InputNode) : + static_cast(Macro->OutputNode)) + { + auto& Pins = Node->Pins; + if (Pins.IsValidIndex(PinIndex)) + { + return Pins[PinIndex]; + } + } + } + return FVoxelGraphMacroPin(); +} + +FText UVoxelGraphMacro::GetMacroName() const +{ + if (CustomName.IsEmpty()) + { + return FText::FromString(FName::NameToDisplayString(GetName(), false)); + } + else + { + return FText::FromString(CustomName); + } +} + +FText UVoxelGraphMacro::GetMacroCategory() const +{ + return CustomCategory.IsEmpty() ? VOXEL_LOCTEXT("Macro nodes") : FText::FromString(CustomCategory); +} + +TSharedPtr UVoxelGraphMacroNode::GetCompilationNode() const +{ + return MakeShared(*this); +} + +FText UVoxelGraphMacroNode::GetTitle() const +{ + return Macro ? Macro->GetMacroName() : Super::GetTitle(); +} + +FText UVoxelGraphMacroNode::GetTooltip() const +{ + return Macro ? FText::FromString(Macro->Tooltip) : Super::GetTooltip(); +} + +int32 UVoxelGraphMacroNode::GetMaxInputPins() const +{ + return Macro && Macro->InputNode ? Macro->InputNode->Pins.Num() : 0; +} + +int32 UVoxelGraphMacroNode::GetMinInputPins() const +{ + return Macro && Macro->InputNode ? Macro->InputNode->Pins.Num() : 0; +} + +int32 UVoxelGraphMacroNode::GetOutputPinsCount() const +{ + return Macro && Macro->OutputNode ? Macro->OutputNode->Pins.Num() : 0; +} + +FName UVoxelGraphMacroNode::GetInputPinName(int32 PinIndex) const +{ + return *GetPin(Macro, EVoxelPinDirection::Input, PinIndex).Name; +} + +FName UVoxelGraphMacroNode::GetOutputPinName(int32 PinIndex) const +{ + return *GetPin(Macro, EVoxelPinDirection::Output, PinIndex).Name; +} + +FString UVoxelGraphMacroNode::GetInputPinToolTip(int32 PinIndex) const +{ + return *GetPin(Macro, EVoxelPinDirection::Input, PinIndex).ToolTip; +} + +FString UVoxelGraphMacroNode::GetOutputPinToolTip(int32 PinIndex) const +{ + return *GetPin(Macro, EVoxelPinDirection::Output, PinIndex).ToolTip; +} + +EVoxelPinCategory UVoxelGraphMacroNode::GetInputPinCategory(int32 PinIndex) const +{ + return GetPin(Macro, EVoxelPinDirection::Input, PinIndex).Category; +} + +EVoxelPinCategory UVoxelGraphMacroNode::GetOutputPinCategory(int32 PinIndex) const +{ + return GetPin(Macro, EVoxelPinDirection::Output, PinIndex).Category; +} + +FString UVoxelGraphMacroNode::GetInputPinDefaultValue(int32 PinIndex) const +{ + const auto& Pin = GetPin(Macro, EVoxelPinDirection::Input, PinIndex); + return Pin.bCustomDefaultValue ? Pin.DefaultValue : Super::GetInputPinDefaultValue(PinIndex); +} + +inline bool ArePinsArraysSameNameAndCategory(const TArray& A, const TArray& B) +{ + if (A.Num() != B.Num()) + { + return false; + } + for (int32 Index = 0; Index < A.Num(); Index++) + { + if (A[Index].PinCategory != B[Index].PinCategory) + { + return false; + } + } + return true; +} + +void UVoxelGraphMacroNode::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + + if (!Macro) + { + ErrorReporter.AddMessageToNode(this, "invalid macro ref", EVoxelGraphNodeMessageType::Error); + } + else if (!Macro->InputNode || !Macro->OutputNode) + { + ErrorReporter.AddMessageToNode(this, "corrupted macro", EVoxelGraphNodeMessageType::Error); + } + else if (!ArePinsArraysSameNameAndCategory(InputPins, Macro->InputNode->OutputPins) || + !ArePinsArraysSameNameAndCategory(OutputPins, Macro->OutputNode->InputPins)) + { + ErrorReporter.AddError("Outdated macro: please right click it and press Reconstruct Node"); + ErrorReporter.AddMessageToNode(this, "outdated macro", EVoxelGraphNodeMessageType::Error); + } +} + +void UVoxelGraphMacroNode::ApplyParameters(const TMap& Parameters) +{ + // This might have unwanted behavior + if (Macro) + { + Macro->ApplyParameters(Parameters); + } +} + +void UVoxelGraphMacroNode::GetParameters(TArray& OutParameters) const +{ + if (Macro) + { + Macro->GetParameters(OutParameters); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp new file mode 100644 index 00000000..f09b58d6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp @@ -0,0 +1,163 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelHeightSplitterNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "NodeFunctions/VoxelMathNodeFunctions.h" + +UVoxelNode_HeightSplitter::UVoxelNode_HeightSplitter() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + +int32 UVoxelNode_HeightSplitter::GetMinInputPins() const +{ + return 1 + NumSplits * 2; +} + +int32 UVoxelNode_HeightSplitter::GetMaxInputPins() const +{ + return GetMinInputPins(); +} + +int32 UVoxelNode_HeightSplitter::GetOutputPinsCount() const +{ + return NumSplits + 1; +} + +FName UVoxelNode_HeightSplitter::GetInputPinName(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return "Height"; + } + PinIndex--; + if (PinIndex < 2 * NumSplits) + { + const int32 SplitIndex = PinIndex / 2; + if (PinIndex % 2 == 0) + { + return *FString::Printf(TEXT("Height %d"), SplitIndex); + } + else + { + return *FString::Printf(TEXT("Falloff %d"), SplitIndex); + } + } + return "Error"; +} + +FName UVoxelNode_HeightSplitter::GetOutputPinName(int32 PinIndex) const +{ + return *FString::Printf(TEXT("Layer %d"), PinIndex); +} + +FString UVoxelNode_HeightSplitter::GetInputPinDefaultValue(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return "0"; + } + PinIndex--; + + if (PinIndex % 2 == 1) + { + return "5"; + } + + PinIndex /= 2; + + const int32 PreviousPinIndex = 2 * (PinIndex - 1) + 1; + if (InputPins.IsValidIndex(PreviousPinIndex)) + { + return FString::SanitizeFloat(FCString::Atof(*InputPins[PreviousPinIndex].DefaultValue) + 10.f); + } + + return FString::SanitizeFloat(PinIndex * 10); +} + +class FVoxelNode_HeightSplitter_LocalVoxelComputeNode : public FVoxelDataComputeNode +{ +public: + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FVoxelNode_HeightSplitter_LocalVoxelComputeNode) + + FVoxelNode_HeightSplitter_LocalVoxelComputeNode(const UVoxelNode_HeightSplitter& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , NumSplits(Node.NumSplits) + { + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + ComputeImpl(Inputs, Outputs); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + ComputeImpl(Inputs, Outputs); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Inputs, Outputs, Constructor); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Inputs, Outputs, Constructor); + } + +private: + const int32 NumSplits; + + template + void ComputeImpl(TInputs Inputs, TOutputs Outputs) const + { + using T = typename TDecay())>::Type; + + TArray> InputsArray; + TArray> OutputsArray; + + for (int32 Split = 0; Split < NumSplits; Split++) + { + InputsArray.Add(Inputs[1 + 2 * Split + 0].template Get()); + InputsArray.Add(Inputs[1 + 2 * Split + 1].template Get()); + } + OutputsArray.SetNumUninitialized(NumSplits + 1); + + FVoxelMathNodeFunctions::HeightSplit(Inputs[0].template Get(), InputsArray, OutputsArray); + + for (int32 Layer = 0; Layer < NumSplits + 1; Layer++) + { + Outputs[Layer].template Get() = OutputsArray[Layer]; + } + } + void ComputeCppImpl(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const + { + Constructor.StartBlock(); + + Constructor.AddLinef(TEXT("TVoxelStaticArray<%s, %d> InputsArray;"), *Constructor.GetTypeString(EVoxelPinCategory::Float), 2 * NumSplits); + Constructor.AddLinef(TEXT("TVoxelStaticArray<%s, %d> OutputsArray;"), *Constructor.GetTypeString(EVoxelPinCategory::Float), NumSplits + 1); + + for (int32 Split = 0; Split < NumSplits; Split++) + { + Constructor.AddLinef(TEXT("InputsArray[%d] = %s;"), 2 * Split + 0, *Inputs[1 + 2 * Split + 0]); + Constructor.AddLinef(TEXT("InputsArray[%d] = %s;"), 2 * Split + 1, *Inputs[1 + 2 * Split + 1]); + } + + Constructor.AddLinef(TEXT("FVoxelMathNodeFunctions::HeightSplit(%s, InputsArray, OutputsArray);"), *Inputs[0]); + + for (int32 Layer = 0; Layer < NumSplits + 1; Layer++) + { + Constructor.AddLinef(TEXT("%s = OutputsArray[%d];"), *Outputs[Layer], Layer); + } + + Constructor.EndBlock(); + } +}; + +TVoxelSharedPtr UVoxelNode_HeightSplitter::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new FVoxelNode_HeightSplitter_LocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp new file mode 100644 index 00000000..14e434d3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp @@ -0,0 +1,156 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelHeightmapSamplerNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +UVoxelNode_HeightmapSampler::UVoxelNode_HeightmapSampler() +{ + SetInputs( + { "X", EC::Float, "X between 0 and heightmap width" }, + { "Y", EC::Float, "Y between 0 and heightmap height" }); + SetOutputs( + { "Height", EC::Float, "Height at position X Y" }, + { "Material", EC::Material, "Material at position X Y" }, + { "Min Height", EC::Float, "Min height of the entire heightmap" }, + { "Max Height", EC::Float, "Max height of the entire heightmap" }, + { "Size X", EC::Float, "Width of the heightmap. Affected by the asset XY Scale setting, so it may be a float" }, + { "Size Y", EC::Float, "Height of the heightmap. Affected by the asset XY Scale setting, so it may be a float" }); +} + +class FVoxelNode_HeightmapSampler_LocalVoxelComputeNode : public FVoxelDataComputeNode +{ +public: + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FVoxelNode_HeightmapSampler_LocalVoxelComputeNode); + + FVoxelNode_HeightmapSampler_LocalVoxelComputeNode(const UVoxelNode_HeightmapSampler& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , bFloat(Node.bFloatHeightmap) + , bCenter(Node.bCenter) + , SamplerType(Node.SamplerType) + , DataUINT16(bFloat ? nullptr : Node.HeightmapUINT16) + , DataFloat(bFloat ? Node.HeightmapFloat : nullptr) + , Variable(bFloat + ? MakeShared(Node, Node.HeightmapFloat) + : MakeShared(Node, Node.HeightmapUINT16)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + const auto Lambda = [&](auto& InData) + { + if (IsOutputUsed(0)) Outputs[0].Get() = InData.GetHeight(GetX(Inputs[0].Get(), InData), GetY(Inputs[1].Get(), InData), SamplerType); + if (IsOutputUsed(1)) Outputs[1].Get() = InData.GetMaterial(GetX(Inputs[0].Get(), InData), GetY(Inputs[1].Get(), InData), SamplerType); + if (IsOutputUsed(2)) Outputs[2].Get() = InData.GetMinHeight(); + if (IsOutputUsed(3)) Outputs[3].Get() = InData.GetMaxHeight(); + if (IsOutputUsed(4)) Outputs[4].Get() = InData.GetWidth(); + if (IsOutputUsed(5)) Outputs[5].Get() = InData.GetHeight(); + }; + if (bFloat) + { + Lambda(DataFloat); + } + else + { + Lambda(DataUINT16); + } + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + const auto Lambda = [&](auto& InData) + { + if (IsOutputUsed(0)) Outputs[0].Get() = InData.GetHeightRange(GetX(Inputs[0].Get(), InData), GetY(Inputs[1].Get(), InData), SamplerType); + if (IsOutputUsed(2)) Outputs[2].Get() = InData.GetMinHeight(); + if (IsOutputUsed(3)) Outputs[3].Get() = InData.GetMaxHeight(); + if (IsOutputUsed(4)) Outputs[4].Get() = InData.GetWidth(); + if (IsOutputUsed(5)) Outputs[5].Get() = InData.GetHeight(); + }; + if (bFloat) + { + Lambda(DataFloat); + } + else + { + Lambda(DataUINT16); + } + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + const FString SampleTypeString = SamplerType == EVoxelSamplerMode::Clamp ? "EVoxelSamplerMode::Clamp" : "EVoxelSamplerMode::Tile"; + if (IsOutputUsed(0)) Constructor.AddLinef(TEXT("%s = %s.GetHeight(%s, %s, %s);"), *Outputs[0], *Variable->CppName, *GetX(Inputs[0]), *GetY(Inputs[1]), *SampleTypeString); + if (IsOutputUsed(1)) Constructor.AddLinef(TEXT("%s = %s.GetMaterial(%s, %s, %s);"), *Outputs[1], *Variable->CppName, *GetX(Inputs[0]), *GetY(Inputs[1]), *SampleTypeString); + if (IsOutputUsed(2)) Constructor.AddLinef(TEXT("%s = %s.GetMinHeight();"), *Outputs[2], *Variable->CppName); + if (IsOutputUsed(3)) Constructor.AddLinef(TEXT("%s = %s.GetMaxHeight();"), *Outputs[3], *Variable->CppName); + if (IsOutputUsed(4)) Constructor.AddLinef(TEXT("%s = %s.GetWidth();"), *Outputs[4], *Variable->CppName); + if (IsOutputUsed(5)) Constructor.AddLinef(TEXT("%s = %s.GetHeight();"), *Outputs[5], *Variable->CppName); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + const FString SampleTypeString = SamplerType == EVoxelSamplerMode::Clamp ? "EVoxelSamplerMode::Clamp" : "EVoxelSamplerMode::Tile"; + if (IsOutputUsed(0)) Constructor.AddLinef(TEXT("%s = %s.GetHeightRange(%s, %s, %s);"), *Outputs[0], *Variable->CppName, *GetX(Inputs[0]), *GetY(Inputs[1]), *SampleTypeString); + if (IsOutputUsed(2)) Constructor.AddLinef(TEXT("%s = %s.GetMinHeight();"), *Outputs[2], *Variable->CppName); + if (IsOutputUsed(3)) Constructor.AddLinef(TEXT("%s = %s.GetMaxHeight();"), *Outputs[3], *Variable->CppName); + if (IsOutputUsed(4)) Constructor.AddLinef(TEXT("%s = %s.GetWidth();"), *Outputs[4], *Variable->CppName); + if (IsOutputUsed(5)) Constructor.AddLinef(TEXT("%s = %s.GetHeight();"), *Outputs[5], *Variable->CppName); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + +private: + const bool bFloat; + const bool bCenter; + const EVoxelSamplerMode SamplerType; + const TVoxelHeightmapAssetSamplerWrapper DataUINT16; + const TVoxelHeightmapAssetSamplerWrapper DataFloat; + const TSharedRef Variable; + + template + FORCEINLINE T GetX(T X, const TData& Data) const + { + return bCenter ? X - Data.GetWidth() / 2 : X; + } + template + FORCEINLINE T GetY(T Y, const TData& Data) const + { + return bCenter ? Y - Data.GetHeight() / 2 : Y; + } + + FString GetX(const FString& X) const + { + return bCenter ? FString::Printf(TEXT("%s - %s.GetWidth() / 2"), *X, *Variable->CppName) : X; + } + FString GetY(const FString& Y) const + { + return bCenter ? FString::Printf(TEXT("%s - %s.GetHeight() / 2"), *Y, *Variable->CppName) : Y; + } +}; + +TVoxelSharedPtr UVoxelNode_HeightmapSampler::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new FVoxelNode_HeightmapSampler_LocalVoxelComputeNode(*this, InCompilationNode)); +} + +FText UVoxelNode_HeightmapSampler::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Heightmap: {0}"), Super::GetTitle()); +} + +void UVoxelNode_HeightmapSampler::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + if ((bFloatHeightmap && !HeightmapFloat) || (!bFloatHeightmap && !HeightmapUINT16)) + { + ErrorReporter.AddMessageToNode(this, "invalid heightmap", EVoxelGraphNodeMessageType::Error); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp new file mode 100644 index 00000000..d9582961 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp @@ -0,0 +1,25 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelIfNode.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" + +UVoxelNode_If::UVoxelNode_If() +{ + SetInputs(EC::Exec, EC::Boolean); + SetOutputs( + { "True", EC::Exec, "Branch used if condition is true" }, + { "False", EC::Exec, "Branch used if condition is false" }); + SetColor(FVoxelNodeColors::ExecNode); +} + +TSharedPtr UVoxelNode_If::GetCompilationNode() const +{ + return MakeShared(*this); +} + +TVoxelSharedPtr UVoxelNode_If::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new FVoxelIfComputeNode(*this, InCompilationNode)); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp new file mode 100644 index 00000000..e3032d32 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp @@ -0,0 +1,336 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelLocalVariables.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "EdGraph/EdGraphNode.h" +#include "Misc/App.h" + +UVoxelLocalVariableDeclaration* UVoxelLocalVariableBase::FindDeclarationInArray(const FGuid& VariableGuid, const TArray& Nodes) const +{ + for (UVoxelNode* Node : Nodes) + { + auto* Declaration = Cast(Node); + if (Declaration && this != Declaration && Declaration->VariableGuid == VariableGuid) + { + return Declaration; + } + } + return nullptr; +} + +UVoxelLocalVariableDeclaration* UVoxelLocalVariableBase::FindDeclarationInGraph(const FGuid& VariableGuid) const +{ + UVoxelLocalVariableDeclaration* Declaration = nullptr; + if (Graph) + { + Declaration = FindDeclarationInArray(VariableGuid, Graph->AllNodes); + } + return Declaration; +} + +EVoxelPinCategory UVoxelLocalVariableDeclaration::GetCategory() const +{ + switch (Category) + { + case EVoxelPortalNodePinCategory::Boolean: + return EVoxelPinCategory::Boolean; + case EVoxelPortalNodePinCategory::Int: + return EVoxelPinCategory::Int; + case EVoxelPortalNodePinCategory::Float: + return EVoxelPinCategory::Float; + case EVoxelPortalNodePinCategory::Material: + return EVoxelPinCategory::Material; + case EVoxelPortalNodePinCategory::Color: + return EVoxelPinCategory::Color; + case EVoxelPortalNodePinCategory::Seed: + return EVoxelPinCategory::Seed; + default: + check(false); + return EVoxelPinCategory::Boolean; + } +} + +void UVoxelLocalVariableDeclaration::SetCategory(EVoxelPinCategory NewCategory) +{ + switch (NewCategory) + { + case EVoxelPinCategory::Boolean: + Category = EVoxelPortalNodePinCategory::Boolean; + break; + case EVoxelPinCategory::Int: + Category = EVoxelPortalNodePinCategory::Int; + break; + case EVoxelPinCategory::Float: + Category = EVoxelPortalNodePinCategory::Float; + break; + case EVoxelPinCategory::Material: + Category = EVoxelPortalNodePinCategory::Material; + break; + case EVoxelPinCategory::Color: + Category = EVoxelPortalNodePinCategory::Color; + break; + case EVoxelPinCategory::Seed: + Category = EVoxelPortalNodePinCategory::Seed; + break; + case EVoxelPinCategory::Wildcard: + case EVoxelPinCategory::Exec: + case EVoxelPinCategory::Vector: + default: + ensure(false); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelLocalVariableDeclaration::PostInitProperties() +{ + Super::PostInitProperties(); + // Init the GUID + UpdateVariableGuid(false, false); +} + +void UVoxelLocalVariableDeclaration::PostLoad() +{ + Super::PostLoad(); + // Init the GUID + UpdateVariableGuid(false, false); +} + +void UVoxelLocalVariableDeclaration::PostDuplicate(bool bDuplicateForPIE) +{ + Super::PostDuplicate(bDuplicateForPIE); + + // We do not force a guid regen here because this function is used when the Material Editor makes a copy of a material to edit. + // If we forced a GUID regen, it would cause all of the guids for a material to change every time a material was edited. + UpdateVariableGuid(false, true); +} + +#if WITH_EDITOR +void UVoxelLocalVariableDeclaration::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty) + { + const FName PropertyName = PropertyChangedEvent.MemberProperty->GetFName(); + if (PropertyName == GET_MEMBER_NAME_STATIC(UVoxelLocalVariableDeclaration, Name)) + { + MakeNameUnique(); + } + else if (PropertyName == GET_MEMBER_NAME_STATIC(UVoxelLocalVariableDeclaration, Category)) + { + if (Graph && GraphNode) + { + GraphNode->ReconstructNode(); + for (auto* Node : Graph->AllNodes) + { + auto* Usage = Cast(Node); + if (Usage && Usage->Declaration == this && Usage->GraphNode) + { + Usage->GraphNode->ReconstructNode(); + } + } + } + } + } +} +#endif // WITH_EDITOR + +EVoxelPinCategory UVoxelLocalVariableDeclaration::GetInputPinCategory(int32 PinIndex) const +{ + return GetCategory(); +} + +TSharedPtr UVoxelLocalVariableDeclaration::GetCompilationNode() const +{ + return MakeShared(*this); +} + +void UVoxelLocalVariableDeclaration::PostCopyNode(const TArray& CopiedNodes) +{ + Super::PostCopyNode(CopiedNodes); + + // Only force regeneration of Guid if there's already a variable with the same one + if (FindDeclarationInGraph(VariableGuid)) + { + // Update Guid, and update the copied usages accordingly + const FGuid OldGuid = VariableGuid; + UpdateVariableGuid(true, true); + for (UVoxelNode* Node : CopiedNodes) + { + auto* Usage = Cast(Node); + if (Usage && Usage->DeclarationGuid == OldGuid) + { + Usage->Declaration = this; + Usage->DeclarationGuid = VariableGuid; + } + } + + // Find a new name + MakeNameUnique(); + } + else + { + // If there's no existing variable with this GUID, only create it if needed + UpdateVariableGuid(false, true); + } +} + +FString UVoxelLocalVariableDeclaration::GetEditableName() const +{ + return Name.ToString(); +} + +void UVoxelLocalVariableDeclaration::SetEditableName(const FString& NewName) +{ + Name = *NewName; + MakeNameUnique(); +#if WITH_EDITOR + for (UVoxelNode* Node : Graph->AllNodes) + { + auto* Usage = Cast(Node); + if (Usage && Usage->Declaration == this && Usage->GraphNode) + { + Usage->GraphNode->ReconstructNode(); + } + } +#endif +} + +void UVoxelLocalVariableDeclaration::UpdateVariableGuid(bool bForceGeneration, bool bAllowMarkingPackageDirty) +{ + // If we are in the editor, and we don't have a valid GUID yet, generate one. + if (GIsEditor && !FApp::IsGame()) + { + if (bForceGeneration || !VariableGuid.IsValid()) + { + VariableGuid = FGuid::NewGuid(); + + if (bAllowMarkingPackageDirty) + { + MarkPackageDirty(); + } + } + } +} + +void UVoxelLocalVariableDeclaration::MakeNameUnique() +{ + if (Graph) + { + int32 NameIndex = 1; + bool bResultNameIndexValid; + FName PotentialName; + + // Find an available unique name + do + { + PotentialName = Name; + if (NameIndex != 1) + { + PotentialName.SetNumber(NameIndex); + } + + bResultNameIndexValid = true; + for (UVoxelNode* Node : Graph->AllNodes) + { + auto* OtherDeclaration = Cast(Node); + if (OtherDeclaration && OtherDeclaration != this && OtherDeclaration->Name == PotentialName) + { + bResultNameIndexValid = false; + break; + } + } + + NameIndex++; + } while (!bResultNameIndexValid); + + Name = PotentialName; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelLocalVariableUsage::PostLoad() +{ + Super::PostLoad(); + if (Selector_DEPRECATED.Input.IsValid()) + { + MarkPackageDirty(); + Declaration = Selector_DEPRECATED.Input.Get(); + if (Declaration && !DeclarationGuid.IsValid()) + { + if (!Declaration->VariableGuid.IsValid()) + { + Declaration->MarkPackageDirty(); + Declaration->VariableGuid = FGuid::NewGuid(); + } + DeclarationGuid = Declaration->VariableGuid; + } + } +} + +int32 UVoxelLocalVariableUsage::GetOutputPinsCount() const +{ + return 1; +} + +EVoxelPinCategory UVoxelLocalVariableUsage::GetOutputPinCategory(int32 PinIndex) const +{ + return Declaration ? Declaration->GetCategory() : EVoxelPinCategory::Float; +} + +FText UVoxelLocalVariableUsage::GetTitle() const +{ + return Declaration ? FText::FromName(Declaration->Name) : FText(); +} + +TSharedPtr UVoxelLocalVariableUsage::GetCompilationNode() const +{ + return MakeShared(*this); +} + +void UVoxelLocalVariableUsage::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + if (!IsDeclarationValid()) + { + ErrorReporter.AddMessageToNode(this, "invalid variable", EVoxelGraphNodeMessageType::Error); + } +} + +void UVoxelLocalVariableUsage::PostCopyNode(const TArray& CopiedNodes) +{ + Super::PostCopyNode(CopiedNodes); + + ensure(!Declaration || Declaration->VariableGuid == DeclarationGuid); + + if (!Declaration) + { + // First try to find the declaration in the copied nodes + Declaration = FindDeclarationInArray(DeclarationGuid, CopiedNodes); + if (!Declaration) + { + // If unsuccessful, try to find it in the whole graph + Declaration = FindDeclarationInGraph(DeclarationGuid); + } + if (Declaration) + { + // Save that Declaration change + MarkPackageDirty(); + } + } +} + +bool UVoxelLocalVariableUsage::IsDeclarationValid() const +{ + // Deleted expressions are marked as pending kill (see FVoxelGraphEditorToolkit::DeleteSelectedNodes) + return Declaration && !Declaration->IsPendingKill(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp new file mode 100644 index 00000000..5899ada4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp @@ -0,0 +1,58 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelMaterialNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +UVoxelNode_GetColor::UVoxelNode_GetColor() +{ + SetInputs({ "Material", EC::Material, "Material" }); + SetOutputs({ "Color", EC::Color, "Material color" }); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GetColor, + DEFINE_INPUTS(FVoxelMaterial), + DEFINE_OUTPUTS(FColor), + _O0 = FVoxelNodeFunctions::ColorFromMaterial(_I0); +) + +UVoxelNode_GetIndex::UVoxelNode_GetIndex() +{ + SetInputs({ "Material", EC::Material, "Material" }); + SetOutputs( + { "Index", EC::Int, "Index between 0 and 255" }); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GetIndex, + DEFINE_INPUTS(FVoxelMaterial), + DEFINE_OUTPUTS(int32, v_flt, v_flt, v_flt), + _O0 = FVoxelNodeFunctions::SingleIndexFromMaterial(_I0); +) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetUVChannel::UVoxelNode_GetUVChannel() +{ + SetInputs( + { "Material", EC::Material, "The material" }, + { "Channel", EC::Int, "The UV channel", "", {0, 255} }); + SetOutputs( + { "U", EC::Float, "U coordinate, between 0 and 1" }, + { "V", EC::Float, "V coordinate, between 0 and 1" }); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_GetUVChannel, + DEFINE_INPUTS(FVoxelMaterial, int32), + DEFINE_OUTPUTS(v_flt, v_flt), + FVoxelNodeFunctions::GetUVChannelFromMaterial(_I0, _I1, _O0, _O1); +) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp new file mode 100644 index 00000000..07f5df20 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp @@ -0,0 +1,973 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelMathNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "NodeFunctions/VoxelMathNodeFunctions.h" + +UVoxelNode_FMax::UVoxelNode_FMax() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} + +GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(UVoxelNode_FMax, FVoxelNodeFunctions::Max, v_flt) + +UVoxelNode_FMin::UVoxelNode_FMin() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(UVoxelNode_FMin, FVoxelNodeFunctions::Min, v_flt) + +UVoxelNode_IMax::UVoxelNode_IMax() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(UVoxelNode_IMax, FVoxelNodeFunctions::Max, int32) + +UVoxelNode_IMin::UVoxelNode_IMin() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(UVoxelNode_IMin, FVoxelNodeFunctions::Min, int32) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_FAdd::UVoxelNode_FAdd() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_FAdd, +, v_flt) + +UVoxelNode_FMultiply::UVoxelNode_FMultiply() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_FMultiply, *, v_flt) + +UVoxelNode_FSubstract::UVoxelNode_FSubstract() +{ + SetInputs(EC::Float, EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FSubstract, + DEFINE_INPUTS(v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = _I0 - _I1; +) + +UVoxelNode_FDivide::UVoxelNode_FDivide() +{ + SetInputs({ "", EC::Float, "", "0" }, { "", EC::Float, "", "1" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FDivide, + DEFINE_INPUTS(v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = _I0 / _I1; +) + +UVoxelNode_IAdd::UVoxelNode_IAdd() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_IAdd, +, int32) + +UVoxelNode_IMultiply::UVoxelNode_IMultiply() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_IMultiply, *, int32) + +UVoxelNode_ISubstract::UVoxelNode_ISubstract() +{ + SetInputs(EC::Int, EC::Int); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_ISubstract, + DEFINE_INPUTS(int32, int32), + DEFINE_OUTPUTS(int32), + _O0 = _I0 - _I1; +) + +UVoxelNode_IDivide::UVoxelNode_IDivide() +{ + SetInputs({ "", EC::Int, "", "0" }, { "", EC::Int, "", "1" }); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_IDivide, + DEFINE_INPUTS(int32, int32), + DEFINE_OUTPUTS(int32), + _O0 = _I1 == 0 ? 0 : _I0 / _I1; +) + +UVoxelNode_ILeftBitShift::UVoxelNode_ILeftBitShift() +{ + SetInputs({ "", EC::Int, "", "0" }, { "", EC::Int, "", "1" }); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_ILeftBitShift, + DEFINE_INPUTS(int32, int32), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::LeftShift(_I0, _I1); +) + +UVoxelNode_IRightBitShift::UVoxelNode_IRightBitShift() +{ + SetInputs({ "", EC::Int, "", "0" }, { "", EC::Int, "", "1" }); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_IRightBitShift, + DEFINE_INPUTS(int32, int32), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::RightShift(_I0, _I1); +) + +UVoxelNode_FloatOfInt::UVoxelNode_FloatOfInt() +{ + SetInputs(EC::Int); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FloatOfInt, + DEFINE_INPUTS(int32), + DEFINE_OUTPUTS(v_flt), + _O0 = _I0; +) + +UVoxelNode_Round::UVoxelNode_Round() +{ + SetInputs(EC::Float); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Round, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::RoundToInt(_I0); +) + +UVoxelNode_Lerp::UVoxelNode_Lerp() +{ + SetInputs( + { "A", EC::Float, "A" }, + { "B", EC::Float, "B" }, + { "Alpha", EC::Float, "Alpha" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Lerp, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Lerp(_I0, _I1, _I2); +) + +UVoxelNode_SafeLerp::UVoxelNode_SafeLerp() +{ + SetInputs( + { "A", EC::Float, "A" }, + { "B", EC::Float, "B" }, + { "Alpha", EC::Float, "Alpha" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SafeLerp, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::SafeLerp(_I0, _I1, _I2); +) + +UVoxelNode_SmoothStep::UVoxelNode_SmoothStep() +{ + SetInputs( + { "A", EC::Float, "Minimum value of X", "0" }, + { "B", EC::Float, "Maximum value of X", "1" }, + { "X", EC::Float, "Parameter" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SmoothStep, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelMathNodeFunctions::SmoothStep(_I0, _I1, _I2); +) + +UVoxelNode_Clamp::UVoxelNode_Clamp() +{ + SetInputs( + { "Value", EC::Float, "Value to clamp" }, + { "Min", EC::Float, "Min value", "0" }, + { "Max", EC::Float, "Max value", "1" }); + SetOutputs(EC::Float); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Clamp, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Clamp(_I0, _I1, _I2); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_BAnd::UVoxelNode_BAnd() +{ + SetInputs(EC::Boolean); + SetOutputs(EC::Boolean); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_BAnd, &&, bool) + +UVoxelNode_BOr::UVoxelNode_BOr() +{ + SetInputs(EC::Boolean); + SetOutputs(EC::Boolean); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} +GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(UVoxelNode_BOr, || , bool) + +UVoxelNode_BNot::UVoxelNode_BNot() +{ + SetInputs(EC::Boolean); + SetOutputs(EC::Boolean); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_BNot, + DEFINE_INPUTS(bool), + DEFINE_OUTPUTS(bool), + _O0 = !_I0; +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SwitchInt::UVoxelNode_SwitchInt() +{ + SetInputs( + { "A", EC::Int, "A" }, + { "B", EC::Int, "B" }, + { "Pick A", EC::Boolean, "Condition" }); + SetOutputs(EC::Int); +} +TSharedPtr UVoxelNode_SwitchInt::GetCompilationNode() const +{ + return MakeShared(*this); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SwitchInt, + DEFINE_INPUTS(int32, int32, bool), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::Switch(_I0, _I1, _I2); +) + +UVoxelNode_SwitchFloat::UVoxelNode_SwitchFloat() +{ + SetInputs( + { "A", EC::Float, "A" }, + { "B", EC::Float, "B" }, + { "Pick A", EC::Boolean, "Condition" }); + SetOutputs(EC::Float); +} +TSharedPtr UVoxelNode_SwitchFloat::GetCompilationNode() const +{ + return MakeShared(*this); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SwitchFloat, + DEFINE_INPUTS(v_flt, v_flt, bool), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Switch(_I0, _I1, _I2); +) + +UVoxelNode_SwitchColor::UVoxelNode_SwitchColor() +{ + SetInputs( + { "A", EC::Color, "A" }, + { "B", EC::Color, "B" }, + { "Pick A", EC::Boolean, "Condition" }); + SetOutputs(EC::Color); +} +TSharedPtr UVoxelNode_SwitchColor::GetCompilationNode() const +{ + return MakeShared(*this); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SwitchColor, + DEFINE_INPUTS(FColor, FColor, bool), + DEFINE_OUTPUTS(FColor), + _O0 = FVoxelNodeFunctions::Switch(_I0, _I1, _I2); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_1MinusX::UVoxelNode_1MinusX() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_1MinusX, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = 1 - _I0; +) + +UVoxelNode_OneOverX::UVoxelNode_OneOverX() +{ + SetInputs({ "", EC::Float, "", "1" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_OneOverX, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::OneOverX(_I0); +) + +UVoxelNode_MinusX::UVoxelNode_MinusX() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_MinusX, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = _I0 * -1; +) + +UVoxelNode_Sqrt::UVoxelNode_Sqrt() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Sqrt, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Sqrt(_I0); +) + +UVoxelNode_Pow::UVoxelNode_Pow() +{ + SetInputs(EC::Float, EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Pow, + DEFINE_INPUTS(v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Pow(_I0, _I1); +) + +UVoxelNode_IMod::UVoxelNode_IMod() +{ + SetInputs(EC::Int, EC::Int); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_IMod, + DEFINE_INPUTS(int32, int32), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::Mod(_I0, _I1); +) + +UVoxelNode_FMod::UVoxelNode_FMod() +{ + SetInputs(EC::Float, EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FMod, + DEFINE_INPUTS(v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Fmod(_I0, _I1); +) + +UVoxelNode_FAbs::UVoxelNode_FAbs() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FAbs, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Abs(_I0); +) + +UVoxelNode_IAbs::UVoxelNode_IAbs() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_IAbs, + DEFINE_INPUTS(int32), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::Abs(_I0); +) + +UVoxelNode_Ceil::UVoxelNode_Ceil() +{ + SetInputs(EC::Float); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Ceil, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::CeilToInt(_I0); +) + +UVoxelNode_Floor::UVoxelNode_Floor() +{ + SetInputs(EC::Float); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Floor, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::FloorToInt(_I0); +) + +UVoxelNode_VectorLength::UVoxelNode_VectorLength() +{ + SetInputs( + { "X", EC::Float, "Z" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_VectorLength, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::VectorLength(_I0, _I1, _I2); +) + +UVoxelNode_Fraction::UVoxelNode_Fraction() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Fraction, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Fractional(_I0); +) + +UVoxelNode_FSign::UVoxelNode_FSign() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_FSign, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Sign(_I0); +) + +UVoxelNode_ISign::UVoxelNode_ISign() +{ + SetInputs(EC::Int); + SetOutputs(EC::Int); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_ISign, + DEFINE_INPUTS(int32), + DEFINE_OUTPUTS(int32), + _O0 = FVoxelNodeFunctions::Sign(_I0); +) + +UVoxelNode_InvSqrt::UVoxelNode_InvSqrt() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_InvSqrt, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::InvSqrt(_I0); +) + +UVoxelNode_Loge::UVoxelNode_Loge() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Loge, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Loge(_I0); +) + +UVoxelNode_Exp::UVoxelNode_Exp() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Exp, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Exp(_I0); +) + +UVoxelNode_Sin::UVoxelNode_Sin() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Sin, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Sin(_I0); +) + +UVoxelNode_Asin::UVoxelNode_Asin() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Asin, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Asin(_I0); +) + +UVoxelNode_Sinh::UVoxelNode_Sinh() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Sinh, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Sinh(_I0); +) + +UVoxelNode_Cos::UVoxelNode_Cos() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Cos, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Cos(_I0); +) + +UVoxelNode_Acos::UVoxelNode_Acos() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Acos, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Acos(_I0); +) + +UVoxelNode_SinCos::UVoxelNode_SinCos() +{ + SetInputs(EC::Float); + AddOutput("Sin", "Sinus"); + AddOutput("Cos", "Cosinus"); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_SinCos, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt, v_flt), + FVoxelNodeFunctions::SinCos(_I0, _O0, _O1); +) + +UVoxelNode_Tan::UVoxelNode_Tan() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Tan, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Tan(_I0); +) + +UVoxelNode_Atan::UVoxelNode_Atan() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Atan, + DEFINE_INPUTS(v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Atan(_I0); +) + +UVoxelNode_Atan2::UVoxelNode_Atan2() +{ + SetInputs( + { "Y", EC::Float, "Y" }, + { "X", EC::Float, "X" }); + SetOutputs(EC::Float); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Atan2, + DEFINE_INPUTS(v_flt, v_flt), + DEFINE_OUTPUTS(v_flt), + _O0 = FVoxelNodeFunctions::Atan2(_I0, _I1); +) + +UVoxelNode_VectorRotateAngleAxis::UVoxelNode_VectorRotateAngleAxis() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }, + { "Axis X", EC::Float, "Axis X" }, + { "Axis Y", EC::Float, "Axis Y" }, + { "Axis Z", EC::Float, "Axis Z" }, + { "Angle", EC::Float, "Angle in degrees" } + ); + SetOutputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" } + ); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_VectorRotateAngleAxis, + DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::VectorRotateAngleAxis(_I0, _I1, _I2, _I3, _I4, _I5, _I6, _O0, _O1, _O2); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_BreakColorInt::UVoxelNode_BreakColorInt() +{ + SetInputs({ "Color", EC::Color, "Color" }); + SetOutputs( + { "R", EC::Int, "Red between 0 and 255" }, + { "G", EC::Int, "Green between 0 and 255" }, + { "B", EC::Int, "Blue between 0 and 255" }, + { "A", EC::Int, "Alpha between 0 and 255" } + ); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_BreakColorInt, + DEFINE_INPUTS(FColor), + DEFINE_OUTPUTS(int32, int32, int32, int32), + FVoxelNodeFunctions::BreakColor(_I0, _O0, _O1, _O2, _O3); +) + +UVoxelNode_BreakColorFloat::UVoxelNode_BreakColorFloat() +{ + SetInputs({ "Color", EC::Color, "Color" }); + SetOutputs( + { "R",EC::Float, "Red between 0 and 1" }, + { "G",EC::Float, "Green between 0 and 1" }, + { "B",EC::Float, "Blue between 0 and 1" }, + { "A",EC::Float, "Alpha between 0 and 1" } + ); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_BreakColorFloat, + DEFINE_INPUTS(FColor), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt, v_flt), + FVoxelNodeFunctions::BreakColorFloat(_I0, _O0, _O1, _O2, _O3); +) + +UVoxelNode_MakeColorInt::UVoxelNode_MakeColorInt() +{ + SetInputs( + { "R",EC::Int , "Red between 0 and 255" , "", {0, 255} }, + { "G",EC::Int , "Green between 0 and 255", "", {0, 255} }, + { "B",EC::Int , "Blue between 0 and 255" , "", {0, 255} }, + { "A",EC::Int , "Alpha between 0 and 255", "", {0, 255} }); + SetOutputs({ "Color", EC::Color, "Color" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_MakeColorInt, + DEFINE_INPUTS(int32, int32, int32, int32), + DEFINE_OUTPUTS(FColor), + _O0 = FVoxelNodeFunctions::MakeColor(_I0, _I1, _I2, _I3); +) + +UVoxelNode_MakeColorFloat::UVoxelNode_MakeColorFloat() +{ + SetInputs( + { "R",EC::Float , "Red between 0 and 1" , "", {0, 1} }, + { "G",EC::Float , "Green between 0 and 1", "", {0, 1} }, + { "B",EC::Float , "Blue between 0 and 1" , "", {0, 1} }, + { "A",EC::Float , "Alpha between 0 and 1", "", {0, 1} }); + SetOutputs({ "Color", EC::Color, "Color" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_MakeColorFloat, + DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(FColor), + _O0 = FVoxelNodeFunctions::MakeColorFloat(_I0, _I1, _I2, _I3); +) + +UVoxelNode_RGBToHSV::UVoxelNode_RGBToHSV() +{ + SetInputs( + { "R", EC::Float , "Red" }, + { "G", EC::Float , "Green" }, + { "B", EC::Float , "Blue" }); + SetOutputs( + { "H", EC::Float , "Hue" }, + { "S", EC::Float , "Saturation" }, + { "V", EC::Float , "Value" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_RGBToHSV, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::RGBToHSV(_I0, _I1, _I2, _O0, _O1, _O2); +) + +UVoxelNode_HSVToRGB::UVoxelNode_HSVToRGB() +{ + SetInputs( + { "H", EC::Float , "Hue between 0 and 360", "0", { 0, 360 } }, + { "S", EC::Float , "Saturation between 0 and 1", "1", { 0, 1 } }, + { "V", EC::Float , "Value", "1" }); + SetOutputs( + { "R", EC::Float , "Red" }, + { "G", EC::Float , "Green" }, + { "B", EC::Float , "Blue" }); +} +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_HSVToRGB, + DEFINE_INPUTS(v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelNodeFunctions::HSVToRGB(_I0, _I1, _I2, _O0, _O1, _O2); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_InverseTransformPositionXZ::UVoxelNode_InverseTransformPositionXZ() +{ + SetInputs( + { "X.X", EC::Float , "X component of the new basis X vector", "1" }, + { "X.Y", EC::Float , "Y component of the new basis X vector", "0" }, + { "X.Z", EC::Float , "Z component of the new basis X vector", "0" }, + { "Z.X", EC::Float , "X component of the new basis Z vector", "0" }, + { "Z.Y", EC::Float , "Y component of the new basis Z vector", "0" }, + { "Z.Z", EC::Float , "Z component of the new basis Z vector", "1" }, + { "X", EC::Float , "X component of the vector to transform" }, + { "Y", EC::Float , "Y component of the vector to transform" }, + { "Z", EC::Float , "Z component of the vector to transform" }); + SetOutputs( + { "X", EC::Float, "X component" }, + { "Y", EC::Float, "Y component" }, + { "Z", EC::Float, "Z component" }); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_InverseTransformPositionXZ, + DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt), + DEFINE_OUTPUTS(v_flt, v_flt, v_flt), + FVoxelMathNodeFunctions::InverseTransformPositionXZ(_I0, _I1, _I2, _I3, _I4, _I5, _I6, _I7, _I8, _O0, _O1, _O2); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_Pi::UVoxelNode_Pi() +{ + SetOutputs(EC::Float); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_Pi, + NO_INPUTS, + DEFINE_OUTPUTS(v_flt), + _O0 = v_flt(3.1415926535897932384626433832795); +) + +////////////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_NormalizeSum::UVoxelNode_NormalizeSum() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} + +void UVoxelNode_NormalizeSum::OnInputPinCountModified() +{ +#if WITH_EDITOR + if (GraphNode) + { + GraphNode->ReconstructNode(); + } +#endif +} + +int32 UVoxelNode_NormalizeSum::GetOutputPinsCount() const +{ + return FMath::Max(2, InputPinCount); // Else the default object has 0 output pins and doesn't show up in context sensitive menu +} + +TVoxelSharedPtr UVoxelNode_NormalizeSum::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_NormalizeSum& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + { + check(InputCount == OutputCount); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + v_flt Sum = 0; + for (int32 Index = 0; Index < InputCount; Index++) + { + Sum += FMath::Max(0, Inputs[Index].Get()); + } + if (Sum == 0) Sum = 1; + for (int32 Index = 0; Index < InputCount; Index++) + { + Outputs[Index].Get() = FMath::Max(0, Inputs[Index].Get()) / Sum; + } + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange & Context) const override + { + TVoxelRange Sum = 0; + for (int32 Index = 0; Index < InputCount; Index++) + { + Sum += FVoxelNodeFunctions::Max(0, Inputs[Index].Get()); + } + if (Sum.IsSingleValue() && Sum.GetSingleValue() == 0) Sum = 1; + for (int32 Index = 0; Index < InputCount; Index++) + { + Outputs[Index].Get() = TVoxelRange::Intersection({ 0, 1 }, Inputs[Index].Get() / Sum); + } + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.StartBlock(); + Constructor.AddLine("v_flt Sum = 0;"); + for (int32 Index = 0; Index < InputCount; Index++) + { + Constructor.AddLinef(TEXT("Sum += FMath::Max(0, %s);"), *Inputs[Index]); + } + Constructor.AddLine("if (Sum == 0) Sum = 1;"); + for (int32 Index = 0; Index < InputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = FMath::Max(0, %s) / Sum;"), *Outputs[Index], *Inputs[Index]); + } + Constructor.EndBlock(); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.StartBlock(); + Constructor.AddLine("TVoxelRange Sum = 0;"); + for (int32 Index = 0; Index < InputCount; Index++) + { + Constructor.AddLinef(TEXT("Sum += FVoxelNodeFunctions::Max(0, %s);"), *Inputs[Index]); + } + Constructor.AddLine("if (Sum.IsSingleValue() && Sum.GetSingleValue() == 0) Sum = 1;"); + for (int32 Index = 0; Index < InputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::Intersection({ 0, 1 }, %s / Sum);"), *Outputs[Index], *Inputs[Index]); + } + Constructor.EndBlock(); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeColors.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeColors.cpp new file mode 100644 index 00000000..e74c63d0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeColors.cpp @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNodeColors.h" + +FLinearColor FVoxelNodeColors::FloatNode = FColor::Green; +FLinearColor FVoxelNodeColors::IntNode = FColor::Cyan; +FLinearColor FVoxelNodeColors::ColorNode = FColor::Blue; +FLinearColor FVoxelNodeColors::BoolNode = FColor::Red; +FLinearColor FVoxelNodeColors::ExecNode = FColor::Red; +FLinearColor FVoxelNodeColors::SeedNode = FLinearColor(1.f, 0.3f, 1.f, 1.f); +FLinearColor FVoxelNodeColors::ExposedNode = FColor::Yellow; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp new file mode 100644 index 00000000..0d7ac64c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp @@ -0,0 +1,100 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNodeHelper.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelAxisDependencies.h" + +EVoxelPinCategory UVoxelNodeHelper::GetInputPinCategory(int32 PinIndex) const +{ + return Pins.GetInputPin(PinIndex).Category; +} + +EVoxelPinCategory UVoxelNodeHelper::GetOutputPinCategory(int32 PinIndex) const +{ + return Pins.GetOutputPin(PinIndex).Category; +} + +FName UVoxelNodeHelper::GetInputPinName(int32 PinIndex) const +{ + return Pins.GetInputPin(PinIndex).Name; +} + +FName UVoxelNodeHelper::GetOutputPinName(int32 PinIndex) const +{ + return Pins.GetOutputPin(PinIndex).Name; +} + +FString UVoxelNodeHelper::GetInputPinToolTip(int32 PinIndex) const +{ + return Pins.GetInputPin(PinIndex).ToolTip; +} + +FString UVoxelNodeHelper::GetOutputPinToolTip(int32 PinIndex) const +{ + return Pins.GetOutputPin(PinIndex).ToolTip; +} + +int32 UVoxelNodeHelper::GetMinInputPins() const +{ + return bCustomInputsCount ? CustomMin : Pins.InputPins.Num(); +} + +int32 UVoxelNodeHelper::GetMaxInputPins() const +{ + return bCustomInputsCount ? CustomMax : Pins.InputPins.Num(); +} + +int32 UVoxelNodeHelper::GetInputPinsIncrement() const +{ + return Increment; +} + +int32 UVoxelNodeHelper::GetOutputPinsCount() const +{ + return Pins.OutputPins.Num(); +} + +FLinearColor UVoxelNodeHelper::GetColor() const +{ + return Color; +} + +FVoxelPinDefaultValueBounds UVoxelNodeHelper::GetInputPinDefaultValueBounds(int32 PinIndex) const +{ + return Pins.GetInputPin(PinIndex).DefaultValueBounds; +} + +FString UVoxelNodeHelper::GetInputPinDefaultValue(int32 PinIndex) const +{ + return Pins.GetInputPin(PinIndex).DefaultValue; +} + +UVoxelSetterNode::UVoxelSetterNode() +{ + SetColor(FVoxelNodeColors::ExecNode); +} + +TSharedPtr UVoxelSetterNode::GetCompilationNode() const +{ + auto Result = MakeShared(*this); + Result->OutputIndex = GetOutputIndex(); + return Result; +} + +TSharedPtr UVoxelPureNode::GetCompilationNode() const +{ + return MakeShared(*this); +} + +TSharedPtr UVoxelNodeWithDependencies::GetCompilationNode() const +{ + auto Result = MakeShared(*this); + Result->DefaultDependencies = GetNodeDependencies(); + return Result; +} + +uint8 UVoxelNodeWithContext::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::X; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelpers.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelpers.cpp new file mode 100644 index 00000000..2ffa69c3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelpers.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "CppTranslation/VoxelCppIds.h" + +void FVoxelNodeHelpers::ReplaceInputsOutputs(FString& S, const TArray& Inputs, const TArray& Outputs) +{ + TArray> Args; + for (int32 I = 0; I < Inputs.Num(); I++) + { + Args.Emplace(TEXT("_I") + FString::FromInt(I), Inputs[I]); + Args.Emplace(TEXT("Input") + FString::FromInt(I), Inputs[I]); + } + for (int32 I = 0; I < Outputs.Num(); I++) + { + Args.Emplace(TEXT("_O") + FString::FromInt(I), Outputs[I]); + Args.Emplace(TEXT("Output") + FString::FromInt(I), Outputs[I]); + } + Args.Emplace(TEXT("_C0"), FVoxelCppIds::Context); + + // Iterate in REVERSE order + // so that Index10 is replaced before Index1 (else collision) + for (int32 Index = Args.Num() - 1; Index >=0; Index--) + { + const auto& It = Args[Index]; + while (S.Contains(It.Key, ESearchCase::CaseSensitive)) + { + S = S.Replace(*It.Key, *It.Value, ESearchCase::CaseSensitive); + } + } +} + +FString FVoxelNodeHelpers::GetPrefixOpLoopString(const TArray& Inputs, const TArray& Outputs, int32 InputCount, const FString& Op) +{ + check(InputCount > 0); + + FString Line; + + Line += Outputs[0] + " = "; + for (int32 I = 0; I < InputCount - 1; I++) + { + Line += Op + "(" + Inputs[I] + ", "; + } + + Line += Inputs[InputCount - 1]; + + for (int32 I = 0; I < InputCount - 1; I++) + { + Line += ")"; + } + + Line += ";"; + + return Line; +} + +FString FVoxelNodeHelpers::GetInfixOpLoopString(const TArray& Inputs, const TArray& Outputs, int32 InputCount, const FString& Op) +{ + check(InputCount > 0); + + FString Line; + + Line += Outputs[0] + " = "; + for (int32 I = 0; I < InputCount - 1; I++) + { + Line += Inputs[I] + " " + Op + " "; + } + Line += Inputs[InputCount - 1]; + Line += ";"; + + return Line; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeVariables.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeVariables.cpp new file mode 100644 index 00000000..8754b012 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeVariables.cpp @@ -0,0 +1,194 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNodeVariables.h" +#include "CppTranslation/VoxelCppUtils.h" + +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" + +#include "Engine/Texture2D.h" +#include "Materials/MaterialInterface.h" +#include "Curves/CurveFloat.h" +#include "Curves/CurveLinearColor.h" + +FVoxelColorTextureVariable::FVoxelColorTextureVariable(const UVoxelExposedNode& Node, UTexture2D* Texture) + : FVoxelExposedVariable( + Node, + "TVoxelTexture", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Texture)) +{ +} + +FString FVoxelColorTextureVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return "FVoxelTextureUtilities::CreateFromTexture_Color(" + FVoxelCppUtils::LoadObjectString(ExposedNameAccessor) + ")"; +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelFloatTextureVariable::FVoxelFloatTextureVariable(const UVoxelExposedNode& Node) + : FVoxelExposedVariable( + Node, + "TVoxelTexture", + "FVoxelFloatTexture", + "") +{ +} + +FString FVoxelFloatTextureVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return ExposedNameAccessor + ".Texture"; +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCurveVariable::FVoxelCurveVariable(const UVoxelExposedNode& Node, UCurveFloat* Curve) + : FVoxelExposedVariable( + Node, + "FVoxelRichCurve", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Curve)) +{ +} + +FString FVoxelCurveVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return FString::Printf(TEXT("FVoxelRichCurve(%s)"), *FVoxelCppUtils::LoadObjectString(ExposedNameAccessor)); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelColorCurveVariable::FVoxelColorCurveVariable(const UVoxelExposedNode& Node, UCurveLinearColor* Curve) + : FVoxelExposedVariable( + Node, + "FVoxelColorRichCurve", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Curve)) +{ +} + +FString FVoxelColorCurveVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return FString::Printf(TEXT("FVoxelColorRichCurve(%s)"), *FVoxelCppUtils::LoadObjectString(ExposedNameAccessor)); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelHeightmapVariable::FVoxelHeightmapVariable(const UVoxelExposedNode& Node, UVoxelHeightmapAssetFloat* Heightmap) + : FVoxelExposedVariable( + Node, + "TVoxelHeightmapAssetSamplerWrapper", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Heightmap)) +{ +} + +FVoxelHeightmapVariable::FVoxelHeightmapVariable(const UVoxelExposedNode& Node, UVoxelHeightmapAssetUINT16* Heightmap) + : FVoxelExposedVariable( + Node, + "TVoxelHeightmapAssetSamplerWrapper", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Heightmap)) +{ +} + +FString FVoxelHeightmapVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return Type + "(" + ExposedNameAccessor + ".LoadSynchronous())"; +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelDataAssetVariable::FVoxelDataAssetVariable(const UVoxelExposedNode & Node, UVoxelDataAsset * Asset) + : FVoxelExposedVariable( + Node, + "TVoxelSharedRef", + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Asset)) +{ +} + +FString FVoxelDataAssetVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return FString::Printf( + TEXT("%s ? %s->GetData() : MakeVoxelShared(nullptr)"), + *FVoxelCppUtils::LoadObjectString(ExposedNameAccessor), + *FVoxelCppUtils::LoadObjectString(ExposedNameAccessor)); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelGeneratorVariable::FVoxelGeneratorVariable(const UVoxelExposedNode& Node, const FVoxelGeneratorPicker& Generator) + : FVoxelExposedVariable( + Node, + "TVoxelSharedRef", + "FVoxelGeneratorPicker", + FVoxelCppUtils::PickerDefaultString(Generator)) +{ +} + +FString FVoxelGeneratorVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return ExposedNameAccessor + ".GetInstance(true /*bSilent*/)"; +} + +/////////////////////////////////////////////////////////////////////////////// + +inline FString GetGeneratorArraysDefaultValue(const TArray& Pickers) +{ + FString Result = "{\n"; + for (auto& Picker : Pickers) + { + Result += "\t\t" + FVoxelCppUtils::PickerDefaultString(Picker) + ",\n"; + } + Result += "\t}"; + return Result; +} + +FVoxelGeneratorArrayVariable::FVoxelGeneratorArrayVariable(const UVoxelExposedNode& Node, const TArray& Generators) + : FVoxelExposedVariable( + Node, + "TArray>", + "TArray", + GetGeneratorArraysDefaultValue(Generators)) +{ +} + +FString FVoxelGeneratorArrayVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return "FVoxelNodeFunctions::CreateGeneratorArray(" + ExposedNameAccessor + ")"; +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelMaterialInterfaceVariable::FVoxelMaterialInterfaceVariable(const UVoxelExposedNode& Node, UMaterialInterface* Material) + : FVoxelExposedVariable( + Node, + FVoxelCppUtils::TypeToString(), + FVoxelCppUtils::SoftObjectPtrString(), + FVoxelCppUtils::ObjectDefaultString(Material)) +{ +} + +FString FVoxelMaterialInterfaceVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return "*" + ExposedNameAccessor + ".GetAssetName()"; +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelColorVariable::FVoxelColorVariable(const UVoxelExposedNode& Node, FLinearColor Color) + : FVoxelExposedVariable( + Node, + FVoxelCppUtils::TypeToString(), + FVoxelCppUtils::TypeToString(), + FVoxelCppUtils::LexToCpp(Color)) +{ +} + +FString FVoxelColorVariable::GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const +{ + return ExposedNameAccessor + ".ToFColor(false)"; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp new file mode 100644 index 00000000..4325c9da --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp @@ -0,0 +1,627 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNoiseNodes.h" +#include "VoxelNodes/VoxelNoiseNodeHelper.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelGraphGenerator.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +#define NOISE_SAMPLE_RANGE 10 + +FName UVoxelNode_NoiseNode::GetInputPinName(int32 PinIndex) const +{ + if (GetDimension() == 2) + { + if (PinIndex == 0) + { + return "X"; + } + else if (PinIndex == 1) + { + return "Y"; + } + else if (PinIndex == 2) + { + return "Frequency"; + } + else if (PinIndex == 3) + { + return "Seed"; + } + } + else + { + if (PinIndex == 0) + { + return "X"; + } + else if (PinIndex == 1) + { + return "Y"; + } + else if (PinIndex == 2) + { + return "Z"; + } + else if (PinIndex == 3) + { + return "Frequency"; + } + else if (PinIndex == 4) + { + return "Seed"; + } + } + + return CustomNoisePins.GetInputPin(PinIndex - GetBaseInputPinsCount(), false).Name; +} + +FName UVoxelNode_NoiseNode::GetOutputPinName(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return IsDerivative() ? "Value" : ""; + } + + if (IsDerivative()) + { + if (PinIndex == 1) + { + return "DX"; + } + else if (PinIndex == 2) + { + return "DY"; + } + else if (PinIndex == 3) + { + return "DZ"; + } + } + + return CustomNoisePins.GetOutputPin(PinIndex - GetBaseOutputPinsCount(), false).Name; +} + +FString UVoxelNode_NoiseNode::GetInputPinToolTip(int32 PinIndex) const +{ + if (GetDimension() == 2) + { + if (PinIndex == 0) + { + return "X"; + } + else if (PinIndex == 1) + { + return "Y"; + } + else if (PinIndex == 2) + { + return "The frequency of the noise"; + } + else if (PinIndex == 3) + { + return "The seed to use"; + } + } + else + { + if (PinIndex == 0) + { + return "X"; + } + else if (PinIndex == 1) + { + return "Y"; + } + else if (PinIndex == 2) + { + return "Z"; + } + else if (PinIndex == 3) + { + return "The frequency of the noise"; + } + else if (PinIndex == 4) + { + return "The seed to use"; + } + } + + return CustomNoisePins.GetInputPin(PinIndex - GetBaseInputPinsCount(), false).ToolTip; +} + +FString UVoxelNode_NoiseNode::GetOutputPinToolTip(int32 PinIndex) const +{ + if (PinIndex == 0) + { + return "The noise value"; + } + else if (PinIndex == 1) + { + return "The derivative along the X axis. Can be used to compute the slope of the noise using GetSlopeFromDerivatives."; + } + else if (PinIndex == 2) + { + return "The derivative along the Y axis. Can be used to compute the slope of the noise using GetSlopeFromDerivatives."; + } + else if (PinIndex == 3) + { + return "The derivative along the Z axis. Can be used to compute the slope of the noise using GetSlopeFromDerivatives."; + } + + return CustomNoisePins.GetOutputPin(PinIndex - GetBaseOutputPinsCount(), false).ToolTip; +} + +EVoxelPinCategory UVoxelNode_NoiseNode::GetInputPinCategory(int32 PinIndex) const +{ + return PinIndex == GetDimension() + 1 ? EVoxelPinCategory::Seed : EVoxelPinCategory::Float; +} + +EVoxelPinCategory UVoxelNode_NoiseNode::GetOutputPinCategory(int32 PinIndex) const +{ + return EVoxelPinCategory::Float; +} + +FString UVoxelNode_NoiseNode::GetInputPinDefaultValue(int32 PinIndex) const +{ + if (PinIndex == GetDimension()) + { + return FString::SanitizeFloat(Frequency); + } + + return CustomNoisePins.GetInputPin(PinIndex - GetBaseInputPinsCount(), false).DefaultValue; +} + +TSharedPtr UVoxelNode_NoiseNode::GetCompilationNode() const +{ + if (NeedRangeAnalysis()) + { + // We handle range analysis ourselves, without using the inputs + return MakeShared(*this); + } + + // eg for gradient perturb + return Super::GetCompilationNode(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelNode_NoiseNode::ComputeOutputRanges() +{ + FVoxelNoiseComputeNode::ComputeOutputRanges(*this); +} + +int32 UVoxelNode_NoiseNode::SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const +{ + // Frequency + Inputs[GetDimension()].Get() = 1; + + return GetBaseInputPinsCount(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelNode_NoiseNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty && + PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + ComputeOutputRanges(); + } +} + +bool UVoxelNode_NoiseNode::CanEditChange(const FProperty* InProperty) const +{ + return + Super::CanEditChange(InProperty) && + (NeedRangeAnalysis() || InProperty->GetFName() != GET_MEMBER_NAME_STATIC(UVoxelNode_NoiseNode, NumberOfSamples)); +} +#endif + +void UVoxelNode_NoiseNode::PostLoad() +{ + Super::PostLoad(); + if (OutputRanges.Num() == 0) + { + ComputeOutputRanges(); + } +} + +void UVoxelNode_NoiseNode::PostInitProperties() +{ + Super::PostInitProperties(); + if (OutputRanges.Num() == 0 && !HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad)) + { + ComputeOutputRanges(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelNoiseComputeNode::FVoxelNoiseComputeNode(const UVoxelNode_NoiseNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Dimension(Node.GetDimension()) + , bIsDerivative(Node.IsDerivative()) + , bIsFractal(Node.IsFractal()) + , Interpolation(Node.Interpolation) + , NoiseVariable(FVoxelVariable("FVoxelFastNoise", UniqueName.ToString() + "_Noise")) +{ + const_cast, 4>&>(OutputRanges).CopyFromArray(Node.OutputRanges); +} + +void FVoxelNoiseComputeNode::Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) +{ + PrivateNoise.SetSeed(Inputs[GetSeedInputIndex()]); + InitNoise(FVoxelNoiseNodeHelper_Runtime(PrivateNoise)); +} + +void FVoxelNoiseComputeNode::InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const +{ + Constructor.AddLine(NoiseVariable.CppName + ".SetSeed(" + Inputs[GetSeedInputIndex()] + ");"); + InitNoise(FVoxelNoiseNodeHelper_Cpp(NoiseVariable.CppName, Constructor)); +} + +void FVoxelNoiseComputeNode::ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const +{ + for (int32 Index = 0; Index < OutputCount; Index++) + { + Outputs[Index].Get() = OutputRanges[Index]; + } +} + +void FVoxelNoiseComputeNode::ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const +{ + const FString& X = Inputs[0]; + const FString& Y = Inputs[1]; + const FString& Z = Inputs[2]; + const FString& Freq = Dimension == 2 ? Inputs[2] : Inputs[3]; + + FString Line; + Line += Outputs[0] + " = "; + Line += NoiseVariable.CppName + "." + GetFunctionName(); + Line += "(" + X + ", " + Y; + if (Dimension == 3) + { + Line += ", " + Z; + } + Line += ", " + Freq; + if (bIsFractal) + { + Line += ", " + GET_OCTAVES_CPP; + } + if (bIsDerivative) + { + for (uint32 Index = 0; Index < Dimension; Index++) + { + Line += "," + Outputs[Index + 1]; + } + } + Line += ");"; + Constructor.AddLine(Line); + + for (int32 Index = 0; Index < OutputCount; Index++) + { + Clamp(Constructor, Outputs, Index); + } +} + +void FVoxelNoiseComputeNode::ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const +{ + for (int32 Index = 0; Index < OutputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = { %ff, %ff };"), *Outputs[Index], OutputRanges[Index].Min, OutputRanges[Index].Max); + } +} + +void FVoxelNoiseComputeNode::GetPrivateVariables(TArray& PrivateVariables) const +{ + PrivateVariables.Add(NoiseVariable); +} + +void FVoxelNoiseComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + Noise.SetInterpolation(Interpolation); +} + +void FVoxelNoiseComputeNode::Clamp(FVoxelCppConstructor& Constructor, const TArray& Outputs, int32 OutputIndex) const +{ + Constructor.AddLinef(TEXT("%s = FMath::Clamp(%s, %f, %f);"), *Outputs[OutputIndex], *Outputs[OutputIndex], OutputRanges[OutputIndex].Min, OutputRanges[OutputIndex].Max); +} + +void FVoxelNoiseComputeNode::ComputeOutputRanges(UVoxelNode_NoiseNode& Node) +{ + auto& OutputRanges = Node.OutputRanges; + OutputRanges.Reset(); + // Init to inf so the values aren't clamped + OutputRanges.Init(TVoxelRange::Infinite(), 4); + + const auto CompilationNode = Node.GetCompilationNode(); + auto ComputeNode = StaticCastSharedPtr(Node.GetComputeNode(*CompilationNode)); + const int32 Dimension = ComputeNode->Dimension; + + ComputeNode->PrivateNoise.SetSeed(0); + ComputeNode->InitNoise(FVoxelNoiseNodeHelper_Runtime(ComputeNode->PrivateNoise)); + + TVoxelStaticArray Min; + TVoxelStaticArray Max; + TVoxelStaticArray Inputs; + TVoxelStaticArray Outputs; + + Inputs.Memzero(); + Outputs.Memzero(); + + ensure(ComputeNode->InputCount == Node.SetupInputsForComputeOutputRanges(Inputs)); + + ComputeNode->Compute(Inputs.GetData(), Outputs.GetData(), FVoxelContext::EmptyContext); + for (int32 OutputIndex = 0; OutputIndex < ComputeNode->OutputCount; OutputIndex++) + { + Min[OutputIndex] = Outputs[OutputIndex].Get(); + Max[OutputIndex] = Outputs[OutputIndex].Get(); + } + + for (uint32 Index = 0; Index < Node.NumberOfSamples; Index++) + { + for (int32 InputIndex = 0; InputIndex < Dimension; InputIndex++) + { + Inputs[InputIndex].Get() = FMath::FRandRange(-NOISE_SAMPLE_RANGE, NOISE_SAMPLE_RANGE); + } + ComputeNode->Compute(Inputs.GetData(), Outputs.GetData(), FVoxelContext::EmptyContext); + for (int32 OutputIndex = 0; OutputIndex < ComputeNode->OutputCount; OutputIndex++) + { + Min[OutputIndex] = FMath::Min(Min[OutputIndex], Outputs[OutputIndex].Get()); + Max[OutputIndex] = FMath::Max(Max[OutputIndex], Outputs[OutputIndex].Get()); + } + } + + OutputRanges.Reset(); + for (int32 OutputIndex = 0; OutputIndex < ComputeNode->OutputCount; OutputIndex++) + { + OutputRanges.Emplace(FVoxelRange(Min[OutputIndex], Max[OutputIndex])); + } + ExpandRanges(OutputRanges, Node.Tolerance); +} + +void FVoxelNoiseComputeNode::ExpandRanges(TArray& Ranges, float Tolerance) +{ + for (auto& Range : Ranges) + { + const float Delta = FMath::Abs(Range.Max - Range.Min) * Tolerance; + Range.Min -= Delta; + Range.Max += Delta; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FLinearColor UVoxelNode_NoiseNodeFractal::GetNodeBodyColor() const +{ + return FMath::Lerp(FColorList::White, FColorList::Orange, FMath::Clamp((FractalOctaves - 1.f) / 10, 0, 1)); +} + +FLinearColor UVoxelNode_NoiseNodeFractal::GetColor() const +{ + return FMath::Lerp(FColorList::Black, FColorList::Orange, FMath::Clamp((FractalOctaves - 1.f) / 10, 0, 1)); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelNode_NoiseNodeFractal::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + LODToOctavesMap.Add("0", FractalOctaves); + int32 MaxInt = 0; + for (auto& It : LODToOctavesMap) + { + if (!It.Key.IsEmpty()) + { + const int32 Int = FVoxelUtilities::ClampDepth(TCString::Atoi(*It.Key)); + MaxInt = FMath::Max(MaxInt, Int); + It.Key = FString::FromInt(Int); + } + } + for (auto& It : LODToOctavesMap) + { + if (It.Key.IsEmpty()) + { + if (uint8 * LOD = LODToOctavesMap.Find(FString::FromInt(MaxInt))) + { + It.Value = *LOD - 1; + } + It.Key = FString::FromInt(++MaxInt); + } + } + LODToOctavesMap.KeySort([](const FString& A, const FString& B) { return TCString::Atoi(*A) < TCString::Atoi(*B); }); + } +} +#endif + +void UVoxelNode_NoiseNodeFractal::PostLoad() +{ + LODToOctavesMap.Add("0", FractalOctaves); + Super::PostLoad(); // Make sure to call ComputeRange after +} + +void UVoxelNode_NoiseNodeFractal::PostInitProperties() +{ + LODToOctavesMap.Add("0", FractalOctaves); + Super::PostInitProperties(); // Make sure to call ComputeRange after +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelNoiseFractalComputeNode::FVoxelNoiseFractalComputeNode(const UVoxelNode_NoiseNodeFractal& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseComputeNode(Node, CompilationNode) + , FractalOctaves(Node.FractalOctaves) + , FractalLacunarity(Node.FractalLacunarity) + , FractalGain(Node.FractalGain) + , FractalType(Node.FractalType) + , LODToOctavesVariable("TStaticArray", UniqueName.ToString() + "_LODToOctaves") +{ + TMap Map; + for (auto& It : Node.LODToOctavesMap) + { + if (!It.Key.IsEmpty()) + { + Map.Add(TCString::Atoi(*It.Key), It.Value); + } + } + Map.Add(0, Node.FractalOctaves); + + for (int32 LODIt = 0; LODIt < 32; LODIt++) + { + int32 LOD = LODIt; + while (!Map.Contains(LOD)) + { + LOD--; + check(LOD >= 0); + } + const_cast&>(LODToOctaves)[LODIt] = Map[LOD]; + } +} + +void FVoxelNoiseFractalComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseComputeNode::InitNoise(Noise); + + Noise.SetFractalOctavesAndGain(FractalOctaves, FractalGain); + Noise.SetFractalLacunarity(FractalLacunarity); + Noise.SetFractalType(FractalType); +} + +void FVoxelNoiseFractalComputeNode::InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const +{ + FVoxelNoiseComputeNode::InitCpp(Inputs, Constructor); + + for (int32 LOD = 0; LOD < 32; LOD++) + { + Constructor.AddLinef(TEXT("%s[%d] = %u;"), *LODToOctavesVariable.CppName, LOD, LODToOctaves[LOD]); + } +} + +void FVoxelNoiseFractalComputeNode::GetPrivateVariables(TArray& PrivateVariables) const +{ + FVoxelNoiseComputeNode::GetPrivateVariables(PrivateVariables); + + PrivateVariables.Add(LODToOctavesVariable); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_IQNoiseBase::UVoxelNode_IQNoiseBase() +{ + bComputeDerivative = true; + FractalType = EVoxelNoiseFractalType::FBM; + FractalOctaves = 15; + Frequency = 0.001; + NumberOfSamples = 1000000; +} + +#if WITH_EDITOR +bool UVoxelNode_IQNoiseBase::CanEditChange(const FProperty* InProperty) const +{ + return + Super::CanEditChange(InProperty) && + InProperty->GetFName() != GET_MEMBER_NAME_STATIC(UVoxelNode_IQNoiseBase, bComputeDerivative) && + InProperty->GetFName() != GET_MEMBER_NAME_STATIC(UVoxelNode_IQNoiseBase, FractalType); +} +#endif + +FVoxel2DIQNoiseComputeNode::FVoxel2DIQNoiseComputeNode(const UVoxelNode_2DIQNoiseBase& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseFractalComputeNode(Node, CompilationNode) + , Rotation(Node.Rotation) +{ +} + +void FVoxel2DIQNoiseComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseFractalComputeNode::InitNoise(Noise); + Noise.SetMatrixFromRotation_2D(Rotation); +} + +FVoxel3DIQNoiseComputeNode::FVoxel3DIQNoiseComputeNode(const UVoxelNode_3DIQNoiseBase& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseFractalComputeNode(Node, CompilationNode) + , Rotation(Node.Rotation) +{ + +} + +void FVoxel3DIQNoiseComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseFractalComputeNode::InitNoise(Noise); + Noise.SetMatrixFromRotation_3D(Rotation); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCellularNoiseComputeNode::FVoxelCellularNoiseComputeNode(const UVoxelNode_CellularNoise& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseComputeNode(Node, CompilationNode) + , DistanceFunction(Node.DistanceFunction) + , ReturnType(Node.ReturnType) + , Jitter(Node.Jitter) +{ + +} + +void FVoxelCellularNoiseComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseComputeNode::InitNoise(Noise); + + Noise.SetCellularJitter(Jitter); + Noise.SetCellularDistanceFunction(DistanceFunction); + Noise.SetCellularReturnType(ReturnType); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCraterNoiseComputeNode::FVoxelCraterNoiseComputeNode(const UVoxelNode_CraterNoise& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseComputeNode(Node, CompilationNode) + , Jitter(Node.Jitter) + , FalloffExponent(Node.FalloffExponent) +{ +} + +void FVoxelCraterNoiseComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseComputeNode::InitNoise(Noise); + + Noise.SetCellularJitter(Jitter); + Noise.SetCraterFalloffExponent(FalloffExponent); +} + +/////////////////////////////////////////////////////////////////////////////// + +FVoxelCraterNoiseFractalComputeNode::FVoxelCraterNoiseFractalComputeNode(const UVoxelNode_CraterNoiseFractal& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelNoiseFractalComputeNode(Node, CompilationNode) + , Jitter(Node.Jitter) + , FalloffExponent(Node.FalloffExponent) +{ +} + +void FVoxelCraterNoiseFractalComputeNode::InitNoise(const IVoxelNoiseNodeHelper& Noise) const +{ + FVoxelNoiseFractalComputeNode::InitNoise(Noise); + + Noise.SetCellularJitter(Jitter); + Noise.SetCraterFalloffExponent(FalloffExponent); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp new file mode 100644 index 00000000..079a093e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp @@ -0,0 +1,493 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelOptimizationNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/Recorders/VoxelGraphEmptyRecorder.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelContext.h" +#include "VoxelMessages.h" +#include "VoxelGraphConstants.h" +#include "VoxelGraphGenerator.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +#include "Async/Async.h" + +UVoxelNode_StaticClampFloat::UVoxelNode_StaticClampFloat() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + +FText UVoxelNode_StaticClampFloat::GetTitle() const +{ + return FText::FromString("Static Clamp: " + FString::SanitizeFloat(Min) + " <= X <= " + FString::SanitizeFloat(Max)); +} + +TSharedPtr UVoxelNode_StaticClampFloat::GetCompilationNode() const +{ + return MakeShared(*this); +} + +TVoxelSharedPtr UVoxelNode_StaticClampFloat::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_StaticClampFloat& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Min(Node.Min) + , Max(Node.Max) + { + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = FMath::Clamp(Inputs[0].Get(), Min, Max); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = FMath::Clamp(%s, %f, %f);"), *Outputs[0], *Inputs[0], Min, Max); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = { Min, Max }; + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = { %ff, %ff };"), *Outputs[0], Min, Max); + } + + private: + const float Min; + const float Max; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RangeAnalysisDebuggerFloat::UVoxelNode_RangeAnalysisDebuggerFloat() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + +void UVoxelNode_RangeAnalysisDebuggerFloat::UpdateFromBin() +{ + if (Bins.IsValid()) + { + Min = Bins->bMinMaxInit ? Bins->MinValue : 0; + Max = Bins->bMinMaxInit ? Bins->MaxValue : 0; + } +} + +void UVoxelNode_RangeAnalysisDebuggerFloat::UpdateGraph() +{ + Curve.GetRichCurve()->Reset(); + Bins->AddToCurve(*Curve.GetRichCurve()); +} + +void UVoxelNode_RangeAnalysisDebuggerFloat::Reset() +{ + Bins = MakeUnique(GraphMin, GraphMax, GraphStep); + UpdateFromBin(); +} + +#if WITH_EDITOR +void UVoxelNode_RangeAnalysisDebuggerFloat::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + Reset(); + UpdateFromBin(); + } +} + +void UVoxelNode_RangeAnalysisDebuggerFloat::PostLoad() +{ + Super::PostLoad(); + Reset(); + UpdateFromBin(); +} +#endif + +TVoxelSharedPtr UVoxelNode_RangeAnalysisDebuggerFloat::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_RangeAnalysisDebuggerFloat& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Bins(Node.GraphMin, Node.GraphMax, Node.GraphStep) + , WeakNode(const_cast(&Node)) + { + } + ~FLocalVoxelComputeNode() + { + AsyncTask(ENamedThreads::GameThread, [WeakNode = WeakNode, Bins = Bins]() + { + if (WeakNode.IsValid()) + { + WeakNode->Bins->AddOtherBins(Bins); + WeakNode->UpdateFromBin(); + WeakNode->UpdateGraph(); + } + else + { + FVoxelMessages::Error("Range Analysis Debugger node was deleted after the graph, values won't be reported"); + } + }); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + + Bins.AddStat(Inputs[0].Get()); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *Inputs[0]); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = Inputs[0].Get(); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *Inputs[0]); + } + + private: + mutable FVoxelBins Bins; + TWeakObjectPtr WeakNode; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_Sleep::UVoxelNode_Sleep() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + +PRAGMA_DISABLE_OPTIMIZATION +TVoxelSharedPtr UVoxelNode_Sleep::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_Sleep& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , NumberOfLoops(Node.NumberOfLoops) + { + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + int32 K = 0; + for (int32 Index = 0; Index < NumberOfLoops; Index++) + { + K++; + } + check(K == NumberOfLoops); + Outputs[0] = Inputs[0]; + } + + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *Inputs[0]); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0] = Inputs[0]; + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *Inputs[0]); + } + + private: + const int32 NumberOfLoops; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} +PRAGMA_ENABLE_OPTIMIZATION + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RangeUnion::UVoxelNode_RangeUnion() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(2, MAX_VOXELNODE_PINS); +} + +GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(UVoxelNode_RangeUnion, FVoxelNodeFunctions::Union, v_flt) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_IsSingleBool::UVoxelNode_IsSingleBool() +{ + SetInputs(EC::Boolean); + SetOutputs(EC::Boolean); +} + +GENERATED_VOXELNODE_IMPL +( + UVoxelNode_IsSingleBool, + DEFINE_INPUTS(bool), + DEFINE_OUTPUTS(bool), + _O0 = FVoxelNodeFunctions::IsSingleBool(_I0); +) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetRangeAnalysis::UVoxelNode_GetRangeAnalysis() +{ + SetInputs(EC::Float); + AddOutput("Min", "The min value of the input value for the current voxel"); + AddOutput("Max", "The max value of the input value for the current voxel"); +} + +TSharedPtr UVoxelNode_GetRangeAnalysis::GetCompilationNode() const +{ + return MakeShared(*this); +} + +TVoxelSharedPtr UVoxelNode_GetRangeAnalysis::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + const int32 VariablesBufferSize; + const TVoxelSharedRef Tree; + + const TVoxelSharedRef BufferId = MakeVoxelShared(); + + struct FThreadRangeVariables + : TThreadSingleton + , TMap, TArray> + { + }; + + FLocalVoxelComputeNode(const UVoxelNode& Node, const FVoxelGetRangeAnalysisCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , VariablesBufferSize(CompilationNode.VariablesBufferSize) + , Tree(CompilationNode.Tree.ToSharedRef()) + { + check(VariablesBufferSize >= 0); + } + + virtual void GetPrivateVariables(TArray& PrivateVariables) const override + { + TSet Nodes; + Tree->GetNodes(Nodes); + + for (auto& Node : Nodes) + { + Node->GetPrivateVariables(PrivateVariables); + } + } + virtual void SetupCpp(FVoxelCppConfig& Config) const override + { + TSet Nodes; + Tree->GetNodes(Nodes); + + for (auto& Node : Nodes) + { + Node->CallSetupCpp(Config); + } + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + TArray SeedVariables; + SeedVariables.SetNumUninitialized(VariablesBufferSize, false); + + FVoxelGraphVMInitBuffers Buffers(SeedVariables.GetData()); + Tree->Init(InitStruct, Buffers); + } + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Tree->InitCpp(Constructor); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + // Note: GetWorldZ etc might not be set if the node isn't depending on them + // However, that should have no impact because then the tree isn't using them + const FVoxelContextRange ContextRange( + Context.LOD, + Context.Items, + Context.LocalToWorld, + Context.bHasCustomTransform, + FVoxelIntBox(Context.GetWorldX(), Context.GetWorldY(), Context.GetWorldZ())); + + FVoxelGraphVMComputeRangeBuffers Buffers(GetBuffer()); + FVoxelGraphEmptyRangeRecorder EmptyRecorder; + Tree->ComputeRange(ContextRange, Buffers, EmptyRecorder); + + const auto ValueRange = Buffers.GraphOutputs.Buffer[FVoxelGraphOutputsIndices::ValueIndex].Get(); + + Outputs[0].Get() = ValueRange.Min; + Outputs[1].Get() = ValueRange.Max; + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + FVoxelGraphVMComputeRangeBuffers Buffers(GetBuffer()); + FVoxelGraphEmptyRangeRecorder EmptyRecorder; + Tree->ComputeRange(Context, Buffers, EmptyRecorder); + + const auto ValueRange = Buffers.GraphOutputs.Buffer[FVoxelGraphOutputsIndices::ValueIndex].Get(); + + Outputs[0].Get() = ValueRange; + Outputs[1].Get() = ValueRange; + } + + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Outputs, Constructor, false); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Outputs, Constructor, true); + } + + private: + FVoxelNodeRangeType* GetBuffer() const + { + auto& BuffersMap = FThreadRangeVariables::Get(); + + auto* BufferPtr = BuffersMap.Find(BufferId); + if (BufferPtr) + { + // Fast path + return BufferPtr->GetData(); + } + + // Cleanup + BuffersMap.Remove(nullptr); + // Add new one + auto& Buffer = BuffersMap.Add(BufferId); + // Resize accordingly + Buffer.Empty(VariablesBufferSize); + Buffer.SetNumUninitialized(VariablesBufferSize); + return Buffer.GetData(); + } + void ComputeCppImpl(const TArray& Outputs, FVoxelCppConstructor& Constructor, bool bIsRangeAnalysis) const + { + Constructor.StartBlock(); + Constructor.AddLine("const auto GetRangeAnalysisValue = [this](const FVoxelContextRange& Context)"); + Constructor.StartBlock(); + { + Constructor.AddLine("TVoxelRange RangeAnalysisValue;"); + Constructor.NewLine(); + + auto Permutation = Constructor.Permutation; + Permutation.AddUnique(FVoxelGraphOutputsIndices::RangeAnalysisIndex); + FVoxelCppConstructor LocalConstructor(Permutation, Constructor.ErrorReporter); + + TArray GraphOutputs; + GraphOutputs.SetNum(FVoxelGraphOutputsIndices::ValueIndex + 1); + GraphOutputs[FVoxelGraphOutputsIndices::ValueIndex] = "RangeAnalysisValue"; + + Tree->ComputeRangeCpp( + LocalConstructor, + FVoxelVariableAccessInfo::Compute(EVoxelFunctionAxisDependencies::XYZWithoutCache), + GraphOutputs); + + Constructor.AddOtherConstructor(LocalConstructor); + + Constructor.NewLine(); + Constructor.AddLine("return RangeAnalysisValue;"); + } + Constructor.EndBlock(true); + + if (bIsRangeAnalysis) + { + Constructor.AddLine("const TVoxelRange RangeAnalysisValue = GetRangeAnalysisValue(Context);"); + Constructor.AddLinef(TEXT("%s = RangeAnalysisValue;"), *Outputs[0]); + Constructor.AddLinef(TEXT("%s = RangeAnalysisValue;"), *Outputs[1]); + } + else + { + Constructor.AddLine("const TVoxelRange RangeAnalysisValue = GetRangeAnalysisValue(FVoxelContextRange("); + Constructor.Indent(); + Constructor.AddLine("Context.LOD,"); + Constructor.AddLine("Context.Items,"); + Constructor.AddLine("Context.LocalToWorld,"); + Constructor.AddLine("Context.bHasCustomTransform,"); + Constructor.AddLine("FVoxelIntBox(Context.GetWorldX(), Context.GetWorldY(), Context.GetWorldZ())));"); + Constructor.Unindent(); + Constructor.NewLine(); + Constructor.AddLinef(TEXT("%s = RangeAnalysisValue.Min;"), *Outputs[0]); + Constructor.AddLinef(TEXT("%s = RangeAnalysisValue.Max;"), *Outputs[1]); + } + + Constructor.EndBlock(); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, CastCheckedVoxel(InCompilationNode))); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmartMin::UVoxelNode_SmartMin() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(3, MAX_VOXELNODE_PINS); +} + +TSharedPtr UVoxelNode_SmartMin::GetCompilationNode() const +{ + const auto Node = MakeShared(*this); + Node->bIsMin = true; + return Node; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmartMax::UVoxelNode_SmartMax() +{ + SetInputs(EC::Exec, EC::Float); + SetOutputs(EC::Exec, EC::Float); + SetInputsCount(3, MAX_VOXELNODE_PINS); +} + +TSharedPtr UVoxelNode_SmartMax::GetCompilationNode() const +{ + const auto Node = MakeShared(*this); + Node->bIsMin = false; + return Node; +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp new file mode 100644 index 00000000..6dfd8eea --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp @@ -0,0 +1,142 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelParameterNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" + +template +class TVoxelParameterComputeNode : public FVoxelDataComputeNode +{ +public: + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(TVoxelParameterComputeNode); + + template + TVoxelParameterComputeNode(const TNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Value(Node.template GetParameter()) + , Variable(MakeShared(Node, FVoxelCppUtils::TypeToString(), FVoxelCppUtils::TypeToString(), FVoxelCppUtils::LexToCpp(Node.GetValue()))) + { + } + + using Type = typename TChooseClass::Value, v_flt, T>::Result; + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Value; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = Value; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Variable->CppName + ";"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + +private: + const T Value; + const TSharedRef Variable; +}; + +UVoxelNode_FloatParameter::UVoxelNode_FloatParameter() +{ + SetOutputs(EC::Float); +} + +TVoxelSharedPtr UVoxelNode_FloatParameter::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeVoxelShared>(*this, InCompilationNode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_IntParameter::UVoxelNode_IntParameter() +{ + SetOutputs(EC::Int); +} + +TVoxelSharedPtr UVoxelNode_IntParameter::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeVoxelShared>(*this, InCompilationNode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_ColorParameter::UVoxelNode_ColorParameter() +{ + SetOutputs(EC::Color); +} + +TVoxelSharedPtr UVoxelNode_ColorParameter::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_ColorParameter& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Value(Node.GetParameter().ToFColor(false)) + , Variable(MakeShared(Node, Node.Color)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = Value; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = Value; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Variable->CppName + ";"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const FColor Value; + const TSharedRef Variable; + }; + return MakeVoxelShared(*this, InCompilationNode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_BoolParameter::UVoxelNode_BoolParameter() +{ + SetOutputs(EC::Boolean); +} + +TVoxelSharedPtr UVoxelNode_BoolParameter::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeVoxelShared>(*this, InCompilationNode); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp new file mode 100644 index 00000000..69398a1d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp @@ -0,0 +1,222 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelPlaceableItemsNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppIds.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGraphDataItemConfig.h" +#include "NodeFunctions/VoxelPlaceableItemsNodeFunctions.h" + +UVoxelNode_DataItemSample::UVoxelNode_DataItemSample() +{ + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }, + { "Smoothness", EC::Float, "The smoothness of the union. The value is a distance: it should be in voxels. If <= 0 Min will be used instead (faster). See SmoothUnion for more info" }, + { "Default", EC::Float, "The value returned when there are no data item nearby", "10" }); + SetOutputs(EC::Float); +} + +class FVoxelNode_DataItemSample_LocalVoxelComputeNode : public FVoxelDataComputeNode +{ +public: + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FVoxelNode_DataItemSample_LocalVoxelComputeNode); + + FVoxelNode_DataItemSample_LocalVoxelComputeNode(const UVoxelNode_DataItemSample& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Mask(Node.Mask) + , CombineMode(Node.CombineMode) + { + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + ComputeImpl(Inputs, Outputs, Context); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + ComputeImpl(Inputs, Outputs, Context); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Inputs, Outputs, Constructor); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Inputs, Outputs, Constructor); + } + +private: + const uint32 Mask; + const EVoxelDataItemCombineMode CombineMode; + + template + void ComputeImpl(TInputs Inputs, TOutputs Outputs, const TContext& Context) const + { + Outputs[0].template Get() = FVoxelNodeFunctions::GetDataItemDistance( + Context.Items.ItemHolder, + Inputs[0].template Get(), + Inputs[1].template Get(), + Inputs[2].template Get(), + Inputs[3].template Get(), + Inputs[4].template Get(), + Mask, + CombineMode); + + } + void ComputeCppImpl(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const + { + Constructor.AddLinef(TEXT("%s = FVoxelNodeFunctions::GetDataItemDistance(%s.Items.ItemHolder, %s, %s, %s, %s, %s, %uu, %s);"), + *Outputs[0], + *FVoxelCppIds::Context, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *Inputs[3], + *Inputs[4], + Mask, + *UEnum::GetValueAsString(CombineMode)); + } +}; + +TVoxelSharedPtr UVoxelNode_DataItemSample::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new FVoxelNode_DataItemSample_LocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int32 UVoxelNode_DataItemParameters::GetOutputPinsCount() const +{ + return Config ? Config->Parameters.Num() : 0; +} + +FName UVoxelNode_DataItemParameters::GetOutputPinName(int32 PinIndex) const +{ + if (!Config || !Config->Parameters.IsValidIndex(PinIndex)) + { + return STATIC_FNAME("Invalid"); + } + else + { + return Config->Parameters[PinIndex]; + } +} + +EVoxelPinCategory UVoxelNode_DataItemParameters::GetOutputPinCategory(int32 PinIndex) const +{ + return EVoxelPinCategory::Float; +} + +#if WITH_EDITOR +void UVoxelNode_DataItemParameters::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (PropertyChangedEvent.Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive && Config) + { + const auto OldPreviewValues = MoveTemp(PreviewValues); + if (Config) + { + for (auto& Parameter : Config->Parameters) + { + PreviewValues.Add(Parameter, OldPreviewValues.FindRef(Parameter)); + } + } + } + + Super::PostEditChangeProperty(PropertyChangedEvent); +} +#endif + +TArray UVoxelNode_DataItemParameters::GetPreviewValues() const +{ + TArray Result; + if (Config) + { + for (auto& Parameter : Config->Parameters) + { + Result.Add(PreviewValues.FindRef(Parameter)); + } + } + return Result; +} + +class FVoxelNode_DataItemParameters_LocalVoxelComputeNode : public FVoxelDataComputeNode +{ +public: + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FVoxelNode_DataItemParameters_LocalVoxelComputeNode); + + FVoxelNode_DataItemParameters_LocalVoxelComputeNode (const UVoxelNode_DataItemParameters& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , PreviewValues(Node.GetPreviewValues()) + { + check(OutputCount == PreviewValues.Num()); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + ComputeImpl(Outputs, Context); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + ComputeImpl(Outputs, Context); + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Outputs, Constructor); + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCppImpl(Outputs, Constructor); + } + +private: + const TArray PreviewValues; + + template + void ComputeImpl(TOutputs Outputs, const TContext& Context) const + { + if (Context.Items.CustomData && Context.Items.CustomData->Num() == PreviewValues.Num()) + { + for (int32 Index = 0; Index < OutputCount; Index++) + { + Outputs[Index].template Get() = (*Context.Items.CustomData).GetData()[Index]; + } + } + else + { + for (int32 Index = 0; Index < OutputCount; Index++) + { + Outputs[Index].template Get() = PreviewValues.GetData()[Index]; + } + } + } + void ComputeCppImpl(const TArray& Outputs, FVoxelCppConstructor& Constructor) const + { + Constructor.AddLinef(TEXT("if (%s.Items.CustomData && %s.Items.CustomData->Num() == %d)"), *FVoxelCppIds::Context, *FVoxelCppIds::Context, PreviewValues.Num()); + Constructor.StartBlock(); + for (int32 Index = 0; Index < OutputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = (*%s.Items.CustomData).GetData()[%d];"), *Outputs[Index], *FVoxelCppIds::Context, Index); + } + Constructor.EndBlock(); + Constructor.AddLine("else"); + Constructor.StartBlock(); + for (int32 Index = 0; Index < OutputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = %f;"), *Outputs[Index], PreviewValues[Index]); + } + Constructor.EndBlock(); + } +}; + +TVoxelSharedPtr UVoxelNode_DataItemParameters::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new FVoxelNode_DataItemParameters_LocalVoxelComputeNode (*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp new file mode 100644 index 00000000..9aba7807 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp @@ -0,0 +1,146 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelRandomNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelNodeFunctions.h" + +UVoxelNode_RandomFloat::UVoxelNode_RandomFloat() +{ + SetInputs({"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Float); +} + +FText UVoxelNode_RandomFloat::GetTitle() const +{ + return FText::FromString("Rand Float " + FString::SanitizeFloat(Min) + " " + FString::SanitizeFloat(Max)); +} + +TVoxelSharedPtr UVoxelNode_RandomFloat::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_RandomFloat& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Min(Node.Min) + , Max(Node.Max) + , RandFloatVariable("float", UniqueName.ToString() + "_RandFloat") + { + } + + void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(RandFloatVariable); + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + RandFloat = FRandomStream(Inputs[0]).FRandRange(Min, Max); + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(RandFloatVariable.CppName + " = FRandomStream(" + Inputs[0] + ")" + ".FRandRange(" + FString::SanitizeFloat(Min) + "f, " + FString::SanitizeFloat(Max) + "f);"); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = RandFloat; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = RandFloat; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + RandFloatVariable.CppName + ";"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *RandFloatVariable.CppName); + } + + private: + float Min = 0; + float Max = 0; + float RandFloat = 0; + FVoxelVariable const RandFloatVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RandomInt::UVoxelNode_RandomInt() +{ + SetInputs({"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Int); +} + +FText UVoxelNode_RandomInt::GetTitle() const +{ + return FText::FromString("Rand Int " + FString::FromInt(Min) + " " + FString::FromInt(Max)); +} + +TVoxelSharedPtr UVoxelNode_RandomInt::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY() + + FLocalVoxelComputeNode(const UVoxelNode_RandomInt& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , Min(Node.Min) + , Max(Node.Max) + , RandIntVariable("int32", UniqueName.ToString() + "_RandInt") + { + } + + void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(RandIntVariable); + } + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + RandInt = FRandomStream(Inputs[0]).RandRange(Min, Max); + } + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(RandIntVariable.CppName + " = FRandomStream(" + Inputs[0] + ")" + ".RandRange(" + FString::FromInt(Min) + ", " + FString::FromInt(Max) + ");"); + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = RandInt; + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = RandInt; + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + RandIntVariable.CppName + ";"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = %s;"), *Outputs[0], *RandIntVariable.CppName); + } + + private: + int32 Min; + int32 Max; + int32 RandInt = 0; + FVoxelVariable const RandIntVariable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp new file mode 100644 index 00000000..9d1c0cf6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp @@ -0,0 +1,444 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelSDFNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelContext.h" +#include "NodeFunctions/VoxelSDFNodeFunctions.h" + +UVoxelSDFNode::UVoxelSDFNode() +{ + AddOutput("", "Result"); +} + +void UVoxelSDFNode::AddPositionInput() +{ + AddVectorInput("Position", "Position to sample the distance function at. Usually X Y Z, with possible some symmetries or perturb applied to them."); +} + +void UVoxelSDFNode::AddStartInput() +{ + AddVectorInput("Start", "Start of the shape"); +} + +void UVoxelSDFNode::AddEndInput() +{ + AddVectorInput("End", "End of the shape"); +} + +void UVoxelSDFNode::AddNormalInput() +{ + AddVectorInput("Normal", "Normal/direction of the shape. Must be normalized!"); +} + +void UVoxelSDFNode::AddRadiusInput() +{ + AddInput("Radius", "Radius of the shape"); +} + +void UVoxelSDFNode::AddStartRadiusInput() +{ + AddInput("Start Radius", "Radius at the start of the shape"); +} + +void UVoxelSDFNode::AddEndRadiusInput() +{ + AddInput("End Radius", "Radius at the end of the shape"); +} + +void UVoxelSDFNode::AddLengthInput() +{ + AddInput("Length", "Length of the shape"); +} + +void UVoxelSDFNode::AddHeightInput() +{ + AddInput("Height", "Height of the shape"); +} + +void UVoxelSDFNode::AddSizeInput() +{ + AddVectorInput("Size", "Size of the shape"); +} + +void UVoxelSDFNode::AddSize2DInput() +{ + AddVector2DInput("Size", "Size of the shape"); +} + +void UVoxelSDFNode::AddSize1DInput() +{ + AddInput("Size", "Size of the shape"); +} + +void UVoxelSDFNode::AddSmoothnessInput() +{ + AddInput("Smoothness", "Controls the smoothness. In the same unit as the size/position input: if you have a size of 100, a smoothness of 30 will result in roughly a 30% displacement."); +} + +void UVoxelSDFNode::AddThicknessInput() +{ + AddInput("Thickness", "Thickness of the shape."); +} + +void UVoxelSDFNode::AddSinCosInput() +{ + AddInput("Sin Angle", "Sinus of the angle. Use the SIN COS node to get it."); + AddInput("Cos Angle", "Cosinus of the angle. Use the SIN COS node to get it."); +} + +void UVoxelSDFNode::AddDistanceAInput() +{ + AddInput("Distance A", "SDF A"); +} + +void UVoxelSDFNode::AddDistanceBInput() +{ + AddInput("Distance B", "SDF B"); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define DEFINE_SDF_INPUTS_0 DEFINE_INPUTS() +#define DEFINE_SDF_INPUTS_1 DEFINE_INPUTS(v_flt) +#define DEFINE_SDF_INPUTS_2 DEFINE_INPUTS(v_flt, v_flt) +#define DEFINE_SDF_INPUTS_3 DEFINE_INPUTS(v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_4 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_5 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_6 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_7 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_8 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_9 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_10 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) +#define DEFINE_SDF_INPUTS_11 DEFINE_INPUTS(v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt, v_flt) + +#define SDF_ARGS_0 +#define SDF_ARGS_1 Input0 +#define SDF_ARGS_2 Input0, Input1 +#define SDF_ARGS_3 Input0, Input1, Input2 +#define SDF_ARGS_4 Input0, Input1, Input2, Input3 +#define SDF_ARGS_5 Input0, Input1, Input2, Input3, Input4 +#define SDF_ARGS_6 Input0, Input1, Input2, Input3, Input4, Input5 +#define SDF_ARGS_7 Input0, Input1, Input2, Input3, Input4, Input5, Input6 +#define SDF_ARGS_8 Input0, Input1, Input2, Input3, Input4, Input5, Input6, Input7 +#define SDF_ARGS_9 Input0, Input1, Input2, Input3, Input4, Input5, Input6, Input7, Input8 +#define SDF_ARGS_10 Input0, Input1, Input2, Input3, Input4, Input5, Input6, Input7, Input8, Input9 +#define SDF_ARGS_11 Input0, Input1, Input2, Input3, Input4, Input5, Input6, Input7, Input8, Input9, Input10 + +#define GENERATED_SDF_NODE_IMPL_EX(Name, ClassName, Count) \ + GENERATED_VOXELNODE_IMPL \ + ( \ + ClassName, \ + DEFINE_SDF_INPUTS_##Count, \ + DEFINE_OUTPUTS(v_flt), \ + Output0 = FVoxelSDFNodeFunctions::Name(SDF_ARGS_##Count); \ + ) + +#define GENERATED_SDF_NODE_IMPL(Name, Count) GENERATED_SDF_NODE_IMPL_EX(Name, UVoxelNode_##Name##SDF, Count) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SphereSDF::UVoxelNode_SphereSDF() +{ + AddPositionInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(Sphere, 4) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_BoxSDF::UVoxelNode_BoxSDF() +{ + AddPositionInput(); + AddSizeInput(); +} +GENERATED_SDF_NODE_IMPL(Box, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RoundBoxSDF::UVoxelNode_RoundBoxSDF() +{ + AddPositionInput(); + AddSizeInput(); + AddSmoothnessInput(); +} +GENERATED_SDF_NODE_IMPL(RoundBox, 7) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_TorusSDF::UVoxelNode_TorusSDF() +{ + AddPositionInput(); + AddRadiusInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(Torus, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CappedTorusSDF::UVoxelNode_CappedTorusSDF() +{ + AddPositionInput(); + AddSinCosInput(); + AddRadiusInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(CappedTorus, 7) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_LinkSDF::UVoxelNode_LinkSDF() +{ + AddPositionInput(); + AddLengthInput(); + AddRadiusInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(Link, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CylinderSDF::UVoxelNode_CylinderSDF() +{ + AddPositionInput(); + AddSize2DInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(Cylinder, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_ConeSDF::UVoxelNode_ConeSDF() +{ + AddPositionInput(); + AddSinCosInput(); + AddHeightInput(); +} +GENERATED_SDF_NODE_IMPL(Cone, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_ConeFastSDF::UVoxelNode_ConeFastSDF() +{ + AddPositionInput(); + AddSinCosInput(); + AddHeightInput(); +} +GENERATED_SDF_NODE_IMPL(ConeFast, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_InfiniteConeSDF::UVoxelNode_InfiniteConeSDF() +{ + AddPositionInput(); + AddSinCosInput(); +} +GENERATED_SDF_NODE_IMPL_EX(Cone, UVoxelNode_InfiniteConeSDF, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_PlaneSDF::UVoxelNode_PlaneSDF() +{ + AddPositionInput(); + AddNormalInput(); +} +GENERATED_SDF_NODE_IMPL(Plane, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_HexPrismSDF::UVoxelNode_HexPrismSDF() +{ + AddPositionInput(); + AddRadiusInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(HexPrism, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_TriPrismSDF::UVoxelNode_TriPrismSDF() +{ + AddPositionInput(); + AddRadiusInput(); + AddThicknessInput(); +} +GENERATED_SDF_NODE_IMPL(TriPrism, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CapsuleSDF::UVoxelNode_CapsuleSDF() +{ + AddPositionInput(); + AddStartInput(); + AddEndInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(Capsule, 10) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VerticalCapsuleSDF::UVoxelNode_VerticalCapsuleSDF() +{ + AddPositionInput(); + AddHeightInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(VerticalCapsule, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VerticalCappedCylinderSDF::UVoxelNode_VerticalCappedCylinderSDF() +{ + AddPositionInput(); + AddHeightInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL_EX(CappedCylinder, UVoxelNode_VerticalCappedCylinderSDF, 5) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CappedCylinderSDF::UVoxelNode_CappedCylinderSDF() +{ + AddPositionInput(); + AddStartInput(); + AddEndInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(CappedCylinder, 10) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RoundedCylinderSDF::UVoxelNode_RoundedCylinderSDF() +{ + AddPositionInput(); + AddRadiusInput(); + AddSmoothnessInput(); + AddHeightInput(); +} +GENERATED_SDF_NODE_IMPL(RoundedCylinder, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VerticalCappedConeSDF::UVoxelNode_VerticalCappedConeSDF() +{ + AddPositionInput(); + AddHeightInput(); + AddStartInput(); + AddEndInput(); +} +GENERATED_SDF_NODE_IMPL_EX(CappedCone, UVoxelNode_VerticalCappedConeSDF, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_CappedConeSDF::UVoxelNode_CappedConeSDF() +{ + AddPositionInput(); + AddStartInput(); + AddEndInput(); + AddStartRadiusInput(); + AddEndRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(CappedCone, 11) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SolidAngleSDF::UVoxelNode_SolidAngleSDF() +{ + AddPositionInput(); + AddSinCosInput(); + AddRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(SolidAngle, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VerticalRoundConeSDF::UVoxelNode_VerticalRoundConeSDF() +{ + AddPositionInput(); + AddStartRadiusInput(); + AddEndRadiusInput(); + AddHeightInput(); +} +GENERATED_SDF_NODE_IMPL_EX(RoundCone, UVoxelNode_VerticalRoundConeSDF, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_RoundConeSDF::UVoxelNode_RoundConeSDF() +{ + AddPositionInput(); + AddStartInput(); + AddEndInput(); + AddStartRadiusInput(); + AddEndRadiusInput(); +} +GENERATED_SDF_NODE_IMPL(RoundCone, 11) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EllipsoidSDF::UVoxelNode_EllipsoidSDF() +{ + AddPositionInput(); + AddSizeInput(); +} +GENERATED_SDF_NODE_IMPL(Ellipsoid, 6) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_OctahedronSDF::UVoxelNode_OctahedronSDF() +{ + AddPositionInput(); + AddSize1DInput(); +} +GENERATED_SDF_NODE_IMPL(Octahedron, 4) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_OctahedronFastSDF::UVoxelNode_OctahedronFastSDF() +{ + AddPositionInput(); + AddSize1DInput(); +} +GENERATED_SDF_NODE_IMPL(OctahedronFast, 4) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_PyramidSDF::UVoxelNode_PyramidSDF() +{ + AddPositionInput(); + AddHeightInput(); +} +GENERATED_SDF_NODE_IMPL(Pyramid, 4) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmoothUnion::UVoxelNode_SmoothUnion() +{ + AddDistanceAInput(); + AddDistanceBInput(); + AddSmoothnessInput(); +} +GENERATED_SDF_NODE_IMPL_EX(SmoothUnion, UVoxelNode_SmoothUnion, 3) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmoothSubtraction::UVoxelNode_SmoothSubtraction() +{ + AddDistanceAInput(); + AddDistanceBInput(); + AddSmoothnessInput(); +} +GENERATED_SDF_NODE_IMPL_EX(SmoothSubtraction, UVoxelNode_SmoothSubtraction, 3) + +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmoothIntersection::UVoxelNode_SmoothIntersection() +{ + AddDistanceAInput(); + AddDistanceBInput(); + AddSmoothnessInput(); +} +GENERATED_SDF_NODE_IMPL_EX(SmoothIntersection, UVoxelNode_SmoothIntersection, 3) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp new file mode 100644 index 00000000..1e221025 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp @@ -0,0 +1,155 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelSeedNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGraphGlobals.h" + +UVoxelSeedNode::UVoxelSeedNode() +{ + SetColor(FVoxelNodeColors::SeedNode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_Seed::UVoxelNode_Seed() +{ + SetOutputs(EC::Seed); + SetColor(FVoxelNodeColors::SeedNode); +} + +TVoxelSharedPtr UVoxelNode_Seed::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSeedComputeNode + { + public: + FLocalVoxelComputeNode(const UVoxelNode_Seed& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelSeedComputeNode(Node, CompilationNode) + , Value(Node.GetParameter()) + , Variable(MakeShared(Node, FVoxelCppUtils::TypeToString(), FVoxelCppUtils::TypeToString(), FVoxelCppUtils::LexToCpp(Node.DefaultValue))) + { + } + + void Init(FVoxelGraphSeed Inputs[], FVoxelGraphSeed Outputs[], const FVoxelGeneratorInit& InitStruct) override + { + Outputs[0] = Value; + } + void InitCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + Variable->CppName + ";"); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const int32 Value; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_AddSeeds::UVoxelNode_AddSeeds() +{ + SetInputs(EC::Seed); + SetOutputs(EC::Seed); + SetInputsCount(1, MAX_VOXELNODE_PINS); +} + +void UVoxelNode_Seed::PostLoad() +{ + Super::PostLoad(); + + if (!Name_DEPRECATED.IsNone()) + { + DisplayName = Name_DEPRECATED.ToString(); + UniqueName = Name_DEPRECATED; + } +} + +TVoxelSharedPtr UVoxelNode_AddSeeds::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSeedComputeNode + { + public: + using FVoxelSeedComputeNode::FVoxelSeedComputeNode; + + void Init(FVoxelGraphSeed Inputs[], FVoxelGraphSeed Outputs[], const FVoxelGeneratorInit& InitStruct) override + { + uint32 X = FVoxelUtilities::MurmurHash32(Inputs[0]); + for (int32 I = 1; I < InputCount; I++) + { + X = FVoxelUtilities::MurmurHash32(X ^ FVoxelUtilities::MurmurHash32(Inputs[I])); + } + Outputs[0] = X; + } + void InitCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + FString Line; + Line += Outputs[0] + " = "; + + for (int32 I = 0; I < InputCount - 1; I++) + { + Line += "FVoxelUtilities::MurmurHash32(FVoxelUtilities::MurmurHash32(" + Inputs[I] + ") ^ "; + } + Line += "FVoxelUtilities::MurmurHash32(" + Inputs[InputCount - 1]; + + for (int32 I = 0; I < InputCount; I++) + { + Line += ")"; + } + Line += ";"; + + Constructor.AddLine(Line); + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_MakeSeeds::UVoxelNode_MakeSeeds() +{ + SetInputs(EC::Seed); + SetOutputs(EC::Seed); +} + +TVoxelSharedPtr UVoxelNode_MakeSeeds::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelSeedComputeNode + { + public: + using FVoxelSeedComputeNode::FVoxelSeedComputeNode; + + void Init(FVoxelGraphSeed Inputs[], FVoxelGraphSeed Outputs[], const FVoxelGeneratorInit& InitStruct) override + { + for (int32 Index = 0; Index < OutputCount; Index++) + { + Outputs[Index] = FVoxelUtilities::MurmurHash32(Index == 0 ? Inputs[0] : Outputs[Index - 1]); + } + } + void InitCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + for (int32 Index = 0; Index < OutputCount; Index++) + { + Constructor.AddLinef(TEXT("%s = FVoxelUtilities::MurmurHash32(%s);"), *Outputs[Index], Index == 0 ? *Inputs[0] : *Outputs[Index - 1]); + } + } + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp new file mode 100644 index 00000000..18e3853a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp @@ -0,0 +1,250 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelTextureSamplerNode.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "VoxelNodes/VoxelNodeVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "Engine/Texture2D.h" + +UVoxelNode_TextureSampler::UVoxelNode_TextureSampler() +{ + SetInputs( + { "X", EC::Float, "Coordinate between 0 and texture width" }, + { "Y", EC::Float, "Coordinate between 0 and texture height" }); + SetOutputs( + { "R", EC::Float, "Red between 0 and 1" }, + { "G", EC::Float, "Green between 0 and 1" }, + { "B", EC::Float, "Blue between 0 and 1" }, + { "A", EC::Float, "Alpha between 0 and 1" }); +} + +TVoxelSharedPtr UVoxelNode_TextureSampler::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_TextureSampler& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , bBilinearInterpolation(Node.bBilinearInterpolation) + , Mode(Node.Mode) + , Texture(FVoxelTextureUtilities::CreateFromTexture_Color(Node.Texture)) + , Variable(MakeShared(Node, Node.Texture)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + if (bBilinearInterpolation) + { + FVoxelNodeFunctions::ReadColorTextureDataFloat( + Texture, + Mode, + Inputs[0].Get(), + Inputs[1].Get(), + Outputs[0].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + } + else + { + FVoxelNodeFunctions::ReadColorTextureDataInt( + Texture, + Mode, + Inputs[0].Get(), + Inputs[1].Get(), + Outputs[0].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + } + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + if (bBilinearInterpolation) + { + FVoxelNodeFunctions::ReadColorTextureDataFloat( + Texture, + Mode, + Inputs[0].Get(), + Inputs[1].Get(), + Outputs[0].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + } + else + { + FVoxelNodeFunctions::ReadColorTextureDataInt( + Texture, + Mode, + Inputs[0].Get(), + Inputs[1].Get(), + Outputs[0].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + } + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine( + "FVoxelNodeFunctions::" + FString(bBilinearInterpolation ? "ReadColorTextureDataFloat" : "ReadColorTextureDataInt") + "(" + + Variable->CppName + "," + + (Mode == EVoxelSamplerMode::Clamp ? "EVoxelSamplerMode::Clamp" : "EVoxelSamplerMode::Tile") + "," + + Inputs[0] + "," + + Inputs[1] + "," + + Outputs[0] + "," + + Outputs[1] + "," + + Outputs[2] + "," + + Outputs[3] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const bool bBilinearInterpolation; + const EVoxelSamplerMode Mode; + const TVoxelTexture Texture; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +EVoxelPinCategory UVoxelNode_TextureSampler::GetInputPinCategory(int32 PinIndex) const +{ + return bBilinearInterpolation ? EC::Float : EC::Int; +} + +FText UVoxelNode_TextureSampler::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Texture: {0}"), Super::GetTitle()); +} + +void UVoxelNode_TextureSampler::LogErrors(FVoxelGraphErrorReporter& ErrorReporter) +{ + Super::LogErrors(ErrorReporter); + + FString Error; + if (!FVoxelTextureUtilities::CanCreateFromTexture(Texture, Error)) + { + ErrorReporter.AddMessageToNode(this, Error, EVoxelGraphNodeMessageType::Error); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_VoxelTextureSampler::UVoxelNode_VoxelTextureSampler() +{ + SetInputs( + {"X", EC::Float, "Coordinate between 0 and texture width"}, + {"Y", EC::Float, "Coordinate between 0 and texture height"}); + SetOutputs(EC::Float); +} + +TVoxelSharedPtr UVoxelNode_VoxelTextureSampler::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + + class FLocalVoxelComputeNode : public FVoxelDataComputeNode + { + public: + GENERATED_DATA_COMPUTE_NODE_BODY(); + + FLocalVoxelComputeNode(const UVoxelNode_VoxelTextureSampler& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , bBilinearInterpolation(Node.bBilinearInterpolation) + , Mode(Node.Mode) + , Texture(Node.GetParameter().Texture) + , Variable(MakeShared(Node)) + { + } + + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + if (bBilinearInterpolation) + { + Outputs[0].Get() = FVoxelNodeFunctions::ReadFloatTextureDataFloat(Texture, Mode, Inputs[0].Get(), Inputs[1].Get()); + } + else + { + Outputs[0].Get() = FVoxelNodeFunctions::ReadFloatTextureDataInt(Texture, Mode, Inputs[0].Get(), Inputs[1].Get()); + } + } + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + if (bBilinearInterpolation) + { + Outputs[0].Get() = FVoxelNodeFunctions::ReadFloatTextureDataFloat(Texture, Mode, Inputs[0].Get(), Inputs[1].Get()); + } + else + { + Outputs[0].Get() = FVoxelNodeFunctions::ReadFloatTextureDataInt(Texture, Mode, Inputs[0].Get(), Inputs[1].Get()); + } + } + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(Outputs[0] + " = " + + "FVoxelNodeFunctions::" + FString(bBilinearInterpolation ? "ReadFloatTextureDataFloat" : "ReadFloatTextureDataInt") + "(" + + Variable->CppName + "," + + (Mode == EVoxelSamplerMode::Clamp ? "EVoxelSamplerMode::Clamp" : "EVoxelSamplerMode::Tile") + "," + + Inputs[0] + "," + + Inputs[1] + ");"); + } + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + ComputeCpp(Inputs, Outputs, Constructor); + } + void SetupCpp(FVoxelCppConfig& Config) const override + { + Config.AddExposedVariable(Variable); + } + + private: + const bool bBilinearInterpolation; + const EVoxelSamplerMode Mode; + const TVoxelTexture Texture; + const TSharedRef Variable; + }; + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); +} + +EVoxelPinCategory UVoxelNode_VoxelTextureSampler::GetInputPinCategory(int32 PinIndex) const +{ + return bBilinearInterpolation ? EC::Float : EC::Int; +} + +FText UVoxelNode_VoxelTextureSampler::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Voxel Texture: {0}"), Super::GetTitle()); +} + +#if WITH_EDITOR +void UVoxelNode_VoxelTextureSampler::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.Property && + PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_STATIC(UVoxelNode_TextureSampler, bBilinearInterpolation) && + GraphNode && + Graph) + { + GraphNode->ReconstructNode(); + Graph->CompileVoxelNodesFromGraphNodes(); + } +} +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp new file mode 100644 index 00000000..072e8bc9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp @@ -0,0 +1,301 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelVoronoiNoiseNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelGraphGenerator.h" +#include "FastNoise/VoxelFastNoise.inl" + +int32 UVoxelNode_VoronoiNoiseBase::GetMinInputPins() const +{ + return GetPins().InputPins.Num(); +} + +int32 UVoxelNode_VoronoiNoiseBase::GetMaxInputPins() const +{ + return GetMinInputPins(); +} + +int32 UVoxelNode_VoronoiNoiseBase::GetOutputPinsCount() const +{ + return GetPins().OutputPins.Num(); +} + +EVoxelPinCategory UVoxelNode_VoronoiNoiseBase::GetInputPinCategory(int32 PinIndex) const +{ + return GetPins().GetInputPin(PinIndex).Category; +} + +EVoxelPinCategory UVoxelNode_VoronoiNoiseBase::GetOutputPinCategory(int32 PinIndex) const +{ + return GetPins().GetOutputPin(PinIndex).Category; +} + +FName UVoxelNode_VoronoiNoiseBase::GetInputPinName(int32 PinIndex) const +{ + return GetPins().GetInputPin(PinIndex).Name; +} + +FName UVoxelNode_VoronoiNoiseBase::GetOutputPinName(int32 PinIndex) const +{ + return GetPins().GetOutputPin(PinIndex).Name; +} + +FString UVoxelNode_VoronoiNoiseBase::GetInputPinToolTip(int32 PinIndex) const +{ + return GetPins().GetInputPin(PinIndex).ToolTip; +} + +FString UVoxelNode_VoronoiNoiseBase::GetOutputPinToolTip(int32 PinIndex) const +{ + return GetPins().GetOutputPin(PinIndex).ToolTip; +} + +FVoxelPinDefaultValueBounds UVoxelNode_VoronoiNoiseBase::GetInputPinDefaultValueBounds(int32 PinIndex) const +{ + return GetPins().GetInputPin(PinIndex).DefaultValueBounds; +} + +FString UVoxelNode_VoronoiNoiseBase::GetInputPinDefaultValue(int32 PinIndex) const +{ + return GetPins().GetInputPin(PinIndex).DefaultValue; +} + +const FVoxelPinsHelper& UVoxelNode_VoronoiNoiseBase::GetPins() const +{ + static const TArray InputPinsDim2 = + { + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Jitter", EC::Float, "Jitter of the noise. Increase this to make the noise less blocky", "0.45"}, + {"Seed", EC::Seed, "Seed"} + }; + static const TArray InputPinsDim3 = + { + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Z", EC::Float, "Z"}, + {"Jitter", EC::Float, "Jitter of the noise. Increase this to make the noise less blocky", "0.45"}, + {"Seed", EC::Seed, "Seed"} + }; + static const TArray OutputPinsDim2 = + { + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"} + }; + static const TArray OutputPinsDim3 = + { + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Z", EC::Float, "Z"} + }; + static const TArray OutputPinsDim2Neighbors = + { + {"X 0", EC::Float, "X coordinate of the closest cell"}, + {"Y 0", EC::Float, "Y coordinate of the closest cell"}, + {"X 1", EC::Float, "X coordinate of the second closest cell"}, + {"Y 1", EC::Float, "Y coordinate of the second closest cell"}, + {"Distance 1", EC::Float, "Distance to the border of second closest cell"}, + {"X 2", EC::Float, "X coordinate of the third closest cell"}, + {"Y 2", EC::Float, "Y coordinate of the third closest cell"}, + {"Distance 2", EC::Float, "Distance to the border of the third closest cell"}, + {"X 3", EC::Float, "X coordinate of the fourth closest cell"}, + {"Y 3", EC::Float, "Y coordinate of the fourth closest cell"}, + {"Distance 3", EC::Float, "Distance to the border of the fourth closest cell"} + }; + static const TArray OutputPinsDim3Neighbors = + { + }; + + static const FVoxelPinsHelper PinsDim2 = { InputPinsDim2, OutputPinsDim2 }; + static const FVoxelPinsHelper PinsDim3 = { InputPinsDim3, OutputPinsDim3 }; + static const FVoxelPinsHelper PinsDim2Neighbors = { InputPinsDim2, OutputPinsDim2Neighbors }; + static const FVoxelPinsHelper PinsDim3Neighbors = { InputPinsDim3, OutputPinsDim3Neighbors }; + + if (bComputeNeighbors) + { + return Dimension == 2 ? PinsDim2Neighbors : PinsDim3Neighbors; + } + else + { + return Dimension == 2 ? PinsDim2 : PinsDim3; + } +} + +template +class TVoxelVoronoiNoiseComputeNode : public FVoxelDataComputeNode +{ +public: + using FLocalVoxelComputeNode = TVoxelVoronoiNoiseComputeNode; + GENERATED_DATA_COMPUTE_NODE_BODY(); + + TVoxelVoronoiNoiseComputeNode(const UVoxelNode_VoronoiNoiseBase& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , bComputeNeighbors(Node.bComputeNeighbors) + , NoiseVariable("FVoxelFastNoise", UniqueName.ToString() + "_Noise") + { + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + PrivateNoise.SetSeed(Inputs[Dimension + 1]); + } + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(NoiseVariable.CppName + ".SetSeed(" + Inputs[Dimension + 1] + ");"); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + if (Dimension == 2) + { + if (bComputeNeighbors) + { + PrivateNoise.GetVoronoiNeighbors_2D( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Outputs[0].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get(), + Outputs[4].Get(), + Outputs[5].Get(), + Outputs[6].Get(), + Outputs[7].Get(), + Outputs[8].Get(), + Outputs[9].Get(), + Outputs[10].Get()); + } + else + { + PrivateNoise.GetVoronoi_2D( + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Outputs[0].Get(), + Outputs[1].Get()); + } + } + } + // TODO distances are positive + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + if (Dimension == 2) + { + if (bComputeNeighbors) + { + Outputs[0].Get() = TVoxelRange::Infinite(); + Outputs[1].Get() = TVoxelRange::Infinite(); + Outputs[2].Get() = TVoxelRange::Infinite(); + Outputs[3].Get() = TVoxelRange::Infinite(); + + Outputs[4].Get() = TVoxelRange::PositiveInfinite(); + + Outputs[5].Get() = TVoxelRange::Infinite(); + Outputs[6].Get() = TVoxelRange::Infinite(); + + Outputs[7].Get() = TVoxelRange::PositiveInfinite(); + + Outputs[8].Get() = TVoxelRange::Infinite(); + Outputs[9].Get() = TVoxelRange::Infinite(); + + Outputs[10].Get() = TVoxelRange::PositiveInfinite(); + } + else + { + Outputs[0].Get() = TVoxelRange::Infinite(); + Outputs[1].Get() = TVoxelRange::Infinite(); + } + } + } + + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + if (Dimension == 2) + { + if (bComputeNeighbors) + { + Constructor.AddLinef(TEXT("%s.GetVoronoiNeighbors_2D(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);"), + *NoiseVariable.CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *Outputs[0], + *Outputs[1], + *Outputs[2], + *Outputs[3], + *Outputs[4], + *Outputs[5], + *Outputs[6], + *Outputs[7], + *Outputs[8], + *Outputs[9], + *Outputs[10]); + } + else + { + Constructor.AddLinef(TEXT("%s.GetVoronoi_2D(%s, %s, %s, %s, %s);"), + *NoiseVariable.CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2], + *Outputs[0], + *Outputs[1]); + } + } + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + if (Dimension == 2) + { + if (bComputeNeighbors) + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[0]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[1]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[2]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[3]); + + Constructor.AddLinef(TEXT("%s = TVoxelRange::PositiveInfinite();"), *Outputs[4]); + + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[5]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[6]); + + Constructor.AddLinef(TEXT("%s = TVoxelRange::PositiveInfinite();"), *Outputs[7]); + + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[8]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[9]); + + Constructor.AddLinef(TEXT("%s = TVoxelRange::PositiveInfinite();"), *Outputs[10]); + } + else + { + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[0]); + Constructor.AddLinef(TEXT("%s = TVoxelRange::Infinite();"), *Outputs[1]); + } + } + } + + virtual void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(NoiseVariable); + } + +private: + const bool bComputeNeighbors; + const FVoxelVariable NoiseVariable; + FVoxelFastNoise PrivateNoise; +}; + +UVoxelNode_2DVoronoiNoise::UVoxelNode_2DVoronoiNoise() +{ + Dimension = 2; +} + +TVoxelSharedPtr UVoxelNode_2DVoronoiNoise::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new TVoxelVoronoiNoiseComputeNode<2>(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp new file mode 100644 index 00000000..5ee3e92a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp @@ -0,0 +1,109 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelWhiteNoiseNodes.h" +#include "VoxelNodes/VoxelNodeHelpers.h" +#include "Runtime/VoxelComputeNode.h" +#include "CppTranslation/VoxelVariables.h" +#include "CppTranslation/VoxelCppConfig.h" +#include "CppTranslation/VoxelCppConstructor.h" +#include "VoxelGraphGenerator.h" +#include "FastNoise/VoxelFastNoise.inl" + +template +class TVoxelWhiteNoiseComputeNode : public FVoxelDataComputeNode +{ +public: + using FLocalVoxelComputeNode = TVoxelWhiteNoiseComputeNode; + GENERATED_DATA_COMPUTE_NODE_BODY(); + + TVoxelWhiteNoiseComputeNode(const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelDataComputeNode(Node, CompilationNode) + , NoiseVariable("FVoxelFastNoise", UniqueName.ToString() + "_Noise") + { + } + + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override + { + PrivateNoise.SetSeed(Inputs[Dimension]); + } + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLine(NoiseVariable.CppName + ".SetSeed(" + Inputs[Dimension] + ");"); + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override + { + Outputs[0].Get() = + Dimension == 2 + ? PrivateNoise.GetWhiteNoise_2D(Inputs[0].Get(), Inputs[1].Get()) + : PrivateNoise.GetWhiteNoise_3D(Inputs[0].Get(), Inputs[1].Get(), Inputs[2].Get()); + } + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override + { + Outputs[0].Get() = { -1, 1 }; + } + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + if (Dimension == 2) + { + Constructor.AddLinef( + TEXT("%s = %s.GetWhiteNoise_2D(%s, %s);"), + *Outputs[0], + *NoiseVariable.CppName, + *Inputs[0], + *Inputs[1]); + } + else + { + Constructor.AddLinef( + TEXT("%s = %s.GetWhiteNoise_3D(%s, %s, %s);"), + *Outputs[0], + *NoiseVariable.CppName, + *Inputs[0], + *Inputs[1], + *Inputs[2]); + } + } + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override + { + Constructor.AddLinef(TEXT("%s = { -1, 1 };"), *Outputs[0]); + } + + virtual void GetPrivateVariables(TArray& PrivateVariables) const override + { + PrivateVariables.Add(NoiseVariable); + } + +private: + FVoxelVariable const NoiseVariable; + FVoxelFastNoise PrivateNoise; +}; + +UVoxelNode_2DWhiteNoise::UVoxelNode_2DWhiteNoise() +{ + SetInputs( + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Float); +} + +TVoxelSharedPtr UVoxelNode_2DWhiteNoise::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new TVoxelWhiteNoiseComputeNode<2>(*this, InCompilationNode)); +} + +UVoxelNode_3DWhiteNoise::UVoxelNode_3DWhiteNoise() +{ + SetInputs( + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Z", EC::Float, "Z"}, + {"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Float); +} + +TVoxelSharedPtr UVoxelNode_3DWhiteNoise::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const +{ + return MakeShareable(new TVoxelWhiteNoiseComputeNode<3>(*this, InCompilationNode)); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelPinCategory.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelPinCategory.cpp new file mode 100644 index 00000000..e6e284f2 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Private/VoxelPinCategory.cpp @@ -0,0 +1,355 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelPinCategory.h" +#include "Runtime/VoxelNodeType.h" + +static const FName PC_Exec(TEXT("exec")); +static const FName PC_Boolean(TEXT("bool")); +static const FName PC_Int(TEXT("int")); +static const FName PC_Float(TEXT("float")); +static const FName PC_Material(TEXT("FVoxelMaterial")); +static const FName PC_Color(TEXT("Color")); +static const FName PC_Seed(TEXT("Seed")); +static const FName PC_Wildcard(TEXT("Wildcard")); +static const FName PC_Vector(TEXT("Vector")); + +EVoxelPinCategory FVoxelPinCategory::DataPinToPin(EVoxelDataPinCategory Category) +{ + switch (Category) + { + case EVoxelDataPinCategory::Boolean: + return EVoxelPinCategory::Boolean; + case EVoxelDataPinCategory::Int: + return EVoxelPinCategory::Int; + case EVoxelDataPinCategory::Float: + return EVoxelPinCategory::Float; + case EVoxelDataPinCategory::Material: + return EVoxelPinCategory::Material; + case EVoxelDataPinCategory::Color: + return EVoxelPinCategory::Color; + default: + check(false); + return EVoxelPinCategory::Boolean; + } +} + +EVoxelPinCategory FVoxelPinCategory::FromString(const FName& String) +{ + if (String == PC_Exec) + { + return EVoxelPinCategory::Exec; + } + else if (String == PC_Boolean) + { + return EVoxelPinCategory::Boolean; + } + else if (String == PC_Int) + { + return EVoxelPinCategory::Int; + } + else if (String == PC_Float) + { + return EVoxelPinCategory::Float; + } + else if (String == PC_Material) + { + return EVoxelPinCategory::Material; + } + else if (String == PC_Color) + { + return EVoxelPinCategory::Color; + } + else if (String == PC_Seed) + { + return EVoxelPinCategory::Seed; + } + else if (String == PC_Wildcard) + { + return EVoxelPinCategory::Wildcard; + } + else if (String == PC_Vector) + { + return EVoxelPinCategory::Vector; + } + else + { + ensure(false); + return EVoxelPinCategory::Exec; + } +} + +FName FVoxelPinCategory::GetName(EVoxelPinCategory Category) +{ + switch (Category) + { + case EVoxelPinCategory::Exec: + return PC_Exec; + case EVoxelPinCategory::Boolean: + return PC_Boolean; + case EVoxelPinCategory::Int: + return PC_Int; + case EVoxelPinCategory::Float: + return PC_Float; + case EVoxelPinCategory::Material: + return PC_Material; + case EVoxelPinCategory::Color: + return PC_Color; + case EVoxelPinCategory::Seed: + return PC_Seed; + case EVoxelPinCategory::Wildcard: + return PC_Wildcard; + case EVoxelPinCategory::Vector: + return PC_Vector; + default: + check(false); + return FName(); + } +} + +FString FVoxelPinCategory::GetDefaultValue(EVoxelPinCategory Category) +{ + switch (Category) + { + case EVoxelPinCategory::Exec: + return FString(); + case EVoxelPinCategory::Boolean: + return FString(); + case EVoxelPinCategory::Int: + return TEXT("0"); + case EVoxelPinCategory::Float: + return TEXT("0"); + case EVoxelPinCategory::Material: + return FString(); + case EVoxelPinCategory::Color: + return FString(); + case EVoxelPinCategory::Seed: + return TEXT("1337"); + case EVoxelPinCategory::Wildcard: + return FString(); + case EVoxelPinCategory::Vector: + return FString(); + default: + check(false); + return FString(); + } +} + +FString FVoxelPinCategory::GetDefaultValue(EVoxelDataPinCategory Category) +{ + const EVoxelPinCategory PinCategory = DataPinToPin(Category); + return GetDefaultValue(PinCategory); +} + +FString FVoxelPinCategory::GetTypeString(EVoxelPinCategory Category) +{ + switch (Category) + { + case EVoxelPinCategory::Boolean: + return "bool"; + case EVoxelPinCategory::Int: + return "int32"; + case EVoxelPinCategory::Float: + return "v_flt"; + case EVoxelPinCategory::Material: + return "FVoxelMaterial"; + case EVoxelPinCategory::Color: + return "FColor"; + case EVoxelPinCategory::Seed: + return "FVoxelGraphSeed"; + case EVoxelPinCategory::Wildcard: + case EVoxelPinCategory::Exec: + case EVoxelPinCategory::Vector: + default: + check(false); + return ""; + } +} + +FString FVoxelPinCategory::GetRangeTypeString(EVoxelPinCategory Category) +{ + switch (Category) + { + case EVoxelPinCategory::Boolean: + return "FVoxelBoolRange"; + case EVoxelPinCategory::Int: + return "TVoxelRange"; + case EVoxelPinCategory::Float: + return "TVoxelRange"; + case EVoxelPinCategory::Material: + return "FVoxelMaterialRange"; + case EVoxelPinCategory::Color: + return "FVoxelColorRange"; + case EVoxelPinCategory::Seed: + return "FVoxelGraphSeed"; + case EVoxelPinCategory::Wildcard: + case EVoxelPinCategory::Exec: + case EVoxelPinCategory::Vector: + default: + check(false); + return ""; + } +} + +FVoxelNodeType FVoxelPinCategory::ConvertDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue) +{ + FVoxelNodeType Value; + switch (Category) + { + case EVoxelPinCategory::Boolean: + Value.Get() = DefaultValue.ToBool(); + break; + case EVoxelPinCategory::Int: + Value.Get() = FCString::Atoi(*DefaultValue); + break; + case EVoxelPinCategory::Float: + Value.Get() = FCString::Atof(*DefaultValue); + break; + case EVoxelPinCategory::Material: + Value.Get() = FVoxelMaterial::Default(); + break; + case EVoxelPinCategory::Color: + { + FLinearColor Color; + if (!Color.InitFromString(DefaultValue)) + { + Color = FColor::Transparent; + } + Value.Get() = Color.ToFColor(false); + break; + } + case EVoxelPinCategory::Seed: + Value.Get() = FCString::Atoi(*DefaultValue); + break; + case EVoxelPinCategory::Exec: + default: + check(false); + Value.Get() = 0; + } + return Value; +} + +FVoxelNodeRangeType FVoxelPinCategory::ConvertRangeDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue) +{ + FVoxelNodeRangeType RangeValue; + const FVoxelNodeType Value = ConvertDefaultValue(Category, DefaultValue); + switch (Category) + { + case EVoxelPinCategory::Boolean: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Int: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Float: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Material: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Color: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Seed: + RangeValue.Get() = Value.Get(); + break; + case EVoxelPinCategory::Exec: + default: + RangeValue.Get() = false; + check(false); + } + return RangeValue; +} + +FString FVoxelPinCategory::ConvertStringDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue) +{ + const FVoxelNodeType Value = ConvertDefaultValue(Category, DefaultValue); + switch (Category) + { + case EVoxelPinCategory::Boolean: + return Value.Get() ? TEXT("true") : TEXT("false"); + case EVoxelPinCategory::Int: + return FString::FromInt(Value.Get()); + case EVoxelPinCategory::Float: + return FString::SanitizeFloat(Value.Get()) + "f"; + case EVoxelPinCategory::Material: + return TEXT("FVoxelMaterial(ForceInit)"); + case EVoxelPinCategory::Color: + return FString::Printf(TEXT("FColor(%d, %d, %d, %d)"), + Value.Get().R, + Value.Get().G, + Value.Get().B, + Value.Get().A); + case EVoxelPinCategory::Seed: + return FString::FromInt(Value.Get()); + case EVoxelPinCategory::Exec: + default: + ensure(false); + return ""; + } +} + +FString FVoxelPinCategory::ToString(EVoxelPinCategory Category, FVoxelNodeType Value) +{ + switch (Category) + { + case EVoxelPinCategory::Boolean: + return LexToString(Value.Get()); + case EVoxelPinCategory::Int: + return LexToString(Value.Get()); + case EVoxelPinCategory::Float: + return FString::SanitizeFloat(Value.Get()); + case EVoxelPinCategory::Seed: + return LexToString(Value.Get()); + case EVoxelPinCategory::Color: + return FLinearColor( + Value.Get().R / 255.999f, + Value.Get().G / 255.999f, + Value.Get().B / 255.999f, + Value.Get().A / 255.999f).ToString(); + case EVoxelPinCategory::Material: + case EVoxelPinCategory::Exec: + default: + ensure(false); + return ""; + } +} + +FString FVoxelPinCategory::ToString(EVoxelPinCategory Category, FVoxelNodeRangeType Value) +{ + switch (Category) + { + case EVoxelPinCategory::Boolean: + return Value.Get().ToString(); + case EVoxelPinCategory::Int: + return Value.Get().ToString(); + case EVoxelPinCategory::Float: + return Value.Get().ToString(); + case EVoxelPinCategory::Material: + case EVoxelPinCategory::Color: + case EVoxelPinCategory::Seed: + default: + check(false); + return ""; + } +} + +bool FVoxelPinCategory::IsInRange(EVoxelPinCategory Category, FVoxelNodeType Value, FVoxelNodeRangeType Range) +{ + switch (Category) + { + case EVoxelPinCategory::Boolean: + return Value.Get() ? Range.Get().bCanBeTrue : Range.Get().bCanBeFalse; + case EVoxelPinCategory::Int: + return Range.Get().Contains(Value.Get()); + case EVoxelPinCategory::Float: + return Range.Get().Contains(Value.Get()); + case EVoxelPinCategory::Material: + return true; + case EVoxelPinCategory::Color: + return true; + case EVoxelPinCategory::Seed: + default: + check(false); + return false; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationEnums.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationEnums.h new file mode 100644 index 00000000..5f5cf340 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationEnums.h @@ -0,0 +1,40 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +enum class EVoxelPinDirection : uint8 +{ + Input, + Output +}; + +enum class EVoxelCompilationNodeType : uint8 +{ + Default, + Macro, + MacroInputOutput, + FlowMerge, + Passthrough, + LocalVariableDeclaration, + LocalVariableUsage, + FunctionSeparator, + FunctionCall, + FunctionInit, + If, + Setter, + RangeAnalysisConstant, + GetRangeAnalysis, + BiomeMerge, + CompileTimeConstant, + Switch, + SmartMinMax +}; + +enum class EVoxelPinIter : uint8 +{ + Input, + Output, + All +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationNode.h new file mode 100644 index 00000000..e6adc86c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelCompilationNode.h @@ -0,0 +1,354 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelPinCategory.h" +#include "Compilation/VoxelCompilationEnums.h" +#include "VoxelMinimal.h" + +class UVoxelNode; +class FVoxelComputeNode; +class FVoxelCompilationNode; +class FVoxelGraphErrorReporter; +struct FVoxelCompilationPin; + +struct VOXELGRAPH_API FVoxelCompilationPin +{ + class FVoxelCompilationPinLinkedToIterator + { + public: + using Type = FVoxelCompilationPin; + FVoxelCompilationPinLinkedToIterator(const Type& Pin) : Pin(Pin) {} + + class FIterator + { + public: + FIterator(const Type& Pin) : Pin(Pin), InitialNum(Pin.LinkedTo.Num()) {} + + inline void operator++() { Index++; } + inline bool operator!=(const FIterator&) const + { + ensureMsgf(Pin.LinkedTo.Num() == InitialNum, TEXT("Array has changed during ranged-for iteration!")); + return Pin.LinkedTo.IsValidIndex(Index); + } + inline Type& operator*() const { return *Pin.LinkedTo[Index]; } + + private: + const Type& Pin; + const int32 InitialNum; + int32 Index = 0; + }; + + inline FIterator begin() { return FIterator(Pin); } + inline FIterator end() { return FIterator(Pin); } + + private: + const Type& Pin; + }; + +public: + FVoxelCompilationNode& Node; + int32 const Index; + EVoxelPinDirection const Direction; + EVoxelPinCategory const PinCategory; + FName const Name; + +public: + inline void BreakLinkTo(FVoxelCompilationPin& Other) + { + ensure(this->LinkedTo.Remove(&Other) == 1); + ensure(Other.LinkedTo.Remove(this) == 1); + } + inline bool IsLinkedTo(FVoxelCompilationPin& Other) const + { + const bool bALinkedToB = this->LinkedTo.Contains(&Other); + const bool bBLinkedToA = Other.LinkedTo.Contains(this); + + ensure((bALinkedToB && bBLinkedToA) || (!bALinkedToB && !bBLinkedToA)); + + return bALinkedToB; + } + inline void LinkTo(FVoxelCompilationPin& Other) + { + ensure(Direction != Other.Direction); + ensure(PinCategory == Other.PinCategory); + + this->LinkedTo.AddUnique(&Other); + Other.LinkedTo.AddUnique(this); + } + inline void BreakAllLinks() + { + auto Copy = LinkedTo; + for (auto* Pin : Copy) + { + BreakLinkTo(*Pin); + } + ensure(LinkedTo.Num() == 0); + } + inline auto IterateLinkedTo() const { return FVoxelCompilationPinLinkedToIterator(*this); } + inline TArray IterateLinkedToCopy() const { return LinkedTo; } + inline const TArray& GetLinkedToArray() const { return LinkedTo; } + inline FVoxelCompilationPin& GetLinkedTo(int32 InIndex) const { return *LinkedTo[InIndex]; } + inline int32 NumLinkedTo() const { return LinkedTo.Num(); } + + void Check(FVoxelGraphErrorReporter& ErrorReporter); + +public: + inline void SetDefaultValue(const FString& String) + { + ensure(Direction == EVoxelPinDirection::Input); + DefaultValue = String; + } + inline FString GetDefaultValue() const + { + ensure(Direction == EVoxelPinDirection::Input); + return DefaultValue; + } + +private: + TArray LinkedTo; + FString DefaultValue; + + FVoxelCompilationPin(FVoxelCompilationNode& Node, int32 Index, EVoxelPinDirection Direction, EVoxelPinCategory PinCategory, const FName& Name); + FVoxelCompilationPin Clone(FVoxelCompilationNode& NewNode) const; + + friend class FVoxelCompilationNode; + friend class TArray; +}; + +class VOXELGRAPH_API FVoxelCompilationNode +{ +public: + template + class TVoxelCompilationNodePinsIterator + { + public: + TVoxelCompilationNodePinsIterator(Type& Node) : Node(Node) {} + + class FIterator + { + public: + FIterator(Type& Node) : Node(Node) {} + + inline void operator++() { Index++; } + inline bool operator!=(const FIterator&) { return Node.template GetCachedPins().IsValidIndex(Index); } + inline PinType& operator*() { return GetPin(Node.template GetCachedPins()[Index]); } + + private: + Type& Node; + int32 Index = 0; + + static inline PinType& GetPin(PinType* Pin) { return *Pin; } + static inline PinType& GetPin(PinType& Pin) { return Pin; } + }; + + inline FIterator begin() { return FIterator(Node); } + inline FIterator end() { return FIterator(Node); } + + private: + Type& Node; + }; + +public: + uint8 Dependencies = 0; + // First are deeper in the callstack + TArray SourceNodes; + const UVoxelNode& Node; + TArray DebugMessages; + + virtual ~FVoxelCompilationNode() {} + +private: + const EVoxelCompilationNodeType PrivateType; + TArray Pins; + TArray InputIds; + TArray OutputIds; + + FVoxelCompilationNode(EVoxelCompilationNodeType Type, const UVoxelNode& Node); + FVoxelCompilationNode(EVoxelCompilationNodeType Type, const UVoxelNode& Node, + const TArray& InputCategories, + const TArray& OutputCategories); + + template + friend class TVoxelCompilationNode; + friend class FVoxelPassthroughCompilationNode; + friend class FVoxelSetterCompilationNode; + +public: + inline FVoxelCompilationPin& GetInputPin (int32 I) { return *CachedInputPins [I]; } + inline const FVoxelCompilationPin& GetInputPin (int32 I) const { return *CachedInputPins [I]; } + inline FVoxelCompilationPin& GetOutputPin(int32 I) { return *CachedOutputPins[I]; } + inline const FVoxelCompilationPin& GetOutputPin(int32 I) const { return *CachedOutputPins[I]; } + inline FVoxelCompilationPin& GetPin(int32 I) { return Pins[I]; } + inline const FVoxelCompilationPin& GetPin(int32 I) const { return Pins[I]; } + + inline int32 GetInputId (int32 I) const { return InputIds [I]; } + inline int32 GetOutputId(int32 I) const { return OutputIds[I]; } + inline void SetInputId (int32 I, int32 Id) { InputIds [I] = Id; } + inline void SetOutputId(int32 I, int32 Id) { OutputIds[I] = Id; } + inline const TArray& GetInputIds() const { return InputIds ; } + inline const TArray& GetOutputIds() const { return OutputIds; } + + inline int32 GetInputCount () const { return CachedInputPins .Num(); } + inline int32 GetOutputCount() const { return CachedOutputPins.Num(); } + inline int32 GetNumPins() const { return Pins.Num(); } + + inline int32 GetInputCountWithoutExecs () const { return InputIds .Num(); } + inline int32 GetOutputCountWithoutExecs() const { return OutputIds.Num(); } + + inline int32 GetExecInputCount () const { return GetInputCount () - GetInputCountWithoutExecs (); } + inline int32 GetExecOutputCount() const { return GetOutputCount() - GetOutputCountWithoutExecs(); } + + TArray GetInputPinCategories() const; + TArray GetOutputPinCategories() const; + + FString GetPrettyName() const; + + bool IsExecNode() const { return GetExecInputCount() > 0 || GetExecOutputCount() > 0; } + bool IsSeedNode() const; + + void BreakAllLinks(); + bool IsLinkedTo(const FVoxelCompilationNode* OtherNode) const; + template + bool IsLinkedToOne(const T& OtherNodes) const + { + for (auto* OtherNode : OtherNodes) + { + if (IsLinkedTo(OtherNode)) + { + return true; + } + } + return false; + } + void CheckIsNotLinked(FVoxelGraphErrorReporter& ErrorReporter) const; + +public: + template + inline bool IsA() const + { + return T::StaticType() == PrivateType; + } + inline EVoxelCompilationNodeType GetPrivateType() const + { + return PrivateType; + } + +public: + //~ Begin FVoxelCompilationNode Interface + // Clone this node + virtual TSharedPtr Clone(bool bFixLinks = false) const = 0; + // Get the compute node corresponding to this compilation node + virtual TVoxelSharedPtr GetComputeNode() const; + // The axis used by this nodes + virtual uint8 GetDefaultAxisDependencies() const { return 0; } + // Can this node be computed at compile time? + virtual bool IsPureNode() const { return false; } + // Check this node validity + virtual void Check(FVoxelGraphErrorReporter& ErrorReporter) const; + // This class name + virtual FString GetClassName() const { return "NONE"; } + //~ End FVoxelCompilationNode Interface + +public: + template + inline auto IteratePins() + { + return TVoxelCompilationNodePinsIterator(*this); + } + template + inline auto IteratePins() const + { + return TVoxelCompilationNodePinsIterator(*this); + } + +protected: + template + TSharedPtr CloneInternal(bool bFixLinks) const + { + auto NewNode = MakeShared(Node, TArray(), TArray()); + CopyPropertiesToNewNode(NewNode, bFixLinks); + return NewNode; + } + +private: + void CopyPropertiesToNewNode(const TSharedRef& NewNode, bool bFixLinks) const; + +private: + // Cache for fast access + TArray CachedInputPins; + TArray CachedOutputPins; + + void RebuildPinsArray(); + + template + const auto& GetCachedPins() const; + template + auto& GetCachedPins(); +}; + +template<> inline const auto& FVoxelCompilationNode::GetCachedPins() const { return CachedInputPins; } +template<> inline auto& FVoxelCompilationNode::GetCachedPins() { return CachedInputPins; } +template<> inline const auto& FVoxelCompilationNode::GetCachedPins() const { return CachedOutputPins; } +template<> inline auto& FVoxelCompilationNode::GetCachedPins() { return CachedOutputPins; } +template<> inline const auto& FVoxelCompilationNode::GetCachedPins() const { return Pins; } +template<> inline auto& FVoxelCompilationNode::GetCachedPins() { return Pins; } + +template +class TVoxelCompilationNode : public InParent +{ +public: + TVoxelCompilationNode(const UVoxelNode& Node) + : InParent(InType, Node) + { + } + TVoxelCompilationNode(const UVoxelNode& Node, const TArray& InputCategories, const TArray& OutputCategories) + : InParent(InType, Node, InputCategories, OutputCategories) + { + } + + virtual TSharedPtr Clone(bool bFixLinks = false) const override final + { + TSharedPtr Result = this->template CloneInternal(bFixLinks); + InClass::CopyProperties(*static_cast(this), *Result.Get()); + return Result; + } + + static void CopyProperties(const InClass& From, InClass& To) + { + // Do copy fixup here + } + + static EVoxelCompilationNodeType StaticType() + { + return InType; + } +}; + +template +inline To* CastVoxel(From* Node) +{ + if (Node && Node->GetPrivateType() == To::StaticType()) + { + return static_cast(Node); + } + else + { + return nullptr; + } +} + +template +inline To& CastCheckedVoxel(From* Node) +{ + check(Node); + auto* Result = CastVoxel(Node); + check(Result); + return *Result; +} +template +inline To& CastCheckedVoxel(From& Node) +{ + return CastCheckedVoxel(&Node); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelDefaultCompilationNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelDefaultCompilationNodes.h new file mode 100644 index 00000000..061fb5a9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelDefaultCompilationNodes.h @@ -0,0 +1,310 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Compilation/VoxelCompilationNode.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelAxisDependencies.h" +#include "VoxelMinimal.h" + +class FVoxelComputeNodeTree; + +template +class TVoxelDefaultCompilationNode : public TVoxelCompilationNode +{ +public: + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "Default"; } +}; + +class FVoxelDefaultCompilationNode : public TVoxelDefaultCompilationNode +{ +public: + using TVoxelDefaultCompilationNode::TVoxelDefaultCompilationNode; +}; + +class FVoxelDefaultPureCompilationNode : public TVoxelDefaultCompilationNode +{ +public: + using TVoxelDefaultCompilationNode::TVoxelDefaultCompilationNode; + + virtual bool IsPureNode() const override { return true; } + virtual FString GetClassName() const override { return "DefaultPure"; } +}; + +class FVoxelPassthroughCompilationNode : public FVoxelCompilationNode +{ +public: + using FVoxelCompilationNode::FVoxelCompilationNode; + + static EVoxelCompilationNodeType StaticType() + { + return EVoxelCompilationNodeType::Passthrough; + } + virtual FString GetClassName() const override + { + return "Passthrough"; + } + + template + friend class TVoxelCompilationNode; +}; + +class FVoxelSetterCompilationNode : public TVoxelCompilationNode +{ +public: + uint32 OutputIndex = -1; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + static void CopyProperties(const FVoxelSetterCompilationNode& From, FVoxelSetterCompilationNode& To) + { + To.OutputIndex = From.OutputIndex; + } + virtual void Check(FVoxelGraphErrorReporter& ErrorReporter) const override + { + ensureVoxelGraph(GetInputCount() > 1 && GetOutputCount() == 1, this); + } + virtual FString GetClassName() const override + { + return "Setter"; + } +}; + +class FVoxelAxisDependenciesCompilationNode : public TVoxelDefaultCompilationNode +{ +public: + uint8 DefaultDependencies = -1; + + using TVoxelDefaultCompilationNode::TVoxelDefaultCompilationNode; + + static void CopyProperties(const FVoxelAxisDependenciesCompilationNode& From, FVoxelAxisDependenciesCompilationNode& To) + { + To.DefaultDependencies = From.DefaultDependencies; + } + virtual uint8 GetDefaultAxisDependencies() const override + { + return DefaultDependencies; + } + virtual FString GetClassName() const override + { + return "Axis Dependencies"; + } +}; + +class FVoxelFunctionSeparatorCompilationNode : public TVoxelCompilationNode +{ +public: + int32 FunctionId = -1; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + static void CopyProperties(const FVoxelFunctionSeparatorCompilationNode& OldNode, FVoxelFunctionSeparatorCompilationNode& NewNode) + { + NewNode.FunctionId = OldNode.FunctionId; + } + virtual FString GetClassName() const override { return "Function Separator"; } +}; + +class FVoxelFunctionInitCompilationNode : public TVoxelCompilationNode +{ +public: + int32 FunctionId = -1; + + using TVoxelCompilationNode::TVoxelCompilationNode; + FVoxelFunctionInitCompilationNode(const FVoxelFunctionSeparatorCompilationNode& Separator); + + static void CopyProperties(const FVoxelFunctionInitCompilationNode& OldNode, FVoxelFunctionInitCompilationNode& NewNode) + { + NewNode.FunctionId = OldNode.FunctionId; + } + virtual void Check(FVoxelGraphErrorReporter& ErrorReporter) const override + { + ensureVoxelGraph(GetInputCount() == 0, this); + } + virtual uint8 GetDefaultAxisDependencies() const override + { + // Functions init depend on X, else their outputs are cached + return EVoxelAxisDependenciesFlags::X; + } + virtual FString GetClassName() const override + { + return "FunctionInit"; + } + + virtual TVoxelSharedPtr GetComputeNode() const override; +}; + +class FVoxelFunctionCallCompilationNode : public TVoxelCompilationNode +{ +public: + int32 FunctionId = -1; + + using TVoxelCompilationNode::TVoxelCompilationNode; + FVoxelFunctionCallCompilationNode(const FVoxelFunctionSeparatorCompilationNode& Separator); + + static void CopyProperties(const FVoxelFunctionCallCompilationNode& OldNode, FVoxelFunctionCallCompilationNode& NewNode) + { + NewNode.FunctionId = OldNode.FunctionId; + } + virtual void Check(FVoxelGraphErrorReporter& ErrorReporter) const override + { + ensureVoxelGraph(GetOutputCount() == 0, this); + } + virtual FString GetClassName() const override + { + return "FunctionCall"; + } + + virtual TVoxelSharedPtr GetComputeNode() const override { check(false); return {}; } + TVoxelSharedPtr GetComputeNode(EVoxelFunctionAxisDependencies FunctionDependencies) const; +}; + +class FVoxelFlowMergeCompilationNode : public TVoxelCompilationNode +{ +public: + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "FlowMerge"; } +}; + +class FVoxelIfCompilationNode : public TVoxelCompilationNode +{ +public: + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "If"; } +}; + +class FVoxelRangeAnalysisConstantCompilationNode : public TVoxelCompilationNode +{ +public: + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "RangeAnalysisConstant"; } +}; + +class FVoxelGetRangeAnalysisCompilationNode : public TVoxelCompilationNode +{ +public: + int32 VariablesBufferSize = -1; + TVoxelSharedPtr Tree; + uint8 DefaultDependencies = 0; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "GetRangeAnalysis"; } + virtual uint8 GetDefaultAxisDependencies() const override { return DefaultDependencies; } + + static void CopyProperties(const FVoxelGetRangeAnalysisCompilationNode& From, FVoxelGetRangeAnalysisCompilationNode& To) + { + To.VariablesBufferSize = From.VariablesBufferSize; + To.Tree = From.Tree; + } +}; + +class FVoxelSmartMinMaxCompilationNode : public TVoxelCompilationNode +{ +public: + bool bIsMin = false; + FVoxelCompilationNode* OutputPassthrough = nullptr; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "SmartMinMax"; } + + static void CopyProperties(const FVoxelSmartMinMaxCompilationNode& From, FVoxelSmartMinMaxCompilationNode& To) + { + To.bIsMin = From.bIsMin; + To.OutputPassthrough = From.OutputPassthrough; + } +}; + +class FVoxelMacroCompilationNode : public TVoxelCompilationNode +{ +public: + FVoxelCompilationNode* InputNode = nullptr; + FVoxelCompilationNode* OutputNode = nullptr; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + static void CopyProperties(const FVoxelMacroCompilationNode& From, FVoxelMacroCompilationNode& To) + { + To.InputNode = From.InputNode; + To.OutputNode = From.OutputNode; + } +}; + +struct FVoxelMacroInputOuputCompilationNode : public TVoxelCompilationNode +{ +public: + TArray Passthroughs; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + static void CopyProperties(const FVoxelMacroInputOuputCompilationNode& From, FVoxelMacroInputOuputCompilationNode& To) + { + To.Passthroughs = From.Passthroughs; + } +}; + +class FVoxelLocalVariableDeclarationCompilationNode : public TVoxelCompilationNode +{ + using TVoxelCompilationNode::TVoxelCompilationNode; +}; + +class FVoxelLocalVariableUsageCompilationNode : public TVoxelCompilationNode +{ +public: + FVoxelCompilationNode* Passthrough = nullptr; // For preview + + using TVoxelCompilationNode::TVoxelCompilationNode; + + static void CopyProperties(const FVoxelLocalVariableUsageCompilationNode& From, FVoxelLocalVariableUsageCompilationNode& To) + { + To.Passthrough = From.Passthrough; + } +}; + +class FVoxelBiomeMergeCompilationNode : public TVoxelCompilationNode +{ +public: + TArray OutputPassthroughs; // For preview + float Tolerance; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "BiomeMerge"; } + + static void CopyProperties(const FVoxelBiomeMergeCompilationNode& From, FVoxelBiomeMergeCompilationNode& To) + { + To.OutputPassthroughs = From.OutputPassthroughs; + To.Tolerance = From.Tolerance; + } +}; + +class FVoxelCompileTimeConstantCompilationNode : public TVoxelCompilationNode +{ +public: + FName Name; + + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "CompileTimeConstant"; } + + static void CopyProperties(const FVoxelCompileTimeConstantCompilationNode& From, FVoxelCompileTimeConstantCompilationNode& To) + { + To.Name = From.Name; + } +}; + +class FVoxelSwitchCompilationNode : public TVoxelCompilationNode +{ +public: + using TVoxelCompilationNode::TVoxelCompilationNode; + + virtual FString GetClassName() const override { return "Switch"; } + virtual bool IsPureNode() const override { return true; } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompiler.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompiler.h new file mode 100644 index 00000000..73178205 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompiler.h @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelMinimal.h" + +class FVoxelCompilationNode; +class UVoxelNode; +class UVoxelGraphGenerator; + +class FVoxelGraphCompiler +{ +public: + FVoxelGraphErrorReporter& ErrorReporter; + + FVoxelCompilationNode* FirstNode = nullptr; + int32 FirstNodePinIndex = -1; + + TMap Parents; + TMap DuplicateCounts; + + FVoxelGraphCompiler(const TSharedRef& ErrorReporter); + FVoxelGraphCompiler(UVoxelGraphGenerator* Graph); + + TSharedRef Clone(const FString& ErrorPrefix, TMap& OutOldNodesToNewNodes) const; + TSharedRef Clone(const FString& ErrorPrefix) const; + + TMap InitFromNodes(const TArray& Nodes, UVoxelNode* FirstNode, int32 FirstNodePinIndex); + +public: + inline const TSet& GetAllNodes() const { return Nodes; } + inline TSet GetAllNodesCopy() const { return Nodes; } + +public: + FVoxelCompilationNode* AddNode(UVoxelNode* Node); + FVoxelCompilationNode* AddNode(const TSharedPtr& Ref, FVoxelCompilationNode* SourceNode = nullptr); + void RemoveNode(FVoxelCompilationNode* Node); + + template + inline void ApplyPass(TArgs&&... Args) + { + if (!ErrorReporter.HasError()) + { +#if CPUPROFILERTRACE_ENABLED + // Static id is static for the template, so it's per class + TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(T::GetName()); +#endif + T::Apply(*this, Forward(Args)...); + Check(); + } + } + +public: + void Check() const; + void AppendAndClear(FVoxelGraphCompiler& Other); + +private: + const TSharedRef ErrorReporterRef; + TArray> NodesRefs; + TSet Nodes; + +public: + inline static int32 GetCompilationId() + { + return CompilationId; + } + inline static void IncreaseCompilationId() + { + CompilationId++; + } + +private: + static int32 CompilationId; +}; + +struct FVoxelCompilationFunctionDescriptor +{ + int32 const FunctionId; + FVoxelCompilationNode* const FirstNode; + TSet Nodes; + + FVoxelCompilationFunctionDescriptor( + int32 FunctionId, + FVoxelCompilationNode* FirstNode) + : FunctionId(FunctionId) + , FirstNode(FirstNode) + { + } +}; + +// Required to avoid duplicating compute nodes +class FVoxelCreatedComputeNodes +{ +public: + FVoxelCreatedComputeNodes() = default; + FVoxelCreatedComputeNodes(FVoxelCreatedComputeNodes const&) = delete; + FVoxelCreatedComputeNodes& operator=(FVoxelCreatedComputeNodes const&) = delete; + + TVoxelSharedRef GetComputeNode(const FVoxelCompilationNode& Node); + +private: + TMap> Map; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompilerManager.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompilerManager.h new file mode 100644 index 00000000..8595cfac --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Compilation/VoxelGraphCompilerManager.h @@ -0,0 +1,52 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphOutputs.h" +#include "VoxelMinimal.h" + +class UVoxelGraphPreviewSettings; +class UVoxelGraphGenerator; +class UVoxelNode; +class FVoxelGraphCompiler; +class FVoxelCompilationNode; +class FVoxelGraphErrorReporter; +struct FVoxelCompiledGraphs; +class FVoxelGraph; +class FVoxelComputeNode; + +class FVoxelGraphCompilerManager +{ +public: + FVoxelGraphCompilerManager( + UVoxelGraphGenerator* Graph, + bool bEnableOptimizations, + bool bPreview, + const UVoxelGraphPreviewSettings* PreviewSettings, + bool bAutomaticPreview, + bool bOnlyShowAxisDependencies); + ~FVoxelGraphCompilerManager(); + + // Might return true with OutError not empty + bool Compile(FVoxelCompiledGraphs& OutGraphs); + +private: + UVoxelGraphGenerator* const Graph; + const bool bEnableOptimizations; + const bool bPreview; + const UVoxelGraphPreviewSettings* PreviewSettings; + const bool bAutomaticPreview; + const bool bOnlyShowAxisDependencies; + + TMap NodesMap; + + bool CompileInternal(FVoxelGraphCompiler& Compiler, FVoxelCompiledGraphs& OutGraphs); + bool CompileOutput(const FVoxelGraphPermutationArray& Permutation, const FString& OutputName, FVoxelGraphCompiler& Compiler, TVoxelSharedPtr& OutGraph); + void SetupPreview(FVoxelGraphCompiler& Compiler); + void DebugCompiler(const FVoxelGraphCompiler& Compiler) const; + void DebugNodes(const TSet& Nodes) const; + void Optimize(FVoxelGraphCompiler& Compiler) const; + + friend struct FVoxelCompilationFunction; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConfig.h new file mode 100644 index 00000000..06f81115 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConfig.h @@ -0,0 +1,70 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UVoxelNode; +class FVoxelGraphErrorReporter; +class FVoxelExposedVariable; + +struct FVoxelCppInclude +{ + const FString Name; + const bool bSTDInclude; + + FVoxelCppInclude(const FString& Name, bool bSTDInclude) + : Name(Name) + , bSTDInclude(bSTDInclude) + { + } + FVoxelCppInclude(const FString& Name) + : FVoxelCppInclude(Name, false) + { + } + template + FVoxelCppInclude(const T* Name) + : FVoxelCppInclude(FString(Name), false) + { + } + + inline bool operator==(const FVoxelCppInclude& Other) const + { + return bSTDInclude == Other.bSTDInclude && Name == Other.Name; + } + + inline FString ToString() const + { + if (bSTDInclude) + { + return "#include <" + Name + ">"; + } + else + { + return "#include \"" + Name + "\""; + } + } +}; + +class VOXELGRAPH_API FVoxelCppConfig +{ +public: + FVoxelGraphErrorReporter& ErrorReporter; + + explicit FVoxelCppConfig(FVoxelGraphErrorReporter& ErrorReporter); + + // Add an exposed variable to the generated class + void AddExposedVariable(const TSharedRef& Variable); + // Add an include to the generated file. Example: AddInclude("CoreMinimal.h") + void AddInclude(const FVoxelCppInclude& Include); + + void BuildExposedVariablesArray(); + + const TArray>& GetExposedVariables() const { return ExposedVariablesArray; } + const TArray& GetIncludes() const { return Includes; } + +private: + TMap> ExposedVariables; + TArray> ExposedVariablesArray; + TArray Includes; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructor.h new file mode 100644 index 00000000..8a0d1d6a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructor.h @@ -0,0 +1,229 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphOutputs.h" +#include "VoxelPinCategory.h" + +struct FVoxelGraphFunctionInfo; +class FVoxelGraphErrorReporter; +class FVoxelComputeNode; + +class VOXELGRAPH_API FVoxelCppConstructor +{ +public: + const FVoxelGraphPermutationArray Permutation; + FVoxelGraphErrorReporter& ErrorReporter; + + FVoxelCppConstructor(const FVoxelGraphPermutationArray& Permutation, FVoxelGraphErrorReporter& ErrorReporter) + : Permutation(Permutation) + , ErrorReporter(ErrorReporter) + { + } + + inline const TArray& GetLines() const { return Lines; } + void GetCode(FString& OutCode) const; + +public: + template + inline void AddLinef(const FmtType& Fmt, Types... Args) + { + AddLine(FString::Printf(Fmt, Args...)); + } + inline void AddLine(const FString& Line) + { + if (!QueuedComment.IsEmpty()) + { + AddLineInternal(QueuedComment); + QueuedComment.Empty(); + } + AddLineInternal(Line); + } + inline void NewLine() + { + AddLineInternal(""); + } + inline void AddPreprocessorLine(const FString& Line) + { + const int32 IndentCopy = CurrentIndent; + CurrentIndent = 0; + AddLine(Line); + CurrentIndent = IndentCopy; + } + + inline void Indent() + { + CurrentIndent++; + check(CurrentIndent >= 0); + } + inline void Unindent() + { + CurrentIndent--; + check(CurrentIndent >= 0); + } + + inline void StartBlock() + { + AddLine("{"); + Indent(); + } + inline void EndBlock(bool bSemicolon = false) + { + Unindent(); + AddLine(FString("}") + (bSemicolon ? ";" : "")); + } + + inline void Private() + { + Unindent(); + AddLine("private:"); + Indent(); + } + inline void Public() + { + Unindent(); + AddLine("public:"); + Indent(); + } + + inline void EnterNamedScope(const FString& Scope) + { + Scopes.Add(Scope); + } + inline void ExitNamedScope(const FString& Scope) + { + ensureAlways(Scope == Scopes.Pop()); + } + inline FString GetScopeAccessor() const + { + FString Result; + for (auto& Scope : Scopes) + { + Result += Scope + "::"; + } + return Result; + } + + // Queue comment and add it before next AddLine + inline void QueueComment(const FString& Comment) + { + QueuedComment = Comment; + } + // Add new line if comment has been done + inline void EndComment() + { + if (QueuedComment.IsEmpty()) + { + NewLine(); + } + else + { + QueuedComment.Empty(); + } + } + +public: + void AddOtherConstructor(const FVoxelCppConstructor& Other); + + void AddFunctionCall(const FVoxelGraphFunctionInfo& Info, const TArray& Args); + void AddFunctionDeclaration(const FVoxelGraphFunctionInfo& Info, const TArray& Args); + + FString GetTypeString(EVoxelPinCategory Category) const; + FString GetTypeString(EVoxelDataPinCategory Category) const; + + FString GetContextTypeString() const; + +public: + void AddVariable(int32 Id, const FString& Value); + bool HasVariable(int32 Id); + bool CurrentScopeHasVariable(int32 Id); + + FString GetVariable(int32 Id, const FVoxelComputeNode* Node); + + void StartScope(); + void EndScope(); + + bool IsNodeInit(FVoxelComputeNode* Node) const; + void SetNodeAsInit(FVoxelComputeNode* Node); + +private: + struct FVoxelVariableScope + { + TMap Variables; + TSet NodesAlreadyInit; + + FVoxelVariableScope() = default; + + FVoxelVariableScope* GetChild() + { + check(!Child); + Child = MakeUnique(); + Child->Parent = this; + return Child.Get(); + } + + const FString* GetVariable(int32 Id) const + { + if (auto* Result = Variables.Find(Id)) + { + return Result; + } + else + { + return Parent ? Parent->GetVariable(Id) : nullptr; + } + } + + bool IsNodeInit(FVoxelComputeNode* Node) const + { + return NodesAlreadyInit.Contains(Node) || (Parent && Parent->IsNodeInit(Node)); + } + + FVoxelVariableScope* GetParent() const { return Parent; } + void RemoveChild() { Child = nullptr; } + + private: + FVoxelVariableScope* Parent = nullptr; + TUniquePtr Child; + }; + + inline void AddLineInternal(const FString& Line) + { + FString FinalLine; + check(CurrentIndent >= 0); + for (int32 I = 0; I < CurrentIndent; I++) + { + FinalLine += "\t"; + } + FinalLine.Append(Line); + Lines.Add(FinalLine); + } + +private: + TArray Lines; + int32 CurrentIndent = 0; + + FVoxelVariableScope MainScope; + FVoxelVariableScope* CurrentScope = &MainScope; + + FString QueuedComment; + TArray Scopes; +}; + +struct FVoxelCppVariableScope +{ + FVoxelCppVariableScope(FVoxelCppConstructor& Constructor) + : Constructor(Constructor) + { + Constructor.StartScope(); + } + + ~FVoxelCppVariableScope() + { + Constructor.EndScope(); + } + +private: + FVoxelCppConstructor& Constructor; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructorManager.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructorManager.h new file mode 100644 index 00000000..de5d6d74 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppConstructorManager.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UVoxelGraphGenerator; +struct FVoxelCompiledGraphs; +class FVoxelGraphErrorReporter; + +class FVoxelCppConstructorManager +{ +public: + FVoxelCppConstructorManager(const FString& ClassName, UVoxelGraphGenerator* Graph); + ~FVoxelCppConstructorManager(); + + bool Compile(FString& OutHeader, FString& OutCpp); + +private: + FString const ClassName; + UVoxelGraphGenerator* const VoxelGraphGenerator; + TUniquePtr const Graphs; + TUniquePtr const ErrorReporter; + + bool CompileInternal(FString& OutHeader, FString& OutCpp); +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppIds.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppIds.h new file mode 100644 index 00000000..270c42a7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppIds.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAxisDependencies.h" + +struct VOXELGRAPH_API FVoxelCppIds +{ + const static FString InitStruct; + const static FString Context; + const static FString GraphOutputs; + const static FString GraphOutputsType; + const static FString ExposedVariablesStruct; + const static FString ExposedVariablesStructType; + + static inline FString GetVariableName(int32 Id) { return "Variable_" + FString::FromInt(Id); } + static FString GetCacheName(EVoxelAxisDependencies Dependencies); + static FString GetCacheType(EVoxelAxisDependencies Dependencies); +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppUtils.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppUtils.h new file mode 100644 index 00000000..228646e0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelCppUtils.h @@ -0,0 +1,182 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Class.h" + +template +struct TVoxelGetCppTypeImpl; + +#define GET_CPP_TYPE(Type) \ + template<> \ + struct TVoxelGetCppTypeImpl \ + { \ + static FString Get() \ + { \ + return #Type; \ + } \ + }; + + GET_CPP_TYPE(bool); + GET_CPP_TYPE(float); + GET_CPP_TYPE(double); + GET_CPP_TYPE(int32); + GET_CPP_TYPE(FColor); + GET_CPP_TYPE(FLinearColor); + GET_CPP_TYPE(FRotator); + GET_CPP_TYPE(FString); + GET_CPP_TYPE(FName); + +#undef GET_CPP_TYPE + +template +struct TVoxelGetCppTypeImpl::Value, FString>::Type> +{ + static FString Get() + { + const FString String = UEnum::GetValueAsString(static_cast(0)); + + TArray Array; + String.ParseIntoArray(Array, TEXT("::")); + ensure(Array.Num() == 2); + + return Array[0]; + } +}; + +struct FVoxelCppUtils +{ + template + static typename TEnableIf::Value, FString>::Type LexToCpp(T Value) = delete; + + static FString LexToCpp(bool Value) + { + return LexToString(Value); + } + static FString LexToCpp(float Value) + { + return FString::SanitizeFloat(Value); + } + static FString LexToCpp(double Value) + { + return FString::SanitizeFloat(Value); + } + static FString LexToCpp(int32 Value) + { + return FString::FromInt(Value); + } + template + static typename TEnableIf::Value, FString>::Type LexToCpp(Enum Value) + { + return UEnum::GetValueAsString(Value); + } + static FString LexToCpp(const FString& Value) + { + return FString::Printf(TEXT("\"%s\""), *Value); + } + static FString LexToCpp(FName Value) + { + return FString::Printf(TEXT("STATIC_FNAME(\"%s\")"), *Value.ToString()); + } + static FString LexToCpp(const FRotator& Rotation) + { + return FString::Printf(TEXT("FRotator(%f, %f, %f)"), Rotation.Pitch, Rotation.Yaw, Rotation.Roll); + } + static FString LexToCpp(const FColor& Color) + { + return FString::Printf(TEXT("FColor(%d, %d, %d, %d)"), Color.R, Color.G, Color.B, Color.A); + } + static FString LexToCpp(const FLinearColor& Color) + { + return FString::Printf(TEXT("FLinearColor(%f, %f, %f, %f)"), Color.R, Color.G, Color.B, Color.A); + } + +public: + template + static FString TypeToString() + { + return TVoxelGetCppTypeImpl::Get(); + } + +public: + template + static FString ArrayToString(const TArray& Array) + { + FString Line = "{"; + for (auto& Name : Array) + { + Line += " "; + Line += LexToCpp(Name); + Line += ","; + } + Line += " }"; + return Line; + } + + template + static void CreateMapString(T& Constructor, const FString& MapName, const TArray& Keys, const TArray& Values, int32 ValuesOffset) + { + Constructor.StartBlock(); + for (int32 I = 0; I < Keys.Num(); I++) + { + Constructor.AddLinef(TEXT("%s.Add(%s), %s);"), *MapName, *LexToCpp(Keys[I]), *Values[ValuesOffset + I]); + } + Constructor.EndBlock(); + } + + template + static FString ClassString() + { + return FString::Printf(TEXT("%s%s"), T::StaticClass()->GetPrefixCPP(), *T::StaticClass()->GetName()); + } + + template + static FString SoftObjectPtrString() + { + return FString::Printf(TEXT("TSoftObjectPtr<%s>"), *ClassString()); + } + template + static FString SoftClassPtrString() + { + return FString::Printf(TEXT("TSoftClassPtr<%s>"), *ClassString()); + } + + template + static FString ObjectDefaultString(T* Object) + { + if (!Object) + { + return ""; + } + return FString::Printf(TEXT("%s(FSoftObjectPath(\"%s\"))"), *SoftObjectPtrString(), *Object->GetPathName()); + } + template + static FString ClassDefaultString(UClass* Class) + { + if (!Class) + { + return ""; + } + return FString::Printf(TEXT("%s(FSoftObjectPath(\"%s\"))"), *SoftClassPtrString(), *Class->GetPathName()); + } + + template + static FString PickerDefaultString(const T& Picker) + { + const FString Struct = T::StaticStruct()->GetStructCPPName(); + if (Picker.IsClass()) + { + return Struct + "(" + ClassDefaultString(Picker.Class) + ")"; + } + else + { + return Struct + "(" + ObjectDefaultString(Picker.Object) + ")"; + } + } + + static FString LoadObjectString(const FString& Name) + { + return Name + ".LoadSynchronous()"; + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h new file mode 100644 index 00000000..42602c1c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h @@ -0,0 +1,74 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UVoxelExposedNode; + +class VOXELGRAPH_API FVoxelVariable +{ +public: + static FString SanitizeName(const FString& Name); + +public: + const FString Type; + const FString ExposedName; + // ExposedName or eg Params.ExposedName if exposed variable + // Should be used to access the variable in C++ + const FString CppName; + + FVoxelVariable(const FString& Type, const FString& Name); + + inline FString GetDeclaration() const { return Type + TEXT(" ") + ExposedName; } + inline FString GetRefDeclaration() const { return Type + TEXT("& ") + ExposedName; } + inline FString GetConstDeclaration() const { return "const " + GetDeclaration(); } + inline FString GetConstRefDeclaration() const { return "const " + GetRefDeclaration(); } + +protected: + FVoxelVariable(const FString& Type, const FString& Name, const FString& CppPrefix); +}; + + +class VOXELGRAPH_API FVoxelExposedVariable : public FVoxelVariable +{ +public: + const UVoxelExposedNode* const Node; + const FString DefaultValue; + const FString ExposedType; + const FString Category; + const FString Tooltip; + const int32 Priority; + const TMap CustomMetaData; + + /** + * Exposed variable + * @param Type The type of the variable + * @param Node The node exposing this variable + * @param DefaultValue The default value of the variable, can be empty + * @param ExposedType If the exposed type isn't the same as the instance type. + You'll be able to do custom stuff through GetLocalVariableFromExposedOne + */ + FVoxelExposedVariable( + const UVoxelExposedNode& Node, + const FString& Type, + const FString& ExposedType, + const FString& DefaultValue); + + virtual ~FVoxelExposedVariable() = default; + + // For example: Name.GetGenerator() + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const + { + return ExposedNameAccessor; + } + // Some exposed variables always have some metadata + virtual TMap GetExposedVariableDefaultMetadata() const + { + return {}; + } + + bool IsSameAs(const FVoxelExposedVariable& Other, bool bCheckNode) const; + FString GetMetadataString() const; +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/IVoxelGraphEditor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/IVoxelGraphEditor.h new file mode 100644 index 00000000..02e422e7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/IVoxelGraphEditor.h @@ -0,0 +1,64 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +enum class EVoxelGraphNodeMessageType : int32; +struct FVoxelGraphMessage; +class FVoxelCompilationNode; +class UEdGraph; +class UEdGraphNode; +class UVoxelNode; +class UVoxelGraphGenerator; + +#if WITH_EDITOR +enum class EVoxelGraphPreviewFlags +{ + None = 0, + ManualPreview = 1 << 0, + UpdateMeshSettings = 1 << 1, + UpdateTextures = 1 << 2, + UpdatePlaceableItems = 1 << 3, + UpdateAll = UpdateMeshSettings | UpdateTextures | UpdatePlaceableItems, +}; +ENUM_CLASS_FLAGS(EVoxelGraphPreviewFlags); + +/** + * Interface for voxel graph interaction with the VoxelEditor module. + */ +class VOXELGRAPH_API IVoxelGraphEditor +{ +public: + virtual ~IVoxelGraphEditor() = default; + + // Called when creating a new voxel graph. + virtual UEdGraph* CreateNewVoxelGraph(UVoxelGraphGenerator* InGenerator) = 0; + + // Sets up a voxel node. + virtual void CreateVoxelGraphNode(UEdGraph* VoxelGraph, UVoxelNode* VoxelNode, bool bSelectNewNode) = 0; + + // Compiles voxel nodes from graph nodes. + virtual void CompileVoxelNodesFromGraphNodes(UVoxelGraphGenerator* Generator) = 0; + + virtual void UpdatePreview(UVoxelGraphGenerator* Generator, EVoxelGraphPreviewFlags Flags) = 0; + + virtual void SelectNodesAndZoomToFit(UEdGraph* Graph, const TArray& Nodes) = 0; + virtual void RefreshNodesMessages(UEdGraph* Graph) = 0; + + virtual void DebugNodes(UEdGraph* DebugGraph, const TSet& Nodes) = 0; + + virtual void AddMessages(const UVoxelGraphGenerator* Generator, const TArray& Messages) = 0; + virtual void ClearMessages(const UVoxelGraphGenerator* Generator, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) = 0; + +public: + // Sets the voxel graph editor implementation + static void SetVoxelGraphEditor(TSharedPtr InVoxelGraphEditor); + + inline static IVoxelGraphEditor* GetVoxelGraphEditor() { return VoxelGraphEditor.Get(); } + +private: + // Ptr to interface to voxel editor operations. + static TSharedPtr VoxelGraphEditor; +}; +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelDeprecatedNodeFunctions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelDeprecatedNodeFunctions.h new file mode 100644 index 00000000..e2332b0d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelDeprecatedNodeFunctions.h @@ -0,0 +1,94 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRange.h" +#include "VoxelGraphGlobals.h" +#include "VoxelUtilities/VoxelRangeUtilities.h" + +class FVoxelPlaceableItemHolder; + +DEPRECATED_VOXEL_GRAPH_FUNCTION() +typedef EVoxelDataItemCombineMode EVoxelDataItemSampleCombineMode; + +namespace FVoxelNodeFunctions +{ + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline v_flt GetPerlinWormsDistance(const FVoxelPlaceableItemHolder& ItemHolder, v_flt X, v_flt Y, v_flt Z) + { + return 0; + } + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline TVoxelRange GetPerlinWormsDistance(const FVoxelPlaceableItemHolder& ItemHolder, const TVoxelRange& X, const TVoxelRange& Y, const TVoxelRange& Z) + { + return 0; + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline FVoxelIntBox BoundsFromRanges(TVoxelRange X, TVoxelRange Y, TVoxelRange Z) + { + return FVoxelRangeUtilities::BoundsFromRanges(X, Y, Z); + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline v_flt GetWorldGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context) + { + return GetGeneratorCustomOutput(Generator, Name, X, Y, Z, Context); + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline TVoxelRange GetWorldGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + const FVoxelContextRange& Context) + { + return GetGeneratorCustomOutput(Generator, Name, X, Y, Z, Context); + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline TArray> CreateWorldGeneratorArray(const TArray& Generators) + { + return CreateGeneratorArray(Generators); + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline void ComputeWorldGeneratorsMerge( + EVoxelMaterialConfig MaterialConfig, + float Tolerance, + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContext& Context, + v_flt X, v_flt Y, v_flt Z, + int32 Index0, float Alpha0, + int32 Index1, float Alpha1, + int32 Index2, float Alpha2, + int32 Index3, float Alpha3, + bool bComputeValue, bool bComputeMaterial, const TArray& ComputeFloatOutputs, + v_flt& OutValue, + FVoxelMaterial& OutMaterial, + TArray>& OutFloatOutputs, + int32& NumGeneratorsQueried) + { + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline void ComputeWorldGeneratorsMergeRange( + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContextRange& Context, + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + bool bComputeValue, const TArray& ComputeFloatOutputs, + TVoxelRange& OutValue, + TArray, TInlineAllocator<128>> & OutFloatOutputs, + TVoxelRange& NumGeneratorsQueried) + { + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelMathNodeFunctions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelMathNodeFunctions.h new file mode 100644 index 00000000..557ea514 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelMathNodeFunctions.h @@ -0,0 +1,187 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelRange.h" +#include "VoxelUtilities/VoxelRangeUtilities.h" + +namespace FVoxelMathNodeFunctions +{ + FORCEINLINE void InverseTransformPositionXZ( + v_flt X_X, v_flt X_Y, v_flt X_Z, + v_flt Z_X, v_flt Z_Y, v_flt Z_Z, + v_flt X, v_flt Y, v_flt Z, + v_flt& OutX, v_flt& OutY, v_flt& OutZ) + { + const FVector BaseX = FVoxelVector(X_X, X_Y, X_Z).GetSafeNormal().ToFloat(); + FVector BaseZ = FVoxelVector(Z_X, Z_Y, Z_Z).GetSafeNormal().ToFloat(); + const FVector BaseY = BaseZ ^ BaseX; + BaseZ = BaseX ^ BaseY; + + if (BaseX.IsNearlyZero() || BaseY.IsNearlyZero() || BaseZ.IsNearlyZero()) + { + OutX = 0; + OutY = 0; + OutZ = 0; + return; + } + + const FMatrix Matrix(BaseX, BaseY, BaseZ, FVector::ZeroVector); + + FMatrix InvertedMatrix; + VectorMatrixInverse(&InvertedMatrix, &Matrix); + + const FVector Result = InvertedMatrix.TransformPosition(FVoxelVector(X, Y, Z).ToFloat()); + + OutX = Result.X; + OutY = Result.Y; + OutZ = Result.Z; + } + FORCEINLINE void InverseTransformPositionXZ( + TVoxelRange X_X, TVoxelRange X_Y, TVoxelRange X_Z, + TVoxelRange Z_X, TVoxelRange Z_Y, TVoxelRange Z_Z, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ) + { + if (!X_X.IsSingleValue() || !X_Y.IsSingleValue() || !X_Z.IsSingleValue() || + !Z_X.IsSingleValue() || !Z_Y.IsSingleValue() || !Z_Z.IsSingleValue()) + { + FVoxelRangeFailStatus::Get().Warning(TEXT("range analysis needs a fixed basis to work")); + OutX = TVoxelRange::Infinite(); + OutY = TVoxelRange::Infinite(); + OutZ = TVoxelRange::Infinite(); + return; + } + + const FVector BaseX = FVoxelVector(X_X.GetSingleValue(), X_Y.GetSingleValue(), X_Z.GetSingleValue()).GetSafeNormal().ToFloat(); + FVector BaseZ = FVoxelVector(Z_X.GetSingleValue(), Z_Y.GetSingleValue(), Z_Z.GetSingleValue()).GetSafeNormal().ToFloat(); + const FVector BaseY = BaseZ ^ BaseX; + BaseZ = BaseX ^ BaseY; + + if (BaseX.IsNearlyZero() || BaseY.IsNearlyZero() || BaseZ.IsNearlyZero()) + { + OutX = 0; + OutY = 0; + OutZ = 0; + return; + } + + const FMatrix Matrix = FMatrix(BaseX, BaseY, BaseZ, FVector::ZeroVector); + + FMatrix InvertedMatrix; + VectorMatrixInverse(&InvertedMatrix, &Matrix); + + bool bOutSet = false; + + const auto Check = [&](v_flt InX, v_flt InY, v_flt InZ) + { + const FVector Result = InvertedMatrix.TransformPosition(FVoxelVector(InX, InY, InZ).ToFloat()); + + if (bOutSet) + { + OutX = TVoxelRange::Union(OutX, Result.X); + OutY = TVoxelRange::Union(OutY, Result.Y); + OutZ = TVoxelRange::Union(OutZ, Result.Z); + } + else + { + OutX = Result.X; + OutY = Result.Y; + OutZ = Result.Z; + bOutSet = true; + } + }; + + Check(X.Min, Y.Min, Z.Min); + Check(X.Max, Y.Min, Z.Min); + Check(X.Min, Y.Max, Z.Min); + Check(X.Max, Y.Max, Z.Min); + Check(X.Min, Y.Min, Z.Max); + Check(X.Max, Y.Min, Z.Max); + Check(X.Min, Y.Max, Z.Max); + Check(X.Max, Y.Max, Z.Max); + } + + template + FORCEINLINE void HeightSplit(v_flt Height, const TInArray& Inputs, TOutArray& Outputs) + { + checkVoxelSlow(Inputs.Num() % 2 == 0); + checkVoxelSlow(Outputs.Num() == Inputs.Num() / 2 + 1); + + const int32 NumSplits = Inputs.Num() / 2; + + const auto GetHeight = [&](int32 Split) { return Inputs[2 * Split]; }; + const auto GetFalloff = [&](int32 Split) { return Inputs[2 * Split + 1]; }; + + for (int32 Layer = 0; Layer < NumSplits + 1; Layer++) + { + float Strength = 1; + + // Lower bound + if (Layer != 0) + { + const v_flt Bound = GetHeight(Layer - 1); + const v_flt Falloff = GetFalloff(Layer - 1); + + Strength = FMath::Min(Strength, FMath::SmoothStep(Bound - Falloff, Bound + Falloff, Height)); + } + + // Upper bound + if (Layer != NumSplits) + { + const v_flt Bound = GetHeight(Layer); + const v_flt Falloff = GetFalloff(Layer); + + Strength = FMath::Min(Strength, 1 - FMath::SmoothStep(Bound - Falloff, Bound + Falloff, Height)); + } + + ensureVoxelSlow(0 <= Strength); + Outputs[Layer] = Strength; + } + } + template + FORCEINLINE void HeightSplit(TVoxelRange Height, const TInArray& Inputs, TOutArray& Outputs) + { + for (auto& It : Outputs) + { + It = TVoxelRange(0, 1); + } + } + + FORCEINLINE v_flt SmoothStep(v_flt A, v_flt B, v_flt X) + { + if (X <= A) + { + return 0.0f; + } + else if (B <= X) + { + return 1.0f; + } + const v_flt InterpFraction = (X - A) / (B - A); + return InterpFraction * InterpFraction * (3.0f - 2.0f * InterpFraction); + } + FORCEINLINE TVoxelRange SmoothStep(TVoxelRange A, TVoxelRange B, TVoxelRange X) + { + if (A.IsSingleValue() && B.IsSingleValue()) + { + return { SmoothStep(A.GetSingleValue(), B.GetSingleValue(), X.Min), SmoothStep(A.GetSingleValue(), B.GetSingleValue(), X.Max) }; + } + + if (X.Max <= A.Min) + { + return 0.f; + } + if (B.Max <= X.Min) + { + return 1.f; + } + + const auto InterpFraction = (X - A) / (B - A); + const auto Result = InterpFraction * InterpFraction * (3.0f - 2.0f * InterpFraction); + + return FVoxelRangeUtilities::Clamp(Result, 0.f, 1.f); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelNodeFunctions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelNodeFunctions.h new file mode 100644 index 00000000..d97e44bf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelNodeFunctions.h @@ -0,0 +1,1232 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelMaterial.h" +#include "VoxelContext.h" +#include "VoxelRange.h" +#include "VoxelTexture.h" +#include "VoxelUtilities/VoxelMathUtilities.h" +#include "VoxelUtilities/VoxelRangeUtilities.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" +#include "VoxelUtilities/VoxelRichCurveUtilities.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelPlaceableItems/VoxelPlaceableItem.h" +#include "Curves/RichCurve.h" +#include "VoxelGraphGlobals.h" + +class UTexture2D; +class UCurveFloat; +class UCurveLinearColor; + +struct VOXELGRAPH_API FVoxelRichCurve +{ + FRichCurve Curve; + + inline float GetMin() const { return Min; } + inline float GetMax() const { return Max; } + + FVoxelRichCurve() = default; + explicit FVoxelRichCurve(const FRichCurve& Curve); + explicit FVoxelRichCurve(const UCurveFloat* Curve); + +private: + float Min = 0; + float Max = 0; +}; + +struct VOXELGRAPH_API FVoxelColorRichCurve +{ + FVoxelRichCurve Curves[4]; + + FVoxelColorRichCurve() = default; + FVoxelColorRichCurve(const UCurveLinearColor* Curve); +}; + +namespace FVoxelNodeFunctions +{ + inline v_flt Sqrt(v_flt F) + { + return FVoxelRangeUtilities::Sqrt(F); + } + inline TVoxelRange Sqrt(const TVoxelRange& F) + { + return FVoxelRangeUtilities::Sqrt(F); + } + + inline v_flt VectorLength(v_flt X, v_flt Y, v_flt Z) + { + return Sqrt(X * X + Y * Y + Z * Z); + } + inline TVoxelRange VectorLength(const TVoxelRange& X, const TVoxelRange& Y, const TVoxelRange& Z) + { + return Sqrt(X * X + Y * Y + Z * Z); + } + + inline void VectorRotateAngleAxis(v_flt X, v_flt Y, v_flt Z, v_flt AxisX, v_flt AxisY, v_flt AxisZ, v_flt Angle, v_flt& OutX, v_flt& OutY, v_flt& OutZ) + { + // taken from FVector; not using directly to keep from allocating a FVector + float S, C; + FMath::SinCos(&S, &C, FMath::DegreesToRadians(Angle)); + + const v_flt XX = AxisX * AxisX; + const v_flt YY = AxisY * AxisY; + const v_flt ZZ = AxisZ * AxisZ; + + const v_flt XY = AxisX * AxisY; + const v_flt YZ = AxisY * AxisZ; + const v_flt ZX = AxisZ * AxisX; + + const v_flt XS = AxisX * S; + const v_flt YS = AxisY * S; + const v_flt ZS = AxisZ * S; + + const v_flt OMC = 1. - C; + + OutX = (OMC * XX + C) * X + (OMC * XY - ZS) * Y + (OMC * ZX + YS) * Z; + OutY = (OMC * XY + ZS) * X + (OMC * YY + C) * Y + (OMC * YZ - XS) * Z; + OutZ = (OMC * ZX - YS) * X + (OMC * YZ + XS) * Y + (OMC * ZZ + C) * Z; + } + inline void VectorRotateAngleAxis( + const TVoxelRange& X, + const TVoxelRange& Y, + const TVoxelRange& Z, + const TVoxelRange& AxisX, + const TVoxelRange& AxisY, + const TVoxelRange& AxisZ, + const TVoxelRange& Angle, + TVoxelRange& OutX, + TVoxelRange& OutY, + TVoxelRange& OutZ) + { + if (X.IsSingleValue() && + Y.IsSingleValue() && + Z.IsSingleValue() && + AxisX.IsSingleValue() && + AxisY.IsSingleValue() && + AxisZ.IsSingleValue() && + Angle.IsSingleValue()) + { + v_flt OutXF, OutYF, OutZF; + VectorRotateAngleAxis( + X.GetSingleValue(), + Y.GetSingleValue(), + Z.GetSingleValue(), + AxisX.GetSingleValue(), + AxisY.GetSingleValue(), + AxisZ.GetSingleValue(), + Angle.GetSingleValue(), + OutXF, + OutYF, + OutZF); + + OutX = OutXF; + OutY = OutYF; + OutZ = OutZF; + } + else + { + FVoxelRangeFailStatus::Get().Warning(TEXT("VectorRotateAngleAxis doesn't support range analysis")); + OutX = TVoxelRange::Infinite(); + OutY = TVoxelRange::Infinite(); + OutZ = TVoxelRange::Infinite(); + } + } + + inline int32 RoundToInt(v_flt Value) + { + return FMath::RoundToInt(Value); + } + inline TVoxelRange RoundToInt(const TVoxelRange& Value) + { + return { FMath::FloorToInt(Value.Min), FMath::CeilToInt(Value.Max) }; + } + + inline v_flt Lerp(v_flt A, v_flt B, v_flt Alpha) + { + return FMath::Lerp(A, B, Alpha); + } + inline TVoxelRange Lerp(const TVoxelRange& A, const TVoxelRange& B, const TVoxelRange& Alpha) + { + return FVoxelRangeUtilities::Lerp(A, B, Alpha); + } + + inline v_flt SafeLerp(v_flt A, v_flt B, v_flt Alpha) + { + return FMath::Lerp(A, B, FMath::Clamp(Alpha, 0, 1)); + } + inline TVoxelRange SafeLerp(const TVoxelRange& A, const TVoxelRange& B, const TVoxelRange& Alpha) + { + if (Alpha.IsSingleValue()) + { + return { SafeLerp(A.Min, B.Min, Alpha.GetSingleValue()), SafeLerp(A.Max, B.Max, Alpha.GetSingleValue()) }; + } + return + { + FMath::Min(SafeLerp(A.Min, B.Min, Alpha.Min), SafeLerp(A.Min, B.Min, Alpha.Max)), + FMath::Max(SafeLerp(A.Max, B.Max, Alpha.Min), SafeLerp(A.Max, B.Max, Alpha.Max)) + }; + } + + template + inline T Clamp(T Value, T Min, T Max) + { + return FMath::Clamp(Value, Min, Max); + } + template + inline TVoxelRange Clamp(const TVoxelRange& Value, const TVoxelRange& Min, const TVoxelRange& Max) + { + return FVoxelRangeUtilities::Clamp(Value, Min, Max); + } + + inline int32 RightShift(int32 A, int32 B) + { + return A >> FMath::Clamp(B, 0, 32); + } + inline TVoxelRange RightShift(TVoxelRange A, TVoxelRange B) + { + // NOTE: Does not take into account overflows + return TVoxelRange::FromList( + RightShift(A.Min, B.Min), + RightShift(A.Min, B.Max), + RightShift(A.Max, B.Min), + RightShift(A.Max, B.Max)); + } + + inline int32 LeftShift(int32 A, int32 B) + { + return A << FMath::Clamp(B, 0, 32); + } + inline TVoxelRange LeftShift(TVoxelRange A, TVoxelRange B) + { + // NOTE: Does not take into account overflows + return TVoxelRange::FromList( + RightShift(A.Min, B.Min), + RightShift(A.Min, B.Max), + RightShift(A.Max, B.Min), + RightShift(A.Max, B.Max)); + } + + inline v_flt Pow(v_flt A, v_flt B) + { + return std::pow(A, B); + } + inline TVoxelRange Pow(const TVoxelRange& A, const TVoxelRange& B) + { + if (B.IsSingleValue()) + { + const v_flt Exp = B.GetSingleValue(); + const int32 IntExp = FMath::RoundToInt(Exp); + if (Exp == IntExp) // If integer + { + if (IntExp % 2 == 0) // If multiple of 2: decreasing [-infinity, 0] and increasing [0, infinity] + { + if (0 <= A.Min) + { + return { Pow(A.Min, Exp), Pow(A.Max, Exp) }; + } + else if (A.Max <= 0) + { + return { Pow(A.Max, Exp), Pow(A.Min, Exp) }; + } + else + { + return { 0, FMath::Max(Pow(A.Max, Exp), Pow(A.Min, Exp)) }; + } + } + else // Increasing + { + return { Pow(A.Min, Exp), Pow(A.Max, Exp) }; + } + } + else + { + // If not integer then pow(x, exp) = 0 if x < 0 + if (0 <= A.Min) + { + return { Pow(A.Min, Exp), Pow(A.Max, Exp) }; + } + else if (A.Max <= 0) + { + return { 0, 0 }; + } + else + { + return { 0, Pow(A.Max, Exp) }; + } + } + } + else + { + FVoxelRangeFailStatus::Get().Warning(TEXT("pow only supports range analysis with a constant exponent")); + return TVoxelRange::Infinite(); + } + } + + template + inline T Abs(T A) + { + return FMath::Abs(A); + } + template + inline TVoxelRange Abs(const TVoxelRange& A) + { + return FVoxelRangeUtilities::Abs(A); + } + + inline int32 CeilToInt(v_flt A) + { + return FMath::CeilToInt(A); + } + inline TVoxelRange CeilToInt(const TVoxelRange& A) + { + return { FMath::CeilToInt(A.Min), FMath::CeilToInt(A.Max) }; + } + + inline int32 FloorToInt(v_flt A) + { + return FMath::FloorToInt(A); + } + inline TVoxelRange FloorToInt(const TVoxelRange& A) + { + return { FMath::FloorToInt(A.Min), FMath::FloorToInt(A.Max) }; + } + + inline v_flt Fractional(v_flt A) + { + if (TIsSame::Value) + { + return FMath::Fractional(A); + } + else + { + return A - int64(A); + } + } + inline TVoxelRange Fractional(const TVoxelRange& A) + { + if (0 <= A.Min) + { + return { 0, 1 }; + } + else if (A.Max <= 0) + { + return { -1, 0 }; + } + else + { + return { -1, 1 }; + } + } + + template + inline T Sign(T A) + { + return FMath::Sign(A); + } + template + inline TVoxelRange Sign(const TVoxelRange& A) + { + return FVoxelRangeUtilities::Sign(A); + } + + inline v_flt InvSqrt(v_flt A) + { + if (A <= 0) + { + return 0; + } + else + { + if (TIsSame::Value) + { + return FMath::InvSqrt(A); + } + else + { + return 1. / std::sqrt(A); + } + } + } + + inline TVoxelRange InvSqrt(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return InvSqrt(A.GetSingleValue()); + } + + if (A.Max <= 0) + { + return { 0, 0 }; + } + else if (0 < A.Min) + { + return { InvSqrt(A.Max), InvSqrt(A.Min) }; + } + else + { + FVoxelRangeFailStatus::Get().Warning(TEXT("inv sqrt does not support range analysis with ranges containing 0")); + return TVoxelRange::Infinite(); + } + } + + inline v_flt Loge(v_flt A) + { + if (A <= 0) + { + return 0; + } + else + { + return std::log(A); + } + } + inline TVoxelRange Loge(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Loge(A.GetSingleValue()); + } + + if (A.Max <= 0) + { + return { 0, 0 }; + } + else if (0 < A.Min) + { + return { Loge(A.Min), Loge(A.Max) }; + } + else + { + FVoxelRangeFailStatus::Get().Warning(TEXT("loge does not support range analysis with ranges containing 0")); + return TVoxelRange::Infinite(); + } + } + + inline v_flt Exp(v_flt A) + { + return std::exp(A); + } + inline TVoxelRange Exp(const TVoxelRange& A) + { + return { Exp(A.Min), Exp(A.Max) }; + } + + inline v_flt Sinh(v_flt A) + { + return std::sinh(A); + } + inline TVoxelRange Sinh(const TVoxelRange& A) + { + return { Sinh(A.Min), Sinh(A.Max) }; + } + + inline v_flt Sin(v_flt A) + { + return std::sin(A); + } + inline TVoxelRange Sin(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Sin(A.GetSingleValue()); + } + else + { + return { -1, 1 }; + } + } + + inline v_flt Cos(v_flt A) + { + return std::cos(A); + } + inline TVoxelRange Cos(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Cos(A.GetSingleValue()); + } + else + { + return { -1, 1 }; + } + } + + inline v_flt Asin(v_flt A) + { + return std::asin(A); + } + inline TVoxelRange Asin(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Asin(A.GetSingleValue()); + } + else + { + return { -2, 2 }; + } + } + + inline v_flt Acos(v_flt A) + { + return std::acos(A); + } + inline TVoxelRange Acos(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Acos(A.GetSingleValue()); + } + else + { + return { 0, 4 }; + } + } + + inline void SinCos(v_flt A, v_flt& OutSin, v_flt& OutCos) + { +#if VOXEL_DOUBLE_PRECISION + OutSin = std::sin(A); + OutCos = std::cos(A); +#else + FMath::SinCos(&OutSin, &OutCos, A); +#endif + } + inline void SinCos(const TVoxelRange& A, TVoxelRange& OutSin, TVoxelRange& OutCos) + { + OutSin = Sin(A); + OutCos = Cos(A); + } + + inline v_flt Tan(v_flt A) + { + return std::tan(A); + } + inline TVoxelRange Tan(const TVoxelRange& A) + { + if (A.IsSingleValue()) + { + return Tan(A.GetSingleValue()); + } + else if (-PI / 2 < A.Min && A.Max < PI / 2) + { + return { Tan(A.Min), Tan(A.Max) }; + } + else + { + FVoxelRangeFailStatus::Get().Warning(TEXT("tan does not support range analysis for values outside [-pi/2, pi/2]")); + return TVoxelRange::Infinite(); + } + } + + inline v_flt Atan(v_flt A) + { + return std::atan(A); + } + inline TVoxelRange Atan(const TVoxelRange& A) + { + return { Atan(A.Min), Atan(A.Max) }; + } + + inline v_flt Atan2(v_flt Y, v_flt X) + { + return std::atan2(Y, X); + } + inline TVoxelRange Atan2(const TVoxelRange& Y, const TVoxelRange& X) + { + return { -PI / 2, PI / 2 }; + } + + template + inline T Min(T A, T B) + { + return FMath::Min(A, B); + } + template + inline TVoxelRange Min(const TVoxelRange& A, const TVoxelRange& B) + { + return FVoxelRangeUtilities::Min(A, B); + } + + template + inline T Max(T A, T B) + { + return FMath::Max(A, B); + } + template + inline TVoxelRange Max(const TVoxelRange& A, const TVoxelRange& B) + { + return FVoxelRangeUtilities::Max(A, B); + } + + inline v_flt Union(v_flt A, v_flt B) + { + return 0; + } + inline TVoxelRange Union(const TVoxelRange& A, const TVoxelRange& B) + { + return TVoxelRange::Union(A, B); + } + + inline bool IsSingleBool(bool bValue) + { + return true; + } + inline FVoxelBoolRange IsSingleBool(FVoxelBoolRange Bool) + { + return !(Bool.bCanBeTrue && Bool.bCanBeFalse); + } + + inline v_flt GetCurveValue(const FVoxelRichCurve& Curve, v_flt Value) + { + return FVoxelRichCurveUtilities::Eval(Curve.Curve, Value); + } + VOXELGRAPH_API TVoxelRange GetCurveValue(const FVoxelRichCurve& Curve, const TVoxelRange& Value); + + inline void ReadColorTextureDataFloat( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + v_flt U, + v_flt V, + v_flt& OutR, + v_flt& OutG, + v_flt& OutB, + v_flt& OutA) + { + const FLinearColor Color = Texture.Sample(U, V, Mode); + OutR = Color.R; + OutG = Color.G; + OutB = Color.B; + OutA = Color.A; + } + inline void ReadColorTextureDataFloat( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + const TVoxelRange& U, + const TVoxelRange& V, + TVoxelRange& OutR, + TVoxelRange& OutG, + TVoxelRange& OutB, + TVoxelRange& OutA) + { + if (U.IsSingleValue() && V.IsSingleValue()) + { + v_flt R, G, B, A; + ReadColorTextureDataFloat(Texture, Mode, U.GetSingleValue(), V.GetSingleValue(), R, G, B, A); + OutR = R; + OutG = G; + OutB = B; + OutA = A; + } + else + { + OutR = { 0, 1 }; + OutG = { 0, 1 }; + OutB = { 0, 1 }; + OutA = { 0, 1 }; + } + } + inline void ReadColorTextureDataInt( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + int32 U, + int32 V, + v_flt& OutR, + v_flt& OutG, + v_flt& OutB, + v_flt& OutA) + { + const FLinearColor Color = Texture.Sample(U, V, Mode); + OutR = Color.R; + OutG = Color.G; + OutB = Color.B; + OutA = Color.A; + } + inline void ReadColorTextureDataInt( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + const TVoxelRange& U, + const TVoxelRange& V, + TVoxelRange& OutR, + TVoxelRange& OutG, + TVoxelRange& OutB, + TVoxelRange& OutA) + { + if (U.IsSingleValue() && V.IsSingleValue()) + { + v_flt R, G, B, A; + ReadColorTextureDataInt(Texture, Mode, U.GetSingleValue(), V.GetSingleValue(), R, G, B, A); + OutR = R; + OutG = G; + OutB = B; + OutA = A; + } + else + { + OutR = { 0, 1 }; + OutG = { 0, 1 }; + OutB = { 0, 1 }; + OutA = { 0, 1 }; + } + } + + inline v_flt ReadFloatTextureDataFloat( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + v_flt U, + v_flt V) + { + return Texture.Sample(U, V, Mode); + } + inline TVoxelRange ReadFloatTextureDataFloat( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + const TVoxelRange& U, + const TVoxelRange& V) + { + if (U.IsSingleValue() && V.IsSingleValue()) + { + return ReadFloatTextureDataFloat(Texture, Mode, U.GetSingleValue(), V.GetSingleValue()); + } + else + { + ensure(Texture.GetMin() <= Texture.GetMax()); + return { Texture.GetMin(), Texture.GetMax() }; + } + } + inline v_flt ReadFloatTextureDataInt( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + int32 U, + int32 V) + { + return Texture.Sample(U, V, Mode); + } + inline TVoxelRange ReadFloatTextureDataInt( + const TVoxelTexture& Texture, + const EVoxelSamplerMode Mode, + const TVoxelRange& U, + const TVoxelRange& V) + { + if (U.IsSingleValue() && V.IsSingleValue()) + { + return ReadFloatTextureDataInt(Texture, Mode, U.GetSingleValue(), V.GetSingleValue()); + } + else + { + ensure(Texture.GetMin() <= Texture.GetMax()); + return { Texture.GetMin(), Texture.GetMax() }; + } + } + + inline void FindColorsAlphas( + const int Threshold, + const TArray& Colors, + const FColor& Color, + v_flt OutAlphas[]) + { + for (int32 Index = 0; Index < Colors.Num(); Index++) + { + // Ignore alpha + auto& A = Color; + auto& B = Colors[Index]; + const int32 DiffR = int32(A.R) - int32(B.R); + const int32 DiffG = int32(A.G) - int32(B.G); + const int32 DiffB = int32(A.B) - int32(B.B); + const int32 Value = FMath::Max3(FMath::Abs(DiffR), FMath::Abs(DiffG), FMath::Abs(DiffB)); + OutAlphas[Index] = Value <= Threshold ? 1 : 0; + } + } + template + inline void FindColorsLerpedAlphas( + const int Threshold, + const TArray& Colors, + const TVoxelTexture& Texture, + const v_flt X, + const v_flt Y, + T SetAlpha) + { + const int32 MinX = FMath::FloorToInt(X); + const int32 MinY = FMath::FloorToInt(Y); + + const int32 MaxX = FMath::CeilToInt(X); + const int32 MaxY = FMath::CeilToInt(Y); + + const v_flt AlphaX = X - MinX; + const v_flt AlphaY = Y - MinY; + + check(Colors.Num() < 256); + v_flt MinXMinYAlphas[256]; + v_flt MaxXMinYAlphas[256]; + v_flt MinXMaxYAlphas[256]; + v_flt MaxXMaxYAlphas[256]; + + FVoxelNodeFunctions::FindColorsAlphas(Threshold, Colors, Texture.SampleRaw(MinX, MinY, EVoxelSamplerMode::Clamp), MinXMinYAlphas); + FVoxelNodeFunctions::FindColorsAlphas(Threshold, Colors, Texture.SampleRaw(MaxX, MinY, EVoxelSamplerMode::Clamp), MaxXMinYAlphas); + FVoxelNodeFunctions::FindColorsAlphas(Threshold, Colors, Texture.SampleRaw(MinX, MaxY, EVoxelSamplerMode::Clamp), MinXMaxYAlphas); + FVoxelNodeFunctions::FindColorsAlphas(Threshold, Colors, Texture.SampleRaw(MaxX, MaxY, EVoxelSamplerMode::Clamp), MaxXMaxYAlphas); + + for (int32 Index = 0; Index < Colors.Num(); Index++) + { + SetAlpha(Index, FVoxelUtilities::BilinearInterpolation( + MinXMinYAlphas[Index], + MaxXMinYAlphas[Index], + MinXMaxYAlphas[Index], + MaxXMaxYAlphas[Index], + AlphaX, + AlphaY)); + } + } + + inline void GlobalToLocal(v_flt InX, v_flt InY, v_flt InZ, v_flt& OutX, v_flt& OutY, v_flt& OutZ, const FVoxelContext& Context) + { + if (Context.bHasCustomTransform) + { + const FVector P = Context.LocalToWorld.InverseTransformPosition(FVector(InX, InY, InZ)); + OutX = P.X; + OutY = P.Y; + OutZ = P.Z; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + inline void LocalToGlobal(v_flt InX, v_flt InY, v_flt InZ, v_flt& OutX, v_flt& OutY, v_flt& OutZ, const FVoxelContext& Context) + { + if (Context.bHasCustomTransform) + { + const FVector P = Context.LocalToWorld.TransformPosition(FVector(InX, InY, InZ)); + OutX = P.X; + OutY = P.Y; + OutZ = P.Z; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + inline void GlobalToLocal( + TVoxelRange InX, TVoxelRange InY, TVoxelRange InZ, + TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ, const FVoxelContextRange& Context) + { + if (Context.bHasCustomTransform) + { + OutX = TVoxelRange::Infinite(); + OutY = TVoxelRange::Infinite(); + OutZ = TVoxelRange::Infinite(); + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + inline void LocalToGlobal( + TVoxelRange InX, TVoxelRange InY, TVoxelRange InZ, + TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ, const FVoxelContextRange& Context) + { + if (Context.bHasCustomTransform) + { + OutX = TVoxelRange::Infinite(); + OutY = TVoxelRange::Infinite(); + OutZ = TVoxelRange::Infinite(); + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + + inline void TransformVector(v_flt InX, v_flt InY, v_flt InZ, v_flt& OutX, v_flt& OutY, v_flt& OutZ, const FVoxelContext& Context) + { + if (Context.bHasCustomTransform) + { + const FVector P = Context.LocalToWorld.TransformVector(FVector(InX, InY, InZ)); + OutX = P.X; + OutY = P.Y; + OutZ = P.Z; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + inline void InverseTransformVector(v_flt InX, v_flt InY, v_flt InZ, v_flt& OutX, v_flt& OutY, v_flt& OutZ, const FVoxelContext& Context) + { + if (Context.bHasCustomTransform) + { + const FVector P = Context.LocalToWorld.InverseTransformVector(FVector(InX, InY, InZ)); + OutX = P.X; + OutY = P.Y; + OutZ = P.Z; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + + inline void TransformVector( + TVoxelRange InX, TVoxelRange InY, TVoxelRange InZ, + TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ, const FVoxelContextRange& Context) + { + if (Context.bHasCustomTransform) + { + const FVector Scale = Context.LocalToWorld.GetScale3D(); + const TVoxelRange ScaleRange{ Scale.GetMin(), Scale.GetMax() }; + const auto Result = TVoxelRange::Union(InX * ScaleRange, InY * ScaleRange, InZ * ScaleRange); + + OutX = Result; + OutY = Result; + OutZ = Result; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + inline void InverseTransformVector( + TVoxelRange InX, TVoxelRange InY, TVoxelRange InZ, + TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ, const FVoxelContextRange& Context) + { + if (Context.bHasCustomTransform) + { + const FVector Scale = FVector(1.f) / Context.LocalToWorld.GetScale3D(); + const TVoxelRange ScaleRange{ Scale.GetMin(), Scale.GetMax() }; + const auto Result = TVoxelRange::Union(InX * ScaleRange, InY * ScaleRange, InZ * ScaleRange); + + OutX = Result; + OutY = Result; + OutZ = Result; + } + else + { + OutX = InX; + OutY = InY; + OutZ = InZ; + } + } + + inline FColor ColorFromMaterial(const FVoxelMaterial& Material) + { + return Material.GetColor(); + } + inline FVoxelColorRange ColorFromMaterial(const FVoxelMaterialRange& Material) + { + return {}; + } + + inline int32 SingleIndexFromMaterial(const FVoxelMaterial& Material) + { + return Material.GetSingleIndex(); + } + inline TVoxelRange SingleIndexFromMaterial(const FVoxelMaterialRange& Material) + { + return { 0, 255 }; + } + + inline void GetUVChannelFromMaterial(FVoxelMaterial Material, int32 Channel, v_flt& U, v_flt& V) + { + U = Material.GetU_AsFloat(Channel); + V = Material.GetV_AsFloat(Channel); + } + inline void GetUVChannelFromMaterial(FVoxelMaterialRange Material, TVoxelRange Channel, TVoxelRange& U, TVoxelRange& V) + { + U = { 0.f, 1.f }; + V = { 0.f, 1.f }; + } + + VOXELGRAPH_API v_flt GetPreviousGeneratorValue( + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator); + VOXELGRAPH_API TVoxelRange GetPreviousGeneratorValue( + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + const FVoxelContextRange& Context, + const FVoxelGeneratorInstance* DefaultGenerator); + + VOXELGRAPH_API FVoxelMaterial GetPreviousGeneratorMaterial( + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator); + + VOXELGRAPH_API v_flt GetPreviousGeneratorCustomOutput( + const FName& Name, + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context, + const FVoxelGeneratorInstance* DefaultGenerator); + VOXELGRAPH_API TVoxelRange GetPreviousGeneratorCustomOutput( + const FName& Name, + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + const FVoxelContextRange& Context, + const FVoxelGeneratorInstance* DefaultGenerator); + + VOXELGRAPH_API v_flt GetGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + v_flt X, v_flt Y, v_flt Z, + const FVoxelContext& Context); + VOXELGRAPH_API TVoxelRange GetGeneratorCustomOutput( + const FVoxelGeneratorInstance& Generator, + const FName& Name, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + const FVoxelContextRange& Context); + + inline v_flt Fmod(v_flt X, v_flt Y) + { + if (FMath::Abs(Y) <= 1.e-8f) + { + return 0.; + } + if (TIsSame::Value) + { + return FMath::Fmod(X, Y); + } + else + { + return std::fmod(X, Y); + } + } + + inline TVoxelRange Fmod(TVoxelRange X, TVoxelRange Y) + { + if (X.IsSingleValue() && Y.IsSingleValue()) + { + return Fmod(X.GetSingleValue(), Y.GetSingleValue()); + } + + const v_flt YMaxAbs = FMath::Max(FMath::Abs(Y.Min), FMath::Abs(Y.Max)); + const TVoxelRange Result = { -YMaxAbs, YMaxAbs }; + if (Result.Contains(X)) + { + return X; + } + else + { + return Result; + } + } + + inline int32 Mod(int32 X, int32 Y) + { + if (Y == 0) + { + return 0; + } + return X % Y; + } + inline TVoxelRange Mod(TVoxelRange X, TVoxelRange Y) + { + if (X.IsSingleValue() && Y.IsSingleValue()) + { + return Mod(X.GetSingleValue(), Y.GetSingleValue()); + } + + const int32 YMaxAbs = FMath::Max(FMath::Abs(Y.Min), FMath::Abs(Y.Max)); + const TVoxelRange Result = { -YMaxAbs, YMaxAbs }; + if (Result.Contains(X)) + { + return X; + } + else + { + return Result; + } + } + + inline v_flt OneOverX(v_flt X) + { + return 1. / X; + } + inline TVoxelRange OneOverX(TVoxelRange X) + { + return TVoxelRange(1.) / X; + } + + inline void BreakColor( + const FColor& Color, + int32& OutR, + int32& OutG, + int32& OutB, + int32& OutA) + { + OutR = Color.R; + OutG = Color.G; + OutB = Color.B; + OutA = Color.A; + } + inline void BreakColor( + const FVoxelColorRange& Color, + TVoxelRange& OutR, + TVoxelRange& OutG, + TVoxelRange& OutB, + TVoxelRange& OutA) + { + OutR = { 0, 255 }; + OutG = { 0, 255 }; + OutB = { 0, 255 }; + OutA = { 0, 255 }; + } + + inline void BreakColorFloat( + const FColor& Color, + v_flt& OutR, + v_flt& OutG, + v_flt& OutB, + v_flt& OutA) + { + OutR = FVoxelUtilities::UINT8ToFloat(Color.R); + OutG = FVoxelUtilities::UINT8ToFloat(Color.G); + OutB = FVoxelUtilities::UINT8ToFloat(Color.B); + OutA = FVoxelUtilities::UINT8ToFloat(Color.A); + } + inline void BreakColorFloat( + const FVoxelColorRange& Color, + TVoxelRange& OutR, + TVoxelRange& OutG, + TVoxelRange& OutB, + TVoxelRange& OutA) + { + OutR = { 0, 1 }; + OutG = { 0, 1 }; + OutB = { 0, 1 }; + OutA = { 0, 1 }; + } + + inline FColor MakeColor(int32 R, int32 G, int32 B, int32 A) + { + return FColor( + FMath::Clamp(R, 0, 255), + FMath::Clamp(G, 0, 255), + FMath::Clamp(B, 0, 255), + FMath::Clamp(A, 0, 255)); + } + inline FVoxelColorRange MakeColor( + const TVoxelRange& R, + const TVoxelRange& G, + const TVoxelRange& B, + const TVoxelRange& A) + { + return {}; + } + + inline FColor MakeColorFloat(v_flt R, v_flt G, v_flt B, v_flt A) + { + return FColor( + FVoxelUtilities::FloatToUINT8(R), + FVoxelUtilities::FloatToUINT8(G), + FVoxelUtilities::FloatToUINT8(B), + FVoxelUtilities::FloatToUINT8(A)); + } + inline FVoxelColorRange MakeColorFloat( + const TVoxelRange& R, + const TVoxelRange& G, + const TVoxelRange& B, + const TVoxelRange& A) + { + return {}; + } + + inline void RGBToHSV(v_flt R, v_flt G, v_flt B, v_flt& OutH, v_flt& OutS, v_flt& OutV) + { + const auto HSVColor = FLinearColor(R, G, B, 0).LinearRGBToHSV(); + OutH = HSVColor.R; + OutS = HSVColor.G; + OutV = HSVColor.B; + } + inline void RGBToHSV( + TVoxelRange R, TVoxelRange G, TVoxelRange B, + TVoxelRange& OutH, TVoxelRange& OutS, TVoxelRange& OutV) + { + OutH = { 0, 360 }; + OutS = { 0, 1 }; + OutV = TVoxelRange::Union(R, G, B); + } + + inline void HSVToRGB(v_flt H, v_flt S, v_flt V, v_flt& OutR, v_flt& OutG, v_flt& OutB) + { + const auto RGBColor = FLinearColor(H, S, V, 0).HSVToLinearRGB(); + OutR = RGBColor.R; + OutG = RGBColor.G; + OutB = RGBColor.B; + } + inline void HSVToRGB( + TVoxelRange H, TVoxelRange S, TVoxelRange V, + TVoxelRange& OutR, TVoxelRange& OutG, TVoxelRange& OutB) + { + OutR = OutG = OutB = TVoxelRange::Union(0, V); + } + + VOXELGRAPH_API TArray> CreateGeneratorArray(const TArray& Generators); + + VOXELGRAPH_API void ComputeGeneratorsMerge( + EVoxelMaterialConfig MaterialConfig, + float Tolerance, + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContext& Context, + v_flt X, v_flt Y, v_flt Z, + int32 Index0, float Alpha0, + int32 Index1, float Alpha1, + int32 Index2, float Alpha2, + int32 Index3, float Alpha3, + bool bComputeValue, bool bComputeMaterial, const TArray& ComputeFloatOutputs, + v_flt& OutValue, + FVoxelMaterial& OutMaterial, + TArray>& OutFloatOutputs, + int32& NumGeneratorsQueried); + + VOXELGRAPH_API void ComputeGeneratorsMergeRange( + const TArray>& InInstances, + const TArray& FloatOutputsNames, + const FVoxelContextRange& Context, + TVoxelRange X, + TVoxelRange Y, + TVoxelRange Z, + bool bComputeValue, const TArray& ComputeFloatOutputs, + TVoxelRange& OutValue, + TArray, TInlineAllocator<128>> & OutFloatOutputs, + TVoxelRange& NumGeneratorsQueried); + + template + inline T Switch(T A, T B, bool bPickA) + { + return bPickA ? A : B; + } + template + inline TVoxelRange Switch(TVoxelRange A, TVoxelRange B, FVoxelBoolRange PickA) + { + if (!PickA.bCanBeTrue) + { + return B; + } + if (!PickA.bCanBeFalse) + { + return A; + } + return TVoxelRange::Union(A, B); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelPlaceableItemsNodeFunctions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelPlaceableItemsNodeFunctions.h new file mode 100644 index 00000000..0f2aa5a7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelPlaceableItemsNodeFunctions.h @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUtilities/VoxelDataItemUtilities.h" + +namespace FVoxelNodeFunctions +{ + inline v_flt GetDataItemDistance( + const FVoxelPlaceableItemHolder& ItemHolder, + v_flt X, v_flt Y, v_flt Z, + v_flt Smoothness, + v_flt Default, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode) + { + return FVoxelUtilities::GetDataItemDistance(ItemHolder, X, Y, Z, Smoothness, Default, Mask, CombineMode); + } + + inline TVoxelRange GetDataItemDistance( + const FVoxelPlaceableItemHolder& ItemHolder, + TVoxelRange X, TVoxelRange Y, TVoxelRange Z, + TVoxelRange Smoothness, + TVoxelRange Default, + uint32 Mask, + EVoxelDataItemCombineMode CombineMode) + { + return FVoxelUtilities::GetDataItemDistanceRange(ItemHolder, X, Y, Z, Smoothness, Default, Mask, CombineMode); + } + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline v_flt GetDataItemDistance(const FVoxelPlaceableItemHolder& ItemHolder, v_flt X, v_flt Y, v_flt Z, v_flt Smoothness, v_flt Default) + { + return GetDataItemDistance(ItemHolder, X, Y, Z, Smoothness, Default, uint32(-1), EVoxelDataItemCombineMode::Min); + } + DEPRECATED_VOXEL_GRAPH_FUNCTION() + inline TVoxelRange GetDataItemDistance(const FVoxelPlaceableItemHolder& ItemHolder, TVoxelRange X, TVoxelRange Y, TVoxelRange Z, TVoxelRange Smoothness, TVoxelRange Default) + { + return GetDataItemDistance(ItemHolder, X, Y, Z, Smoothness, Default, uint32(-1), EVoxelDataItemCombineMode::Min); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelSDFNodeFunctions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelSDFNodeFunctions.h new file mode 100644 index 00000000..6738f484 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/NodeFunctions/VoxelSDFNodeFunctions.h @@ -0,0 +1,229 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelUtilities/VoxelSDFUtilities.h" +#include "VoxelUtilities/VoxelSDFRangeUtilities.h" + +namespace FVoxelSDFNodeFunctions +{ +#define DEFINE_SDF_FUNCTION_EX(Name, Prefix, Args, sdArgs) \ + template \ + FORCEINLINE typename TEnableIf::Value, T>::Type Name Args { return FVoxelSDFUtilities::Prefix##Name sdArgs; } \ + template \ + FORCEINLINE typename TEnableIf>::Value, T>::Type Name Args { return FVoxelSDFRangeUtilities::Prefix##Name sdArgs; } + +#define DEFINE_SDF_FUNCTION(Name, Args, sdArgs) DEFINE_SDF_FUNCTION_EX(Name, sd, Args, sdArgs) + +// Put the args in parenthesis, else they are expanded and DEFINE_SDF_FUNCTION is called with too many arguments +// DEFINE_SDF_FUNCTION_EX accounts for the args being in parenthesis +// NOTE: this is only an issue on Clang, MSVC compiles fine :/ +#define ARG(...) (__VA_ARGS__) + +#define VECTOR_A(Name) T Name##X, T Name##Y, T Name##Z +// NOTE: We swap Y and Z to match the axis of shadertoy vs unreal +#define VECTOR_B(Name) { Name##X, Name##Z, Name##Y } + +#define VECTOR2_A(Name) T Name##X, T Name##Y +#define VECTOR2_B(Name) { Name##X, Name##Y } + +#define SCALAR_A(Name) T Name +#define SCALAR_B(Name) Name + + // Sphere - exact + DEFINE_SDF_FUNCTION( + Sphere, + ARG(VECTOR_A(p), SCALAR_A(s)), + ARG(VECTOR_B(p), SCALAR_B(s))); + + // Box - exact + DEFINE_SDF_FUNCTION( + Box, + ARG(VECTOR_A(p), VECTOR_A(b)), + ARG(VECTOR_B(p), VECTOR_B(b))); + + // Round Box - exact + DEFINE_SDF_FUNCTION( + RoundBox, + ARG(VECTOR_A(p), VECTOR_A(b), SCALAR_A(r)), + ARG(VECTOR_B(p), VECTOR_B(b), SCALAR_B(r))); + + // Torus - exact + DEFINE_SDF_FUNCTION( + Torus, + ARG(VECTOR_A(p), VECTOR2_A(t)), + ARG(VECTOR_B(p), VECTOR2_B(t))); + + // Capped Torus - exact + DEFINE_SDF_FUNCTION( + CappedTorus, + ARG(VECTOR_A(p), VECTOR2_A(sc), SCALAR_A(ra), SCALAR_A(rb)), + ARG(VECTOR_B(p), VECTOR2_B(sc), SCALAR_B(ra), SCALAR_B(rb))); + + // Link - exact + DEFINE_SDF_FUNCTION( + Link, + ARG(VECTOR_A(p), SCALAR_A(le), SCALAR_A(r1), SCALAR_A(r2)), + ARG(VECTOR_B(p), SCALAR_B(le), SCALAR_B(r1), SCALAR_B(r2))); + + // Infinite Cylinder - exact + DEFINE_SDF_FUNCTION( + Cylinder, + ARG(VECTOR_A(p), VECTOR_A(c)), + ARG(VECTOR_B(p), VECTOR_B(c))); + + // Cone - exact + DEFINE_SDF_FUNCTION( + Cone, + ARG(VECTOR_A(p), VECTOR2_A(c), SCALAR_A(h)), + ARG(VECTOR_B(p), VECTOR2_B(c), SCALAR_B(h))); + + // Cone - bound(not exact!) + DEFINE_SDF_FUNCTION( + ConeFast, + ARG(VECTOR_A(p), VECTOR2_A(c), SCALAR_A(h)), + ARG(VECTOR_B(p), VECTOR2_B(c), SCALAR_B(h))); + + // Infinite Cone - exact + DEFINE_SDF_FUNCTION( + Cone, + ARG(VECTOR_A(p), VECTOR2_A(c)), + ARG(VECTOR_B(p), VECTOR2_B(c))); + + // Plane - exact + DEFINE_SDF_FUNCTION( + Plane, + ARG(VECTOR_A(p), VECTOR_A(n)), + ARG(VECTOR_B(p), VECTOR_B(n))); + + // Hexagonal Prism - exact + DEFINE_SDF_FUNCTION( + HexPrism, + ARG(VECTOR_A(p), VECTOR2_A(h)), + ARG(VECTOR_B(p), VECTOR2_B(h))); + + // Triangular Prism - bound + DEFINE_SDF_FUNCTION( + TriPrism, + ARG(VECTOR_A(p), VECTOR2_A(h)), + ARG(VECTOR_B(p), VECTOR2_B(h))); + + // Capsule / Line - exact + DEFINE_SDF_FUNCTION( + Capsule, + ARG(VECTOR_A(p), VECTOR_A(a), VECTOR_A(b), SCALAR_A(r)), + ARG(VECTOR_B(p), VECTOR_B(a), VECTOR_B(b), SCALAR_B(r))); + + // Capsule / Line - exact + DEFINE_SDF_FUNCTION( + VerticalCapsule, + ARG(VECTOR_A(p), SCALAR_A(h), SCALAR_A(r)), + ARG(VECTOR_B(p), SCALAR_B(h), SCALAR_B(r))); + + // Capped Cylinder - exact + DEFINE_SDF_FUNCTION( + CappedCylinder, + ARG(VECTOR_A(p), SCALAR_A(h), SCALAR_A(r)), + ARG(VECTOR_B(p), SCALAR_B(h), SCALAR_B(r))); + + // Capped Cylinder - exact + DEFINE_SDF_FUNCTION( + CappedCylinder, + ARG(VECTOR_A(p), VECTOR_A(a), VECTOR_A(b), SCALAR_A(r)), + ARG(VECTOR_B(p), VECTOR_B(a), VECTOR_B(b), SCALAR_B(r))); + + // Rounded Cylinder - exact + DEFINE_SDF_FUNCTION( + RoundedCylinder, + ARG(VECTOR_A(p), SCALAR_A(ra), SCALAR_A(rb), SCALAR_A(h)), + ARG(VECTOR_B(p), SCALAR_B(ra), SCALAR_B(rb), SCALAR_B(h))); + + // Capped Cone - exact + DEFINE_SDF_FUNCTION( + CappedCone, + ARG(VECTOR_A(p), SCALAR_A(h), SCALAR_A(r1), SCALAR_A(r2)), + ARG(VECTOR_B(p), SCALAR_B(h), SCALAR_B(r1), SCALAR_B(r2))); + + // Capped Cone - exact + DEFINE_SDF_FUNCTION( + CappedCone, + ARG(VECTOR_A(p), VECTOR_A(a), VECTOR_A(b), SCALAR_A(ra), SCALAR_A(rb)), + ARG(VECTOR_B(p), VECTOR_B(a), VECTOR_B(b), SCALAR_B(ra), SCALAR_B(rb))); + + // Solid Angle - exact + DEFINE_SDF_FUNCTION( + SolidAngle, + ARG(VECTOR_A(p), VECTOR2_A(c), SCALAR_A(ra)), + ARG(VECTOR_B(p), VECTOR2_B(c), SCALAR_B(ra))); + + // Round cone - exact + DEFINE_SDF_FUNCTION( + RoundCone, + ARG(VECTOR_A(p), SCALAR_A(r1), SCALAR_A(r2), SCALAR_A(h)), + ARG(VECTOR_B(p), SCALAR_B(r1), SCALAR_B(r2), SCALAR_B(h))); + + // Round Cone - exact + DEFINE_SDF_FUNCTION( + RoundCone, + ARG(VECTOR_A(p), VECTOR_A(a), VECTOR_A(b), SCALAR_A(r1), SCALAR_A(r2)), + ARG(VECTOR_B(p), VECTOR_B(a), VECTOR_B(b), SCALAR_B(r1), SCALAR_B(r2))); + + // Ellipsoid - bound(not exact!) + DEFINE_SDF_FUNCTION( + Ellipsoid, + ARG(VECTOR_A(p), VECTOR_A(r)), + ARG(VECTOR_B(p), VECTOR_B(r))); + + // Octahedron - exact + DEFINE_SDF_FUNCTION( + Octahedron, + ARG(VECTOR_A(p), SCALAR_A(s)), + ARG(VECTOR_B(p), SCALAR_B(s))); + + // Octahedron - bound(not exact) + DEFINE_SDF_FUNCTION( + OctahedronFast, + ARG(VECTOR_A(p), SCALAR_A(s)), + ARG(VECTOR_B(p), SCALAR_B(s))); + + // Pyramid - exact + DEFINE_SDF_FUNCTION( + Pyramid, + ARG(VECTOR_A(p), SCALAR_A(h)), + ARG(VECTOR_B(p), SCALAR_B(h))); + + ////////////////////////////////////////////////////////////////////////////// + + DEFINE_SDF_FUNCTION_EX( + SmoothUnion, + op, + ARG(SCALAR_A(d1), SCALAR_A(d2), SCALAR_A(k)), + ARG(SCALAR_B(d1), SCALAR_B(d2), SCALAR_B(k))); + + DEFINE_SDF_FUNCTION_EX( + SmoothSubtraction, + op, + ARG(SCALAR_A(d1), SCALAR_A(d2), SCALAR_A(k)), + ARG(SCALAR_B(d1), SCALAR_B(d2), SCALAR_B(k))); + + DEFINE_SDF_FUNCTION_EX( + SmoothIntersection, + op, + ARG(SCALAR_A(d1), SCALAR_A(d2), SCALAR_A(k)), + ARG(SCALAR_B(d1), SCALAR_B(d2), SCALAR_B(k))); + +#undef DEFINE_SDF_FUNCTION_EX +#undef DEFINE_SDF_FUNCTION + +#undef ARG + +#undef VECTOR_A +#undef VECTOR_B + +#undef VECTOR2_A +#undef VECTOR2_B + +#undef SCALAR_A +#undef SCALAR_B +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphEmptyRecorder.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphEmptyRecorder.h new file mode 100644 index 00000000..48ffb02d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphEmptyRecorder.h @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/VoxelNodeType.h" + +struct FVoxelContext; +struct FVoxelContextRange; + +class FVoxelComputeNode; +class FVoxelDataComputeNode; +class FVoxelExecComputeNode; + +class FVoxelGraphEmptyRecorder +{ +public: + struct FScope {}; + + FORCEINLINE FScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) + { + return {}; + } + FORCEINLINE FScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) + { + return {}; + } +}; + +class FVoxelGraphEmptyRangeRecorder +{ +public: + struct FScope {}; + + FORCEINLINE FScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) + { + return {}; + } + FORCEINLINE FScope MakeIfScope(FVoxelExecComputeNode* ExecNode) + { + return {}; + } + FORCEINLINE FScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) + { + return {}; + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphRangeAnalysisRecorder.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphRangeAnalysisRecorder.h new file mode 100644 index 00000000..3827d049 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphRangeAnalysisRecorder.h @@ -0,0 +1,143 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" + +class FVoxelGraphRangeAnalysisRecorder +{ +public: + struct FScope + { + FVoxelDataComputeNode& Node; + FVoxelNodeOutputBuffer Source; + TArray& Destination; + + FScope(FVoxelDataComputeNode& Node, FVoxelNodeOutputBuffer Source, TArray& Destination) + : Node(Node) + , Source(Source) + , Destination(Destination) + { + } + ~FScope() + { + Destination.Empty(Node.OutputCount); + Destination.SetNumZeroed(Node.OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node.OutputCount; OutputIndex++) + { + Destination[OutputIndex] = Source[OutputIndex]; + } + } + }; + struct FEmptyScope + { + }; + + FORCEINLINE FScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) + { + ensureVoxelSlowNoSideEffects(!NodesOutputs.Contains(Node)); + return FScope(*Node, Outputs, NodesOutputs.Add(Node)); + } + FORCEINLINE FEmptyScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) + { + return {}; + } + + const auto& GetNodesOutputs() const { return NodesOutputs; } + +private: + TMap> NodesOutputs; +}; + +class FVoxelGraphStatsRangeAnalysisRangeRecorder +{ +public: + struct FScope + { + FVoxelComputeNode& Node; + FVoxelGraphStatsRangeAnalysisRangeRecorder& Recorder; + + FScope(FVoxelComputeNode& Node, FVoxelGraphStatsRangeAnalysisRangeRecorder& Recorder) + : Node(Node) + , Recorder(Recorder) + { + } + ~FScope() + { + auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); + if (RangeFailStatus.HasFailed() || RangeFailStatus.HasWarning()) + { + auto* Message = RangeFailStatus.GetMessage(); + if (!ensure(Message)) return; + + FString PrettyMessage; + if (RangeFailStatus.HasFailed()) + { + PrettyMessage = FString::Printf(TEXT("range analysis failed:\n%s"), Message); + } + else + { + check(RangeFailStatus.HasWarning()); + PrettyMessage = FString::Printf(TEXT("range analysis warning:\n%s"), Message); + + // Clear the warning + FVoxelRangeFailStatus::Get().Reset(); + } + + Recorder.NodesRangeMessages.FindOrAdd(&Node).Add(PrettyMessage); + } + } + }; + + struct FDataScope : FScope + { + FVoxelNodeOutputRangeBuffer OutputBuffer; + + FDataScope(FVoxelDataComputeNode& Node, FVoxelGraphStatsRangeAnalysisRangeRecorder& Recorder, FVoxelNodeOutputRangeBuffer OutputBuffer) + : FScope(Node, Recorder) + , OutputBuffer(OutputBuffer) + { + } + ~FDataScope() + { + ensureVoxelSlowNoSideEffects(!Recorder.NodesOutputs.Contains(&Node)); + auto& Destination = Recorder.NodesOutputs.Add(&Node); + + Destination.Empty(Node.OutputCount); + Destination.SetNum(Node.OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node.OutputCount; OutputIndex++) + { + Destination[OutputIndex] = OutputBuffer[OutputIndex]; + } + } + }; + + struct FEmptyScope + { + }; + + FORCEINLINE FDataScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) + { + return FDataScope(*Node, *this, Outputs); + } + FORCEINLINE FEmptyScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) + { + return {}; + } + + FORCEINLINE FScope MakeIfScope(FVoxelExecComputeNode* ExecNode) + { + return FScope(*ExecNode, *this); + } + + const auto& GetNodesOutputs() const { return NodesOutputs; } + const auto& GetNodesRangeMessages() const { return NodesRangeMessages; } + +private: + TMap> NodesOutputs; + TMap> NodesRangeMessages; + + friend FScope; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphStatsRecorder.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphStatsRecorder.h new file mode 100644 index 00000000..c8633297 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/Recorders/VoxelGraphStatsRecorder.h @@ -0,0 +1,82 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelComputeNode.h" + +class FVoxelGraphStatsRecorderBase +{ +public: + static constexpr double Threshold = 1e-4; + + struct FNodeStats + { + double EstimatedCppTime = 0; + double VirtualMachineTime = 0; + + FNodeStats& operator+=(const FNodeStats& Other) + { + EstimatedCppTime += Other.EstimatedCppTime; + VirtualMachineTime += Other.VirtualMachineTime; + return *this; + } + }; + struct FScope + { + }; + + const auto& GetStats() const { return Stats; } + +protected: + TMap Stats; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelGraphStatsRecorder : public FVoxelGraphStatsRecorderBase +{ +public: + FORCEINLINE FScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) + { + auto& NodeStats = Stats.FindOrAdd(Node); + ensureVoxelSlowNoSideEffects(NodeStats.EstimatedCppTime == 0); + ensureVoxelSlowNoSideEffects(NodeStats.VirtualMachineTime == 0); + + NodeStats.EstimatedCppTime = Node->ComputeStats(Threshold, true, Inputs, Context); + NodeStats.VirtualMachineTime = Node->ComputeStats(Threshold, false, Inputs, Context); + + return {}; + } + FORCEINLINE FScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) + { + return {}; + } +}; + +class FVoxelGraphStatsRangeRecorder : public FVoxelGraphStatsRecorderBase +{ +public: + FORCEINLINE FScope MakeDataScope(FVoxelDataComputeNode* Node, FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) + { + auto& NodeStats = Stats.FindOrAdd(Node); + + ensureVoxelSlowNoSideEffects(NodeStats.EstimatedCppTime == 0); + NodeStats.EstimatedCppTime = Node->ComputeRangeStats(Threshold, true, Inputs, Context); + NodeStats.VirtualMachineTime = Node->ComputeRangeStats(Threshold, false, Inputs, Context); + + return {}; + } + FORCEINLINE FScope MakeSetterScope(FVoxelExecComputeNode* ExecNode, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) + { + return {}; + } + + FORCEINLINE FScope MakeIfScope(FVoxelExecComputeNode* ExecNode) + { + return {}; + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelCompiledGraphs.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelCompiledGraphs.h new file mode 100644 index 00000000..2994c633 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelCompiledGraphs.h @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelGraphOutputs.h" + +class FVoxelGraph; + +struct VOXELGRAPH_API FVoxelCompiledGraphs +{ + inline const TVoxelSharedPtr& GetFast(uint32 Hash) const + { + return FastAccess[Hash]; + } + inline const TVoxelSharedPtr& Get(FVoxelGraphPermutationArray Array) const + { + Array.Sort(); + return Graphs[Array]; + } + inline TVoxelSharedPtr& Add(FVoxelGraphPermutationArray Array) + { + Array.Sort(); + return Graphs.FindOrAdd(Array); + } + inline const auto& GetGraphsMap() const + { + return Graphs; + } + + void Compact(); + +private: + TMap> Graphs; + TMap> FastAccess; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNode.h new file mode 100644 index 00000000..eb28a60b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNode.h @@ -0,0 +1,340 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/VoxelNodeType.h" +#include "VoxelAxisDependencies.h" +#include "VoxelPinCategory.h" +#include "VoxelGraphGlobals.h" + +class UVoxelNode; +class UVoxelGraphGenerator; +class FVoxelCppConstructor; +class FVoxelCppConfig; +class FVoxelCompilationNode; +class FVoxelGraphFunction; +class FVoxelVariable; +struct FVoxelContext; +struct FVoxelGeneratorInit; +struct FVoxelContextRange; +struct FVoxelGraphVMOutputBuffers; +struct FVoxelGraphVMRangeOutputBuffers; + +enum class EVoxelVariableType : uint8 +{ + Constant, + Init, + Compute +}; + +struct FVoxelVariableAccessInfo +{ +public: + inline bool IsInit() const + { + return bIsInit; + } + inline bool IsConstant() const + { + return bIsConstant; + } + inline bool IsStructDeclaration() const + { + return bIsStructDeclaration; + } + inline EVoxelAxisDependencies GetStructDeclarationDependencies() const + { + check(bIsStructDeclaration); + return StructDependencies; + } + inline bool IsFunctionParameter() const + { + return bIsFunctionParameter; + } + + inline EVoxelFunctionAxisDependencies GetFunctionDependencies() const + { + check(FunctionDependencies != EVoxelFunctionAxisDependencies(-1)); + return FunctionDependencies; + } + +public: + inline static FVoxelVariableAccessInfo Init() + { + FVoxelVariableAccessInfo Info; + Info.bIsInit = true; + return Info; + } + inline static FVoxelVariableAccessInfo Constant() + { + FVoxelVariableAccessInfo Info; + Info.bIsConstant = true; + return Info; + } + inline static FVoxelVariableAccessInfo Compute(EVoxelFunctionAxisDependencies FunctionDependencies) + { + FVoxelVariableAccessInfo Info; + Info.FunctionDependencies = FunctionDependencies; + return Info; + } + inline static FVoxelVariableAccessInfo StructDeclaration(EVoxelAxisDependencies StructDependencies) + { + FVoxelVariableAccessInfo Info; + Info.bIsStructDeclaration = true; + Info.StructDependencies = StructDependencies; + return Info; + } + inline static FVoxelVariableAccessInfo FunctionParameter() + { + FVoxelVariableAccessInfo Info; + Info.bIsFunctionParameter = true; + return Info; + } + +private: + bool bIsInit = false; + bool bIsConstant = false; + bool bIsStructDeclaration = false; + bool bIsFunctionParameter = false; + EVoxelFunctionAxisDependencies FunctionDependencies = EVoxelFunctionAxisDependencies(-1); + + // Only valid when used to declare structs + EVoxelAxisDependencies StructDependencies = EVoxelAxisDependencies(-1); + + FVoxelVariableAccessInfo() = default; +}; + +enum class EVoxelComputeNodeType : uint8 +{ + Data, + Seed, + Exec +}; + +class VOXELGRAPH_API FVoxelComputeNode +{ +public: + const EVoxelComputeNodeType Type; + const int32 InputCount; + const int32 OutputCount; + const TArray> InputIds; + const TArray> OutputIds; + const TArray CacheOutputs; + +public: + const FString PrettyName; + const FName UniqueName; + const TWeakObjectPtr Node; + // First are deeper in the callstack + const TArray> SourceNodes; + const EVoxelAxisDependencies Dependencies; + + virtual ~FVoxelComputeNode() = default; + +public: + UVoxelGraphGenerator* GetGraph() const; + + inline FString GetDefaultValueString(int32 Index) const { return DefaultValueStrings[Index]; } + + FORCEINLINE int32 GetInputId(int32 Index) const { checkVoxelGraph(0 <= Index && Index < InputCount); return InputIds.GetData()[Index]; } + FORCEINLINE int32 GetOutputId(int32 Index) const { checkVoxelGraph(0 <= Index && Index < OutputCount); return OutputIds.GetData()[Index]; } + + FORCEINLINE bool IsOutputUsed(int32 Index) const { return GetOutputId(Index) != -1; } + + EVoxelPinCategory GetInputCategory(int32 Index) const { return InputsCategories[Index]; } + EVoxelPinCategory GetOutputCategory(int32 Index) const { return OutputsCategories[Index]; } + + void DeclareOutput(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, int32 OutputIndex) const; + void DeclareOutputs(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const; + + TArray GetInputsNamesCpp(FVoxelCppConstructor& Constructor) const { return GetInputsNamesCppImpl(Constructor, false); } + TArray GetOutputsNamesCpp(FVoxelCppConstructor& Constructor) const; + + TArray GetSeedInputsNamesCpp(FVoxelCppConstructor& Constructor) const { return GetInputsNamesCppImpl(Constructor, true); } + +public: + template + T GetDefaultValue(int32 Index) const; + template + void CopyVariablesToInputs(T* RESTRICT Variables, T* RESTRICT InputBuffer) const; + template + void CopyOutputsToVariables(T* RESTRICT OutputBuffer, T* RESTRICT Variables) const; + +public: + void CallSetupCpp(FVoxelCppConfig& Config) { check(!bIsConstructorSetup); bIsConstructorSetup = true; SetupCpp(Config); } + +protected: + FVoxelComputeNode(EVoxelComputeNodeType Type, const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode); + + //~ Begin FVoxelComputeNode Interface +public: + virtual void GetPrivateVariables(TArray& PrivateVariables) const; +protected: + virtual void SetupCpp(FVoxelCppConfig& Config) const {} + //~ End FVoxelComputeNode Interface + +private: + TArray DefaultValues; + TArray DefaultRangeValues; + TArray DefaultValueStrings; + TArray InputsCategories; + TArray OutputsCategories; + + bool bIsConstructorSetup = false; + + TArray GetInputsNamesCppImpl(FVoxelCppConstructor& Constructor, bool bOnlySeeds) const; + FString GetUnusedOutputName(int32 OutputIndex) const; +}; + +template<> +inline FVoxelGraphSeed FVoxelComputeNode::GetDefaultValue(int32 Index) const { return DefaultValues[Index].Get(); } + +template<> +inline FVoxelNodeType FVoxelComputeNode::GetDefaultValue(int32 Index) const { checkVoxelGraph(DefaultValues.IsValidIndex(Index)); return DefaultValues.GetData()[Index]; } + +template<> +inline FVoxelNodeRangeType FVoxelComputeNode::GetDefaultValue(int32 Index) const { return DefaultRangeValues[Index]; } + +template +inline void FVoxelComputeNode::CopyVariablesToInputs(T* RESTRICT Variables, T* RESTRICT InputBuffer) const +{ + for (int32 InputIndex = 0; InputIndex < InputCount; InputIndex++) + { + int32 Id = GetInputId(InputIndex); + if (Id == -1) + { + checkVoxelGraph(0 <= InputIndex && InputIndex < MAX_VOXELNODE_PINS); + InputBuffer[InputIndex] = GetDefaultValue(InputIndex); + } + else + { + checkVoxelGraph(0 <= InputIndex && InputIndex < MAX_VOXELNODE_PINS); + InputBuffer[InputIndex] = Variables[Id]; + } + } +} + +template +inline void FVoxelComputeNode::CopyOutputsToVariables(T* RESTRICT OutputBuffer, T* RESTRICT Variables) const +{ + for (int32 OutputIndex = 0; OutputIndex < OutputCount; OutputIndex++) + { + int32 Id = GetOutputId(OutputIndex); + if (Id != -1) // Can be -1 if unused + { + checkVoxelGraph(0 <= OutputIndex && OutputIndex < MAX_VOXELNODE_PINS); + Variables[Id] = OutputBuffer[OutputIndex]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXELGRAPH_API FVoxelDataComputeNode : public FVoxelComputeNode +{ +public: + FVoxelDataComputeNode(const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelComputeNode(EVoxelComputeNodeType::Data, Node, CompilationNode) + { + } + + using FComputeFunction = void(FVoxelDataComputeNode::*)(FVoxelNodeInputBuffer, FVoxelNodeOutputBuffer, const FVoxelContext&) const; + FComputeFunction ComputeFunctionPtr = nullptr; + + //~ Begin FVoxelDataComputeNode Interface + virtual void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) {} + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const {} + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const = 0; + virtual void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const = 0; + virtual void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const = 0; + virtual void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const = 0; + + virtual double ComputeStats(double Threshold, bool bSimulateCpp, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) const = 0; + virtual double ComputeRangeStats(double Threshold, bool bSimulateCpp, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) const = 0; + + virtual void CacheFunctionPtr() = 0; + //~ End FVoxelDataComputeNode Interface + + void CallInitCpp (FVoxelCppConstructor& Constructor); + void CallComputeCpp (FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const; + void CallComputeRangeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo) const; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class VOXELGRAPH_API FVoxelSeedComputeNode : public FVoxelComputeNode +{ +public: + FVoxelSeedComputeNode(const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelComputeNode(EVoxelComputeNodeType::Seed, Node, CompilationNode) + { + } + + //~ Begin FVoxelSeedComputeNode Interface + virtual void Init(FVoxelGraphSeed Inputs[], FVoxelGraphSeed Outputs[], const FVoxelGeneratorInit& InitStruct) {}; + virtual void InitCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const {} + //~ End FVoxelSeedComputeNode Interface + + void CallInitCpp(FVoxelCppConstructor& Constructor); +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum class EVoxelComputeNodeExecType : uint8 +{ + Passthrough, + If, + Setter, + FunctionCall, + FunctionInit +}; + +class VOXELGRAPH_API FVoxelExecComputeNode : public FVoxelComputeNode +{ +public: + const EVoxelComputeNodeExecType ExecType; + + FVoxelExecComputeNode(EVoxelComputeNodeExecType ExecType, const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelComputeNode(EVoxelComputeNodeType::Exec, Node, CompilationNode) + , ExecType(ExecType) + { + } + + //~ Begin FVoxelExecComputeNode Interface + // Used only for materials node to check that the right config is used + virtual void Init(const FVoxelGeneratorInit& InitStruct) {}; + //~ End FVoxelExecComputeNode Interface +}; + +template +class TVoxelExecComputeNodeHelper : public FVoxelExecComputeNode +{ +public: + TVoxelExecComputeNodeHelper(const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelExecComputeNode(T, Node, CompilationNode) + { + } +}; + +using FVoxelPassthroughComputeNode = TVoxelExecComputeNodeHelper; + +class VOXELGRAPH_API FVoxelSetterComputeNode : public TVoxelExecComputeNodeHelper +{ +public: + using TVoxelExecComputeNodeHelper::TVoxelExecComputeNodeHelper; + + //~ Begin FVoxelSetterComputeNode Interface + virtual void ComputeSetterNode(FVoxelNodeInputBuffer Inputs, FVoxelGraphVMOutputBuffers& GraphOutputs) const = 0; + virtual void ComputeRangeSetterNode(FVoxelNodeInputRangeBuffer Inputs, FVoxelGraphVMRangeOutputBuffers& GraphOutputs) const = 0; + virtual void ComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const = 0; + virtual void ComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const TArray& Inputs, const TArray& GraphOutputs) const = 0; + //~ End FVoxelSetterComputeNode Interface + + void CallComputeSetterNodeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) const; + void CallComputeRangeSetterNodeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs) const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.h new file mode 100644 index 00000000..f31689c4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.h @@ -0,0 +1,65 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +struct FVoxelNodeType; +class FVoxelCppConstructor; +struct FVoxelGeneratorInit; +struct FVoxelContext; +struct FVoxelContextRange; +class FVoxelComputeNode; +class FVoxelGraphFunction; +class FVoxelDataComputeNode; +class FVoxelSeedComputeNode; +class FVoxelExecComputeNode; +class UVoxelNode; +struct FVoxelGraphVMInitBuffers; +struct FVoxelGraphVMComputeBuffers; +struct FVoxelGraphVMComputeRangeBuffers; +struct FVoxelVariableAccessInfo; + +/** + * Tree of the compute nodes. Used to interpret the graph at runtime and to compile it + */ +class FVoxelComputeNodeTree +{ +public: + FVoxelComputeNodeTree() = default; + + // Runtime + void Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const; + + template + const FVoxelGraphFunction* Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, T& Recorder) const; + template + const FVoxelGraphFunction* ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const; + + // Compilation + void GetNodes(TSet& Nodes) const; + + void InitCpp(FVoxelCppConstructor& Constructor) const; + void ComputeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs); + void ComputeRangeCpp(FVoxelCppConstructor& Constructor, const FVoxelVariableAccessInfo& VariableInfo, const TArray& GraphOutputs); + + const TArray& GetDataNodes() const { return DataNodes; } + const TArray& GetSeedNodes() const { return SeedNodes; } + const FVoxelExecComputeNode* GetExecNode() const { return ExecNode; } + const TArray& GetChildren() const { return Children; } + +private: + TArray DataNodes; + TArray> DataNodesRefs; + + TArray SeedNodes; + TArray> SeedNodesRefs; + + FVoxelExecComputeNode* ExecNode = nullptr; + TVoxelSharedPtr ExecNodeRef; + + TArray Children; + + friend class FVoxelCompilationNodeTree; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.inl b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.inl new file mode 100644 index 00000000..ab9bbe59 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelComputeNodeTree.inl @@ -0,0 +1,195 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "Runtime/VoxelComputeNodeTree.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/VoxelDefaultComputeNodes.h" +#include "VoxelNodes/VoxelIfNode.h" + +template +const FVoxelGraphFunction* FVoxelComputeNodeTree::Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, T& Recorder) const +{ + for (int32 Index = 0; Index < DataNodes.Num(); Index++) + { + FVoxelNodeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelNodeType NodeOutputBuffer[MAX_VOXELNODE_PINS]; + + auto* Node = DataNodes.GetData()[Index]; + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + { + auto Scope = Recorder.MakeDataScope(Node, NodeInputBuffer, NodeOutputBuffer, Context); + (Node->*(Node->ComputeFunctionPtr))(NodeInputBuffer, NodeOutputBuffer, Context); + } + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + } + + if (!ExecNode) + { + return nullptr; + } + + switch (ExecNode->ExecType) + { + case EVoxelComputeNodeExecType::FunctionInit: + case EVoxelComputeNodeExecType::Passthrough: + { + if (Children.Num() > 0) + { + checkVoxelGraph(Children.Num() == 1); + return Children.GetData()[0].Compute(Context, Buffers, Recorder); + } + else + { + return nullptr; + } + } + case EVoxelComputeNodeExecType::If: + { + checkVoxelGraph(Children.Num() == 2); + const int32 InputId = ExecNode->GetInputId(0); + const bool bCondition = InputId == -1 ? ExecNode->GetDefaultValue(0).Get() : Buffers.Variables[InputId].Get(); + return Children.GetData()[bCondition ? 0 : 1].Compute(Context, Buffers, Recorder); + } + case EVoxelComputeNodeExecType::Setter: + { + FVoxelNodeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + ExecNode->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + + { + auto Scope = Recorder.MakeSetterScope(ExecNode, NodeInputBuffer, Context); + static_cast(ExecNode)->ComputeSetterNode(NodeInputBuffer, Buffers.GraphOutputs); + } + + if (Children.Num() > 0) + { + checkVoxelGraph(Children.Num() == 1); + return Children.GetData()[0].Compute(Context, Buffers, Recorder); + } + else + { + return nullptr; + } + } + case EVoxelComputeNodeExecType::FunctionCall: + { + checkVoxelGraph(Children.Num() == 0); + ExecNode->CopyVariablesToInputs(Buffers.Variables, Buffers.FunctionInputsOutputs); + return static_cast(ExecNode)->GetFunction(); + } + default: + { + checkVoxelSlow(false); + return nullptr; + } + } +} + +template +const FVoxelGraphFunction* FVoxelComputeNodeTree::ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const +{ + auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); + for (auto& Node : DataNodes) + { + ensureVoxelSlowNoSideEffects(!RangeFailStatus.HasFailed()); + + FVoxelNodeRangeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + FVoxelNodeRangeType NodeOutputBuffer[MAX_VOXELNODE_PINS]; + + Node->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + { + auto Scope = Recorder.MakeDataScope(Node, NodeInputBuffer, NodeOutputBuffer, Context); + Node->ComputeRange(NodeInputBuffer, NodeOutputBuffer, Context); + } + Node->CopyOutputsToVariables(NodeOutputBuffer, Buffers.Variables); + + if (RangeFailStatus.HasFailed()) + { + return nullptr; + } + } + + if (!ExecNode) + { + return nullptr; + } + + switch (ExecNode->ExecType) + { + case EVoxelComputeNodeExecType::FunctionInit: + case EVoxelComputeNodeExecType::Passthrough: + { + if (Children.Num() > 0) + { + return Children[0].ComputeRange(Context, Buffers, Recorder); + } + else + { + return nullptr; + } + } + case EVoxelComputeNodeExecType::If: + { + checkVoxelSlow(Children.Num() == 2); + const int32 InputId = ExecNode->GetInputId(0); + + bool bCondition; + { + auto Scope = Recorder.MakeIfScope(ExecNode); + + bCondition = + InputId == -1 + ? bool(ExecNode->GetDefaultValue(0).Get()) + : bool(Buffers.Variables[InputId].Get()); + + if (RangeFailStatus.HasFailed()) // The bool is unknown + { + auto* IfNode = static_cast(ExecNode); + if (IfNode->BranchToUseForRangeAnalysis != EVoxelNodeIfBranchToUseForRangeAnalysis::None) + { + bCondition = IfNode->BranchToUseForRangeAnalysis == EVoxelNodeIfBranchToUseForRangeAnalysis::UseTrue; + checkVoxelSlow(bCondition || IfNode->BranchToUseForRangeAnalysis == EVoxelNodeIfBranchToUseForRangeAnalysis::UseFalse); + + RangeFailStatus.Reset(); + } + else + { + return nullptr; + } + } + } + + return Children[bCondition ? 0 : 1].ComputeRange(Context, Buffers, Recorder); + } + case EVoxelComputeNodeExecType::Setter: + { + FVoxelNodeRangeType NodeInputBuffer[MAX_VOXELNODE_PINS]; + ExecNode->CopyVariablesToInputs(Buffers.Variables, NodeInputBuffer); + + { + auto Scope = Recorder.MakeSetterScope(ExecNode, NodeInputBuffer, Context); + static_cast(ExecNode)->ComputeRangeSetterNode(NodeInputBuffer, Buffers.GraphOutputs); + } + + if (Children.Num() > 0) + { + return Children[0].ComputeRange(Context, Buffers, Recorder); + } + else + { + return nullptr; + } + } + case EVoxelComputeNodeExecType::FunctionCall: + { + checkVoxelSlow(Children.Num() == 0); + ExecNode->CopyVariablesToInputs(Buffers.Variables, Buffers.FunctionInputsOutputs); + return static_cast(ExecNode)->GetFunction(); + } + default: + { + check(false); + return nullptr; + } + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelDefaultComputeNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelDefaultComputeNodes.h new file mode 100644 index 00000000..f1da651c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelDefaultComputeNodes.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/VoxelComputeNode.h" +#include "Runtime/VoxelGraph.h" + +using FVoxelFunctionInitComputeNode = TVoxelExecComputeNodeHelper; + +class FVoxelFunctionCallComputeNode final : public FVoxelExecComputeNode +{ +public: + const int32 FunctionId; + const EVoxelFunctionAxisDependencies FunctionDependencies; + + FVoxelFunctionCallComputeNode(int32 FunctionId, EVoxelFunctionAxisDependencies FunctionDependencies, const UVoxelNode& Node, const FVoxelCompilationNode& CompilationNode) + : FVoxelExecComputeNode(EVoxelComputeNodeExecType::FunctionCall, Node, CompilationNode) + , FunctionId(FunctionId) + , FunctionDependencies(FunctionDependencies) + { + check(FunctionId >= 0); + } + + inline void SetFunctions(const FVoxelGraphFunctions& InFunctions) + { + check(!Functions.IsValid() && InFunctions.IsValid()); + Functions = InFunctions; + Function = &Functions.Get(FunctionDependencies); + } + inline const FVoxelGraphFunction* GetFunction() const { return Function; } + +private: + FVoxelGraphFunctions Functions; + const FVoxelGraphFunction* Function = nullptr; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.h new file mode 100644 index 00000000..f5263597 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.h @@ -0,0 +1,119 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelAxisDependencies.h" + +class FVoxelGraphFunction; +struct FVoxelNodeType; +struct FVoxelGeneratorInit; +struct FVoxelContext; +struct FVoxelContextRange; +class FVoxelCppConstructor; +struct FVoxelGraphVMInitBuffers; +struct FVoxelGraphVMComputeBuffers; +struct FVoxelGraphVMComputeRangeBuffers; +class FVoxelDataComputeNode; +class FVoxelSeedComputeNode; +class FVoxelComputeNode; +class FVoxelVariable; + +struct FVoxelGraphFunctions +{ + int32 FunctionId = -1; + TVoxelSharedPtr FunctionX; + TVoxelSharedPtr FunctionXYWithCache; + TVoxelSharedPtr FunctionXYWithoutCache; + TVoxelSharedPtr FunctionXYZWithCache; + TVoxelSharedPtr FunctionXYZWithoutCache; + + FVoxelGraphFunctions() = default; + FVoxelGraphFunctions( + int32 FunctionId, + const TVoxelSharedPtr& FunctionX, + const TVoxelSharedPtr& FunctionXYWithCache, + const TVoxelSharedPtr& FunctionXYWithoutCache, + const TVoxelSharedPtr& FunctionXYZWithCache, + const TVoxelSharedPtr& FunctionXYZWithoutCache); + + const FVoxelGraphFunction& Get(EVoxelFunctionAxisDependencies Dependencies) const + { + switch (Dependencies) + { + case EVoxelFunctionAxisDependencies::X: + return *FunctionX.Get(); + case EVoxelFunctionAxisDependencies::XYWithCache: + return *FunctionXYWithCache.Get(); + case EVoxelFunctionAxisDependencies::XYWithoutCache: + return *FunctionXYWithoutCache.Get(); + case EVoxelFunctionAxisDependencies::XYZWithCache: + return *FunctionXYZWithCache.Get(); + case EVoxelFunctionAxisDependencies::XYZWithoutCache: + return *FunctionXYZWithoutCache.Get(); + default: + check(false); + return *FunctionX.Get(); + } + } + bool IsValid() const + { + return FunctionX.IsValid(); + } + TVoxelStaticArray Iterate() const + { + return + { + FunctionX.Get(), + FunctionXYWithCache.Get(), + FunctionXYWithoutCache.Get(), + FunctionXYZWithCache.Get(), + FunctionXYZWithoutCache.Get(), + }; + } +}; + +class VOXELGRAPH_API FVoxelGraph : public TVoxelSharedFromThis +{ +public: + FVoxelGraph( + const FString& Name, + const TArray& Functions, + const FVoxelGraphFunctions& FirstFunctions, + const TArray>& ConstantComputeNodes, + const TArray>& SeedComputeNodes, + int32 VariablesBufferSize); + + const FString Name; + const TArray AllFunctions; + const FVoxelGraphFunctions FirstFunctions; + const TArray> ConstantComputeNodes; + const TArray> SeedComputeNodes; + const int32 VariablesBufferSize = 0; + +public: + void Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const; + + void ComputeConstants(FVoxelGraphVMComputeBuffers& Buffers) const; + template + void Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, EVoxelFunctionAxisDependencies Dependencies, T& Recorder) const; + + void ComputeRangeConstants(FVoxelGraphVMComputeRangeBuffers& Buffers) const; + template + void ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const; + +public: + void GetSeedNodes(TSet& Nodes) const; + void GetConstantNodes(TSet& Nodes) const; + void GetNotConstantNodes(TSet& Nodes) const; + void GetAllNodes(TSet& Nodes) const; + + void Init(FVoxelCppConstructor& Constructor) const; + void ComputeConstants(FVoxelCppConstructor& Constructor) const; + void Compute(FVoxelCppConstructor& Constructor, EVoxelFunctionAxisDependencies Dependencies) const; + + void DeclareInitFunctions(FVoxelCppConstructor& Constructor) const; + void DeclareComputeFunctions(FVoxelCppConstructor& Constructor, const TArray& GraphOutputs) const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.inl b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.inl new file mode 100644 index 00000000..9441626b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraph.inl @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "Runtime/VoxelGraph.h" +#include "Runtime/VoxelGraphFunction.inl" + +template +void FVoxelGraph::Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, EVoxelFunctionAxisDependencies Dependencies, T& Recorder) const +{ + FirstFunctions.Get(Dependencies).Compute(Context, Buffers, Recorder); +} + +template +void FVoxelGraph::ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const +{ + ensure(!FVoxelRangeFailStatus::Get().HasFailed()); + FirstFunctions.Get(EVoxelFunctionAxisDependencies::XYZWithoutCache).ComputeRange(Context, Buffers, Recorder); +} + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.h new file mode 100644 index 00000000..4a4655ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.h @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAxisDependencies.h" +#include "VoxelMinimal.h" + +class FVoxelComputeNode; +class FVoxelComputeNodeTree; +class FVoxelCppConstructor; + +struct FVoxelGeneratorInit; + +struct FVoxelGraphVMInitBuffers; +struct FVoxelGraphVMComputeBuffers; +struct FVoxelGraphVMComputeRangeBuffers; + +struct FVoxelContext; +struct FVoxelContextRange; + +enum class EVoxelFunctionType : uint8 +{ + Init, + Compute +}; + +struct FVoxelGraphFunctionInfo +{ + const int32 FunctionId; + const EVoxelFunctionType FunctionType; + const EVoxelFunctionAxisDependencies Dependencies; + + FVoxelGraphFunctionInfo(int32 FunctionId, EVoxelFunctionType FunctionType, EVoxelFunctionAxisDependencies Dependencies) + : FunctionId(FunctionId) + , FunctionType(FunctionType) + , Dependencies(Dependencies) + { + } + + FString GetFunctionName() const; +}; + +class FVoxelGraphFunction +{ +public: + const int32 FunctionId; + const EVoxelFunctionAxisDependencies Dependencies; + + FVoxelGraphFunction( + const TVoxelSharedRef& Tree, + const TVoxelSharedRef& FunctionInit, + int32 FunctionId, + EVoxelFunctionAxisDependencies Dependencies); + + void Call(FVoxelCppConstructor& Constructor, const TArray& Args, EVoxelFunctionType FunctionType) const; + + bool IsUsedForInit() const; + bool IsUsedForCompute(FVoxelCppConstructor& Constructor) const; + + inline const FVoxelComputeNodeTree& GetTree() const + { + return *Tree; + } + +public: + void Init(const FVoxelGeneratorInit& InitStruct, FVoxelGraphVMInitBuffers& Buffers) const; + + template + void Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, T& Recorder) const; + template + void ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const; + +public: + void GetNodes(TSet& Nodes) const; + + void DeclareInitFunction(FVoxelCppConstructor& Constructor) const; + void DeclareComputeFunction(FVoxelCppConstructor& Constructor, const TArray& GraphOutputs) const; + +private: + const TVoxelSharedRef Tree; + const TVoxelSharedRef FunctionInit; + + void DeclareFunction(FVoxelCppConstructor& Constructor, EVoxelFunctionType Type) const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.inl b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.inl new file mode 100644 index 00000000..97bc2123 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphFunction.inl @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "Runtime/VoxelGraphFunction.h" +#include "Runtime/VoxelComputeNodeTree.inl" + +template +void FVoxelGraphFunction::Compute(const FVoxelContext& Context, FVoxelGraphVMComputeBuffers& Buffers, T& Recorder) const +{ + FunctionInit->CopyOutputsToVariables(Buffers.FunctionInputsOutputs, Buffers.Variables); + const FVoxelGraphFunction* NextFunction = Tree->Compute(Context, Buffers, Recorder); + if (NextFunction) + { + NextFunction->Compute(Context, Buffers, Recorder); + } +} + +template +void FVoxelGraphFunction::ComputeRange(const FVoxelContextRange& Context, FVoxelGraphVMComputeRangeBuffers& Buffers, T& Recorder) const +{ + FunctionInit->CopyOutputsToVariables(Buffers.FunctionInputsOutputs, Buffers.Variables); + const FVoxelGraphFunction* NextFunction = Tree->ComputeRange(Context, Buffers, Recorder); + if (NextFunction && !FVoxelRangeFailStatus::Get().HasFailed()) + { + NextFunction->ComputeRange(Context, Buffers, Recorder); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphGeneratorInstance.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphGeneratorInstance.h new file mode 100644 index 00000000..128a28fd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphGeneratorInstance.h @@ -0,0 +1,179 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAxisDependencies.h" +#include "VoxelGraphGeneratorHelpers.h" +#include "Runtime/VoxelNodeType.h" +#include "Runtime/VoxelGraphVMUtils.h" +#include "Runtime/VoxelCompiledGraphs.h" + +class UVoxelGraphGenerator; + +class VOXELGRAPH_API FVoxelGraphGeneratorInstance : public TVoxelGraphGeneratorInstanceHelper +{ +public: + class FTarget + { + public: + const FVoxelGraph& Graph; + mutable FVoxelGraphVMComputeBuffers Buffers; + + struct FBufferX {}; + struct FBufferXY {}; + struct FOutput + { + FVoxelGraphVMOutputBuffers* const GraphOutputs; + + void Init(const FVoxelGraphOutputsInit& Init) + { + GraphOutputs->MaterialBuilder.Clear(); + GraphOutputs->MaterialBuilder.SetMaterialConfig(Init.MaterialConfig); + } + + template + T Get() const + { + return GraphOutputs->Buffer[Index].Get(); + } + template + void Set(T Value) + { + GraphOutputs->Buffer[Index].Get() = Value; + } + }; + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutput GetOutputs() const { return { &Buffers.GraphOutputs }; } + + inline void ComputeX(const FVoxelContext& Context, FBufferX& BufferX) const + { + Compute(EVoxelFunctionAxisDependencies::X, Context); + } + inline void ComputeXYWithCache(const FVoxelContext& Context, const FBufferX& BufferX, FBufferXY& BufferXY) const + { + Compute(EVoxelFunctionAxisDependencies::XYWithCache, Context); + } + inline void ComputeXYZWithCache(const FVoxelContext& Context, const FBufferX& BufferX, const FBufferXY& BufferXY, FOutput& Outputs) const + { + Compute(EVoxelFunctionAxisDependencies::XYZWithCache, Context); + } + inline void ComputeXYZWithoutCache(const FVoxelContext& Context, FOutput& Outputs) const + { + Compute(EVoxelFunctionAxisDependencies::XYZWithoutCache, Context); + } + + private: + VOXELGRAPH_API void Compute(EVoxelFunctionAxisDependencies Dependencies, const FVoxelContext& Context) const; + }; + + class FRangeTarget + { + public: + const FVoxelGraph& Graph; + mutable FVoxelGraphVMComputeRangeBuffers Buffers; + + struct FBufferX {}; + struct FBufferXY {}; + struct FOutput + { + FVoxelGraphVMRangeOutputBuffers* const GraphOutputs; + + void Init(const FVoxelGraphOutputsInit& Init) + { + } + + template + TVoxelRange Get() const + { + return GraphOutputs->Buffer[Index].Get(); + } + template + void Set(TVoxelRange Value) + { + GraphOutputs->Buffer[Index].Get() = Value; + } + }; + + inline FBufferX GetBufferX() const { return {}; } + inline FBufferXY GetBufferXY() const { return {}; } + inline FOutput GetOutputs() const { return { &Buffers.GraphOutputs }; } + + VOXELGRAPH_API void ComputeXYZWithoutCache(const FVoxelContextRange& Context, FOutput& Outputs) const; + }; + +public: + FVoxelGraphGeneratorInstance( + const TVoxelSharedRef& Graphs, + UVoxelGraphGenerator& Generator, + const TMap& FloatOutputs, + const TMap& Int32Outputs, + const TMap& ColorOutputs); + ~FVoxelGraphGeneratorInstance(); + + //~ Begin TVoxelGraphGeneratorInstanceHelper Interface + void InitGraph(const FVoxelGeneratorInit& InitStruct) override final; + + template + inline auto GetTarget() const + { + static_assert(FVoxelGraphPermutation::IsSorted(), ""); + static_assert(sizeof...(InPermutation) == 1 || + !FVoxelGraphPermutation::Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex), ""); + + auto& Graph = Graphs->GetFast(FVoxelGraphPermutation::Hash()); + return FTarget{ *Graph, FVoxelGraphVMComputeBuffers(GetVariablesBuffer(Graph)) }; + } + template + inline auto GetRangeTarget() const + { + static_assert(FVoxelGraphPermutation::IsSorted(), ""); + static_assert(FVoxelGraphPermutation::Contains(FVoxelGraphOutputsIndices::RangeAnalysisIndex), ""); + + auto& Graph = Graphs->GetFast(FVoxelGraphPermutation::Hash()); + return FRangeTarget{ *Graph, FVoxelGraphVMComputeRangeBuffers(GetRangeVariablesBuffer(Graph)) }; + } + //~ End TVoxelGraphGeneratorInstanceHelper Interface + + inline UVoxelGraphGenerator* GetOwner() const + { + return Generator.Get(); + } + +public: + template + TVoxelSharedPtr GetGraph() const + { + static_assert(FVoxelGraphPermutation::IsSorted(), ""); + return Graphs->GetFast(FVoxelGraphPermutation::Hash()); + } + + FVoxelNodeType* GetVariablesBuffer(const TVoxelWeakPtr& Graph) const; + FVoxelNodeRangeType* GetRangeVariablesBuffer(const TVoxelWeakPtr& Graph) const; + +private: + const TWeakObjectPtr Generator; + const TVoxelSharedRef Graphs; + + TMap, TArray> Variables; + TMap, TArray> RangeVariables; + + struct FThreadVariables + : TThreadSingleton + , TMap, TArray> + { + }; + struct FThreadRangeVariables + : TThreadSingleton + , TMap, TArray> + { + }; +}; + +template<> +inline FVoxelMaterial FVoxelGraphGeneratorInstance::FTarget::FOutput::Get() const +{ + return GraphOutputs->MaterialBuilder.Build(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphVMUtils.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphVMUtils.h new file mode 100644 index 00000000..bf134810 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelGraphVMUtils.h @@ -0,0 +1,54 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelGraphGlobals.h" +#include "VoxelMaterialBuilder.h" +#include "Runtime/VoxelNodeType.h" + +struct FVoxelGraphVMInitBuffers +{ + FVoxelGraphSeed* RESTRICT const Variables; + + explicit FVoxelGraphVMInitBuffers(FVoxelGraphSeed* RESTRICT Variables) + : Variables(Variables) + { + } +}; + +struct FVoxelGraphVMOutputBuffers +{ + FVoxelNodeType Buffer[MAX_VOXELGRAPH_OUTPUTS]; + FVoxelMaterialBuilder MaterialBuilder; +}; + +struct FVoxelGraphVMComputeBuffers +{ + FVoxelNodeType* RESTRICT const Variables; + FVoxelNodeType FunctionInputsOutputs[MAX_VOXELFUNCTION_ARGS]; + FVoxelGraphVMOutputBuffers GraphOutputs; + + explicit FVoxelGraphVMComputeBuffers(FVoxelNodeType* RESTRICT Variables) + : Variables(Variables) + { + } +}; + +struct FVoxelGraphVMRangeOutputBuffers +{ + FVoxelNodeRangeType Buffer[MAX_VOXELGRAPH_OUTPUTS]; +}; + +struct FVoxelGraphVMComputeRangeBuffers +{ + FVoxelNodeRangeType* RESTRICT const Variables; + FVoxelNodeRangeType FunctionInputsOutputs[MAX_VOXELFUNCTION_ARGS]; + FVoxelGraphVMRangeOutputBuffers GraphOutputs; + + explicit FVoxelGraphVMComputeRangeBuffers(FVoxelNodeRangeType* RESTRICT Variables) + : Variables(Variables) + { + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelNodeType.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelNodeType.h new file mode 100644 index 00000000..4b8202d4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/Runtime/VoxelNodeType.h @@ -0,0 +1,108 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphGlobals.h" +#include "VoxelMaterial.h" +#include "VoxelRange.h" + +using FVoxelGraphSeed = int32; + +struct FVoxelNodeType +{ + static constexpr int32 VoxelNodeTypeSize = FMath::Max(sizeof(v_flt), FMath::Max(sizeof(FVoxelMaterial), sizeof(int32))); + + FVoxelNodeType() = default; + + template + FORCEINLINE T& Get() + { + static_assert(sizeof(T) <= VoxelNodeTypeSize, ""); + static_assert(TOr, TIsSame, TIsSame, TIsSame, TIsSame>::Value, ""); + return *reinterpret_cast(Data); + } + template + FORCEINLINE const T& Get() const + { + static_assert(sizeof(T) <= VoxelNodeTypeSize, ""); + static_assert(TOr, TIsSame, TIsSame, TIsSame, TIsSame>::Value, ""); + return *reinterpret_cast(Data); + } + +private: + static_assert(sizeof(v_flt) <= VoxelNodeTypeSize, ""); + static_assert(sizeof(int32) <= VoxelNodeTypeSize, ""); + static_assert(sizeof(bool) <= VoxelNodeTypeSize, ""); + static_assert(sizeof(FVoxelMaterial) <= VoxelNodeTypeSize, ""); + static_assert(sizeof(FColor) <= VoxelNodeTypeSize, ""); + + uint8 Data[VoxelNodeTypeSize]; +}; + +struct FVoxelNodeRangeType +{ + static constexpr int32 VoxelNodeRangeTypeSize = sizeof(TVoxelRange); + + FVoxelNodeRangeType() = default; + + template + auto& Get(); + template + const auto& Get() const; + +private: + static_assert(sizeof(TVoxelRange) <= VoxelNodeRangeTypeSize, ""); + static_assert(sizeof(TVoxelRange) <= VoxelNodeRangeTypeSize, ""); + static_assert(sizeof(FVoxelBoolRange) <= VoxelNodeRangeTypeSize, ""); + static_assert(sizeof(FVoxelMaterialRange) <= VoxelNodeRangeTypeSize, ""); + static_assert(sizeof(FVoxelColorRange) <= VoxelNodeRangeTypeSize, ""); + + uint8 Data[VoxelNodeRangeTypeSize]; +}; + +#define DEFINE_GET(T1, T2) \ + template<> FORCEINLINE auto& FVoxelNodeRangeType::Get() { static_assert(sizeof(T2) <= VoxelNodeRangeTypeSize, ""); return *reinterpret_cast(Data); } \ + template<> FORCEINLINE const auto& FVoxelNodeRangeType::Get() const { static_assert(sizeof(T2) <= VoxelNodeRangeTypeSize, ""); return *reinterpret_cast(Data); } + + DEFINE_GET(v_flt, TVoxelRange); + DEFINE_GET(int32, TVoxelRange); + DEFINE_GET(bool, FVoxelBoolRange); + DEFINE_GET(FVoxelMaterial, FVoxelMaterialRange); + DEFINE_GET(FColor, FVoxelColorRange); +#undef DEFINE_GET + +template +struct TVoxelNodeBuffer +{ + T* RESTRICT const Buffer; + + TVoxelNodeBuffer(T* Buffer) + : Buffer(Buffer) + { + } + + FORCEINLINE T& operator[](int32 Index) + { + checkVoxelGraph(0 <= Index && Index < MAX_VOXELNODE_PINS); + return Buffer[Index]; + } +}; + +struct FVoxelNodeInputBuffer : TVoxelNodeBuffer +{ + using TVoxelNodeBuffer::TVoxelNodeBuffer; +}; +struct FVoxelNodeOutputBuffer : TVoxelNodeBuffer +{ + using TVoxelNodeBuffer::TVoxelNodeBuffer; +}; + +struct FVoxelNodeInputRangeBuffer : TVoxelNodeBuffer +{ + using TVoxelNodeBuffer::TVoxelNodeBuffer; +}; +struct FVoxelNodeOutputRangeBuffer : TVoxelNodeBuffer +{ + using TVoxelNodeBuffer::TVoxelNodeBuffer; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelAxisDependencies.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelAxisDependencies.h new file mode 100644 index 00000000..09f6fa09 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelAxisDependencies.h @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAxisDependencies.generated.h" + +enum class EVoxelAxisDependencies : uint8 +{ + Constant, + X, + XY, + XYZ, +}; + +namespace EVoxelAxisDependenciesFlags +{ + enum : uint8 + { + X = 0x1, + Y = 0x2, + Z = 0x4, + XY = X | Y, + XZ = X | Z, + YZ = Y | Z, + XYZ = X | Y | Z + }; +} + +UENUM() +enum class EVoxelFunctionAxisDependencies : uint8 +{ + X, + // X was run + XYWithCache, + // X wasn't run + XYWithoutCache, + // XY was run + XYZWithCache, + // XY wasn't run + XYZWithoutCache +}; + +namespace FVoxelAxisDependencies +{ + inline EVoxelAxisDependencies GetVoxelAxisDependenciesFromFlag(uint8 Flag) + { + if (Flag & EVoxelAxisDependenciesFlags::Z) + { + return EVoxelAxisDependencies::XYZ; + } + else if (Flag & EVoxelAxisDependenciesFlags::Y) + { + return EVoxelAxisDependencies::XY; + } + else if (Flag & EVoxelAxisDependenciesFlags::X) + { + return EVoxelAxisDependencies::X; + } + else + { + return EVoxelAxisDependencies::Constant; + } + } + + inline bool IsConstant(uint8 Flag) + { + return GetVoxelAxisDependenciesFromFlag(Flag) == EVoxelAxisDependencies::Constant; + } + + inline TArray> GetAllFunctionDependencies() + { + return + { + EVoxelFunctionAxisDependencies::X, + EVoxelFunctionAxisDependencies::XYWithCache, + EVoxelFunctionAxisDependencies::XYWithoutCache, + EVoxelFunctionAxisDependencies::XYZWithCache, + EVoxelFunctionAxisDependencies::XYZWithoutCache + }; + } + + VOXELGRAPH_API FString ToString(EVoxelAxisDependencies Dependencies); + VOXELGRAPH_API FString ToString(EVoxelFunctionAxisDependencies Dependencies); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelContext.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelContext.h new file mode 100644 index 00000000..02cbf2f0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelContext.h @@ -0,0 +1,111 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelRange.h" +#include "VoxelIntBox.h" +#include "VoxelItemStack.h" + +struct VOXELGRAPH_API FVoxelContext +{ + const int32 LOD; + const FVoxelItemStack Items; + const FTransform LocalToWorld; + const bool bHasCustomTransform; + + static const FVoxelContext EmptyContext; + + FVoxelContext( + int32 LOD, + const FVoxelItemStack& Items, + const FTransform& LocalToWorld, + bool bHasCustomTransform) + : LOD(LOD) + , Items(Items) + , LocalToWorld(LocalToWorld) + , bHasCustomTransform(bHasCustomTransform) + { + } + + FORCEINLINE v_flt GetWorldX() const { return WorldX; } + FORCEINLINE v_flt GetWorldY() const { return WorldY; } + FORCEINLINE v_flt GetWorldZ() const { return WorldZ; } + + FORCEINLINE v_flt GetLocalX() const { return LocalX; } + FORCEINLINE v_flt GetLocalY() const { return LocalY; } + FORCEINLINE v_flt GetLocalZ() const { return LocalZ; } + +private: + v_flt WorldX = 0; + v_flt WorldY = 0; + v_flt WorldZ = 0; + + v_flt LocalX = 0; + v_flt LocalY = 0; + v_flt LocalZ = 0; + + template + FORCEINLINE void UpdateCoordinates(v_flt NewWorldX, v_flt NewWorldY, v_flt NewWorldZ) + { + checkVoxelSlow(bCustomTransform == bHasCustomTransform); + + WorldX = NewWorldX; + WorldY = NewWorldY; + WorldZ = NewWorldZ; + + if (bCustomTransform) + { + const FVector Local = LocalToWorld.InverseTransformPosition(FVector(WorldX, WorldY, WorldZ)); + LocalX = Local.X; + LocalY = Local.Y; + LocalZ = Local.Z; + } + else + { + LocalX = NewWorldX; + LocalY = NewWorldY; + LocalZ = NewWorldZ; + } + } + + template + friend class TVoxelGraphGeneratorInstanceHelper; + friend class FVoxelGraphPreview; +}; + +struct VOXELGRAPH_API FVoxelContextRange +{ + const int32 LOD; + const FVoxelItemStack Items; + const FTransform LocalToWorld; + const bool bHasCustomTransform; + const FVoxelIntBox WorldBounds; + const FVoxelIntBox LocalBounds; + + static const FVoxelContextRange EmptyContext; + + FVoxelContextRange( + int32 LOD, + const FVoxelItemStack& Items, + const FTransform& LocalToWorld, + bool bHasCustomTransform, + const FVoxelIntBox& WorldBounds) + : LOD(LOD) + , Items(Items) + , LocalToWorld(LocalToWorld) + , bHasCustomTransform(bHasCustomTransform) + , WorldBounds(WorldBounds) + , LocalBounds(bHasCustomTransform ? WorldBounds.ApplyTransform(LocalToWorld, 1 << LOD) : WorldBounds) + { + } + + FORCEINLINE TVoxelRange GetWorldX() const { return { v_flt(WorldBounds.Min.X), v_flt(WorldBounds.Max.X) }; } + FORCEINLINE TVoxelRange GetWorldY() const { return { v_flt(WorldBounds.Min.Y), v_flt(WorldBounds.Max.Y) }; } + FORCEINLINE TVoxelRange GetWorldZ() const { return { v_flt(WorldBounds.Min.Z), v_flt(WorldBounds.Max.Z) }; } + + FORCEINLINE TVoxelRange GetLocalX() const { return { v_flt(LocalBounds.Min.X), v_flt(LocalBounds.Max.X) }; } + FORCEINLINE TVoxelRange GetLocalY() const { return { v_flt(LocalBounds.Min.Y), v_flt(LocalBounds.Max.Y) }; } + FORCEINLINE TVoxelRange GetLocalZ() const { return { v_flt(LocalBounds.Min.Z), v_flt(LocalBounds.Max.Z) }; } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsDefinitions.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsDefinitions.h new file mode 100644 index 00000000..f9e75a07 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsDefinitions.h @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +#if PLATFORM_COMPILER_CLANG +#define PRAGMA_GENERATED_VOXEL_GRAPH_START \ + _Pragma("clang diagnostic push") \ + //_Pragma("clang diagnostic ignored \"-Wnull-dereference\"") + +#define PRAGMA_GENERATED_VOXEL_GRAPH_END \ + _Pragma("clang diagnostic pop") +#else +#define PRAGMA_GENERATED_VOXEL_GRAPH_START \ + __pragma(warning(push)) \ + __pragma(warning(disable: \ + 4101 /* unreferenced local variable */\ + 4701 /* potentially uninitialized local variable */\ + 4723 /* potential divide by 0, happens with FORCEINLINE of math functions */)) + +#define PRAGMA_GENERATED_VOXEL_GRAPH_END \ + __pragma(warning(pop)) +#endif \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsIncludes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsIncludes.h new file mode 100644 index 00000000..1936dfa1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsIncludes.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelGenerators/VoxelEmptyGenerator.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" + +#include "VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h" + +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h" +#include "VoxelTools/VoxelHardnessHandler.h" + +#include "NodeFunctions/VoxelNodeFunctions.h" +#include "NodeFunctions/VoxelSDFNodeFunctions.h" +#include "NodeFunctions/VoxelMathNodeFunctions.h" +#include "NodeFunctions/VoxelDeprecatedNodeFunctions.h" +#include "NodeFunctions/VoxelPlaceableItemsNodeFunctions.h" + +#include "VoxelContext.h" +#include "VoxelMessages.h" +#include "VoxelMaterialBuilder.h" +#include "FastNoise/VoxelFastNoise.h" +#include "FastNoise/VoxelFastNoise.inl" +#include "VoxelGraphGeneratorHelpers.h" +#include "VoxelGeneratedWorldGeneratorsDefinitions.h" + +#include "Curves/RichCurve.h" +#include "Curves/CurveFloat.h" +#include "Curves/CurveLinearColor.h" +#include "Engine/Texture2D.h" +#include "Containers/StaticArray.h" \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsPCH.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsPCH.h new file mode 100644 index 00000000..ec54125c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsPCH.h @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGeneratedWorldGeneratorsIncludes.h" \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphConstants.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphConstants.h new file mode 100644 index 00000000..79c61216 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphConstants.h @@ -0,0 +1,69 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphGlobals.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" + +namespace FVoxelGraphOutputsIndices +{ + enum : uint16 + { + RangeAnalysisIndex = 0, // Not used as an actual output + ValueIndex = 1, + MaterialIndex = 2, + UpVectorXIndex = 3, + UpVectorYIndex = 4, + UpVectorZIndex = 5, + DefaultOutputsLastUsed = 5, + DefaultOutputsMax = 32, + OutputsMax = 256 + }; +} + +static_assert(FVoxelGraphOutputsIndices::OutputsMax == MAX_VOXELGRAPH_OUTPUTS, ""); + +namespace FVoxelGraphPermutation +{ + template + inline bool constexpr Contains(uint32 Value) { return Value == A; } + template + inline bool constexpr Contains(uint32 Value) + { + return Value == A || Contains(Value); + } + + template + static inline constexpr bool IsSorted() { return true; } + static inline constexpr bool IsSorted() { return true; } + template + static inline constexpr bool IsSorted() + { + return A <= B && IsSorted(); + } + + template + inline uint32 constexpr Hash() + { + return FVoxelUtilities::MurmurHash32(A); + } + template + inline uint32 constexpr Hash() + { + return FVoxelUtilities::MurmurHash32(FVoxelUtilities::MurmurHash32(A) ^ Hash()); + } + + template + inline uint32 Hash(const TArray& Array, int32 Index = 0) + { + if (Array.Num() - Index == 1) + { + return FVoxelUtilities::MurmurHash32(Array[Index]); + } + else + { + return FVoxelUtilities::MurmurHash32(FVoxelUtilities::MurmurHash32(Array[Index]) ^ Hash(Array, Index + 1)); + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphDataItemConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphDataItemConfig.h new file mode 100644 index 00000000..2a7cdfc8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphDataItemConfig.h @@ -0,0 +1,16 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphDataItemConfig.generated.h" + +UCLASS() +class VOXELGRAPH_API UVoxelGraphDataItemConfig : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + TArray Parameters; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h new file mode 100644 index 00000000..b07f9825 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Misc/EnumRange.h" +#include "UObject/WeakObjectPtr.h" + +class UVoxelNode; +class UVoxelGraphGenerator; +class UEdGraph; +class UEdGraphNode; +class FVoxelCompilationNode; +class FVoxelComputeNode; + +enum class EVoxelGraphNodeMessageType : int32 +{ + Info, + Warning, + Error +}; +ENUM_RANGE_BY_FIRST_AND_LAST(EVoxelGraphNodeMessageType, EVoxelGraphNodeMessageType::Info, EVoxelGraphNodeMessageType::Error); + +struct FVoxelGraphMessage +{ + TWeakObjectPtr Node; + FString Message; + EVoxelGraphNodeMessageType Type; +}; + +class VOXELGRAPH_API FVoxelGraphErrorReporter +{ +public: + FVoxelGraphErrorReporter(const UVoxelGraphGenerator* VoxelGraphGenerator); + // Will copy to Parent on deletion + FVoxelGraphErrorReporter(FVoxelGraphErrorReporter& Parent, const FString& ErrorPrefix); + ~FVoxelGraphErrorReporter(); + + bool HasError() const { return bHasError; } + + void AddError(const FString& Error); + void AddInternalError(const FString Error); // Won't break but will report an error + + void AddMessageToNode( + const UVoxelNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode = true, + bool bShowInList = true); + void AddMessageToNode( + const FVoxelCompilationNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode = true, + bool bShowInList = true); + void AddMessageToNode( + const FVoxelComputeNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity, + bool bSelectNode = true, + bool bShowInList = true); + + void AddNodeToSelect(const UVoxelNode* Node); + void AddNodeToSelect(const FVoxelCompilationNode* Node); + +public: + void Apply(bool bSelectNodes); + +public: + void CopyFrom(FVoxelGraphErrorReporter& Other); + +public: + static void ClearMessages(const UVoxelGraphGenerator* Graph, bool bClearAll = true, EVoxelGraphNodeMessageType MessagesToClear = EVoxelGraphNodeMessageType::Error); + static void ClearNodesMessages(const UVoxelGraphGenerator* Graph, bool bRecursive = true, bool bClearAll = true, EVoxelGraphNodeMessageType MessagesToClear = EVoxelGraphNodeMessageType::Error); + static void ClearCompilationMessages(const UVoxelGraphGenerator* Graph); + + static void AddMessageToNodeInternal( + const UVoxelNode* Node, + const FString& Message, + EVoxelGraphNodeMessageType Severity); + +private: + const UVoxelGraphGenerator* const VoxelGraphGenerator; + FVoxelGraphErrorReporter* const Parent; + const FString ErrorPrefix; + + bool bHasError = false; + + TArray Messages; + + TSet NodesToSelect; + TSet GraphsToRefresh; + + FString AddPrefixToError(const FString& Error) const; +}; + +namespace FEnsureVoxelGraphHelper +{ + // Returns true + bool Check(FVoxelGraphErrorReporter& ErrorReporter, const FString& Expr, const FString& File, int32 Line, const FVoxelCompilationNode* Node); +} + +#define ensureVoxelGraphImpl(Expr, Node, ErrorReporter) (ensure((Expr)) || FEnsureVoxelGraphHelper::Check(ErrorReporter, #Expr, __FILE__, __LINE__, Node)) +#define ensureVoxelGraph(Expr, Node) ensureVoxelGraphImpl(Expr, Node, ErrorReporter) +#define ensureVoxelGraphExitPass(Expr, Node) if (!ensureVoxelGraphImpl(Expr, Node, Compiler.ErrorReporter)) return; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGenerator.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGenerator.h new file mode 100644 index 00000000..c20f890e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGenerator.h @@ -0,0 +1,208 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelTexture.h" +#include "VoxelGraphOutputs.h" +#include "VoxelAxisDependencies.h" +#include "VoxelGenerators/VoxelGenerator.h" + +#include "Engine/EngineTypes.h" +#include "EdGraph/EdGraphPin.h" +#include "VoxelGraphGenerator.generated.h" + +class UEdGraph; +class UVoxelNode; +class UTexture2D; +class UVoxelExposedNode; +class UVoxelGraphOutputsConfig; +class UVoxelGraphPreviewSettings; +class FVoxelGraphGeneratorInstance; +struct FVoxelCompiledGraphs; + +UENUM() +enum class EVoxelGraphGeneratorDebugLevel : uint8 +{ + BeforeMacroInlining, + AfterMacroInlining, + AfterBiomeMergeReplace, + AfterSmartMinMaxReplace, + BeforeFillFunctionSeparators, + Output, + Function, + Axis +}; + +/** + * A graph generator + */ +UCLASS(BlueprintType, HideCategories = (Object), HideDropdown) +class VOXELGRAPH_API UVoxelGraphGenerator : public UVoxelTransformableGenerator +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + UVoxelGraphOutputsConfig* Outputs; + + TMap GetOutputs() const; + TArray GetPermutations() const; + +public: + UPROPERTY() + bool bAutomaticPreview = true; + + UPROPERTY() + bool bShowFlowMergeAndFunctionsWarnings = true; + +public: + UPROPERTY(EditAnywhere, Category = "Automatic compilation", meta= (DisplayName = "Compile to C++ on Save")) + bool bCompileToCppOnSave = false; + + // Relative to project directory + UPROPERTY(EditAnywhere, Category = "Automatic compilation", meta = (FilePathFilter = "h", EditCondition = bCompileToCppOnSave)) + FFilePath SaveLocation; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + FString LastSavePath; +#endif + +public: + // Range analysis gives a pretty significant speed-up. You should not disable it + UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Range Analysis") + bool bEnableRangeAnalysis = true; + +public: + // Will show the nodes functions. If DetailedErrors is false, will only show TargetToDebug + UPROPERTY(EditAnywhere, Category = "Debug", meta = (Refresh)) + bool bShowFunctions = false; + + // Show errors callstacks + UPROPERTY(EditAnywhere, Category = "Debug", meta = (Refresh)) + bool bDetailedErrors = false; + + // Can be enabled in Window->Debug Graph + UPROPERTY(EditAnywhere, Category = "Debug", meta = (Refresh)) + bool bEnableDebugGraph = false; + + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + bool bShowPinsIds = false; + + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + bool bShowAxisDependencies = false; + + // The level of compilation to debug + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + EVoxelGraphGeneratorDebugLevel DebugLevel; + + // The target to debug, if DebugLevel is below or equal to Target + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + FString TargetToDebug = "Value"; + + // The function to debug, if DebugLevel is below or equal to Function + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + int32 FunctionToDebug = 0; + + // The axis to debug, if DebugLevel is Axis + UPROPERTY(EditAnywhere, Category = "Debug", meta = (EditCondition = bEnableDebugGraph, Refresh)) + EVoxelFunctionAxisDependencies AxisDependenciesToDebug; + + // Increase this if your macro nodes are overlapping in the debug graph + UPROPERTY(EditAnywhere, Category = "Debug", AdvancedDisplay, meta = (EditCondition = bEnableDebugGraph, Refresh)) + float NodesDepthScaleFactor = 1; + + UPROPERTY(EditAnywhere, Category = "Debug", AdvancedDisplay, meta = (EditCondition = bEnableDebugGraph, Refresh)) + bool bHideDataNodes = false; + +public: + UPROPERTY() + TArray AllNodes; + + UPROPERTY() + TArray DebugNodes; + + UPROPERTY() + UVoxelNode* FirstNode; + + UPROPERTY() + FGuid FirstNodePinId; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + UEdGraph* VoxelGraph; + + UPROPERTY() + UEdGraph* VoxelDebugGraph; + + FEdGraphPinReference PreviewedPin; +#endif + + UPROPERTY() + UVoxelGraphPreviewSettings* PreviewSettings; + +public: + TMap TransientParameters; + +public: + // Create the cpp file of this graph + bool CompileToCpp(FString& OutHeader, FString& OutCpp, const FString& Filename); + // Create the compiled graphs of this graph + bool CreateGraphs( + FVoxelCompiledGraphs& OutGraphs, + bool bPreview, + bool bAutomaticPreview, + bool bOnlyShowAxisDependencies); + // Get the generator instance of this graph, optionally enabling preview + bool GetGraphInstance( + TVoxelSharedPtr& OutGenerator, + bool bPreview, + bool bAutomaticPreview); + + //~ Begin UVoxelGenerator Interface + virtual void ApplyParameters(const TMap& Parameters) override; + virtual void GetParameters(TArray& OutParameters) const override; + virtual TVoxelSharedRef GetTransformableInstance() override; + virtual TVoxelSharedRef GetTransformableInstance(const TMap& Parameters) override;; + //~ End UVoxelGenerator Interface + +#if WITH_EDITOR + //~ Begin UObject Interface + void PostInitProperties() override; + void PostLoad() override; + void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + //~ End UObject Interface + + // Create a new node of NewNodeClass + UVoxelNode* ConstructNewNode(UClass* NewNodeClass, const FVector2D& Position, bool bSelectNewNode = true); + template + T* ConstructNewNode(const FVector2D& Position, bool bSelectNewNode = true) + { + return CastChecked(ConstructNewNode(T::StaticClass(), Position, bSelectNewNode)); + } + // Create the basic voxel graph + void CreateGraphs(); + + // Use the EdGraph representation to compile the VoxelNodes + void CompileVoxelNodesFromGraphNodes(); +#endif + +#if WITH_EDITORONLY_DATA +public: + UTexture2D* GetPreviewTexture(); + void SetPreviewTexture(const TArray& Colors, int32 Size); + +private: + UPROPERTY(NonTransactional) + TArray PreviewTextureSave; + + UPROPERTY(Transient) + UTexture2D* PreviewTexture; +#endif + +private: + void UpdateSetterNodes(); + void BindUpdateSetterNodes(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGeneratorHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGeneratorHelpers.h new file mode 100644 index 00000000..1509cf05 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGeneratorHelpers.h @@ -0,0 +1,404 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/StaticArray.h" +#include "VoxelMinimal.h" +#include "VoxelContext.h" +#include "VoxelGraphConstants.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "VoxelGraphGeneratorHelpers.generated.h" + +// See https://godbolt.org/z/4IzS-b +#if defined(_MSC_VER) && !defined(INTELLISENSE_PARSER) +#define MSVC_TEMPLATE +#else +#define MSVC_TEMPLATE template +#endif + +struct FVoxelGraphOutputsInit +{ + EVoxelMaterialConfig MaterialConfig; +}; + +template +class TVoxelGraphGeneratorInstanceHelper : public TVoxelTransformableGeneratorInstanceHelper +{ +public: + using FVoxelGeneratorInstance::TOutputFunctionPtr; + using FVoxelGeneratorInstance::TRangeOutputFunctionPtr; + using FVoxelTransformableGeneratorInstance::TOutputFunctionPtr_Transform; + using FVoxelTransformableGeneratorInstance::TRangeOutputFunctionPtr_Transform; + + using FVoxelGeneratorInstance::FCustomFunctionPtrs; + using FVoxelTransformableGeneratorInstance::FCustomFunctionPtrs_Transform; + + DEPRECATED_VOXEL_GRAPH_FUNCTION() + TVoxelGraphGeneratorInstanceHelper( + const TMap& FloatOutputs, + const TMap& Int32Outputs, + const TMap& ColorOutputs, + + const FCustomFunctionPtrs& CustomFunctionPtrs, + const FCustomFunctionPtrs_Transform& CustomFunctionPtrs_Transform, + + bool bEnableRangeAnalysis) + : TVoxelTransformableGeneratorInstanceHelper(nullptr, CustomFunctionPtrs, CustomFunctionPtrs_Transform) + , bEnableRangeAnalysis(bEnableRangeAnalysis) + , CustomOutputsNames(FName()) + { + auto& Array = const_cast&>(CustomOutputsNames); + for (auto& It : FloatOutputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + for (auto& It : Int32Outputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + for (auto& It : ColorOutputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + } + + TVoxelGraphGeneratorInstanceHelper( + const TMap& FloatOutputs, + const TMap& Int32Outputs, + const TMap& ColorOutputs, + + const FCustomFunctionPtrs& CustomFunctionPtrs, + const FCustomFunctionPtrs_Transform& CustomFunctionPtrs_Transform, + + UWorldObject& Object) + : TVoxelTransformableGeneratorInstanceHelper(&Object, CustomFunctionPtrs, CustomFunctionPtrs_Transform) + , bEnableRangeAnalysis(Object.bEnableRangeAnalysis) + , CustomOutputsNames(FName()) + { + auto& Array = const_cast&>(CustomOutputsNames); + for (auto& It : FloatOutputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + for (auto& It : Int32Outputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + for (auto& It : ColorOutputs) + { + ensure(Array[It.Value] == FName()); + Array[It.Value] = It.Key; + } + } + +public: + template + T GetOutput(const FTransform& LocalToWorld, T DefaultValue, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + ensure(bInit); + + auto&& Target = This().template GetTarget(); + auto Outputs = Target.GetOutputs(); + + Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); + Outputs.template Set(DefaultValue); + + FVoxelContext Context(LOD, Items, LocalToWorld, bCustomTransform); + Context.UpdateCoordinates(X, Y, Z); + Target.ComputeXYZWithoutCache(Context, Outputs); + + return Outputs.template Get(); + } + + template + TVoxelRange GetOutputRange(const FTransform& LocalToWorld, TVoxelRange DefaultValue, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + ensure(bInit); + + if (!bEnableRangeAnalysis) + { + return TVoxelRange::Infinite(); + } + + auto&& Target = This().template GetRangeTarget(); + auto Outputs = Target.GetOutputs(); + + Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); + Outputs.template Set(DefaultValue); + + auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); + + ensure(!RangeFailStatus.HasFailed()); + RangeFailStatus.Reset(); + + const FVoxelContextRange Context(LOD, Items, LocalToWorld, bCustomTransform, WorldBounds); + + Target.ComputeXYZWithoutCache(Context, Outputs); + + if (RangeFailStatus.HasFailed()) + { + RangeFailStatus.Reset(); + return TVoxelRange::Infinite(); + } + + return Outputs.template Get(); + } + + template + void GetOutput(const FTransform& LocalToWorld, T DefaultValue, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const + { + ensure(bInit); + + auto&& Target = This().template GetTarget(); + + FVoxelContext Context(LOD, Items, LocalToWorld, bCustomTransform); + + if (!bCustomTransform) + { + // We can only use the dependencies analysis if we don't have a transform, or if it's only translation + scale + // (and thus not changing the axis). Not checking that second case though. + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + Context.LocalX = Context.WorldX = X; + + auto BufferX = Target.GetBufferX(); + Target.ComputeX(Context, BufferX); + + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + Context.LocalY = Context.WorldY = Y; + + auto BufferXY = Target.GetBufferXY(); + Target.ComputeXYWithCache(Context, BufferX, BufferXY); + + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + Context.LocalZ = Context.WorldZ = Z; + + auto Outputs = Target.GetOutputs(); + Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); + Outputs.template Set(DefaultValue); + Target.ComputeXYZWithCache(Context, static_cast(BufferX), static_cast(BufferXY), Outputs); + QueryZone.Set(X, Y, Z, QueryZoneType(Outputs.template Get())); + } + } + } + } + else + { + // Have to query all the voxels individually + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) + { + for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) + { + Context.UpdateCoordinates(X, Y, Z); + + auto Outputs = Target.GetOutputs(); + Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); + Outputs.template Set(DefaultValue); + Target.ComputeXYZWithoutCache(Context, Outputs); + QueryZone.Set(X, Y, Z, QueryZoneType(Outputs.template Get())); + } + } + } + } + } + + template + T GetDataImpl(const FTransform& LocalToWorld, T DefaultValue, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return GetOutput(LocalToWorld, DefaultValue, X, Y, Z, LOD, Items); + } + + template + void GetData(const FTransform& LocalToWorld, T DefaultValue, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const + { + return GetOutput(LocalToWorld, DefaultValue, QueryZone, LOD, Items); + } + + template + T GetCustomOutputImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + static_assert(Index < MAX_VOXELGRAPH_OUTPUTS, ""); + return GetOutput(LocalToWorld, T{}, X, Y, Z, LOD, Items); + } + + template + TVoxelRange GetCustomOutputRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + static_assert(Index < MAX_VOXELGRAPH_OUTPUTS, ""); + return GetOutputRange(LocalToWorld, T{}, WorldBounds, LOD, Items); + } + + template + T GetCustomOutputWithTransform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return GetCustomOutputImpl(LocalToWorld, X, Y, Z, LOD, Items); + } + template + TVoxelRange GetCustomOutputRangeWithTransform(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + return GetCustomOutputRangeImpl(LocalToWorld, WorldBounds, LOD, Items); + } + template + T GetCustomOutputNoTransform(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return GetCustomOutputImpl(FTransform(), X, Y, Z, LOD, Items); + } + template + TVoxelRange GetCustomOutputRangeNoTransform(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + return GetCustomOutputRangeImpl(FTransform(), Bounds, LOD, Items); + } + +public: + //~ Begin FVoxelGeneratorInstance Interface + virtual void Init(const FVoxelGeneratorInit& InitStruct) override final + { + bInit = true; + MaterialConfig = InitStruct.MaterialConfig; + InitGraph(InitStruct); + } + + template + v_flt GetValueImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return GetDataImpl(LocalToWorld, 1, X, Y, Z, LOD, Items); + } + template + FVoxelMaterial GetMaterialImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return GetDataImpl(LocalToWorld, FVoxelMaterial::Default(), X, Y, Z, LOD, Items); + } + template + TVoxelRange GetValueRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const + { + return GetOutputRange(LocalToWorld, 1, WorldBounds, LOD, Items); + } + + virtual void GetValues(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final + { + GetData(FTransform(), 1, QueryZone, LOD, Items); + } + virtual void GetMaterials(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final + { + GetData(FTransform(), FVoxelMaterial::Default(), QueryZone, LOD, Items); + } + + virtual void GetValues_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final + { + GetData(LocalToWorld, 1, QueryZone, LOD, Items); + } + virtual void GetMaterials_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final + { + GetData(LocalToWorld, FVoxelMaterial::Default(), QueryZone, LOD, Items); + } + + virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + auto&& Target = This().template GetTarget< + FVoxelGraphOutputsIndices::UpVectorXIndex, + FVoxelGraphOutputsIndices::UpVectorYIndex, + FVoxelGraphOutputsIndices::UpVectorZIndex>(); + + auto Outputs = Target.GetOutputs(); + Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); + + Outputs.template Set(0); + Outputs.template Set(0); + Outputs.template Set(1); + + FVoxelContext Context(0, FVoxelItemStack::Empty, FTransform::Identity, false); + Context.UpdateCoordinates(X, Y, Z); + + Target.ComputeXYZWithoutCache(Context, Outputs); + + return FVector( + Outputs.template Get(), + Outputs.template Get(), + Outputs.template Get()).GetSafeNormal(); + } + //~ End FVoxelGeneratorInstance Interface + +public: + virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) = 0; + +protected: + template + struct NoTransformAccessor + { + template + static ReturnType Get() + { + return static_cast(&TChild::template GetCustomOutputNoTransform); + } + }; + template + struct NoTransformRangeAccessor + { + template + static ReturnType Get() + { + return static_cast(&TChild::template GetCustomOutputRangeNoTransform); + } + }; + template + struct WithTransformAccessor + { + template + static ReturnType Get() + { + return static_cast(&TChild::template GetCustomOutputWithTransform); + } + }; + template + struct WithTransformRangeAccessor + { + template + static ReturnType Get() + { + return static_cast(&TChild::template GetCustomOutputRangeWithTransform); + } + }; + +private: + const bool bEnableRangeAnalysis; + // Used to forward the custom output calls to the generator in the stack + const TStaticArray CustomOutputsNames; + + bool bInit = false; + EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig(-1); + + const TChild& This() const + { + return static_cast(*this); + } + TChild& This() + { + return static_cast(*this); + } +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelGraphGeneratorHelper : public UVoxelTransformableGenerator +{ + GENERATED_BODY() + +public: + // Range analysis gives a pretty significant speed-up. You should not disable it + UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Misc", meta = (HideInGenerator)) + bool bEnableRangeAnalysis = true; + +protected: + DEPRECATED_VOXEL_GRAPH_FUNCTION() + virtual TMap GetDefaultSeeds() const { return {}; } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGlobals.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGlobals.h new file mode 100644 index 00000000..79cbcd43 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphGlobals.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +#define ENABLE_VOXELGRAPH_CHECKS 0 + +#define MAX_VOXELNODE_PINS 256 +#define MAX_VOXELFUNCTION_ARGS 256 +#define MAX_VOXELGRAPH_OUTPUTS 256 + +#if ENABLE_VOXELGRAPH_CHECKS +#define checkVoxelGraph(...) check(__VA_ARGS__) +#else +#define checkVoxelGraph(...) +#endif + +#define DEPRECATED_VOXEL_GRAPH_FUNCTION() UE_DEPRECATED(0, "Outdated C++ voxel graph, you should compile it to C++ again") + +#define VOXEL_GRAPH_GENERATED_VERSION 1 \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphModule.h new file mode 100644 index 00000000..f7f6b355 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphModule.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelGraphModule : public IModuleInterface +{ +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputs.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputs.h new file mode 100644 index 00000000..7a169335 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputs.h @@ -0,0 +1,57 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelPinCategory.h" +#include "VoxelGraphOutputs.generated.h" + +class FVoxelCppConstructor; + +using FVoxelGraphPermutationArray = TArray>; + +inline uint32 GetTypeHash(const FVoxelGraphPermutationArray& Array) +{ + if (Array.Num() == 0) + { + return 0; + } + else + { + return FCrc::MemCrc32(Array.GetData(), Array.Num()); + } +} + +USTRUCT() +struct FVoxelGraphOutput +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Outputs") + FName Name; + + UPROPERTY(EditAnywhere, Category = "Outputs") + EVoxelDataPinCategory Category = EVoxelDataPinCategory::Float; + + UPROPERTY() + FGuid GUID; + + UPROPERTY(Transient) + uint32 Index = -1; + + FString GetDeclaration(FVoxelCppConstructor& Constructor) const; + FString GetRefDeclaration(FVoxelCppConstructor& Constructor) const; + + static const TArray DefaultOutputs; + static const TArray DefaultOutputsPermutations; +}; + +namespace FVoxelGraphOutputsUtils +{ + FString GetPermutationName(const FVoxelGraphPermutationArray& Permutation, const TMap& Outputs); + TMap GetSingleOutputsNamesMap( + const TArray& Permutations, + const TMap& Outputs, + EVoxelDataPinCategory CategoryFilter); + VOXELGRAPH_API bool IsVoxelGraphOutputHidden(int32 Index); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputsConfig.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputsConfig.h new file mode 100644 index 00000000..dfb8d8d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphOutputsConfig.h @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphOutputs.h" +#include "VoxelSpawners/VoxelSpawnerOutputsConfig.h" +#include "VoxelGraphOutputsConfig.generated.h" + +UCLASS(CollapseCategories) +class VOXELGRAPH_API UVoxelGraphOutputsConfig : public UVoxelSpawnerOutputsConfig +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + TArray Outputs; + + //~ Begin UVoxelSpawnerOutputConfig Interface + virtual TArray GetFloatOutputs() const override; + //~ End UVoxelSpawnerOutputConfig Interface + +public: +#if WITH_EDITOR + FSimpleMulticastDelegate OnPropertyChanged; +#endif + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual void PostLoad() override; + //~ End UObject Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphPreviewSettings.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphPreviewSettings.h new file mode 100644 index 00000000..059eb62f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelGraphPreviewSettings.h @@ -0,0 +1,316 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.h" +#include "VoxelUtilities/VoxelIntVectorUtilities.h" +#include "VoxelGraphPreviewSettings.generated.h" + +class UStaticMesh; +class UMaterialInterface; +class UVoxelPlaceableItemManager; +class UVoxelMaterialCollectionBase; + +UENUM() +enum class EVoxelGraphPreviewAxes : uint8 +{ + X, + Y, + Z +}; + +UENUM() +enum class EVoxelGraphPreviewType : uint8 +{ + Density, + Material, + Cost, + RangeAnalysis +}; + + +UENUM() +enum class EVoxelGraphPreviewShowValue : uint8 +{ + ShowValue, + ShowRange, + ShowValueAndRange +}; + +UENUM() +enum class EVoxelGraphMaterialPreviewType : uint8 +{ + // Show the material RGB values + RGB, + // Show the material Alpha value + Alpha, + // Assign one color per index + SingleIndex, + // Blends the indices colors + MultiIndex_Overview, + // Only shows the strength of a single index (set by MultiIndexToPreview) + MultiIndex_SingleIndexPreview, + // Wetness + MultiIndex_Wetness, + // Red-Green preview of UV0 + UV0, + // Red-Green preview of UV1 + UV1, + // Red-Green preview of UV2 + UV2, + // Red-Green preview of UV3 + UV3 +}; + +UCLASS() +class VOXELGRAPH_API UVoxelGraphPreviewSettings : public UObject +{ + GENERATED_BODY() + +public: + UVoxelGraphPreviewSettings(); + +public: + UPROPERTY() + bool bShowStats = false; + + UPROPERTY() + bool bShowValues = false; + +public: + // Min displayed value + UPROPERTY(VisibleAnywhere, Category = "Preview Info") + mutable FString MinValue; + + // Max displayed value + UPROPERTY(VisibleAnywhere, Category = "Preview Info") + mutable FString MaxValue; + + UPROPERTY(VisibleAnywhere, Category = "Preview Info", AdvancedDisplay) + FVoxelIntBox PreviewedBounds; + +public: + UPROPERTY(EditAnywhere, Category = "Preview Zone") + EVoxelGraphPreviewAxes LeftToRight = EVoxelGraphPreviewAxes::X; + + UPROPERTY(EditAnywhere, Category = "Preview Zone") + EVoxelGraphPreviewAxes BottomToTop = EVoxelGraphPreviewAxes::Y; + + UPROPERTY(EditAnywhere, Category = "Preview Zone", meta = (ClampMin = 32, ClampMax = 8192, UIMin = 100, UIMax = 1000)) + int32 Resolution = 512; + + UPROPERTY(EditAnywhere, Category = "Preview Zone", meta = (UIMin = 0, UIMax = 20)) + int32 ResolutionMultiplierLog = 0; + + // Right click & pan the preview to change it + UPROPERTY(EditAnywhere, Category = "Preview Zone") + FIntVector Center = FIntVector(0, 0, 0); + + // Left click the preview to set it + UPROPERTY(EditAnywhere, Category = "Preview Zone") + FIntVector PreviewedVoxel = FIntVector(0, 0, 0); + + UPROPERTY(EditAnywhere, Category = "Preview Zone", AdvancedDisplay) + EVoxelGraphPreviewShowValue ShowValue = EVoxelGraphPreviewShowValue::ShowValue; + +public: + // Set this to the material config your voxel world will use + UPROPERTY(EditAnywhere, Category = "Voxel World Settings") + EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig::RGB; + + // Use to preview Get Index from Material Collection + UPROPERTY(EditAnywhere, Category = "Voxel World Settings") + UVoxelMaterialCollectionBase* MaterialCollection = nullptr; + + // Used to preview placeable items + UPROPERTY(EditAnywhere, Category = "Voxel World Settings", Instanced, meta = (Automatic, UpdateItems)) + UVoxelPlaceableItemManager* PlaceableItemManager = nullptr; + + // Value returned by the Voxel Size node + UPROPERTY(EditAnywhere, Category = "Voxel World Settings", AdvancedDisplay) + float VoxelSize = 100; + + UPROPERTY(EditAnywhere, Category = "Voxel World Settings", AdvancedDisplay) + EVoxelRenderType RenderType = EVoxelRenderType::MarchingCubes; + +public: + UPROPERTY(EditAnywhere, Category = "2D Preview Color") + EVoxelGraphPreviewType PreviewType2D = EVoxelGraphPreviewType::Density; + + // If true, will color the distance field orange when positive, blue when negative, and will apply a cosine to make progression easier to see + // This coloring is directly derived from Inigo Quilez's work + UPROPERTY(EditAnywhere, Category = "2D Preview Color") + bool bDrawColoredDistanceField = true; + + UPROPERTY(EditAnywhere, Category = "2D Preview Color") + EVoxelGraphMaterialPreviewType MaterialPreviewType = EVoxelGraphMaterialPreviewType::RGB; + + // Used if material preview type is MultiIndex_SingleIndexPreview + UPROPERTY(EditAnywhere, Category = "2D Preview Color") + int32 MultiIndexToPreview = 0; + + UPROPERTY(EditAnywhere, Category = "2D Preview Color") + TArray IndexColors; + + // If true, areas where the density is > 0 will be shown as black + UPROPERTY(EditAnywhere, Category = "2D Preview Color", AdvancedDisplay) + bool bHybridMaterialRendering = true; + + // Increase this if there's too much noise in the cost view + UPROPERTY(EditAnywhere, Category = "2D Preview Color", AdvancedDisplay, meta = (UIMin = 0, UIMax = 1)) + float CostPercentile = 0.05f; + + UPROPERTY(EditAnywhere, Category = "2D Preview Color", AdvancedDisplay, meta = (UIMin = 1, UIMax = 1024)) + int32 NumRangeAnalysisChunksPerAxis = 64; + +public: + UPROPERTY(EditAnywhere, Category = "3D Preview Settings") + bool bHeightmapMode = true; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings") + bool bHeightBasedColor = true; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings", meta = (MeshOnly, EditCondition = bHeightBasedColor)) + bool bEnableWater = false; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings", meta = (MeshOnly)) + float Height = 200; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings", meta = (MeshOnly)) + FVector LightDirection = FVector(1, 1, 1); + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings|Raytraced Shadows", meta = (MeshOnly)) + float StartBias = 0.01; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings|Raytraced Shadows", meta = (MeshOnly)) + int32 MaxSteps = 128; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings|Raytraced Shadows", meta = (MeshOnly, UIMin = 0, UIMax = 1)) + float Brightness = 1; + + UPROPERTY(EditAnywhere, Category = "3D Preview Heightmap Settings|Raytraced Shadows", meta = (MeshOnly, UIMin = 0)) + float ShadowDensity = 8; + +public: + UPROPERTY() + UStaticMesh* Mesh = nullptr; + + UPROPERTY() + UMaterialInterface* HeightmapMaterial = nullptr; + + UPROPERTY() + UMaterialInterface* SliceMaterial = nullptr; + +public: + // Will set black to the lowest value in the image, and white to the highest + UPROPERTY(EditAnywhere, Category = "Misc") + bool bAutoNormalize = true; + + // Black + UPROPERTY(EditAnywhere, Category = "Misc", AdvancedDisplay, meta = (EditCondition = "!bAutoNormalize")) + float NormalizeMinValue = -1; + + // White + UPROPERTY(EditAnywhere, Category = "Misc", AdvancedDisplay, meta = (EditCondition = "!bAutoNormalize")) + float NormalizeMaxValue = 1; + + // Simulate querying a chunk at a specific LOD, eg to check fractal noise settings + UPROPERTY(EditAnywhere, Category = "Misc", meta = (ClampMin = 0, ClampMax = 26, UIMin = 0, UIMax = 26, DisplayName = "LOD to preview")) + int32 LODToPreview = 0; + +public: + UPROPERTY() + class UVoxelGraphGenerator* Graph; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; + +struct VOXELGRAPH_API FVoxelGraphPreviewSettingsWrapper +{ +public: + int32 LOD; // != LOD to preview! + int32 Step; + int32 Resolution; + + FIntVector Start; + FIntVector Size; + FIntVector Center; + + FVoxelIntBox Bounds; + + EVoxelGraphPreviewAxes LeftToRight = EVoxelGraphPreviewAxes::X; + EVoxelGraphPreviewAxes BottomToTop = EVoxelGraphPreviewAxes::Y; + + explicit FVoxelGraphPreviewSettingsWrapper(const UVoxelGraphPreviewSettings& Settings); + +public: + template + static auto& GetAxis(T& Vector, EVoxelGraphPreviewAxes Axis) + { + switch (Axis) + { + case EVoxelGraphPreviewAxes::X: + return Vector.X; + case EVoxelGraphPreviewAxes::Y: + return Vector.Y; + case EVoxelGraphPreviewAxes::Z: + default: + return Vector.Z; + } + } + + FIntVector GetRelativePosition(int32 X, int32 Y) const + { + FIntVector Position(0, 0, 0); + GetAxis(Position, LeftToRight) = X; + GetAxis(Position, BottomToTop) = Y; + return Position; + } + FVector GetRelativePosition(float X, float Y) const + { + FVector Position(0, 0, 0); + GetAxis(Position, LeftToRight) = X; + GetAxis(Position, BottomToTop) = Y; + return Position; + } + + FIntVector GetWorldPosition(int32 X, int32 Y) const + { + return Start + Step * GetRelativePosition(X, Y); + } + + FIntPoint GetScreenPosition(FIntVector WorldPosition) const + { + WorldPosition -= Start; + WorldPosition = FVoxelUtilities::DivideRound(WorldPosition, Step); + + FIntPoint Result; + Result.X = GetAxis(WorldPosition, LeftToRight); + Result.Y = GetAxis(WorldPosition, BottomToTop); + return Result; + } + FVector2D GetScreenPosition(FVector WorldPosition) const + { + WorldPosition -= FVector(Start); + WorldPosition /= Step; + + FVector2D Result; + Result.X = GetAxis(WorldPosition, LeftToRight); + Result.Y = GetAxis(WorldPosition, BottomToTop); + return Result; + } + + int32 GetDataIndex(int32 X, int32 Y) const + { + const FIntVector Position = GetRelativePosition(X, Y); + return Position.X + Position.Y * Size.X + Position.Z * Size.X * Size.Y; + } + int32 GetTextureIndex(int32 X, int32 Y) const + { + return X + Resolution * (Resolution - 1 - Y); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNode.h new file mode 100644 index 00000000..9525e418 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNode.h @@ -0,0 +1,184 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelPinCategory.h" +#include "EdGraph/EdGraphNode.h" +#include "VoxelNode.generated.h" + +class UVoxelNode; +class UEdGraphNode; +class UVoxelGraphGenerator; +class FVoxelComputeNode; +class FVoxelCompilationNode; +class FVoxelGraphErrorReporter; +struct FVoxelGeneratorParameter; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelGraphNodeInterface : public UEdGraphNode +{ + GENERATED_BODY() + +public: + UPROPERTY(Transient) + FString InfoMsg; + + UPROPERTY(Transient) + FString WarningMsg; + + virtual UVoxelNode* GetVoxelNode() const { return nullptr; } + virtual bool IsOutdated() const { return false; } + +#if WITH_EDITOR + virtual void PostLoad() override; + virtual void ReconstructNode() override; +#endif +}; + +USTRUCT() +struct FVoxelPin +{ + GENERATED_BODY() + + UPROPERTY() + FGuid PinId; + + UPROPERTY() + FString DefaultValue; + + // Used for macros to check that the nodes are the same + UPROPERTY() + EVoxelPinCategory PinCategory = EVoxelPinCategory::Exec; + + UPROPERTY() + TArray OtherNodes; + + UPROPERTY() + TArray OtherPinIds; + + FVoxelPin() = default; + + FVoxelPin(const FGuid& PinId, const FString& DefaultValue, EVoxelPinCategory PinCategory) + : PinId(PinId) + , DefaultValue(DefaultValue) + , PinCategory(PinCategory) + { + } +}; + +struct FVoxelPinDefaultValueBounds +{ + TOptional Min; + TOptional Max; +}; + +/** + * Base class for VoxelNodes + */ +UCLASS(Abstract, HideCategories = Object, EditInlineNew) +class VOXELGRAPH_API UVoxelNode : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY() + TArray InputPins; + + UPROPERTY() + TArray OutputPins; + + UPROPERTY() + UVoxelGraphGenerator* Graph; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + UVoxelGraphNodeInterface* GraphNode; +#endif + + UPROPERTY() + int32 InputPinCount; + +public: + int32 GetInputPinIndex(const FGuid& PinId); + int32 GetOutputPinIndex(const FGuid& PinId); + + bool HasInputPinWithCategory(EVoxelPinCategory Category) const; + bool HasOutputPinWithCategory(EVoxelPinCategory Category) const; + +public: + //~ Begin UVoxelNode Interface + virtual int32 GetMaxInputPins() const { return 0; } + virtual int32 GetMinInputPins() const { return 0; } + virtual int32 GetInputPinsIncrement() const { return 1; } + virtual void OnInputPinCountModified() {} + + virtual int32 GetOutputPinsCount() const { return 0; } + + virtual FLinearColor GetColor() const { return FLinearColor::Black; } + virtual FLinearColor GetNodeBodyColor() const { return FLinearColor::White; } + virtual FText GetTitle() const; + virtual FText GetTooltip() const; + virtual bool IsCompact() const { return false; } + + virtual FName GetInputPinName(int32 PinIndex) const { return FName(); } + virtual FName GetOutputPinName(int32 PinIndex) const { return FName(); } + + virtual FString GetInputPinToolTip(int32 PinIndex) const { return FString(); } + virtual FString GetOutputPinToolTip(int32 PinIndex) const { return FString(); } + + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const { return EVoxelPinCategory::Float; } + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const { return EVoxelPinCategory::Float; } + + virtual FVoxelPinDefaultValueBounds GetInputPinDefaultValueBounds(int32 PinIndex) const { return {}; } + virtual FString GetInputPinDefaultValue(int32 PinIndex) const { return ""; } + + virtual TVoxelSharedPtr GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const; + virtual TSharedPtr GetCompilationNode() const; + + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter); + + virtual void ApplyParameters(const TMap& Parameters) {} + virtual void GetParameters(TArray& OutParameters) const {} + + virtual bool CanUserDeleteNode() const { return true; } + virtual bool CanDuplicateNode() const { return true; } + + /** + * Can this node be renamed? + */ + virtual bool CanRenameNode() const { return false; } + + /** + * Returns the current 'name' of the node + * Only valid to call on a node that previously returned CanRenameNode() = true. + */ + virtual FString GetEditableName() const { return ""; } + + /** + * Sets the current 'name' of the node + * Only valid to call on a node that previously returned CanRenameNode() = true. + */ + virtual void SetEditableName(const FString& NewName) {} + + /** + * Called after a node copy, once the outer is set correctly and that all new nodes are added to Graph->AllNodes + * @param CopiedNodes The nodes copied in this copy + */ + virtual void PostCopyNode(const TArray& CopiedNodes) {} + //~ End UVoxelNode Interface + +#if WITH_EDITOR + //~ Begin UObject Interface + virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override; + virtual void PostLoad() override; + //~ End UObject Interface +#endif //WITH_EDITOR + +protected: + void UpdatePreview(bool bReconstructNode) const; + +private: + bool IsOutdated() const; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelAssetPickerNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelAssetPickerNode.h new file mode 100644 index 00000000..76c83598 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelAssetPickerNode.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelExposedNodes.h" +#include "VoxelAssetPickerNode.generated.h" + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelAssetPickerNode : public UVoxelExposedNode +{ + GENERATED_BODY() +public: + //~ Begin UVoxelAssetPickerNode Interface + virtual UObject* GetAsset() const { return nullptr; } + virtual UClass* GetAssetClass() const { return nullptr; } + virtual void SetAsset(UObject* Object) {} + virtual bool ShouldFilterAsset(const struct FAssetData& Asset) const { return false; } + //~ End UVoxelAssetPickerNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBinaryNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBinaryNodes.h new file mode 100644 index 00000000..c1c38006 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBinaryNodes.h @@ -0,0 +1,142 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelBinaryNodes.generated.h" + +// A < B +UCLASS(DisplayName = "float < float", Category = "Math|Float", meta = (Keywords = "< less")) +class VOXELGRAPH_API UVoxelNode_FLess : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("<") + + UVoxelNode_FLess(); +}; + +// A <= B +UCLASS(DisplayName = "float <= float", Category = "Math|Float", meta = (Keywords = "<= less")) +class VOXELGRAPH_API UVoxelNode_FLessEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("<=") + + UVoxelNode_FLessEqual(); +}; + +// A > B +UCLASS(DisplayName = "float > float", Category = "Math|Float", meta = (Keywords = "> greater")) +class VOXELGRAPH_API UVoxelNode_FGreater : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE(">") + + UVoxelNode_FGreater(); +}; + +// A >= B +UCLASS(DisplayName = "float >= float", Category = "Math|Float", meta = (Keywords = ">= greater")) +class VOXELGRAPH_API UVoxelNode_FGreaterEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE(">=") + + UVoxelNode_FGreaterEqual(); +}; + +// A == B +UCLASS(DisplayName = "float == float", Category = "Math|Float", meta = (Keywords = "== equal")) +class VOXELGRAPH_API UVoxelNode_FEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("==") + + UVoxelNode_FEqual(); +}; + +// A != B +UCLASS(DisplayName = "float != float", Category = "Math|Float", meta = (Keywords = "!= not equal")) +class VOXELGRAPH_API UVoxelNode_FNotEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("!=") + + UVoxelNode_FNotEqual(); +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// A < B +UCLASS(DisplayName = "int < int", Category = "Math|Integer", meta = (Keywords = "< less")) +class VOXELGRAPH_API UVoxelNode_ILess : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("<") + + UVoxelNode_ILess(); +}; + +// A <= B +UCLASS(DisplayName = "int <= int", Category = "Math|Integer", meta = (Keywords = "<= less")) +class VOXELGRAPH_API UVoxelNode_ILessEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("<=") + + UVoxelNode_ILessEqual(); +}; + +// A > B +UCLASS(DisplayName = "int > int", Category = "Math|Integer", meta = (Keywords = "> greater")) +class VOXELGRAPH_API UVoxelNode_IGreater : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE(">") + + UVoxelNode_IGreater(); +}; + +// A >= B +UCLASS(DisplayName = "int >= int", Category = "Math|Integer", meta = (Keywords = ">= greater")) +class VOXELGRAPH_API UVoxelNode_IGreaterEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE(">=") + + UVoxelNode_IGreaterEqual(); +}; + +// A == B +UCLASS(DisplayName = "int == int", Category = "Math|Integer", meta = (Keywords = "== equal")) +class VOXELGRAPH_API UVoxelNode_IEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("==") + + UVoxelNode_IEqual(); +}; + +// A != B +UCLASS(DisplayName = "int != int", Category = "Math|Integer", meta = (Keywords = "!= not equal")) +class VOXELGRAPH_API UVoxelNode_INotEqual : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("!=") + + UVoxelNode_INotEqual(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMapNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMapNode.h new file mode 100644 index 00000000..65439495 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMapNode.h @@ -0,0 +1,55 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelExposedNodes.h" +#include "VoxelBiomeMapNode.generated.h" + +class UTexture2D; + +USTRUCT() +struct VOXELGRAPH_API FBiomeMapElement +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + FColor Color; + + UPROPERTY(EditAnywhere, Category = "Voxel") + FString Name; +}; + +// Find the strength of biomes from a biome map. Note: Alpha is ignored when computing the color distance +UCLASS(DisplayName = "Biome Map Sampler", Category = "Biomes") +class VOXELGRAPH_API UVoxelNode_BiomeMapSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Biomes") + UTexture2D* Texture; + + // Distance = Max(Abs(ColorA - ColorB)). Values with a distance below or equal to this will be set to 1, value strictly above to 0 + UPROPERTY(EditAnywhere, Category = "Biomes", meta = (ClampMin = 0, ClampMax = 255, UIMin = 0, UIMax = 255)) + int Threshold = 0; + + UPROPERTY(EditAnywhere, Category = "Biomes", meta = (ReconstructNode)) + TArray Biomes; + + TArray GetColors() const; + + UVoxelNode_BiomeMapSampler(); + + virtual int32 GetOutputPinsCount() const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual FText GetTitle() const override; + + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Texture); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h new file mode 100644 index 00000000..a7959d16 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h @@ -0,0 +1,42 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "VoxelNodeStructs.h" +#include "VoxelBiomeMergeNode.generated.h" + +// Merge biomes by generating nodes to do so. +// Will also generate function separators, so you need to make all your data go through this +// (check the Additional Data field) +UCLASS(DisplayName = "Biome Merge", Category = "Biomes") +class VOXELGRAPH_API UVoxelNode_BiomeMerge : public UVoxelNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Biomes", meta = (ReconstructNode)) + TArray Biomes; + + UPROPERTY(EditAnywhere, Category = "Config") + float Tolerance = 0.00001; + + UVoxelNode_BiomeMerge() = default; + + virtual int32 GetMinInputPins() const override; + virtual int32 GetMaxInputPins() const override; + virtual int32 GetOutputPinsCount() const override; + + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + + virtual TSharedPtr GetCompilationNode() const override; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h new file mode 100644 index 00000000..8bffb2d1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h @@ -0,0 +1,63 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelConstantNodes.generated.h" + +// Returns the current LOD +UCLASS(DisplayName = "LOD", Category = "Constants") +class VOXELGRAPH_API UVoxelNode_LOD : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_LOD(); +}; + +// Voxel Size +UCLASS(DisplayName = "Voxel Size", Category = "Constants") +class VOXELGRAPH_API UVoxelNode_VoxelSize : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VoxelSize(); +}; + +// World Size +UCLASS(DisplayName = "World Size", Category = "Constants") +class VOXELGRAPH_API UVoxelNode_WorldSize : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_WorldSize(); +}; + +// Use this to access compilation constants such as preview size, target... +UCLASS(DisplayName = "Compile-Time Constant", Category = "Constants", meta = (Keywords = "is")) +class VOXELGRAPH_API UVoxelNode_CompileTimeConstant : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + FName Name = ""; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ReconstructNode)) + EVoxelPinCategory Type = EVoxelPinCategory::Boolean; + + UPROPERTY(Transient, VisibleAnywhere, Category = "Voxel") + TMap Constants; + + UVoxelNode_CompileTimeConstant(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCoordinatesNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCoordinatesNodes.h new file mode 100644 index 00000000..5372582f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCoordinatesNodes.h @@ -0,0 +1,130 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelCoordinatesNodes.generated.h" + +UCLASS(Abstract, Category = "Coordinates") +class VOXELGRAPH_API UVoxelCoordinateNode : public UVoxelNodeWithDependencies +{ + GENERATED_BODY() + +public: + UVoxelCoordinateNode(); +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// Return the current X +UCLASS(DisplayName = "X", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_XF : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_XF(); + virtual uint8 GetNodeDependencies() const override; +}; + +// Return the current Y +UCLASS(DisplayName = "Y", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_YF : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_YF(); + virtual uint8 GetNodeDependencies() const override; +}; + +// Return the current Z +UCLASS(DisplayName = "Z", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_ZF : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_ZF(); + virtual uint8 GetNodeDependencies() const override; +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// Return the current X in global space, before the transform is applied to it. Same as X if not a graph asset +UCLASS(DisplayName = "Global X", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_GlobalX : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GlobalX(); + virtual uint8 GetNodeDependencies() const override; +}; + +// Return the current Y in global space, before the transform is applied to it. Same as Y if not a graph asset +UCLASS(DisplayName = "Global Y", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_GlobalY : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GlobalY(); + virtual uint8 GetNodeDependencies() const override; +}; + +// Return the current Z in global space, before the transform is applied to it. Same as Z if not a graph asset +UCLASS(DisplayName = "Global Z", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_GlobalZ : public UVoxelCoordinateNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GlobalZ(); + virtual uint8 GetNodeDependencies() const override; +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// Transform coordinates from local voxel space to global voxel space. Used for graph assets +UCLASS(DisplayName = "Local To Global", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_LocalToGlobal : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_LocalToGlobal(); +}; + +// Transform coordinates from global voxel space to local voxel space. Used for graph assets +UCLASS(DisplayName = "Global To Local", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_GlobalToLocal : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GlobalToLocal(); +}; + +// Transform vector from local voxel space to global voxel space. Used for graph assets +UCLASS(DisplayName = "Transform Vector", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_TransformVector : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_TransformVector(); +}; + +// Transform vector from global voxel space to local voxel space. Used for graph assets +UCLASS(DisplayName = "Inverse Transform Vector", Category = "Coordinates") +class VOXELGRAPH_API UVoxelNode_InverseTransformVector : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_InverseTransformVector(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCurveNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCurveNodes.h new file mode 100644 index 00000000..acb3a416 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelCurveNodes.h @@ -0,0 +1,45 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelExposedNodes.h" +#include "VoxelCurveNodes.generated.h" + +class UCurveFloat; +class UCurveLinearColor; + +// Apply a float curve +UCLASS(DisplayName = "Float Curve", Category = "Curve") +class VOXELGRAPH_API UVoxelNode_Curve : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (NonNull)) + UCurveFloat* Curve; + + UVoxelNode_Curve(); + + virtual FText GetTitle() const override; + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Curve); } +}; + +// Apply a color curve +// TODO option to output color +UCLASS(DisplayName = "Color Curve", Category = "Curve") +class VOXELGRAPH_API UVoxelNode_CurveColor : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (NonNull)) + UCurveLinearColor* Curve; + + UVoxelNode_CurveColor(); + + virtual FText GetTitle() const override; + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Curve); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDataAssetSamplerNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDataAssetSamplerNode.h new file mode 100644 index 00000000..c236ab7c --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDataAssetSamplerNode.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelExposedNodes.h" +#include "VoxelDataAssetSamplerNode.generated.h" + +class UVoxelDataAsset; + +// Voxel data asset sampler +UCLASS(DisplayName = "Data Asset Sampler", Category = "Heightmap") +class VOXELGRAPH_API UVoxelNode_DataAssetSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config", meta = (NonNull)) + UVoxelDataAsset* Asset; + + UPROPERTY(EditAnywhere, Category = "Config", meta = (ReconstructNode)) + bool bBilinearInterpolation = true; + + UVoxelNode_DataAssetSampler(); + + //~ Begin UVoxelNode Interface + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Asset); } + //~ End UVoxelExposedNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDeprecatedNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDeprecatedNodes.h new file mode 100644 index 00000000..ed637207 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelDeprecatedNodes.h @@ -0,0 +1,177 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelGraphGlobals.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelGraphErrorReporter.h" + +#include "VoxelGenerators/VoxelGeneratorPicker.h" + +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelNodes/VoxelExposedNodes.h" +#include "VoxelNodes/VoxelMaterialNodes.h" + +#include "VoxelDeprecatedNodes.generated.h" + +UCLASS(DisplayName = "Make Material From Single Index", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_MakeMaterialFromSingleIndex : public UVoxelMaterialNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use Set Single Index") + + UVoxelNode_MakeMaterialFromSingleIndex() + { + SetInputs(EC::Int, EC::Float, EC::Float, EC::Float); + SetOutputs(EC::Material); + } +}; + +UCLASS(DisplayName = "Get Double Index", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_GetDoubleIndex : public UVoxelMaterialNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use Multi Index") + + UVoxelNode_GetDoubleIndex() + { + SetInputs({ "Material", EC::Material, "Material" }); + SetOutputs( + { "Index A", EC::Int, "Index A between 0 and 255" }, + { "Index B", EC::Int, "Index B between 0 and 255" }, + { "Blend", EC::Float, "Blend factor, between 0 and 1" }, + { "Data", EC::Float, "Data sent to material shader" }); + } +}; + +UCLASS(DisplayName = "Make Material From Color", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_MakeMaterialFromColor : public UVoxelMaterialNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use SetColor") + + UVoxelNode_MakeMaterialFromColor() + { + SetInputs({ "Color", EC::Color, "Color" }); + SetOutputs(EC::Material); + } +}; + +UCLASS(DisplayName = "Make Material From Double Index", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_MakeMaterialFromDoubleIndex : public UVoxelMaterialNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use Multi Index") + + UVoxelNode_MakeMaterialFromDoubleIndex() +{ + SetInputs( + { "Index A", EC::Int, "Index A between 0 and 255", "", {0, 255} }, + { "Index B", EC::Int, "Index B between 0 and 255", "", {0, 255} }, + { "Blend", EC::Float, "Blend factor, between 0 and 1", "", {0, 1} }, + { "Data", EC::Float, "Data to send to the material shader", "", {0, 1} }); + SetOutputs(EC::Material); +} +}; + +UCLASS(DisplayName = "Create Double Index Material", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_CreateDoubleIndexMaterial : public UVoxelMaterialNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use Multi Index") + + UVoxelNode_CreateDoubleIndexMaterial() + { + SetInputsCount(3, MAX_VOXELNODE_PINS); + SetInputIncrement(2); + SetOutputs(EC::Material); + } +}; + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(DisplayName = "Set Double Index", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_SetDoubleIndex : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use MultiIndex nodes instead") + + UVoxelNode_SetDoubleIndex() + { + SetInputs( + EC::Exec, + { "Index A", EC::Int, "Index A between 0 and 255", "", {0, 255} }, + { "Index B", EC::Int, "Index B between 0 and 255", "", {0, 255} }, + { "Blend", EC::Float, "Blend between 0 and 1", "", {0, 1} }, + { "Data", EC::Float, "Data sent to material shader", "", {0, 1} }); + SetOutputs(EC::Exec); + } +}; + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(DisplayName = "Generator Sampler", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_WorldGeneratorSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("use Get Generator Value / Get Generator Material instead") + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + FVoxelGeneratorPicker WorldGenerator; + + UPROPERTY(EditAnywhere, Category = "Voxel") + TArray Seeds; +}; + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(DisplayName = "X (int)", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_XI : public UVoxelNodeHelper +{ + GENERATED_BODY() + SET_VOXELNODE_TITLE("X") + DEPRECATED_VOXELNODE("please use the float version instead") + + UVoxelNode_XI() { SetOutputs(EC::Int); } +}; + +UCLASS(DisplayName = "Y (int)", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_YI : public UVoxelNodeHelper +{ + GENERATED_BODY() + SET_VOXELNODE_TITLE("Y") + DEPRECATED_VOXELNODE("please use the float version instead") + + UVoxelNode_YI() { SetOutputs(EC::Int); } +}; + +UCLASS(DisplayName = "Z (int)", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_ZI : public UVoxelNodeHelper +{ + GENERATED_BODY() + SET_VOXELNODE_TITLE("Z") + DEPRECATED_VOXELNODE("please use the float version instead") + + UVoxelNode_ZI() { SetOutputs(EC::Int); } +}; + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(DisplayName = "Perlin Worm Distance", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_PerlinWormDistance : public UVoxelNodeHelper +{ + GENERATED_BODY() + DEPRECATED_VOXELNODE("Use DataItemSample instead") + + UVoxelNode_PerlinWormDistance() + { + SetInputs( + { "X", EC::Float, "X" }, + { "Y", EC::Float, "Y" }, + { "Z", EC::Float, "Z" }); + SetOutputs(EC::Float); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h new file mode 100644 index 00000000..87046fcd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h @@ -0,0 +1,179 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelNodeStructs.h" +#include "VoxelGraphOutputs.h" +#include "VoxelGraphConstants.h" +#include "VoxelExecNodes.generated.h" + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_MaterialSetter : public UVoxelSetterNode +{ + GENERATED_BODY() + +public: + virtual int32 GetOutputIndex() const override; +}; + +// Set the color at that position. Inputs between 0 and 1 +// Will not work in multi index! +// In single index, Alpha will be ignored (as it's used for the index) +UCLASS(DisplayName = "Set Color") +class VOXELGRAPH_API UVoxelNode_SetColor : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SetColor(); +}; + +// Set the material index at that position. Input clamped between 0 and 255. +UCLASS(DisplayName = "Set Single Index") +class VOXELGRAPH_API UVoxelNode_SetSingleIndex : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SetSingleIndex(); +}; + +// Set the multi index wetness as that position, between 0 and 1. Wetness can be queried using the GetMultiIndexWetness material function in your shader. +UCLASS(DisplayName = "Set Multi Index Wetness") +class VOXELGRAPH_API UVoxelNode_SetMultiIndexWetness : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SetMultiIndexWetness(); +}; + +// Add multi index with the specified strength +// The strength will be normalized according to the other strengths set, except if Lock Strength is true +UCLASS(DisplayName = "Add Multi Index") +class VOXELGRAPH_API UVoxelNode_AddMultiIndex : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_AddMultiIndex(); +}; + +// Set the material additional UVs channels +// By default the plugin has 2 UV channels that can be queried using TexCoord[1] and TexCoord[2] in the material +// Values should be between 0.f and 1.f +// Index should be 0 or 1 (or 2/3 if you enabled them in VoxelUserDefinitions.h) +// UVs 0 and 1 will not be set in MultiIndex! +UCLASS(DisplayName = "Set UV Channel") +class VOXELGRAPH_API UVoxelNode_SetUVs : public UVoxelNode_MaterialSetter +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bSetU = true; + + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bSetV = true; + + UVoxelNode_SetUVs(); +}; + +UCLASS(DisplayName = "Set Node", NotPlaceable) +class VOXELGRAPH_API UVoxelNode_SetNode : public UVoxelSetterNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UVoxelNode_SetNode(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual FName GetInputPinName(int32 PinIndex) const override; + + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelSetterNode Interface + virtual int32 GetOutputIndex() const override; + //~ End UVoxelSetterNode Interface + +#if WITH_EDITOR +public: + // Returns: if valid + bool UpdateSetterNode(); + void SetIndex(uint32 NewIndex); + +protected: + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void PostLoad() override; +#endif + +protected: + UPROPERTY() + uint32 Index; + + UPROPERTY() + FVoxelGraphOutput CachedOutput; +}; + +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelNode_SetValueNode : public UVoxelNode_SetNode +{ + GENERATED_BODY() + +public: + UVoxelNode_SetValueNode() + { + Index = FVoxelGraphOutputsIndices::ValueIndex; + } +}; + +// Break the graph into multiple functions +UCLASS(DisplayName = "Function Separator", Category = "Flow Control") +class VOXELGRAPH_API UVoxelNode_FunctionSeparator : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UVoxelNode_FunctionSeparator(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +// Merges exec flow +UCLASS(DisplayName = "Flow Merge", Category = "Flow Control") +class VOXELGRAPH_API UVoxelNode_FlowMerge : public UVoxelNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ReconstructNode)) + TArray Types = { {EVoxelDataPinCategory::Float, "Value"} }; + + //~ Begin UVoxelNode Interface + virtual FLinearColor GetColor() const override; + + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + + virtual int32 GetMinInputPins() const override; + virtual int32 GetMaxInputPins() const override; + + virtual int32 GetOutputPinsCount() const override; + + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExposedNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExposedNodes.h new file mode 100644 index 00000000..599a6b68 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelExposedNodes.h @@ -0,0 +1,89 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelExposedNodes.generated.h" + +UCLASS(Abstract, Category = "Parameters") +class VOXELGRAPH_API UVoxelExposedNode : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + FString DisplayName; + + UPROPERTY(VisibleAnywhere, Category = "Parameter Settings") + FName UniqueName; + + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + FString Category; + + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + FString Tooltip; + + // Lowest values on top + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + int32 Priority; + + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + FString UIMin; + + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + FString UIMax; + + UPROPERTY(EditAnywhere, Category = "Parameter Settings") + TMap CustomMetaData; + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const { ensure(false); return {}; } + virtual TMap GetMetaData() const; + //~ End UVoxelExposedNode Interface + + //~ Begin UVoxelNode Interface + virtual FLinearColor GetColor() const override; + virtual FText GetTitle() const override; + virtual bool CanRenameNode() const override; + virtual FString GetEditableName() const override; + virtual void SetEditableName(const FString& NewName) override; + virtual void ApplyParameters(const TMap& Parameters) override; + virtual void GetParameters(TArray& OutParameters) const override; + //~ End UVoxelNode Interface + +public: + struct FDummy + { + static UScriptStruct* Get() { return nullptr; } + }; + + template + T GetParameter() const + { + T Temp{}; + const void* Result = GetParameterInternal(static_cast(&Temp), TChooseClass, TIsPointer>::Value, FDummy, TBaseStructure>::Result::Get()); + check(Result); + + return *static_cast(Result); + } + +protected: + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual void PostEditImport() override; + virtual void PostLoad() override; + //~ End UObject Interface + +private: + // Only allow renaming on creation, else the name is wrong (GetTitle never called) + UPROPERTY() + bool bCanBeRenamed = true; + + void MakeNameUnique(); + const void* GetParameterInternal(void* Temp, UScriptStruct* Struct) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h new file mode 100644 index 00000000..07707417 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h @@ -0,0 +1,81 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNoiseNodes.h" +#include "VoxelNoiseNodesMacros.h" +#include "VoxelGavoronoiNoiseNode.generated.h" + +// 2D Gavoronoi Noise +// This noise can be directed, and is used to fake erosion +// See https://www.shadertoy.com/view/llsGWl +UCLASS(DisplayName = "2D Gavoronoi Noise", Category = "Noise|Gavoronoi Noise") +class VOXELGRAPH_API UVoxelNode_2DGavoronoiNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DGavoronoiNoise(); + + UPROPERTY(EditAnywhere, Category = "Gavoronoi Noise settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.45; + + //~ Begin UVoxelNode_NoiseNode Interface + virtual uint32 GetDimension() const override { return 2; } + virtual int32 SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const override; + //~ End UVoxelNode_NoiseNode Interface +}; + +// 2D Gavoronoi Noise Fractal +// This noise can be directed, and is used to fake erosion +// See https://www.shadertoy.com/view/llsGWl +UCLASS(DisplayName = "2D Gavoronoi Noise Fractal", Category = "Noise|Gavoronoi Noise") +class VOXELGRAPH_API UVoxelNode_2DGavoronoiNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DGavoronoiNoiseFractal(); + + UPROPERTY(EditAnywhere, Category = "Gavoronoi Noise settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.45; + + //~ Begin UVoxelNode_NoiseNode Interface + virtual uint32 GetDimension() const override { return 2; } + virtual int32 SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const override; + //~ End UVoxelNode_NoiseNode Interface +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// 2D Erosion +// Add this to your noise to fake erosion +// From https://www.shadertoy.com/view/MtGcWh +UCLASS(DisplayName = "2D Erosion", Category = "Noise|Erosion") +class VOXELGRAPH_API UVoxelNode_2DErosion : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DErosion(); + +public: + // Controls the jitter of the noise used for the "ravines" + UPROPERTY(EditAnywhere, Category = "Erosion settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.25; + +public: + //~ Begin UVoxelNode_NoiseNode Interface + virtual uint32 GetDimension() const override { return 2; } + virtual void ComputeOutputRanges() override; + //~ End UVoxelNode_NoiseNode Interface + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual bool CanEditChange(const FProperty* InProperty) const override; +#endif + //~ End UObject Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorMergeNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorMergeNode.h new file mode 100644 index 00000000..f7168ea8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorMergeNode.h @@ -0,0 +1,43 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGeneratorSamplerNodes.h" +#include "VoxelGeneratorMergeNode.generated.h" + +class UVoxelGraphOutputsConfig; + +UCLASS(DisplayName = "Generator Merge", Category = "Generator") +class VOXELGRAPH_API UVoxelNode_GeneratorMerge : public UVoxelNode_GeneratorSamplerBase +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + UVoxelGraphOutputsConfig* Outputs; + + UPROPERTY(EditAnywhere, Category = "Config") + EVoxelMaterialConfig MaterialConfig; + + UPROPERTY(EditAnywhere, Category = "Config") + TArray Generators; + + UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Config") + float Tolerance = 0.00001; + + UVoxelNode_GeneratorMerge(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + virtual int32 GetOutputPinsCount() const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + //~ End UVoxelNode Interface + + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Generators); } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h new file mode 100644 index 00000000..225f07a4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h @@ -0,0 +1,77 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelExposedNodes.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGeneratorSamplerNodes.generated.h" + +UCLASS(Abstract, Category = "Generator") +class VOXELGRAPH_API UVoxelNode_GeneratorSamplerBase : public UVoxelExposedNode +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_SingleGeneratorSamplerBase : public UVoxelNode_GeneratorSamplerBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config") + FVoxelGeneratorPicker Generator; + + UVoxelNode_SingleGeneratorSamplerBase(); + + //~ Begin UVoxelNode Interface + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Generator); } + //~ End UVoxelExposedNode Interface +}; + +UCLASS(DisplayName = "Get Generator Value") +class VOXELGRAPH_API UVoxelNode_GetGeneratorValue : public UVoxelNode_SingleGeneratorSamplerBase +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetGeneratorValue(); +}; + +UCLASS(DisplayName = "Get Generator Material") +class VOXELGRAPH_API UVoxelNode_GetGeneratorMaterial : public UVoxelNode_SingleGeneratorSamplerBase +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetGeneratorMaterial(); +}; + +UCLASS(DisplayName = "Get Generator Custom Output") +class VOXELGRAPH_API UVoxelNode_GetGeneratorCustomOutput : public UVoxelNode_SingleGeneratorSamplerBase +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetGeneratorCustomOutput(); + + UPROPERTY(EditAnywhere, Category = "Config") + FName OutputName = "Value"; + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGetMaterialCollectionIndexNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGetMaterialCollectionIndexNode.h new file mode 100644 index 00000000..797368e0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGetMaterialCollectionIndexNode.h @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAssetPickerNode.h" +#include "VoxelGetMaterialCollectionIndexNode.generated.h" + +class UMaterialInterface; + +// Retrieve the index of a material function or a material instance in the voxel world material collection +UCLASS(DisplayName = "Get Material Collection Index", Category = "Material") +class VOXELGRAPH_API UVoxelNode_GetMaterialCollectionIndex : public UVoxelAssetPickerNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UPROPERTY(EditAnywhere, Category = "Config", meta = (ReconstructNode)) + UMaterialInterface* Material; + + UVoxelNode_GetMaterialCollectionIndex(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelAssetPickerNode Interface + virtual UObject* GetAsset() const override; + virtual UClass* GetAssetClass() const override; + virtual void SetAsset(UObject* Object) override; + virtual bool ShouldFilterAsset(const FAssetData& Asset) const override; + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Material); } + //~ End UVoxelAssetPickerNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGradientPerturbNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGradientPerturbNodes.h new file mode 100644 index 00000000..7bc1e3fb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGradientPerturbNodes.h @@ -0,0 +1,93 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNoiseNodesBase.h" +#include "VoxelGradientPerturbNodes.generated.h" + +template +class TVoxelGradientPerturbHelper : public Parent +{ +public: + using Parent::Parent; + + //~ Begin UVoxelNode Interface + virtual FName GetInputPinName(int32 PinIndex) const override { return UVoxelNodeHelper::GetInputPinName(PinIndex); } + virtual FName GetOutputPinName(int32 PinIndex) const override { return UVoxelNodeHelper::GetOutputPinName(PinIndex); } + virtual FString GetInputPinToolTip(int32 PinIndex) const override { return UVoxelNodeHelper::GetInputPinToolTip(PinIndex); } + virtual FString GetOutputPinToolTip(int32 PinIndex) const override { return UVoxelNodeHelper::GetOutputPinToolTip(PinIndex); } + virtual int32 GetMinInputPins() const override { return UVoxelNodeHelper::GetMinInputPins(); } + virtual int32 GetMaxInputPins() const override { return UVoxelNodeHelper::GetMaxInputPins(); } + virtual int32 GetOutputPinsCount() const override { return UVoxelNodeHelper::GetOutputPinsCount(); } + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override { return UVoxelNodeHelper::GetInputPinCategory(PinIndex); } + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override { return UVoxelNodeHelper::GetOutputPinCategory(PinIndex); } + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override { return UVoxelNodeHelper::GetInputPinDefaultValue(PinIndex); } + //~ End UVoxelNode Interface + + //~ Begin UVoxelNode_NoiseNode Interface + virtual bool NeedRangeAnalysis() const override { return false; } + //~ End UVoxelNode_NoiseNode Interface +}; + +#define UVoxelNode_NoiseNode TVoxelGradientPerturbHelper +#define UVoxelNode_NoiseNodeFractal TVoxelGradientPerturbHelper + +UCLASS(Abstract, Category = "Noise|Gradient Perturb") +class VOXELGRAPH_API UVoxelNode_GradientPerturb : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() +}; + +UCLASS(Abstract, Category = "Noise|Gradient Perturb") +class VOXELGRAPH_API UVoxelNode_GradientPerturbFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() +}; + +#undef UVoxelNode_NoiseNode +#undef UVoxelNode_NoiseNodeFractal + +// 2D Gradient Perturb +UCLASS(DisplayName = "2D Gradient Perturb") +class VOXELGRAPH_API UVoxelNode_2DGradientPerturb : public UVoxelNode_GradientPerturb +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DGradientPerturb(); + virtual uint32 GetDimension() const override { return 2; } +}; + +// 2D Gradient Perturb Fractal +UCLASS(DisplayName = "2D Gradient Perturb Fractal") +class VOXELGRAPH_API UVoxelNode_2DGradientPerturbFractal : public UVoxelNode_GradientPerturbFractal +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DGradientPerturbFractal(); + virtual uint32 GetDimension() const override { return 2; } +}; + +// 3D Gradient Perturb +UCLASS(DisplayName = "3D Gradient Perturb") +class VOXELGRAPH_API UVoxelNode_3DGradientPerturb : public UVoxelNode_GradientPerturb +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_3DGradientPerturb(); + virtual uint32 GetDimension() const override { return 3; } +}; + +// 3D Gradient Perturb Fractal +UCLASS(DisplayName = "3D Gradient Perturb Fractal") +class VOXELGRAPH_API UVoxelNode_3DGradientPerturbFractal : public UVoxelNode_GradientPerturbFractal +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_3DGradientPerturbFractal(); + virtual uint32 GetDimension() const override { return 3; } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphAssetNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphAssetNodes.h new file mode 100644 index 00000000..deed06b7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphAssetNodes.h @@ -0,0 +1,76 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelGenerators/VoxelGeneratorPicker.h" +#include "VoxelGraphAssetNodes.generated.h" + +UCLASS(Abstract, Category = "Graph Asset") +class VOXELGRAPH_API UVoxelGraphAssetNode : public UVoxelNodeWithContext +{ + GENERATED_BODY() + +public: + UVoxelGraphAssetNode() = default; + + // Generator to sample from when not used as an asset. Useful to preview. Not used when compiled to C++ + UPROPERTY(EditAnywhere, Category = "Preview", meta = (ReconstructNode)) + FVoxelGeneratorPicker DefaultGenerator; + + //~ Begin UVoxelNode Interface + virtual int32 GetMaxInputPins() const override; + //~ End UVoxelNode Interface +}; + +// Get the previous generator value. Only for graph assets +UCLASS(DisplayName= "Get Previous Generator Value") +class VOXELGRAPH_API UVoxelNode_EditGetValue : public UVoxelGraphAssetNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_EditGetValue(); +}; + +// Get the previous generator material. Only for graph assets +UCLASS(DisplayName= "Get Previous Generator Material") +class VOXELGRAPH_API UVoxelNode_EditGetMaterial : public UVoxelGraphAssetNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UVoxelNode_EditGetMaterial(); +}; + +// Get the previous generator custom output. Only for graph assets +UCLASS(DisplayName= "Get Previous Generator Custom Output") +class VOXELGRAPH_API UVoxelNode_EditGetCustomOutput : public UVoxelGraphAssetNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UVoxelNode_EditGetCustomOutput(); + + UPROPERTY(EditAnywhere, Category = "Config") + FName OutputName = "Value"; + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface +}; + +// Get the material hardness +UCLASS(DisplayName= "Get Hardness", Category = "Material") +class VOXELGRAPH_API UVoxelNode_EditGetHardness : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UVoxelNode_EditGetHardness(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h new file mode 100644 index 00000000..b1f8a6ab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h @@ -0,0 +1,161 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphGenerator.h" +#include "VoxelNode.h" +#include "VoxelGraphMacro.generated.h" + +USTRUCT() +struct FVoxelGraphMacroPin +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + FString Name; + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelPinCategory Category; + + UPROPERTY(EditAnywhere, Category = "Voxel") + FString ToolTip; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (EditCondition = bCustomDefaultValue)) + FString DefaultValue; + + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (InlineEditConditionToggle)) + bool bCustomDefaultValue = false; + + inline FString GetDefaultValueEqual() const + { + return (DefaultValue.IsEmpty() || !bCustomDefaultValue) ? "" : (" = " + DefaultValue); + } + + inline FString GetDefaultValue() const + { + return bCustomDefaultValue ? DefaultValue : ""; + } +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelGraphMacroInputOutputNode : public UVoxelNode +{ + GENERATED_BODY() +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ReconstructNode)) + TArray Pins; + + UPROPERTY() + class UVoxelGraphMacro* Macro; + + virtual FLinearColor GetColor() const override; + virtual bool CanUserDeleteNode() const override; + virtual bool CanDuplicateNode() const override; + + virtual int32 GetMaxInputPins() const override; + virtual int32 GetMinInputPins() const override; + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + + virtual int32 GetOutputPinsCount() const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + + virtual TSharedPtr GetCompilationNode() const override; + +#if WITH_EDITOR + void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + void PostLoad() override; +#endif // WITH_EDITOR +}; + +UCLASS(DisplayName = "Input", NotPlaceable) +class VOXELGRAPH_API UVoxelGraphMacroInputNode : public UVoxelGraphMacroInputOutputNode +{ + GENERATED_BODY() + + FName GetInputPinName(int32 PinIndex) const override { return *(Pins[PinIndex].Name + Pins[PinIndex].GetDefaultValueEqual()); } + FName GetOutputPinName(int32 PinIndex) const override { return *Pins[PinIndex].Name; } +}; + +UCLASS(DisplayName = "Output", NotPlaceable) +class VOXELGRAPH_API UVoxelGraphMacroOutputNode : public UVoxelGraphMacroInputOutputNode +{ + GENERATED_BODY() + + FName GetInputPinName(int32 PinIndex) const override { return *Pins[PinIndex].Name; } + FName GetOutputPinName(int32 PinIndex) const override { return *Pins[PinIndex].Name; } +}; + +/** + * A graph macro + */ +UCLASS(BlueprintType, HideCategories = (Object), HideDropdown) +class VOXELGRAPH_API UVoxelGraphMacro : public UVoxelGraphGenerator +{ + GENERATED_BODY() +public: + // Shift+Enter for new line + UPROPERTY(EditAnywhere, Category = "Macro Config", meta = (MultiLine = true, DisplayName = "Tooltip (Shift+Enter for new line)")) + FString Tooltip; + + UPROPERTY(EditAnywhere, Category = "Macro Config") + FString Keywords; + + // If empty the Macro Nodes category is used + UPROPERTY(EditAnywhere, Category = "Macro Config") + FString CustomCategory; + + // If empty the asset name is used + UPROPERTY(EditAnywhere, Category = "Macro Config") + FString CustomName; + + UPROPERTY(EditAnywhere, Category = "Macro Config") + bool bShowInContextMenu = true; + + UPROPERTY(EditAnywhere, Category = "Macro Config") + bool bVectorOnlyNode = false; + + UPROPERTY() + UVoxelGraphMacroInputNode* InputNode; + + UPROPERTY() + UVoxelGraphMacroOutputNode* OutputNode; + + FText GetMacroName() const; + FText GetMacroCategory() const; +}; + +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelGraphMacroNode : public UVoxelNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ReconstructNode)) + UVoxelGraphMacro* Macro; + + virtual TSharedPtr GetCompilationNode() const override; + + virtual FText GetTitle() const override; + virtual FText GetTooltip() const override; + + virtual int32 GetMaxInputPins() const override; + virtual int32 GetMinInputPins() const override; + virtual int32 GetOutputPinsCount() const override; + + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + + virtual FString GetInputPinToolTip(int32 PinIndex) const override; + virtual FString GetOutputPinToolTip(int32 PinIndex) const override; + + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override; + + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + + virtual void ApplyParameters(const TMap& Parameters) override; + virtual void GetParameters(TArray& OutParameters) const override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightSplitterNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightSplitterNode.h new file mode 100644 index 00000000..b8f9ec54 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightSplitterNode.h @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodes/VoxelNodeHelperMacros.h" +#include "VoxelHeightSplitterNode.generated.h" + +// Splits a float input based on different layers, and outputs the strength of each layer +// Input heights must be ordered! +UCLASS(DisplayName = "Height Splitter", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_HeightSplitter : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config", meta = (ReconstructNode), meta = (ClampMin = 1, ClampMax = 30)) + int32 NumSplits = 4; + + UVoxelNode_HeightSplitter(); + + //~ Begin UVoxelNode Interface + virtual int32 GetMinInputPins() const override; + virtual int32 GetMaxInputPins() const override; + virtual int32 GetOutputPinsCount() const override; + + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightmapSamplerNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightmapSamplerNode.h new file mode 100644 index 00000000..57789523 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightmapSamplerNode.h @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelExposedNodes.h" +#include "VoxelEnums.h" +#include "VoxelHeightmapSamplerNode.generated.h" + +class UVoxelHeightmapAssetFloat; +class UVoxelHeightmapAssetUINT16; + +// Heightmap sampler +UCLASS(DisplayName = "Heightmap Sampler", Category = "Heightmap") +class VOXELGRAPH_API UVoxelNode_HeightmapSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY() + bool bFloatHeightmap = false; + + UPROPERTY(EditAnywhere, Category = "Heightmap settings", meta = (DisplayName = "Heightmap (float)", EditCondition = "bFloatHeightmap")) + UVoxelHeightmapAssetFloat* HeightmapFloat; + + UPROPERTY(EditAnywhere, Category = "Heightmap settings", meta = (DisplayName = "Heightmap (uint16)", EditCondition = "!bFloatHeightmap")) + UVoxelHeightmapAssetUINT16* HeightmapUINT16; + + UPROPERTY(EditAnywhere, Category = "Heightmap settings") + EVoxelSamplerMode SamplerType = EVoxelSamplerMode::Tile; + + // If true, the heightmap will be centered + UPROPERTY(EditAnywhere, Category = "Heightmap settings") + bool bCenter = false; + + UVoxelNode_HeightmapSampler(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return bFloatHeightmap ? GET_OWN_MEMBER_NAME(HeightmapFloat) : GET_OWN_MEMBER_NAME(HeightmapUINT16); } + //~ End UVoxelExposedNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h new file mode 100644 index 00000000..40fe8b50 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h @@ -0,0 +1,76 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "Runtime/VoxelComputeNode.h" +#include "VoxelIfNode.generated.h" + +UENUM() +enum class EVoxelNodeIfBranchToUseForRangeAnalysis : uint8 +{ + None, + UseTrue, + UseFalse +}; + +// Branch node +UCLASS(DisplayName = "If", Category = "Flow Control", meta = (Keywords = "branch")) +class VOXELGRAPH_API UVoxelNode_If : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + // If the condition range analysis fails, use this branch instead of failing. + // DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING, ELSE YOUR WORLD WILL HAVE HOLES + UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Voxel") + EVoxelNodeIfBranchToUseForRangeAnalysis BranchToUseForRangeAnalysis = EVoxelNodeIfBranchToUseForRangeAnalysis::None; + +public: + UVoxelNode_If(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +// Helper +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelNode_IfWithDefaultToFalse : public UVoxelNode_If +{ + GENERATED_BODY() + +public: + UVoxelNode_IfWithDefaultToFalse() + { + BranchToUseForRangeAnalysis = EVoxelNodeIfBranchToUseForRangeAnalysis::UseFalse; + } +}; + +// Helper +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelNode_IfWithDefaultToTrue : public UVoxelNode_If +{ + GENERATED_BODY() + +public: + UVoxelNode_IfWithDefaultToTrue() + { + BranchToUseForRangeAnalysis = EVoxelNodeIfBranchToUseForRangeAnalysis::UseTrue; + } +}; + +class FVoxelIfComputeNode : public TVoxelExecComputeNodeHelper +{ +public: + const EVoxelNodeIfBranchToUseForRangeAnalysis BranchToUseForRangeAnalysis; + + FVoxelIfComputeNode(const UVoxelNode_If& Node, const FVoxelCompilationNode& CompilationNode) + : TVoxelExecComputeNodeHelper(Node, CompilationNode) + , BranchToUseForRangeAnalysis(Node.BranchToUseForRangeAnalysis) + { + } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h new file mode 100644 index 00000000..6da824d6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h @@ -0,0 +1,143 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "VoxelLocalVariables.generated.h" + +class UVoxelLocalVariableDeclaration; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelLocalVariableBase : public UVoxelNode +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNode Interface + virtual FLinearColor GetColor() const override { return FColorList::BakerChocolate; } + //~ End UVoxelNode Interface + +protected: + /** + * Find a variable declaration in an array of expressions + * @param VariableGuid The GUID of the variable to find + * @param Nodes The nodes to search in + * @return null if not found + */ + UVoxelLocalVariableDeclaration* FindDeclarationInArray(const FGuid& VariableGuid, const TArray& Nodes) const; + /** + * Find a variable declaration in the entire graph + * @param VariableGuid The GUID of the variable to find + * @return null if not found + */ + UVoxelLocalVariableDeclaration* FindDeclarationInGraph(const FGuid& VariableGuid) const; +}; + +UENUM() +enum class EVoxelPortalNodePinCategory : uint8 +{ + Boolean, + Int, + Float, + Material, + Color, + Seed +}; + +USTRUCT() +struct VOXELGRAPH_API FVoxelPortalNodeSelector +{ + GENERATED_BODY() + + UPROPERTY() + TWeakObjectPtr Input; +}; + +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelLocalVariableDeclaration : public UVoxelLocalVariableBase +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + FName Name = TEXT("Name"); + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelPortalNodePinCategory Category = EVoxelPortalNodePinCategory::Float; + + // The variable GUID, to support copy across graphs + UPROPERTY() + FGuid VariableGuid; + + EVoxelPinCategory GetCategory() const; + void SetCategory(EVoxelPinCategory NewCategory); + +public: + //~ Begin UObject Interface + virtual void PostInitProperties() override; + virtual void PostLoad() override; + virtual void PostDuplicate(bool bDuplicateForPIE) override; +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR + //~ End UObject Interface + + //~ Begin UVoxelNode Interface + virtual int32 GetMinInputPins() const override { return 1; } + virtual int32 GetMaxInputPins() const override { return 1; } + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual FText GetTitle() const override { return FText::FromName(Name); } + virtual TSharedPtr GetCompilationNode() const override; + virtual void PostCopyNode(const TArray& CopiedNodes) override; + virtual bool CanRenameNode() const override { return true; } + virtual FString GetEditableName() const override; + virtual void SetEditableName(const FString& NewName) override; + //~ End UVoxelNode Interface + +private: + /** + * Generates a GUID for the variable if one doesn't already exist + * @param bForceGeneration Whether we should generate a GUID even if it is already valid. + */ + void UpdateVariableGuid(bool bForceGeneration, bool bAllowMarkingPackageDirty); + void MakeNameUnique(); +}; + +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelLocalVariableUsage : public UVoxelLocalVariableBase +{ + GENERATED_BODY() + +public: + UPROPERTY() + FVoxelPortalNodeSelector Selector_DEPRECATED; + + // The declaration this node is linked to + UPROPERTY() + UVoxelLocalVariableDeclaration* Declaration; + + // The variable GUID, to support copy across graphs + UPROPERTY() + FGuid DeclarationGuid; + +public: + //~ Begin UObject Interface + virtual void PostLoad() override; + //~ End UObject Interface + + //~ Begin UVoxelNode Interface + virtual int32 GetOutputPinsCount() const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + + virtual FText GetTitle() const override; + + virtual TSharedPtr GetCompilationNode() const override; + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + virtual void PostCopyNode(const TArray& CopiedNodes) override; + //~ End UVoxelNode Interface + +private: + // Check that the declaration isn't deleted + bool IsDeclarationValid() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMaterialNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMaterialNodes.h new file mode 100644 index 00000000..5cfd65c5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMaterialNodes.h @@ -0,0 +1,46 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelMaterialNodes.generated.h" + +UCLASS(Abstract, Category = "Math|Material") +class VOXELGRAPH_API UVoxelMaterialNode : public UVoxelNodeHelper +{ + GENERATED_BODY() +}; + +// Get the material color. Outputs are between 0 and 1 +UCLASS(DisplayName = "Get Color") +class VOXELGRAPH_API UVoxelNode_GetColor : public UVoxelMaterialNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetColor(); + + // Note: Materials aren't supported by pure nodes +}; + +// Get the index of the material. Output is between 0 and 255 +UCLASS(DisplayName = "Get Single Index") +class VOXELGRAPH_API UVoxelNode_GetIndex : public UVoxelMaterialNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetIndex(); +}; + +// Read a UV channel from a material. +UCLASS(DisplayName = "Get UV Channel") +class VOXELGRAPH_API UVoxelNode_GetUVChannel : public UVoxelMaterialNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetUVChannel(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h new file mode 100644 index 00000000..d9bf54be --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h @@ -0,0 +1,754 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelMathNodes.generated.h" + +// Max +UCLASS(DisplayName = "Max (float)", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_FMax : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_FMax(); +}; + +// Min +UCLASS(DisplayName = "Min (float)", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_FMin : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_FMin(); +}; + +// Max +UCLASS(DisplayName = "Max (int)", Category = "Math|Integer") +class VOXELGRAPH_API UVoxelNode_IMax : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_IMax(); +}; + +// Min +UCLASS(DisplayName = "Min (int)", Category = "Math|Integer") +class VOXELGRAPH_API UVoxelNode_IMin : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_IMin(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Add +UCLASS(DisplayName = "float + float", Category = "Math|Float", meta = (Keywords = "+ add plus")) +class VOXELGRAPH_API UVoxelNode_FAdd : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("+") + + UVoxelNode_FAdd(); +}; + +// Multiply +UCLASS(DisplayName = "float * float", Category = "Math|Float", meta = (Keywords = "* multiply")) +class VOXELGRAPH_API UVoxelNode_FMultiply : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("*") + + UVoxelNode_FMultiply(); +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// Subtract +UCLASS(DisplayName = "float - float", Category = "Math|Float", meta = (Keywords = "- subtract minus")) +class VOXELGRAPH_API UVoxelNode_FSubstract : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("-") + + UVoxelNode_FSubstract(); +}; + +// Divide +UCLASS(DisplayName = "float / float", Category = "Math|Float", meta = (Keywords = "/ divide division")) +class VOXELGRAPH_API UVoxelNode_FDivide : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("/") + + UVoxelNode_FDivide(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Add +UCLASS(DisplayName = "int + int", Category = "Math|Integer", meta = (Keywords = "+ add plus")) +class VOXELGRAPH_API UVoxelNode_IAdd : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("+") + + UVoxelNode_IAdd(); +}; + +// Multiply +UCLASS(DisplayName = "int * int", Category = "Math|Integer", meta = (Keywords = "* multiply")) +class VOXELGRAPH_API UVoxelNode_IMultiply : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("*") + + UVoxelNode_IMultiply(); +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// Subtract +UCLASS(DisplayName = "int - int", Category = "Math|Integer", meta = (Keywords = "- subtract minus")) +class VOXELGRAPH_API UVoxelNode_ISubstract : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("-") + + UVoxelNode_ISubstract(); +}; + +// Divide +UCLASS(DisplayName = "int / int", Category = "Math|Integer", meta = (Keywords = "/ divide division")) +class VOXELGRAPH_API UVoxelNode_IDivide : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("/") + + UVoxelNode_IDivide(); +}; + +// Left bit shift +UCLASS(DisplayName = "<<", Category = "Math|Integer", meta = (Keywords = "<< left bit shift")) +class VOXELGRAPH_API UVoxelNode_ILeftBitShift : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("<<") + + UVoxelNode_ILeftBitShift(); +}; + +// Right bit shift +UCLASS(DisplayName = ">>", Category = "Math|Integer", meta = (Keywords = ">> right bit shift")) +class VOXELGRAPH_API UVoxelNode_IRightBitShift : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE(">>") + + UVoxelNode_IRightBitShift(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Cast to float +UCLASS(DisplayName = "int to float", Category = "Math|Integer", meta = (Keywords = "cast convert")) +class VOXELGRAPH_API UVoxelNode_FloatOfInt : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("float") + + UVoxelNode_FloatOfInt(); +}; + +// Round to int32 +UCLASS(DisplayName = "Round", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Round : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("Round") + + UVoxelNode_Round(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Lerp between A and B. Warning: Alpha not clamped! Lerp(0, 20, 2) = 40! +UCLASS(DisplayName = "Lerp", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Lerp : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_Lerp(); +}; + +// Lerp between A and B, with a clamped alpha +UCLASS(DisplayName = "Safe Lerp", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_SafeLerp : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SafeLerp(); +}; + + // Returns a smooth Hermite interpolation between 0 and 1 for the value X (where X ranges between A and B) + // Clamped to 0 for X <= A and 1 for X >= B. +UCLASS(DisplayName = "Smooth Step", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_SmoothStep : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SmoothStep(); +}; + +// Clamp Value between Min and Max +UCLASS(DisplayName = "Clamp", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Clamp : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_Clamp(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// And +UCLASS(DisplayName = "AND Boolean", Category = "Math|Boolean", meta = (Keywords = "& and")) +class VOXELGRAPH_API UVoxelNode_BAnd : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("AND") + + UVoxelNode_BAnd(); +}; + +// Or +UCLASS(DisplayName = "OR Boolean", Category = "Math|Boolean", meta = (Keywords = "| or")) +class VOXELGRAPH_API UVoxelNode_BOr : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("OR") + + UVoxelNode_BOr(); +}; + +// Not +UCLASS(DisplayName = "NOT Boolean", Category = "Math|Boolean", meta = (Keywords = "! not")) +class VOXELGRAPH_API UVoxelNode_BNot : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("NOT") + + UVoxelNode_BNot(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Switch: if bool is true, then A is returned, else B is +UCLASS(DisplayName = "Switch (int)", Category = "Math|Integer", meta = (Keywords = "if branch select")) +class VOXELGRAPH_API UVoxelNode_SwitchInt : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SwitchInt(); + virtual TSharedPtr GetCompilationNode() const override final; +}; + +// Switch: if bool is true, then A is returned, else B is +UCLASS(DisplayName = "Switch (float)", Category = "Math|Float", meta = (Keywords = "if branch select")) +class VOXELGRAPH_API UVoxelNode_SwitchFloat : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SwitchFloat(); + virtual TSharedPtr GetCompilationNode() const override final; +}; + +// Switch: if bool is true, then A is returned, else B is +UCLASS(DisplayName = "Switch (color)", Category = "Math|Color", meta = (Keywords = "if branch select")) +class VOXELGRAPH_API UVoxelNode_SwitchColor : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SwitchColor(); + virtual TSharedPtr GetCompilationNode() const override final; +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// 1 - X +UCLASS(DisplayName = "1 - X", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_1MinusX : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("1 - X") + + UVoxelNode_1MinusX(); +}; + +// 1 / X +UCLASS(DisplayName = "1 / X", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_OneOverX : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("1 / X") + + UVoxelNode_OneOverX(); +}; + +// -X +UCLASS(DisplayName = "* -1", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_MinusX : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("* -1") + + UVoxelNode_MinusX(); +}; + +// Square root +UCLASS(DisplayName = "Sqrt", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Sqrt : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SQRT") + + UVoxelNode_Sqrt(); +}; + +// Pow +UCLASS(DisplayName = "Pow", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Pow : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("POW") + + UVoxelNode_Pow(); +}; + +// Modulo +UCLASS(DisplayName = "% (int)", Category = "Math|Integer", meta = (Keywords = "% modulus")) +class VOXELGRAPH_API UVoxelNode_IMod : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("%") + + UVoxelNode_IMod(); +}; + +/** + * Returns the floating-point remainder of X / Y + * Warning: Always returns remainder toward 0, not toward the smaller multiple of Y. + * So for example Fmod(2.8f, 2) gives .8f as you would expect, however, Fmod(-2.8f, 2) gives -.8f, NOT 1.2f + * Use Floor instead when snapping positions that can be negative to a grid + */ +UCLASS(DisplayName = "FMod", Category = "Math|Float", meta = (Keywords = "% modulus fmod")) +class VOXELGRAPH_API UVoxelNode_FMod : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("FMOD") + + UVoxelNode_FMod(); +}; + +// Absolute value +UCLASS(DisplayName = "Absolute (float)", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_FAbs : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("ABS") + + UVoxelNode_FAbs(); +}; + +// Absolute value +UCLASS(DisplayName = "Absolute (int)", Category = "Math|Integer") +class VOXELGRAPH_API UVoxelNode_IAbs : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("ABS") + + UVoxelNode_IAbs(); +}; + +// Returns the smallest integer greater than or equal to the input +UCLASS(DisplayName = "Ceil", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Ceil : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("CEIL") + + UVoxelNode_Ceil(); +}; + +// Returns the largest integer less than or equal to the input +UCLASS(DisplayName = "Floor", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Floor : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("FLOOR") + + UVoxelNode_Floor(); +}; + +// Return the length of (X, Y, Z) +UCLASS(DisplayName = "Vector Length", Category = "Math|Vector") +class VOXELGRAPH_API UVoxelNode_VectorLength : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VectorLength(); +}; + +// Return the signed fractional part of the input (ie 9.45 -> 0.45). Negative if the input is negative +UCLASS(DisplayName = "Fraction", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Fraction : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("FRACT") + + UVoxelNode_Fraction(); +}; + +// Return the sign of the input (1, 0 or -1) +UCLASS(DisplayName = "Sign (float)", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_FSign : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SIGN") + + UVoxelNode_FSign(); +}; + +// Return the sign of the input (1, 0 or -1) +UCLASS(DisplayName = "Sign (int)", Category = "Math|Integer") +class VOXELGRAPH_API UVoxelNode_ISign : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SIGN") + + UVoxelNode_ISign(); +}; + +// Return the inverse square root of the input +UCLASS(DisplayName = "InvSqrt", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_InvSqrt : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("INVSQRT") + + UVoxelNode_InvSqrt(); +}; + +// Return the loge of the input +UCLASS(DisplayName = "Loge", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Loge : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("LOGE") + + UVoxelNode_Loge(); +}; + +// Return the exponential of the input +UCLASS(DisplayName = "Exp", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Exp : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("EXP") + + UVoxelNode_Exp(); +}; + +// Return the sine of the input +// Input is in radians +UCLASS(DisplayName = "Sin", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Sin : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SIN") + + UVoxelNode_Sin(); +}; + +// Return the asin (inverse of sine) of the input +// Output is in radians +UCLASS(DisplayName = "Asin", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Asin : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("ASIN") + + UVoxelNode_Asin(); +}; + +// Return the sinh (hyperbolic sine) of the input +// Input is in radians +UCLASS(DisplayName = "Sinh", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Sinh : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SINH") + + UVoxelNode_Sinh(); +}; + +// Return the cosine of the input +// Input is in radians +UCLASS(DisplayName = "Cos", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Cos : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("COS") + + UVoxelNode_Cos(); +}; + +// Return the acos (inverse of cosine) of the input +// Output is in radians +UCLASS(DisplayName = "Acos", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Acos : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("ACOS") + + UVoxelNode_Acos(); +}; + +// Return the Sinus and Cosinus of the input +// Input is in radians +UCLASS(DisplayName = "SinCos", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_SinCos : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("SIN COS") + + UVoxelNode_SinCos(); +}; + +// Return the tan of the input +// Input is in radians +UCLASS(DisplayName = "Tan", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Tan : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("TAN") + + UVoxelNode_Tan(); +}; + +// Return the atan of the input +// Output is in radians +UCLASS(DisplayName = "Atan", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Atan : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("ATAN") + + UVoxelNode_Atan(); +}; + +// Return atan2(Y, X) +// Input is in radians +UCLASS(DisplayName = "Atan2", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_Atan2 : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_Atan2(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Rotates around Axis (assumes Axis.Size() == 1). Angle is in degrees +UCLASS(DisplayName = "Vector Rotate Angle Axis", Category = "Math|Vector") +class VOXELGRAPH_API UVoxelNode_VectorRotateAngleAxis : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VectorRotateAngleAxis(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Break a color into its 4 integer RGBA components +UCLASS(DisplayName = "Break Color Int", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_BreakColorInt : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_BreakColorInt(); +}; + +// Break a color into its 4 RGBA components, and convert them to floats between 0 and 1 +UCLASS(DisplayName = "Break Color", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_BreakColorFloat : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_BreakColorFloat(); +}; + +// Make a color from its 4 integer RGBA components +UCLASS(DisplayName = "Make Color Int", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_MakeColorInt : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_MakeColorInt(); +}; + +// Make a color into its 4 RGBA components as floats between 0 and 1 +UCLASS(DisplayName = "Make Color", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_MakeColorFloat : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_MakeColorFloat(); +}; + +// Convert a color in RGB space to HSV +UCLASS(DisplayName = "RGB to HSV", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_RGBToHSV : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_RGBToHSV(); +}; + +// Convert a color in HSV space to RGB +UCLASS(DisplayName = "HSV to RGB", Category = "Math|Color") +class VOXELGRAPH_API UVoxelNode_HSVToRGB : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_HSVToRGB(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Transform a position to a new basis defined by X and Z vectors +UCLASS(DisplayName = "Inverse Transform Position XZ", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_InverseTransformPositionXZ : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_InverseTransformPositionXZ(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Pi = 3.1415926535897932384626433832795 +UCLASS(DisplayName = "Pi", Category = "Math|Constants") +class VOXELGRAPH_API UVoxelNode_Pi : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("PI"); + + UVoxelNode_Pi(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// Normalize an arbitrary number of values so that their sum is 1. +// Not the same as normalizing a vector! +// Only works on positive values +UCLASS(DisplayName = "Normalize Sum", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_NormalizeSum : public UVoxelPureNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_NormalizeSum(); + + virtual void OnInputPinCountModified() override; + virtual int32 GetOutputPinsCount() const override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeColors.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeColors.h new file mode 100644 index 00000000..9f08b46b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeColors.h @@ -0,0 +1,16 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +struct VOXELGRAPH_API FVoxelNodeColors +{ + static FLinearColor FloatNode; + static FLinearColor IntNode; + static FLinearColor ColorNode; + static FLinearColor BoolNode; + static FLinearColor ExecNode; + static FLinearColor SeedNode; + static FLinearColor ExposedNode; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h new file mode 100644 index 00000000..7ec513f3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h @@ -0,0 +1,431 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "VoxelNodeHelper.generated.h" + +struct FVoxelHelperPin +{ + FName Name; + EVoxelPinCategory Category; + FString DefaultValue; + FVoxelPinDefaultValueBounds DefaultValueBounds; + FString ToolTip; + + FVoxelHelperPin() = default; + FVoxelHelperPin(EVoxelPinCategory Category) + : Category(Category) + { + } + FVoxelHelperPin(const FName& Name, EVoxelPinCategory Category, const FString& ToolTip, const FString& DefaultValue = "", const FVoxelPinDefaultValueBounds& DefaultValueBounds = {}) + : Name(Name) + , Category(Category) + , DefaultValue(DefaultValue) + , DefaultValueBounds(DefaultValueBounds) + , ToolTip(ToolTip) + { + } +}; + +struct VOXELGRAPH_API FVoxelPinsHelper +{ + TArray InputPins; + TArray OutputPins; + + FVoxelHelperPin GetInputPin(int32 PinIndex, bool bClamp = true) const + { + // Clamping is needed for variable input pins count + if (bClamp) + { + PinIndex = FMath::Clamp(PinIndex, 0, InputPins.Num() - 1); + } + + if (InputPins.IsValidIndex(PinIndex)) + { + return InputPins[PinIndex]; + } + else + { + return {}; + } + } + FVoxelHelperPin GetOutputPin(int32 PinIndex, bool bClamp = true) const + { + // Clamping is needed for variable input pins count + if (bClamp) + { + PinIndex = FMath::Clamp(PinIndex, 0, OutputPins.Num() - 1); + } + + if (OutputPins.IsValidIndex(PinIndex)) + { + return OutputPins[PinIndex]; + } + else + { + return {}; + } + } +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNodeHelper : public UVoxelNode +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNode Interface + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + virtual FString GetInputPinToolTip(int32 PinIndex) const override; + virtual FString GetOutputPinToolTip(int32 PinIndex) const override; + virtual int32 GetMinInputPins() const override; + virtual int32 GetMaxInputPins() const override; + virtual int32 GetInputPinsIncrement() const override; + virtual int32 GetOutputPinsCount() const override; + virtual FLinearColor GetColor() const override; + virtual FVoxelPinDefaultValueBounds GetInputPinDefaultValueBounds(int32 PinIndex) const override; + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override; + //~ End UVoxelNode Interface + +protected: + // To allow using initializer lists + void SetInputs(FVoxelHelperPin Pin) + { + Pins.InputPins.Add(Pin); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + Pins.InputPins.Add(Pin4); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + Pins.InputPins.Add(Pin4); + Pins.InputPins.Add(Pin5); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5, + FVoxelHelperPin Pin6) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + Pins.InputPins.Add(Pin4); + Pins.InputPins.Add(Pin5); + Pins.InputPins.Add(Pin6); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5, + FVoxelHelperPin Pin6, + FVoxelHelperPin Pin7) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + Pins.InputPins.Add(Pin4); + Pins.InputPins.Add(Pin5); + Pins.InputPins.Add(Pin6); + Pins.InputPins.Add(Pin7); + } + void SetInputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5, + FVoxelHelperPin Pin6, + FVoxelHelperPin Pin7, + FVoxelHelperPin Pin8) + { + Pins.InputPins.Add(Pin0); + Pins.InputPins.Add(Pin1); + Pins.InputPins.Add(Pin2); + Pins.InputPins.Add(Pin3); + Pins.InputPins.Add(Pin4); + Pins.InputPins.Add(Pin5); + Pins.InputPins.Add(Pin6); + Pins.InputPins.Add(Pin7); + Pins.InputPins.Add(Pin8); + } + + void SetOutputs(FVoxelHelperPin Pin) + { + Pins.OutputPins.Add(Pin); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + Pins.OutputPins.Add(Pin3); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + Pins.OutputPins.Add(Pin3); + Pins.OutputPins.Add(Pin4); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + Pins.OutputPins.Add(Pin3); + Pins.OutputPins.Add(Pin4); + Pins.OutputPins.Add(Pin5); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5, + FVoxelHelperPin Pin6) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + Pins.OutputPins.Add(Pin3); + Pins.OutputPins.Add(Pin4); + Pins.OutputPins.Add(Pin5); + Pins.OutputPins.Add(Pin6); + } + void SetOutputs( + FVoxelHelperPin Pin0, + FVoxelHelperPin Pin1, + FVoxelHelperPin Pin2, + FVoxelHelperPin Pin3, + FVoxelHelperPin Pin4, + FVoxelHelperPin Pin5, + FVoxelHelperPin Pin6, + FVoxelHelperPin Pin7) + { + Pins.OutputPins.Add(Pin0); + Pins.OutputPins.Add(Pin1); + Pins.OutputPins.Add(Pin2); + Pins.OutputPins.Add(Pin3); + Pins.OutputPins.Add(Pin4); + Pins.OutputPins.Add(Pin5); + Pins.OutputPins.Add(Pin6); + Pins.OutputPins.Add(Pin7); + } + + template + void SetInputs(FVoxelHelperPin Pin, TArgs... Args) + { + Pins.InputPins.Add(Pin); + SetInputs(Args...); + } + template + void SetOutputs(FVoxelHelperPin Pin, TArgs... Args) + { + Pins.OutputPins.Add(Pin); + SetOutputs(Args...); + } + + void SetInputs(const TArray& InPins) + { + Pins.InputPins.Append(InPins); + } + void SetOutputs(const TArray& InPins) + { + Pins.OutputPins.Append(InPins); + } + + void SetColor(const FLinearColor& InColor) + { + Color = InColor; + } + + void SetInputsCount(int32 Min, int32 Max) + { + bCustomInputsCount = true; + CustomMin = Min; + CustomMax = Max; + } + void SetInputIncrement(int32 InIncrement) + { + ensure(InIncrement > 0); + Increment = InIncrement; + } + + void AddVectorInput(const FString& Name, const FString& ToolTip, EVoxelPinCategory Category = EVoxelPinCategory::Float) + { + Pins.InputPins.Add({ *FString(Name + " X"), Category, ToolTip }); + Pins.InputPins.Add({ *FString(Name + " Y"), Category, ToolTip }); + Pins.InputPins.Add({ *FString(Name + " Z"), Category, ToolTip }); + } + void AddVector2DInput(const FString& Name, const FString& ToolTip, EVoxelPinCategory Category = EVoxelPinCategory::Float) + { + Pins.InputPins.Add({ *FString(Name + " X"), Category, ToolTip }); + Pins.InputPins.Add({ *FString(Name + " Y"), Category, ToolTip }); + } + void AddInput(const FString& Name, const FString& ToolTip, EVoxelPinCategory Category = EVoxelPinCategory::Float) + { + Pins.InputPins.Add({ *Name, Category, ToolTip }); + } + + void AddOutput(const FString& Name, const FString& ToolTip, EVoxelPinCategory Category = EVoxelPinCategory::Float) + { + Pins.OutputPins.Add({ *Name, Category, ToolTip }); + } + +private: + FVoxelPinsHelper Pins; + FLinearColor Color = FLinearColor::Black; + bool bCustomInputsCount = false; + int32 CustomMin = 0; + int32 CustomMax = 0; + int32 Increment = 1; +}; + +UCLASS(Abstract, Category = "Setter nodes") +class VOXELGRAPH_API UVoxelSetterNode : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UVoxelSetterNode(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelSetterNode Interface + virtual int32 GetOutputIndex() const { unimplemented(); return -1; }; + //~ End UVoxelSetterNode Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelPureNode : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNodeWithDependencies : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelNodeWithDependencies Interface + virtual uint8 GetNodeDependencies() const { unimplemented(); return 0; }; + //~ End UVoxelNodeWithDependencies Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNodeWithContext : public UVoxelNodeWithDependencies +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelNodeWithDependencies Interface + virtual uint8 GetNodeDependencies() const override final; + //~ End UVoxelNodeWithDependencies Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h new file mode 100644 index 00000000..ea9d81b4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h @@ -0,0 +1,322 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +using EC = EVoxelPinCategory; + +#define GENERATED_VOXELNODE_BODY() \ +TVoxelSharedPtr GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const override; public: + +#define GENERATED_VOXELNODE_IMPL_IMPL(Name, ...) \ +TVoxelSharedPtr Name::GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const \ +{ \ + __VA_ARGS__ \ + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); \ +} + +#define GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FLocalVoxelComputeNode) \ +virtual double ComputeStats(double InThreshold, bool bSimulateCpp, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) const override final \ +{ \ + return FVoxelNodeHelpers::ComputeStatsImpl(this, InThreshold, bSimulateCpp, Inputs, Context); \ +} \ +virtual double ComputeRangeStats(double InThreshold, bool bSimulateCpp, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) const override final \ +{ \ + return FVoxelNodeHelpers::ComputeRangeStatsImpl(this, InThreshold, bSimulateCpp, Inputs, Context); \ +} \ +virtual void CacheFunctionPtr() override final \ +{ \ + this->ComputeFunctionPtr = static_cast(&FLocalVoxelComputeNode::Compute); \ +} + +#define GENERATED_DATA_COMPUTE_NODE_BODY() GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(FLocalVoxelComputeNode) + +#define GENERATED_COMPUTE(InputsDef, OutputsDef, ...) \ +void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& _C0) const override \ +{ \ + InputsDef \ + OutputsDef \ + __VA_ARGS__ \ +} \ +void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& _C0) const override \ +{ \ + InputsDef \ + OutputsDef \ + __VA_ARGS__ \ +} \ +void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ +{ \ + FString X = TEXT(#__VA_ARGS__); \ + FVoxelNodeHelpers::ReplaceInputsOutputs(X, Inputs, Outputs); \ + Constructor.AddLine(X); \ +} \ +void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ +{ \ + ComputeCpp(Inputs, Outputs, Constructor); \ +} + +#define GENERATED_COMPUTENODE(InputsDef, OutputsDef, ...) \ +class FLocalVoxelComputeNode : public FVoxelDataComputeNode \ +{ \ +public: \ + GENERATED_DATA_COMPUTE_NODE_BODY() \ + using FVoxelDataComputeNode::FVoxelDataComputeNode; \ + GENERATED_COMPUTE \ + ( \ + InputsDef, \ + OutputsDef, \ + __VA_ARGS__ \ + ) \ +}; + +#define GENERATED_VOXELNODE_IMPL(Name, InputsDef, OutputsDef, Code) GENERATED_VOXELNODE_IMPL_IMPL(Name, GENERATED_COMPUTENODE(InputsDef, OutputsDef, Code)) + +#define GENERATED_COMPUTENODE_PREFIXOPLOOP(Op, Type) \ +class FLocalVoxelComputeNode : public FVoxelDataComputeNode \ +{ \ +public: \ + GENERATED_DATA_COMPUTE_NODE_BODY() \ + using FVoxelDataComputeNode::FVoxelDataComputeNode; \ + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override \ + { \ + if(InputCount == 2) \ + { \ + Outputs[0].Get() = Op(Inputs[0].Get(), Inputs[1].Get()); \ + } \ + else \ + { \ + auto X = Inputs[0].Get(); \ + for (int32 I = 1; I < InputCount; I++) \ + { \ + X = Op(X, Inputs[I].Get()); \ + } \ + Outputs[0].Get() = X; \ + } \ + } \ + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& _C0) const override \ + { \ + if(InputCount == 2) \ + { \ + Outputs[0].Get() = Op(Inputs[0].Get(), Inputs[1].Get()); \ + } \ + else \ + { \ + auto X = Inputs[0].Get(); \ + for (int32 I = 1; I < InputCount; I++) \ + { \ + X = Op(X, Inputs[I].Get()); \ + } \ + Outputs[0].Get() = X; \ + } \ + } \ + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ + { \ + Constructor.AddLine(FVoxelNodeHelpers::GetPrefixOpLoopString(Inputs, Outputs, InputCount, #Op)); \ + } \ + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ + { \ + Constructor.AddLine(FVoxelNodeHelpers::GetPrefixOpLoopString(Inputs, Outputs, InputCount, #Op)); \ + } \ +}; + +#define GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(Name, Op, Type) GENERATED_VOXELNODE_IMPL_IMPL(Name, GENERATED_COMPUTENODE_PREFIXOPLOOP(Op, Type)) + +#define GENERATED_COMPUTENODE_INFIXOPLOOP(Op, Type) \ +class FLocalVoxelComputeNode : public FVoxelDataComputeNode \ +{ \ +public: \ + GENERATED_DATA_COMPUTE_NODE_BODY() \ + using FVoxelDataComputeNode::FVoxelDataComputeNode; \ + void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override \ + { \ + if(InputCount == 2) \ + { \ + Outputs[0].Get() = Inputs[0].Get() Op Inputs[1].Get(); \ + } \ + else \ + { \ + auto X = Inputs[0].Get(); \ + for (int32 I = 1; I < InputCount; I++) \ + { \ + X = X Op Inputs[I].Get(); \ + } \ + Outputs[0].Get() = X; \ + } \ + } \ + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& _C0) const override \ + { \ + if(InputCount == 2) \ + { \ + Outputs[0].Get() = Inputs[0].Get() Op Inputs[1].Get(); \ + } \ + else \ + { \ + auto X = Inputs[0].Get(); \ + for (int32 I = 1; I < InputCount; I++) \ + { \ + X = X Op Inputs[I].Get(); \ + } \ + Outputs[0].Get() = X; \ + } \ + } \ + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ + { \ + Constructor.AddLine(FVoxelNodeHelpers::GetInfixOpLoopString(Inputs, Outputs, InputCount, #Op)); \ + } \ + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override \ + { \ + Constructor.AddLine(FVoxelNodeHelpers::GetInfixOpLoopString(Inputs, Outputs, InputCount, #Op)); \ + } \ +}; + +#define GENERATED_VOXELNODE_IMPL_INFIXOPLOOP(Name, Op, Type) GENERATED_VOXELNODE_IMPL_IMPL(Name, GENERATED_COMPUTENODE_INFIXOPLOOP(Op, Type)) + +#define GENERATED_BINARY_VOXELNODE(Name, Op, ECType, RealType) \ +Name::Name() \ +{ \ + SetInputs(EC::ECType, EC::ECType); \ + SetOutputs(EC::Boolean); \ +} \ +GENERATED_VOXELNODE_IMPL_IMPL(Name, \ +GENERATED_COMPUTENODE\ +(\ + DEFINE_INPUTS(RealType, RealType),\ + DEFINE_OUTPUTS(bool),\ + _O0 = _I0 Op _I1;\ +)) + +#define GENERATED_VOXELNODE_IMPL_BINARY_FLOAT(Name, Op) GENERATED_BINARY_VOXELNODE(Name, Op, Float, v_flt) +#define GENERATED_VOXELNODE_IMPL_BINARY_INT(Name, Op) GENERATED_BINARY_VOXELNODE(Name, Op, Int, int32) + +#define COMPACT_VOXELNODE(Text) \ +bool IsCompact() const override { return true; } \ +FText GetTitle() const override { return VOXEL_LOCTEXT(Text); } + +#define SET_VOXELNODE_TITLE(Text) \ +FText GetTitle() const override { return VOXEL_LOCTEXT(Text); } + +#define DEPRECATED_VOXELNODE(Message) \ +void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) \ +{ \ + Super::LogErrors(ErrorReporter); \ + ErrorReporter.AddMessageToNode(this, "deprecated node: " Message, EVoxelGraphNodeMessageType::Error); \ +} + +#define __DEFINE_INPUTS_0(A) auto& _I0 = Inputs[0].Get(); auto& Input0 = _I0; +#define __DEFINE_INPUTS_1(A, ...) auto& _I1 = Inputs[1].Get(); auto& Input1 = _I1; EXPAND(__DEFINE_INPUTS_0(__VA_ARGS__)) +#define __DEFINE_INPUTS_2(A, ...) auto& _I2 = Inputs[2].Get(); auto& Input2 = _I2; EXPAND(__DEFINE_INPUTS_1(__VA_ARGS__)) +#define __DEFINE_INPUTS_3(A, ...) auto& _I3 = Inputs[3].Get(); auto& Input3 = _I3; EXPAND(__DEFINE_INPUTS_2(__VA_ARGS__)) +#define __DEFINE_INPUTS_4(A, ...) auto& _I4 = Inputs[4].Get(); auto& Input4 = _I4; EXPAND(__DEFINE_INPUTS_3(__VA_ARGS__)) +#define __DEFINE_INPUTS_5(A, ...) auto& _I5 = Inputs[5].Get(); auto& Input5 = _I5; EXPAND(__DEFINE_INPUTS_4(__VA_ARGS__)) +#define __DEFINE_INPUTS_6(A, ...) auto& _I6 = Inputs[6].Get(); auto& Input6 = _I6; EXPAND(__DEFINE_INPUTS_5(__VA_ARGS__)) +#define __DEFINE_INPUTS_7(A, ...) auto& _I7 = Inputs[7].Get(); auto& Input7 = _I7; EXPAND(__DEFINE_INPUTS_6(__VA_ARGS__)) +#define __DEFINE_INPUTS_8(A, ...) auto& _I8 = Inputs[8].Get(); auto& Input8 = _I8; EXPAND(__DEFINE_INPUTS_7(__VA_ARGS__)) +#define __DEFINE_INPUTS_9(A, ...) auto& _I9 = Inputs[9].Get(); auto& Input9 = _I9; EXPAND(__DEFINE_INPUTS_8(__VA_ARGS__)) +#define __DEFINE_INPUTS_10(A, ...) auto& _I10 = Inputs[10].Get(); auto& Input10 = _I10; EXPAND(__DEFINE_INPUTS_9(__VA_ARGS__)) +#define __DEFINE_INPUTS_11(A, ...) auto& _I11 = Inputs[11].Get(); auto& Input11 = _I11; EXPAND(__DEFINE_INPUTS_10(__VA_ARGS__)) +#define __DEFINE_INPUTS_12(A, ...) auto& _I12 = Inputs[12].Get(); auto& Input12 = _I12; EXPAND(__DEFINE_INPUTS_11(__VA_ARGS__)) +#define __DEFINE_INPUTS_13(A, ...) auto& _I13 = Inputs[13].Get(); auto& Input13 = _I13; EXPAND(__DEFINE_INPUTS_12(__VA_ARGS__)) +#define __DEFINE_INPUTS_14(A, ...) auto& _I14 = Inputs[14].Get(); auto& Input14 = _I14; EXPAND(__DEFINE_INPUTS_13(__VA_ARGS__)) +#define __DEFINE_INPUTS_15(A, ...) auto& _I15 = Inputs[15].Get(); auto& Input15 = _I15; EXPAND(__DEFINE_INPUTS_14(__VA_ARGS__)) +#define __DEFINE_INPUTS_16(A, ...) auto& _I16 = Inputs[16].Get(); auto& Input16 = _I16; EXPAND(__DEFINE_INPUTS_15(__VA_ARGS__)) +#define __DEFINE_INPUTS_17(A, ...) auto& _I17 = Inputs[17].Get(); auto& Input17 = _I17; EXPAND(__DEFINE_INPUTS_16(__VA_ARGS__)) +#define __DEFINE_INPUTS_18(A, ...) auto& _I18 = Inputs[18].Get(); auto& Input18 = _I18; EXPAND(__DEFINE_INPUTS_17(__VA_ARGS__)) +#define __DEFINE_INPUTS_19(A, ...) auto& _I19 = Inputs[19].Get(); auto& Input19 = _I19; EXPAND(__DEFINE_INPUTS_18(__VA_ARGS__)) +#define __DEFINE_INPUTS_20(A, ...) auto& _I20 = Inputs[20].Get(); auto& Input20 = _I20; EXPAND(__DEFINE_INPUTS_19(__VA_ARGS__)) +#define __DEFINE_INPUTS_21(A, ...) auto& _I21 = Inputs[21].Get(); auto& Input21 = _I21; EXPAND(__DEFINE_INPUTS_20(__VA_ARGS__)) +#define __DEFINE_INPUTS_22(A, ...) auto& _I22 = Inputs[22].Get(); auto& Input22 = _I22; EXPAND(__DEFINE_INPUTS_21(__VA_ARGS__)) +#define __DEFINE_INPUTS_23(A, ...) auto& _I23 = Inputs[23].Get(); auto& Input23 = _I23; EXPAND(__DEFINE_INPUTS_22(__VA_ARGS__)) +#define __DEFINE_INPUTS_24(A, ...) auto& _I24 = Inputs[24].Get(); auto& Input24 = _I24; EXPAND(__DEFINE_INPUTS_23(__VA_ARGS__)) +#define __DEFINE_INPUTS_25(A, ...) auto& _I25 = Inputs[25].Get(); auto& Input25 = _I25; EXPAND(__DEFINE_INPUTS_24(__VA_ARGS__)) +#define __DEFINE_INPUTS_26(A, ...) auto& _I26 = Inputs[26].Get(); auto& Input26 = _I26; EXPAND(__DEFINE_INPUTS_25(__VA_ARGS__)) +#define __DEFINE_INPUTS_27(A, ...) auto& _I27 = Inputs[27].Get(); auto& Input27 = _I27; EXPAND(__DEFINE_INPUTS_26(__VA_ARGS__)) +#define __DEFINE_INPUTS_28(A, ...) auto& _I28 = Inputs[28].Get(); auto& Input28 = _I28; EXPAND(__DEFINE_INPUTS_27(__VA_ARGS__)) +#define __DEFINE_INPUTS_29(A, ...) auto& _I29 = Inputs[29].Get(); auto& Input29 = _I29; EXPAND(__DEFINE_INPUTS_28(__VA_ARGS__)) +#define __DEFINE_INPUTS_30(A, ...) auto& _I30 = Inputs[30].Get(); auto& Input30 = _I30; EXPAND(__DEFINE_INPUTS_29(__VA_ARGS__)) +#define __DEFINE_INPUTS_31(A, ...) auto& _I31 = Inputs[31].Get(); auto& Input31 = _I31; EXPAND(__DEFINE_INPUTS_30(__VA_ARGS__)) +#define __DEFINE_INPUTS_32(A, ...) auto& _I32 = Inputs[32].Get(); auto& Input32 = _I32; EXPAND(__DEFINE_INPUTS_31(__VA_ARGS__)) + +#define __DEFINE_OUTPUTS_0(A) auto& _O0 = Outputs[0].Get(); auto& Output0 = _O0; +#define __DEFINE_OUTPUTS_1(A, ...) auto& _O1 = Outputs[1].Get(); auto& Output1 = _O1; EXPAND(__DEFINE_OUTPUTS_0(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_2(A, ...) auto& _O2 = Outputs[2].Get(); auto& Output2 = _O2; EXPAND(__DEFINE_OUTPUTS_1(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_3(A, ...) auto& _O3 = Outputs[3].Get(); auto& Output3 = _O3; EXPAND(__DEFINE_OUTPUTS_2(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_4(A, ...) auto& _O4 = Outputs[4].Get(); auto& Output4 = _O4; EXPAND(__DEFINE_OUTPUTS_3(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_5(A, ...) auto& _O5 = Outputs[5].Get(); auto& Output5 = _O5; EXPAND(__DEFINE_OUTPUTS_4(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_6(A, ...) auto& _O6 = Outputs[6].Get(); auto& Output6 = _O6; EXPAND(__DEFINE_OUTPUTS_5(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_7(A, ...) auto& _O7 = Outputs[7].Get(); auto& Output7 = _O7; EXPAND(__DEFINE_OUTPUTS_6(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_8(A, ...) auto& _O8 = Outputs[8].Get(); auto& Output8 = _O8; EXPAND(__DEFINE_OUTPUTS_7(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_9(A, ...) auto& _O9 = Outputs[9].Get(); auto& Output9 = _O9; EXPAND(__DEFINE_OUTPUTS_8(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_10(A, ...) auto& _O10 = Outputs[10].Get(); auto& Output10 = _O10; EXPAND(__DEFINE_OUTPUTS_9(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_11(A, ...) auto& _O11 = Outputs[11].Get(); auto& Output11 = _O11; EXPAND(__DEFINE_OUTPUTS_10(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_12(A, ...) auto& _O12 = Outputs[12].Get(); auto& Output12 = _O12; EXPAND(__DEFINE_OUTPUTS_11(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_13(A, ...) auto& _O13 = Outputs[13].Get(); auto& Output13 = _O13; EXPAND(__DEFINE_OUTPUTS_12(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_14(A, ...) auto& _O14 = Outputs[14].Get(); auto& Output14 = _O14; EXPAND(__DEFINE_OUTPUTS_13(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_15(A, ...) auto& _O15 = Outputs[15].Get(); auto& Output15 = _O15; EXPAND(__DEFINE_OUTPUTS_14(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_16(A, ...) auto& _O16 = Outputs[16].Get(); auto& Output16 = _O16; EXPAND(__DEFINE_OUTPUTS_15(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_17(A, ...) auto& _O17 = Outputs[17].Get(); auto& Output17 = _O17; EXPAND(__DEFINE_OUTPUTS_16(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_18(A, ...) auto& _O18 = Outputs[18].Get(); auto& Output18 = _O18; EXPAND(__DEFINE_OUTPUTS_17(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_19(A, ...) auto& _O19 = Outputs[19].Get(); auto& Output19 = _O19; EXPAND(__DEFINE_OUTPUTS_18(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_20(A, ...) auto& _O20 = Outputs[20].Get(); auto& Output20 = _O20; EXPAND(__DEFINE_OUTPUTS_19(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_21(A, ...) auto& _O21 = Outputs[21].Get(); auto& Output21 = _O21; EXPAND(__DEFINE_OUTPUTS_20(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_22(A, ...) auto& _O22 = Outputs[22].Get(); auto& Output22 = _O22; EXPAND(__DEFINE_OUTPUTS_21(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_23(A, ...) auto& _O23 = Outputs[23].Get(); auto& Output23 = _O23; EXPAND(__DEFINE_OUTPUTS_22(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_24(A, ...) auto& _O24 = Outputs[24].Get(); auto& Output24 = _O24; EXPAND(__DEFINE_OUTPUTS_23(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_25(A, ...) auto& _O25 = Outputs[25].Get(); auto& Output25 = _O25; EXPAND(__DEFINE_OUTPUTS_24(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_26(A, ...) auto& _O26 = Outputs[26].Get(); auto& Output26 = _O26; EXPAND(__DEFINE_OUTPUTS_25(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_27(A, ...) auto& _O27 = Outputs[27].Get(); auto& Output27 = _O27; EXPAND(__DEFINE_OUTPUTS_26(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_28(A, ...) auto& _O28 = Outputs[28].Get(); auto& Output28 = _O28; EXPAND(__DEFINE_OUTPUTS_27(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_29(A, ...) auto& _O29 = Outputs[29].Get(); auto& Output29 = _O29; EXPAND(__DEFINE_OUTPUTS_28(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_30(A, ...) auto& _O30 = Outputs[30].Get(); auto& Output30 = _O30; EXPAND(__DEFINE_OUTPUTS_29(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_31(A, ...) auto& _O31 = Outputs[31].Get(); auto& Output31 = _O31; EXPAND(__DEFINE_OUTPUTS_30(__VA_ARGS__)) +#define __DEFINE_OUTPUTS_32(A, ...) auto& _O32 = Outputs[32].Get(); auto& Output32 = _O32; EXPAND(__DEFINE_OUTPUTS_31(__VA_ARGS__)) + +#define _GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, N, ...) EXPAND(N) + +#define EXPAND( x ) x + +#define DEFINE_INPUTS_REVERSED(...) \ + EXPAND(_GET_NTH_ARG(__VA_ARGS__, __DEFINE_INPUTS_32, __DEFINE_INPUTS_31, __DEFINE_INPUTS_30, __DEFINE_INPUTS_29, __DEFINE_INPUTS_28, __DEFINE_INPUTS_27, __DEFINE_INPUTS_26, __DEFINE_INPUTS_25, __DEFINE_INPUTS_24, __DEFINE_INPUTS_23, __DEFINE_INPUTS_22, __DEFINE_INPUTS_21, __DEFINE_INPUTS_20, __DEFINE_INPUTS_19, __DEFINE_INPUTS_18, __DEFINE_INPUTS_17, __DEFINE_INPUTS_16, __DEFINE_INPUTS_15, __DEFINE_INPUTS_14, __DEFINE_INPUTS_13, __DEFINE_INPUTS_12, __DEFINE_INPUTS_11, __DEFINE_INPUTS_10, __DEFINE_INPUTS_9, __DEFINE_INPUTS_8, __DEFINE_INPUTS_7, __DEFINE_INPUTS_6, __DEFINE_INPUTS_5, __DEFINE_INPUTS_4, __DEFINE_INPUTS_3, __DEFINE_INPUTS_2, __DEFINE_INPUTS_1, __DEFINE_INPUTS_0)(__VA_ARGS__)) + +#define DEFINE_OUTPUTS_REVERSED(...) \ + EXPAND(_GET_NTH_ARG(__VA_ARGS__, __DEFINE_OUTPUTS_32, __DEFINE_OUTPUTS_31, __DEFINE_OUTPUTS_30, __DEFINE_OUTPUTS_29, __DEFINE_OUTPUTS_28, __DEFINE_OUTPUTS_27, __DEFINE_OUTPUTS_26, __DEFINE_OUTPUTS_25, __DEFINE_OUTPUTS_24, __DEFINE_OUTPUTS_23, __DEFINE_OUTPUTS_22, __DEFINE_OUTPUTS_21, __DEFINE_OUTPUTS_20, __DEFINE_OUTPUTS_19, __DEFINE_OUTPUTS_18, __DEFINE_OUTPUTS_17, __DEFINE_OUTPUTS_16, __DEFINE_OUTPUTS_15, __DEFINE_OUTPUTS_14, __DEFINE_OUTPUTS_13, __DEFINE_OUTPUTS_12, __DEFINE_OUTPUTS_11, __DEFINE_OUTPUTS_10, __DEFINE_OUTPUTS_9, __DEFINE_OUTPUTS_8, __DEFINE_OUTPUTS_7, __DEFINE_OUTPUTS_6, __DEFINE_OUTPUTS_5, __DEFINE_OUTPUTS_4, __DEFINE_OUTPUTS_3, __DEFINE_OUTPUTS_2, __DEFINE_OUTPUTS_1, __DEFINE_OUTPUTS_0)(__VA_ARGS__)) + +#define __REVERSE_0(a) a +#define __REVERSE_1(a,b) b,a +#define __REVERSE_2(a,...) EXPAND(__REVERSE_1(__VA_ARGS__)),a +#define __REVERSE_3(a,...) EXPAND(__REVERSE_2(__VA_ARGS__)),a +#define __REVERSE_4(a,...) EXPAND(__REVERSE_3(__VA_ARGS__)),a +#define __REVERSE_5(a,...) EXPAND(__REVERSE_4(__VA_ARGS__)),a +#define __REVERSE_6(a,...) EXPAND(__REVERSE_5(__VA_ARGS__)),a +#define __REVERSE_7(a,...) EXPAND(__REVERSE_6(__VA_ARGS__)),a +#define __REVERSE_8(a,...) EXPAND(__REVERSE_7(__VA_ARGS__)),a +#define __REVERSE_9(a,...) EXPAND(__REVERSE_8(__VA_ARGS__)),a +#define __REVERSE_10(a,...) EXPAND(__REVERSE_9(__VA_ARGS__)),a +#define __REVERSE_11(a,...) EXPAND(__REVERSE_10(__VA_ARGS__)),a +#define __REVERSE_12(a,...) EXPAND(__REVERSE_11(__VA_ARGS__)),a +#define __REVERSE_13(a,...) EXPAND(__REVERSE_12(__VA_ARGS__)),a +#define __REVERSE_14(a,...) EXPAND(__REVERSE_13(__VA_ARGS__)),a +#define __REVERSE_15(a,...) EXPAND(__REVERSE_14(__VA_ARGS__)),a +#define __REVERSE_16(a,...) EXPAND(__REVERSE_15(__VA_ARGS__)),a +#define __REVERSE_17(a,...) EXPAND(__REVERSE_16(__VA_ARGS__)),a +#define __REVERSE_18(a,...) EXPAND(__REVERSE_17(__VA_ARGS__)),a +#define __REVERSE_19(a,...) EXPAND(__REVERSE_18(__VA_ARGS__)),a +#define __REVERSE_20(a,...) EXPAND(__REVERSE_19(__VA_ARGS__)),a +#define __REVERSE_21(a,...) EXPAND(__REVERSE_20(__VA_ARGS__)),a +#define __REVERSE_22(a,...) EXPAND(__REVERSE_21(__VA_ARGS__)),a +#define __REVERSE_23(a,...) EXPAND(__REVERSE_22(__VA_ARGS__)),a +#define __REVERSE_24(a,...) EXPAND(__REVERSE_23(__VA_ARGS__)),a +#define __REVERSE_25(a,...) EXPAND(__REVERSE_24(__VA_ARGS__)),a +#define __REVERSE_26(a,...) EXPAND(__REVERSE_25(__VA_ARGS__)),a +#define __REVERSE_27(a,...) EXPAND(__REVERSE_26(__VA_ARGS__)),a +#define __REVERSE_28(a,...) EXPAND(__REVERSE_27(__VA_ARGS__)),a +#define __REVERSE_29(a,...) EXPAND(__REVERSE_28(__VA_ARGS__)),a +#define __REVERSE_30(a,...) EXPAND(__REVERSE_29(__VA_ARGS__)),a +#define __REVERSE_31(a,...) EXPAND(__REVERSE_30(__VA_ARGS__)),a +#define __REVERSE_32(a,...) EXPAND(__REVERSE_31(__VA_ARGS__)),a + +#define DEFINE_INPUTS(...) DEFINE_INPUTS_REVERSED(EXPAND(_GET_NTH_ARG(__VA_ARGS__, __REVERSE_32, __REVERSE_31, __REVERSE_30, __REVERSE_29, __REVERSE_28, __REVERSE_27, __REVERSE_26, __REVERSE_25, __REVERSE_24, __REVERSE_23, __REVERSE_22, __REVERSE_21, __REVERSE_20, __REVERSE_19, __REVERSE_18, __REVERSE_17, __REVERSE_16, __REVERSE_15, __REVERSE_14, __REVERSE_13, __REVERSE_12, __REVERSE_11, __REVERSE_10, __REVERSE_9, __REVERSE_8, __REVERSE_7, __REVERSE_6, __REVERSE_5, __REVERSE_4, __REVERSE_3, __REVERSE_2, __REVERSE_1, __REVERSE_0)(__VA_ARGS__))) +#define DEFINE_OUTPUTS(...) DEFINE_OUTPUTS_REVERSED(EXPAND(_GET_NTH_ARG(__VA_ARGS__, __REVERSE_32, __REVERSE_31, __REVERSE_30, __REVERSE_29, __REVERSE_28, __REVERSE_27, __REVERSE_26, __REVERSE_25, __REVERSE_24, __REVERSE_23, __REVERSE_22, __REVERSE_21, __REVERSE_20, __REVERSE_19, __REVERSE_18, __REVERSE_17, __REVERSE_16, __REVERSE_15, __REVERSE_14, __REVERSE_13, __REVERSE_12, __REVERSE_11, __REVERSE_10, __REVERSE_9, __REVERSE_8, __REVERSE_7, __REVERSE_6, __REVERSE_5, __REVERSE_4, __REVERSE_3, __REVERSE_2, __REVERSE_1, __REVERSE_0)(__VA_ARGS__))) + +#define NO_INPUTS +#define NO_OUTPUTS \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelpers.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelpers.h new file mode 100644 index 00000000..773c3906 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelpers.h @@ -0,0 +1,127 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelContext.h" +#include "VoxelGraphGlobals.h" +#include "Runtime/VoxelComputeNode.h" + +struct VOXELGRAPH_API FVoxelNodeHelpers +{ + template + static double ComputeStatsImplEx(const T* This, bool bSimulateCpp, int32 NumberOfLoops, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) + { + FVoxelNodeType Outputs[MAX_VOXELNODE_PINS]; + + const uint64 Start = FPlatformTime::Cycles64(); + if (bSimulateCpp) + { + for (int32 Index = 0; Index < NumberOfLoops; Index++) + { + This->T::Compute(Inputs, Outputs, Context); + } + } + else + { + for (int32 Index = 0; Index < NumberOfLoops; Index++) + { + (This->*(static_cast(This)->ComputeFunctionPtr))(Inputs, Outputs, Context); + } + } + const uint64 End = FPlatformTime::Cycles64(); + + // Make sure it's not optimizing away the loop above + { + float Value = 0; + for (int32 Index = 0; Index < MAX_VOXELNODE_PINS; Index++) + { + Value += Inputs[Index].Get(); + Value += Outputs[Index].Get(); + } + if (Value == PI) + { + LOG_VOXEL(Log, TEXT("Win!")); + } + } + + return FPlatformTime::ToSeconds64(End - Start); + } + + template + static double ComputeStatsImpl(const T* This, double Threshold, bool bSimulateCpp, FVoxelNodeInputBuffer Inputs, const FVoxelContext& Context) + { + double Duration; + int32 NumberOfLoops = 1000; + + do + { + NumberOfLoops *= 10; + Duration = ComputeStatsImplEx(This, bSimulateCpp, NumberOfLoops, Inputs, Context); + } while (Duration < Threshold); + + return Duration / double(NumberOfLoops); + } + +public: + template + static double ComputeRangeStatsImplEx(const T* This, bool bSimulateCpp, int32 NumberOfLoops, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) + { + FVoxelNodeRangeType Outputs[MAX_VOXELNODE_PINS]; + + const uint64 Start = FPlatformTime::Cycles64(); + if (bSimulateCpp) + { + for (int32 Index = 0; Index < NumberOfLoops; Index++) + { + This->T::ComputeRange(Inputs, Outputs, Context); + } + } + else + { + for (int32 Index = 0; Index < NumberOfLoops; Index++) + { + static_cast(This)->ComputeRange(Inputs, Outputs, Context); + } + } + const uint64 End = FPlatformTime::Cycles64(); + + // Make sure it's not optimizing away the loop above + { + float Value = 0; + for (int32 Index = 0; Index < MAX_VOXELNODE_PINS; Index++) + { + Value += Inputs[Index].Get().Min; + Value += Inputs[Index].Get().Max; + Value += Outputs[Index].Get().Min; + Value += Outputs[Index].Get().Max; + } + if (Value == PI) + { + LOG_VOXEL(Log, TEXT("Win!")); + } + } + + return FPlatformTime::ToSeconds64(End - Start); + } + + template + static double ComputeRangeStatsImpl(const T* This, double Threshold, bool bSimulateCpp, FVoxelNodeInputRangeBuffer Inputs, const FVoxelContextRange& Context) + { + double Duration; + int32 NumberOfLoops = 1000; + + do + { + NumberOfLoops *= 10; + Duration = ComputeRangeStatsImplEx(This, bSimulateCpp, NumberOfLoops, Inputs, Context); + } while (Duration < Threshold); + + return Duration / double(NumberOfLoops); + } + +public: + static void ReplaceInputsOutputs(FString& S, const TArray& Inputs, const TArray& Outputs); + static FString GetPrefixOpLoopString(const TArray& Inputs, const TArray& Outputs, int32 InputCount, const FString& Op); + static FString GetInfixOpLoopString(const TArray& Inputs, const TArray& Outputs, int32 InputCount, const FString& Op); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeStructs.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeStructs.h new file mode 100644 index 00000000..ef632522 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeStructs.h @@ -0,0 +1,19 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelPinCategory.h" +#include "VoxelNodeStructs.generated.h" + +USTRUCT() +struct FVoxelNamedDataPin +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + EVoxelDataPinCategory Type = EVoxelDataPinCategory::Float; + + UPROPERTY(EditAnywhere, Category = "Voxel") + FString Name = "Value"; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeVariables.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeVariables.h new file mode 100644 index 00000000..efd8b6c4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeVariables.h @@ -0,0 +1,97 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "CppTranslation/VoxelVariables.h" + +class UVoxelDataAsset; +class UTexture2D; +class UCurveFloat; +class UCurveLinearColor; +class UMaterialInterface; +class UVoxelHeightmapAssetFloat; +class UVoxelHeightmapAssetUINT16; +class UVoxelNode_GeneratorMerge; +struct FVoxelGeneratorPicker; + +class VOXELGRAPH_API FVoxelColorTextureVariable : public FVoxelExposedVariable +{ +public: + FVoxelColorTextureVariable(const UVoxelExposedNode& Node, UTexture2D* Texture); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelFloatTextureVariable : public FVoxelExposedVariable +{ +public: + FVoxelFloatTextureVariable(const UVoxelExposedNode& Node); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelCurveVariable : public FVoxelExposedVariable +{ +public: + FVoxelCurveVariable(const UVoxelExposedNode& Node, UCurveFloat* Curve); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelColorCurveVariable : public FVoxelExposedVariable +{ +public: + FVoxelColorCurveVariable(const UVoxelExposedNode& Node, UCurveLinearColor* Curve); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelHeightmapVariable : public FVoxelExposedVariable +{ +public: + FVoxelHeightmapVariable(const UVoxelExposedNode& Node, UVoxelHeightmapAssetFloat* Heightmap); + FVoxelHeightmapVariable(const UVoxelExposedNode& Node, UVoxelHeightmapAssetUINT16* Heightmap); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelDataAssetVariable : public FVoxelExposedVariable +{ +public: + FVoxelDataAssetVariable(const UVoxelExposedNode& Node, UVoxelDataAsset* Asset); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelGeneratorVariable : public FVoxelExposedVariable +{ +public: + FVoxelGeneratorVariable(const UVoxelExposedNode& Node, const FVoxelGeneratorPicker& Generator); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelGeneratorArrayVariable : public FVoxelExposedVariable +{ +public: + FVoxelGeneratorArrayVariable(const UVoxelExposedNode& Node, const TArray& Generators); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelMaterialInterfaceVariable : public FVoxelExposedVariable +{ +public: + FVoxelMaterialInterfaceVariable(const UVoxelExposedNode& Node, UMaterialInterface* Material); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; + +class VOXELGRAPH_API FVoxelColorVariable : public FVoxelExposedVariable +{ +public: + FVoxelColorVariable(const UVoxelExposedNode& Node, FLinearColor Color); + + virtual FString GetLocalVariableFromExposedOne(const FString& ExposedNameAccessor) const override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodeHelper.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodeHelper.h new file mode 100644 index 00000000..f877dc85 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodeHelper.h @@ -0,0 +1,87 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "FastNoise/VoxelFastNoise.h" +#include "CppTranslation/VoxelCppUtils.h" +#include "CppTranslation/VoxelCppConstructor.h" + +class IVoxelNoiseNodeHelper +{ +public: + virtual ~IVoxelNoiseNodeHelper() = default; + +#define FUNCTION(Name, ArgType) virtual void Name(ArgType Value) const = 0; + FUNCTION(SetInterpolation, EVoxelNoiseInterpolation); + FUNCTION(SetFractalLacunarity, v_flt); + FUNCTION(SetFractalType, EVoxelNoiseFractalType); + FUNCTION(SetMatrixFromRotation_2D, float); + FUNCTION(SetMatrixFromRotation_3D, const FRotator&); + FUNCTION(SetCellularDistanceFunction, EVoxelCellularDistanceFunction); + FUNCTION(SetCellularReturnType, EVoxelCellularReturnType); + FUNCTION(SetCellularJitter, v_flt); + FUNCTION(SetCraterFalloffExponent, v_flt); +#undef FUNCTION + + virtual void SetFractalOctavesAndGain(int32 Octaves, v_flt NewGain) const = 0; +}; + +class FVoxelNoiseNodeHelper_Runtime : public IVoxelNoiseNodeHelper +{ +public: + FVoxelFastNoise& Noise; + + explicit FVoxelNoiseNodeHelper_Runtime(FVoxelFastNoise& Noise) + : Noise(Noise) + { + } + +#define FUNCTION(Name, ArgType) virtual void Name(ArgType Value) const override { Noise.Name(Value); } + FUNCTION(SetInterpolation, EVoxelNoiseInterpolation); + FUNCTION(SetFractalLacunarity, v_flt); + FUNCTION(SetFractalType, EVoxelNoiseFractalType); + FUNCTION(SetMatrixFromRotation_2D, float); + FUNCTION(SetMatrixFromRotation_3D, const FRotator&); + FUNCTION(SetCellularDistanceFunction, EVoxelCellularDistanceFunction); + FUNCTION(SetCellularReturnType, EVoxelCellularReturnType); + FUNCTION(SetCellularJitter, v_flt); + FUNCTION(SetCraterFalloffExponent, v_flt); +#undef FUNCTION + + virtual void SetFractalOctavesAndGain(int32 Octaves, v_flt NewGain) const override + { + Noise.SetFractalOctavesAndGain(Octaves, NewGain); + } +}; + +class FVoxelNoiseNodeHelper_Cpp : public IVoxelNoiseNodeHelper +{ +public: + FString NoiseName; + FVoxelCppConstructor& Constructor; + + FVoxelNoiseNodeHelper_Cpp(const FString& NoiseName, FVoxelCppConstructor& Constructor) + : NoiseName(NoiseName) + , Constructor(Constructor) + { + } + +#define FUNCTION(Name, ArgType) virtual void Name(ArgType Value) const override { Constructor.AddLinef(TEXT("%s.%s(%s);"), *NoiseName, *FString(#Name), *FVoxelCppUtils::LexToCpp(Value)); } + FUNCTION(SetInterpolation, EVoxelNoiseInterpolation); + FUNCTION(SetFractalLacunarity, v_flt); + FUNCTION(SetFractalType, EVoxelNoiseFractalType); + FUNCTION(SetMatrixFromRotation_2D, float); + FUNCTION(SetMatrixFromRotation_3D, const FRotator&); + FUNCTION(SetCellularDistanceFunction, EVoxelCellularDistanceFunction); + FUNCTION(SetCellularReturnType, EVoxelCellularReturnType); + FUNCTION(SetCellularJitter, v_flt); + FUNCTION(SetCraterFalloffExponent, v_flt); +#undef FUNCTION + + virtual void SetFractalOctavesAndGain(int32 Octaves, v_flt NewGain) const override + { + Constructor.AddLinef(TEXT("%s.SetFractalOctavesAndGain(%s, %s);"), *NoiseName, *FVoxelCppUtils::LexToCpp(Octaves), *FVoxelCppUtils::LexToCpp(NewGain)); + } +}; + diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h new file mode 100644 index 00000000..90dcf3fd --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h @@ -0,0 +1,317 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNoiseNodesBase.h" +#include "VoxelNoiseNodesMacros.h" +#include "VoxelNoiseNodes.generated.h" + +// 2D Value Noise +UCLASS(DisplayName = "2D Value Noise", Category = "Noise|Value Noise") +class VOXELGRAPH_API UVoxelNode_2DValueNoise : public UVoxelNode_NoiseNodeWithDerivative +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DERIVATIVE_DIM2(GetValue) +}; + +// 2D Value Noise Fractal +UCLASS(DisplayName = "2D Value Noise Fractal", Category = "Noise|Value Noise") +class VOXELGRAPH_API UVoxelNode_2DValueNoiseFractal : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM2(GetValueFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 2D Perlin Noise +UCLASS(DisplayName = "2D Perlin Noise", Category = "Noise|Perlin Noise") +class VOXELGRAPH_API UVoxelNode_2DPerlinNoise : public UVoxelNode_NoiseNodeWithDerivative +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DERIVATIVE_DIM2(GetPerlin) +}; + +// 2D Perlin Noise Fractal +UCLASS(DisplayName = "2D Perlin Noise Fractal", Category = "Noise|Perlin Noise") +class VOXELGRAPH_API UVoxelNode_2DPerlinNoiseFractal : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM2(GetPerlinFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 2D Simplex Noise +UCLASS(DisplayName = "2D Simplex Noise", Category = "Noise|Simplex Noise") +class VOXELGRAPH_API UVoxelNode_2DSimplexNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM2(GetSimplex) +}; + +// 2D Simplex Noise Fractal +UCLASS(DisplayName = "2D Simplex Noise Fractal", Category = "Noise|Simplex Noise") +class VOXELGRAPH_API UVoxelNode_2DSimplexNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DIM2(GetSimplexFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 2D Cubic Noise +UCLASS(DisplayName = "2D Cubic Noise", Category = "Noise|Cubic Noise") +class VOXELGRAPH_API UVoxelNode_2DCubicNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM2(GetCubic) +}; + +// 2D Cubic Noise Fractal +UCLASS(DisplayName = "2D Cubic Noise Fractal", Category = "Noise|Cubic Noise") +class VOXELGRAPH_API UVoxelNode_2DCubicNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DIM2(GetCubicFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// 3D Value Noise +UCLASS(DisplayName = "3D Value Noise", Category = "Noise|Value Noise") +class VOXELGRAPH_API UVoxelNode_3DValueNoise : public UVoxelNode_NoiseNodeWithDerivative +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DERIVATIVE_DIM3(GetValue) +}; + +// 3D Value Noise Fractal +UCLASS(DisplayName = "3D Value Noise Fractal", Category = "Noise|Value Noise") +class VOXELGRAPH_API UVoxelNode_3DValueNoiseFractal : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM3(GetValueFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 3D Perlin Noise +UCLASS(DisplayName = "3D Perlin Noise", Category = "Noise|Perlin Noise") +class VOXELGRAPH_API UVoxelNode_3DPerlinNoise : public UVoxelNode_NoiseNodeWithDerivative +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DERIVATIVE_DIM3(GetPerlin) +}; + +// 3D Perlin Noise Fractal +UCLASS(DisplayName = "3D Perlin Noise Fractal", Category = "Noise|Perlin Noise") +class VOXELGRAPH_API UVoxelNode_3DPerlinNoiseFractal : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM3(GetPerlinFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 3D Simplex Noise +UCLASS(DisplayName = "3D Simplex Noise", Category = "Noise|Simplex Noise") +class VOXELGRAPH_API UVoxelNode_3DSimplexNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM3(GetSimplex) +}; + +// 3D Simplex Noise Fractal +UCLASS(DisplayName = "3D Simplex Noise Fractal", Category = "Noise|Simplex Noise") +class VOXELGRAPH_API UVoxelNode_3DSimplexNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DIM3(GetSimplexFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// + +// 3D Cubic Noise +UCLASS(DisplayName = "3D Cubic Noise", Category = "Noise|Cubic Noise") +class VOXELGRAPH_API UVoxelNode_3DCubicNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM3(GetCubic) +}; + +// 3D Cubic Noise Fractal +UCLASS(DisplayName = "3D Cubic Noise Fractal", Category = "Noise|Cubic Noise") +class VOXELGRAPH_API UVoxelNode_3DCubicNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_FRACTAL_DIM3(GetCubicFractal) +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_CellularNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Cellular Noise settings") + EVoxelCellularDistanceFunction DistanceFunction; + + UPROPERTY(EditAnywhere, Category = "Cellular Noise settings") + EVoxelCellularReturnType ReturnType; + + UPROPERTY(EditAnywhere, Category = "Cellular Noise settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.45; +}; + +class FVoxelCellularNoiseComputeNode : public FVoxelNoiseComputeNode +{ +public: + FVoxelCellularNoiseComputeNode(const UVoxelNode_CellularNoise& Node, const FVoxelCompilationNode& CompilationNode); + + void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + +protected: + const EVoxelCellularDistanceFunction DistanceFunction; + const EVoxelCellularReturnType ReturnType; + const float Jitter; +}; + +// 2D Cellular Noise +UCLASS(DisplayName = "2D Cellular Noise", Category = "Noise|Cellular Noise") +class VOXELGRAPH_API UVoxelNode_2DCellularNoise : public UVoxelNode_CellularNoise +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(2,,, GetCellular, FVoxelCellularNoiseComputeNode) +}; + +// 3D Cellular Noise +UCLASS(DisplayName = "3D Cellular Noise", Category = "Noise|Cellular Noise") +class VOXELGRAPH_API UVoxelNode_3DCellularNoise : public UVoxelNode_CellularNoise +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(3,,, GetCellular, FVoxelCellularNoiseComputeNode) +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// 2D IQ Noise: uses the derivative to "smooth" the fractals. +// For more details: +// http://www.iquilezles.org/www/articles/morenoise/morenoise.htm +// http://www.decarpentier.nl/scape-procedural-basics +UCLASS(DisplayName = "2D IQ Noise", Category = "Noise|IQ Noise") +class VOXELGRAPH_API UVoxelNode_2DIQNoise : public UVoxelNode_2DIQNoiseBase +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(2, _DERIV, _FRACTAL, IQNoise, FVoxel2DIQNoiseComputeNode) +}; + +// 3D IQ Noise: uses the derivative to "smooth" the fractals. +// For more details: +// http://www.iquilezles.org/www/articles/morenoise/morenoise.htm +// http://www.decarpentier.nl/scape-procedural-basics +UCLASS(DisplayName = "3D IQ Noise", Category = "Noise|IQ Noise") +class VOXELGRAPH_API UVoxelNode_3DIQNoise : public UVoxelNode_3DIQNoiseBase +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(3, _DERIV, _FRACTAL, IQNoise, FVoxel3DIQNoiseComputeNode) +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_CraterNoise : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Crater Noise settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.45; + + // Higher value = flatter crater borders + // 0 to disable (much faster disabled) + UPROPERTY(EditAnywhere, Category = "Crater Noise settings") + float FalloffExponent = 0.f; +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_CraterNoiseFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Crater Noise settings", meta = (UIMin = 0, UIMax = 0.5)) + float Jitter = 0.45; + + // Higher value = flatter crater borders + // 0 to disable (much faster disabled) + UPROPERTY(EditAnywhere, Category = "Crater Noise settings") + float FalloffExponent = 0.f; +}; + +class FVoxelCraterNoiseComputeNode : public FVoxelNoiseComputeNode +{ +public: + FVoxelCraterNoiseComputeNode(const UVoxelNode_CraterNoise& Node, const FVoxelCompilationNode& CompilationNode); + + void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + +protected: + const float Jitter; + const float FalloffExponent; +}; + +class FVoxelCraterNoiseFractalComputeNode : public FVoxelNoiseFractalComputeNode +{ +public: + FVoxelCraterNoiseFractalComputeNode(const UVoxelNode_CraterNoiseFractal& Node, const FVoxelCompilationNode& CompilationNode); + + void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + +protected: + const float Jitter; + const float FalloffExponent; +}; + +// 2D Crater Noise +UCLASS(DisplayName = "2D Crater Noise", Category = "Noise|Crater Noise") +class VOXELGRAPH_API UVoxelNode_2DCraterNoise : public UVoxelNode_CraterNoise +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(2,,, GetCrater, FVoxelCraterNoiseComputeNode) +}; + +// 3D Crater Noise +UCLASS(DisplayName = "3D Crater Noise", Category = "Noise|Crater Noise") +class VOXELGRAPH_API UVoxelNode_3DCraterNoise : public UVoxelNode_CraterNoise +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(3,,, GetCrater, FVoxelCraterNoiseComputeNode) +}; + +// 2D Crater Noise Fractal +UCLASS(DisplayName = "2D Crater Noise Fractal", Category = "Noise|Crater Noise") +class VOXELGRAPH_API UVoxelNode_2DCraterNoiseFractal : public UVoxelNode_CraterNoiseFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(2,, _FRACTAL, GetCraterFractal, FVoxelCraterNoiseFractalComputeNode) +}; + +// 3D Crater Noise Fractal +UCLASS(DisplayName = "3D Crater Noise Fractal", Category = "Noise|Crater Noise") +class VOXELGRAPH_API UVoxelNode_3DCraterNoiseFractal : public UVoxelNode_CraterNoiseFractal +{ + GENERATED_BODY() + GENERATED_NOISENODE_BODY_DIM_IMPL(3,, _FRACTAL, GetCraterFractal, FVoxelCraterNoiseFractalComputeNode) +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h new file mode 100644 index 00000000..079f0f22 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h @@ -0,0 +1,283 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/StaticArray.h" +#include "VoxelRange.h" +#include "VoxelNode.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "FastNoise/VoxelFastNoise.h" +#include "CppTranslation/VoxelVariables.h" +#include "Runtime/VoxelComputeNode.h" +#include "VoxelNoiseNodesBase.generated.h" + +class FVoxelCppConstructor; +class IVoxelNoiseNodeHelper; +struct FVoxelGeneratorInit; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_NoiseNode : public UVoxelNodeWithContext // For LOD for octaves +{ + GENERATED_BODY() + +public: + // Do not use here, exposed as pin now + UPROPERTY(VisibleAnywhere, AdvancedDisplay, Category = "Noise settings", meta = (DisplayName = "Old Frequency")) + float Frequency = 0.02; + + UPROPERTY(EditAnywhere, Category = "Noise settings") + EVoxelNoiseInterpolation Interpolation = EVoxelNoiseInterpolation::Quintic; + + // To find the output range, NumberOfSamples random samples are computed + // Increase this if the output range is too irregular, and if you start to see flat areas in your noise + UPROPERTY(EditAnywhere, Category = "Range analysis settings") + uint32 NumberOfSamples = 1000000; + + // The range analysis interval will be expended by this much (relatively). Increase if you see flat areas in your noise + UPROPERTY(EditAnywhere, Category = "Range analysis settings", meta = (UIMin = 0, UIMax = 1)) + float Tolerance = 0.1f; + + UPROPERTY(VisibleAnywhere, Category = "Range analysis settings") + TArray OutputRanges; + + //~ Begin UVoxelNode Interface + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + virtual FString GetInputPinToolTip(int32 PinIndex) const override; + virtual FString GetOutputPinToolTip(int32 PinIndex) const override; + virtual int32 GetMinInputPins() const override { return GetBaseInputPinsCount() + CustomNoisePins.InputPins.Num(); } + virtual int32 GetMaxInputPins() const override { return GetMinInputPins(); } + virtual int32 GetOutputPinsCount() const override { return GetBaseOutputPinsCount() + CustomNoisePins.OutputPins.Num(); } + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override; + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelNode_NoiseNode Interface + virtual uint32 GetDimension() const { unimplemented(); return 0; } + virtual bool IsDerivative() const { return false; } + virtual bool IsFractal() const { return false; } + virtual bool NeedRangeAnalysis() const { return true; } + virtual void ComputeOutputRanges(); + // Returns num input pins to detect errors + virtual int32 SetupInputsForComputeOutputRanges(TVoxelStaticArray& Inputs) const; + //~ End UVoxelNode_NoiseNode Interface + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual bool CanEditChange(const FProperty* InProperty) const override; +#endif + virtual void PostLoad() override; + virtual void PostInitProperties() override; + //~ End UObject Interface + +protected: + FVoxelPinsHelper CustomNoisePins; + + int32 GetBaseInputPinsCount() const { return GetDimension() + 2; } + int32 GetBaseOutputPinsCount() const { return IsDerivative() ? GetDimension() + 1 : 1; } +}; + +class FVoxelNoiseComputeNode : public FVoxelDataComputeNode +{ +public: + const uint32 Dimension; + const bool bIsDerivative; + const bool bIsFractal; + + FVoxelNoiseComputeNode(const UVoxelNode_NoiseNode& Node, const FVoxelCompilationNode& CompilationNode); + + void Init(FVoxelGraphSeed Inputs[], const FVoxelGeneratorInit& InitStruct) override; + void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override; + + void ComputeRange(FVoxelNodeInputRangeBuffer Inputs, FVoxelNodeOutputRangeBuffer Outputs, const FVoxelContextRange& Context) const override; + void ComputeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override; + void ComputeRangeCpp(const TArray& Inputs, const TArray& Outputs, FVoxelCppConstructor& Constructor) const override; + + void GetPrivateVariables(TArray& PrivateVariables) const override; + + //~ Begin FVoxelNoiseComputeNode Interface + virtual FString GetFunctionName() const { return ""; } + virtual int32 GetSeedInputIndex() const { return Dimension + 1; } + + virtual void InitNoise(const IVoxelNoiseNodeHelper& Noise) const; + //~ End FVoxelNoiseComputeNode Interface + +protected: + const FVoxelFastNoise& GetNoise() const { return PrivateNoise; } + const FString& GetNoiseName() const { return NoiseVariable.CppName; } + const TVoxelStaticArray, 4>& GetOutputRanges() const { return OutputRanges; } + + FORCEINLINE void Clamp(FVoxelNodeOutputBuffer Outputs, int32 OutputIndex) const + { + Outputs[OutputIndex].Get() = FMath::Clamp(Outputs[OutputIndex].Get(), OutputRanges[OutputIndex].Min, OutputRanges[OutputIndex].Max); + } + void Clamp(FVoxelCppConstructor& Constructor, const TArray& Outputs, int32 OutputIndex) const; + +private: + const EVoxelNoiseInterpolation Interpolation; + const TVoxelStaticArray, 4> OutputRanges; + const FVoxelVariable NoiseVariable; + FVoxelFastNoise PrivateNoise; + +public: + static void ComputeOutputRanges(UVoxelNode_NoiseNode& Node); + static void ExpandRanges(TArray& Ranges, float Tolerance); +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_NoiseNodeFractal : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Fractal Noise settings", meta = (UIMin = 1, ClampMin = 1)) + int32 FractalOctaves = 3; + + // A multiplier that determines how quickly the frequency increases for each successive octave + // The frequency of each successive octave is equal to the product of the previous octave's frequency and the lacunarity value. + UPROPERTY(EditAnywhere, Category = "Fractal Noise settings", meta = (UIMin = 0)) + float FractalLacunarity = 2; + + // A multiplier that determines how quickly the amplitudes diminish for each successive octave + // The amplitude of each successive octave is equal to the product of the previous octave's amplitude and the gain value. Increasing the gain produces "rougher" Perlin noise. + UPROPERTY(EditAnywhere, Category = "Fractal Noise settings", meta = (UIMin = 0, UIMax = 1)) + float FractalGain = 0.5; + + UPROPERTY(EditAnywhere, Category = "Fractal Noise settings") + EVoxelNoiseFractalType FractalType = EVoxelNoiseFractalType::FBM; + + // To use lower quality noise for far LODs + UPROPERTY(EditAnywhere, Category = "LOD settings", meta = (DisplayName = "LOD to Octaves map")) + TMap LODToOctavesMap; + + //~ Begin UVoxelNode Interface + virtual FLinearColor GetNodeBodyColor() const override; + virtual FLinearColor GetColor() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelNode_NoiseNode Interface + virtual bool IsFractal() const override final { return true; } + //~ End UVoxelNode_NoiseNode Interface + +protected: + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + virtual void PostLoad() override; + virtual void PostInitProperties() override; + //~ End UObject Interface +}; + +class FVoxelNoiseFractalComputeNode : public FVoxelNoiseComputeNode +{ +public: + FVoxelNoiseFractalComputeNode(const UVoxelNode_NoiseNodeFractal& Node, const FVoxelCompilationNode& CompilationNode); + + virtual void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + + virtual void InitCpp(const TArray& Inputs, FVoxelCppConstructor& Constructor) const override; + virtual void GetPrivateVariables(TArray& PrivateVariables) const override; + + const uint8 FractalOctaves; + const float FractalLacunarity; + const float FractalGain; + const EVoxelNoiseFractalType FractalType; + const TStaticArray LODToOctaves; + const FVoxelVariable LODToOctavesVariable; +}; + +#define GET_OCTAVES LODToOctaves[FMath::Clamp(Context.LOD, 0, 31)] +#define GET_OCTAVES_CPP FString(static_cast(*this).LODToOctavesVariable.CppName + "[FMath::Clamp(" + FVoxelCppIds::Context + ".LOD, 0, 31)]") + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_NoiseNodeWithDerivative : public UVoxelNode_NoiseNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Noise settings", meta = (ReconstructNode)) + bool bComputeDerivative = false; + + virtual bool IsDerivative() const override { return bComputeDerivative; } +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_NoiseNodeWithDerivativeFractal : public UVoxelNode_NoiseNodeFractal +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Noise settings", meta = (ReconstructNode)) + bool bComputeDerivative = false; + + virtual bool IsDerivative() const override { return bComputeDerivative; } +}; + +////////////////////////////////////////////////////////////////////////////////////// + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_IQNoiseBase : public UVoxelNode_NoiseNodeWithDerivativeFractal +{ + GENERATED_BODY() + +public: + UVoxelNode_IQNoiseBase(); + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual bool CanEditChange(const FProperty* InProperty) const override; +#endif + //~ End UObject Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_2DIQNoiseBase : public UVoxelNode_IQNoiseBase +{ + GENERATED_BODY() + +public: + // Rotation (in degrees) applied to the position between each octave + UPROPERTY(EditAnywhere, Category = "IQ Noise settings") + float Rotation = 40; +}; + +class FVoxel2DIQNoiseComputeNode : public FVoxelNoiseFractalComputeNode +{ +public: + FVoxel2DIQNoiseComputeNode(const UVoxelNode_2DIQNoiseBase& Node, const FVoxelCompilationNode& CompilationNode); + + void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + +private: + const float Rotation; +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_3DIQNoiseBase : public UVoxelNode_IQNoiseBase +{ + GENERATED_BODY() + +public: + // Rotation (in degrees) applied to the position between each octave + UPROPERTY(EditAnywhere, Category = "IQ Noise settings") + FRotator Rotation = { 40, 45, 50 }; +}; + +class FVoxel3DIQNoiseComputeNode : public FVoxelNoiseFractalComputeNode +{ +public: + FVoxel3DIQNoiseComputeNode(const UVoxelNode_3DIQNoiseBase& Node, const FVoxelCompilationNode& CompilationNode); + + void InitNoise(const IVoxelNoiseNodeHelper& Noise) const override; + +private: + const FRotator Rotation; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h new file mode 100644 index 00000000..04113094 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h @@ -0,0 +1,240 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelpers.h" +#include "FastNoise/VoxelFastNoise.inl" + + +template +class TVoxelNoiseComputeNode : public TParent +{ +public: + using TParent::TParent; + GENERATED_DATA_COMPUTE_NODE_BODY_IMPL(TVoxelNoiseComputeNode) + + FORCEINLINE static int32 GetOctaves(const FVoxelNoiseComputeNode& Node, int32 LOD) + { + ensure(false); + return 0; + } + FORCEINLINE static int32 GetOctaves(const FVoxelNoiseFractalComputeNode& Node, int32 LOD) + { + return Node.LODToOctaves[FMath::Clamp(LOD, 0, 31)]; + } + + virtual void Compute(FVoxelNodeInputBuffer Inputs, FVoxelNodeOutputBuffer Outputs, const FVoxelContext& Context) const override final + { + ensureVoxelSlow(Dimension == this->Dimension); + ensureVoxelSlow(bIsFractal == this->bIsFractal); + + const FVoxelFastNoise& Noise = this->GetNoise(); + if (!bIsFractal) + { + if (Dimension == 2) + { + if (this->bIsDerivative) + { + Outputs[0].Get() = TImpl::Call_Deriv( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Outputs[1].Get(), + Outputs[2].Get()); + + this->Clamp(Outputs, 0); + this->Clamp(Outputs, 1); + this->Clamp(Outputs, 2); + } + else + { + Outputs[0].Get() = TImpl::Call( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get()); + + this->Clamp(Outputs, 0); + } + } + else + { + if (this->bIsDerivative) + { + Outputs[0].Get() = TImpl::Call_Deriv( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[3].Get(), + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + + this->Clamp(Outputs, 0); + this->Clamp(Outputs, 1); + this->Clamp(Outputs, 2); + this->Clamp(Outputs, 3); + } + else + { + Outputs[0].Get() = TImpl::Call( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[3].Get()); + + this->Clamp(Outputs, 0); + } + } + } + else + { + const int32 Octaves = GetOctaves(*this, Context.LOD); + if (Dimension == 2) + { + if (this->bIsDerivative) + { + Outputs[0].Get() = TImpl::Call_Deriv( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Octaves, + Outputs[1].Get(), + Outputs[2].Get()); + + this->Clamp(Outputs, 0); + this->Clamp(Outputs, 1); + this->Clamp(Outputs, 2); + } + else + { + Outputs[0].Get() = TImpl::Call( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Octaves); + + this->Clamp(Outputs, 0); + } + } + else + { + if (this->bIsDerivative) + { + Outputs[0].Get() = TImpl::Call_Deriv( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[3].Get(), + Octaves, + Outputs[1].Get(), + Outputs[2].Get(), + Outputs[3].Get()); + + this->Clamp(Outputs, 0); + this->Clamp(Outputs, 1); + this->Clamp(Outputs, 2); + this->Clamp(Outputs, 3); + } + else + { + Outputs[0].Get() = TImpl::Call( + Noise, + Inputs[0].Get(), + Inputs[1].Get(), + Inputs[2].Get(), + Inputs[3].Get(), + Octaves); + + this->Clamp(Outputs, 0); + } + } + } + } +}; + +#define VOXEL_NOISE_ARG_TYPES_2D v_flt x, v_flt y, v_flt frequency +#define VOXEL_NOISE_ARG_NAMES_2D x, y, frequency +#define VOXEL_NOISE_ARG_TYPES_2D_FRACTAL v_flt x, v_flt y, v_flt frequency, int32 octaves +#define VOXEL_NOISE_ARG_NAMES_2D_FRACTAL x, y, frequency, octaves +#define VOXEL_NOISE_ARG_TYPES_2D_DERIV v_flt x, v_flt y, v_flt frequency , v_flt& outDx, v_flt& outDy +#define VOXEL_NOISE_ARG_NAMES_2D_DERIV x, y, frequency , outDx, outDy +#define VOXEL_NOISE_ARG_TYPES_2D_DERIV_FRACTAL v_flt x, v_flt y, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy +#define VOXEL_NOISE_ARG_NAMES_2D_DERIV_FRACTAL x, y, frequency, octaves, outDx, outDy + +#define VOXEL_NOISE_ARG_TYPES_3D v_flt x, v_flt y, v_flt z, v_flt frequency +#define VOXEL_NOISE_ARG_NAMES_3D x, y, z, frequency +#define VOXEL_NOISE_ARG_TYPES_3D_FRACTAL v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves +#define VOXEL_NOISE_ARG_NAMES_3D_FRACTAL x, y, z, frequency, octaves +#define VOXEL_NOISE_ARG_TYPES_3D_DERIV v_flt x, v_flt y, v_flt z, v_flt frequency , v_flt& outDx, v_flt& outDy, v_flt& outDz +#define VOXEL_NOISE_ARG_NAMES_3D_DERIV x, y, z, frequency , outDx, outDy, outDz +#define VOXEL_NOISE_ARG_TYPES_3D_DERIV_FRACTAL v_flt x, v_flt y, v_flt z, v_flt frequency, int32 octaves, v_flt& outDx, v_flt& outDy, v_flt& outDz +#define VOXEL_NOISE_ARG_NAMES_3D_DERIV_FRACTAL x, y, z, frequency, octaves, outDx, outDy, outDz + +#define VOXEL_NOISE_IS_FRACTAL false +#define VOXEL_NOISE_IS_FRACTAL_FRACTAL true + +#define DEFINE_VOXEL_NOISE_IMPL(Name, ArgTypes, ArgNames, ArgTypesDeriv, ArgNamesDeriv) \ + struct FVoxelNoiseComputeNodeImpl \ + { \ + FORCEINLINE static v_flt Call(const FVoxelFastNoise& Noise, ArgTypes) { return Noise.Name(ArgNames); } \ + FORCEINLINE static v_flt Call(...) { ensure(false); return 0.f; } \ + FORCEINLINE static v_flt Call_Deriv(...) { ensure(false); return 0.f; } \ + }; + +#define DEFINE_VOXEL_NOISE_IMPL_DERIV(Name, ArgTypes, ArgNames, ArgTypesDeriv, ArgNamesDeriv) \ + struct FVoxelNoiseComputeNodeImpl \ + { \ + FORCEINLINE static v_flt Call(const FVoxelFastNoise& Noise, ArgTypes) { return Noise.Name(ArgNames); } \ + FORCEINLINE static v_flt Call(...) { ensure(false); return 0.f; } \ + FORCEINLINE static v_flt Call_Deriv(const FVoxelFastNoise& Noise, ArgTypesDeriv) { return Noise.Name##_Deriv(ArgNamesDeriv); } \ + FORCEINLINE static v_flt Call_Deriv(...) { ensure(false); return 0.f; } \ + }; + +#define GENERATED_NOISENODE_BODY_DIM_IMPL(Dimension, Derivative, Fractal, FunctionName, Parent) \ + virtual uint32 GetDimension() const override final { return Dimension; } \ + TVoxelSharedPtr GetComputeNode(const FVoxelCompilationNode& InCompilationNode) const \ + { \ + return MakeShareable(new FLocalVoxelComputeNode(*this, InCompilationNode)); \ + } \ + DEFINE_VOXEL_NOISE_IMPL##Derivative( \ + FunctionName##_##Dimension##D, \ + VOXEL_NOISE_ARG_TYPES_##Dimension##D##Fractal, \ + VOXEL_NOISE_ARG_NAMES_##Dimension##D##Fractal, \ + VOXEL_NOISE_ARG_TYPES_##Dimension##D##Derivative##Fractal, \ + VOXEL_NOISE_ARG_NAMES_##Dimension##D##Derivative##Fractal) \ + class FLocalVoxelComputeNode : public TVoxelNoiseComputeNode \ + { \ + public: \ + using Super = TVoxelNoiseComputeNode; \ + using Super::Super; \ + virtual FString GetFunctionName() const override { return FString::Printf(TEXT("%s_%dD%s"), TEXT(#FunctionName), Dimension, bIsDerivative ? TEXT("_Deriv") : TEXT("")); } \ + }; + + +#define GENERATED_NOISENODE_BODY_DIM2(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(2,,, FunctionName, FVoxelNoiseComputeNode) +#define GENERATED_NOISENODE_BODY_DIM3(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(3,,, FunctionName, FVoxelNoiseComputeNode) + +#define GENERATED_NOISENODE_BODY_FRACTAL_DIM2(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(2,, _FRACTAL, FunctionName, FVoxelNoiseFractalComputeNode) +#define GENERATED_NOISENODE_BODY_FRACTAL_DIM3(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(3,, _FRACTAL, FunctionName, FVoxelNoiseFractalComputeNode) + +#define GENERATED_NOISENODE_BODY_DERIVATIVE_DIM2(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(2, _DERIV,, FunctionName, FVoxelNoiseComputeNode) +#define GENERATED_NOISENODE_BODY_DERIVATIVE_DIM3(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(3, _DERIV,, FunctionName, FVoxelNoiseComputeNode) + +#define GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM2(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(2, _DERIV, _FRACTAL, FunctionName, FVoxelNoiseFractalComputeNode) +#define GENERATED_NOISENODE_BODY_FRACTAL_DERIVATIVE_DIM3(FunctionName) \ + GENERATED_NOISENODE_BODY_DIM_IMPL(3, _DERIV, _FRACTAL, FunctionName, FVoxelNoiseFractalComputeNode) \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h new file mode 100644 index 00000000..d2023c92 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h @@ -0,0 +1,225 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "Curves/CurveFloat.h" +#include "VoxelOptimizationNodes.generated.h" + +// Nodes before this won't be computed for range analysis +UCLASS(DisplayName = "Static Clamp", Category = "Optimization", meta = (Keywords = "range analysis")) +class VOXELGRAPH_API UVoxelNode_StaticClampFloat : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Static Clamp") + float Min = 0; + + UPROPERTY(EditAnywhere, Category = "Static Clamp") + float Max = 0; + + UVoxelNode_StaticClampFloat(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +// Use this to debug the range of a value. Will plot the runtime values in a graph +UCLASS(DisplayName = "Range Analysis Debugger", Category = "Optimization") +class VOXELGRAPH_API UVoxelNode_RangeAnalysisDebuggerFloat : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(VisibleAnywhere, Category = "Bounds") + float Min = 0; + + UPROPERTY(VisibleAnywhere, Category = "Bounds") + float Max = 0; + +public: + UPROPERTY(EditAnywhere, Category = "Graph") + float GraphMin = -100; + + UPROPERTY(EditAnywhere, Category = "Graph") + float GraphMax = 100; + + UPROPERTY(EditAnywhere, Category = "Graph", meta = (ClampMin = 0.001)) + float GraphStep = 1; + + UPROPERTY(EditAnywhere, Category = "Graph") + FRuntimeFloatCurve Curve; + + struct FVoxelBins + { + const float Min; + const float Max; + const float Step; + + float MinValue = 0; + float MaxValue = 0; + bool bMinMaxInit = false; + TArray Counts; + + FVoxelBins(float Min, float Max, float Step) + : Min(Min) + , Max(Max) + , Step(Step) + { + const int32 Num = FMath::CeilToInt((Max - Min) / Step); + Counts.SetNum(Num); + } + FVoxelBins(const FVoxelBins& Other) + : FVoxelBins(Other.Min, Other.Max, Other.Step) + { + MinValue = Other.MinValue; + MaxValue = Other.MaxValue; + bMinMaxInit = Other.bMinMaxInit; + Counts = Other.Counts; + } + + inline void AddStat(float Value) + { + FScopeLock Lock(&Section); + AddToMinMax(Value); + const int32 Bin = FMath::FloorToInt((Value - Min) / Step); + if (Counts.IsValidIndex(Bin)) + { + Counts[Bin]++; + } + } + + inline void AddOtherBins(const FVoxelBins& Other) + { + FScopeLock Lock(&Section); + if (!ensure(Min == Other.Min && Max == Other.Max && Step == Other.Step && Counts.Num() == Other.Counts.Num())) + { + return; + } + for (int32 Index = 0; Index < Counts.Num(); Index++) + { + Counts[Index] += Other.Counts[Index]; + } + AddToMinMax(Other.MinValue); + AddToMinMax(Other.MaxValue); + } + + inline void AddToCurve(FRichCurve& InCurve) const + { + for (int32 Index = 0; Index < Counts.Num(); Index++) + { + const float Value = Min + Index * Step; + InCurve.AddKey(Value, Counts[Index]); + } + } + + public: + inline void AddToMinMax(float Value) + { + if (!bMinMaxInit) + { + bMinMaxInit = true; + MinValue = Value; + MaxValue = Value; + } + else + { + MinValue = FMath::Min(MinValue, Value); + MaxValue = FMath::Max(MaxValue, Value); + } + } + + FCriticalSection Section; + }; + + TUniquePtr Bins = MakeUnique(GraphMin, GraphMax, GraphStep); + + UVoxelNode_RangeAnalysisDebuggerFloat(); + + void UpdateFromBin(); + void UpdateGraph(); + void Reset(); + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void PostLoad() override; +#endif +}; + +// Runs a for loop +UCLASS(DisplayName = "Stress", Category = "Optimization") +class VOXELGRAPH_API UVoxelNode_Sleep : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 NumberOfLoops = 1000; + + UVoxelNode_Sleep(); +}; + +// In range analysis, does the union of the inputs ranges. In other modes, returns 0 +UCLASS(DisplayName = "Range Union", Category = "Optimization") +class VOXELGRAPH_API UVoxelNode_RangeUnion : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_RangeUnion(); +}; + +// In range analysis, returns false if the condition can be true or false. In other modes, always returns true +UCLASS(DisplayName = "Is Single bool", Category = "Optimization") +class VOXELGRAPH_API UVoxelNode_IsSingleBool : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_IsSingleBool(); +}; + +// Get the range analysis value +UCLASS(DisplayName = "Get Range", Category = "Optimization", meta = (Keywords = "range analysis")) +class VOXELGRAPH_API UVoxelNode_GetRangeAnalysis : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_GetRangeAnalysis(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +UCLASS(DisplayName = "Smart Min", Category = "Optimization", meta = (Keywords = "smart range min fast")) +class VOXELGRAPH_API UVoxelNode_SmartMin : public UVoxelNodeHelper +{ + GENERATED_BODY() + + UVoxelNode_SmartMin(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; + +UCLASS(DisplayName = "Smart Max", Category = "Optimization", meta = (Keywords = "smart range max fast")) +class VOXELGRAPH_API UVoxelNode_SmartMax : public UVoxelNodeHelper +{ + GENERATED_BODY() + + UVoxelNode_SmartMax(); + + //~ Begin UVoxelNode Interface + virtual TSharedPtr GetCompilationNode() const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelParameterNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelParameterNodes.h new file mode 100644 index 00000000..5fbb4ca9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelParameterNodes.h @@ -0,0 +1,85 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelExposedNodes.h" +#include "VoxelParameterNodes.generated.h" + +// Float parameter +UCLASS(DisplayName = "float parameter", meta = (Keywords = "constant")) +class VOXELGRAPH_API UVoxelNode_FloatParameter : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + float Value; + + UVoxelNode_FloatParameter(); + + auto GetValue() const { return Value; } + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Value); } + //~ End UVoxelExposedNode Interface +}; + +// Int parameter +UCLASS(DisplayName = "int parameter", meta = (Keywords = "constant")) +class VOXELGRAPH_API UVoxelNode_IntParameter : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 Value; + + UVoxelNode_IntParameter(); + + auto GetValue() const { return Value; } + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Value); } + //~ End UVoxelExposedNode Interface +}; + +// Color parameter +UCLASS(DisplayName = "color parameter") +class VOXELGRAPH_API UVoxelNode_ColorParameter : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + FLinearColor Color = FLinearColor(0, 0, 0, 1); + + UVoxelNode_ColorParameter(); + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Color); } + //~ End UVoxelExposedNode Interface +}; + +// Bool parameter +UCLASS(DisplayName = "bool parameter", meta = (Keywords = "constant")) +class VOXELGRAPH_API UVoxelNode_BoolParameter : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + bool Value; + + UVoxelNode_BoolParameter(); + + auto GetValue() const { return Value; } + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Value); } + //~ End UVoxelExposedNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelPlaceableItemsNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelPlaceableItemsNodes.h new file mode 100644 index 00000000..d9111af6 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelPlaceableItemsNodes.h @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelPlaceableItemsNodes.generated.h" + +class UVoxelGraphDataItemConfig; + +UCLASS(DisplayName = "Data Item Sample", Category = "Placeable Items") +class VOXELGRAPH_API UVoxelNode_DataItemSample : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UVoxelNode_DataItemSample(); + + // Only items matching the channels ticked here will be sampled (only the items matching (Mask & Item.Mask) != 0) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Config", meta = (Bitmask, BitmaskEnum = EVoxel32BitMask)) + int32 Mask = 1; + + UPROPERTY(EditAnywhere, Category = "Config") + EVoxelDataItemCombineMode CombineMode = EVoxelDataItemCombineMode::Min; + +}; + +UCLASS(DisplayName = "Data Item Parameters", Category = "Placeable Items") +class VOXELGRAPH_API UVoxelNode_DataItemParameters : public UVoxelNodeWithContext +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Config", meta = (ReconstructNode, NonNull)) + UVoxelGraphDataItemConfig* Config; + + // If no parameters are provided these will be used + UPROPERTY(EditAnywhere, Category = "Preview") + TMap PreviewValues; + +public: + UVoxelNode_DataItemParameters() = default; + + //~ Begin UVoxelNode Interface + virtual int32 GetOutputPinsCount() const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + //~ End UVoxelNode Interface + +#if WITH_EDITOR + //~ Begin UObject Interface + void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + //~ End UObject Interface +#endif + + TArray GetPreviewValues() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelRandomNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelRandomNodes.h new file mode 100644 index 00000000..1a671b20 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelRandomNodes.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelRandomNodes.generated.h" + +// A random number >= Min and <= Max +UCLASS(DisplayName = "Rand Float", Category = "Math|Float") +class VOXELGRAPH_API UVoxelNode_RandomFloat : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + float Min = 0; + UPROPERTY(EditAnywhere, Category = "Voxel") + float Max = 1; + + UVoxelNode_RandomFloat(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface +}; + +// A random number >= Min and <= Max +UCLASS(DisplayName = "Rand int", Category = "Math|Integer") +class VOXELGRAPH_API UVoxelNode_RandomInt : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 Min = 0; + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 Max = 100; + + UVoxelNode_RandomInt(); + + //~ Begin UVoxelNode Interface + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h new file mode 100644 index 00000000..33f542d1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h @@ -0,0 +1,344 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "Compilation/VoxelDefaultCompilationNodes.h" +#include "VoxelSDFNodes.generated.h" + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelSDFNode : public UVoxelPureNode +{ + GENERATED_BODY() + +protected: + UVoxelSDFNode(); + + void AddPositionInput(); + void AddStartInput(); + void AddEndInput(); + + void AddNormalInput(); + + void AddRadiusInput(); + void AddStartRadiusInput(); + void AddEndRadiusInput(); + + void AddLengthInput(); + void AddHeightInput(); + + void AddSizeInput(); + void AddSize2DInput(); + void AddSize1DInput(); + + void AddSmoothnessInput(); + void AddThicknessInput(); + + void AddSinCosInput(); + void AddDistanceAInput(); + void AddDistanceBInput(); +}; + +// Sphere - exact +UCLASS(DisplayName = "Sphere SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_SphereSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SphereSDF(); +}; + +// Box - exact +UCLASS(DisplayName = "Box SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_BoxSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_BoxSDF(); +}; + +// Round Box - exact +UCLASS(DisplayName = "Round Box SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_RoundBoxSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_RoundBoxSDF(); +}; + +// Torus - exact +UCLASS(DisplayName = "Torus SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_TorusSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_TorusSDF(); +}; + +// Capped Torus - exact +UCLASS(DisplayName = "Capped Torus SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_CappedTorusSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_CappedTorusSDF(); +}; + +// Link - exact +UCLASS(DisplayName = "Link SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_LinkSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_LinkSDF(); +}; + +// Infinite Cylinder - exact +UCLASS(DisplayName = "Cylinder SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_CylinderSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_CylinderSDF(); +}; + +// Cone - exact +UCLASS(DisplayName = "Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_ConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_ConeSDF(); +}; + +// Cone - bound(not exact!) +UCLASS(DisplayName = "Fast Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_ConeFastSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_ConeFastSDF(); +}; + +// Infinite Cone - exact +UCLASS(DisplayName = "Infinite Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_InfiniteConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_InfiniteConeSDF(); +}; + +// Plane - exact +UCLASS(DisplayName = "Plane SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_PlaneSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_PlaneSDF(); +}; + +// Hexagonal Prism - exact +UCLASS(DisplayName = "Hexagonal Prism SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_HexPrismSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_HexPrismSDF(); +}; + +// Triangular Prism - bound +UCLASS(DisplayName = "Triangular Prism SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_TriPrismSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_TriPrismSDF(); +}; + +// Capsule / Line - exact +UCLASS(DisplayName = "Capsule SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_CapsuleSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_CapsuleSDF(); +}; + +// Capsule / Line - exact +UCLASS(DisplayName = "Vertical Capsule SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_VerticalCapsuleSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VerticalCapsuleSDF(); +}; + +// Capped Cylinder - exact +UCLASS(DisplayName = "Vertical Capped Cylinder SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_VerticalCappedCylinderSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VerticalCappedCylinderSDF(); +}; + +// Capped Cylinder - exact +UCLASS(DisplayName = "Capped Cylinder SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_CappedCylinderSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_CappedCylinderSDF(); +}; + +// Rounded Cylinder - exact +UCLASS(DisplayName = "Rounded Cylinder SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_RoundedCylinderSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_RoundedCylinderSDF(); +}; + +// Capped Cone - exact +UCLASS(DisplayName = "Vertical Capped Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_VerticalCappedConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VerticalCappedConeSDF(); +}; + +// Capped Cone - exact +UCLASS(DisplayName = "Capped Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_CappedConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_CappedConeSDF(); +}; + +// Solid Angle - exact +UCLASS(DisplayName = "Solid Angle SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_SolidAngleSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SolidAngleSDF(); +}; + +// Round cone - exact +UCLASS(DisplayName = "Vertical Round Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_VerticalRoundConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_VerticalRoundConeSDF(); +}; + +// Round Cone - exact +UCLASS(DisplayName = "Round Cone SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_RoundConeSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_RoundConeSDF(); +}; + +// Ellipsoid - bound(not exact!) +UCLASS(DisplayName = "Ellipsoid SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_EllipsoidSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_EllipsoidSDF(); +}; + +// Octahedron - exact +UCLASS(DisplayName = "Octahedron SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_OctahedronSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_OctahedronSDF(); +}; + +// Octahedron - bound(not exact) +UCLASS(DisplayName = "Fast Octahedron SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_OctahedronFastSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_OctahedronFastSDF(); +}; + +// Pyramid - exact +UCLASS(DisplayName = "Pyramid SDF", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_PyramidSDF : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_PyramidSDF(); +}; + +/////////////////////////////////////////////////////////////////////////////// + +// Smooth Union of two SDFs +UCLASS(DisplayName = "Smooth Union", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_SmoothUnion : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SmoothUnion(); +}; + +// Smooth Subtraction of two SDFs +UCLASS(DisplayName = "Smooth Subtraction", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_SmoothSubtraction : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SmoothSubtraction(); +}; + +// Smooth Intersection of two SDFs +UCLASS(DisplayName = "Smooth Intersection", Category = "Distance Field") +class VOXELGRAPH_API UVoxelNode_SmoothIntersection : public UVoxelSDFNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_SmoothIntersection(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSeedNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSeedNodes.h new file mode 100644 index 00000000..0b84dd00 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelSeedNodes.h @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelExposedNodes.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelSeedNodes.generated.h" + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelSeedNode : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UVoxelSeedNode(); +}; + +// Seed parameter +UCLASS(DisplayName = "Seed", Category = "Seed") +class VOXELGRAPH_API UVoxelNode_Seed : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel") + int32 DefaultValue = 1443; + + UPROPERTY() + FName Name_DEPRECATED = "SeedName"; + + UVoxelNode_Seed(); + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(DefaultValue); } + //~ End UVoxelExposedNode Interface + + //~ Begin UObject Interface + virtual void PostLoad() override; + //~ End UObject Interface +}; + +// Combine seeds by hashing them +UCLASS(DisplayName = "Hash Seeds", Category = "Seed", meta = (Keywords = "combine add seed")) +class VOXELGRAPH_API UVoxelNode_AddSeeds : public UVoxelSeedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + COMPACT_VOXELNODE("HASH") + + UVoxelNode_AddSeeds(); +}; + +// Make several new seeds from a single one +UCLASS(DisplayName = "Make Seeds", Category = "Seed", meta = (Keywords = "make combine add seed")) +class VOXELGRAPH_API UVoxelNode_MakeSeeds : public UVoxelSeedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ClampMin = 2, ClampMax = 32, ReconstructNode)) + int32 NumOutputs = 5; + + UVoxelNode_MakeSeeds(); + + //~ Begin UVoxelNode Interface + virtual int32 GetOutputPinsCount() const override { return NumOutputs; } + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelTextureSamplerNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelTextureSamplerNode.h new file mode 100644 index 00000000..f6ac2943 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelTextureSamplerNode.h @@ -0,0 +1,77 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEnums.h" +#include "VoxelTexture.h" +#include "VoxelExposedNodes.h" +#include "VoxelTextureSamplerNode.generated.h" + +class UTexture2D; + +// Texture sampler. Inputs are in the texture dimension, not between 0 and 1 +UCLASS(DisplayName = "Texture Sampler", Category = "Texture", meta = (Keywords = "constant parameter")) +class VOXELGRAPH_API UVoxelNode_TextureSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Texture settings") + UTexture2D* Texture; + + UPROPERTY(EditAnywhere, Category = "Texture settings", meta = (ReconstructNode)) + bool bBilinearInterpolation = true; + + UPROPERTY(EditAnywhere, Category = "Texture settings") + EVoxelSamplerMode Mode = EVoxelSamplerMode::Tile; + + UVoxelNode_TextureSampler(); + + //~ Begin UVoxelNode Interface + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual FText GetTitle() const override; + virtual void LogErrors(FVoxelGraphErrorReporter& ErrorReporter) override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Texture); } + //~ End UVoxelExposedNode Interface +}; + +// Voxel Texture sampler. Inputs are in the texture dimension, not between 0 and 1 +// The voxel texture can only be set in BP +// You can create a voxel texture from another graph, or using erosion +UCLASS(DisplayName = "Voxel Texture Sampler", Category = "Texture", meta = (Keywords = "constant parameter")) +class VOXELGRAPH_API UVoxelNode_VoxelTextureSampler : public UVoxelExposedNode +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Texture settings") + bool bBilinearInterpolation = true; + + UPROPERTY(EditAnywhere, Category = "Texture settings") + EVoxelSamplerMode Mode = EVoxelSamplerMode::Tile; + + // For parameters to work + UPROPERTY() + FVoxelFloatTexture Texture; + + UVoxelNode_VoxelTextureSampler(); + + //~ Begin UVoxelNode Interface + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual FText GetTitle() const override; + //~ End UVoxelNode Interface + + //~ Begin UVoxelExposedNode Interface + virtual FName GetParameterPropertyName() const override { return GET_OWN_MEMBER_NAME(Texture); } + //~ End UVoxelExposedNode Interface + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelVoronoiNoiseNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelVoronoiNoiseNodes.h new file mode 100644 index 00000000..c8f97193 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelVoronoiNoiseNodes.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelVoronoiNoiseNodes.generated.h" + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNode_VoronoiNoiseBase : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voronoi settings", meta = (ReconstructNode)) + bool bComputeNeighbors = false; + + UPROPERTY() + int32 Dimension; + + //~ Begin UVoxelNode Interface + virtual int32 GetMinInputPins() const override; + virtual int32 GetMaxInputPins() const override; + virtual int32 GetOutputPinsCount() const override; + virtual EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override; + virtual EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override; + virtual FName GetInputPinName(int32 PinIndex) const override; + virtual FName GetOutputPinName(int32 PinIndex) const override; + virtual FString GetInputPinToolTip(int32 PinIndex) const override; + virtual FString GetOutputPinToolTip(int32 PinIndex) const override; + virtual FVoxelPinDefaultValueBounds GetInputPinDefaultValueBounds(int32 PinIndex) const override; + virtual FString GetInputPinDefaultValue(int32 PinIndex) const override; + //~ End UVoxelNode Interface + +private: + const FVoxelPinsHelper& GetPins() const; +}; + +// 2D Voronoi Noise +UCLASS(DisplayName = "2D Voronoi Noise", Category = "Noise|Voronoi Noise") +class VOXELGRAPH_API UVoxelNode_2DVoronoiNoise : public UVoxelNode_VoronoiNoiseBase +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DVoronoiNoise(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelWhiteNoiseNodes.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelWhiteNoiseNodes.h new file mode 100644 index 00000000..717cff04 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelNodes/VoxelWhiteNoiseNodes.h @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.h" +#include "VoxelWhiteNoiseNodes.generated.h" + +// 2D White Noise +UCLASS(DisplayName = "2D White Noise", Category = "Noise|White Noise") +class VOXELGRAPH_API UVoxelNode_2DWhiteNoise : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_2DWhiteNoise(); +}; + +// 3D White Noise +UCLASS(DisplayName = "3D White Noise", Category = "Noise|White Noise") +class VOXELGRAPH_API UVoxelNode_3DWhiteNoise : public UVoxelNodeHelper +{ + GENERATED_BODY() + GENERATED_VOXELNODE_BODY() + + UVoxelNode_3DWhiteNoise(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelPinCategory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelPinCategory.h new file mode 100644 index 00000000..26469820 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/Public/VoxelPinCategory.h @@ -0,0 +1,56 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelPinCategory.generated.h" + +struct FVoxelNodeType; +struct FVoxelNodeRangeType; + +UENUM() +enum class EVoxelPinCategory : uint8 +{ + Exec, + Boolean, + Int, + Float, + Material, + Color, + Seed, + Wildcard UMETA(Hidden), + Vector UMETA(Hidden) +}; + +UENUM() +enum class EVoxelDataPinCategory : uint8 +{ + Boolean, + Int, + Float, + Material, + Color +}; + +struct VOXELGRAPH_API FVoxelPinCategory +{ + static EVoxelPinCategory DataPinToPin(EVoxelDataPinCategory Category); + static EVoxelPinCategory FromString(const FName& String); + static FName GetName(EVoxelPinCategory Category); + + static FString GetDefaultValue(EVoxelPinCategory Category); + static FString GetDefaultValue(EVoxelDataPinCategory Category); + + static FString GetTypeString(EVoxelPinCategory Category); + static FString GetTypeString(EVoxelDataPinCategory Category) { return GetTypeString(DataPinToPin(Category)); } + static FString GetRangeTypeString(EVoxelPinCategory Category); + + static FVoxelNodeType ConvertDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue); + static FVoxelNodeRangeType ConvertRangeDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue); + static FString ConvertStringDefaultValue(EVoxelPinCategory Category, const FString& DefaultValue); + + static FString ToString(EVoxelPinCategory Category, FVoxelNodeType Value); + static FString ToString(EVoxelPinCategory Category, FVoxelNodeRangeType Value); + + static bool IsInRange(EVoxelPinCategory Category, FVoxelNodeType Value, FVoxelNodeRangeType Range); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/VoxelGraph.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/VoxelGraph.Build.cs new file mode 100644 index 00000000..632c1d3e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraph/VoxelGraph.Build.cs @@ -0,0 +1,30 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelGraph : ModuleRules +{ + public VoxelGraph(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "Voxel" + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/DummyObject.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/DummyObject.h new file mode 100644 index 00000000..2a3d5206 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/DummyObject.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#include "CoreMinimal.h" +#include "DummyObject.generated.h" + +// Random class to make UHT happy + +UCLASS(Abstract, Deprecated, NotPlaceable, NotBlueprintable, HideDropdown) +class UDEPRECATED_GraphEditorDummyObject : public UObject +{ + GENERATED_BODY() +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.cpp new file mode 100644 index 00000000..9b3b58d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.cpp @@ -0,0 +1,220 @@ +// Copyright 2020 Phyronnaz + +#include "SVoxelGraphPreview.h" +#include "VoxelGraphPreviewSettings.h" +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" + +#include "Widgets/SCanvas.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Layout/SBox.h" +#include "Engine/Texture2D.h" +#include "Rendering/DrawElements.h" +#include "Brushes/SlateColorBrush.h" + +void SVoxelGraphPreview::Construct(const FArguments& Args) +{ + PreviewSettings = Args._PreviewSettings; + check(PreviewSettings.IsValid()); + + TextureBrush.DrawAs = ESlateBrushDrawType::NoDrawType; + + ChildSlot + [ + SNew(SBox) + .WidthOverride(Size) + .HeightOverride(Size) + [ + SNew(SCanvas) + + SCanvas::Slot() + .Size(FVector2D(Size, Size)) + .Position(TAttribute::Create(TAttribute::FGetter::CreateLambda([=]() { return Position; }))) + [ + SNew(SImage) + .Image(&TextureBrush) + ] + ] + ]; +} + +void SVoxelGraphPreview::SetTexture(UTexture2D* Texture) +{ + check(Texture); + + Texture->Filter = TextureFilter::TF_Nearest; + + TextureBrush.SetResourceObject(Texture); + TextureBrush.ImageSize.X = Texture->GetSizeX(); + TextureBrush.ImageSize.Y = Texture->GetSizeY(); + TextureBrush.DrawAs = ESlateBrushDrawType::Image; +} + +void SVoxelGraphPreview::SetDebugData(const UVoxelPlaceableItemManager* Manager) +{ + if (!Manager) + { + DebugLines.Reset(); + DebugPoints.Reset(); + return; + } + + const FVoxelGraphPreviewSettingsWrapper Wrapper(*PreviewSettings); + + const auto GetScreenPosition = [&](const FVector& Point) + { + FVector2D ScreenPoint = Wrapper.GetScreenPosition(Point) / Wrapper.Resolution * Size; + ScreenPoint.Y = Size - ScreenPoint.Y; + return ScreenPoint; + }; + + DebugLines.Reset(Manager->GetDebugLines().Num()); + for (auto& Line : Manager->GetDebugLines()) + { + DebugLines.Add(FLine{ + GetScreenPosition(Line.Start), + GetScreenPosition(Line.End), + Line.Color }); + } + + DebugPoints.Reset(Manager->GetDebugPoints().Num()); + for (auto& Point : Manager->GetDebugPoints()) + { + DebugPoints.Add(FPoint{ + GetScreenPosition(Point.Position), + Point.Color }); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FReply SVoxelGraphPreview::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + const int32 Delta = FMath::Clamp(FMath::RoundToInt(MouseEvent.GetWheelDelta()), -1, 1); + + if (Delta != 0) + { + PreviewSettings->ResolutionMultiplierLog -= Delta; + FPropertyChangedEvent PropertyChangedEvent(UVoxelGraphPreviewSettings::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UVoxelGraphPreviewSettings, ResolutionMultiplierLog))); + PreviewSettings->PostEditChangeProperty(PropertyChangedEvent); + } + + return FReply::Handled(); +} + +FReply SVoxelGraphPreview::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton)) + { + Position += TransformVector(Inverse(GetCachedGeometry().GetAccumulatedRenderTransform()), MouseEvent.GetCursorDelta()); + return FReply::Handled(); + } + return FReply::Unhandled(); +} + +FReply SVoxelGraphPreview::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) + { + const FVector2D LocalClickPosition = GetCachedGeometry().AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); + const FVector2D RelativePosition = LocalClickPosition / Size; + + const FVoxelGraphPreviewSettingsWrapper Wrapper(*PreviewSettings); + PreviewSettings->PreviewedVoxel = Wrapper.Start + FVoxelUtilities::RoundToInt(Wrapper.GetRelativePosition(RelativePosition.X, 1 - RelativePosition.Y) * FVector(Wrapper.Size * Wrapper.Step)); + + if (!PreviewSettings->bShowStats && !PreviewSettings->bShowValues) + { + // Make sure one of them is toggled if we click the preview + PreviewSettings->bShowValues = true; + } + + FPropertyChangedEvent PropertyChangedEvent(UVoxelGraphPreviewSettings::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UVoxelGraphPreviewSettings, PreviewedVoxel))); + PreviewSettings->PostEditChangeProperty(PropertyChangedEvent); + } + + return FReply::Unhandled(); +} + +FReply SVoxelGraphPreview::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.GetEffectingButton() == EKeys::RightMouseButton) + { + if (Position.IsZero()) + { + return FReply::Unhandled(); + } + + const FVector2D RelativePosition = Position / Size; + + const FVoxelGraphPreviewSettingsWrapper Wrapper(*PreviewSettings); + PreviewSettings->Center -= FVoxelUtilities::RoundToInt(Wrapper.GetRelativePosition(RelativePosition.X, -RelativePosition.Y) * FVector(Wrapper.Size * Wrapper.Step)); + + FPropertyChangedEvent PropertyChangedEvent(UVoxelGraphPreviewSettings::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UVoxelGraphPreviewSettings, Center))); + PreviewSettings->PostEditChangeProperty(PropertyChangedEvent); + + Position = FVector2D::ZeroVector; + + return FReply::Handled(); + } + + return FReply::Unhandled(); +} + +FCursorReply SVoxelGraphPreview::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const +{ + return FCursorReply::Cursor(EMouseCursor::Crosshairs); +} + +int32 SVoxelGraphPreview::OnPaint( + const FPaintArgs& Args, + const FGeometry& AllottedGeometry, + const FSlateRect& MyCullingRect, + FSlateWindowElementList& OutDrawElements, + int32 LayerId, + const FWidgetStyle& InWidgetStyle, + bool bParentEnabled) const +{ + VOXEL_FUNCTION_COUNTER(); + + LayerId = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); + LayerId++; + + ensure(GetCachedGeometry().Size.X == Size); + ensure(GetCachedGeometry().Size.Y == Size); + + TArray Points; + Points.SetNum(2); + for (auto& Line : DebugLines) + { + Points[0] = Position + Line.Start; + Points[1] = Position + Line.End; + + if (!MyCullingRect.ContainsPoint(GetCachedGeometry().LocalToAbsolute(Points[0])) && + !MyCullingRect.ContainsPoint(GetCachedGeometry().LocalToAbsolute(Points[1]))) + { + continue; + } + + FSlateDrawElement::MakeLines( + OutDrawElements, + LayerId, + AllottedGeometry.ToPaintGeometry(), + Points, + ESlateDrawEffect::None, + Line.Color); + } + + FSlateColorBrush Brush(FLinearColor::White); + for (auto& Point : DebugPoints) + { + FSlateDrawElement::MakeBox( + OutDrawElements, + LayerId, + AllottedGeometry.ToPaintGeometry(Position + Point.Position, FVector2D(2, 2)), + &Brush, + ESlateDrawEffect::None, + Point.Color); + } + + return LayerId; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.h new file mode 100644 index 00000000..be451deb --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.h @@ -0,0 +1,65 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateBrush.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +class UTexture2D; +class UVoxelPlaceableItemManager; +class UVoxelGraphPreviewSettings; + +class SVoxelGraphPreview : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SVoxelGraphPreview) {} + SLATE_ARGUMENT(TWeakObjectPtr, PreviewSettings) + SLATE_END_ARGS() + + void Construct(const FArguments& Args); + void SetTexture(UTexture2D* Texture); + + void SetDebugData(const UVoxelPlaceableItemManager* Manager); + +public: + //~ Begin SCompoundWidget Interface + virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FCursorReply OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const override; + + virtual int32 OnPaint( + const FPaintArgs& Args, + const FGeometry& AllottedGeometry, + const FSlateRect& MyCullingRect, + FSlateWindowElementList& OutDrawElements, + int32 LayerId, + const FWidgetStyle& InWidgetStyle, + bool bParentEnabled) const override; + //~ End SCompoundWidget Interface + +private: + TWeakObjectPtr PreviewSettings; + FSlateBrush TextureBrush; + FVector2D Position = FVector2D::ZeroVector; + + struct FLine + { + FVector2D Start; + FVector2D End; + FLinearColor Color; + }; + TArray DebugLines; + + struct FPoint + { + FVector2D Position; + FLinearColor Color; + }; + TArray DebugPoints; + + static constexpr float Size = 100.f; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp new file mode 100644 index 00000000..d59b54dc --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp @@ -0,0 +1,186 @@ +// Copyright 2020 Phyronnaz + +#include "SVoxelGraphPreviewViewport.h" +#include "IVoxelGraphEditorToolkit.h" + +#include "EditorViewportClient.h" +#include "SEditorViewport.h" +#include "AdvancedPreviewScene.h" +#include "Slate/SceneViewport.h" +#include "AssetViewerSettings.h" + +/** Viewport Client for the preview viewport */ +class FVoxelGraphEditorViewportClient : public FEditorViewportClient +{ +public: + FVoxelGraphEditorViewportClient(FAdvancedPreviewScene* InPreviewScene, const TSharedRef& InVoxelGraphEditorViewport); + + // FEditorViewportClient interface + virtual bool InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed = 1.f, bool bGamepad = false) override; + virtual bool InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples/* =1 */, bool bGamepad/* =false */) override; + virtual FLinearColor GetBackgroundColor() const override; + virtual void Tick(float DeltaSeconds) override; + virtual void Draw(FViewport* Viewport, FCanvas* Canvas) override; + virtual bool ShouldOrbitCamera() const override; + +private: + + /** Preview Scene - uses advanced preview settings */ + class FAdvancedPreviewScene* AdvancedPreviewScene; +}; + +FVoxelGraphEditorViewportClient::FVoxelGraphEditorViewportClient(FAdvancedPreviewScene* InPreviewScene, const TSharedRef& InVoxelGraphEditorViewport) + : FEditorViewportClient(nullptr, InPreviewScene, StaticCastSharedRef(InVoxelGraphEditorViewport)) +{ + // Setup defaults for the common draw helper. + DrawHelper.bDrawPivot = false; + DrawHelper.bDrawWorldBox = false; + DrawHelper.bDrawKillZ = false; + DrawHelper.bDrawGrid = false; + DrawHelper.GridColorAxis = FColor(80, 80, 80); + DrawHelper.GridColorMajor = FColor(72, 72, 72); + DrawHelper.GridColorMinor = FColor(64, 64, 64); + DrawHelper.PerspectiveGridSize = HALF_WORLD_MAX1; + + SetViewMode(VMI_Lit); + + EngineShowFlags.DisableAdvancedFeatures(); + EngineShowFlags.SetSnap(false); + EngineShowFlags.SetSeparateTranslucency(true); + + OverrideNearClipPlane(1.0f); + bUsingOrbitCamera = true; + + // Don't want to display the widget in this viewport + Widget->SetDefaultVisibility(false); + + AdvancedPreviewScene = InPreviewScene; + +} + +void FVoxelGraphEditorViewportClient::Tick(float DeltaSeconds) +{ + FEditorViewportClient::Tick(DeltaSeconds); + + // Tick the preview scene world. + if (!GIntraFrameDebuggingGameThread) + { + PreviewScene->GetWorld()->Tick(LEVELTICK_All, DeltaSeconds); + } +} + +void FVoxelGraphEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas) +{ + FEditorViewportClient::Draw(InViewport, Canvas); +} + +bool FVoxelGraphEditorViewportClient::ShouldOrbitCamera() const +{ + // Should always orbit around the preview object to keep it in view. + return true; +} + +bool FVoxelGraphEditorViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool bGamepad) +{ + bool bHandled = FEditorViewportClient::InputKey(InViewport, ControllerId, Key, Event, AmountDepressed, false); + + // Handle viewport screenshot. + bHandled |= InputTakeScreenshot(InViewport, Key, Event); + + bHandled |= AdvancedPreviewScene->HandleInputKey(InViewport, ControllerId, Key, Event, AmountDepressed, bGamepad); + + return bHandled; +} + +bool FVoxelGraphEditorViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples/* =1 */, bool bGamepad/* =false */) +{ + bool bResult = true; + + if (!bDisableInput) + { + bResult = AdvancedPreviewScene->HandleViewportInput(InViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad); + if (bResult) + { + Invalidate(); + } + else + { + bResult = FEditorViewportClient::InputAxis(InViewport, ControllerId, Key, Delta, DeltaTime, NumSamples, bGamepad); + } + } + + return bResult; +} + +FLinearColor FVoxelGraphEditorViewportClient::GetBackgroundColor() const +{ + if (AdvancedPreviewScene != nullptr) + { + return AdvancedPreviewScene->GetBackgroundColor(); + } + else + { + return FLinearColor::White; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SVoxelGraphPreviewViewport::Construct(const FArguments& InArgs) +{ + VoxelGraphEditorToolkit = InArgs._VoxelGraphEditorToolkit; + AdvancedPreviewScene = VoxelGraphEditorToolkit.Pin()->GetPreviewScene(); + + SEditorViewport::Construct(SEditorViewport::FArguments()); +} + +SVoxelGraphPreviewViewport::~SVoxelGraphPreviewViewport() +{ + UAssetViewerSettings::Get()->OnAssetViewerSettingsChanged().RemoveAll(this); + + if (EditorViewportClient.IsValid()) + { + EditorViewportClient->Viewport = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TSharedRef SVoxelGraphPreviewViewport::GetViewportWidget() +{ + return SharedThis(this); +} + +TSharedPtr SVoxelGraphPreviewViewport::GetExtenders() const +{ + TSharedPtr Result(MakeShareable(new FExtender)); + return Result; +} + +void SVoxelGraphPreviewViewport::OnFloatingButtonClicked() +{ +} + +void SVoxelGraphPreviewViewport::RefreshViewport() +{ + Client->RedrawRequested(nullptr); + GEditor->UpdateSingleViewportClient(Client.Get(), true, false); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TSharedRef SVoxelGraphPreviewViewport::MakeEditorViewportClient() +{ + EditorViewportClient = MakeShareable(new FVoxelGraphEditorViewportClient(AdvancedPreviewScene, SharedThis(this))); + EditorViewportClient->SetViewLocation(FVector::ZeroVector); + EditorViewportClient->SetViewRotation(FRotator(0.0f, -90.0f, 0.0f)); + EditorViewportClient->SetViewLocationForOrbiting(FVector::ZeroVector); + + return EditorViewportClient.ToSharedRef(); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.h new file mode 100644 index 00000000..50a74399 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.h @@ -0,0 +1,50 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "SEditorViewport.h" +#include "SCommonEditorViewportToolbarBase.h" + +class FVoxelGraphPreviewViewportClient; +class IVoxelGraphEditorToolkit; +class FEditorViewportClient; +class FAdvancedPreviewScene; +class SDockTab; + +/** + * Material Editor Preview viewport widget + */ +class SVoxelGraphPreviewViewport : public SEditorViewport, public ICommonEditorViewportToolbarInfoProvider +{ +public: + SLATE_BEGIN_ARGS( SVoxelGraphPreviewViewport ){} + SLATE_ARGUMENT(TWeakPtr, VoxelGraphEditorToolkit) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + ~SVoxelGraphPreviewViewport(); + + // ICommonEditorViewportToolbarInfoProvider interface + virtual TSharedRef GetViewportWidget() override; + virtual TSharedPtr GetExtenders() const override; + virtual void OnFloatingButtonClicked() override; + // End of ICommonEditorViewportToolbarInfoProvider interface + + void RefreshViewport(); + +protected: + /** SEditorViewport interface */ + virtual TSharedRef MakeEditorViewportClient() override; + +private: + /** The parent tab where this viewport resides */ + TWeakPtr ParentTab; + + /** Pointer back to the material editor tool that owns us */ + TWeakPtr VoxelGraphEditorToolkit; + + /** Level viewport client */ + TSharedPtr EditorViewportClient; + FAdvancedPreviewScene* AdvancedPreviewScene = nullptr; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp new file mode 100644 index 00000000..f68442e4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp @@ -0,0 +1,1041 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphPreview.h" +#include "VoxelValue.h" +#include "VoxelMaterial.h" +#include "VoxelGraphEditor.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphPreviewSettings.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelDebug/VoxelLineBatchComponent.h" +#include "Runtime/VoxelGraph.inl" +#include "Runtime/VoxelGraphGeneratorInstance.h" +#include "Runtime/Recorders/VoxelGraphStatsRecorder.h" +#include "Runtime/Recorders/VoxelGraphRangeAnalysisRecorder.h" +#include "VoxelData/VoxelDataIncludes.h" +#include "VoxelUtilities/VoxelMaterialUtilities.h" +#include "VoxelUtilities/VoxelTextureUtilities.h" +#include "VoxelUtilities/VoxelThreadingUtilities.h" +#include "VoxelUtilities/VoxelDistanceFieldUtilities.h" +#include "VoxelPlaceableItems/VoxelPlaceableItemManager.h" +#include "VoxelGenerators/VoxelGeneratorCache.h" +#include "VoxelWorldInterface.h" + +#include "SVoxelGraphPreview.h" +#include "SVoxelGraphPreviewViewport.h" + +#include "Misc/MessageDialog.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "Components/StaticMeshComponent.h" +#include "AdvancedPreviewScene.h" +#include "Kismet/KismetMathLibrary.h" + +FVoxelGraphPreview::FVoxelGraphPreview( + UVoxelGraphGenerator* Generator, + const TSharedPtr& Preview, + const TSharedPtr& PreviewViewport, + const TSharedPtr& PreviewScene) + : Generator(Generator) + , Preview(Preview) + , PreviewViewport(PreviewViewport) + , PreviewScene(PreviewScene) +{ + check(Generator && Generator->PreviewSettings); + + PreviewScene->SetLightBrightness(0.f); + PreviewScene->SetFloorVisibility(false, true); + PreviewScene->SetEnvironmentVisibility(false, true); + PreviewScene->SetSkyBrightness(0.f); + + PreviewSceneFloor = NewObject(); + LineBatchComponent = NewObject(); + + PreviewScene->AddComponent(PreviewSceneFloor, FTransform::Identity); + PreviewScene->AddComponent(LineBatchComponent, FTransform::Identity); +} + +void FVoxelGraphPreview::Update(EVoxelGraphPreviewFlags Flags) +{ + if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::UpdatePlaceableItems)) + { + Flags |= EVoxelGraphPreviewFlags::UpdateTextures; + } + if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::UpdateTextures)) + { + Flags |= EVoxelGraphPreviewFlags::UpdateMeshSettings; + } + + if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::UpdateTextures)) + { + UpdateTextures(Flags); + } + if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::UpdateMeshSettings)) + { + UpdateMaterialParameters(); + } + + PreviewViewport->RefreshViewport(); +} + +void FVoxelGraphPreview::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(PreviewSceneFloor); + Collector.AddReferencedObject(LineBatchComponent); + + Collector.AddReferencedObject(HeightmapMaterial); + Collector.AddReferencedObject(SliceMaterial); + + Collector.AddReferencedObject(DensitiesTexture); + Collector.AddReferencedObject(MaterialsTexture); + Collector.AddReferencedObject(MaterialsTextureWithCrossAndNoAlpha); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphPreview::UpdateTextures(EVoxelGraphPreviewFlags Flags) +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelGraphErrorReporter::ClearNodesMessages(Generator); + + auto& PreviewSettings = *Generator->PreviewSettings; + const auto Settings = FVoxelGraphPreviewSettingsWrapper(PreviewSettings); + + TVoxelSharedPtr GeneratorInstance; + if (!Generator->GetGraphInstance(GeneratorInstance, true, !EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::ManualPreview))) + { + return; + } + + FVoxelGeneratorInit Init( + PreviewSettings.VoxelSize, + Settings.Size.GetMax(), + PreviewSettings.RenderType, + PreviewSettings.MaterialConfig, + PreviewSettings.MaterialCollection, + nullptr); + { + VOXEL_SCOPE_COUNTER("Init"); + GeneratorInstance->Init(Init); + } + + if (!Data || EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::UpdatePlaceableItems)) + { + VOXEL_SCOPE_COUNTER("Create Data"); + + Data = FVoxelData::Create(FVoxelDataSettings(32, GeneratorInstance.ToSharedRef(), false, false), 0); + + if (PreviewSettings.PlaceableItemManager) + { + PreviewSettings.PlaceableItemManager->Clear(); + PreviewSettings.PlaceableItemManager->Generate(); + PreviewSettings.PlaceableItemManager->ApplyToData(*Data); + } + } + + // Hack to reuse the same data with the items + const_cast&>(Data->Generator) = GeneratorInstance.ToSharedRef(); + + // Always do it as it's using Center + { + VOXEL_SCOPE_COUNTER("Apply Debug"); + + Preview->SetDebugData(PreviewSettings.PlaceableItemManager); + + LineBatchComponent->Flush(); + if (PreviewSettings.PlaceableItemManager) + { + class FPreviewInterface : public IVoxelWorldInterface + { + public: + const FVoxelGraphPreviewSettingsWrapper& Settings; + + explicit FPreviewInterface(const FVoxelGraphPreviewSettingsWrapper& Settings) + : Settings(Settings) + { + } + + virtual FVector LocalToGlobal(const FIntVector& Position) const override { return FVector(Position - Settings.Center) / Settings.Step; } + virtual FVector LocalToGlobalFloat(const FVoxelVector& Position) const override { return (Position - Settings.Center).ToFloat() / Settings.Step; } + }; + PreviewSettings.PlaceableItemManager->DrawDebug(FPreviewInterface(Settings), *LineBatchComponent); + } + } + +#if DO_THREADSAFE_CHECKS + FVoxelReadScopeLock Lock(*Data, FVoxelIntBox::Infinite, FUNCTION_FNAME); +#endif + + const int32 RangeAnalysisChunkSize = FVoxelUtilities::DivideCeil(Settings.Resolution, PreviewSettings.NumRangeAnalysisChunksPerAxis); + + const bool bIsPreviewingPin = Generator->PreviewedPin.Get() != nullptr; + + const bool bShowDensities = PreviewSettings.PreviewType2D == EVoxelGraphPreviewType::Density || (PreviewSettings.PreviewType2D == EVoxelGraphPreviewType::Material && bIsPreviewingPin); + const bool bComputeDensities = true; // Always needed for 3D preview + + const bool bShowMaterials = PreviewSettings.PreviewType2D == EVoxelGraphPreviewType::Material && !bIsPreviewingPin; + const bool bComputeMaterials = bShowMaterials || !PreviewSettings.bHeightBasedColor; + + const bool bShowCosts = PreviewSettings.PreviewType2D == EVoxelGraphPreviewType::Cost; + const bool bComputeCosts = bShowCosts; + + const bool bShowRangeAnalysis = PreviewSettings.PreviewType2D == EVoxelGraphPreviewType::RangeAnalysis; + const bool bComputeRangeAnalysis = bShowRangeAnalysis; + + check(bShowDensities + bShowMaterials + bShowCosts + bShowRangeAnalysis == 1); + + TArray Values; + TArray Materials; + TArray Costs; // In ns + TArray> Ranges; + + const int32 NumPixels = FMath::Square(Settings.Resolution); + + if (bComputeDensities) Values.SetNumUninitialized(NumPixels); + if (bComputeMaterials) Materials.SetNumUninitialized(NumPixels); + if (bComputeCosts) Costs.SetNumUninitialized(NumPixels); + if (bComputeRangeAnalysis) Ranges.SetNumUninitialized(FMath::Square(PreviewSettings.NumRangeAnalysisChunksPerAxis)); + + if (bComputeCosts) + { + VOXEL_SCOPE_COUNTER("Compute cost"); + + FVoxelUtilities::ParallelFor_PerThreadData(Settings.Resolution, [&]() + { + return MakeUnique(*Data); + },[&](const TUniquePtr& Accelerator, int32 X) + { + for (int32 Y = 0; Y < Settings.Resolution; Y++) + { + const int32 Index = Settings.GetDataIndex(X, Y); + const FIntVector Position = Settings.GetWorldPosition(X, Y); + + const double StartTime = FPlatformTime::Seconds(); + Values[Index] = Accelerator->GetFloatValue(Position, PreviewSettings.LODToPreview); + const double EndTime = FPlatformTime::Seconds(); + + Costs[Index] = (EndTime - StartTime) * 1e9; + } + }, true); // not multithreaded as it messes up the results :/ + } + else + { + TArray Octrees; + FVoxelOctreeUtilities::IterateTreeInBounds(Data->GetOctree(), Settings.Bounds, [&](FVoxelDataOctreeBase& Octree) + { + if (Octree.IsLeafOrHasNoChildren()) + { + Octrees.Add(&Octree); + } + }); + + const TVoxelQueryZone GlobalQueryZone(Settings.Bounds, Settings.Size, Settings.LOD, Values); + ParallelFor(Octrees.Num(), [&](int32 Index) + { + auto& Octree = *Octrees[Index]; + auto QueryZone = GlobalQueryZone.ShrinkTo(Octree.GetBounds()); + + GeneratorInstance->GetOutput( + FTransform(), + 1, + QueryZone, + PreviewSettings.LODToPreview, + FVoxelItemStack(Octree.GetItemHolder())); + }); + } + + if (bComputeMaterials) + { + TVoxelQueryZone QueryZone(Settings.Bounds, Settings.Size, Settings.LOD, Materials); + Data->Get(QueryZone, PreviewSettings.LODToPreview); + } + + if (bComputeRangeAnalysis) + { + for (int32 X = 0; X < PreviewSettings.NumRangeAnalysisChunksPerAxis; X++) + { + for (int32 Y = 0; Y < PreviewSettings.NumRangeAnalysisChunksPerAxis; Y++) + { + const auto Bounds = FVoxelIntBox::SafeConstruct( + Settings.GetWorldPosition(X * RangeAnalysisChunkSize, Y * RangeAnalysisChunkSize), + Settings.GetWorldPosition((X + 1) * RangeAnalysisChunkSize, (Y + 1) * RangeAnalysisChunkSize)); + + TOptional> Range; + FVoxelOctreeUtilities::IterateTreeInBounds(Data->GetOctree(), Bounds, [&](FVoxelDataOctreeBase& Octree) + { + if (Octree.IsLeafOrHasNoChildren()) + { + const auto LocalRange = GeneratorInstance->GetValueRangeImpl( + {}, + Bounds.Overlap(Octree.GetBounds()), + PreviewSettings.LODToPreview, + FVoxelItemStack(Octree.GetItemHolder())); + + if (Range.IsSet()) + { + Range = TVoxelRange::Union(Range.GetValue(), LocalRange); + } + else + { + Range = LocalRange; + } + } + }); + + if (ensure(Range)) + { + Ranges[X + Y * PreviewSettings.NumRangeAnalysisChunksPerAxis] = Range.GetValue(); + } + } + } + } + + AddMessages(*GeneratorInstance); + + TArray NewDensities; + TArray NewMaterials; + TArray NewMaterialsWithCrossAndNoAlpha; + NewDensities.SetNumUninitialized(NumPixels); + NewMaterials.SetNumUninitialized(NumPixels); + NewMaterialsWithCrossAndNoAlpha.SetNumUninitialized(NumPixels); + + v_flt MinValue; + v_flt MaxValue; + if (PreviewSettings.bAutoNormalize) + { + MinValue = Values[0]; + MaxValue = Values[0]; + for (const auto& Value : Values) + { + MinValue = FMath::Min(Value, MinValue); + MaxValue = FMath::Max(Value, MaxValue); + } + } + else + { + MinValue = PreviewSettings.NormalizeMinValue; + MaxValue = PreviewSettings.NormalizeMaxValue; + } + + double MinCost = 0; + double MaxCost = 0; + if (bComputeCosts) + { + VOXEL_SCOPE_COUNTER("Sort"); + auto SortedCopy = Costs; + SortedCopy.Sort(); + MinCost = SortedCopy[FMath::Clamp(FMath::FloorToInt(SortedCopy.Num() * PreviewSettings.CostPercentile), 0, SortedCopy.Num() - 1)]; + MaxCost = SortedCopy[FMath::Clamp(FMath::CeilToInt(SortedCopy.Num() * (1 - PreviewSettings.CostPercentile)), 0, SortedCopy.Num() - 1)]; + } + + if (bShowDensities) + { + PreviewSettings.MinValue = LexToString(MinValue); + PreviewSettings.MaxValue = LexToString(MaxValue); + } + else if (bShowMaterials) + { + PreviewSettings.MinValue = "N/A"; + PreviewSettings.MaxValue = "N/A"; + } + else if (bShowCosts) + { + PreviewSettings.MinValue = LexToString(MinCost) + " ns"; + PreviewSettings.MaxValue = LexToString(MaxCost) + " ns"; + } + else + { + check(bShowRangeAnalysis); + PreviewSettings.MinValue = "N/A"; + PreviewSettings.MaxValue = "N/A"; + } + + const FIntPoint ScreenPreviewedVoxel = Settings.GetScreenPosition(PreviewSettings.PreviewedVoxel); + for (int32 X = 0; X < Settings.Resolution; X++) + { + for (int32 Y = 0; Y < Settings.Resolution; Y++) + { + const int32 DataIndex = Settings.GetDataIndex(X, Y); + const int32 TextureIndex = Settings.GetTextureIndex(X, Y); + + const float Value = Values[DataIndex]; + const float Alpha = (Value - MinValue) / (MaxValue - MinValue); + + { + uint8 IntAlpha = FVoxelUtilities::FloatToUINT8(Alpha); + NewDensities[TextureIndex] = FColor(IntAlpha, IntAlpha, IntAlpha, 255); + } + + if (bShowDensities) + { + if (PreviewSettings.bDrawColoredDistanceField) + { + const float ScaledValue = Value / (Settings.Resolution * Settings.Step) * 2; + NewMaterials[TextureIndex] = FVoxelDistanceFieldUtilities::GetDistanceFieldColor(ScaledValue); + } + else + { + NewMaterials[TextureIndex] = NewDensities[TextureIndex]; + } + } + else if (bShowMaterials) + { + const FVoxelMaterial& Material = Materials[DataIndex]; + FColor Color = FColor::Black; + + const auto GetColor = [&](int32 Index) + { + if (PreviewSettings.IndexColors.IsValidIndex(Index)) + { + return PreviewSettings.IndexColors[Index]; + } + else + { + return FColor::Black; + } + }; + + switch (PreviewSettings.MaterialPreviewType) + { + case EVoxelGraphMaterialPreviewType::RGB: + { + Color.R = Material.GetR(); + Color.G = Material.GetG(); + Color.B = Material.GetB(); + break; + } + case EVoxelGraphMaterialPreviewType::Alpha: + { + Color.R = Material.GetA(); + Color.G = Material.GetA(); + Color.B = Material.GetA(); + break; + } + case EVoxelGraphMaterialPreviewType::SingleIndex: + { + Color = GetColor(Material.GetSingleIndex()); + break; + } + case EVoxelGraphMaterialPreviewType::MultiIndex_Overview: + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + + const FColor Color0 = GetColor(Material.GetMultiIndex_Index0()); + const FColor Color1 = GetColor(Material.GetMultiIndex_Index1()); + const FColor Color2 = GetColor(Material.GetMultiIndex_Index2()); + const FColor Color3 = GetColor(Material.GetMultiIndex_Index3()); + + Color.R = FVoxelUtilities::ClampToUINT8(FMath::RoundToInt(Color0.R * Strengths[0] + Color1.R * Strengths[1] + Color2.R * Strengths[2] + Color3.R * Strengths[3])); + Color.G = FVoxelUtilities::ClampToUINT8(FMath::RoundToInt(Color0.G * Strengths[0] + Color1.G * Strengths[1] + Color2.G * Strengths[2] + Color3.G * Strengths[3])); + Color.B = FVoxelUtilities::ClampToUINT8(FMath::RoundToInt(Color0.B * Strengths[0] + Color1.B * Strengths[1] + Color2.B * Strengths[2] + Color3.B * Strengths[3])); + + break; + } + case EVoxelGraphMaterialPreviewType::MultiIndex_SingleIndexPreview: + { + const TVoxelStaticArray Strengths = FVoxelUtilities::GetMultiIndexStrengths(Material); + + float Strength = 0; + if (PreviewSettings.MultiIndexToPreview == Material.GetMultiIndex_Index0()) Strength += Strengths[0]; + if (PreviewSettings.MultiIndexToPreview == Material.GetMultiIndex_Index1()) Strength += Strengths[1]; + if (PreviewSettings.MultiIndexToPreview == Material.GetMultiIndex_Index2()) Strength += Strengths[2]; + if (PreviewSettings.MultiIndexToPreview == Material.GetMultiIndex_Index3()) Strength += Strengths[3]; + + Color.R = FVoxelUtilities::FloatToUINT8(Strength); + Color.G = FVoxelUtilities::FloatToUINT8(Strength); + Color.B = FVoxelUtilities::FloatToUINT8(Strength); + + break; + } + case EVoxelGraphMaterialPreviewType::MultiIndex_Wetness: + { + Color.R = Material.GetMultiIndex_Wetness(); + Color.G = Material.GetMultiIndex_Wetness(); + Color.B = Material.GetMultiIndex_Wetness(); + + break; + } + case EVoxelGraphMaterialPreviewType::UV0: + { + Color.R = Material.GetU0(); + Color.G = Material.GetV0(); + break; + } + case EVoxelGraphMaterialPreviewType::UV1: + { + Color.R = Material.GetU1(); + Color.G = Material.GetV1(); + break; + } + case EVoxelGraphMaterialPreviewType::UV2: + { + Color.R = Material.GetU2(); + Color.G = Material.GetV2(); + break; + } + case EVoxelGraphMaterialPreviewType::UV3: + { + Color.R = Material.GetU3(); + Color.G = Material.GetV3(); + break; + } + default: ensure(false); + } + + Color.A = 255; + + if (PreviewSettings.bHybridMaterialRendering && Value > 0) + { + Color = FColor::Transparent; + } + + NewMaterials[TextureIndex] = Color; + } + else if (bShowCosts) + { + const float CostAlpha = FMath::Clamp((Costs[DataIndex] - MinCost) / (MaxCost - MinCost), 0, 1); + auto Color = FMath::Lerp(FLinearColor::Green, FLinearColor::Red, CostAlpha); + Color = FMath::Lerp(Color, FLinearColor::Black, Value > 0 ? 0.5f : 0.f); + NewMaterials[TextureIndex] = Color.ToFColor(false); + } + else + { + check(bShowRangeAnalysis); + + const int32 LocalX = FVoxelUtilities::DivideFloor(X, RangeAnalysisChunkSize); + const int32 LocalY = FVoxelUtilities::DivideFloor(Y, RangeAnalysisChunkSize); + const auto Range = Ranges[LocalX + LocalY * PreviewSettings.NumRangeAnalysisChunksPerAxis]; + + auto Color = Range.Contains(0.f) ? FLinearColor::Red : FLinearColor::Green; + Color = FMath::Lerp(Color, FLinearColor::Black, Value > 0 ? 0.5f : 0.f); + NewMaterials[TextureIndex] = Color.ToFColor(false); + } + + const int32 NumSameAxes = (FMath::Abs(X - ScreenPreviewedVoxel.X) < 2) + (FMath::Abs(Y - ScreenPreviewedVoxel.Y) < 2); + if (NumSameAxes == 1 && (PreviewSettings.bShowStats || PreviewSettings.bShowValues)) + { + NewMaterialsWithCrossAndNoAlpha[TextureIndex] = FMath::Lerp(FLinearColor(NewMaterials[TextureIndex]), FLinearColor::Black, 0.95f).ToFColor(true); + } + else + { + NewMaterialsWithCrossAndNoAlpha[TextureIndex] = NewMaterials[TextureIndex]; + } + NewMaterialsWithCrossAndNoAlpha[TextureIndex].A = 255; + } + } + + FVoxelTextureUtilities::UpdateColorTexture(DensitiesTexture, FIntPoint(Settings.Resolution, Settings.Resolution), NewDensities); + FVoxelTextureUtilities::UpdateColorTexture(MaterialsTexture, FIntPoint(Settings.Resolution, Settings.Resolution), NewMaterials); + FVoxelTextureUtilities::UpdateColorTexture(MaterialsTextureWithCrossAndNoAlpha, FIntPoint(Settings.Resolution, Settings.Resolution), NewMaterialsWithCrossAndNoAlpha); + + Preview->SetTexture(MaterialsTextureWithCrossAndNoAlpha); + Generator->SetPreviewTexture(NewMaterials, Settings.Resolution); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphPreview::UpdateMaterialParameters() +{ + VOXEL_FUNCTION_COUNTER(); + + const UVoxelGraphPreviewSettings& Settings = *Generator->PreviewSettings; + const auto Wrapper = FVoxelGraphPreviewSettingsWrapper(Settings); + + if (Settings.bHeightmapMode) + { + HeightmapMaterial = UMaterialInstanceDynamic::Create(Settings.HeightmapMaterial, nullptr); + if (!ensure(HeightmapMaterial)) + { + return; + } + + HeightmapMaterial->SetTextureParameterValue(TEXT("Color"), MaterialsTexture); + HeightmapMaterial->SetTextureParameterValue(TEXT("Height"), DensitiesTexture); + HeightmapMaterial->SetScalarParameterValue(TEXT("Height"), Settings.Height); + HeightmapMaterial->SetScalarParameterValue(TEXT("StartBias"), Settings.StartBias); + HeightmapMaterial->SetScalarParameterValue(TEXT("MaxSteps"), Settings.MaxSteps); + HeightmapMaterial->SetScalarParameterValue(TEXT("UseHeightAsColor"), Settings.bHeightBasedColor ? 1.f : 0.f); + HeightmapMaterial->SetScalarParameterValue(TEXT("UseWater"), Settings.bEnableWater ? 1.f : 0.f); + HeightmapMaterial->SetVectorParameterValue(TEXT("LightDirection"), Settings.LightDirection); + HeightmapMaterial->SetScalarParameterValue(TEXT("Brightness"), Settings.Brightness); + HeightmapMaterial->SetScalarParameterValue(TEXT("ShadowDensity"), Settings.ShadowDensity); + + PreviewSceneFloor->SetStaticMesh(Settings.Mesh); + PreviewSceneFloor->SetMaterial(0, HeightmapMaterial); + PreviewSceneFloor->SetWorldScale3D(FVector(10)); + PreviewSceneFloor->SetBoundsScale(1e6f); + PreviewSceneFloor->SetWorldRotation(FRotator::ZeroRotator); + } + else + { + SliceMaterial = UMaterialInstanceDynamic::Create(Settings.SliceMaterial, nullptr); + if (!ensure(SliceMaterial)) + { + return; + } + + SliceMaterial->SetTextureParameterValue(TEXT("Color"), MaterialsTexture); + + PreviewSceneFloor->SetStaticMesh(Settings.Mesh); + PreviewSceneFloor->SetMaterial(0, SliceMaterial); + PreviewSceneFloor->SetWorldScale3D(FVector(Wrapper.Resolution / 200.f)); + + const auto GetRotation = [&]() + { + const auto Make = [](const FVector& Vector, float Angle) + { + return FTransform(UKismetMathLibrary::RotatorFromAxisAndAngle(Vector, Angle)); + }; + const FVector X(1, 0, 0); + const FVector Y(0, 1, 0); + const FVector Z(0, 0, 1); + + switch (Settings.LeftToRight) + { + default: ensure(false); + case EVoxelGraphPreviewAxes::X: + { + switch (Settings.BottomToTop) + { + default: ensure(false); + case EVoxelGraphPreviewAxes::Y: return Make(Z, 90) * Make(Y, 180); + case EVoxelGraphPreviewAxes::Z: return Make(X, -90) * Make(Y, -90); + } + } + case EVoxelGraphPreviewAxes::Y: + { + switch (Settings.BottomToTop) + { + default: ensure(false); + case EVoxelGraphPreviewAxes::X: return FTransform::Identity; + case EVoxelGraphPreviewAxes::Z: return Make(Y, -90); + } + } + case EVoxelGraphPreviewAxes::Z: + { + switch (Settings.BottomToTop) + { + default: ensure(false); + case EVoxelGraphPreviewAxes::X: return Make(X, 90); + case EVoxelGraphPreviewAxes::Y: return Make(Y, 90) * Make(X, 90); + } + } + } + }; + PreviewSceneFloor->SetWorldRotation(GetRotation().Rotator()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +using FVoxelNodeArray = TArray>; + +uint32 GetTypeHash(const FVoxelNodeArray& Array) +{ + uint32 Hash = Array.Num(); + for (auto& It : Array) + { + Hash = HashCombine(Hash, GetTypeHash(It)); + } + return Hash; +} + +void FVoxelGraphPreview::AddMessages(FVoxelGraphGeneratorInstance& GraphGeneratorInstance) const +{ + VOXEL_FUNCTION_COUNTER(); + + auto& PreviewSettings = *Generator->PreviewSettings; + + const int32 PreviewLOD = PreviewSettings.LODToPreview; + const FIntVector PreviewedVoxel = PreviewSettings.PreviewedVoxel; + + auto& Octree = FVoxelOctreeUtilities::GetBottomNode(Data->GetOctree(), PreviewedVoxel.X, PreviewedVoxel.Y, PreviewedVoxel.Z); + + FVoxelContext Context(PreviewLOD, FVoxelItemStack(Octree.GetItemHolder()), {}, false); + Context.LocalX = PreviewedVoxel.X; + Context.LocalY = PreviewedVoxel.Y; + Context.LocalZ = PreviewedVoxel.Z; + Context.WorldX = PreviewedVoxel.X; + Context.WorldY = PreviewedVoxel.Y; + Context.WorldZ = PreviewedVoxel.Z; + + // If no placeable items the node will be huge + const FVoxelIntBox RangeBounds = FVoxelIntBox(PreviewedVoxel).Extend(32); + const FVoxelContextRange ContextRange(PreviewLOD, FVoxelItemStack(Octree.GetItemHolder()), {}, false, Octree.GetBounds().Overlap(RangeBounds)); + + if (PreviewSettings.bShowStats) + { + VOXEL_SCOPE_COUNTER("ShowStats"); + + TMap, FVoxelGraphStatsRecorder::FNodeStats> NodeStats; + { + VOXEL_SCOPE_COUNTER("Normal"); + + const auto Graph = GraphGeneratorInstance.GetGraph(); + check(Graph); + + FVoxelGraphVMComputeBuffers Buffers(GraphGeneratorInstance.GetVariablesBuffer(Graph)); + FVoxelGraphStatsRecorder Recorder; + Graph->Compute(Context, Buffers, EVoxelFunctionAxisDependencies::XYZWithoutCache, Recorder); + + for (auto& It : Recorder.GetStats()) + { + for (auto& SourceNode : It.Key->SourceNodes) + { + NodeStats.FindOrAdd(SourceNode) += It.Value; + } + } + } + + TMap, FVoxelGraphStatsRecorder::FNodeStats> RangeNodeStats; + { + VOXEL_SCOPE_COUNTER("Range"); + + const auto Graph = GraphGeneratorInstance.GetGraph(); + check(Graph); + + FVoxelGraphVMComputeRangeBuffers Buffers(GraphGeneratorInstance.GetRangeVariablesBuffer(Graph)); + FVoxelGraphStatsRangeRecorder Recorder; + Graph->ComputeRange(ContextRange, Buffers, Recorder); + FVoxelRangeFailStatus::Get().Reset(); + + for (auto& It : Recorder.GetStats()) + { + for (auto& SourceNode : It.Key->SourceNodes) + { + RangeNodeStats.FindOrAdd(SourceNode) += It.Value; + } + } + } + + TSet> Keys; + for (auto& It : NodeStats) + { + Keys.Add(It.Key); + } + for (auto& It : RangeNodeStats) + { + Keys.Add(It.Key); + } + + FVoxelGraphErrorReporter ErrorReporter(Generator); + for (auto& Key : Keys) + { + auto* Node = Key.Get(); + if (!ensure(Node)) + { + continue; + } + + const auto Stats = NodeStats.FindRef(Key); + const auto RangeStats = RangeNodeStats.FindRef(Key); + + ErrorReporter.AddMessageToNode(Node, + FString::Printf(TEXT("C++: %.2fns (range: %.2fns)"), + 1e9 * Stats.EstimatedCppTime, + 1e9 * RangeStats.EstimatedCppTime), + EVoxelGraphNodeMessageType::Warning, + false, false); + + ErrorReporter.AddMessageToNode(Node, + FString::Printf(TEXT(" VM: %.2fns (range: %.2fns)"), + 1e9 * Stats.VirtualMachineTime, + 1e9 * RangeStats.VirtualMachineTime), + EVoxelGraphNodeMessageType::Warning, + false, false); + } + ErrorReporter.Apply(false); + } + + if (PreviewSettings.bShowValues) + { + VOXEL_SCOPE_COUNTER("ShowValues"); + + struct FNodeValues + { + EVoxelPinCategory Category; + TOptional Value; + TOptional RangeValue; + bool bIsUsed = false; + }; + // Make sure to use the full source nodes array, else we get range error in macros due to mismatchs + TMap> NodesValues; + TMap> NodesRangeMessages; + + { + VOXEL_SCOPE_COUNTER("Normal"); + + const auto Graph = GraphGeneratorInstance.GetGraph(); + check(Graph); + + FVoxelGraphVMComputeBuffers Buffers(GraphGeneratorInstance.GetVariablesBuffer(Graph)); + FVoxelGraphRangeAnalysisRecorder Recorder; + Graph->Compute(Context, Buffers, EVoxelFunctionAxisDependencies::XYZWithoutCache, Recorder); + + auto NodeOutputs = Recorder.GetNodesOutputs(); + + // Copy constants + { + TSet Nodes; + Graph->GetConstantNodes(Nodes); + for (auto* Node : Nodes) + { + ensure(!NodeOutputs.Contains(Node)); + auto& Outputs = NodeOutputs.Add(Node); + + Outputs.Empty(Node->OutputCount); + Outputs.SetNumZeroed(Node->OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node->OutputCount; OutputIndex++) + { + int32 OutputId = Node->GetOutputId(OutputIndex); + if (OutputId != -1) + { + Outputs[OutputIndex] = Buffers.Variables[OutputId]; + } + } + } + } + + for (auto& It : NodeOutputs) + { + const FVoxelComputeNode& Node = *It.Key; + if (!ensure(Node.SourceNodes.Num() > 0)) + { + continue; + } + + auto& NodeValues = NodesValues.FindOrAdd(It.Key->SourceNodes); + NodeValues.SetNum(Node.OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node.OutputCount; OutputIndex++) + { + auto& NodeValue = NodeValues[OutputIndex]; + NodeValue.Category = Node.GetOutputCategory(OutputIndex); + NodeValue.Value = It.Value[OutputIndex]; + NodeValue.bIsUsed = Node.IsOutputUsed(OutputIndex); + } + } + } + + { + VOXEL_SCOPE_COUNTER("Range"); + + const auto Graph = GraphGeneratorInstance.GetGraph(); + check(Graph); + + FVoxelGraphVMComputeRangeBuffers Buffers(GraphGeneratorInstance.GetRangeVariablesBuffer(Graph)); + FVoxelGraphStatsRangeAnalysisRangeRecorder Recorder; + Graph->ComputeRange(ContextRange, Buffers, Recorder); + FVoxelRangeFailStatus::Get().Reset(); + + auto NodeOutputs = Recorder.GetNodesOutputs(); + + // Copy constants + { + TSet Nodes; + Graph->GetConstantNodes(Nodes); + for (auto* Node : Nodes) + { + ensure(!NodeOutputs.Contains(Node)); + auto& Outputs = NodeOutputs.Add(Node); + + Outputs.Empty(Node->OutputCount); + Outputs.SetNumZeroed(Node->OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node->OutputCount; OutputIndex++) + { + int32 OutputId = Node->GetOutputId(OutputIndex); + if (OutputId != -1) + { + Outputs[OutputIndex] = Buffers.Variables[OutputId]; + } + } + } + } + + for (auto& It : NodeOutputs) + { + const FVoxelComputeNode& Node = *It.Key; + if (!ensure(Node.SourceNodes.Num() > 0)) + { + continue; + } + + auto& NodeValues = NodesValues.FindOrAdd(It.Key->SourceNodes); + NodeValues.SetNum(Node.OutputCount); + for (int32 OutputIndex = 0; OutputIndex < Node.OutputCount; OutputIndex++) + { + auto& NodeValue = NodeValues[OutputIndex]; + NodeValue.Category = Node.GetOutputCategory(OutputIndex); + NodeValue.RangeValue = It.Value[OutputIndex]; + NodeValue.bIsUsed = Node.IsOutputUsed(OutputIndex); + } + } + + for (auto& It : Recorder.GetNodesRangeMessages()) + { + const FVoxelComputeNode& Node = *It.Key; + for (auto& SourceNode : Node.SourceNodes) + { + ensure(SourceNode.IsValid()); + NodesRangeMessages.FindOrAdd(It.Key->SourceNodes).Append(It.Value); + } + } + } + + FVoxelGraphErrorReporter ErrorReporter(Generator); + for (auto& It : NodesValues) + { + auto* Node = It.Key[0].Get(); + if (!ensure(Node)) + { + continue; + } + + for (int32 OutputIndex = 0; OutputIndex < It.Value.Num(); OutputIndex++) + { + auto& Value = It.Value[OutputIndex]; + + if (!Value.bIsUsed) + { + // Else we read invalid memory + continue; + } + + bool bCanShowRange = false; + switch (Value.Category) + { + default: ensure(false); + case EVoxelPinCategory::Exec: + case EVoxelPinCategory::Seed: + case EVoxelPinCategory::Wildcard: + case EVoxelPinCategory::Vector: + case EVoxelPinCategory::Material: + continue; + case EVoxelPinCategory::Boolean: + case EVoxelPinCategory::Int: + case EVoxelPinCategory::Float: + bCanShowRange = true; + break; + case EVoxelPinCategory::Color: + bCanShowRange = false; + break; + } + + const FName OutputName = Node->GetOutputPinName(OutputIndex); + + FString RangeString; + if (Value.RangeValue.IsSet() && bCanShowRange) + { + RangeString = FVoxelPinCategory::ToString(Value.Category, Value.RangeValue.GetValue()); + } + + FString ValueString; + if (Value.Value.IsSet()) + { + ValueString = FVoxelPinCategory::ToString(Value.Category, Value.Value.GetValue()); + } + + FString Message; + if (PreviewSettings.ShowValue == EVoxelGraphPreviewShowValue::ShowValue) + { + if (!ValueString.IsEmpty()) + { + if (OutputName.IsNone()) + { + Message = ValueString; + } + else + { + Message = OutputName.ToString() + ": " + ValueString; + } + } + } + else if (PreviewSettings.ShowValue == EVoxelGraphPreviewShowValue::ShowValueAndRange) + { + if (!ValueString.IsEmpty()) + { + if (OutputName.IsNone()) + { + Message = ValueString; + } + else + { + Message = OutputName.ToString() + ": " + ValueString; + } + + if (!RangeString.IsEmpty()) + { + Message += "\nRange: " + RangeString; + } + } + else if (!RangeString.IsEmpty()) + { + if (OutputName.IsNone()) + { + Message = "Range: " + ValueString; + } + else + { + Message = OutputName.ToString() + " Range: " + ValueString; + } + } + } + else + { + ensure(PreviewSettings.ShowValue == EVoxelGraphPreviewShowValue::ShowRange); + + if (!RangeString.IsEmpty()) + { + if (OutputName.IsNone()) + { + Message = "Range: " + ValueString; + } + else + { + Message = OutputName.ToString() + " Range: " + ValueString; + } + } + } + + const bool bIsInRange = + !Value.Value.IsSet() || + !Value.RangeValue.IsSet() || + FVoxelPinCategory::IsInRange(Value.Category, Value.Value.GetValue(), Value.RangeValue.GetValue()); + + if (!Message.IsEmpty() || !bIsInRange) + { + ErrorReporter.AddMessageToNode( + Node, + bIsInRange ? Message : ("RANGE ANALYSIS ERROR:\n" + Message), + bIsInRange ? EVoxelGraphNodeMessageType::Info : EVoxelGraphNodeMessageType::Error, + !bIsInRange, + !bIsInRange); + } + } + } + for (auto& It : NodesRangeMessages) + { + auto* Node = It.Key[0].Get(); + if (!ensure(Node)) + { + continue; + } + + for (auto& Message : It.Value) + { + ErrorReporter.AddMessageToNode( + Node, + Message, + EVoxelGraphNodeMessageType::Warning, + false, + false); + } + } + ErrorReporter.Apply(false); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.h new file mode 100644 index 00000000..3fde19d3 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.h @@ -0,0 +1,57 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +class SVoxelGraphPreview; +class SVoxelGraphPreviewViewport; + +class FVoxelData; +class FReferenceCollector; +class FAdvancedPreviewScene; +class FVoxelGraphGeneratorInstance; + +class UTexture2D; +class UStaticMeshComponent; +class UVoxelGraphGenerator; +class UVoxelLineBatchComponent; +class UMaterialInstanceDynamic; + +enum class EVoxelGraphPreviewFlags; + +class FVoxelGraphPreview +{ +public: + FVoxelGraphPreview( + UVoxelGraphGenerator* Generator, + const TSharedPtr& Preview, + const TSharedPtr& PreviewViewport, + const TSharedPtr& PreviewScene); + + void Update(EVoxelGraphPreviewFlags Flags); + void AddReferencedObjects(FReferenceCollector& Collector); + +private: + UVoxelGraphGenerator* const Generator; + TSharedPtr const Preview; + TSharedPtr const PreviewViewport; + TSharedPtr const PreviewScene; + + TVoxelSharedPtr Data; + + UStaticMeshComponent* PreviewSceneFloor = nullptr; + UVoxelLineBatchComponent* LineBatchComponent = nullptr; + + UMaterialInstanceDynamic* HeightmapMaterial = nullptr; + UMaterialInstanceDynamic* SliceMaterial = nullptr; + + UTexture2D* DensitiesTexture = nullptr; + UTexture2D* MaterialsTexture = nullptr; + UTexture2D* MaterialsTextureWithCrossAndNoAlpha = nullptr; + + void UpdateTextures(EVoxelGraphPreviewFlags Flags); + void UpdateMaterialParameters(); + void AddMessages(FVoxelGraphGeneratorInstance& GraphGeneratorInstance) const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.cpp new file mode 100644 index 00000000..607a84ab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.cpp @@ -0,0 +1,19 @@ +// Copyright 2020 Phyronnaz + +#include "SVoxelPalette.h" +#include "VoxelGraphSchema.h" + +void SVoxelPalette::Construct(const FArguments& InArgs) +{ + SGraphPalette::Construct(SGraphPalette::FArguments().AutoExpandActionMenu(false)); +} + +void SVoxelPalette::CollectAllActions(FGraphActionListBuilderBase& OutAllActions) +{ + const UVoxelGraphSchema* Schema = GetDefault(); + + FGraphActionMenuBuilder ActionMenuBuilder; + + Schema->GetPaletteActions(ActionMenuBuilder); + OutAllActions.Append(ActionMenuBuilder); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.h new file mode 100644 index 00000000..bbfa0973 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/SVoxelPalette.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "SGraphPalette.h" + +class SVoxelPalette : public SGraphPalette +{ +public: + SLATE_BEGIN_ARGS( SVoxelPalette ) {}; + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + +protected: + /** Callback used to populate all actions list in SGraphActionMenu */ + virtual void CollectAllActions(FGraphActionListBuilderBase& OutAllActions) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.cpp new file mode 100644 index 00000000..daf0b86d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.cpp @@ -0,0 +1,171 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelDebugGraphUtils.h" +#include "VoxelGraphGenerator.h" +#include "Compilation/VoxelGraphCompiler.h" +#include "VoxelGraphNodes/VoxelGraphNode.h" + +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphPin.h" + +void FVoxelDebugGraphUtils::DebugNodes(const TSet& Nodes, UVoxelGraphGenerator* Generator) +{ + struct FVoxelTmpLink + { + FVoxelCompilationNode* From; + int32 FromIndex; + FVoxelCompilationNode* To; + int32 ToIndex; + }; + + UEdGraph* const Graph = Generator->VoxelDebugGraph; + Graph->Modify(); + Generator->Modify(); + + // Delete existing nodes + { + auto NodesCopy = Graph->Nodes; // Copy as we are modifying it + for (auto& Node : NodesCopy) + { + Graph->RemoveNode(Node); + } + } + Generator->DebugNodes.Reset(); + + int32 MaxSourceNodesDepth = 0; + for (auto& Node : Nodes) + { + MaxSourceNodesDepth = FMath::Max(MaxSourceNodesDepth, Node->SourceNodes.Num()); + } + + TMap NodeMap; + TArray LinksToCreate; + for (auto* CompilationNode : Nodes) + { + UVoxelDebugNode* VoxelNode = NewObject(); + Generator->DebugNodes.Add(VoxelNode); + + VoxelNode->CompilationNode = CompilationNode->Clone(); + VoxelNode->Name = FText::FromString(CompilationNode->GetPrettyName()); + VoxelNode->Tooltip = FText::FromString("Class: " + CompilationNode->GetClassName()); + if (CompilationNode->SourceNodes.Num() > 0) + { + auto* SourceNode = CompilationNode->SourceNodes.Last(); + VoxelNode->Color = SourceNode->GetColor(); + VoxelNode->bIsCompact = SourceNode->IsCompact(); + } + + FGraphNodeCreator NodeCreator(*Graph); + UVoxelGraphNode* GraphNode = NodeCreator.CreateNode(); + for (auto& Message : CompilationNode->DebugMessages) + { + GraphNode->InfoMsg += Message; + } + + VoxelNode->GraphNode = GraphNode; + GraphNode->SetVoxelNode(VoxelNode); + NodeCreator.Finalize(); + + FString& InfoMsg = CastChecked(GraphNode)->InfoMsg; + if (Generator->bShowPinsIds) + { + int32 InputIdsCount = CompilationNode->GetInputCountWithoutExecs(); + int32 OutputIdsCount = CompilationNode->GetOutputCountWithoutExecs(); + for (int32 Index = 0; Index < FMath::Max(InputIdsCount, OutputIdsCount); Index++) + { + if (!InfoMsg.IsEmpty()) + { + InfoMsg += "\n"; + } + if (Index < InputIdsCount) + { + InfoMsg += FString::FromInt(CompilationNode->GetInputId(Index)); + } + InfoMsg += "\t\t"; + if (Index < OutputIdsCount) + { + InfoMsg += FString::FromInt(CompilationNode->GetOutputId(Index)); + } + } + } + if (Generator->bShowAxisDependencies) + { + if (!InfoMsg.IsEmpty()) + { + InfoMsg += "\n"; + } + InfoMsg += FVoxelAxisDependencies::ToString(FVoxelAxisDependencies::GetVoxelAxisDependenciesFromFlag(CompilationNode->Dependencies)); + } + + // Pin Debug + for (int32 Index = 0; Index < CompilationNode->GetInputCount() ; Index++) + { + GraphNode->GetInputPin(Index)->PinToolTip = "NumLinkedTo: " + FString::FromInt(CompilationNode->GetInputPin(Index).NumLinkedTo()); + } + for (int32 Index = 0; Index < CompilationNode->GetOutputCount(); Index++) + { + GraphNode->GetOutputPin(Index)->PinToolTip = "NumLinkedTo: " + FString::FromInt(CompilationNode->GetOutputPin(Index).NumLinkedTo()); + } + + GraphNode->NodePosX = 0; + GraphNode->NodePosY = 0; + + for (int32 Index = 0; Index < MaxSourceNodesDepth; Index++) + { + GraphNode->NodePosX *= Generator->NodesDepthScaleFactor; + GraphNode->NodePosY *= Generator->NodesDepthScaleFactor; + + auto& SourceNodes = CompilationNode->SourceNodes; + if (SourceNodes.IsValidIndex(Index)) + { + auto* SourceNode = SourceNodes.Last(Index); + if (SourceNode && SourceNode->GraphNode) + { + GraphNode->NodePosX += SourceNode->GraphNode->NodePosX; + GraphNode->NodePosY += SourceNode->GraphNode->NodePosY; + } + } + } + + + NodeMap.Add(CompilationNode, GraphNode); + + for (int32 I = 0; I < CompilationNode->GetOutputCount(); I++) + { + auto& Pin = CompilationNode->GetOutputPin(I); + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + auto& OtherNode = LinkedToPin.Node; + + if (Nodes.Contains(&OtherNode) && (Pin.PinCategory == EVoxelPinCategory::Exec || !Generator->bHideDataNodes)) + { + LinksToCreate.Add({ CompilationNode, I, &OtherNode, LinkedToPin.Index }); + } + } + } + for (int32 I = 0; I < CompilationNode->GetInputCount(); I++) + { + auto& Pin = CompilationNode->GetInputPin(I); + for (auto& LinkedToPin : Pin.IterateLinkedTo()) + { + auto& OtherNode = LinkedToPin.Node; + + if (Nodes.Contains(&OtherNode) && (Pin.PinCategory == EVoxelPinCategory::Exec || !Generator->bHideDataNodes)) + { + LinksToCreate.Add({ &OtherNode, LinkedToPin.Index, CompilationNode, I }); + } + } + } + } + + for (auto& Link : LinksToCreate) + { + auto* From = NodeMap[Link.From]; + auto* To = NodeMap[Link.To]; + + auto* FromPin = From->GetOutputPin(Link.FromIndex); + auto* ToPin = To->GetInputPin(Link.ToIndex); + + FromPin->MakeLinkTo(ToPin); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h new file mode 100644 index 00000000..551dbed9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h @@ -0,0 +1,50 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "Compilation/VoxelCompilationNode.h" +#include "VoxelDebugGraphUtils.generated.h" + +class FVoxelGraphCompiler; +class UVoxelGraphGenerator; + +UCLASS(NotPlaceable) +class UVoxelDebugNode : public UVoxelNode +{ + GENERATED_BODY() + +public: + TSharedPtr CompilationNode; + FLinearColor Color = FLinearColor::Black; + FText Name; + FText Tooltip; + bool bIsCompact = false; + +public: + int32 GetMaxInputPins() const override { return CompilationNode->GetInputCount(); } + int32 GetMinInputPins() const override { return CompilationNode->GetInputCount(); } + int32 GetOutputPinsCount() const override { return CompilationNode->GetOutputCount(); } + + FLinearColor GetColor() const override { return Color; } + FText GetTitle() const override { return Name; } + FText GetTooltip() const override { return Tooltip; } + bool IsCompact() const override { return bIsCompact; } + + FName GetInputPinName(int32 PinIndex) const override { return CompilationNode->GetInputPin(PinIndex).Name; } + FName GetOutputPinName(int32 PinIndex) const override { return CompilationNode->GetOutputPin(PinIndex).Name; } + + EVoxelPinCategory GetInputPinCategory(int32 PinIndex) const override { return CompilationNode->GetInputPin(PinIndex).PinCategory; } + EVoxelPinCategory GetOutputPinCategory(int32 PinIndex) const override { return CompilationNode->GetOutputPin(PinIndex).PinCategory; } + + FString GetInputPinDefaultValue(int32 PinIndex) const override { return CompilationNode->GetInputPin(PinIndex).GetDefaultValue(); } + + bool CanUserDeleteNode() const override { return false; } + bool CanDuplicateNode() const override { return false; } +}; + +namespace FVoxelDebugGraphUtils +{ + void DebugNodes(const TSet& Nodes, UVoxelGraphGenerator* Generator); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.cpp new file mode 100644 index 00000000..a8b143c9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.cpp @@ -0,0 +1,9 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelEdGraph.h" +#include "VoxelGraphGenerator.h" + +UVoxelGraphGenerator* UVoxelEdGraph::GetGenerator() const +{ + return CastChecked(GetOuter()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.h new file mode 100644 index 00000000..c586906e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelEdGraph.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraph.h" +#include "VoxelEdGraph.generated.h" + +class UVoxelGraphGenerator; + +UCLASS() +class UVoxelEdGraph : public UEdGraph +{ + GENERATED_BODY() + +public: + UVoxelGraphGenerator* GetGenerator() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.cpp new file mode 100644 index 00000000..1a4f405b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.cpp @@ -0,0 +1,399 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphCompileToCpp.h" +#include "VoxelGraphGenerator.h" + +#include "DesktopPlatformModule.h" +#include "Widgets/SWindow.h" +#include "Widgets/Notifications/SNotificationList.h" + +#include "HAL/PlatformFilemanager.h" + +#include "Misc/Paths.h" +#include "Misc/FileHelper.h" +#include "Misc/MessageDialog.h" + +#include "Framework/Application/SlateApplication.h" +#include "Framework/Notifications/NotificationManager.h" + +#include "Dom/JsonObject.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "Serialization/JsonWriter.h" +#include "GameProjectGenerationModule.h" +#include "GameProjectUtils.h" + +static const FString GeneratedBuildText( + R"(using UnrealBuildTool; + +public class GeneratedWorldGenerators : ModuleRules +{ + public GeneratedWorldGenerators(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Voxel", "VoxelGraph" }); + PrivatePCHHeaderFile = "GeneratedWorldGeneratorsPCH.h"; + } +} +)"); + +static const FString GeneratedModuleHeaderText( +R"(#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FGeneratedWorldGeneratorsModule : public IModuleInterface +{ +}; +)"); + +static const FString GeneratedModuleCppText( +R"(#include "GeneratedWorldGenerators.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FGeneratedWorldGeneratorsModule, GeneratedWorldGenerators); +)"); + +static const FString GeneratedModulePCHText = "#include \"VoxelGeneratedWorldGeneratorsPCH.h\""; + +static void Popup(const FString& Text, bool bSuccess = true) +{ + FNotificationInfo Info(FText::FromString(Text)); + Info.ExpireDuration = 10.f; + Info.CheckBoxState = bSuccess ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + FSlateNotificationManager::Get().AddNotification(Info); +} + +static void UpdateTextFileIfNeeded(const FString& Path, const FString& Text) +{ + FString OldText; + if (FFileHelper::LoadFileToString(OldText, *Path)) + { + if (OldText == Text) + { + Popup(Path + " didn't change: not updating it"); + } + else + { + if (FFileHelper::SaveStringToFile(Text, *Path)) + { + Popup(Path + " was successfully updated"); + } + else + { + Popup(Path + " was NOT successfully updated"); + } + } + } + else + { + if (FFileHelper::SaveStringToFile(Text, *Path)) + { + Popup(Path + " was successfully created"); + } + else + { + Popup(Path + " was NOT successfully created"); + } + } +} + +inline bool IsUnderDirectory(const FString& InPath, const FString& InDirectory) +{ +#if ENGINE_MINOR_VERSION < 24 + FString Path = FPaths::ConvertRelativePathToFull(InPath); + + FString Directory = FPaths::ConvertRelativePathToFull(InDirectory); + if (Directory.EndsWith(TEXT("/"))) + { + Directory.RemoveAt(Directory.Len() - 1); + } + +#if PLATFORM_WINDOWS || PLATFORM_XBOXONE || PLATFORM_HOLOLENS + int Compare = FCString::Strnicmp(*Path, *Directory, Directory.Len()); +#else + int Compare = FCString::Strncmp(*Path, *Directory, Directory.Len()); +#endif + + return Compare == 0 && (Path.Len() == Directory.Len() || Path[Directory.Len()] == '/'); +#else + return FPaths::IsUnderDirectory(InPath, InDirectory); +#endif +} + +static bool CompileInternal(UVoxelGraphGenerator* Generator, bool bIsAutomaticCompile, FString& OutError) +{ + auto& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); + auto& FileManager = IFileManager::Get(); + + // cpp project + if (!GameProjectUtils::ProjectHasCodeFiles()) + { + auto Result = FMessageDialog::Open(EAppMsgType::OkCancel, VOXEL_LOCTEXT( + "Your project is a blueprint only project. You need to convert it to a C++ one.\n" + "In the next window, click Next and then Create Class, leaving everything to default.")); + if (Result == EAppReturnType::Cancel) + { + OutError = "Canceled"; + return false; + } + FGameProjectGenerationModule::Get().OpenAddCodeToProjectDialog(); + OutError = "Need to convert to a C++ project"; + return false; + } + + // GeneratedWorldGenerators module + const FString GeneratedDirectory(FPaths::GameSourceDir() + "GeneratedWorldGenerators/"); + if (!FPaths::DirectoryExists(GeneratedDirectory)) + { + auto Result = FMessageDialog::Open(EAppMsgType::OkCancel, VOXEL_LOCTEXT( + "The GeneratedWorldGenerators module is missing. It will be automatically created.\n" + "A GeneratedWorldGenerators folder will be created in your game Source directory.")); + if (Result == EAppReturnType::Cancel) + { + OutError = "Canceled"; + return false; + } + + PlatformFile.CreateDirectoryTree(*GeneratedDirectory); + + const FString GeneratedBuild = GeneratedDirectory + "GeneratedWorldGenerators.Build.cs"; + FFileHelper::SaveStringToFile(GeneratedBuildText, *GeneratedBuild); + + const FString GeneratedModuleHeader = GeneratedDirectory + "GeneratedWorldGenerators.h"; + FFileHelper::SaveStringToFile(GeneratedModuleHeaderText, *GeneratedModuleHeader); + + const FString GeneratedModuleCpp = GeneratedDirectory + "GeneratedWorldGenerators.cpp"; + FFileHelper::SaveStringToFile(GeneratedModuleCppText, *GeneratedModuleCpp); + + const FString GeneratedModulePCH = GeneratedDirectory + "GeneratedWorldGeneratorsPCH.h"; + FFileHelper::SaveStringToFile(GeneratedModulePCHText, *GeneratedModulePCH); + + Popup(GeneratedDirectory + " successfully created"); + } + + // GeneratedWorldGenerators module dependency + { + TArray FoundFiles; + FileManager.FindFiles(FoundFiles, *FPaths::ProjectDir(), TEXT(".uproject")); + + if (FoundFiles.Num() != 1) + { + OutError = FString::Printf(TEXT("Error: %d .uproject found in the game directory"), FoundFiles.Num()); + return false; + } + + for (const auto& File : FoundFiles) + { + const FString UProjectPath(FPaths::ProjectDir() + File); + FString UProjectContent; + FFileHelper::LoadFileToString(UProjectContent, *UProjectPath); + + TSharedPtr UProjectObject; + TSharedRef> UProjectReader = TJsonReaderFactory<>::Create(UProjectContent); + if (!FJsonSerializer::Deserialize(UProjectReader, UProjectObject)) + { + OutError = "Invalid .uproject: can't deserialize json"; + return false; + } + check(UProjectObject.IsValid()); + + bool bModuleInUProject = false; + + const TArray>* ModulesPtr = nullptr; + if (!UProjectObject->TryGetArrayField("Modules", ModulesPtr)) + { + OutError = "Invalid .uproject: No Modules array"; + return false; + } + check(ModulesPtr); + + const auto& Modules = *ModulesPtr; + for (auto& Module : Modules) + { + if (Module->AsObject()->GetStringField("Name") == "GeneratedWorldGenerators") + { + bModuleInUProject = true; + break; + } + } + + if (!bModuleInUProject) + { + auto Result = FMessageDialog::Open(EAppMsgType::OkCancel, VOXEL_LOCTEXT( + "The GeneratedWorldGenerators module dependency is missing from your .uproject. It will automatically added.\n" + "A backup of your .uproject will be created next to it.")); + if (Result == EAppReturnType::Cancel) + { + OutError = "Canceled"; + return false; + } + + auto NewModules = Modules; + TSharedPtr GeneratedWorldGeneratorsModule = MakeShared(); + GeneratedWorldGeneratorsModule->Values.Add("Name", MakeShared("GeneratedWorldGenerators")); + GeneratedWorldGeneratorsModule->Values.Add("Type", MakeShared("Runtime")); + GeneratedWorldGeneratorsModule->Values.Add("LoadingPhase", MakeShared("Default")); + NewModules.Add(MakeShared(GeneratedWorldGeneratorsModule)); + + UProjectObject->SetArrayField("Modules", NewModules); + + // Backup uproject + const FString Backup = UProjectPath + ".bak"; + FileManager.Copy(*Backup, *UProjectPath); + + FString Json; + TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&Json, 0); + if (!FJsonSerializer::Serialize(UProjectObject.ToSharedRef(), JsonWriter)) + { + OutError = "Failed to serialize new uproject"; + return false; + } + if (!FFileHelper::SaveStringToFile(Json, *UProjectPath)) + { + OutError = "Failed to write new uproject"; + return false; + } + + Popup("Module dependency successfully added"); + } + } + } + + { + TArray FoundFiles; + FileManager.FindFiles(FoundFiles, *FPaths::GameSourceDir(), TEXT(".Target.cs")); + + for (const auto& File : FoundFiles) + { + const FString TargetPath(FPaths::GameSourceDir() + File); + FString TargetContent; + FFileHelper::LoadFileToString(TargetContent, *TargetPath); + if (!TargetContent.Contains("\"GeneratedWorldGenerators\"")) + { + auto Result = FMessageDialog::Open(EAppMsgType::YesNoCancel, FText::FromString(File + " doesn't have the GeneratedWorldGenerators module dependency. Add it automatically? (You'll have to restart the editor)\n\nChoose Yes if you don't know")); + switch (Result) + { + case EAppReturnType::No: + continue; + case EAppReturnType::Yes: + break; + case EAppReturnType::Cancel: + OutError = "Canceled"; + return false; + default: + check(false); + break; + } + + int32 Position = TargetContent.Find("ExtraModuleNames.AddRange("); + if (Position < 0) + { + Position = TargetContent.Find("ExtraModuleNames.Add("); + } + if (Position < 0) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Cannot add the GeneratedWorldGenerators module dependency. Please add the following code to " + File + ": \n\nExtraModuleNames.AddRange( new string[] { \"GeneratedWorldGenerators\" } );")); + } + else + { + TargetContent.InsertAt(Position, "ExtraModuleNames.AddRange( new string[] { \"GeneratedWorldGenerators\" } );\n\t\t"); + + FileManager.Move(*(TargetPath + ".bak"), *TargetPath); + FFileHelper::SaveStringToFile(TargetContent, *TargetPath); + + Popup("GeneratedWorldGenerators module dependency successfully added to " + File); + } + } + } + } + + // Show the file browser dialog + { + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + check(DesktopPlatform); + + TArray OutFiles; + if (bIsAutomaticCompile) + { + if (Generator->SaveLocation.FilePath.IsEmpty()) + { + OutError = "Save location is empty!"; + return false; + } + OutFiles.Add(FPaths::ProjectDir() / Generator->SaveLocation.FilePath); + } + else + { + if (DesktopPlatform->SaveFileDialog( + FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr), + "File to create", + Generator->LastSavePath.IsEmpty() + ? GeneratedDirectory + : FPaths::ProjectDir() / Generator->LastSavePath, + Generator->GetName() + ".h", + TEXT("C++ header (*.h)|*.h"), + EFileDialogFlags::None, + OutFiles)) + { + check(OutFiles.Num() == 1); + } + } + + if (OutFiles.Num() > 0) + { + check(OutFiles.Num() == 1); + + const FString HeaderPath = FPaths::ConvertRelativePathToFull(OutFiles[0]); + if (!HeaderPath.EndsWith(".h")) + { + OutError = "Filename must end with .h"; + return false; + } + + const FString FolderPath = FPaths::GetPath(HeaderPath); + const FString BaseName = FPaths::GetBaseFilename(HeaderPath); + + FString HeaderText; + FString CppText; + if (!Generator->CompileToCpp(HeaderText, CppText, BaseName)) + { + Popup("Compilation failed!", false); + return true; + } + + const FString CppPath = FolderPath + "/" + BaseName + ".cpp"; + + UpdateTextFileIfNeeded(HeaderPath, HeaderText); + UpdateTextFileIfNeeded(CppPath, CppText); + + if (IsUnderDirectory(FolderPath, FPaths::ProjectDir())) + { + Generator->LastSavePath = FolderPath; + FPaths::MakePathRelativeTo(Generator->LastSavePath, *FPaths::ProjectDir()); + } + else + { + Generator->LastSavePath.Reset(); + } + Generator->MarkPackageDirty(); + } + } + + return true; +} + +void FVoxelGraphCompileToCpp::Compile(UVoxelGraphGenerator* Generator, bool bIsAutomaticCompile) +{ + if (!ensure(Generator)) + { + return; + } + + FString Error; + if (!CompileInternal(Generator, bIsAutomaticCompile, Error)) + { + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to compile " + Generator->GetName() + ":\n" + Error)); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.h new file mode 100644 index 00000000..8915ca43 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphCompileToCpp.h @@ -0,0 +1,13 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UVoxelGraphGenerator; +class SWidget; + +namespace FVoxelGraphCompileToCpp +{ + void Compile(UVoxelGraphGenerator* Generator, bool bIsAutomaticCompile); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.cpp new file mode 100644 index 00000000..39f0c8d5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.cpp @@ -0,0 +1,99 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphConnectionDrawingPolicy.h" +#include "VoxelGraphSchema.h" +#include "VoxelPinCategory.h" + +#include "BlueprintEditorSettings.h" +#include "EdGraph/EdGraph.h" + +FConnectionDrawingPolicy* FVoxelGraphConnectionDrawingPolicyFactory::CreateConnectionPolicy(const UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) const +{ + if (Schema->IsA(UVoxelGraphSchema::StaticClass())) + { + return new FVoxelGraphConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements, InGraphObj); + } + return nullptr; +} + +FVoxelGraphConnectionDrawingPolicy::FVoxelGraphConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) + : FConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, ZoomFactor, InClippingRect, InDrawElements) + , GraphObj(InGraphObj) +{ + // Don't want to draw ending arrowheads + ArrowImage = nullptr; + ArrowRadius = FVector2D::ZeroVector; + + // But we do want to draw midpoint arrowheads + if (GetDefault()->bDrawMidpointArrowsInBlueprints) + { + MidpointImage = FEditorStyle::GetBrush(TEXT("Graph.Arrow")); + MidpointRadius = MidpointImage->ImageSize * ZoomFactor * 0.5f; + } + + // Cache off the editor options + AttackColor = Settings->TraceAttackColor; + SustainColor = Settings->TraceSustainColor; + ReleaseColor = Settings->TraceReleaseColor; + + AttackWireThickness = Settings->TraceAttackWireThickness; + SustainWireThickness = Settings->TraceSustainWireThickness; + ReleaseWireThickness = Settings->TraceReleaseWireThickness; + DefaultDataWireThickness = Settings->DefaultDataWireThickness; + DefaultExecutionWireThickness = Settings->DefaultExecutionWireThickness; + + TracePositionBonusPeriod = Settings->TracePositionBonusPeriod; + TracePositionExponent = Settings->TracePositionExponent; + AttackHoldPeriod = Settings->TraceAttackHoldPeriod; + DecayPeriod = Settings->TraceDecayPeriod; + DecayExponent = Settings->TraceDecayExponent; + SustainHoldPeriod = Settings->TraceSustainHoldPeriod; + ReleasePeriod = Settings->TraceReleasePeriod; + ReleaseExponent = Settings->TraceReleaseExponent; +} + +void FVoxelGraphConnectionDrawingPolicy::Draw(TMap, FArrangedWidget>& InPinGeometries, FArrangedChildren& ArrangedNodes) +{ + // Draw everything + FConnectionDrawingPolicy::Draw(InPinGeometries, ArrangedNodes); +} + +// Give specific editor modes a chance to highlight this connection or darken non-interesting connections +void FVoxelGraphConnectionDrawingPolicy::DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) +{ + Params.WireThickness = DefaultDataWireThickness; + Params.AssociatedPin1 = OutputPin; + Params.AssociatedPin2 = InputPin; + + // Get the schema and grab the default color from it + check(OutputPin); + check(GraphObj); + const UEdGraphSchema* Schema = GraphObj->GetSchema(); + + if (OutputPin->bOrphanedPin || (InputPin && InputPin->bOrphanedPin)) + { + Params.WireColor = FLinearColor::Red; + } + else + { + Params.WireColor = Schema->GetPinTypeColor(OutputPin->PinType); + } + + if (FVoxelPinCategory::FromString(OutputPin->PinType.PinCategory) == EVoxelPinCategory::Exec) + { + Params.WireThickness = DefaultExecutionWireThickness; + } + + if (OutputPin->bIsDiffing) + { + Params.WireThickness *= 5.f; + Params.bDrawBubbles = true; + } + + const bool bDeemphasizeUnhoveredPins = HoveredPins.Num() > 0; + + if (bDeemphasizeUnhoveredPins) + { + ApplyHoverDeemphasis(OutputPin, InputPin, /*inout*/ Params.WireThickness, /*inout*/ Params.WireColor); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.h new file mode 100644 index 00000000..d808599b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.h @@ -0,0 +1,77 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "EdGraphUtilities.h" +#include "ConnectionDrawingPolicy.h" + +struct FVoxelGraphConnectionDrawingPolicyFactory : public FGraphPanelPinConnectionFactory +{ +public: + virtual ~FVoxelGraphConnectionDrawingPolicyFactory() {} + + // FGraphPanelPinConnectionFactory + virtual FConnectionDrawingPolicy* CreateConnectionPolicy(const UEdGraphSchema* Schema, int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) const override; + // ~FGraphPanelPinConnectionFactory + +}; + +// This class draws the connections for an UEdGraph using a Voxel schema +class FVoxelGraphConnectionDrawingPolicy : public FConnectionDrawingPolicy +{ +protected: + // Times for one execution pair within the current graph + struct FTimePair + { + double PredExecTime; + double ThisExecTime; + + FTimePair() + : PredExecTime(0.0) + , ThisExecTime(0.0) + { + } + }; + + // Map of pairings + typedef TMap FExecPairingMap; + + // Map of nodes that preceded before a given node in the execution sequence (one entry for each pairing) + TMap PredecessorNodes; + + UEdGraph* GraphObj; + + FLinearColor ActiveColor; + FLinearColor InactiveColor; + + float ActiveWireThickness; + float InactiveWireThickness; + +public: + FVoxelGraphConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float ZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj); + + // FConnectionDrawingPolicy interface + virtual void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params) override; + virtual void Draw(TMap, FArrangedWidget>& PinGeometries, FArrangedChildren& ArrangedNodes) override; + // End of FConnectionDrawingPolicy interface + + FLinearColor AttackColor; + FLinearColor SustainColor; + FLinearColor ReleaseColor; + + float AttackWireThickness; + float SustainWireThickness; + float ReleaseWireThickness; + float DefaultDataWireThickness; + float DefaultExecutionWireThickness; + + float TracePositionBonusPeriod; + float TracePositionExponent; + float AttackHoldPeriod; + float DecayPeriod; + float DecayExponent; + float SustainHoldPeriod; + float ReleasePeriod; + float ReleaseExponent; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.cpp new file mode 100644 index 00000000..9045167d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.cpp @@ -0,0 +1,237 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditor.h" +#include "VoxelEdGraph.h" +#include "VoxelNode.h" +#include "VoxelGraphSchema.h" +#include "VoxelGraphGenerator.h" +#include "IVoxelGraphEditorToolkit.h" +#include "VoxelGraphEditorUtilities.h" +#include "VoxelGraphNodes/VoxelGraphNode_Root.h" +#include "VoxelGraphNodes/VoxelGraphNode.h" +#include "VoxelGraphNodes/VoxelGraphNode_Knot.h" + +#include "Kismet2/BlueprintEditorUtils.h" + +inline TArray CreateVoxelPinsFromGraphPin(UEdGraphPin& Pin) +{ + TArray Result; + + if (Pin.SubPins.Num() == 0) + { + Result.Add(FVoxelPin(Pin.PinId, Pin.DefaultValue, FVoxelPinCategory::FromString(Pin.PinType.PinCategory))); + } + else + { + TArray SubDefaultValues; + Pin.DefaultValue.ParseIntoArray(SubDefaultValues, TEXT(",")); + + for (int32 Index = 0; Index < Pin.SubPins.Num(); Index++) + { + auto& SubPin = *Pin.SubPins[Index]; + Result.Add(FVoxelPin( + SubPin.PinId, + SubDefaultValues.IsValidIndex(Index) ? SubDefaultValues[Index] : "", + FVoxelPinCategory::FromString(SubPin.PinType.PinCategory))); + } + } + + const auto CheckOtherPin = [&](UEdGraphPin& OtherPin) + { + auto* const OtherNode = Cast(OtherPin.GetOwningNode()); + if (!OtherNode) + { + return; + } + + if (Pin.SubPins.Num() > 0) + { + if (!ensure(Pin.SubPins.Num() == OtherPin.SubPins.Num())) + { + return; + } + + for (int32 Index = 0; Index < Pin.SubPins.Num(); Index++) + { + Result[Index].OtherNodes.Add(OtherNode->VoxelNode); + Result[Index].OtherPinIds.Add(OtherPin.SubPins[Index]->PinId); + } + } + else + { + Result[0].OtherNodes.Add(OtherNode->VoxelNode); + Result[0].OtherPinIds.Add(OtherPin.PinId); + } + }; + + if (Pin.LinkedTo.Num() > 0) + { + for (UEdGraphPin* OtherPin : Pin.LinkedTo) + { + auto Knot = Cast(OtherPin->GetOwningNode()); + if (Knot) + { + const auto NewOtherPins = Pin.Direction == EGPD_Input ? Knot->GetAllInputPins() : Knot->GetAllOutputPins(); + for (auto& NewOtherPin : NewOtherPins) + { + CheckOtherPin(*NewOtherPin); + } + } + else + { + CheckOtherPin(*OtherPin); + } + } + } + + return Result; +} + +UEdGraph* FVoxelGraphEditor::CreateNewVoxelGraph(UVoxelGraphGenerator* InGenerator) +{ + return CastChecked(FBlueprintEditorUtils::CreateNewGraph(InGenerator, NAME_None, UVoxelEdGraph::StaticClass(), UVoxelGraphSchema::StaticClass())); +} + +void FVoxelGraphEditor::CreateVoxelGraphNode(UEdGraph* VoxelGraph, UVoxelNode* InVoxelNode, bool bSelectNewNode) +{ + FGraphNodeCreator NodeCreator(*VoxelGraph); + UVoxelGraphNode* GraphNode = NodeCreator.CreateUserInvokedNode(bSelectNewNode); + InVoxelNode->GraphNode = GraphNode; + GraphNode->SetVoxelNode(InVoxelNode); + NodeCreator.Finalize(); +} + +void FVoxelGraphEditor::CompileVoxelNodesFromGraphNodes(UVoxelGraphGenerator* Generator) +{ + Generator->Modify(); + { + Generator->FirstNode = nullptr; + Generator->AllNodes.Empty(); + + TArray AllNodes; + for (auto& Node : Generator->VoxelGraph->Nodes) + { + UVoxelGraphNode* GraphNode = Cast(Node); + if (GraphNode && GraphNode->VoxelNode) + { + UVoxelNode* VoxelNode = GraphNode->VoxelNode; + check(VoxelNode); + + check(!AllNodes.Contains(VoxelNode)); + AllNodes.Add(VoxelNode); + + TArray InputPins; + for (auto& InputPin : GraphNode->GetInputPins()) + { + if (!InputPin->bHidden) + { + InputPins.Append(CreateVoxelPinsFromGraphPin(*InputPin)); + } + } + + TArray OutputPins; + for (auto& OutputPin : GraphNode->GetOutputPins()) + { + if (!OutputPin->bHidden) + { + OutputPins.Append(CreateVoxelPinsFromGraphPin(*OutputPin)); + } + } + + VoxelNode->SetFlags(RF_Transactional); + VoxelNode->Modify(); + VoxelNode->InputPins = InputPins; + VoxelNode->OutputPins = OutputPins; + VoxelNode->PostEditChange(); + } + else + { + UVoxelGraphNode_Root* GraphNodeRoot = Cast(Node); + if (GraphNodeRoot) + { + const TArray OutputPins = GraphNodeRoot->GetOutputPins(); + + check(OutputPins.Num() == 1); + check(OutputPins[0]->LinkedTo.Num() <= 1); + if (OutputPins[0]->LinkedTo.Num() == 1) + { + UEdGraphPin* OtherPin = OutputPins[0]->LinkedTo[0]; + + auto Knot = Cast(OtherPin->GetOwningNode()); + + if (Knot) + { + auto NewOtherPins = Knot->GetAllOutputPins(); + if (NewOtherPins.Num() > 0) + { + check(NewOtherPins.Num() == 1); + auto NewOtherPin = NewOtherPins[0]; + Generator->FirstNode = CastChecked(NewOtherPin->GetOwningNode())->VoxelNode; + Generator->FirstNodePinId = NewOtherPin->PinId; + } + } + else + { + Generator->FirstNode = CastChecked(OtherPin->GetOwningNode())->VoxelNode; + Generator->FirstNodePinId = OtherPin->PinId; + } + } + } + } + } + + AllNodes.Remove(nullptr); + Generator->AllNodes = AllNodes; + } + Generator->PostEditChange(); + + UpdatePreview(Generator, EVoxelGraphPreviewFlags::UpdateTextures); +} + +void FVoxelGraphEditor::UpdatePreview(UVoxelGraphGenerator* Generator, EVoxelGraphPreviewFlags Flags) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Generator->VoxelGraph)) + { + Editor->TriggerUpdatePreview(Flags); + } +} + +void FVoxelGraphEditor::SelectNodesAndZoomToFit(UEdGraph* Graph, const TArray& Nodes) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Graph)) + { + Editor->SelectNodesAndZoomToFit(Nodes); + } +} + +void FVoxelGraphEditor::RefreshNodesMessages(UEdGraph* Graph) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Graph)) + { + Editor->RefreshNodesMessages(); + } +} + +void FVoxelGraphEditor::DebugNodes(UEdGraph* DebugGraph, const TSet& Nodes) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(DebugGraph)) + { + Editor->DebugNodes(Nodes); + } +} + +void FVoxelGraphEditor::AddMessages(const UVoxelGraphGenerator* Generator, const TArray& Messages) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Generator->VoxelGraph)) + { + Editor->AddMessages(Messages); + } +} + +void FVoxelGraphEditor::ClearMessages(const UVoxelGraphGenerator* Generator, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) +{ + if (auto Editor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Generator->VoxelGraph)) + { + Editor->ClearMessages(bClearAll, MessagesToClear); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.h new file mode 100644 index 00000000..d82a42d4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditor.h @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "IVoxelGraphEditor.h" + +class FVoxelGraphEditor : public IVoxelGraphEditor +{ +public: + virtual UEdGraph* CreateNewVoxelGraph(UVoxelGraphGenerator* InGenerator) override; + virtual void CreateVoxelGraphNode(UEdGraph* VoxelGraph, UVoxelNode* InVoxelNode, bool bSelectNewNode) override; + virtual void CompileVoxelNodesFromGraphNodes(UVoxelGraphGenerator* Generator) override; + virtual void UpdatePreview(UVoxelGraphGenerator* Generator, EVoxelGraphPreviewFlags Flags) override; + virtual void SelectNodesAndZoomToFit(UEdGraph* Graph, const TArray& Nodes) override; + virtual void RefreshNodesMessages(UEdGraph* Graph) override; + virtual void DebugNodes(UEdGraph* DebugGraph, const TSet& Nodes) override; + virtual void AddMessages(const UVoxelGraphGenerator* Generator, const TArray& Messages) override; + virtual void ClearMessages(const UVoxelGraphGenerator* Generator, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorCommands.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorCommands.h new file mode 100644 index 00000000..fdfea9a4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorCommands.h @@ -0,0 +1,92 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" + +class FVoxelGraphEditorCommands : public TCommands +{ +public: + // Constructor + FVoxelGraphEditorCommands() + : TCommands + ( + "VoxelGraphEditor", // Context name for icons + VOXEL_LOCTEXT("Voxel Graph Editor"), // Localized context name for displaying + NAME_None, // Parent + "VoxelGraphStyle" // Icon Style Set + ) + { + } + + // Compile the graph to C++ + TSharedPtr CompileToCpp; + + // Compile the nodes + TSharedPtr RecreateNodes; + + // Enable auto preview update + TSharedPtr ToggleAutomaticPreview; + + // Update preview + TSharedPtr UpdatePreview; + TSharedPtr UpdateVoxelWorlds; + TSharedPtr ClearNodesMessages; + + TSharedPtr ShowAxisDependencies; + + TSharedPtr ShowStats; + TSharedPtr ShowValues; + + // Adds an input to the node + TSharedPtr AddInput; + + // Removes an input from the node + TSharedPtr DeleteInput; + + TSharedPtr TogglePinPreview; + TSharedPtr SplitPin; + TSharedPtr CombinePin; + + // Local variables + TSharedPtr SelectLocalVariableUsages; + TSharedPtr SelectLocalVariableDeclaration; + TSharedPtr ConvertVariablesToReroute; + TSharedPtr ConvertRerouteToVariables; + + TSharedPtr ReconstructNode; + +#define LOCTEXT_NAMESPACE "Voxel" + // Initialize commands + virtual void RegisterCommands() override + { + UI_COMMAND(CompileToCpp, "Compile To C++", "Create C++ file from graph", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(RecreateNodes, "Recreate Nodes", "Reconstruct all the nodes", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(ToggleAutomaticPreview, "Automatic Preview", "Enable Automatic Preview", EUserInterfaceActionType::ToggleButton, FInputChord()); + UI_COMMAND(UpdatePreview, "Update Preview", "Update preview", EUserInterfaceActionType::Button, FInputChord(EKeys::F5)); + UI_COMMAND(UpdateVoxelWorlds, "Update Voxel Worlds", "Update voxel worlds", EUserInterfaceActionType::Button, FInputChord(EKeys::F5, false, true, false, false)); + + UI_COMMAND(ClearNodesMessages, "Clear Messages", "Clear nodes messages", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(ShowAxisDependencies, "Show Axis Dependencies", "Show Axis Dependencies", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(ShowStats, "Show Stats", "Show statistics about the previewed voxel. Click the graph preview to change the previewed voxel.", EUserInterfaceActionType::ToggleButton, FInputChord()); + UI_COMMAND(ShowValues, "Show Values", "Show the values of all the nodes on the previewed voxel. Click the graph preview to change the previewed voxel.", EUserInterfaceActionType::ToggleButton, FInputChord()); + + UI_COMMAND(AddInput, "Add Input", "Adds an input to the node", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(DeleteInput, "Delete Input", "Removes an input from the node", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(TogglePinPreview, "Toggle pin preview", "Makes the preview viewport start/stop previewing this pin", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(SplitPin, "Split vector pin", "Split this pin into X Y Z pins", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(CombinePin, "Combine pins into vector", "Combine this pin with its neighbors to make a vector", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(SelectLocalVariableUsages, "Select Usages", "Select this variable usages", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(SelectLocalVariableDeclaration, "Select Declaration", "Select this variable declaration", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(ConvertVariablesToReroute, "Convert to reroute", "Convert this variable to a reroute node", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(ConvertRerouteToVariables, "Convert to variables", "Convert this reroute node to local variables", EUserInterfaceActionType::Button, FInputChord()); + + UI_COMMAND(ReconstructNode, "Reconstruct Node", "Recreate this node to update for pins changes", EUserInterfaceActionType::Button, FInputChord()); + } +#undef LOCTEXT_NAMESPACE +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp new file mode 100644 index 00000000..f07712b0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp @@ -0,0 +1,145 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditorModule.h" + +#include "VoxelGraphEditor.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphCompileToCpp.h" +#include "VoxelGraphEditorToolkit.h" +#include "VoxelGraphPanelPinFactory.h" +#include "VoxelGraphConnectionDrawingPolicy.h" +#include "VoxelGraphNodes/VoxelGraphNodeFactory.h" + +#include "ContentBrowserModule.h" +#include "Modules/ModuleManager.h" +#include "Brushes/SlateImageBrush.h" +#include "Styling/SlateStyle.h" +#include "Styling/SlateStyleRegistry.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + +const FVector2D Icon14x14(14.0f, 14.0f); +const FVector2D Icon16x16(16.0f, 16.0f); +const FVector2D Icon20x20(20.0f, 20.0f); +const FVector2D Icon40x40(40.0f, 40.0f); +const FVector2D Icon64x64(64.0f, 64.0f); +const FVector2D Icon512x512(512.0f, 512.0f); + +/** + * Implements the VoxelEditor module. + */ +class FVoxelGraphEditorModule : public IVoxelGraphEditorModule +{ +public: + virtual void StartupModule() override + { + IVoxelGraphEditor::SetVoxelGraphEditor(MakeShared()); + + FEdGraphUtilities::RegisterVisualPinConnectionFactory(MakeShared()); + FEdGraphUtilities::RegisterVisualNodeFactory(MakeShared()); + FEdGraphUtilities::RegisterVisualPinFactory(MakeShared()); + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")); + ContentBrowserModule.GetAllAssetViewContextMenuExtenders().Add(FContentBrowserMenuExtender_SelectedAssets::CreateLambda([=](const TArray& SelectedAssets) + { + const auto Extender = MakeShared(); + + for (auto& It : SelectedAssets) + { + if (!It.GetClass()->IsChildOf()) + { + return Extender; + } + } + + Extender->AddMenuExtension( + "CommonAssetActions", + EExtensionHook::After, + nullptr, + FMenuExtensionDelegate::CreateLambda([=](FMenuBuilder& MenuBuilder) + { + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Compile voxel graph to C++"), + TAttribute(), + FSlateIcon(NAME_None, NAME_None), + FUIAction(FExecuteAction::CreateLambda([=]() + { + for (auto& Asset : SelectedAssets) + { + FVoxelGraphCompileToCpp::Compile(Cast(Asset.GetAsset()), true); + } + }))); + })); + + return Extender; + })); + + // Icons + { + StyleSet = MakeShareable(new FSlateStyleSet("VoxelGraphStyle")); + StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate")); + StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); + + // Compile To C++ + StyleSet->Set("VoxelGraphEditor.CompileToCpp" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_compile_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.CompileToCpp.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_compile_40x.png")), Icon20x20)); + + // Update Macros + StyleSet->Set("VoxelGraphEditor.RecreateNodes" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_Cascade_RestartInLevel_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.RecreateNodes.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_Cascade_RestartInLevel_40x.png")), Icon20x20)); + + // Enable automatic preview + StyleSet->Set("VoxelGraphEditor.ToggleAutomaticPreview" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_MatEd_LivePreview_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.ToggleAutomaticPreview.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_MatEd_LivePreview_40x.png")), Icon20x20)); + + // Update preview + StyleSet->Set("VoxelGraphEditor.UpdatePreview" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_Cascade_RestartInLevel_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.UpdatePreview.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_Cascade_RestartInLevel_40x.png")), Icon20x20)); + + // Clear messages + StyleSet->Set("VoxelGraphEditor.ClearNodesMessages" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_file_new_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.ClearNodesMessages.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_file_new_16px.png")), Icon20x20)); + + // Show Axis Dependencies + StyleSet->Set("VoxelGraphEditor.ShowAxisDependencies" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/Profiler/profiler_Calls_32x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.ShowAxisDependencies.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/Profiler/profiler_Calls_32x.png")), Icon20x20)); + + // Show stats + StyleSet->Set("VoxelGraphEditor.ShowStats" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/Profiler/Profiler_Data_Capture_40x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.ShowStats.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/Profiler/Profiler_Data_Capture_40x.png")), Icon20x20)); + + // Show values + StyleSet->Set("VoxelGraphEditor.ShowValues" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_TextureEd_CompressNow_512x.png")), Icon40x40)); + StyleSet->Set("VoxelGraphEditor.ShowValues.Small" , new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Icons/icon_TextureEd_CompressNow_512x.png")), Icon20x20)); + + FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); + } + } + + virtual void ShutdownModule() override + { + if (StyleSet.IsValid()) + { + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); + StyleSet.Reset(); + } + } + + virtual bool SupportsDynamicReloading() override + { + return true; + } + + virtual TSharedRef CreateVoxelGraphEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, UVoxelGraphGenerator* Generator) override + { + TSharedRef NewVoxelEditor(new FVoxelGraphEditorToolkit()); + NewVoxelEditor->InitVoxelEditor(Mode, InitToolkitHost, Generator); + return NewVoxelEditor; + } + +private: + TSharedPtr StyleSet; +}; + +IMPLEMENT_MODULE(FVoxelGraphEditorModule, VoxelGraphEditor); + +#undef IMAGE_PLUGIN_BRUSH \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp new file mode 100644 index 00000000..7f01a250 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp @@ -0,0 +1,1737 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditorToolkit.h" +#include "VoxelGraphEditorUtilities.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphShortcuts.h" +#include "VoxelGraphCompileToCpp.h" +#include "VoxelGraphPreviewSettings.h" +#include "VoxelGraphSchema.h" +#include "Runtime/VoxelCompiledGraphs.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelNodes/VoxelLocalVariables.h" +#include "VoxelGraphEditorCommands.h" +#include "VoxelGraphNodes/VoxelGraphNode_Root.h" +#include "VoxelGraphNodes/VoxelGraphNode.h" +#include "VoxelGraphNodes/SVoxelGraphNode.h" +#include "VoxelGraphNodes/VoxelGraphNode_Knot.h" +#include "VoxelMessages.h" + +#include "VoxelDebugGraphUtils.h" +#include "VoxelEditorModule.h" + +#include "SVoxelPalette.h" +#include "Preview/SVoxelGraphPreview.h" +#include "Preview/SVoxelGraphPreviewViewport.h" +#include "Preview/VoxelGraphPreview.h" + +#include "IDetailsView.h" +#include "PropertyEditorModule.h" +#include "Modules/ModuleManager.h" +#include "ScopedTransaction.h" +#include "Misc/MessageDialog.h" +#include "HAL/PlatformApplicationMisc.h" + +#include "Editor.h" +#include "EditorStyleSet.h" +#include "GraphEditorActions.h" +#include "GraphEditor.h" +#include "Kismet2/BlueprintEditorUtils.h" + +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Commands/GenericCommands.h" +#include "Widgets/Docking/SDockTab.h" +#include "Widgets/Layout/SScaleBox.h" + +#include "EdGraph/EdGraph.h" +#include "EdGraphUtilities.h" +#include "AdvancedPreviewScene.h" + +#include "MessageLogModule.h" +#include "IMessageLogListing.h" +#include "Logging/TokenizedMessage.h" + +const FName FVoxelGraphEditorToolkit::GraphCanvasTabId(TEXT("VoxelGraphEditor_GraphCanvas")); +const FName FVoxelGraphEditorToolkit::DebugGraphCanvasTabId(TEXT("VoxelGraphEditor_DebugGraphCanvas")); +const FName FVoxelGraphEditorToolkit::PropertiesTabId(TEXT("VoxelGraphEditor_Properties")); +const FName FVoxelGraphEditorToolkit::ShortcutsTabId(TEXT("VoxelGraphEditor_Shortcuts")); +const FName FVoxelGraphEditorToolkit::PreviewSettingsTabId(TEXT("VoxelGraphEditor_PreviewSettings")); +const FName FVoxelGraphEditorToolkit::PaletteTabId(TEXT("VoxelGraphEditor_Palette")); +const FName FVoxelGraphEditorToolkit::PreviewTabId(TEXT("VoxelGraphEditor_Preview")); +const FName FVoxelGraphEditorToolkit::PreviewViewportTabId(TEXT("VoxelGraphEditor_PreviewViewport")); +const FName FVoxelGraphEditorToolkit::MessagesTabId(TEXT("VoxelGraphEditor_Messages")); + +FVoxelGraphEditorToolkit::FVoxelGraphEditorToolkit() +{ + +} + +FVoxelGraphEditorToolkit::~FVoxelGraphEditorToolkit() +{ + GEditor->UnregisterForUndo(this); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::RegisterTabSpawners(const TSharedRef& InTabManager) +{ + WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(VOXEL_LOCTEXT("Voxel Editor")); + auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); + + FAssetEditorToolkit::RegisterTabSpawners(InTabManager); + + InTabManager->RegisterTabSpawner(GraphCanvasTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_GraphCanvas)) + .SetDisplayName(VOXEL_LOCTEXT("Main Graph")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x")); + + InTabManager->RegisterTabSpawner(DebugGraphCanvasTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_DebugGraphCanvas)) + .SetDisplayName(VOXEL_LOCTEXT("Debug Graph")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "GraphEditor.EventGraph_16x")); + + InTabManager->RegisterTabSpawner(PropertiesTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Properties)) + .SetDisplayName(VOXEL_LOCTEXT("Details")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(ShortcutsTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Shortcuts)) + .SetDisplayName(VOXEL_LOCTEXT("Shortcuts")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(PreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_PreviewSettings)) + .SetDisplayName(VOXEL_LOCTEXT("Preview Settings")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details")); + + InTabManager->RegisterTabSpawner(PaletteTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Palette)) + .SetDisplayName(VOXEL_LOCTEXT("Palette")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "Kismet.Tabs.Palette")); + + InTabManager->RegisterTabSpawner(PreviewTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Preview)) + .SetDisplayName(VOXEL_LOCTEXT("Preview")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports")); + + InTabManager->RegisterTabSpawner(PreviewViewportTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_PreviewViewport)) + .SetDisplayName(VOXEL_LOCTEXT("3D Preview")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports")); + + InTabManager->RegisterTabSpawner(MessagesTabId, FOnSpawnTab::CreateSP(this, &FVoxelGraphEditorToolkit::SpawnTab_Messages)) + .SetDisplayName(VOXEL_LOCTEXT("Messages")) + .SetGroup(WorkspaceMenuCategoryRef) + .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "MessageLog.TabIcon")); +} + +void FVoxelGraphEditorToolkit::UnregisterTabSpawners(const TSharedRef& InTabManager) +{ + FAssetEditorToolkit::UnregisterTabSpawners(InTabManager); + + InTabManager->UnregisterTabSpawner(GraphCanvasTabId); + InTabManager->UnregisterTabSpawner(DebugGraphCanvasTabId); + InTabManager->UnregisterTabSpawner(PropertiesTabId); + InTabManager->UnregisterTabSpawner(ShortcutsTabId); + InTabManager->UnregisterTabSpawner(PreviewSettingsTabId); + InTabManager->UnregisterTabSpawner(PaletteTabId); + InTabManager->UnregisterTabSpawner(PreviewTabId); + InTabManager->UnregisterTabSpawner(PreviewViewportTabId); + InTabManager->UnregisterTabSpawner(MessagesTabId); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::InitVoxelEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit) +{ + + Generator = CastChecked(ObjectToEdit); + + if (!ensureAlways(Generator->VoxelGraph && Generator->VoxelDebugGraph)) + { + FAssetEditorToolkit::InitAssetEditor( + Mode, + InitToolkitHost, + TEXT("VoxelGraphEditorApp"), + FTabManager::NewLayout("Standalone_VoxelGraphEditor_Crash")->AddArea(FTabManager::NewPrimaryArea()), + false, + false, + ObjectToEdit, + false); + return; + } + + // Support undo/redo + Generator->SetFlags(RF_Transactional); + + GEditor->RegisterForUndo(this); + + FGraphEditorCommands::Register(); + FVoxelGraphEditorCommands::Register(); + + BindGraphCommands(); + + CreateInternalWidgets(); + + const TSharedRef StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_VoxelGraphEditor_Layout_v8") + ->AddArea + ( + FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Vertical) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.1f) + ->SetHideTabWell( true ) + ->AddTab(GetToolbarTabId(), ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation(Orient_Horizontal) ->SetSizeCoefficient(0.9f) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical) ->SetSizeCoefficient(0.2f) + ->Split + ( + FTabManager::NewStack() + ->AddTab( PaletteTabId, ETabState::ClosedTab ) + ->AddTab( PreviewSettingsTabId, ETabState::OpenedTab) + ) + ->Split + ( + FTabManager::NewStack() + ->AddTab( ShortcutsTabId, ETabState::OpenedTab ) + ->AddTab( PropertiesTabId, ETabState::OpenedTab ) + ) + ) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation( Orient_Vertical ) + ->SetSizeCoefficient(0.7f) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.8f) + ->SetHideTabWell( true ) + ->AddTab( GraphCanvasTabId, ETabState::OpenedTab ) + ->AddTab( DebugGraphCanvasTabId, ETabState::ClosedTab ) + ) + ->Split + ( + FTabManager::NewStack() + ->SetSizeCoefficient(0.2f) + ->AddTab( MessagesTabId, ETabState::OpenedTab ) + ) + ) + ->Split + ( + FTabManager::NewSplitter() ->SetOrientation(Orient_Vertical) + ->SetSizeCoefficient(0.3f) + ->Split + ( + FTabManager::NewStack() + ->SetHideTabWell( true ) + ->AddTab( PreviewTabId, ETabState::OpenedTab ) + ) + ->Split + ( + FTabManager::NewStack() + ->SetHideTabWell( true ) + ->AddTab( PreviewViewportTabId, ETabState::OpenedTab ) + ) + ) + ) + ); + + const bool bCreateDefaultStandaloneMenu = true; + const bool bCreateDefaultToolbar = true; + FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, TEXT("VoxelGraphEditorApp"), StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit, false); + + ExtendToolbar(); + ExtendMenu(); + RegenerateMenusAndToolbars(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::CreateInternalWidgets() +{ + VoxelGraphEditor = CreateGraphEditorWidget(false); + VoxelDebugGraphEditor = CreateGraphEditorWidget(true); + + FDetailsViewArgs Args; + Args.bHideSelectionTip = true; + Args.NotifyHook = this; + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + VoxelProperties = PropertyModule.CreateDetailView(Args); + VoxelProperties->SetObject(Generator); + + ShortcutsProperties = PropertyModule.CreateDetailView(Args); + ShortcutsProperties->SetObject(GetMutableDefault()); + + if (!Generator->PreviewSettings) + { + Generator->PreviewSettings = NewObject(Generator); + Generator->PreviewSettings->Graph = Generator; + } + + // Needed for undo/redo + Generator->PreviewSettings->SetFlags(RF_Transactional); + + PreviewSettings = PropertyModule.CreateDetailView(Args); + PreviewSettings->SetObject(Generator->PreviewSettings); + + // Must be created before PreviewViewport + PreviewScene = MakeShareable(new FAdvancedPreviewScene(FPreviewScene::ConstructionValues())); + + Palette = SNew(SVoxelPalette); + Preview = SNew(SVoxelGraphPreview).PreviewSettings(Generator->PreviewSettings); + PreviewViewport = SNew(SVoxelGraphPreviewViewport).VoxelGraphEditorToolkit(SharedThis(this)); + + PreviewHandler = MakeShared(Generator, Preview, PreviewViewport, PreviewScene); + + Preview->SetTexture(Generator->GetPreviewTexture()); + + // Messages panel + FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); + FMessageLogInitializationOptions LogOptions; + LogOptions.bShowPages = false; + LogOptions.bShowFilters = true; + LogOptions.bAllowClear = false; + LogOptions.MaxPageCount = 1; + + MessagesListing = MessageLogModule.CreateLogListing("VoxelGraphEditorErrors", LogOptions); + MessagesWidget = MessageLogModule.CreateLogListingWidget(MessagesListing.ToSharedRef()); +} + +void FVoxelGraphEditorToolkit::FillToolbar(FToolBarBuilder& ToolbarBuilder) +{ + ToolbarBuilder.BeginSection("Toolbar"); + + auto& Commands = FVoxelGraphEditorCommands::Get(); + ToolbarBuilder.AddToolBarButton(Commands.CompileToCpp); + ToolbarBuilder.AddSeparator(); + ToolbarBuilder.AddToolBarButton(Commands.ToggleAutomaticPreview); + ToolbarBuilder.AddToolBarButton(Commands.UpdatePreview); + ToolbarBuilder.AddSeparator(); + ToolbarBuilder.AddToolBarButton(Commands.ClearNodesMessages); + ToolbarBuilder.AddSeparator(); + ToolbarBuilder.AddToolBarButton(Commands.ShowAxisDependencies); + ToolbarBuilder.AddSeparator(); + ToolbarBuilder.AddToolBarButton(Commands.ShowStats); + ToolbarBuilder.AddToolBarButton(Commands.ShowValues); + + ToolbarBuilder.EndSection(); +} + +void FVoxelGraphEditorToolkit::ExtendToolbar() +{ + TSharedPtr ToolbarExtender = MakeShareable(new FExtender); + + ToolbarExtender->AddToolBarExtension( + "Asset", + EExtensionHook::After, + GetToolkitCommands(), + FToolBarExtensionDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::FillToolbar) + ); + + AddToolbarExtender(ToolbarExtender); +} + +void FVoxelGraphEditorToolkit::FillVoxelMenu(FMenuBuilder& MenuBuilder) +{ + auto& Commands = FVoxelGraphEditorCommands::Get(); + + MenuBuilder.AddMenuEntry(Commands.RecreateNodes); +} + +void FVoxelGraphEditorToolkit::AddEditorMenus(FMenuBarBuilder& MenuBarBuilder) +{ + MenuBarBuilder.AddPullDownMenu( + VOXEL_LOCTEXT("Voxel"), + VOXEL_LOCTEXT("Open the Voxel menu"), + FNewMenuDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::FillVoxelMenu), + "Voxel"); +} + +void FVoxelGraphEditorToolkit::ExtendMenu() +{ + TSharedPtr MenuExtender = MakeShareable(new FExtender); + + MenuExtender->AddMenuBarExtension( + "Edit", + EExtensionHook::After, + GetToolkitCommands(), + FMenuBarExtensionDelegate::CreateRaw(this, &FVoxelGraphEditorToolkit::AddEditorMenus)); + + AddMenuExtender(MenuExtender); +} + +void FVoxelGraphEditorToolkit::BindGraphCommands() +{ + auto& Commands = FVoxelGraphEditorCommands::Get(); + + ToolkitCommands->MapAction( + Commands.CompileToCpp, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CompileToCpp)); + + ToolkitCommands->MapAction( + Commands.RecreateNodes, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::RecreateNodes)); + + ToolkitCommands->MapAction( + Commands.ToggleAutomaticPreview, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ToggleAutomaticPreview), + FCanExecuteAction(), + FIsActionChecked::CreateSP(this, &FVoxelGraphEditorToolkit::IsToggleAutomaticPreviewChecked)); + + ToolkitCommands->MapAction( + Commands.UpdatePreview, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UpdatePreview, EVoxelGraphPreviewFlags::UpdateAll | EVoxelGraphPreviewFlags::ManualPreview)); + + ToolkitCommands->MapAction( + Commands.UpdateVoxelWorlds, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UpdateVoxelWorlds)); + + ToolkitCommands->MapAction( + Commands.ClearNodesMessages, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ClearNodesMessages)); + + ToolkitCommands->MapAction( + Commands.ShowAxisDependencies, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ShowAxisDependencies)); + + ToolkitCommands->MapAction( + Commands.ShowStats, + FExecuteAction::CreateWeakLambda(Generator, [=]() + { + Generator->PreviewSettings->bShowStats = !Generator->PreviewSettings->bShowStats; + UpdatePreview(EVoxelGraphPreviewFlags::UpdateTextures); + }), + FCanExecuteAction(), + FIsActionChecked::CreateWeakLambda(Generator, [=]() { return Generator->PreviewSettings->bShowStats; })); + + ToolkitCommands->MapAction( + Commands.ShowValues, + FExecuteAction::CreateWeakLambda(Generator, [=]() + { + Generator->PreviewSettings->bShowValues = !Generator->PreviewSettings->bShowValues; + UpdatePreview(EVoxelGraphPreviewFlags::UpdateTextures); + }), + FCanExecuteAction(), + FIsActionChecked::CreateWeakLambda(Generator, [=]() { return Generator->PreviewSettings->bShowValues; })); + + ToolkitCommands->MapAction( + FGenericCommands::Get().Undo, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::UndoGraphAction)); + + ToolkitCommands->MapAction( + FGenericCommands::Get().Redo, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::RedoGraphAction)); +} + +TSharedRef FVoxelGraphEditorToolkit::CreateGraphEditorWidget(bool bDebug) +{ + if (!GraphEditorCommands.IsValid()) + { + GraphEditorCommands = MakeShareable(new FUICommandList); + + GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().AddInput, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::AddInput), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanAddInput)); + + GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().DeleteInput, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DeleteInput), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDeleteInput)); + + GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().TogglePinPreview, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnTogglePinPreview)); + + GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().SplitPin, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSplitPin)); + GraphEditorCommands->MapAction(FVoxelGraphEditorCommands::Get().CombinePin, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnCombinePin)); + + // Graph Editor Commands + GraphEditorCommands->MapAction(FGraphEditorCommands::Get().CreateComment, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnCreateComment) + ); + + // Editing commands + GraphEditorCommands->MapAction(FGenericCommands::Get().SelectAll, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::SelectAllNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanSelectAllNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Delete, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DeleteSelectedNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDeleteNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Copy, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CopySelectedNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanCopyNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Cut, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CutSelectedNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanCutNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Paste, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::PasteNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanPasteNodes) + ); + + GraphEditorCommands->MapAction(FGenericCommands::Get().Duplicate, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::DuplicateNodes), + FCanExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::CanDuplicateNodes) + ); + + GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().SelectLocalVariableDeclaration, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectLocalVariableDeclaration) + ); + + GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().SelectLocalVariableUsages, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectLocalVariableUsages) + ); + + GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ConvertRerouteToVariables, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnConvertRerouteToVariables) + ); + + GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ConvertVariablesToReroute, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::OnConvertVariablesToReroute) + ); + + GraphEditorCommands->MapAction( FVoxelGraphEditorCommands::Get().ReconstructNode, + FExecuteAction::CreateSP(this, &FVoxelGraphEditorToolkit::ReconstructNode) + ); + + // Alignment Commands + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesTop, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignTop ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesMiddle, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignMiddle ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesBottom, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignBottom ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesLeft, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignLeft ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesCenter, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignCenter ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().AlignNodesRight, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnAlignRight ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().StraightenConnections, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnStraightenConnections ) + ); + + // Distribution Commands + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().DistributeNodesHorizontally, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnDistributeNodesH ) + ); + + GraphEditorCommands->MapAction( FGraphEditorCommands::Get().DistributeNodesVertically, + FExecuteAction::CreateSP( this, &FVoxelGraphEditorToolkit::OnDistributeNodesV ) + ); + } + + if (bDebug) + { + FGraphAppearanceInfo AppearanceInfo; + AppearanceInfo.CornerText = VOXEL_LOCTEXT("VOXEL DEBUG"); + + return SNew(SGraphEditor) + .IsEditable(true) + .Appearance(AppearanceInfo) + .GraphToEdit(Generator->VoxelDebugGraph) + .AutoExpandActionMenu(false) + .ShowGraphStateOverlay(false); + } + else + { + FGraphAppearanceInfo AppearanceInfo; + AppearanceInfo.CornerText = VOXEL_LOCTEXT("VOXEL"); + + SGraphEditor::FGraphEditorEvents InEvents; + InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FVoxelGraphEditorToolkit::OnSelectedNodesChanged); + InEvents.OnTextCommitted = FOnNodeTextCommitted::CreateSP(this, &FVoxelGraphEditorToolkit::OnNodeTitleCommitted); + InEvents.OnNodeDoubleClicked = FSingleNodeEvent::CreateSP(this, &FVoxelGraphEditorToolkit::OnNodeDoubleClicked); + InEvents.OnSpawnNodeByShortcut = SGraphEditor::FOnSpawnNodeByShortcut::CreateSP(this, &FVoxelGraphEditorToolkit::OnSpawnGraphNodeByShortcut); + + return SNew(SGraphEditor) + .AdditionalCommands(GraphEditorCommands) + .IsEditable(true) + .Appearance(AppearanceInfo) + .GraphToEdit(Generator->VoxelGraph) + .GraphEvents(InEvents) + .AutoExpandActionMenu(false) + .ShowGraphStateOverlay(false); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FVoxelGraphEditorToolkit::GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) +{ + return VoxelGraphEditor->GetBoundsForSelectedNodes(Rect, Padding); +} + +int32 FVoxelGraphEditorToolkit::GetNumberOfSelectedNodes() const +{ + return VoxelGraphEditor->GetSelectedNodes().Num(); +} + +FGraphPanelSelectionSet FVoxelGraphEditorToolkit::GetSelectedNodes() const +{ + return VoxelGraphEditor->GetSelectedNodes(); +} + +void FVoxelGraphEditorToolkit::SelectNodesAndZoomToFit(const TArray& Nodes) +{ + if (Nodes.Num() > 0) + { + VoxelGraphEditor->ClearSelectionSet(); + for (auto& Node : Nodes) + { + VoxelGraphEditor->SetNodeSelection(Node, true); + } + VoxelGraphEditor->ZoomToFit(true); + } +} + +void FVoxelGraphEditorToolkit::RefreshNodesMessages() +{ + for (auto* Node : Generator->VoxelGraph->Nodes) + { + if (Node->IsA() && !Node->IsA()) + { + TSharedPtr Widget = Node->DEPRECATED_NodeWidget.Pin(); + if (Widget.IsValid()) + { + static_cast(Widget.Get())->RefreshErrorInfo(); + } + } + } +} + +void FVoxelGraphEditorToolkit::TriggerUpdatePreview(EVoxelGraphPreviewFlags Flags) +{ + if (Generator->bAutomaticPreview || EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::ManualPreview)) + { + bUpdatePreviewOnNextTick = true; + NextPreviewFlags |= Flags; + } +} + +FAdvancedPreviewScene* FVoxelGraphEditorToolkit::GetPreviewScene() const +{ + return PreviewScene.Get(); +} + +void FVoxelGraphEditorToolkit::DebugNodes(const TSet& Nodes) +{ + FVoxelDebugGraphUtils::DebugNodes(Nodes, Generator); + VoxelDebugGraphEditor->ZoomToFit(false); +} + +inline EMessageSeverity::Type VoxelMessageTypeToMessageSeverity(EVoxelGraphNodeMessageType Type) +{ + switch (Type) + { + default: ensure(false); + case EVoxelGraphNodeMessageType::Info: + return EMessageSeverity::Info; + case EVoxelGraphNodeMessageType::Warning: + return EMessageSeverity::Warning; + case EVoxelGraphNodeMessageType::Error: + return EMessageSeverity::Error; + } +} + +void FVoxelGraphEditorToolkit::AddMessages(const TArray& Messages) +{ + CurrentMessages.Append(Messages); + + TArray> ListingMessages; + for (auto& Message : Messages) + { + TSharedRef ListingMessage = FTokenizedMessage::Create(VoxelMessageTypeToMessageSeverity(Message.Type)); + if (Message.Node.IsValid()) + { + ListingMessage->AddToken(FActionToken::Create( + Message.Node->GetTitle(), + Message.Node->GetTitle(), + FOnActionTokenExecuted::CreateSP( + this, + &FVoxelGraphEditorToolkit::SelectNodeAndZoomToFit, + Message.Node) + )); + } + ListingMessage->AddToken(FTextToken::Create(FText::FromString(Message.Message))); + ListingMessages.Add(ListingMessage); + } + MessagesListing->AddMessages(ListingMessages, false); +} + +void FVoxelGraphEditorToolkit::ClearMessages(bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) +{ + MessagesListing->ClearMessages(); + if (bClearAll) + { + CurrentMessages.Reset(); + } + else + { + TArray Copy = CurrentMessages; + Copy.RemoveAll([&](auto& Message) { return Message.Type == MessagesToClear; }); + CurrentMessages.Reset(); + + AddMessages(Copy); + } +} + +void FVoxelGraphEditorToolkit::SaveAsset_Execute() +{ + if (Generator->bCompileToCppOnSave) + { + FVoxelGraphCompileToCpp::Compile(Generator, true); + } + + // Make sure to save AFTER compile to cpp to avoid dirtying it again + FAssetEditorToolkit::SaveAsset_Execute(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::AddReferencedObjects(FReferenceCollector& Collector) +{ + Collector.AddReferencedObject(Generator); + if (PreviewHandler.IsValid()) + { + PreviewHandler->AddReferencedObjects(Collector); + } +} + +void FVoxelGraphEditorToolkit::PostUndo(bool bSuccess) +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->ClearSelectionSet(); + VoxelGraphEditor->NotifyGraphChanged(); + } + TriggerUpdatePreview(EVoxelGraphPreviewFlags::UpdateAll); +} + +void FVoxelGraphEditorToolkit::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged) +{ + if (VoxelGraphEditor.IsValid() && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + VoxelGraphEditor->NotifyGraphChanged(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::Tick(float DeltaTime) +{ + if (bUpdatePreviewOnNextTick) + { + UpdatePreview(NextPreviewFlags); + bUpdatePreviewOnNextTick = false; + NextPreviewFlags = EVoxelGraphPreviewFlags::None; + } +} + +TStatId FVoxelGraphEditorToolkit::GetStatId() const +{ + RETURN_QUICK_DECLARE_CYCLE_STAT(FVoxelGraphEditorToolkit, STATGROUP_Tickables); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_GraphCanvas(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == GraphCanvasTabId); + + auto Tab = SNew(SDockTab) + .Label(VOXEL_LOCTEXT("Main Graph")); + + GraphTab = Tab; + GraphTab->SetContent(VoxelGraphEditor.ToSharedRef()); + + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_DebugGraphCanvas(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == DebugGraphCanvasTabId); + + auto Tab = SNew(SDockTab) + .Label(VOXEL_LOCTEXT("Debug Graph")); + + DebugGraphTab = Tab; + DebugGraphTab->SetContent(VoxelDebugGraphEditor.ToSharedRef()); + + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_Properties(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PropertiesTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Details")) + [ + VoxelProperties.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_Shortcuts(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == ShortcutsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Shortcuts")) + [ + ShortcutsProperties.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_PreviewSettings(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PreviewSettingsTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Details")) + .Label(VOXEL_LOCTEXT("Preview Settings")) + [ + PreviewSettings.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_Palette(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PaletteTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("Kismet.Tabs.Palette")) + .Label(VOXEL_LOCTEXT("Palette")) + [ + Palette.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_Preview(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PreviewTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports")) + .Label(VOXEL_LOCTEXT("Preview")) + [ + // Do the scaling here to make math easier + SNew(SScaleBox) + .Stretch(EStretch::ScaleToFit) + [ + Preview.ToSharedRef() + ] + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_PreviewViewport(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == PreviewViewportTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("LevelEditor.Tabs.Viewports")) + .Label(VOXEL_LOCTEXT("3D Preview")) + [ + PreviewViewport.ToSharedRef() + ]; + return Tab; +} + +TSharedRef FVoxelGraphEditorToolkit::SpawnTab_Messages(const FSpawnTabArgs& Args) +{ + check(Args.GetTabId() == MessagesTabId); + + auto Tab = + SNew(SDockTab) + .Icon(FEditorStyle::GetBrush("MessageLog.TabIcon")) + .Label(VOXEL_LOCTEXT("Messages")) + [ + MessagesWidget.ToSharedRef() + ]; + return Tab; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::OnSelectedNodesChanged(const TSet& NewSelection) +{ + TArray Selection; + + if (NewSelection.Num()) + { + for (auto* Object : NewSelection) + { + if (Cast(Object) || Cast(Object)) + { + Selection.Add(Generator); + } + else if (UVoxelGraphNode* GraphNode = Cast(Object)) + { + Selection.Add(GraphNode->VoxelNode); + } + else + { + Selection.Add(Object); + } + } + } + else + { + Selection.Add(Generator); + } + + VoxelProperties->SetObjects(Selection); +} + +void FVoxelGraphEditorToolkit::OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged) +{ + if (NodeBeingChanged) + { + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Rename Node")); + NodeBeingChanged->Modify(); + NodeBeingChanged->OnRenameNode(NewText.ToString()); + } +} + +void FVoxelGraphEditorToolkit::OnNodeDoubleClicked(UEdGraphNode* Node) +{ + if (Node->CanJumpToDefinition()) + { + Node->JumpToDefinition(); + } +} + +FReply FVoxelGraphEditorToolkit::OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition) +{ + auto* Ptr = GetDefault()->Shortcuts.FindByPredicate([&](auto& Key) { return Key.IsSameAs(InChord); }); + UClass* ClassToSpawn = Ptr ? Ptr->Class : nullptr; + if (ClassToSpawn) + { + FVoxelGraphSchemaAction_NewNode Action(FText(), FText(), FText(), 0); + Action.VoxelNodeClass = ClassToSpawn; + Action.PerformAction(Generator->VoxelGraph, nullptr, InPosition); + } + + return FReply::Handled(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::AddInput() +{ + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + + // Iterator used but should only contain one node + for (auto* SelectedNode : SelectedNodes) + { + if (auto* Node = Cast(SelectedNode)) + { + Node->AddInputPin(); + break; + } + } +} + +bool FVoxelGraphEditorToolkit::CanAddInput() const +{ + return GetSelectedNodes().Num() == 1; +} + +void FVoxelGraphEditorToolkit::DeleteInput() +{ + UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu(); + UVoxelGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode()); + + if (SelectedNode && SelectedNode == SelectedPin->GetOwningNode()) + { + SelectedNode->RemoveInputPin(SelectedPin); + } +} + +bool FVoxelGraphEditorToolkit::CanDeleteInput() const +{ + return true; +} + +void FVoxelGraphEditorToolkit::OnCreateComment() +{ + FVoxelGraphSchemaAction_NewComment CommentAction; + CommentAction.PerformAction(Generator->VoxelGraph, NULL, VoxelGraphEditor->GetPasteLocation()); +} + +void FVoxelGraphEditorToolkit::OnTogglePinPreview() +{ + UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu(); + UVoxelGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode()); + UVoxelGraphNode* GraphNodeToPreview = Cast(SelectedNode); + if (GraphNodeToPreview && GraphNodeToPreview->VoxelNode) + { + const bool bIsPreviewing = SelectedPin->bIsDiffing; + + if (Generator->PreviewedPin.Get()) + { + ensure(!bIsPreviewing || SelectedPin == Generator->PreviewedPin.Get()); + ensure(Generator->PreviewedPin.Get()->bIsDiffing); + Generator->PreviewedPin.Get()->bIsDiffing = false; + Generator->PreviewedPin.SetPin(nullptr); + } + + ensure(!SelectedPin->bIsDiffing); + if (!bIsPreviewing) + { + SelectedPin->bIsDiffing = true; + Generator->PreviewedPin.SetPin(SelectedPin); + } + + VoxelGraphEditor->NotifyGraphChanged(); + } + UpdatePreview(EVoxelGraphPreviewFlags::UpdateAll | EVoxelGraphPreviewFlags::ManualPreview); +} + +void FVoxelGraphEditorToolkit::OnSplitPin() +{ + UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu(); + UVoxelGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode()); + SelectedNode->TrySplitPin(*SelectedPin, false); +} + +void FVoxelGraphEditorToolkit::OnCombinePin() +{ + UEdGraphPin* SelectedPin = VoxelGraphEditor->GetGraphPinForMenu(); + UVoxelGraphNode* SelectedNode = Cast(SelectedPin->GetOwningNode()); + SelectedNode->TryCombinePin(*SelectedPin, false); +} + +void FVoxelGraphEditorToolkit::SelectAllNodes() +{ + VoxelGraphEditor->SelectAllNodes(); +} + +void FVoxelGraphEditorToolkit::DeleteSelectedNodes() +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Delete Selected Voxel Node")); + + VoxelGraphEditor->GetCurrentGraph()->Modify(); + + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + + VoxelGraphEditor->ClearSelectionSet(); + + for (auto* Object : SelectedNodes) + { + UEdGraphNode* Node = CastChecked(Object); + + if (Node->CanUserDeleteNode()) + { + if (UVoxelGraphNode* VoxelGraphNode = Cast(Node)) + { + UVoxelNode* VoxelNode = VoxelGraphNode->VoxelNode; + if (VoxelNode) + { + VoxelNode->Modify(); + VoxelNode->MarkPendingKill(); + } + + auto* PreviewedPin = Generator->PreviewedPin.Get(); + if (PreviewedPin && PreviewedPin->GetOwningNode() == VoxelGraphNode) + { + // Clear previewed pin if we delete the owning node + Generator->PreviewedPin = {}; + // Clear since we're not previewing it anymore + PreviewedPin->bIsDiffing = false; + } + + FBlueprintEditorUtils::RemoveNode(NULL, VoxelGraphNode, true); + + // Make sure Voxel is updated to match graph + Generator->CompileVoxelNodesFromGraphNodes(); + + // Remove this node from the list of all VoxelNodes + Generator->AllNodes.Remove(VoxelNode); + Generator->MarkPackageDirty(); + } + else + { + FBlueprintEditorUtils::RemoveNode(NULL, Node, true); + } + } + } +} + +bool FVoxelGraphEditorToolkit::CanDeleteNodes() const +{ + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + + if (SelectedNodes.Num() == 1) + { + for (auto* Node : SelectedNodes) + { + UVoxelGraphNode* GraphNode = Cast(Node); + if (GraphNode && !GraphNode->CanUserDeleteNode()) + { + return false; + } + } + } + + return SelectedNodes.Num() > 0; +} + +void FVoxelGraphEditorToolkit::DeleteSelectedDuplicatableNodes() +{ + // Cache off the old selection + const FGraphPanelSelectionSet OldSelectedNodes = GetSelectedNodes(); + + // Clear the selection and only select the nodes that can be duplicated + FGraphPanelSelectionSet RemainingNodes; + VoxelGraphEditor->ClearSelectionSet(); + + for (auto* SelectedNode : OldSelectedNodes) + { + UEdGraphNode* Node = Cast(SelectedNode); + if (Node && Node->CanDuplicateNode()) + { + VoxelGraphEditor->SetNodeSelection(Node, true); + } + else + { + RemainingNodes.Add(Node); + } + } + + // Delete the duplicable nodes + DeleteSelectedNodes(); + + // Reselect whatever's left from the original selection after the deletion + VoxelGraphEditor->ClearSelectionSet(); + + for (auto* RemainingNode : RemainingNodes) + { + if (UEdGraphNode* Node = Cast(RemainingNode)) + { + VoxelGraphEditor->SetNodeSelection(Node, true); + } + } +} + +void FVoxelGraphEditorToolkit::CutSelectedNodes() +{ + CopySelectedNodes(); + // Cut should only delete nodes that can be duplicated + DeleteSelectedDuplicatableNodes(); +} + +bool FVoxelGraphEditorToolkit::CanCutNodes() const +{ + return CanCopyNodes() && CanDeleteNodes(); +} + +void FVoxelGraphEditorToolkit::CopySelectedNodes() +{ + // Export the selected nodes and place the text on the clipboard + FGraphPanelSelectionSet SelectedNodes; + { + FGraphPanelSelectionSet AllSelectedNodes = GetSelectedNodes(); + for (auto* SelectedNode : AllSelectedNodes) + { + auto* Node = Cast(SelectedNode); + if (Node && Node->CanDuplicateNode()) + { + SelectedNodes.Add(Node); + } + } + } + + FString ExportedText; + + for (auto It = SelectedNodes.CreateIterator(); It; ++It) + { + CastChecked(*It)->PrepareForCopying(); + } + + FEdGraphUtilities::ExportNodesToText(SelectedNodes, /*out*/ ExportedText); + FPlatformApplicationMisc::ClipboardCopy(*ExportedText); + + // Make sure the voxel graph remains the owner of the copied nodes + for (auto It = SelectedNodes.CreateIterator(); It; ++It) + { + if (auto* Node = Cast(*It)) + { + Node->PostCopyNode(); + } + } +} + +bool FVoxelGraphEditorToolkit::CanCopyNodes() const +{ + // If any of the nodes can be duplicated then we should allow copying + const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); + for (auto* SelectedNode : SelectedNodes) + { + UEdGraphNode* Node = Cast(SelectedNode); + if (Node && Node->CanDuplicateNode()) + { + return true; + } + } + return false; +} + +void FVoxelGraphEditorToolkit::PasteNodes() +{ + PasteNodesHere(VoxelGraphEditor->GetPasteLocation()); +} + +void FVoxelGraphEditorToolkit::PasteNodesHere(const FVector2D& Location) +{ + // Undo/Redo support + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Paste Voxel Node")); + Generator->VoxelGraph->Modify(); + Generator->Modify(); + + // Clear the selection set (newly pasted stuff will be selected) + VoxelGraphEditor->ClearSelectionSet(); + + // Grab the text to paste from the clipboard. + FString TextToImport; + FPlatformApplicationMisc::ClipboardPaste(TextToImport); + + // Import the nodes + TSet PastedNodes; + FEdGraphUtilities::ImportNodesFromText(Generator->VoxelGraph, TextToImport, /*out*/ PastedNodes); + + //Average position of nodes so we can move them while still maintaining relative distances to each other + FVector2D AvgNodePosition(0.0f, 0.0f); + + for (auto* Node : PastedNodes) + { + AvgNodePosition.X += Node->NodePosX; + AvgNodePosition.Y += Node->NodePosY; + } + + if (PastedNodes.Num() > 0) + { + float InvNumNodes = 1.0f / float(PastedNodes.Num()); + AvgNodePosition.X *= InvNumNodes; + AvgNodePosition.Y *= InvNumNodes; + } + + TArray PastedVoxelNodes; + for (auto* Node : PastedNodes) + { + if (UVoxelGraphNode* VoxelGraphNode = Cast(Node)) + { + if (auto* VoxelNode = VoxelGraphNode->VoxelNode) + { + PastedVoxelNodes.Add(VoxelNode); + Generator->AllNodes.Add(VoxelNode); + VoxelNode->Graph = Generator; + } + } + + // Select the newly pasted stuff + VoxelGraphEditor->SetNodeSelection(Node, true); + + Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X; + Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y; + + Node->SnapToGrid(SNodePanel::GetSnapGridSize()); + + // Give new node a different Guid from the old one + Node->CreateNewGuid(); + } + + // Force new pasted VoxelNodes to have same connections as graph nodes + Generator->CompileVoxelNodesFromGraphNodes(); + + // Post copy for local variables + for (auto* Node : PastedVoxelNodes) + { + Node->PostCopyNode(PastedVoxelNodes); + } + + // Update UI + VoxelGraphEditor->NotifyGraphChanged(); + + Generator->PostEditChange(); + Generator->MarkPackageDirty(); +} + +bool FVoxelGraphEditorToolkit::CanPasteNodes() const +{ + FString ClipboardContent; + FPlatformApplicationMisc::ClipboardPaste(ClipboardContent); + + return FEdGraphUtilities::CanImportNodesFromText(Generator->VoxelGraph, ClipboardContent); +} + +void FVoxelGraphEditorToolkit::DuplicateNodes() +{ + // Copy and paste current selection + CopySelectedNodes(); + PasteNodes(); +} + +bool FVoxelGraphEditorToolkit::CanDuplicateNodes() const +{ + return CanCopyNodes(); +} + +void FVoxelGraphEditorToolkit::OnAlignTop() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignTop(); + } +} + +void FVoxelGraphEditorToolkit::OnAlignMiddle() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignMiddle(); + } +} + +void FVoxelGraphEditorToolkit::OnAlignBottom() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignBottom(); + } +} + +void FVoxelGraphEditorToolkit::OnAlignLeft() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignLeft(); + } +} + +void FVoxelGraphEditorToolkit::OnAlignCenter() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignCenter(); + } +} + +void FVoxelGraphEditorToolkit::OnAlignRight() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnAlignRight(); + } +} + +void FVoxelGraphEditorToolkit::OnStraightenConnections() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnStraightenConnections(); + } +} + +void FVoxelGraphEditorToolkit::OnDistributeNodesH() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnDistributeNodesH(); + } +} + +void FVoxelGraphEditorToolkit::OnDistributeNodesV() +{ + if (VoxelGraphEditor.IsValid()) + { + VoxelGraphEditor->OnDistributeNodesV(); + } +} + +void FVoxelGraphEditorToolkit::OnSelectLocalVariableDeclaration() +{ + const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes(); + if (SelectedNodes.Num() == 1) + { + VoxelGraphEditor->ClearSelectionSet(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UVoxelGraphNode* GraphNode = Cast(*NodeIt); + if (GraphNode) + { + UVoxelNode* CurrentSelectedNoe = GraphNode->VoxelNode; + UVoxelLocalVariableUsage* Usage = Cast(CurrentSelectedNoe); + if (Usage && Usage->Declaration) + { + UEdGraphNode* DeclarationGraphNode = Usage->Declaration->GraphNode; + if (DeclarationGraphNode) + { + VoxelGraphEditor->SetNodeSelection(DeclarationGraphNode, true); + } + } + } + } + VoxelGraphEditor->ZoomToFit(true); + } +} + +void FVoxelGraphEditorToolkit::OnSelectLocalVariableUsages() +{ + const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes(); + if (SelectedNodes.Num() == 1) + { + bool bZoom = false; + VoxelGraphEditor->ClearSelectionSet(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UVoxelGraphNode* GraphNode = Cast(*NodeIt); + if (GraphNode) + { + UVoxelNode* CurrentSelectedNode = GraphNode->VoxelNode; + UVoxelLocalVariableDeclaration* Declaration = Cast(CurrentSelectedNode); + for (UVoxelNode* Node : Generator->AllNodes) + { + auto* Usage = Cast(Node); + if (Usage && Usage->Declaration == Declaration) + { + UEdGraphNode* UsageGraphNode = Usage->GraphNode; + if (UsageGraphNode) + { + bZoom = true; + VoxelGraphEditor->SetNodeSelection(UsageGraphNode, true); + } + } + } + } + } + if (bZoom) + { + VoxelGraphEditor->ZoomToFit(true); + } + } +} + +void FVoxelGraphEditorToolkit::OnConvertRerouteToVariables() +{ + const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes(); + if (SelectedNodes.Num() == 1) + { + VoxelGraphEditor->ClearSelectionSet(); + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UVoxelGraphNode_Knot* GraphNode = Cast(*NodeIt); + if (GraphNode) + { + UEdGraph* Graph = GraphNode->GetGraph(); + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Convert reroute to local variables")); + Graph->Modify(); + + const TArray& InputPins = GraphNode->GetInputPin()->LinkedTo; + TArray OutputPins = GraphNode->GetOutputPin()->LinkedTo; + OutputPins.Sort([](UEdGraphPin& A, UEdGraphPin& B) { return A.GetOwningNode()->NodePosY < B.GetOwningNode()->NodePosY; }); + + TArray Usages; + int UsageIndex = -OutputPins.Num() / 2; + for (auto* OutputPin : OutputPins) + { + auto* Usage = Generator->ConstructNewNode(FVector2D(GraphNode->NodePosX + 50, GraphNode->NodePosY + 50 * UsageIndex)); + Usages.Add(Usage); + UsageIndex++; + } + + // Spawn declaration AFTER usages so that it gets renamed + auto* Declaration = Generator->ConstructNewNode(FVector2D(GraphNode->NodePosX - 50, GraphNode->NodePosY)); + Declaration->SetCategory(FVoxelPinCategory::FromString(GraphNode->GetInputPin()->PinType.PinCategory)); + Declaration->GraphNode->ReconstructNode(); + + check(Declaration->GraphNode->Pins.Num() == 1); + UEdGraphPin* DeclarationInputPin = Declaration->GraphNode->Pins[0]; + check(DeclarationInputPin->Direction == EEdGraphPinDirection::EGPD_Input) + for (auto* InputPin : InputPins) + { + InputPin->MakeLinkTo(DeclarationInputPin); + } + + for (int32 Index = 0; Index < OutputPins.Num() ; Index++) + { + auto* Usage = Usages[Index]; + Usage->Declaration = Declaration; + Usage->DeclarationGuid = Declaration->VariableGuid; + Usage->GraphNode->ReconstructNode(); + Usage->GraphNode->GetAllPins()[0]->MakeLinkTo(OutputPins[Index]); // usage node has a single pin + } + + GraphNode->DestroyNode(); + } + } + } +} + +void FVoxelGraphEditorToolkit::OnConvertVariablesToReroute() +{ + const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes(); + if (SelectedNodes.Num() == 1) + { + for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt) + { + UVoxelGraphNode* GraphNode = Cast(*NodeIt); + if (GraphNode) + { + UEdGraph* Graph = GraphNode->GetGraph(); + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Convert local variables to reroute")); + Graph->Modify(); + + UVoxelNode* CurrentSelectedNode = GraphNode->VoxelNode; + UVoxelLocalVariableDeclaration* Declaration = Cast(CurrentSelectedNode); + if (!Declaration) + { + UVoxelLocalVariableUsage* Usage = Cast(CurrentSelectedNode); + if (Usage) + { + Declaration = Usage->Declaration; + } + } + if (!Declaration) + { + return; + } + UEdGraphNode* DeclarationGraphNode = Declaration->GraphNode; + + FGraphNodeCreator KnotNodeCreator(*Graph); + UVoxelGraphNode_Knot* KnotNode = KnotNodeCreator.CreateNode(); + KnotNodeCreator.Finalize(); + + KnotNode->NodePosX = DeclarationGraphNode->NodePosX + 50; + KnotNode->NodePosY = DeclarationGraphNode->NodePosY; + + for (UEdGraphPin* Pin : DeclarationGraphNode->GetAllPins()) + { + if (Pin->Direction == EEdGraphPinDirection::EGPD_Input) + { + for (UEdGraphPin* InputPin : Pin->LinkedTo) + { + KnotNode->GetInputPin()->MakeLinkTo(InputPin); + } + } + if (Pin->Direction == EEdGraphPinDirection::EGPD_Output) + { + for (UEdGraphPin* OutputPin : Pin->LinkedTo) + { + KnotNode->GetOutputPin()->MakeLinkTo(OutputPin); + } + } + } + DeclarationGraphNode->DestroyNode(); + + for(UVoxelNode* Node : Generator->AllNodes) + { + auto* Usage = Cast(Node); + if (Usage && Usage->Declaration == Declaration) + { + UEdGraphNode* UsageGraphNode = Usage->GraphNode; + if (UsageGraphNode) + { + UEdGraphPin* Pin = Usage->GraphNode->GetAllPins()[0]; // usage node has a single pin + for (UEdGraphPin* OutputPin : Pin->LinkedTo) + { + KnotNode->GetOutputPin()->MakeLinkTo(OutputPin); + } + UsageGraphNode->DestroyNode(); + } + } + } + KnotNode->PropagatePinType(); + } + } + } +} + +void FVoxelGraphEditorToolkit::ReconstructNode() +{ + const FGraphPanelSelectionSet SelectedNodes = VoxelGraphEditor->GetSelectedNodes(); + for(auto& Object : SelectedNodes) + { + if (auto* Node = Cast(Object)) + { + Node->ReconstructNode(); + } + } + Generator->CompileVoxelNodesFromGraphNodes(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::RecreateNodes() +{ + for (int32 I = 0; I < 4; I++) // Hack to make sure they are really recreated + { + TArray AllNodes; + VoxelGraphEditor->GetCurrentGraph()->GetNodesOfClass(AllNodes); + + for (auto* Node : AllNodes) + { + Node->ReconstructNode(); + } + + Generator->CompileVoxelNodesFromGraphNodes(); + + GraphTab->ClearContent(); + VoxelGraphEditor = CreateGraphEditorWidget(false); + GraphTab->SetContent(VoxelGraphEditor.ToSharedRef()); + } + ClearNodesMessages(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::CompileToCpp() +{ + FVoxelGraphCompileToCpp::Compile(Generator, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::ToggleAutomaticPreview() +{ + Generator->Modify(); + Generator->bAutomaticPreview = !Generator->bAutomaticPreview; +} + +bool FVoxelGraphEditorToolkit::IsToggleAutomaticPreviewChecked() const +{ + return Generator->bAutomaticPreview; +} + +void FVoxelGraphEditorToolkit::UpdatePreview(EVoxelGraphPreviewFlags Flags) +{ + PreviewHandler->Update(Flags); + +} + +void FVoxelGraphEditorToolkit::UpdateVoxelWorlds() +{ + IVoxelEditorModule* VoxelEditorModule = &FModuleManager::LoadModuleChecked("VoxelEditor"); + VoxelEditorModule->RefreshVoxelWorlds(Generator); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::ClearNodesMessages() +{ + FVoxelGraphErrorReporter::ClearNodesMessages(Generator); + ClearMessages(true, EVoxelGraphNodeMessageType::Info); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::ShowAxisDependencies() +{ + FVoxelGraphErrorReporter::ClearNodesMessages(Generator); + + FVoxelCompiledGraphs Graphs; + Generator->CreateGraphs(Graphs, false, false, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::UndoGraphAction() +{ + GEditor->UndoTransaction(); +} + +void FVoxelGraphEditorToolkit::RedoGraphAction() +{ + // Clear selection, to avoid holding refs to nodes that go away + VoxelGraphEditor->ClearSelectionSet(); + + GEditor->RedoTransaction(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::SelectNodeAndZoomToFit(TWeakObjectPtr Node) +{ + if (Node.IsValid() && Node->Graph) + { + if (Node->Graph == Generator) + { + SelectNodesAndZoomToFit({ Node->GraphNode }); + } + else + { +#if ENGINE_MINOR_VERSION < 24 + if (ensure(FAssetEditorManager::Get().OpenEditorForAsset(Node->Graph))) +#else + if (ensure(GEditor->GetEditorSubsystem()->OpenEditorForAsset(Node->Graph))) +#endif + { + auto NewEditor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Node->Graph->VoxelGraph); + if (ensure(NewEditor.IsValid())) + { + NewEditor->SelectNodesAndZoomToFit({ Node->GraphNode }); + } + } + } + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h new file mode 100644 index 00000000..07b12bad --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h @@ -0,0 +1,261 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/GCObject.h" +#include "Misc/NotifyHook.h" +#include "EditorUndoClient.h" +#include "TickableEditorObject.h" +#include "VoxelGraphErrorReporter.h" +#include "IVoxelGraphEditorToolkit.h" +#include "VoxelGraphEditor.h" +#include "VoxelMinimal.h" + +class IDetailsView; +class SVoxelGraphPreview; +class FVoxelGraphPreview; +class SVoxelPalette; +class SVoxelGraphPreviewViewport; +class SGraphEditor; +class SWidget; +class IMessageLogListing; + +class FVoxelGraphEditorToolkit : public IVoxelGraphEditorToolkit, public FGCObject, public FNotifyHook, public FEditorUndoClient, public FTickableEditorObject +{ +public: + FVoxelGraphEditorToolkit(); + virtual ~FVoxelGraphEditorToolkit(); + + virtual void RegisterTabSpawners(const TSharedRef& TabManager) override; + virtual void UnregisterTabSpawners(const TSharedRef& TabManager) override; + + void InitVoxelEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UObject* ObjectToEdit); + +private: + // Creates all internal widgets for the tabs to point at + void CreateInternalWidgets(); + + // Add the toolbar buttons + void FillToolbar(FToolBarBuilder& ToolbarBuilder); + // Builds the toolbar. Calls FillToolbar + void ExtendToolbar(); + + // Fill the voxel menu dropdown + void FillVoxelMenu(FMenuBuilder& MenuBuilder); + // Adds additional dropdowns. Calls FillMenu + void AddEditorMenus(FMenuBarBuilder& MenuBarBuilder); + // Builds the menu. Calls AddEditorMenus + void ExtendMenu(); + + // Binds new graph commands to delegates + void BindGraphCommands(); + + // Create new graph editor widget + TSharedRef CreateGraphEditorWidget(bool bDebug); + +public: + //~ Begin IVoxelGraphEditorToolkit interface + virtual bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) override; + virtual int32 GetNumberOfSelectedNodes() const override; + virtual TSet GetSelectedNodes() const override; + virtual void SelectNodesAndZoomToFit(const TArray& Nodes) override; + virtual void RefreshNodesMessages() override; + void TriggerUpdatePreview(EVoxelGraphPreviewFlags Flags) override; + virtual FAdvancedPreviewScene* GetPreviewScene() const override; + virtual void DebugNodes(const TSet& Nodes) override; + virtual void AddMessages(const TArray& Messages) override; + virtual void ClearMessages(bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) override; + //~ End IVoxelGraphEditorToolkit interface + + //~ Begin IToolkit interface + virtual FName GetToolkitFName() const override { return "VoxelGraphEditor"; } + virtual FText GetBaseToolkitName() const override { return VOXEL_LOCTEXT("Voxel Graph Editor"); } + virtual FString GetWorldCentricTabPrefix() const override { return "VoxelGraphEditor"; } + virtual FLinearColor GetWorldCentricTabColorScale() const override { return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f); } + virtual void SaveAsset_Execute() override; + //~ End IToolkit interface + + //~ Begin FGCObject interface + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + //~ End FGCObject interface + + //~ Begin FEditorUndoClient Interface + virtual void PostUndo(bool bSuccess) override; + virtual void PostRedo(bool bSuccess) override { PostUndo(bSuccess); } + //~ End FEditorUndoClient Interface + + //~ Begin FNotifyHook Interface + virtual void NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged) override; + //~ End FNotifyHook Interface + + //~ Begin FTickableGameObject Interface + virtual void Tick(float DeltaTime) override; + virtual ETickableTickType GetTickableTickType() const override { return ETickableTickType::Always; } + virtual TStatId GetStatId() const override; + //~ End FTickableGameObject Interface + +private: + TSharedRef SpawnTab_GraphCanvas(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_DebugGraphCanvas(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Properties(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Shortcuts(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_PreviewSettings(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Palette(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Preview(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_PreviewViewport(const FSpawnTabArgs& Args); + TSharedRef SpawnTab_Messages(const FSpawnTabArgs& Args); + +private: + /** + * Graph events + */ + + void OnSelectedNodesChanged(const TSet& NewSelection); + + // Called when a node's title is committed for a rename + void OnNodeTitleCommitted(const FText& NewText, ETextCommit::Type CommitInfo, UEdGraphNode* NodeBeingChanged); + + void OnNodeDoubleClicked(UEdGraphNode* Node); + + FReply OnSpawnGraphNodeByShortcut(FInputChord InChord, const FVector2D& InPosition); + +public: + /** + * Graph bindings + */ + + void AddInput(); + bool CanAddInput() const; + + void DeleteInput(); + bool CanDeleteInput() const; + + void OnCreateComment(); + + void OnTogglePinPreview(); + + void OnSplitPin(); + void OnCombinePin(); + + void SelectAllNodes(); + bool CanSelectAllNodes() const { return true; } + + void DeleteSelectedNodes(); + bool CanDeleteNodes() const; + void DeleteSelectedDuplicatableNodes(); // For cut + + void CutSelectedNodes(); + bool CanCutNodes() const; + + void CopySelectedNodes(); + bool CanCopyNodes() const; + + void PasteNodes(); + virtual void PasteNodesHere(const FVector2D& Location) override; + virtual bool CanPasteNodes() const override; + + void DuplicateNodes(); + bool CanDuplicateNodes() const; + + void OnAlignTop(); + void OnAlignMiddle(); + void OnAlignBottom(); + void OnAlignLeft(); + void OnAlignCenter(); + void OnAlignRight(); + + void OnStraightenConnections(); + + void OnDistributeNodesH(); + void OnDistributeNodesV(); + + void OnSelectLocalVariableDeclaration(); + void OnSelectLocalVariableUsages(); + void OnConvertRerouteToVariables(); + void OnConvertVariablesToReroute(); + + void ReconstructNode(); + +public: + /** + * Toolbar bindings + */ + + void RecreateNodes(); + + void CompileToCpp(); + + void ToggleAutomaticPreview(); + bool IsToggleAutomaticPreviewChecked() const; + void UpdatePreview(EVoxelGraphPreviewFlags Flags); + void UpdateVoxelWorlds(); + + void ClearNodesMessages(); + + void ShowAxisDependencies(); + + void UndoGraphAction(); + void RedoGraphAction(); + +public: + // Message list action + void SelectNodeAndZoomToFit(TWeakObjectPtr Node); + +private: + // The Voxel asset being inspected + UVoxelGraphGenerator* Generator = nullptr; + + // Command list for this editor + TSharedPtr GraphEditorCommands; + + bool bUpdatePreviewOnNextTick = false; + EVoxelGraphPreviewFlags NextPreviewFlags = EVoxelGraphPreviewFlags::None; + + TArray CurrentMessages; + +private: + /** + * Tabs + */ + + // Graphs tabs + TSharedPtr GraphTab; + TSharedPtr VoxelGraphEditor; + TSharedPtr DebugGraphTab; + TSharedPtr VoxelDebugGraphEditor; + + // Properties tabs + TSharedPtr VoxelProperties; + TSharedPtr ShortcutsProperties; + + // Preview settings tab + TSharedPtr PreviewSettings; + + // Palette of Voxel Node types + TSharedPtr Palette; + + // Preview tab + TSharedPtr Preview; + // 3D Preview + TSharedPtr PreviewViewport; + + // Preview handler + TSharedPtr PreviewHandler; + TSharedPtr PreviewScene; + + // Messages panel + TSharedPtr MessagesWidget; + TSharedPtr MessagesListing; + + // The tab ids for all the tabs used + static const FName GraphCanvasTabId; + static const FName DebugGraphCanvasTabId; + static const FName PropertiesTabId; + static const FName ShortcutsTabId; + static const FName PreviewSettingsTabId; + static const FName PaletteTabId; + static const FName PreviewTabId; + static const FName PreviewViewportTabId; + static const FName MessagesTabId; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.cpp new file mode 100644 index 00000000..21302801 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.cpp @@ -0,0 +1,78 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditorUtilities.h" +#include "IVoxelGraphEditorToolkit.h" +#include "VoxelGraphGenerator.h" +#include "VoxelEdGraph.h" + +#include "EdGraph/EdGraph.h" +#include "Toolkits/ToolkitManager.h" + +bool FVoxelGraphEditorUtilities::CanPasteNodes(const UEdGraph* Graph) +{ + bool bCanPaste = false; + TSharedPtr VoxelEditor = GetIVoxelEditorForGraph(Graph); + if (VoxelEditor.IsValid()) + { + bCanPaste = VoxelEditor->CanPasteNodes(); + } + return bCanPaste; +} + +void FVoxelGraphEditorUtilities::PasteNodesHere(UEdGraph* Graph, const FVector2D& Location) +{ + TSharedPtr VoxelEditor = GetIVoxelEditorForGraph(Graph); + if (VoxelEditor.IsValid()) + { + VoxelEditor->PasteNodesHere(Location); + } +} + +bool FVoxelGraphEditorUtilities::GetBoundsForSelectedNodes(const UEdGraph* Graph, FSlateRect& Rect, float Padding) +{ + TSharedPtr VoxelEditor = GetIVoxelEditorForGraph(Graph); + if (VoxelEditor.IsValid()) + { + return VoxelEditor->GetBoundsForSelectedNodes(Rect, Padding); + } + return false; +} + +int32 FVoxelGraphEditorUtilities::GetNumberOfSelectedNodes(const UEdGraph* Graph) +{ + TSharedPtr VoxelEditor = GetIVoxelEditorForGraph(Graph); + if (VoxelEditor.IsValid()) + { + return VoxelEditor->GetNumberOfSelectedNodes(); + } + return 0; +} + +TSet FVoxelGraphEditorUtilities::GetSelectedNodes(const UEdGraph* Graph) +{ + TSharedPtr VoxelEditor = GetIVoxelEditorForGraph(Graph); + if (VoxelEditor.IsValid()) + { + return VoxelEditor->GetSelectedNodes(); + } + return TSet(); +} + +TSharedPtr FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(const UObject* ObjectToFocusOn) +{ + if (!ensure(ObjectToFocusOn)) return {}; + + // Find the associated VoxelGraphGenerator + UVoxelGraphGenerator* VoxelGraphGenerator = Cast(ObjectToFocusOn)->GetGenerator(); + + TSharedPtr VoxelEditor; + if (VoxelGraphGenerator) + { + TSharedPtr FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(VoxelGraphGenerator); + if (FoundAssetEditor.IsValid()) + { + VoxelEditor = StaticCastSharedPtr(FoundAssetEditor); + } + } + return VoxelEditor; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.h new file mode 100644 index 00000000..91b6d2ad --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class IVoxelGraphEditorToolkit; +class UEdGraph; +class FSlateRect; + +namespace FVoxelGraphEditorUtilities +{ + /** Can we paste to this graph? */ + bool CanPasteNodes(const UEdGraph* Graph); + + /** Perform paste on graph, at location */ + void PasteNodesHere(UEdGraph* Graph, const FVector2D& Location); + + /** Get the bounding area for the currently selected nodes + * + * @param Graph The Graph we are finding bounds for + * @param Rect Final output bounding area, including padding + * @param Padding An amount of padding to add to all sides of the bounds + * + * @return false if nothing is selected*/ + bool GetBoundsForSelectedNodes(const UEdGraph* Graph, FSlateRect& Rect, float Padding = 0.0f); + + /** Gets the number of nodes that are currently selected */ + int32 GetNumberOfSelectedNodes(const UEdGraph* Graph); + + /** Get the currently selected set of nodes */ + TSet GetSelectedNodes(const UEdGraph* Graph); + + /** Get IVoxelEditor for given object, if it exists */ + TSharedPtr GetIVoxelEditorForGraph(const UObject* ObjectToFocusOn); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SGraphNodeDefaultVoxel.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SGraphNodeDefaultVoxel.h new file mode 100644 index 00000000..f57dcb1d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SGraphNodeDefaultVoxel.h @@ -0,0 +1,27 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "SGraphNode.h" + +class SGraphNodeDefaultVoxel : public SGraphNode +{ +public: + + SLATE_BEGIN_ARGS(SGraphNodeDefaultVoxel) + : _GraphNodeObj(static_cast(NULL)) + { + } + + SLATE_ARGUMENT(UEdGraphNode*, GraphNodeObj) + SLATE_END_ARGS() + + + void Construct(const FArguments& InArgs) + { + GraphNode = InArgs._GraphNodeObj; + SetCursor(EMouseCursor::CardinalCross); + UpdateGraphNode(); + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.cpp new file mode 100644 index 00000000..02c42521 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.cpp @@ -0,0 +1,758 @@ +// Copyright 2020 Phyronnaz + +#include "SVoxelGraphNode.h" +#include "VoxelGraphNode_Base.h" +#include "VoxelNodes/VoxelParameterNodes.h" +#include "VoxelNodes/VoxelAssetPickerNode.h" +#include "GraphEditorSettings.h" +#include "IDocumentation.h" +#include "TutorialMetaData.h" + +#include "Widgets/Images/SImage.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/SToolTip.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SSpacer.h" +#include "Widgets/Text/SInlineEditableTextBlock.h" +#include "Widgets/Colors/SColorBlock.h" +#include "Widgets/Colors/SColorPicker.h" +#include "SCommentBubble.h" +#include "SLevelOfDetailBranchNode.h" +#include "SGraphPin.h" +#include "Engine/Engine.h" +#include "PropertyCustomizationHelpers.h" +#include "LevelEditor.h" + +void SVoxelGraphNode::Construct(const FArguments& InArgs, class UVoxelGraphNode_Base* InNode) +{ + GraphNode = InNode; + VoxelNode = InNode; + + SetCursor(EMouseCursor::CardinalCross); + + UpdateGraphNode(); +} + +void SVoxelGraphNode::RefreshErrorInfo() +{ + SetupErrorReporting(); +} + +void SVoxelGraphNode::UpdateGraphNode() +{ + if (CastChecked(GraphNode)->IsCompact()) + { + UpdateCompactNode(); + } + else + { + UpdateStandardNode(); + } +} + +void SVoxelGraphNode::CreateOutputSideAddButton(TSharedPtr OutputBox) +{ + TSharedRef AddPinButton = AddPinButtonContent( + VOXEL_LOCTEXT("Add input"), + VOXEL_LOCTEXT("Adds an input to the Voxel node") + ); + + FMargin AddPinPadding = Settings->GetOutputPinPadding(); + AddPinPadding.Top += 6.0f; + + OutputBox->AddSlot() + .AutoHeight() + .VAlign(VAlign_Center) + .Padding(AddPinPadding) + [ + AddPinButton + ]; +} + +EVisibility SVoxelGraphNode::IsAddPinButtonVisible() const +{ + EVisibility ButtonVisibility = SGraphNode::IsAddPinButtonVisible(); + if (ButtonVisibility == EVisibility::Visible) + { + if (!VoxelNode->CanAddInputPin()) + { + ButtonVisibility = EVisibility::Collapsed; + } + } + return ButtonVisibility; +} + +FReply SVoxelGraphNode::OnAddPin() +{ + VoxelNode->AddInputPin(); + + return FReply::Handled(); +} + +TSharedRef SVoxelGraphNode::CreateTitleWidget(TSharedPtr NodeTitle) +{ + SAssignNew(InlineEditableText, SInlineEditableTextBlock) + .Style(FEditorStyle::Get(), "Graph.Node.NodeTitleInlineEditableText") + .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) + .OnVerifyTextChanged(this, &SVoxelGraphNode::OnVerifyNameTextChanged) + .OnTextCommitted(this, &SVoxelGraphNode::OnNameTextCommited) + .IsReadOnly(this, &SVoxelGraphNode::IsNameReadOnly) + .IsSelected(this, &SVoxelGraphNode::IsSelectedExclusively); + InlineEditableText->SetColorAndOpacity(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &SVoxelGraphNode::GetNodeTitleTextColor))); + + return InlineEditableText.ToSharedRef(); +} + +void SVoxelGraphNode::UpdateStandardNode() +{ + InputPins.Empty(); + OutputPins.Empty(); + + // Reset variables that are going to be exposed, in case we are refreshing an already setup node. + RightNodeBox.Reset(); + LeftNodeBox.Reset(); + + // + // ______________________ + // | TITLE AREA | + // +-------+------+-------+ + // | (>) L | | R (>) | + // | (>) E | | I (>) | + // | (>) F | | G (>) | + // | (>) T | | H (>) | + // | | | T (>) | + // |_______|______|_______| + // + TSharedPtr MainVerticalBox; + SetupErrorReporting(); + + TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode); + + // Get node icon + IconColor = FLinearColor::White; + const FSlateBrush* IconBrush = NULL; + if (GraphNode != NULL && GraphNode->ShowPaletteIconOnNode()) + { + IconBrush = GraphNode->GetIconAndTint(IconColor).GetOptionalIcon(); + } + + TSharedRef DefaultTitleAreaWidget = + SNew(SOverlay) + +SOverlay::Slot() + [ + SNew(SImage) + .Image( FEditorStyle::GetBrush("Graph.Node.TitleGloss") ) + .ColorAndOpacity( this, &SGraphNode::GetNodeTitleIconColor ) + ] + +SOverlay::Slot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + [ + SNew(SBorder) + .BorderImage( FEditorStyle::GetBrush("Graph.Node.ColorSpill") ) + // The extra margin on the right + // is for making the color spill stretch well past the node title + .Padding( FMargin(10,5,30,3) ) + .BorderBackgroundColor( this, &SGraphNode::GetNodeTitleColor ) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .VAlign(VAlign_Top) + .Padding(FMargin(0.f, 0.f, 4.f, 0.f)) + .AutoWidth() + [ + SNew(SImage) + .Image(IconBrush) + .ColorAndOpacity(this, &SGraphNode::GetNodeTitleIconColor) + ] + + SHorizontalBox::Slot() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + CreateTitleWidget(NodeTitle) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + NodeTitle.ToSharedRef() + ] + ] + ] + ] + +SOverlay::Slot() + .VAlign(VAlign_Top) + [ + SNew(SBorder) + .Visibility(EVisibility::HitTestInvisible) + .BorderImage( FEditorStyle::GetBrush( "Graph.Node.TitleHighlight" ) ) + .BorderBackgroundColor( this, &SGraphNode::GetNodeTitleIconColor ) + [ + SNew(SSpacer) + .Size(FVector2D(20,20)) + ] + ]; + + SetDefaultTitleAreaWidget(DefaultTitleAreaWidget); + + TSharedRef TitleAreaWidget = + SNew(SLevelOfDetailBranchNode) + .UseLowDetailSlot(this, &SVoxelGraphNode::UseLowDetailNodeTitles) + .LowDetail() + [ + SNew(SBorder) + .BorderImage( FEditorStyle::GetBrush("Graph.Node.ColorSpill") ) + .Padding( FMargin(75.0f, 22.0f) ) // Saving enough space for a 'typical' title so the transition isn't quite so abrupt + .BorderBackgroundColor( this, &SGraphNode::GetNodeTitleColor ) + ] + .HighDetail() + [ + DefaultTitleAreaWidget + ]; + + + if (!SWidget::GetToolTip().IsValid()) + { + TSharedRef DefaultToolTip = IDocumentation::Get()->CreateToolTip( TAttribute< FText >( this, &SGraphNode::GetNodeTooltip ), NULL, GraphNode->GetDocumentationLink(), GraphNode->GetDocumentationExcerptName() ); + SetToolTip(DefaultToolTip); + } + + // Setup a meta tag for this node + FGraphNodeMetaData TagMeta(TEXT("Graphnode")); + PopulateMetaTag(&TagMeta); + + TSharedPtr InnerVerticalBox; + this->ContentScale.Bind( this, &SGraphNode::GetContentScale ); + + + InnerVerticalBox = SNew(SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Top) + .Padding(Settings->GetNonPinNodeBodyPadding()) + [ + TitleAreaWidget + ] + + +SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Top) + [ + CreateNodeContentArea() + ]; + + if ((GraphNode->GetDesiredEnabledState() != ENodeEnabledState::Enabled) && !GraphNode->IsAutomaticallyPlacedGhostNode()) + { + const bool bDevelopmentOnly = GraphNode->GetDesiredEnabledState() == ENodeEnabledState::DevelopmentOnly; + const FText StatusMessage = bDevelopmentOnly ? VOXEL_LOCTEXT("Development Only") : VOXEL_LOCTEXT("Disabled"); + const FText StatusMessageTooltip = bDevelopmentOnly ? + VOXEL_LOCTEXT("This node will only be executed in the editor and in Development builds in a packaged game (it will be treated as disabled in Shipping or Test builds cooked from a commandlet)") : + VOXEL_LOCTEXT("This node is currently disabled and will not be executed"); + + InnerVerticalBox->AddSlot() + .AutoHeight() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Top) + .Padding(FMargin(2, 0)) + [ + SNew(SBorder) + .BorderImage(FEditorStyle::GetBrush(bDevelopmentOnly ? "Graph.Node.DevelopmentBanner" : "Graph.Node.DisabledBanner")) + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + [ + SNew(STextBlock) + .Text(StatusMessage) + .ToolTipText(StatusMessageTooltip) + .Justification(ETextJustify::Center) + .ColorAndOpacity(FLinearColor::White) + .ShadowOffset(FVector2D::UnitVector) + .Visibility(EVisibility::Visible) + ] + ]; + } + + InnerVerticalBox->AddSlot() + .AutoHeight() + .Padding(Settings->GetNonPinNodeBodyPadding()) + [ + InfoReporting->AsWidget() + ]; + InnerVerticalBox->AddSlot() + .AutoHeight() + .Padding(Settings->GetNonPinNodeBodyPadding()) + [ + WarningReporting->AsWidget() + ]; + InnerVerticalBox->AddSlot() + .AutoHeight() + .Padding(Settings->GetNonPinNodeBodyPadding()) + [ + ErrorReporting->AsWidget() + ]; + + + + this->GetOrAddSlot( ENodeZone::Center ) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SAssignNew(MainVerticalBox, SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SOverlay) + .AddMetaData(TagMeta) + +SOverlay::Slot() + .Padding(Settings->GetNonPinNodeBodyPadding()) + [ + SNew(SImage) + .Image(FEditorStyle::GetBrush("Graph.Node.Body")) + .ColorAndOpacity(this, &SVoxelGraphNode::GetNodeBodyColor) + ] + +SOverlay::Slot() + [ + InnerVerticalBox.ToSharedRef() + ] + ] + ]; + + // Create comment bubble + TSharedPtr CommentBubble; + const FSlateColor CommentColor = GetDefault()->DefaultCommentNodeTitleColor; + + SAssignNew( CommentBubble, SCommentBubble ) + .GraphNode( GraphNode ) + .Text( this, &SGraphNode::GetNodeComment ) + .OnTextCommitted( this, &SGraphNode::OnCommentTextCommitted ) + .OnToggled( this, &SGraphNode::OnCommentBubbleToggled ) + .ColorAndOpacity( CommentColor ) + .AllowPinning( true ) + .EnableTitleBarBubble( true ) + .EnableBubbleCtrls( true ) + .GraphLOD( this, &SGraphNode::GetCurrentLOD ) + .IsGraphNodeHovered( this, &SGraphNode::IsHovered ); + + GetOrAddSlot( ENodeZone::TopCenter ) + .SlotOffset( TAttribute( CommentBubble.Get(), &SCommentBubble::GetOffset )) + .SlotSize( TAttribute( CommentBubble.Get(), &SCommentBubble::GetSize )) + .AllowScaling( TAttribute( CommentBubble.Get(), &SCommentBubble::IsScalingAllowed )) + .VAlign( VAlign_Top ) + [ + CommentBubble.ToSharedRef() + ]; + + CreateBelowWidgetControls(MainVerticalBox); + CreatePinWidgets(); + CreateInputSideAddButton(LeftNodeBox); + CreateOutputSideAddButton(RightNodeBox); + CreateBelowPinControls(InnerVerticalBox); + CreateAdvancedViewArrow(InnerVerticalBox); +} + +void SVoxelGraphNode::UpdateCompactNode() +{ + InputPins.Empty(); + OutputPins.Empty(); + + // error handling set-up + SetupErrorReporting(); + + // Reset variables that are going to be exposed, in case we are refreshing an already setup node. + RightNodeBox.Reset(); + LeftNodeBox.Reset(); + + if (!SWidget::GetToolTip().IsValid()) + { + TSharedRef DefaultToolTip = IDocumentation::Get()->CreateToolTip( TAttribute< FText >( this, &SGraphNode::GetNodeTooltip ), NULL, GraphNode->GetDocumentationLink(), GraphNode->GetDocumentationExcerptName() ); + SetToolTip(DefaultToolTip); + } + + // Setup a meta tag for this node + FGraphNodeMetaData TagMeta(TEXT("Graphnode")); + PopulateMetaTag(&TagMeta); + + TSharedPtr NodeTitle = SNew(SNodeTitle, GraphNode).Text(this, &SVoxelGraphNode::GetNodeCompactTitle); + + TSharedRef NodeOverlay = SNew(SOverlay); + + // add optional node specific widget to the overlay: + TSharedPtr OverlayWidget = GraphNode->CreateNodeImage(); + if (OverlayWidget.IsValid()) + { + NodeOverlay->AddSlot() + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(70.f) + .HeightOverride(70.f) + [ + OverlayWidget.ToSharedRef() + ] + ]; + } + + NodeOverlay->AddSlot() + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + .Padding(45.f, 0.f, 45.f, 0.f) + [ + // MIDDLE + SNew(SVerticalBox) + + SVerticalBox::Slot() + .HAlign(HAlign_Center) + .AutoHeight() + [ + SNew(STextBlock) + .TextStyle(FEditorStyle::Get(), "Graph.CompactNode.Title") + .Text(NodeTitle.Get(), &SNodeTitle::GetHeadTitle) + .WrapTextAt(128.0f) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + NodeTitle.ToSharedRef() + ] + ]; + + NodeOverlay->AddSlot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .Padding(0.f, 0.f, 55.f, 0.f) + [ + // LEFT + SAssignNew(LeftNodeBox, SVerticalBox) + ]; + + NodeOverlay->AddSlot() + .HAlign(HAlign_Right) + .VAlign(VAlign_Center) + .Padding(55.f, 0.f, 0.f, 0.f) + [ + // RIGHT + SAssignNew(RightNodeBox, SVerticalBox) + ]; + + // + // ______________________ + // | (>) L | | R (>) | + // | (>) E | | I (>) | + // | (>) F | + | G (>) | + // | (>) T | | H (>) | + // | | | T (>) | + // |_______|______|_______| + // + this->ContentScale.Bind(this, &SGraphNode::GetContentScale); + this->GetOrAddSlot(ENodeZone::Center) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + // NODE CONTENT AREA + SNew(SOverlay) + + SOverlay::Slot() + [ + SNew(SImage) + .Image(FEditorStyle::GetBrush("Graph.VarNode.Body")) + .ColorAndOpacity(this, &SVoxelGraphNode::GetNodeBodyColor) + ] + + SOverlay::Slot() + [ + SNew(SImage) + .Image(FEditorStyle::GetBrush("Graph.VarNode.Gloss")) + .ColorAndOpacity(this, &SVoxelGraphNode::GetNodeBodyColor) + ] + + SOverlay::Slot() + .Padding(FMargin(0, 3)) + [ + NodeOverlay + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(FMargin(5.0f, 1.0f)) + [ + InfoReporting->AsWidget() + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(FMargin(5.0f, 1.0f)) + [ + WarningReporting->AsWidget() + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(FMargin(5.0f, 1.0f)) + [ + ErrorReporting->AsWidget() + ] + ]; + + CreatePinWidgets(); + + // Hide pin labels + for (auto& InputPin : InputPins) + { + if (InputPin->GetPinObj()->ParentPin == nullptr) + { + InputPin->SetShowLabel(false); + } + } + + for (auto& OutputPin : OutputPins) + { + if (OutputPin->GetPinObj()->ParentPin == nullptr) + { + OutputPin->SetShowLabel(false); + } + } + + // Create comment bubble + TSharedPtr CommentBubble; + const FSlateColor CommentColor = GetDefault()->DefaultCommentNodeTitleColor; + + SAssignNew(CommentBubble, SCommentBubble) + .GraphNode(GraphNode) + .Text(this, &SGraphNode::GetNodeComment) + .OnTextCommitted(this, &SGraphNode::OnCommentTextCommitted) + .ColorAndOpacity(CommentColor) + .AllowPinning(true) + .EnableTitleBarBubble(true) + .EnableBubbleCtrls(true) + .GraphLOD(this, &SGraphNode::GetCurrentLOD) + .IsGraphNodeHovered(this, &SVoxelGraphNode::IsHovered); + + GetOrAddSlot(ENodeZone::TopCenter) + .SlotOffset(TAttribute(CommentBubble.Get(), &SCommentBubble::GetOffset)) + .SlotSize(TAttribute(CommentBubble.Get(), &SCommentBubble::GetSize)) + .AllowScaling(TAttribute(CommentBubble.Get(), &SCommentBubble::IsScalingAllowed)) + .VAlign(VAlign_Top) + [ + CommentBubble.ToSharedRef() + ]; + + CreateInputSideAddButton(LeftNodeBox); + CreateOutputSideAddButton(RightNodeBox); +} + +FText SVoxelGraphNode::GetNodeCompactTitle() const +{ + return GraphNode->GetNodeTitle(ENodeTitleType::FullTitle); +} + +FSlateColor SVoxelGraphNode::GetNodeBodyColor() const +{ + return CastChecked(GraphNode)->GetNodeBodyColor(); +} + +void SVoxelGraphNode::SetupErrorReporting() +{ + UpdateErrorInfo(); + + if (!InfoReporting.IsValid()) + { + TSharedPtr InfoTextWidget; + + // generate widget + SAssignNew(InfoTextWidget, SErrorText) + .BackgroundColor(this, &SVoxelGraphNode::GetInfoColor) + .ToolTipText(this, &SVoxelGraphNode::GetInfoMsgToolTip); + + InfoReporting = InfoTextWidget; + } + if (!WarningReporting.IsValid()) + { + TSharedPtr WarningTextWidget; + + // generate widget + SAssignNew(WarningTextWidget, SErrorText) + .BackgroundColor(this, &SVoxelGraphNode::GetWarningColor) + .ToolTipText(this, &SVoxelGraphNode::GetWarningMsgToolTip); + + WarningReporting = WarningTextWidget; + } + if (!ErrorReporting.IsValid()) + { + TSharedPtr ErrorTextWidget; + + // generate widget + SAssignNew(ErrorTextWidget, SErrorText) + .BackgroundColor(this, &SVoxelGraphNode::GetErrorColor) + .ToolTipText(this, &SVoxelGraphNode::GetErrorMsgToolTip); + + ErrorReporting = ErrorTextWidget; + } + InfoReporting->SetError(InfoMsg); + WarningReporting->SetError(WarningMsg); + ErrorReporting->SetError(ErrorMsg); +} + +void SVoxelGraphNode::UpdateErrorInfo() +{ + InfoColor = FEditorStyle::GetColor("InfoReporting.BackgroundColor"); + WarningColor = FEditorStyle::GetColor("ErrorReporting.WarningBackgroundColor"); + ErrorColor = FEditorStyle::GetColor("ErrorReporting.BackgroundColor"); + + InfoMsg = VoxelNode->InfoMsg; + WarningMsg = VoxelNode->WarningMsg; + ErrorMsg = VoxelNode->ErrorMsg; +} + +void SVoxelGraphNode::OnNameTextCommited(const FText& InText, ETextCommit::Type CommitInfo) +{ + OnTextCommitted.ExecuteIfBound(InText, CommitInfo, GraphNode); + + UpdateErrorInfo(); + if (ErrorReporting.IsValid()) + { + ErrorReporting->SetError(ErrorMsg); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SVoxelColorGraphNode::Construct(const FArguments& InArgs, UVoxelGraphNode_Base* InNode, UVoxelNode_ColorParameter* InColorNode) +{ + ColorNode = InColorNode; + SVoxelGraphNode::Construct({}, InNode); +} + +void SVoxelColorGraphNode::CreateBelowPinControls(TSharedPtr MainBox) +{ + if (ColorNode) + { + const float NegativeHPad = FMath::Max(-Settings->PaddingTowardsNodeEdge, 0.0f); + const float ExtraPad = 5; + + const float ExpressionPreviewSize = 50; + const float CentralPadding = 5.0f; + + LeftNodeBox->AddSlot() + .Padding(FMargin(NegativeHPad + ExtraPad, 0.0f, 0.0f, 0.0f)) + .AutoHeight() + [ + SNew(SBox) + .WidthOverride(ExpressionPreviewSize) + .HeightOverride(ExpressionPreviewSize) + [ + SNew(SBorder) + .Padding(CentralPadding) + .BorderImage( FEditorStyle::GetBrush("NoBorder") ) + [ + SAssignNew(DefaultValueWidget, SColorBlock) + .Color(this, &SVoxelColorGraphNode::GetParameterColor) + .ShowBackgroundForAlpha(true) + .OnMouseButtonDown(this, &SVoxelColorGraphNode::OnColorBoxClicked) + ] + ] + ]; + + DefaultValueWidget->SetCursor(EMouseCursor::Default); + } +} + +FLinearColor SVoxelColorGraphNode::GetParameterColor() const +{ + return ColorNode->Color; +} + +void SVoxelColorGraphNode::SetParameterColor(FLinearColor Color) +{ + ColorNode->Color = Color; +} + +FReply SVoxelColorGraphNode::OnColorBoxClicked(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) + { + SelectedColor = ColorNode->Color; + TArray LinearColorArray; + LinearColorArray.Add(&SelectedColor); + + FColorPickerArgs PickerArgs; + PickerArgs.bIsModal = true; + PickerArgs.ParentWidget = DefaultValueWidget; + PickerArgs.DisplayGamma = TAttribute::Create(TAttribute::FGetter::CreateUObject(GEngine, &UEngine::GetDisplayGamma)); + PickerArgs.LinearColorArray = &LinearColorArray; + PickerArgs.OnColorCommitted = FOnLinearColorValueChanged::CreateSP(this, &SVoxelColorGraphNode::SetParameterColor); + PickerArgs.bUseAlpha = true; + + OpenColorPicker(PickerArgs); + + return FReply::Handled(); + } + else + { + return FReply::Unhandled(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SVoxelAssetPickerGraphNode::Construct(const FArguments& InArgs, UVoxelGraphNode_Base* InNode, UVoxelAssetPickerNode* InAssetPickerNode) +{ + AssetPickerNode = InAssetPickerNode; + SVoxelGraphNode::Construct({}, InNode); +} + +void SVoxelAssetPickerGraphNode::CreateBelowPinControls(TSharedPtr MainBox) +{ + if (AssetPickerNode) + { + const float NegativeHPad = FMath::Max(-Settings->PaddingTowardsNodeEdge, 0.0f); + const float ExtraPad = 5; + + const float CentralPadding = 0.0f; + + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + TSharedPtr ThumbnailPool = LevelEditorModule.GetFirstLevelEditor()->GetThumbnailPool(); + + LeftNodeBox->AddSlot() + .Padding(FMargin(NegativeHPad + ExtraPad, 0.0f, 0.0f, 0.0f)) + .AutoHeight() + [ + SNew(SBox) + [ + SNew(SBorder) + .Padding(CentralPadding) + .BorderImage( FEditorStyle::GetBrush("NoBorder") ) + [ + SAssignNew(DefaultValueWidget, SObjectPropertyEntryBox) + .IsEnabled(true) + .ObjectPath(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &SVoxelAssetPickerGraphNode::GetObjectPath))) + .AllowedClass(AssetPickerNode->GetAssetClass()) + .OnObjectChanged(FOnSetObject::CreateSP(this, &SVoxelAssetPickerGraphNode::SetAsset)) + .OnShouldFilterAsset(FOnShouldFilterAsset::CreateSP(this, &SVoxelAssetPickerGraphNode::OnShouldFilterAsset)) + .ThumbnailPool(ThumbnailPool) + ] + ] + ]; + DefaultValueWidget->SetCursor(EMouseCursor::Default); + } +} + +void SVoxelAssetPickerGraphNode::SetAsset(const FAssetData& Asset) +{ + AssetPickerNode->SetAsset(Asset.GetAsset()); +} + +bool SVoxelAssetPickerGraphNode::OnShouldFilterAsset(const FAssetData& Asset) +{ + return AssetPickerNode->ShouldFilterAsset(Asset); +} + +FString SVoxelAssetPickerGraphNode::GetObjectPath() const +{ + UObject* PickedAsset = AssetPickerNode->GetAsset(); + return PickedAsset ? PickedAsset->GetPathName() : ""; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.h new file mode 100644 index 00000000..cc00cd9d --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.h @@ -0,0 +1,102 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "SGraphNode.h" + +class SVoxelGraphNode : public SGraphNode +{ +public: + SLATE_BEGIN_ARGS(SVoxelGraphNode){} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, class UVoxelGraphNode_Base* InNode); + + /** Called when GraphNode changes its error information, may be called when no change has actually occurred: */ + void RefreshErrorInfo(); + +protected: + //~ Begin SGraphNode Interface + virtual void UpdateGraphNode() override; + virtual void CreateOutputSideAddButton(TSharedPtr OutputBox) override; + virtual EVisibility IsAddPinButtonVisible() const override; + virtual FReply OnAddPin() override; + virtual TSharedRef CreateTitleWidget(TSharedPtr NodeTitle) override; + //~ End SGraphNode Interface + +private: + /** Set up node in 'standard' mode */ + void UpdateStandardNode(); + /** Set up node in 'compact' mode */ + void UpdateCompactNode(); + /** Get title in compact mode */ + FText GetNodeCompactTitle() const; + /** @return the tint for the node's main body */ + FSlateColor GetNodeBodyColor() const; + /** Set-up the error reporting widget for the node */ + void SetupErrorReporting(); + /** Called to set error text on the node */ + void UpdateErrorInfo(); + /* Called when text is committed on the node */ + void OnNameTextCommited ( const FText& InText, ETextCommit::Type CommitInfo ) ; + + FSlateColor GetInfoColor() const { return InfoColor; } + FText GetInfoMsgToolTip() const { return FText::FromString(InfoMsg); } + + FSlateColor GetWarningColor() const { return WarningColor; } + FText GetWarningMsgToolTip() const { return FText::FromString(WarningMsg); } + + TSharedPtr InfoReporting; + TSharedPtr WarningReporting; + FSlateColor InfoColor; + FSlateColor WarningColor; + FString InfoMsg; + FString WarningMsg; + + UVoxelGraphNode_Base* VoxelNode = nullptr; +}; + +class SVoxelColorGraphNode : public SVoxelGraphNode +{ +public: + SLATE_BEGIN_ARGS(SVoxelColorGraphNode) {} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, class UVoxelGraphNode_Base* InNode, class UVoxelNode_ColorParameter* InColorNode); + + //~ Begin SGraphNode Interface + virtual void CreateBelowPinControls(TSharedPtr MainBox) override; + //~ End SGraphNode Interface + +private: + FLinearColor SelectedColor; + UVoxelNode_ColorParameter* ColorNode = nullptr; + TSharedPtr DefaultValueWidget; + + FLinearColor GetParameterColor() const; + void SetParameterColor(FLinearColor Color); + + FReply OnColorBoxClicked(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); +}; + +class SVoxelAssetPickerGraphNode : public SVoxelGraphNode +{ +public: + SLATE_BEGIN_ARGS(SVoxelAssetPickerGraphNode) {} + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, class UVoxelGraphNode_Base* InNode, class UVoxelAssetPickerNode* InAssetPickerNode); + + //~ Begin SGraphNode Interface + virtual void CreateBelowPinControls(TSharedPtr MainBox) override; + //~ End SGraphNode Interface + +private: + UVoxelAssetPickerNode* AssetPickerNode = nullptr; + TSharedPtr DefaultValueWidget; + + void SetAsset(const FAssetData& Asset); + bool OnShouldFilterAsset(const FAssetData& Asset); + FString GetObjectPath() const; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp new file mode 100644 index 00000000..af900e10 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp @@ -0,0 +1,829 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphNode.h" +#include "VoxelGraphGenerator.h" +#include "VoxelNode.h" +#include "VoxelNodes/VoxelLocalVariables.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelGraphNodes/VoxelGraphNode_Knot.h" + +#include "VoxelGraphEditorUtilities.h" +#include "IVoxelGraphEditorToolkit.h" +#include "VoxelGraphEditorCommands.h" +#include "VoxelEdGraph.h" + +#include "EdGraph/EdGraphPin.h" +#include "EdGraph/EdGraphNode.h" +#include "GraphEditorActions.h" + +#include "Engine/Font.h" +#include "ScopedTransaction.h" +#include "Editor/EditorEngine.h" +#include "Toolkits/AssetEditorManager.h" +#include "Framework/Commands/GenericCommands.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Launch/Resources/Version.h" + +void UVoxelGraphNode::SetVoxelNode(UVoxelNode* InNode) +{ + check(InNode); + VoxelNode = InNode; + bCanRenameNode = VoxelNode->CanRenameNode(); +} + +void UVoxelGraphNode::PostCopyNode() +{ + // Make sure the VoxelNode goes back to being owned by the generator after copying. + ResetVoxelNodeOwner(); +} + +void UVoxelGraphNode::CreateInputPin() +{ + const int32 PinIndex = GetInputCount(); + + UEdGraphPin* NewPin = CreatePin(EGPD_Input, FVoxelPinCategory::GetName(VoxelNode->GetInputPinCategory(PinIndex)), FName(), nullptr, VoxelNode->GetInputPinName(PinIndex)); + + if (NewPin->PinName.IsNone()) + { + // Makes sure pin has a name for lookup purposes but user will never see it + NewPin->PinName = CreateUniquePinName(TEXT("Input")); + NewPin->PinFriendlyName = FText::FromString(TEXT(" ")); + } + + NewPin->DefaultValue = VoxelNode->GetInputPinDefaultValue(PinIndex); + if (NewPin->DefaultValue.IsEmpty()) + { + NewPin->DefaultValue = FVoxelPinCategory::GetDefaultValue(VoxelNode->GetInputPinCategory(PinIndex)); + } +} + +void UVoxelGraphNode::CreateOutputPin() +{ + const int32 PinIndex = GetOutputCount(); + + UEdGraphPin* NewPin = CreatePin(EGPD_Output, FVoxelPinCategory::GetName(VoxelNode->GetOutputPinCategory(PinIndex)), FName(), nullptr, VoxelNode->GetOutputPinName(PinIndex)); + + if (NewPin->PinName.IsNone()) + { + // Makes sure pin has a name for lookup purposes but user will never see it + NewPin->PinName = CreateUniquePinName(TEXT("Output")); + NewPin->PinFriendlyName = FText::FromString(TEXT(" ")); + } +} + +void UVoxelGraphNode::AddInputPin() +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Add Input Pin")); + Modify(); + const int32 Increment = VoxelNode->GetInputPinsIncrement(); + VoxelNode->InputPinCount += Increment; + ensure(VoxelNode->InputPinCount <= VoxelNode->GetMaxInputPins()); + for (int32 Index = 0; Index < Increment; ++Index) + { + CreateInputPin(); + } + + VoxelNode->OnInputPinCountModified(); + + UVoxelGraphGenerator* Generator = CastChecked(GetGraph())->GetGenerator(); + Generator->CompileVoxelNodesFromGraphNodes(); + + // Refresh the current graph, so the pins can be updated + GetGraph()->NotifyGraphChanged(); +} + +void UVoxelGraphNode::RemoveInputPin(UEdGraphPin* InGraphPin) +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Delete Input Pin")); + Modify(); + + for (auto* InputPin : GetInputPins()) + { + if (InGraphPin == InputPin) + { + InGraphPin->MarkPendingKill(); + Pins.Remove(InGraphPin); + + const int32 Increment = VoxelNode->GetInputPinsIncrement(); + if (Increment > 1) + { + const int32 PinIndex = VoxelNode->GetInputPinIndex(InGraphPin->PinId); + if (ensure(PinIndex != -1)) + { + // Below = higher index! + const int32 PinsBelow = (VoxelNode->InputPinCount - 1 - PinIndex) % Increment; + const int32 PinsAbove = Increment - 1 - PinsBelow; + for (int32 Index = PinIndex - PinsAbove; Index <= PinIndex + PinsBelow; Index++) + { + if (ensure(VoxelNode->InputPins.IsValidIndex(Index))) + { + const auto PinId = VoxelNode->InputPins[Index].PinId; + Pins.RemoveAll([&](auto& ArrayPin) { return ArrayPin->PinId == PinId; }); + } + } + } + } + + // also remove the VoxelNode child node so ordering matches + VoxelNode->Modify(); + VoxelNode->InputPinCount -= Increment; + ensure(VoxelNode->InputPinCount >= VoxelNode->GetMinInputPins()); + break; + } + } + + VoxelNode->OnInputPinCountModified(); + + UVoxelGraphGenerator* Generator = CastChecked(GetGraph())->GetGenerator(); + Generator->CompileVoxelNodesFromGraphNodes(); + + // Refresh the current graph, so the pins can be updated + GetGraph()->NotifyGraphChanged(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelGraphNode::CanSplitPin_Voxel(const UEdGraphPin& Pin) const +{ + return const_cast(this)->TrySplitPin(const_cast(Pin), true); +} + +bool UVoxelGraphNode::CanCombinePin(const UEdGraphPin& Pin) const +{ + return const_cast(this)->TryCombinePin(const_cast(Pin), true); +} + +bool UVoxelGraphNode::TrySplitPin(UEdGraphPin& Pin, bool bOnlyCheck) +{ + ensure(!Pin.bHidden); + if (Pin.SubPins.Num() == 0 || Pin.LinkedTo.Num() > 0) + { + return false; + } + + if (bOnlyCheck) + { + return true; + } + + TArray SubDefaultValues; + Pin.DefaultValue.ParseIntoArray(SubDefaultValues, TEXT(",")); + + for (int32 Index = 0; Index < Pin.SubPins.Num(); Index++) + { + auto* SubPin = Pin.SubPins[Index]; + ensure(SubPin->bHidden); + ensure(SubPin->ParentPin == &Pin); + SubPin->bHidden = false; + SubPin->ParentPin = nullptr; + SubPin->DefaultValue = SubDefaultValues.IsValidIndex(Index) ? SubDefaultValues[Index] : ""; + } + Pin.SubPins.Empty(); + + ensure(RemovePin(&Pin)); + + GetGraph()->NotifyGraphChanged(); + + return true; +} + +bool UVoxelGraphNode::TryCombinePin(UEdGraphPin& Pin, bool bOnlyCheck) +{ + ensure(!Pin.bHidden); + + const auto NeighborPins = Pin.Direction == EGPD_Input ? GetInputPins() : GetOutputPins(); + const int32 PinIndex = NeighborPins.Find(&Pin); + + if (!ensure(PinIndex != -1)) + { + return false; + } + + const auto CheckStart = [&](int32 Index) + { + if (!NeighborPins.IsValidIndex(Index) || + !NeighborPins.IsValidIndex(Index + 2)) + { + return false; + } + + FString Name = NeighborPins[Index]->GetName(); + if (!Name.RemoveFromStart("X")) + { + return false; + } + return + NeighborPins[Index + 1]->GetName() == "Y" + Name && + NeighborPins[Index + 2]->GetName() == "Z" + Name; + }; + const auto CheckEnd = [&](int32 Index) + { + if (!NeighborPins.IsValidIndex(Index) || + !NeighborPins.IsValidIndex(Index + 2)) + { + return false; + } + + FString Name = NeighborPins[Index]->GetName(); + if (!Name.RemoveFromEnd("X")) + { + return false; + } + return + NeighborPins[Index + 1]->GetName() == Name + "Y" && + NeighborPins[Index + 2]->GetName() == Name + "Z"; + }; + + int32 IndexX = -1; + bool bIsStart = false; + for (int32 Index = PinIndex - 2; Index <= PinIndex; Index++) + { + if (CheckStart(Index)) + { + bIsStart = true; + IndexX = Index; + break; + } + if (CheckEnd(Index)) + { + bIsStart = false; + IndexX = Index; + break; + } + } + + if (IndexX == -1) + { + return false; + } + + for (int32 Index = 0; Index < 3; Index++) + { + if (NeighborPins[IndexX + Index]->LinkedTo.Num() > 0) + { + return false; + } + } + + if (bOnlyCheck) + { + return true; + } + + FString ParentPinName = NeighborPins[IndexX]->GetName(); + if (bIsStart) + { + ensure(ParentPinName.RemoveFromStart("X")); + ParentPinName.RemoveFromStart("."); + } + else + { + ensure(ParentPinName.RemoveFromEnd("X")); + ParentPinName.RemoveFromEnd("."); + } + + auto* ParentPin = CreatePin(Pin.Direction, FVoxelPinCategory::GetName(EVoxelPinCategory::Vector), FName(), nullptr, *ParentPinName); + Pins.Pop(false); + + FVector DefaultValue; + for (int32 Index = 0; Index < 3; Index++) + { + auto* SubPin = NeighborPins[IndexX + Index]; + SubPin->bHidden = true; + SubPin->ParentPin = ParentPin; + + DefaultValue[Index] = FCString::Atof(*SubPin->DefaultValue); + + ParentPin->SubPins.Add(SubPin); + } + ParentPin->DefaultValue = FString::Printf(TEXT("%f,%f,%f"), DefaultValue.X, DefaultValue.Y, DefaultValue.Z); + + // Add the parent before the sub pins + const int32 InsertIndex = Pins.Find(NeighborPins[IndexX]); + check(InsertIndex != -1); + Pins.Insert(ParentPin, InsertIndex); + + GetGraph()->NotifyGraphChanged(); + + return true; +} + +void UVoxelGraphNode::CombineAll() +{ + const auto Copy = Pins; + for (auto& Pin : Copy) + { + if (!Pin->bHidden) + { + TryCombinePin(*Pin, false); + } + } +} + +bool UVoxelGraphNode::HasVectorPin(UVoxelNode& Node, EEdGraphPinDirection Direction) +{ + TArray Names; + + if (Direction == EGPD_Input) + { + const int32 InputCount = Node.GetMinInputPins(); + for (int32 Index = 0; Index < InputCount; Index++) + { + Names.Add(Node.GetInputPinName(Index).ToString()); + } + } + else + { + const int32 OutputCount = Node.GetOutputPinsCount(); + for (int32 Index = 0; Index < OutputCount; Index++) + { + Names.Add(Node.GetOutputPinName(Index).ToString()); + } + } + + const auto CheckStart = [&](int32 Index) + { + FString Name = Names[Index]; + if (!Name.RemoveFromStart("X")) + { + return false; + } + return + Names[Index + 1] == "Y" + Name && + Names[Index + 2] == "Z" + Name; + }; + const auto CheckEnd = [&](int32 Index) + { + FString Name = Names[Index]; + if (!Name.RemoveFromEnd("X")) + { + return false; + } + return + Names[Index + 1] == Name + "Y" && + Names[Index + 2] == Name + "Z"; + }; + + for (int32 Index = 0; Index < Names.Num() - 2; Index++) + { + if (CheckStart(Index) || CheckEnd(Index)) + { + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelGraphNode::CanAddInputPin() const +{ + if (VoxelNode) + { + const int32 MinPins = VoxelNode->GetMinInputPins(); + const int32 MaxPins = VoxelNode->GetMaxInputPins(); + if (MinPins == MaxPins) + { + return false; + } + else + { + return GetInputCount() < MaxPins; + } + } + else + { + return false; + } +} + +bool UVoxelGraphNode::IsCompact() const +{ + return VoxelNode && VoxelNode->IsCompact(); +} + +FLinearColor UVoxelGraphNode::GetNodeBodyColor() const +{ + if (!IsNodeEnabled()) + { + return FLinearColor(1.0f, 1.0f, 1.0f, 0.5f); + } + if (VoxelNode) + { + for (auto& Pin : Pins) + { + if (Pin->bIsDiffing) + { + return FLinearColor(0.f, 0.f, 1.0f, 1.f); + } + } + return VoxelNode->GetNodeBodyColor(); + } + return FLinearColor::White; +} + +bool UVoxelGraphNode::IsOutdated() const +{ + int32 InputIndex = 0; + int32 OutputIndex = 0; + for (auto* Pin : Pins) + { + if (Pin->SubPins.Num() > 0) + { + continue; + } + + if (Pin->Direction == EGPD_Input) + { + if (FVoxelPinCategory::GetName(VoxelNode->GetInputPinCategory(InputIndex)) != Pin->PinType.PinCategory) + { + return true; + } + const FName PinName = VoxelNode->GetInputPinName(InputIndex); + if (!PinName.IsNone() && PinName != Pin->PinName) + { + return true; + } + InputIndex++; + } + else + { + check(Pin->Direction == EGPD_Output); + if (FVoxelPinCategory::GetName(VoxelNode->GetOutputPinCategory(OutputIndex)) != Pin->PinType.PinCategory) + { + return true; + } + const FName PinName = VoxelNode->GetOutputPinName(OutputIndex); + if (!PinName.IsNone() && PinName != Pin->PinName) + { + return true; + } + OutputIndex++; + } + } + return false; +} + +void UVoxelGraphNode::CreateInputPins() +{ + if (!ensure(VoxelNode)) return; + + VoxelNode->InputPinCount = FMath::Clamp(VoxelNode->InputPinCount, VoxelNode->GetMinInputPins(), VoxelNode->GetMaxInputPins()); + while (GetInputCount() < VoxelNode->InputPinCount) + { + CreateInputPin(); + } +} + +void UVoxelGraphNode::CreateOutputPins() +{ + if (!ensure(VoxelNode)) return; + + while (GetOutputCount() < VoxelNode->GetOutputPinsCount()) + { + CreateOutputPin(); + } +} + +void UVoxelGraphNode::RestoreVectorPins(const TArray& OldInputPins, const TArray& OldOutputPins) +{ + const auto NewInputPins = GetInputPins(); + const auto NewOutputPins = GetOutputPins(); + + const auto Restore = [&](const TArray& OldPins, const TArray& NewPins) + { + int32 NewIndex = 0; + for (int32 Index = 0; Index < OldPins.Num() && NewIndex < NewPins.Num(); Index++) + { + auto* OldPin = OldPins[Index]; + if (OldPin->SubPins.Num() == 0) + { + // Not a parent pin + auto* NewPin = NewPins[NewIndex++]; + + if (OldPin->ParentPin && !NewPin->ParentPin) + { + TryCombinePin(*NewPin, false); + } + } + } + }; + + Restore(OldInputPins, NewInputPins); + Restore(OldOutputPins, NewOutputPins); +} + +FText UVoxelGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + if (VoxelNode) + { + if (TitleType == ENodeTitleType::EditableTitle) + { + return FText::FromString(VoxelNode->GetEditableName()); + } + else + { + return VoxelNode->GetTitle(); + } + } + else + { + return Super::GetNodeTitle(TitleType); + } +} + +FLinearColor UVoxelGraphNode::GetNodeTitleColor() const +{ + if (VoxelNode) + { + return VoxelNode->GetColor(); + } + else + { + return FLinearColor::Gray; + } +} + +void UVoxelGraphNode::PrepareForCopying() +{ + if (VoxelNode) + { + // Temporarily take ownership of the VoxelNode, so that it is not deleted when cutting + VoxelNode->Rename(NULL, this, REN_DontCreateRedirectors); + } +} + +#if ENGINE_MINOR_VERSION < 24 +void UVoxelGraphNode::GetContextMenuActions(const FGraphNodeContextMenuBuilder& Context) const +{ + if (Context.Pin) + { + if (VoxelNode) + { + // If on an input that can be deleted, show option + if (Context.Pin->Direction == EGPD_Input) + { + const int32 MinPins = VoxelNode->GetMinInputPins(); + const int32 MaxPins = VoxelNode->GetMaxInputPins(); + if (MinPins != MaxPins && MinPins < VoxelNode->InputPinCount) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().DeleteInput); + } + } + // Preview + if (Context.Pin->Direction == EGPD_Output && FVoxelPinCategory::FromString(Context.Pin->PinType.PinCategory) == EVoxelPinCategory::Float) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().TogglePinPreview); + } + if (CanSplitPin_Voxel(*Context.Pin)) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().SplitPin); + } + if (CanCombinePin(*Context.Pin)) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().CombinePin); + } + } + } + else if (Context.Node) + { + // Add a 'Convert to Local Variables' option to reroute nodes + if (auto* Knot = Cast(this)) + { + EVoxelPinCategory Category = FVoxelPinCategory::FromString(Knot->GetInputPin()->PinType.PinCategory); + if (Category != EVoxelPinCategory::Exec && + Category != EVoxelPinCategory::Wildcard && + Category != EVoxelPinCategory::Vector) + { + Context.MenuBuilder->BeginSection("MaterialEditorMenu1"); + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().ConvertRerouteToVariables); + } + Context.MenuBuilder->EndSection(); + } + } + + if (VoxelNode) + { + // Add local variables selection & conversion to reroute nodes + if (VoxelNode->IsA(UVoxelLocalVariableBase::StaticClass())) + { + Context.MenuBuilder->BeginSection("MaterialEditorMenu1"); + { + if (VoxelNode->IsA(UVoxelLocalVariableDeclaration::StaticClass())) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().SelectLocalVariableUsages); + } + if (VoxelNode->IsA(UVoxelLocalVariableUsage::StaticClass())) + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().SelectLocalVariableDeclaration); + } + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().ConvertVariablesToReroute); + } + Context.MenuBuilder->EndSection(); + } + } + + + Context.MenuBuilder->BeginSection("VoxelGraphNodeEdit"); + { + Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Delete); + Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Cut); + Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy); + Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Duplicate); + } + Context.MenuBuilder->EndSection(); + + Context.MenuBuilder->BeginSection("VoxelGraphNodeMisc"); + { + Context.MenuBuilder->AddMenuEntry(FVoxelGraphEditorCommands::Get().ReconstructNode); + } + Context.MenuBuilder->EndSection(); + + Context.MenuBuilder->AddSubMenu(VOXEL_LOCTEXT("Alignment"), FText(), FNewMenuDelegate::CreateLambda([](FMenuBuilder& InMenuBuilder) { + + InMenuBuilder.BeginSection("EdGraphSchemaAlignment", VOXEL_LOCTEXT("Align")); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesTop); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesMiddle); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesBottom); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesLeft); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesCenter); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesRight); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().StraightenConnections); + InMenuBuilder.EndSection(); + + InMenuBuilder.BeginSection("EdGraphSchemaDistribution", VOXEL_LOCTEXT("Distribution")); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesHorizontally); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesVertically); + InMenuBuilder.EndSection(); + })); + } +} +#endif + +FText UVoxelGraphNode::GetTooltipText() const +{ + if (VoxelNode) + { + return VoxelNode->GetTooltip(); + } + else + { + return GetNodeTitle(ENodeTitleType::ListView); + } +} + +FString UVoxelGraphNode::GetDocumentationExcerptName() const +{ + // Default the node to searching for an excerpt named for the C++ node class name, including the U prefix. + // This is done so that the excerpt name in the doc file can be found by find-in-files when searching for the full class name. + UClass* MyClass = (VoxelNode != NULL) ? VoxelNode->GetClass() : this->GetClass(); + return FString::Printf(TEXT("%s%s"), MyClass->GetPrefixCPP(), *MyClass->GetName()); +} + +bool UVoxelGraphNode::CanUserDeleteNode() const +{ + return !VoxelNode || VoxelNode->CanUserDeleteNode(); +} + +bool UVoxelGraphNode::CanDuplicateNode() const +{ + return !VoxelNode || VoxelNode->CanDuplicateNode(); +} + +bool UVoxelGraphNode::CanJumpToDefinition() const +{ + return VoxelNode && ((VoxelNode->IsA(UVoxelGraphMacroNode::StaticClass()) && CastChecked(VoxelNode)->Macro) || VoxelNode->IsA()); +} + +void UVoxelGraphNode::JumpToDefinition() const +{ + if (auto* Macro = Cast(VoxelNode)) + { +#if ENGINE_MINOR_VERSION < 24 + FAssetEditorManager::Get().OpenEditorForAsset(Macro->Macro); +#else + GEditor->GetEditorSubsystem()->OpenEditorForAsset(Macro->Macro); +#endif + } + else if (auto* Usage = Cast(VoxelNode)) + { + if (Usage->Declaration) + { + FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(VoxelNode->Graph->VoxelGraph)->SelectNodesAndZoomToFit({ Usage->Declaration->GraphNode }); + } + } +} + +void UVoxelGraphNode::OnRenameNode(const FString& NewName) +{ + if (VoxelNode) + { + VoxelNode->Modify(); + VoxelNode->SetEditableName(NewName); + VoxelNode->MarkPackageDirty(); + } +} + +void UVoxelGraphNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const +{ + if (!VoxelNode) + { + return; + } + + TArray PinIds; + + PinIds.Add(Pin.PinId); + for (auto& SubPin : Pin.SubPins) + { + PinIds.Add(SubPin->PinId); + } + + for (auto& PinId : PinIds) + { + int32 Index = VoxelNode->GetInputPinIndex(PinId); + if (Index != -1) + { + if (!HoverTextOut.IsEmpty()) HoverTextOut += "\n"; + HoverTextOut += VoxelNode->GetInputPinToolTip(Index); + } + else + { + Index = VoxelNode->GetOutputPinIndex(PinId); + if (Index != -1) + { + if (!HoverTextOut.IsEmpty()) HoverTextOut += "\n"; + HoverTextOut += VoxelNode->GetOutputPinToolTip(Index); + } + } + } +} + +void UVoxelGraphNode::PostLoad() +{ + Super::PostLoad(); + + // Fixup any VoxelNode back pointers that may be out of date + if (VoxelNode) + { + VoxelNode->GraphNode = this; + } + + for (int32 Index = 0; Index < Pins.Num(); ++Index) + { + UEdGraphPin* Pin = Pins[Index]; + Pin->PinType.bIsConst = false; + Pin->PinType.ContainerType = EPinContainerType::None; // Remove preview + if (Pin->PinName.IsNone()) + { + // Makes sure pin has a name for lookup purposes but user will never see it + if (Pin->Direction == EGPD_Input) + { + Pin->PinName = CreateUniquePinName(TEXT("Input")); + } + else + { + Pin->PinName = CreateUniquePinName(TEXT("Output")); + } + Pin->PinFriendlyName = FText::FromString(TEXT(" ")); + } + } +} + +void UVoxelGraphNode::PostEditImport() +{ + // Make sure this VoxelNode is owned by the generator it's being pasted into. + ResetVoxelNodeOwner(); +} + +void UVoxelGraphNode::PostDuplicate(bool bDuplicateForPIE) +{ + Super::PostDuplicate(bDuplicateForPIE); + + if (!bDuplicateForPIE) + { + CreateNewGuid(); + } +} + +void UVoxelGraphNode::ResetVoxelNodeOwner() +{ + if (VoxelNode) + { + UVoxelGraphGenerator* Generator = CastChecked(GetGraph())->GetGenerator(); + + if (VoxelNode->GetOuter() != Generator) + { + // Ensures VoxelNode is owned by the generator + VoxelNode->Rename(NULL, Generator, REN_DontCreateRedirectors); + } + + // Set up the back pointer for newly created voxel nodes + VoxelNode->GraphNode = this; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h new file mode 100644 index 00000000..7d63f2d8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h @@ -0,0 +1,84 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphNode_Base.h" +#include "VoxelGraphNode.generated.h" + +class UVoxelNode; + +UCLASS() +class UVoxelGraphNode : public UVoxelGraphNode_Base +{ + GENERATED_BODY() + +public: + UPROPERTY() + UVoxelNode* VoxelNode; + + /** Set the VoxelNode this represents (also assigns this to the VoxelNode in Editor)*/ + void SetVoxelNode(UVoxelNode* InVoxelNode); + /** Fix up the node's owner after being copied */ + void PostCopyNode(); + /** Create a new input pin for this node */ + void CreateInputPin(); + /** Create a new output pin for this node */ + void CreateOutputPin(); + /** Remove a specific input pin from this node and recompile the generator */ + void RemoveInputPin(UEdGraphPin* InGraphPin); + +public: + bool CanSplitPin_Voxel(const UEdGraphPin& Pin) const; + bool CanCombinePin(const UEdGraphPin& Pin) const; + + bool TrySplitPin(UEdGraphPin& Pin, bool bOnlyCheck); + bool TryCombinePin(UEdGraphPin& Pin, bool bOnlyCheck); + + void CombineAll(); + + static bool HasVectorPin(UVoxelNode& Node, EEdGraphPinDirection Direction); + +public: + // UVoxelGraphNodeInterface interface + virtual UVoxelNode* GetVoxelNode() const override { return VoxelNode; } + virtual bool IsOutdated() const override; + // End of UVoxelGraphNodeInterface interface + + // UVoxelGraphNode_Base interface + virtual void CreateInputPins() override; + virtual void CreateOutputPins() override; + virtual void RestoreVectorPins(const TArray& OldInputPins, const TArray& OldOutputPins) override; + virtual bool IsCompact() const override; + virtual FLinearColor GetNodeBodyColor() const override; + virtual void AddInputPin() override; + virtual bool CanAddInputPin() const override; + // End of UVoxelGraphNode_Base interface + + // UEdGraphNode interface + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + virtual FLinearColor GetNodeTitleColor() const override; + virtual void PrepareForCopying() override; +#if ENGINE_MINOR_VERSION < 24 + virtual void GetContextMenuActions(const FGraphNodeContextMenuBuilder& Context) const override; +#endif + virtual FText GetTooltipText() const override; + virtual FString GetDocumentationExcerptName() const override; + virtual bool CanUserDeleteNode() const override; + virtual bool CanDuplicateNode() const override; + virtual bool CanJumpToDefinition() const override; + virtual void JumpToDefinition() const override; + virtual void OnRenameNode(const FString& NewName) override; + virtual void GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const override; + // End of UEdGraphNode interface + + // UObject interface + virtual void PostLoad() override; + virtual void PostEditImport() override; + virtual void PostDuplicate(bool bDuplicateForPIE) override; + // End of UObject interface + +private: + /** Make sure the voxel node is owned by the generator */ + void ResetVoxelNodeOwner(); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNodeFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNodeFactory.h new file mode 100644 index 00000000..82091888 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNodeFactory.h @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "EdGraphUtilities.h" +#include "SGraphNodeKnot.h" +#include "VoxelGraphNodes/SVoxelGraphNode.h" +#include "VoxelGraphNodes/VoxelGraphNode_Knot.h" +#include "VoxelGraphNodes/VoxelGraphNode.h" +#include "VoxelNodes/VoxelParameterNodes.h" +#include "VoxelNodes/VoxelAssetPickerNode.h" + +class FVoxelGraphNodeFactory : public FGraphPanelNodeFactory +{ + virtual TSharedPtr CreateNode(class UEdGraphNode* InNode) const override + { + if (auto* Knot = Cast(InNode)) + { + return SNew(SGraphNodeKnot, Knot); + } + else if (auto* VoxelGraphNodeBase = Cast(InNode)) + { + if (auto* VoxelGraphNode = Cast(InNode)) + { + if (auto* ColorNode = Cast(VoxelGraphNode->VoxelNode)) + { + return SNew(SVoxelColorGraphNode, VoxelGraphNode, ColorNode); + } + if (auto* AssetPickerNode = Cast(VoxelGraphNode->VoxelNode)) + { + return SNew(SVoxelAssetPickerGraphNode, VoxelGraphNode, AssetPickerNode); + } + } + return SNew(SVoxelGraphNode, VoxelGraphNodeBase); + } + return nullptr; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.cpp new file mode 100644 index 00000000..70de82f9 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.cpp @@ -0,0 +1,330 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphNode_Base.h" +#include "VoxelGraphSchema.h" +#include "VoxelPinCategory.h" +#include "EdGraph/EdGraphSchema.h" +#include "EdGraph/EdGraphPin.h" +#include "EdGraph/EdGraph.h" + +TArray UVoxelGraphNode_Base::GetOutputPins() const +{ + return Pins.FilterByPredicate([&](const UEdGraphPin* Pin) { return Pin->Direction == EGPD_Output; }); +} + +TArray UVoxelGraphNode_Base::GetInputPins() const +{ + return Pins.FilterByPredicate([&](const UEdGraphPin* Pin) { return Pin->Direction == EGPD_Input; }); +} + +UEdGraphPin* UVoxelGraphNode_Base::GetInputPin(int32 InputIndex) +{ + check(InputIndex >= 0 && InputIndex < GetInputCount()); + + for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Input) + { + if (InputIndex == FoundInputs) + { + return Pins[PinIndex]; + } + else + { + FoundInputs++; + } + } + } + + return nullptr; +} + +UEdGraphPin* UVoxelGraphNode_Base::GetOutputPin(int32 OutputIndex) +{ + check(OutputIndex >= 0 && OutputIndex < GetOutputCount()); + + for (int32 PinIndex = 0, FoundOutputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Output) + { + if (OutputIndex == FoundOutputs) + { + return Pins[PinIndex]; + } + else + { + FoundOutputs++; + } + } + } + + return nullptr; +} + +int32 UVoxelGraphNode_Base::GetInputPinIndex(const UEdGraphPin* Pin) const +{ + for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Input) + { + if (Pins[PinIndex] == Pin) + { + return FoundInputs; + } + else + { + FoundInputs++; + } + } + } + + return -1; +} + +int32 UVoxelGraphNode_Base::GetOutputPinIndex(const UEdGraphPin* Pin) const +{ + for (int32 PinIndex = 0, FoundOutputs = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Output) + { + if (Pins[PinIndex] == Pin) + { + return FoundOutputs; + } + else + { + FoundOutputs++; + } + } + } + + return -1; +} + +int32 UVoxelGraphNode_Base::GetInputCount() const +{ + int32 InputCount = 0; + + for (int32 PinIndex = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Input) + { + InputCount++; + } + } + + return InputCount; +} + +int32 UVoxelGraphNode_Base::GetOutputCount() const +{ + int32 OutputCount = 0; + + for (int32 PinIndex = 0; PinIndex < Pins.Num(); PinIndex++) + { + if (Pins[PinIndex]->Direction == EGPD_Output) + { + OutputCount++; + } + } + + return OutputCount; +} + +void UVoxelGraphNode_Base::InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin, TSet& OutNodeList) +{ + const UVoxelGraphSchema* Schema = CastChecked(GetSchema()); + + // The pin we are creating from already has a connection that needs to be broken. We want to "insert" the new node in between, so that the output of the new node is hooked up too + UEdGraphPin* OldLinkedPin = FromPin->LinkedTo[0]; + check(OldLinkedPin); + + FromPin->BreakAllPinLinks(); + + // Hook up the old linked pin to the first valid output pin on the new node + for (int32 OutpinPinIdx = 0; OutpinPinIdx < Pins.Num(); OutpinPinIdx++) + { + UEdGraphPin* OutputExecPin = Pins[OutpinPinIdx]; + check(OutputExecPin); + if (ECanCreateConnectionResponse::CONNECT_RESPONSE_MAKE == Schema->CanCreateConnection(OldLinkedPin, OutputExecPin).Response) + { + if (Schema->TryCreateConnection(OldLinkedPin, OutputExecPin)) + { + OutNodeList.Add(OldLinkedPin->GetOwningNode()); + OutNodeList.Add(this); + } + break; + } + } + + if (Schema->TryCreateConnection(FromPin, NewLinkPin)) + { + OutNodeList.Add(FromPin->GetOwningNode()); + OutNodeList.Add(this); + } +} + +void UVoxelGraphNode_Base::AllocateDefaultPins() +{ + check(Pins.Num() == 0); + + CreateInputPins(); + CreateOutputPins(); +} + +inline bool MovePin(UEdGraphPin* OldPin, UEdGraphPin* NewPin) +{ + const auto OldType = FVoxelPinCategory::FromString(OldPin->PinType.PinCategory); + const auto NewType = FVoxelPinCategory::FromString(NewPin->PinType.PinCategory); + if (OldType == NewType || + OldType == EVoxelPinCategory::Wildcard || + NewType == EVoxelPinCategory::Wildcard) + { + NewPin->MovePersistentDataFromOldPin(*OldPin); + return true; + } + else + { + return false; + } +} + +inline void MovePins(const TArray& OldPins, const TArray& NewPins) +{ + TSet MovedOldPins; + TSet MovedNewPins; + + // Tricky case: renaming a macro node pin + + // First try the PinId + for (auto* OldPin : OldPins) + { + UEdGraphPin* const * NewPinPtr = nullptr; + if (!NewPinPtr) + { + NewPinPtr = NewPins.FindByPredicate([&](auto* NewPin) { return NewPin->PinId == OldPin->PinId; }); + } + if (NewPinPtr) + { + auto* NewPin = *NewPinPtr; + if (!MovedNewPins.Contains(NewPin) && MovePin(OldPin, NewPin)) + { + MovedOldPins.Add(OldPin); + MovedNewPins.Add(NewPin); + } + } + } + + // Else use the index + for (int32 Index = 0; Index < OldPins.Num(); Index++) + { + auto* OldPin = OldPins[Index]; + if (!MovedOldPins.Contains(OldPin) && NewPins.IsValidIndex(Index)) + { + auto* NewPin = NewPins[Index]; + if (!MovedNewPins.Contains(NewPin) && MovePin(OldPin, NewPin)) + { + MovedOldPins.Add(OldPin); + MovedNewPins.Add(NewPin); + } + } + } +} + +void UVoxelGraphNode_Base::ReconstructNode() +{ + Super::ReconstructNode(); + + if (!ensure(this)) + { + return; + } + Modify(); + + // Break any links to 'orphan' pins + for (auto& Pin : Pins) + { + for (auto& OtherPin : Pin->LinkedTo) + { + // If we are linked to a pin that its owner doesn't know about, break that link + if (!OtherPin->GetOwningNode()->Pins.Contains(OtherPin)) + { + Pin->LinkedTo.Remove(OtherPin); + } + } + } + + // Store the old Input and Output pins + const TArray OldInputPins = GetInputPins(); + const TArray OldOutputPins = GetOutputPins(); + + // Move the existing pins to a saved array + const TArray OldPins = Pins; + Pins.Reset(); + + // Recreate the new pins + AllocateDefaultPins(); + // Restore vector pins + RestoreVectorPins(OldInputPins, OldOutputPins); + + // Get new Input and Output pins + const TArray NewInputPins = GetInputPins(); + const TArray NewOutputPins = GetOutputPins(); + + MovePins(OldInputPins, NewInputPins); + MovePins(OldOutputPins, NewOutputPins); + + // Throw away the original pins + for (UEdGraphPin* OldPin : OldPins) + { + OldPin->Modify(); + UEdGraphNode::DestroyPin(OldPin); + } + + GetGraph()->NotifyGraphChanged(); +} + +void UVoxelGraphNode_Base::AutowireNewNode(UEdGraphPin* FromPin) +{ + if (FromPin != NULL) + { + const UVoxelGraphSchema* Schema = CastChecked(GetSchema()); + + TSet NodeList; + + // auto-connect from dragged pin to first compatible pin on the new node + for (int32 i = 0; i < Pins.Num(); i++) + { + UEdGraphPin* Pin = Pins[i]; + check(Pin); + FPinConnectionResponse Response = Schema->CanCreateConnection(FromPin, Pin); + + if (ECanCreateConnectionResponse::CONNECT_RESPONSE_MAKE == Response.Response) + { + if (Schema->TryCreateConnection(FromPin, Pin)) + { + NodeList.Add(FromPin->GetOwningNode()); + NodeList.Add(this); + } + break; + } + else if (ECanCreateConnectionResponse::CONNECT_RESPONSE_BREAK_OTHERS_A == Response.Response) + { + InsertNewNode(FromPin, Pin, NodeList); + break; + } + } + + // Send all nodes that received a new pin connection a notification + for (auto It = NodeList.CreateConstIterator(); It; ++It) + { + UEdGraphNode* Node = (*It); + Node->NodeConnectionListChanged(); + } + } +} + +bool UVoxelGraphNode_Base::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const +{ + return Schema->IsA(UVoxelGraphSchema::StaticClass()); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.h new file mode 100644 index 00000000..ea5f8845 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.h @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "VoxelGraphNode_Base.generated.h" + +class UEdGraphPin; +class UEdGraphSchema; + +UCLASS() +class UVoxelGraphNode_Base : public UVoxelGraphNodeInterface +{ + GENERATED_BODY() + +public: + //~ Begin UVoxelGraphNode_Base Interface + virtual void CreateInputPins() {} + virtual void CreateOutputPins() {} + virtual void RestoreVectorPins(const TArray& OldInputPins, const TArray& OldOutputPins) {} + + virtual bool IsCompact() const { return false; } + virtual FLinearColor GetNodeBodyColor() const { return FLinearColor::White; } + + /** Add an input pin to this node and recompile the generator */ + virtual void AddInputPin() {} + /** Checks whether an input can be added to this node */ + virtual bool CanAddInputPin() const { return false; } + //~ End UVoxelGraphNode_Base Interface + + TArray GetInputPins() const; + TArray GetOutputPins() const; + + UEdGraphPin* GetInputPin(int32 InputIndex); + UEdGraphPin* GetOutputPin(int32 OutputIndex); + + int32 GetInputPinIndex(const UEdGraphPin* Pin) const; + int32 GetOutputPinIndex(const UEdGraphPin* Pin) const; + + int32 GetInputCount() const; + int32 GetOutputCount() const; + + /** + * Handles inserting the node between the FromPin and what the FromPin was original connected to + * + * @param FromPin The pin this node is being spawned from + * @param NewLinkPin The new pin the FromPin will connect to + * @param OutNodeList Any nodes that are modified will get added to this list for notification purposes + */ + void InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin, TSet& OutNodeList); + + //~ Begin UEdGraphNode Interface. + virtual void AllocateDefaultPins() final override; + virtual void ReconstructNode() override; + virtual void AutowireNewNode(UEdGraphPin* FromPin) override; + virtual bool CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const override; + //~ End UEdGraphNode Interface. +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.cpp new file mode 100644 index 00000000..91de6baf --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.cpp @@ -0,0 +1,238 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphNode_Knot.h" +#include "EdGraph/EdGraphPin.h" +#include "VoxelPinCategory.h" + +const static FName Wildcard(FVoxelPinCategory::GetName(EVoxelPinCategory::Wildcard)); + +void UVoxelGraphNode_Knot::CreateInputPins() +{ + UEdGraphPin* InputPin = CreatePin(EGPD_Input, Wildcard, "InputPin"); + InputPin->bDefaultValueIsIgnored = true; +} + +void UVoxelGraphNode_Knot::CreateOutputPins() +{ + CreatePin(EGPD_Output, Wildcard, "OutputPin"); +} + +void UVoxelGraphNode_Knot::ReconstructNode() +{ + Super::ReconstructNode(); + PropagatePinType(); +} + +FText UVoxelGraphNode_Knot::GetTooltipText() const +{ + return VOXEL_LOCTEXT("Reroute Node (reroutes wires)"); +} + +FText UVoxelGraphNode_Knot::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + if (TitleType == ENodeTitleType::EditableTitle) + { + return FText::FromString(NodeComment); + } + else if (TitleType == ENodeTitleType::MenuTitle) + { + return VOXEL_LOCTEXT("Add Reroute Node..."); + } + else + { + return VOXEL_LOCTEXT("Reroute Node"); + } +} + +bool UVoxelGraphNode_Knot::CanSplitPin(const UEdGraphPin* Pin) const +{ + return false; +} + +void UVoxelGraphNode_Knot::PropagatePinType() +{ + UEdGraphPin* InputPin = GetInputPin(); + UEdGraphPin* OutputPin = GetOutputPin(); + + for (UEdGraphPin* Inputs : InputPin->LinkedTo) + { + if (Inputs->PinType.PinCategory != Wildcard) + { + PropagatePinTypeFromInput(); + return; + } + } + + for (UEdGraphPin* Outputs : OutputPin->LinkedTo) + { + if (Outputs->PinType.PinCategory != Wildcard) + { + PropagatePinTypeFromOutput(); + return; + } + } + + // if all inputs/outputs are wildcards, still favor the inputs first (propagate array/reference/etc. state) + if (InputPin->LinkedTo.Num() > 0) + { + // If we can't mirror from output type, we should at least get the type information from the input connection chain + PropagatePinTypeFromInput(); + } + else if (OutputPin->LinkedTo.Num() > 0) + { + // Try to mirror from output first to make sure we get appropriate member references + PropagatePinTypeFromOutput(); + } + else + { + // Revert to wildcard + InputPin->BreakAllPinLinks(); + InputPin->PinType.ResetToDefaults(); + InputPin->PinType.PinCategory = Wildcard; + + OutputPin->BreakAllPinLinks(); + OutputPin->PinType.ResetToDefaults(); + OutputPin->PinType.PinCategory = Wildcard; + } +} + +TArray UVoxelGraphNode_Knot::GetAllInputPins() +{ + TArray KnotRecursiveInputPins; + { + TArray KnotsToProcess; + KnotsToProcess.Add(this); + + while (KnotsToProcess.Num() > 0) + { + auto CurrentKnot = KnotsToProcess.Pop(); + auto InputPin = CurrentKnot->GetInputPin(); + for (auto& Pin : InputPin->LinkedTo) + { + auto Knot = Cast(Pin->GetOwningNode()); + if (Knot) + { + KnotsToProcess.Add(Knot); + } + else + { + KnotRecursiveInputPins.Add(Pin); + } + } + } + } + return KnotRecursiveInputPins; +} + +TArray UVoxelGraphNode_Knot::GetAllOutputPins() +{ + TArray KnotRecursiveOutputPins; + { + TArray KnotsToProcess; + KnotsToProcess.Add(this); + + while (KnotsToProcess.Num() > 0) + { + auto CurrentKnot = KnotsToProcess.Pop(); + auto OutputPin = CurrentKnot->GetOutputPin(); + for (auto& Pin : OutputPin->LinkedTo) + { + auto Knot = Cast(Pin->GetOwningNode()); + if (Knot) + { + KnotsToProcess.Add(Knot); + } + else + { + KnotRecursiveOutputPins.Add(Pin); + } + } + } + } + return KnotRecursiveOutputPins; +} + +void UVoxelGraphNode_Knot::PropagatePinTypeFromInput() +{ + if (bRecursionGuard) + { + return; + } + // Set the type of the pin based on input connections. + // We have to move up the chain of linked reroute nodes until we reach a node + // with type information before percolating that information down. + UEdGraphPin* MyInputPin = GetInputPin(); + UEdGraphPin* MyOutputPin = GetOutputPin(); + + TGuardValue RecursionGuard(bRecursionGuard, true); + + for (UEdGraphPin* InPin : MyInputPin->LinkedTo) + { + if (UVoxelGraphNode_Knot* KnotNode = Cast(InPin->GetOwningNode())) + { + KnotNode->PropagatePinTypeFromInput(); + } + } + + UEdGraphPin* TypeSource = MyInputPin->LinkedTo.Num() ? MyInputPin->LinkedTo[0] : nullptr; + if (TypeSource) + { + MyInputPin->PinType = TypeSource->PinType; + MyOutputPin->PinType = TypeSource->PinType; + } +} + +void UVoxelGraphNode_Knot::PropagatePinTypeFromOutput() +{ + if (bRecursionGuard) + { + return; + } + // Set the type of the pin based on the output connection, and then percolate + // that type information up until we no longer reach another Reroute node + UEdGraphPin* InputPin = GetInputPin(); + UEdGraphPin* OutputPin = GetOutputPin(); + + TGuardValue RecursionGuard(bRecursionGuard, true); + + for (UEdGraphPin* InPin : OutputPin->LinkedTo) + { + if (UVoxelGraphNode_Knot* KnotNode = Cast(InPin->GetOwningNode())) + { + KnotNode->PropagatePinTypeFromOutput(); + } + } + + UEdGraphPin* TypeSource = OutputPin->LinkedTo.Num() ? OutputPin->LinkedTo[0] : nullptr; + if (TypeSource) + { + InputPin->PinType = TypeSource->PinType; + OutputPin->PinType = TypeSource->PinType; + } +} + +bool UVoxelGraphNode_Knot::ShouldOverridePinNames() const +{ + return true; +} + +FText UVoxelGraphNode_Knot::GetPinNameOverride(const UEdGraphPin& Pin) const +{ + // Keep the pin size tiny + return FText::GetEmpty(); +} + +void UVoxelGraphNode_Knot::OnRenameNode(const FString& NewName) +{ + NodeComment = NewName; +} + +UEdGraphPin* UVoxelGraphNode_Knot::GetPassThroughPin(const UEdGraphPin* FromPin) const +{ + if (FromPin && Pins.Contains(FromPin)) + { + return FromPin == Pins[0] ? Pins[1] : Pins[0]; + } + + return nullptr; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.h new file mode 100644 index 00000000..eb3675e5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.h @@ -0,0 +1,55 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphNode.h" +#include "VoxelGraphNode_Knot.generated.h" + +UCLASS() +class UVoxelGraphNode_Knot : public UVoxelGraphNode +{ + GENERATED_BODY() + +public: + // UEdGraphNode interface + + virtual void CreateInputPins() override; + virtual void CreateOutputPins() override; + + virtual void ReconstructNode() override; + virtual FText GetTooltipText() const override; + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + virtual bool ShouldOverridePinNames() const override; + virtual FText GetPinNameOverride(const UEdGraphPin& Pin) const override; + virtual void OnRenameNode(const FString& NewName) override; + virtual bool CanSplitPin(const UEdGraphPin* Pin) const override; + virtual bool IsCompilerRelevant() const override { return false; } + virtual UEdGraphPin* GetPassThroughPin(const UEdGraphPin* FromPin) const override; + virtual bool ShouldDrawNodeAsControlPointOnly(int32& OutInputPinIndex, int32& OutOutputPinIndex) const override { OutInputPinIndex = 0; OutOutputPinIndex = 1; return true; } + // End of UEdGraphNode interface + + virtual bool IsCompact() const override { return true; } + + UEdGraphPin* GetInputPin() const + { + return Pins[0]; + } + + UEdGraphPin* GetOutputPin() const + { + return Pins[1]; + } + + void PropagatePinType(); + + TArray GetAllInputPins(); + TArray GetAllOutputPins(); + +private: + void PropagatePinTypeFromInput(); + void PropagatePinTypeFromOutput(); + + /** Recursion guard boolean to prevent PropagatePinType from infinitely recursing if you manage to create a loop of knots */ + bool bRecursionGuard; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Root.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Root.h new file mode 100644 index 00000000..df626935 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Root.h @@ -0,0 +1,29 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphNode_Base.h" +#include "VoxelPinCategory.h" +#include "VoxelGraphNode_Root.generated.h" + +UCLASS() +class UVoxelGraphNode_Root : public UVoxelGraphNode_Base +{ + GENERATED_BODY() + +public: + // UEdGraphNode interface + virtual bool CanUserDeleteNode() const override { return false; }; + virtual bool CanDuplicateNode() const override { return false; } + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override { return FText::FromString(TEXT("Start")); }; + virtual FLinearColor GetNodeTitleColor() const override { return FLinearColor::Red; }; + // End of UEdGraphNode interface + + // UVoxelGraphNode_Base interface + virtual void CreateOutputPins() override + { + CreatePin(EGPD_Output, FVoxelPinCategory::GetName(EVoxelPinCategory::Exec), FName(), nullptr, FName(" ")); + } + // End of UVoxelGraphNode_Base interface +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphPanelPinFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphPanelPinFactory.h new file mode 100644 index 00000000..ff2ffeab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphPanelPinFactory.h @@ -0,0 +1,52 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelGraphSchema.h" +#include "VoxelPinCategory.h" + +#include "EdGraphUtilities.h" +#include "KismetPins/SGraphPinExec.h" +#include "KismetPins/SGraphPinBool.h" +#include "KismetPins/SGraphPinInteger.h" +#include "KismetPins/SGraphPinNum.h" +#include "KismetPins/SGraphPinColor.h" +#include "KismetPins/SGraphPinVector.h" + +class FVoxelGraphPanelPinFactory : public FGraphPanelPinFactory +{ + virtual TSharedPtr CreatePin(UEdGraphPin* InPin) const override + { + if (InPin->GetSchema()->IsA(UVoxelGraphSchema::StaticClass())) + { + const EVoxelPinCategory Category = FVoxelPinCategory::FromString(InPin->PinType.PinCategory); + + switch (Category) + { + case EVoxelPinCategory::Exec: + return SNew(SGraphPinExec, InPin); + case EVoxelPinCategory::Boolean: + return SNew(SGraphPinBool, InPin); + case EVoxelPinCategory::Int: + return SNew(SGraphPinInteger, InPin); + case EVoxelPinCategory::Float: + return SNew(SGraphPinNum, InPin); + case EVoxelPinCategory::Material: + return nullptr; + case EVoxelPinCategory::Color: + return SNew(SGraphPinColor, InPin); + case EVoxelPinCategory::Seed: + return SNew(SGraphPinInteger, InPin); + case EVoxelPinCategory::Wildcard: + return SNew(SGraphPin, InPin); + case EVoxelPinCategory::Vector: + return SNew(SGraphPinVector, InPin); + default: + check(false); + return nullptr; + } + } + return nullptr; + } +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp new file mode 100644 index 00000000..f5881882 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp @@ -0,0 +1,1177 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphSchema.h" +#include "VoxelEdGraph.h" +#include "VoxelGraphEditorUtilities.h" +#include "VoxelGraphEditorCommands.h" +#include "IVoxelGraphEditorToolkit.h" + +#include "VoxelAssets/VoxelHeightmapAsset.h" +#include "VoxelAssets/VoxelDataAsset.h" + +#include "VoxelNode.h" +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelNodes/VoxelLocalVariables.h" +#include "VoxelNodes/VoxelExecNodes.h" +#include "VoxelNodes/VoxelHeightmapSamplerNode.h" +#include "VoxelNodes/VoxelDataAssetSamplerNode.h" +#include "VoxelNodes/VoxelTextureSamplerNode.h" +#include "VoxelNodes/VoxelCurveNodes.h" +#include "VoxelNodes/VoxelMathNodes.h" +#include "VoxelNodes/VoxelGeneratorSamplerNodes.h" + +#include "VoxelGraphNodes/VoxelGraphNode_Knot.h" +#include "VoxelGraphNodes/VoxelGraphNode.h" +#include "VoxelGraphNodes/VoxelGraphNode_Root.h" + +#include "ScopedTransaction.h" +#include "EdGraphNode_Comment.h" +#include "GraphEditorSettings.h" +#include "Layout/SlateRect.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Commands/GenericCommands.h" +#include "UObject/UObjectIterator.h" +#include "GraphEditorActions.h" +#include "AssetRegistryModule.h" +#include "Modules/ModuleManager.h" +#include "Engine/StreamableManager.h" +#include "Curves/CurveFloat.h" +#include "Curves/CurveLinearColor.h" + +#if ENGINE_MINOR_VERSION >= 24 +#include "ToolMenu.h" +#include "ToolMenuSection.h" +#endif + +UEdGraphNode* FVoxelGraphSchemaAction_NewNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + check(VoxelNodeClass); + UVoxelGraphGenerator* Generator = CastChecked(ParentGraph)->GetGenerator(); + + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New voxel node")); + + UVoxelNode* NewNode = Generator->ConstructNewNode(VoxelNodeClass, Location, bSelectNewNode); + NewNode->GraphNode->ReconstructNode(); + + // Autowire before combining if not vector + if (FromPin && FVoxelPinCategory::FromString(FromPin->PinType.PinCategory) != EVoxelPinCategory::Vector) + { + NewNode->GraphNode->AutowireNewNode(FromPin); + } + + // Combine all vector pins on spawn + if (auto* VoxelNode = Cast(NewNode->GraphNode)) + { + VoxelNode->CombineAll(); + } + + // Autowire after combining if vector + if (FromPin && FVoxelPinCategory::FromString(FromPin->PinType.PinCategory) == EVoxelPinCategory::Vector) + { + NewNode->GraphNode->AutowireNewNode(FromPin); + } + + // Else the voxel pin arrays are invalid + Generator->CompileVoxelNodesFromGraphNodes(); + + return NewNode->GraphNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewMacroNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + check(Macro); + UVoxelGraphGenerator* Generator = CastChecked(ParentGraph)->GetGenerator(); + + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New macro node")); + + UVoxelGraphMacroNode* NewNode = Generator->ConstructNewNode(Location, bSelectNewNode); + NewNode->Macro = Macro; + NewNode->GraphNode->ReconstructNode(); + + // Autowire before combining if not vector + if (FromPin && FVoxelPinCategory::FromString(FromPin->PinType.PinCategory) != EVoxelPinCategory::Vector) + { + NewNode->GraphNode->AutowireNewNode(FromPin); + } + + // Combine all vector pins on spawn + if (auto* VoxelNode = Cast(NewNode->GraphNode)) + { + VoxelNode->CombineAll(); + } + + // Autowire after combining if vector + if (FromPin && FVoxelPinCategory::FromString(FromPin->PinType.PinCategory) == EVoxelPinCategory::Vector) + { + NewNode->GraphNode->AutowireNewNode(FromPin); + } + + // Else the voxel pin arrays are invalid + Generator->CompileVoxelNodesFromGraphNodes(); + + return NewNode->GraphNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewLocalVariableDeclaration::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + UVoxelGraphGenerator* Generator = CastChecked(ParentGraph)->GetGenerator(); + + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New local variable declaration")); + + UVoxelLocalVariableDeclaration* Declaration = Generator->ConstructNewNode(Location, bSelectNewNode); + Declaration->SetCategory(PinCategory); + + if (!DefaultName.IsNone()) + { + Declaration->Name = DefaultName; + } + + Declaration->GraphNode->ReconstructNode(); + Declaration->GraphNode->AutowireNewNode(FromPin); + + // Else the voxel pin arrays are invalid + Generator->CompileVoxelNodesFromGraphNodes(); + + return Declaration->GraphNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewLocalVariableUsage::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + check(Declaration); + UVoxelGraphGenerator* Generator = CastChecked(ParentGraph)->GetGenerator(); + + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New local variable usage")); + + UVoxelLocalVariableUsage* Usage = Generator->ConstructNewNode(Location, bSelectNewNode); + Usage->Declaration = Declaration; + Usage->DeclarationGuid = Declaration->VariableGuid; + + Usage->GraphNode->ReconstructNode(); + Usage->GraphNode->AutowireNewNode(FromPin); + + // Else the voxel pin arrays are invalid + Generator->CompileVoxelNodesFromGraphNodes(); + + return Usage->GraphNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewSetterNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + UVoxelGraphGenerator* Generator = CastChecked(ParentGraph)->GetGenerator(); + + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New setter node")); + + UVoxelNode_SetNode* NewNode = Generator->ConstructNewNode(Location, bSelectNewNode); + NewNode->SetIndex(Index); + NewNode->GraphNode->ReconstructNode(); + NewNode->GraphNode->AutowireNewNode(FromPin); + + // Else the voxel pin arrays are invalid + Generator->CompileVoxelNodesFromGraphNodes(); + + return NewNode->GraphNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewKnotNode::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("New reroute node")); + ParentGraph->Modify(); + + FGraphNodeCreator KnotNodeCreator(*ParentGraph); + UVoxelGraphNode_Knot* KnotNode = KnotNodeCreator.CreateNode(bSelectNewNode); + KnotNodeCreator.Finalize(); + + KnotNode->NodePosX = Location.X; + KnotNode->NodePosY = Location.Y; + + KnotNode->AutowireNewNode(FromPin); + KnotNode->PropagatePinType(); + + return KnotNode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_NewComment::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + // Add menu item for creating comment boxes + UEdGraphNode_Comment* CommentTemplate = NewObject(); + + FVector2D SpawnLocation = Location; + + FSlateRect Bounds; + if (FVoxelGraphEditorUtilities::GetBoundsForSelectedNodes(ParentGraph, Bounds, 50.0f)) + { + CommentTemplate->SetBounds(Bounds); + SpawnLocation.X = CommentTemplate->NodePosX; + SpawnLocation.Y = CommentTemplate->NodePosY; + } + + return FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(ParentGraph, CommentTemplate, SpawnLocation); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +UEdGraphNode* FVoxelGraphSchemaAction_Paste::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) +{ + FVoxelGraphEditorUtilities::PasteNodesHere(ParentGraph, Location); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool UVoxelGraphSchema::ConnectionCausesLoop(const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const +{ + UEdGraphNode* const StartNode = OutputPin->GetOwningNode(); + + TSet ProcessedNodes; + + TArray NodesToProcess; + NodesToProcess.Add(InputPin->GetOwningNode()); + + while (NodesToProcess.Num() > 0) + { + UEdGraphNode* Node = NodesToProcess.Pop(false); + if (ProcessedNodes.Contains(Node)) + { + continue; + } + ProcessedNodes.Add(Node); + + if (auto* PortalInputGraphNode = Cast(Node)) + { + if (auto* Declaration = Cast(PortalInputGraphNode->VoxelNode)) + { + if (!ensure(Declaration->Graph)) + { + continue; + } + for (auto* OtherNode : Declaration->Graph->AllNodes) + { + auto* Usage = Cast(OtherNode); + if (Usage && Usage->Declaration == Declaration) + { + if (StartNode == Usage->GraphNode) + { + return true; + } + NodesToProcess.Add(Usage->GraphNode); + } + } + } + } + + for (UEdGraphPin* Pin : Node->GetAllPins()) + { + if (Pin->Direction == EGPD_Output) + { + for (auto& LPin : Pin->LinkedTo) + { + check(LPin->Direction == EGPD_Input); + + UEdGraphNode* NewNode = LPin->GetOwningNode(); + check(NewNode); + + if (StartNode == NewNode) + { + return true; + } + NodesToProcess.Add(NewNode); + } + } + } + } + + return false; +} + +void UVoxelGraphSchema::GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder) const +{ + GetAllVoxelNodeActions(ActionMenuBuilder); + GetCommentAction(ActionMenuBuilder); +} + +void UVoxelGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const +{ + GetAllVoxelNodeActions(ContextMenuBuilder, ContextMenuBuilder.CurrentGraph); + GetCommentAction(ContextMenuBuilder, ContextMenuBuilder.CurrentGraph); + + if (!ContextMenuBuilder.FromPin && FVoxelGraphEditorUtilities::CanPasteNodes(ContextMenuBuilder.CurrentGraph)) + { + TSharedPtr NewAction(new FVoxelGraphSchemaAction_Paste(FText::GetEmpty(), VOXEL_LOCTEXT("Paste here"), FText::GetEmpty(), 0)); + ContextMenuBuilder.AddAction(NewAction); + } +} + +bool UVoxelGraphSchema::TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const +{ + bool bModified = UEdGraphSchema::TryCreateConnection(A, B); + + auto AK = Cast(A->GetOwningNode()); + auto BK = Cast(B->GetOwningNode()); + if (AK) + { + AK->PropagatePinType(); + } + if (BK) + { + BK->PropagatePinType(); + } + + if (bModified) + { + CastChecked(A->GetOwningNode()->GetGraph())->GetGenerator()->CompileVoxelNodesFromGraphNodes(); + } + + return bModified; +} + +void UVoxelGraphSchema::TrySetDefaultValue( + UEdGraphPin& Pin, + const FString& NewDefaultValue +#if ENGINE_MINOR_VERSION >= 24 + , bool bMarkAsModified +#endif +) const +{ + FString DefaultValue = NewDefaultValue; + + auto Node = Cast(Pin.GetOwningNode()); + if (Node && Node->VoxelNode) + { + int32 Index = Node->GetInputPinIndex(&Pin); + if (Index >= 0) + { + auto Category = FVoxelPinCategory::FromString(Pin.PinType.PinCategory); + if (Category == EVoxelPinCategory::Float) + { + float Value = FCString::Atof(*DefaultValue); + auto Bounds = Node->VoxelNode->GetInputPinDefaultValueBounds(Index); + + if (Bounds.Min.IsSet()) + { + Value = FMath::Max(Bounds.Min.GetValue(), Value); + } + if (Bounds.Max.IsSet()) + { + Value = FMath::Min(Bounds.Max.GetValue(), Value); + } + + DefaultValue = FString::SanitizeFloat(Value); + } + else if (Category == EVoxelPinCategory::Int) + { + int32 Value = FCString::Atoi(*DefaultValue); + auto Bounds = Node->VoxelNode->GetInputPinDefaultValueBounds(Index); + + if (Bounds.Min.IsSet()) + { + Value = FMath::Max(FMath::RoundToInt(Bounds.Min.GetValue()), Value); + } + if (Bounds.Max.IsSet()) + { + Value = FMath::Min(FMath::RoundToInt(Bounds.Max.GetValue()), Value); + } + + DefaultValue = FString::FromInt(Value); + } + } + } + + Super::TrySetDefaultValue(Pin, DefaultValue); + + CastChecked(Pin.GetOwningNode()->GetGraph())->GetGenerator()->CompileVoxelNodesFromGraphNodes(); +} + +bool UVoxelGraphSchema::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* PinA, UEdGraphPin* PinB) const +{ + if (PinA->Direction == EGPD_Input) + { + //Swap so that A is the from pin and B is the to pin. + UEdGraphPin* Temp = PinA; + PinA = PinB; + PinB = Temp; + } + + EVoxelPinCategory AType = FVoxelPinCategory::FromString(PinA->PinType.PinCategory); + EVoxelPinCategory BType = FVoxelPinCategory::FromString(PinB->PinType.PinCategory); + + if (AType != BType && (AType == EVoxelPinCategory::Float || AType == EVoxelPinCategory::Int) && (BType == EVoxelPinCategory::Float || BType == EVoxelPinCategory::Int)) + { + UEdGraphNode* ANode = PinA->GetOwningNode(); + UEdGraphNode* BNode = PinB->GetOwningNode(); + UVoxelEdGraph* Graph = CastChecked(ANode->GetGraph()); + UVoxelGraphGenerator* Generator = Graph->GetGenerator(); + + // Since we'll be adding a node, make sure to modify the graph itself. + Graph->Modify(); + UVoxelNode* ConvertNode; + FVector2D Position((ANode->NodePosX + BNode->NodePosX) / 2, (ANode->NodePosY + BNode->NodePosY) / 2); + if (AType == EVoxelPinCategory::Int) + { + ConvertNode = Generator->ConstructNewNode(Position, false); + } + else + { + ConvertNode = Generator->ConstructNewNode(Position, false); + } + + UVoxelGraphNode* ConvertGraphNode = CastChecked(ConvertNode->GraphNode); + + auto InputPin = ConvertGraphNode->GetInputPin(0); + auto OutputPin = ConvertGraphNode->GetOutputPin(0); + + check(InputPin->PinType.PinCategory == PinA->PinType.PinCategory); + check(OutputPin->PinType.PinCategory == PinB->PinType.PinCategory); + + if (!UEdGraphSchema::TryCreateConnection(PinA, InputPin)) + { + Graph->RemoveNode(ConvertGraphNode); + return false; + } + if (!UEdGraphSchema::TryCreateConnection(OutputPin, PinB)) + { + InputPin->BreakAllPinLinks(); + Graph->RemoveNode(ConvertGraphNode); + return false; + } + return true; + } + else + { + return false; + } +} + +TArray UVoxelGraphSchema::VoxelNodeClasses; +bool UVoxelGraphSchema::bVoxelNodeClassesInitialized = false; + +FLinearColor UVoxelGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const +{ + EVoxelPinCategory Category = FVoxelPinCategory::FromString(PinType.PinCategory); + const UGraphEditorSettings* Settings = GetDefault(); + + if (Category == EVoxelPinCategory::Exec) + { + return Settings->ExecutionPinTypeColor; + } + else if (Category == EVoxelPinCategory::Float) + { + return Settings->FloatPinTypeColor; + } + else if (Category == EVoxelPinCategory::Boolean) + { + return Settings->BooleanPinTypeColor; + } + else if (Category == EVoxelPinCategory::Int) + { + return Settings->IntPinTypeColor; + } + else if (Category == EVoxelPinCategory::Material) + { + return Settings->ObjectPinTypeColor; + } + else if (Category == EVoxelPinCategory::Color) + { + return Settings->StructPinTypeColor; + } + else if (Category == EVoxelPinCategory::Seed) + { + return Settings->SoftClassPinTypeColor; + } + else if (Category == EVoxelPinCategory::Vector) + { + return Settings->VectorPinTypeColor; + } + + // Type does not have a defined color! + return Settings->DefaultPinTypeColor; +} + +TSharedPtr UVoxelGraphSchema::GetCreateCommentAction() const +{ + return TSharedPtr(static_cast(new FVoxelGraphSchemaAction_NewComment)); +} + +int32 UVoxelGraphSchema::GetNodeSelectionCount(const UEdGraph* Graph) const +{ + return FVoxelGraphEditorUtilities::GetNumberOfSelectedNodes(Graph); +} + +#if ENGINE_MINOR_VERSION < 24 +void UVoxelGraphSchema::GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) const +{ + if (InGraphPin != NULL) + { + MenuBuilder->BeginSection("EdGraphSchemaPinActions", VOXEL_LOCTEXT("Pin Actions")); + { + if (!bIsDebugging) + { + // Break pin links + if (InGraphPin->LinkedTo.Num() > 0) + { + MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); + } + } + } + MenuBuilder->EndSection(); + } + + Super::GetContextMenuActions(CurrentGraph, InGraphNode, InGraphPin, MenuBuilder, bIsDebugging); +} +#else +void UVoxelGraphSchema::GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const +{ + const UEdGraph* CurrentGraph = Context->Graph; + const UEdGraphNode* InGraphNode = Context->Node; + const UEdGraphPin* InGraphPin = Context->Pin; + + if (InGraphPin) + { + FToolMenuSection& Section = Menu->AddSection("MaterialGraphSchemaPinActions", VOXEL_LOCTEXT("Pin Actions")); + if (InGraphPin->LinkedTo.Num() > 0) + { + Section.AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); + } + + if (auto* Node = Cast(InGraphNode)) + { + if (auto* VoxelNode = Node->VoxelNode) + { + // If on an input that can be deleted, show option + if (InGraphPin->Direction == EGPD_Input) + { + const int32 MinPins = VoxelNode->GetMinInputPins(); + const int32 MaxPins = VoxelNode->GetMaxInputPins(); + if (MinPins != MaxPins && MinPins < VoxelNode->InputPinCount) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().DeleteInput); + } + } + // Preview + if (InGraphPin->Direction == EGPD_Output && FVoxelPinCategory::FromString(InGraphPin->PinType.PinCategory) == EVoxelPinCategory::Float) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().TogglePinPreview); + } + if (Node->CanSplitPin_Voxel(*InGraphPin)) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().SplitPin); + } + if (Node->CanCombinePin(*InGraphPin)) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().CombinePin); + } + } + } + } + else if (InGraphNode) + { + // Add a 'Convert to Local Variables' option to reroute nodes + if (auto* Knot = Cast(InGraphNode)) + { + const EVoxelPinCategory Category = FVoxelPinCategory::FromString(Knot->GetInputPin()->PinType.PinCategory); + if (Category != EVoxelPinCategory::Exec && + Category != EVoxelPinCategory::Wildcard && + Category != EVoxelPinCategory::Vector) + { + FToolMenuSection& Section = Menu->AddSection("MaterialEditorMenu1"); + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().ConvertRerouteToVariables); + } + } + + if (auto* Node = Cast(InGraphNode)) + { + if (auto* VoxelNode = Node->VoxelNode) + { + // Add local variables selection & conversion to reroute nodes + if (VoxelNode->IsA(UVoxelLocalVariableBase::StaticClass())) + { + FToolMenuSection& Section = Menu->AddSection("MaterialEditorMenu1"); + if (VoxelNode->IsA(UVoxelLocalVariableDeclaration::StaticClass())) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().SelectLocalVariableUsages); + } + if (VoxelNode->IsA(UVoxelLocalVariableUsage::StaticClass())) + { + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().SelectLocalVariableDeclaration); + } + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().ConvertVariablesToReroute); + } + } + } + + { + FToolMenuSection& Section = Menu->AddSection("VoxelGraphNodeEdit"); + Section.AddMenuEntry(FGenericCommands::Get().Delete); + Section.AddMenuEntry(FGenericCommands::Get().Cut); + Section.AddMenuEntry(FGenericCommands::Get().Copy); + Section.AddMenuEntry(FGenericCommands::Get().Duplicate); + } + { + FToolMenuSection& Section = Menu->AddSection("VoxelGraphNodeMisc"); + Section.AddMenuEntry(FVoxelGraphEditorCommands::Get().ReconstructNode); + } + + { + FToolMenuSection& Section = Menu->AddSection("VoxelGraphNodeAligment"); + Section.AddSubMenu("Alignment", VOXEL_LOCTEXT("Alignment"), FText(), FNewMenuDelegate::CreateLambda([](FMenuBuilder& InMenuBuilder) { + + InMenuBuilder.BeginSection("EdGraphSchemaAlignment", VOXEL_LOCTEXT("Align")); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesTop); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesMiddle); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesBottom); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesLeft); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesCenter); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesRight); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().StraightenConnections); + InMenuBuilder.EndSection(); + + InMenuBuilder.BeginSection("EdGraphSchemaDistribution", VOXEL_LOCTEXT("Distribution")); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesHorizontally); + InMenuBuilder.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesVertically); + InMenuBuilder.EndSection(); + })); + } + } +} +#endif + +void UVoxelGraphSchema::DroppedAssetsOnGraph(const TArray& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const +{ + auto* Generator = CastChecked(Graph)->GetGenerator(); + FStreamableManager AssetLoader; + for(auto& AssetData : Assets) + { + FStringAssetReference AssetRef(AssetData.ObjectPath.ToString()); + UObject* Asset = AssetLoader.LoadSynchronous(AssetRef); + if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + if (Asset->IsA()) + { + Node->bFloatHeightmap = true; + Node->HeightmapFloat = CastChecked(Asset); + } + else if (ensure(Asset->IsA())) + { + Node->bFloatHeightmap = false; + Node->HeightmapUINT16 = CastChecked(Asset); + } + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Asset = CastChecked(Asset); + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Texture = CastChecked(Asset); + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Curve = CastChecked(Asset); + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Curve = CastChecked(Asset); + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Macro = CastChecked(Asset); + Node->GraphNode->ReconstructNode(); + Node->SetEditableName(Asset->GetName()); + } + else if (Asset->IsA()) + { + auto* Node = Generator->ConstructNewNode(GraphPosition); + Node->Generator = CastChecked(Asset); + Node->SetEditableName(Asset->GetName()); + } + } + Generator->CompileVoxelNodesFromGraphNodes(); +} + +void UVoxelGraphSchema::GetAssetsGraphHoverMessage(const TArray& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const +{ + FStreamableManager AssetLoader; + for(auto& AssetData : Assets) + { + FStringAssetReference AssetRef(AssetData.ObjectPath.ToString()); + UObject* Asset = AssetLoader.LoadSynchronous(AssetRef); + if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Heightmap Sampler node"; + } + else if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Texture Sampler node"; + } + else if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Curve Sampler node"; + } + else if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Color Curve Sampler node"; + } + else if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Macro node"; + } + else if (Asset->IsA()) + { + OutOkIcon = true; + OutTooltipText = "Add Generator Sampler node"; + } + } +} + +void UVoxelGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const +{ + FGraphNodeCreator StartNodeCreator(Graph); + UVoxelGraphNode_Root* StartNode = StartNodeCreator.CreateNode(); + StartNodeCreator.Finalize(); + SetNodeMetaData(StartNode, FNodeMetadata::DefaultGraphNode); + + UVoxelGraphMacro* Macro = Cast(CastChecked(&Graph)->GetGenerator()); + if (Macro) + { + UVoxelGraphMacroInputNode* InputNode = Macro->ConstructNewNode(FVector2D(-100, 0)); + UVoxelGraphMacroOutputNode* OutputNode = Macro->ConstructNewNode(FVector2D(100, 0)); + + Macro->InputNode = InputNode; + Macro->OutputNode = OutputNode; + + SetNodeMetaData(InputNode->GraphNode, FNodeMetadata::DefaultGraphNode); + SetNodeMetaData(OutputNode->GraphNode, FNodeMetadata::DefaultGraphNode); + + StartNode->NodePosX = -500; + } +} + +void UVoxelGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const +{ + Super::BreakNodeLinks(TargetNode); + + CastChecked(TargetNode.GetGraph())->GetGenerator()->CompileVoxelNodesFromGraphNodes(); +} + +void UVoxelGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Break Pin Links")); + + auto OldLinkedTo = TargetPin.LinkedTo; + Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation); + + // if this would notify the node then we need to compile the generator + if (bSendsNodeNotifcation) + { + CastChecked(TargetPin.GetOwningNode()->GetGraph())->GetGenerator()->CompileVoxelNodesFromGraphNodes(); + } + + auto AK = Cast(TargetPin.GetOwningNode()); + if (AK) + { + AK->PropagatePinType(); + } + for (auto& Pin : OldLinkedTo) + { + auto BK = Cast(Pin->GetOwningNode()); + if (BK) + { + BK->PropagatePinType(); + } + } +} + +void UVoxelGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const +{ + const FScopedTransaction Transaction(VOXEL_LOCTEXT("Create Reroute Node")); + + const FVector2D NodeSpacerSize(42.0f, 24.0f); + const FVector2D KnotTopLeft = GraphPosition - (NodeSpacerSize * 0.5f); + + // Create a new knot + UEdGraph* ParentGraph = PinA->GetOwningNode()->GetGraph(); + + FVoxelGraphSchemaAction_NewKnotNode Action; + UVoxelGraphNode_Knot* NewKnot = Cast(Action.PerformAction(ParentGraph, NULL, KnotTopLeft, true)); + + // Move the connections across (only notifying the knot, as the other two didn't really change) + PinA->BreakLinkTo(PinB); + PinA->MakeLinkTo((PinA->Direction == EGPD_Output) ? NewKnot->GetInputPin() : NewKnot->GetOutputPin()); + PinB->MakeLinkTo((PinB->Direction == EGPD_Output) ? NewKnot->GetInputPin() : NewKnot->GetOutputPin()); + NewKnot->PropagatePinType(); + + // Recompile + CastChecked(PinA->GetOwningNode()->GetGraph())->GetGenerator()->CompileVoxelNodesFromGraphNodes(); +} + +const FPinConnectionResponse UVoxelGraphSchema::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const +{ + // Make sure the pins are not on the same node + if (PinA->GetOwningNode() == PinB->GetOwningNode()) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, VOXEL_LOCTEXT("Both are on the same node")); + } + + // Compare the directions + const UEdGraphPin* InputPin = NULL; + const UEdGraphPin* OutputPin = NULL; + + if (!CategorizePinsByDirection(PinA, PinB, /*out*/ InputPin, /*out*/ OutputPin)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, VOXEL_LOCTEXT("Directions are not compatible")); + } + + check(InputPin); + check(OutputPin); + auto InputCategory = FVoxelPinCategory::FromString(InputPin->PinType.PinCategory); + auto OutputCategory = FVoxelPinCategory::FromString(OutputPin->PinType.PinCategory); + + if (InputCategory != OutputCategory && InputCategory != EVoxelPinCategory::Wildcard && OutputCategory != EVoxelPinCategory::Wildcard) + { + if (InputCategory == EVoxelPinCategory::Float && OutputCategory == EVoxelPinCategory::Int) + { + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, VOXEL_LOCTEXT("Cast to float")); + } + else if (InputCategory == EVoxelPinCategory::Int && OutputCategory == EVoxelPinCategory::Float) + { + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, VOXEL_LOCTEXT("Round to int")); + } + else + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, VOXEL_LOCTEXT("Types are not compatible")); + } + } + + if (ConnectionCausesLoop(InputPin, OutputPin)) + { + return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, VOXEL_LOCTEXT("Connection would cause loop")); + } + + // Break existing connections on inputs only except for exec - multiple output connections are acceptable + if (InputCategory != EVoxelPinCategory::Exec && InputPin->LinkedTo.Num() > 0) + { + ECanCreateConnectionResponse ReplyBreakOutputs; + if (InputPin == PinA) + { + ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_A; + } + else + { + ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_B; + } + return FPinConnectionResponse(ReplyBreakOutputs, VOXEL_LOCTEXT("Replace existing connections")); + } + + if (OutputCategory == EVoxelPinCategory::Exec && OutputPin->LinkedTo.Num() > 0) + { + ECanCreateConnectionResponse ReplyBreakOutputs; + if (OutputPin == PinA) + { + ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_A; + } + else + { + ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_B; + } + return FPinConnectionResponse(ReplyBreakOutputs, VOXEL_LOCTEXT("Replace existing connections")); + } + + return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT("")); +} + +void UVoxelGraphSchema::GetAllVoxelNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph) const +{ + InitVoxelNodeClasses(); + + auto* FromPin = ActionMenuBuilder.FromPin; + EVoxelPinCategory Category = FromPin ? FVoxelPinCategory::FromString(FromPin->PinType.PinCategory) : EVoxelPinCategory::Wildcard; + + const int32 RerouteNodePriority = 20; + const int32 LocalVariablesPriority = 10; + const int32 SetterNodesPriority = 5; + const int32 ParameterNodesPriority = 0; + const int32 MacroNodesPriority = 0; + + const auto PinMatchesNode = [&](UVoxelNode* Node) + { + if (Category == EVoxelPinCategory::Vector) + { + // Make sure to check the opposite direction of FromPin + return UVoxelGraphNode::HasVectorPin(*Node, FromPin->Direction == EGPD_Input ? EGPD_Output : EGPD_Input); + } + + // Make sure to check the opposite direction of FromPin + return FromPin->Direction == EGPD_Input + ? Node->HasOutputPinWithCategory(Category) + : Node->HasInputPinWithCategory(Category); + }; + + // Macros + { + // Load the asset registry module + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); + + // Collect a full list of assets with the specified class + TArray AssetDataList; + AssetRegistryModule.Get().GetAssetsByClass(UVoxelGraphMacro::StaticClass()->GetFName(), AssetDataList); + + for (const FAssetData& AssetData : AssetDataList) + { + FStreamableManager AssetLoader; + FStringAssetReference AssetRef(AssetData.ObjectPath.ToString()); + UVoxelGraphMacro* Macro = Cast(AssetLoader.LoadSynchronous(AssetRef)); + + if (!Macro || !Macro->InputNode || !Macro->OutputNode || !Macro->bShowInContextMenu) + { + continue; + } + + const auto PinMatchesMacro = [&]() + { + // Make sure to check the opposite direction of FromPin + auto* Node = FromPin->Direction == EGPD_Input ? static_cast(Macro->OutputNode) : Macro->InputNode; + if (!Node) + { + return false; + } + + if (Category != EVoxelPinCategory::Vector && Macro->bVectorOnlyNode) + { + // Having all the vector macros when dragging a float is annoying + return false; + } + + return PinMatchesNode(Node); + }; + + if (Macro->bShowInContextMenu && (!FromPin || PinMatchesMacro())) + { + const FText Name = Macro->GetMacroName(); + const FText AddToolTip = FText::FromString(Macro->Tooltip); + const FText Keywords = FText::FromString(Macro->Keywords); + const FText MacroCategory = Macro->GetMacroCategory(); + + TSharedPtr NewNodeAction( + new FVoxelGraphSchemaAction_NewMacroNode( + MacroCategory, + Name, + AddToolTip, + MacroNodesPriority, + Keywords)); + NewNodeAction->Macro = Macro; + ActionMenuBuilder.AddAction(NewNodeAction); + } + } + } + + // Local variables declaration + { + TSharedPtr NewNodeAction( + new FVoxelGraphSchemaAction_NewLocalVariableDeclaration( + FText::GetEmpty(), + VOXEL_LOCTEXT("Create local variable"), + VOXEL_LOCTEXT("Create a new local variable here"), + RerouteNodePriority)); + NewNodeAction->PinCategory = EVoxelPinCategory::Float; + bool bAdd = false; + if (FromPin) + { + if (FromPin->Direction == EGPD_Output) + { + if (Category != EVoxelPinCategory::Exec && + Category != EVoxelPinCategory::Wildcard && + Category != EVoxelPinCategory::Vector) + { + NewNodeAction->DefaultName = FromPin->PinName; + NewNodeAction->PinCategory = Category; + bAdd = true; + } + } + } + else + { + bAdd = true; + } + if (bAdd) + { + ActionMenuBuilder.AddAction(NewNodeAction); + } + } + + // For the palette actions CurrentGraph is null + if (CurrentGraph) + { + auto* Graph = CastChecked(CurrentGraph); + auto* Generator = Graph->GetGenerator(); + + // Local variables usage + if (!FromPin || FromPin->Direction == EGPD_Input) + { + for (auto& Node : Generator->AllNodes) + { + auto* Declaration = Cast(Node); + if (Declaration && (!FromPin || Declaration->GetCategory() == Category)) + { + const FText Name = FText::FromName(Declaration->Name); + const FText AddToolTip = FText::Format(VOXEL_LOCTEXT("Use {0} here"), Name); + + TSharedPtr NewNodeAction( + new FVoxelGraphSchemaAction_NewLocalVariableUsage( + VOXEL_LOCTEXT("Local variables"), + Name, + AddToolTip, + LocalVariablesPriority)); + NewNodeAction->Declaration = Declaration; + ActionMenuBuilder.AddAction(NewNodeAction); + } + } + } + + // Setter nodes + { + auto Outputs = Generator->GetOutputs(); + for (auto It : Outputs) + { + auto Output = It.Value; + auto Index = It.Key; + if (FVoxelGraphOutputsUtils::IsVoxelGraphOutputHidden(Index)) + { + continue; + } + if (!FromPin || Category == EVoxelPinCategory::Exec || Category == FVoxelPinCategory::DataPinToPin(Output.Category)) + { + const FText Name = FText::FromString("Set " + Output.Name.ToString()); + const FText AddToolTip = FText::Format(VOXEL_LOCTEXT("Adds {0} node here"), Name); + + TSharedPtr NewNodeAction( + new FVoxelGraphSchemaAction_NewSetterNode( + VOXEL_LOCTEXT("Setter nodes"), + Name, + AddToolTip, + SetterNodesPriority)); + NewNodeAction->Index = Index; + ActionMenuBuilder.AddAction(NewNodeAction); + } + } + } + } + + for (auto& NodeClass : VoxelNodeClasses) + { + UVoxelNode* DefaultNode = NodeClass->GetDefaultObject(); + if (!FromPin || PinMatchesNode(DefaultNode)) + { + const auto GetCategory = [](UClass* Class) + { + return Class->GetMetaDataText(TEXT("Category"), TEXT("UObjectCategory"), Class->GetFullGroupName(false)); + }; + + FText ActionCategory = GetCategory(NodeClass); + if (ActionCategory.IsEmpty()) + { + UClass* Class = NodeClass->GetSuperClass(); + while (Class && ActionCategory.IsEmpty()) + { + ActionCategory = GetCategory(Class); + Class = Class->GetSuperClass(); + } + } + + int32 Priority = 0; + + if (NodeClass->IsChildOf(UVoxelExposedNode::StaticClass())) + { + Priority = ParameterNodesPriority; + } + if (NodeClass->IsChildOf(UVoxelSetterNode::StaticClass())) + { + Priority = SetterNodesPriority; + } + + FText Name = FText::FromString(NodeClass->GetDescription()); + FText AddToolTip = NodeClass->GetToolTipText(); + FText Keywords = NodeClass->GetMetaDataText(TEXT("Keywords"), TEXT("UObjectKeywords"), GetClass()->GetFullGroupName(false)); + + TSharedPtr NewNodeAction( + new FVoxelGraphSchemaAction_NewNode( + ActionCategory, + Name, + AddToolTip, + Priority, + Keywords)); + NewNodeAction->VoxelNodeClass = NodeClass; + ActionMenuBuilder.AddAction(NewNodeAction); + } + } + + if (FromPin) + { + const FText MenuDescription = VOXEL_LOCTEXT("Add reroute node"); + const FText ToolTip = VOXEL_LOCTEXT("Create a reroute node."); + TSharedPtr NewNodeAction(new FVoxelGraphSchemaAction_NewKnotNode(FText::GetEmpty(), MenuDescription, ToolTip, RerouteNodePriority)); + ActionMenuBuilder.AddAction(NewNodeAction); + } +} + +void UVoxelGraphSchema::GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph /*= NULL*/) const +{ + if (!ActionMenuBuilder.FromPin) + { + const bool bIsManyNodesSelected = CurrentGraph ? (FVoxelGraphEditorUtilities::GetNumberOfSelectedNodes(CurrentGraph) > 0) : false; + const FText MenuDescription = bIsManyNodesSelected ? VOXEL_LOCTEXT("Create Comment from Selection") : VOXEL_LOCTEXT("Add Comment"); + const FText ToolTip = VOXEL_LOCTEXT("Creates a comment."); + + TSharedPtr NewAction(new FVoxelGraphSchemaAction_NewComment(FText::GetEmpty(), MenuDescription, ToolTip, 0)); + ActionMenuBuilder.AddAction(NewAction); + } + +} + +void UVoxelGraphSchema::InitVoxelNodeClasses() +{ + if (bVoxelNodeClassesInitialized) + { + return; + } + VoxelNodeClasses.Empty(); + + // Construct list of non-abstract voxel node classes. + for (TObjectIterator It; It; ++It) + { + if (It->IsChildOf(UVoxelNode::StaticClass()) && !It->HasAnyClassFlags(CLASS_Abstract | CLASS_NotPlaceable | CLASS_Deprecated)) + { + VoxelNodeClasses.Add(*It); + } + } + + VoxelNodeClasses.Sort(); + + bVoxelNodeClassesInitialized = true; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h new file mode 100644 index 00000000..7b3ba6f7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h @@ -0,0 +1,166 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraphSchema.h" +#include "VoxelMinimal.h" +#include "VoxelPinCategory.h" +#include "VoxelGraphSchema.generated.h" + +class UVoxelGraphMacro; +class UVoxelLocalVariableDeclaration; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewNode : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + /** Class of node we want to create */ + UPROPERTY() + UClass* VoxelNodeClass = nullptr; + + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewMacroNode : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + UPROPERTY() + UVoxelGraphMacro* Macro = nullptr; + + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewLocalVariableDeclaration : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + UPROPERTY() + FName DefaultName; + + UPROPERTY() + EVoxelPinCategory PinCategory; + + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewLocalVariableUsage : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + UPROPERTY() + UVoxelLocalVariableDeclaration* Declaration = nullptr; + + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewSetterNode : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + UPROPERTY() + uint32 Index = 0; + + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +USTRUCT() +struct FVoxelGraphSchemaAction_NewKnotNode : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +/** Action to create new comment */ +USTRUCT() +struct FVoxelGraphSchemaAction_NewComment : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + +/** Action to paste clipboard contents into the graph */ +USTRUCT() +struct FVoxelGraphSchemaAction_Paste : public FEdGraphSchemaAction +{ + GENERATED_BODY(); + +public: + using FEdGraphSchemaAction::FEdGraphSchemaAction; + virtual UEdGraphNode* PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode = true) override; +}; + + +UCLASS() +class VOXELGRAPHEDITOR_API UVoxelGraphSchema : public UEdGraphSchema +{ + GENERATED_BODY() + +public: + bool ConnectionCausesLoop(const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const; + + /** Helper method to add items valid to the palette list */ + void GetPaletteActions(FGraphActionMenuBuilder& ActionMenuBuilder) const; + + //~ Begin UEdGraphSchema Interface + virtual void GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const override; + virtual const FPinConnectionResponse CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const override; + virtual bool TryCreateConnection(UEdGraphPin* A, UEdGraphPin* B) const override; + virtual void TrySetDefaultValue( + UEdGraphPin& Pin, + const FString& NewDefaultValue +#if ENGINE_MINOR_VERSION >= 24 + , bool bMarkAsModified +#endif + ) const override; + virtual bool CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const override; + virtual void CreateDefaultNodesForGraph(UEdGraph& Graph) const override; + virtual void BreakNodeLinks(UEdGraphNode& TargetNode) const override; + virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override; + virtual void OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const override; + virtual FLinearColor GetPinTypeColor(const FEdGraphPinType& PinType) const override; + virtual TSharedPtr GetCreateCommentAction() const override; + virtual int32 GetNodeSelectionCount(const UEdGraph* Graph) const override; +#if ENGINE_MINOR_VERSION < 24 + virtual void GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) const override; +#else + virtual void GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const override; +#endif + virtual void DroppedAssetsOnGraph(const TArray& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const override; + virtual void GetAssetsGraphHoverMessage(const TArray& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const override; + //~ End UEdGraphSchema Interface + +private: + /** Adds actions for creating every type of VoxelNode */ + void GetAllVoxelNodeActions(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr) const; + /** Adds action for creating a comment */ + void GetCommentAction(FGraphActionMenuBuilder& ActionMenuBuilder, const UEdGraph* CurrentGraph = nullptr) const; + +private: + static void InitVoxelNodeClasses(); + + static TArray VoxelNodeClasses; + static bool bVoxelNodeClassesInitialized; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.cpp new file mode 100644 index 00000000..fd694e32 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.cpp @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphShortcuts.h" +#include "UnrealEdMisc.h" +#include "VoxelNodes/VoxelMathNodes.h" +#include "VoxelNodes/VoxelCoordinatesNodes.h" +#include "VoxelNodes/VoxelParameterNodes.h" + +UVoxelGraphShortcuts::UVoxelGraphShortcuts() +{ + Shortcuts = { + {EKeys::X, UVoxelNode_XF::StaticClass()}, + {EKeys::Y, UVoxelNode_YF::StaticClass()}, + {EKeys::Z, UVoxelNode_ZF::StaticClass()}, + {EKeys::One, UVoxelNode_FloatParameter::StaticClass()}, + + {EKeys::Multiply, UVoxelNode_FMultiply::StaticClass()}, + {EKeys::Add, UVoxelNode_FAdd::StaticClass()}, + {EKeys::Subtract, UVoxelNode_FSubstract::StaticClass()}, + {EKeys::Divide, UVoxelNode_FDivide::StaticClass()}, + + {EKeys::Asterix, UVoxelNode_FMultiply::StaticClass()}, + {EKeys::Slash, UVoxelNode_FDivide::StaticClass()}, + {EKeys::Hyphen, UVoxelNode_FSubstract::StaticClass()} + }; +} + +void UVoxelGraphShortcuts::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (!FUnrealEdMisc::Get().IsDeletePreferences()) + { + SaveConfig(); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.h new file mode 100644 index 00000000..9e77323f --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.h @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "InputCoreTypes.h" +#include "Templates/SubclassOf.h" +#include "Framework/Commands/InputChord.h" +#include "VoxelGraphShortcuts.generated.h" + +class UVoxelNode; + +USTRUCT() +struct FVoxelGraphEditorKeyBinding +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bCtrlDown = false; + + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bAltDown = false; + + UPROPERTY(EditAnywhere, Category = "Voxel") + bool bShiftDown = false; + + UPROPERTY(EditAnywhere, Category = "Voxel") + FKey Key; + + UPROPERTY(EditAnywhere, Category = "Voxel") + TSubclassOf Class; + + FVoxelGraphEditorKeyBinding() = default; + + FVoxelGraphEditorKeyBinding(FKey Key, TSubclassOf Class) + : Key(Key) + , Class(Class) + { + } + + inline bool IsSameAs(const FInputChord& Chord) + { + return bCtrlDown == Chord.bCtrl && bAltDown == Chord.bAlt && bShiftDown == Chord.bShift && Key == Chord.Key; + } +}; + +UCLASS(Config = EditorKeyBindings) +class UVoxelGraphShortcuts : public UObject +{ + GENERATED_BODY() + +public: + UVoxelGraphShortcuts(); + + UPROPERTY(Config, EditAnywhere, Category = "Voxel") + TArray Shortcuts; + + //~ Begin UObject Interface +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/IVoxelGraphEditorToolkit.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/IVoxelGraphEditorToolkit.h new file mode 100644 index 00000000..44e1215a --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/IVoxelGraphEditorToolkit.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Toolkits/AssetEditorToolkit.h" + +class FAdvancedPreviewScene; +class FVoxelGraphCompiler; +class UVoxelGraphGenerator; +class UEdGraphNode; +class FVoxelCompilationNode; + +struct FVoxelGraphMessage; + +enum class EVoxelGraphPreviewFlags; +enum class EVoxelGraphNodeMessageType : int32; + +class IVoxelGraphEditorToolkit : public FAssetEditorToolkit +{ +public: + // Checks whether nodes can currently be pasted + virtual bool CanPasteNodes() const = 0; + // Paste nodes at a specific location + virtual void PasteNodesHere(const FVector2D& Location) = 0; + + // Get the bounding area for the currently selected nodes. returns false if nothing is selected + virtual bool GetBoundsForSelectedNodes(class FSlateRect& Rect, float Padding) = 0; + + // Gets the number of nodes that are currently selected + virtual int32 GetNumberOfSelectedNodes() const = 0; + + // Get the currently selected set of nodes + virtual TSet GetSelectedNodes() const = 0; + + virtual void SelectNodesAndZoomToFit(const TArray& Nodes) = 0; + + virtual void RefreshNodesMessages() = 0; + + virtual void TriggerUpdatePreview(EVoxelGraphPreviewFlags Flags) = 0; + + virtual FAdvancedPreviewScene* GetPreviewScene() const = 0; + + virtual void DebugNodes(const TSet& Nodes) = 0; + + virtual void AddMessages(const TArray& Messages) = 0; + virtual void ClearMessages(bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/VoxelGraphEditorModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/VoxelGraphEditorModule.h new file mode 100644 index 00000000..b0c5dee8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/Public/VoxelGraphEditorModule.h @@ -0,0 +1,21 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" +#include "Toolkits/IToolkit.h" + +class IVoxelGraphEditorToolkit; +class UVoxelGraphGenerator; +class IToolkitHost; +class UEdGraphNode; + +class IVoxelGraphEditorModule : public IModuleInterface +{ +public: + virtual TSharedRef CreateVoxelGraphEditor( + const EToolkitMode::Type Mode, + const TSharedPtr& InitToolkitHost, + UVoxelGraphGenerator* Generator) = 0; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs new file mode 100644 index 00000000..09f99870 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs @@ -0,0 +1,60 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelGraphEditor : ModuleRules +{ + public VoxelGraphEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + DynamicallyLoadedModuleNames.AddRange( + new string[] { + "AssetRegistry" + }); + + PrivateDependencyModuleNames.AddRange( + new string[] { + "Core", + "CoreUObject", + "Engine", + "Voxel", + "VoxelGraph", + "KismetWidgets", + "AdvancedPreviewScene", + "Slate", + "SlateCore", + "UnrealEd", + "InputCore", + "ApplicationCore", + "GraphEditor", + "EditorStyle", + "Projects", + "BlueprintGraph", + "DesktopPlatform", + "Json", + "GameProjectGeneration", + "MessageLog", + "AppFramework", + "PropertyEditor", +#if UE_4_24_OR_LATER + "ToolMenus" +#endif + }); + + PrivateIncludePathModuleNames.AddRange( + new string[] { + "VoxelEditor" + }); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionBlendMaterialAttributesBarycentric.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionBlendMaterialAttributesBarycentric.cpp new file mode 100644 index 00000000..64ed6756 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionBlendMaterialAttributesBarycentric.cpp @@ -0,0 +1,113 @@ +// Copyright 2020 Phyronnaz + +#include "MaterialExpressionBlendMaterialAttributesBarycentric.h" +#include "MaterialCompiler.h" + +UMaterialExpressionBlendMaterialAttributesBarycentric::UMaterialExpressionBlendMaterialAttributesBarycentric() +{ + // Structure to hold one-time initialization + struct FConstructorStatics + { + FText NAME_MaterialAttributes; + FConstructorStatics() + : NAME_MaterialAttributes(NSLOCTEXT("Voxel", "Material Attributes", "Material Attributes")) + { + } + }; + static FConstructorStatics ConstructorStatics; +#if WITH_EDITORONLY_DATA + MenuCategories.Add(ConstructorStatics.NAME_MaterialAttributes); + + Outputs.Reset(); + Outputs.Add(FExpressionOutput(TEXT(""), 0, 0, 0, 0, 0)); +#endif +} + +#if WITH_EDITOR +int32 UMaterialExpressionBlendMaterialAttributesBarycentric::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) +{ + const FGuid AttributeID = Compiler->GetMaterialAttribute(); + + int32 ResultA = A.CompileWithDefault(Compiler, AttributeID); + int32 ResultB = B.CompileWithDefault(Compiler, AttributeID); + int32 ResultC = C.CompileWithDefault(Compiler, AttributeID); + int32 ResultAlphaA = AlphaA.Compile(Compiler); + int32 ResultAlphaB = AlphaB.Compile(Compiler); + + return Compiler->Add + ( + Compiler->Add + ( + Compiler->Mul(ResultA, ResultAlphaA), + Compiler->Mul(ResultB, ResultAlphaB) + ), + Compiler->Mul + ( + ResultC, + Compiler->Sub + ( + Compiler->Constant(1.0f), + Compiler->Add(ResultAlphaA, ResultAlphaB) + ) + ) + ); +} + +void UMaterialExpressionBlendMaterialAttributesBarycentric::GetCaption(TArray& OutCaptions) const +{ + OutCaptions.Add(TEXT("BlendMaterialAttributesBarycentric")); +} + +const TArray UMaterialExpressionBlendMaterialAttributesBarycentric::GetInputs() +{ + TArray Result; + Result.Add(&A); + Result.Add(&B); + Result.Add(&C); + Result.Add(&AlphaA); + Result.Add(&AlphaB); + return Result; +} + +FExpressionInput* UMaterialExpressionBlendMaterialAttributesBarycentric::GetInput(int32 InputIndex) +{ + if (InputIndex == 0) + { + return &A; + } + else if (InputIndex == 1) + { + return &B; + } + else if (InputIndex == 2) + { + return &C; + } + else if (InputIndex == 4) + { + return &AlphaA; + } + else if (InputIndex == 4) + { + return &AlphaB; + } + + return nullptr; +} + +FName UMaterialExpressionBlendMaterialAttributesBarycentric::GetInputName(int32 InputIndex) const +{ + FName Name; + + switch (InputIndex) + { + case 0: Name = TEXT("A"); break; + case 1: Name = TEXT("B"); break; + case 2: Name = TEXT("C"); break; + case 3: Name = TEXT("Alpha0"); break; + case 4: Name = TEXT("Alpha1"); break; + }; + + return Name; +} +#endif // WITH_EDITOR \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionPack.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionPack.cpp new file mode 100644 index 00000000..0b321c56 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/MaterialExpressionPack.cpp @@ -0,0 +1,273 @@ +// Copyright 2020 Phyronnaz + +#include "MaterialExpressionPack.h" +#include "MaterialCompiler.h" +#include "EdGraph/EdGraphNode.h" +#include "Materials/MaterialFunction.h" +#include "Materials/MaterialExpressionReroute.h" +#include "Materials/MaterialExpressionFunctionInput.h" +#include "Materials/MaterialExpressionFunctionOutput.h" +#include "Materials/MaterialExpressionMaterialFunctionCall.h" + +UMaterialExpressionPack::UMaterialExpressionPack() +{ +#if WITH_EDITORONLY_DATA + MenuCategories.Add(INVTEXT("Packing")); +#endif +} + +#if WITH_EDITOR +void UMaterialExpressionPack::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty) // Else crash when calling reconstruct node + { + // There's a bug when copy pasting arrays with default values at the end + for (auto& Input : Inputs) + { + if (Input.InputName == "DEFAULT_DO_NOT_USE") + { + Input.InputName = {}; + } + } + + if (GraphNode) + { + GraphNode->ReconstructNode(); + } + + OnPostEditChangeProperty.Broadcast(); + } +} + +int32 UMaterialExpressionPack::Compile(FMaterialCompiler* Compiler, int32 OutputIndex) +{ + if (!FMaterialExpressionUnpackScope::GetQueuedElement()) + { + Compiler->Error(TEXT("Pack is not followed by an Unpack node!")); + return -1; + } + + const auto Element = FMaterialExpressionUnpackScope::Pop(); + if (!Element.Unpack->MatchesPack(this)) + { + Compiler->Error(TEXT("Unpack is out of sync! Click it and press Refresh")); + return -1; + } + + if (!ensure(Inputs.IsValidIndex(Element.OutputIndex))) + { + Compiler->Error(TEXT("Pack: Invalid OutputIndex! (INTERNAL ERROR)")); + return -1; + } + + return Inputs[Element.OutputIndex].Input.Compile(Compiler); +} + +void UMaterialExpressionPack::GetCaption(TArray& OutCaptions) const +{ + OutCaptions.Add(TEXT("Pack")); +} + +const TArray UMaterialExpressionPack::GetInputs() +{ + TArray Result; + for (auto& Input : Inputs) + { + Result.Add(&Input.Input); + } + return Result; +} + +FExpressionInput* UMaterialExpressionPack::GetInput(int32 InputIndex) +{ + if (Inputs.IsValidIndex(InputIndex)) + { + return &Inputs[InputIndex].Input; + } + return nullptr; +} + +FName UMaterialExpressionPack::GetInputName(int32 InputIndex) const +{ + if (Inputs.IsValidIndex(InputIndex)) + { + return Inputs[InputIndex].InputName; + } + return {}; +} +#endif // WITH_EDITOR + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UMaterialExpressionUnpack::UMaterialExpressionUnpack() +{ +#if WITH_EDITORONLY_DATA + MenuCategories.Add(INVTEXT("Packing")); + Outputs.Reset(); + + bShowOutputNameOnPin = true; +#endif +} + +#if WITH_EDITOR +void UMaterialExpressionUnpack::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty) // Else crash when calling reconstruct node + { + RefreshPack(); + } +} + +int32 UMaterialExpressionUnpack::Compile(FMaterialCompiler* Compiler, int32 OutputIndex) +{ + if (FMaterialExpressionUnpackScope::GetQueuedElement()) + { + Compiler->Error(TEXT("Multiple unpacks are used with no pack in-between! This is not supported")); + return -1; + } + + static TMap OutputIndicesGUIDs; + FGuid& OutputGUID = OutputIndicesGUIDs.FindOrAdd(OutputIndex); + if (!OutputGUID.IsValid()) + { + OutputGUID = FGuid::NewGuid(); + } + + // If we don't do that, the output will be cached & the same thing will be used for all output indices + FScopedMaterialCompilerAttribute AttributeScope(Compiler, OutputGUID); + FMaterialExpressionUnpackScope UnpackScope(this, OutputIndex); + + const int32 Ret = Input.Compile(Compiler); + + if (Ret != -1) + { + if (Compiler->GetType(Ret) == MCT_StaticBool) + { + // Messes up the instances, due to the code in FMaterialEditorUtilities::GetStaticSwitchExpressionValue + Compiler->Error(TEXT("Pack/Unpack nodes cannot be used with static bools")); + return -1; + } + + if (Input.Expression && !Input.Expression->IsResultMaterialAttributes(0)) + { + // If Ret is -1, reroute nodes will return false + Compiler->Error(TEXT("Pack/Unpack nodes can only be connected to material attributes function input/output")); + } + } + + return Ret; +} + +void UMaterialExpressionUnpack::GetCaption(TArray& OutCaptions) const +{ + OutCaptions.Add(TEXT("Unpack")); +} + +void UMaterialExpressionUnpack::RefreshPack() +{ + bRefresh = false; + + Outputs.Reset(); + auto* Pack = GetPack_NotForCompile(); + if (Pack) + { + for (const auto& PackInput : Pack->Inputs) + { + Outputs.Add(FExpressionOutput(PackInput.InputName)); + } + + if (!Pack->OnPostEditChangeProperty.IsBoundToObject(this)) + { + Pack->OnPostEditChangeProperty.AddUObject(this, &UMaterialExpressionUnpack::RefreshPack); + } + } + + if (GraphNode) + { + GraphNode->ReconstructNode(); + } +} + +UMaterialExpressionPack* UMaterialExpressionUnpack::GetPack_NotForCompile() const +{ + UMaterialExpression* Expression = Input.Expression; + TSet VisitedExpressions; + while (!VisitedExpressions.Contains(Expression)) + { + VisitedExpressions.Add(Expression); + + if (auto* Pack = Cast(Expression)) + { + return Pack; + } + if (auto* Reroute = Cast(Expression)) + { + Expression = Reroute->Input.Expression; + } + if (auto* FunctionInput = Cast(Expression)) + { + Expression = FunctionInput->Preview.Expression; + } + if (auto* FunctionOutput = Cast(Expression)) + { + Expression = FunctionOutput->A.Expression; + } + if (auto* FunctionCall = Cast(Expression)) + { + if (ensure(FunctionCall->FunctionOutputs.IsValidIndex(Input.OutputIndex))) + { + const auto& FunctionOutput = FunctionCall->FunctionOutputs[Input.OutputIndex]; + if (ensure(FunctionOutput.ExpressionOutput)) // If this gets raised, the ExpressionOutput is not cached and needs to be + { + Expression = FunctionOutput.ExpressionOutput; + } + } + } + } + return nullptr; +} + +bool UMaterialExpressionUnpack::MatchesPack(UMaterialExpressionPack* Pack) +{ + check(Pack); + + if (Pack->Inputs.Num() != Outputs.Num()) + { + return false; + } + for (int32 Index = 0; Index < Outputs.Num(); Index++) + { + if (Pack->Inputs[Index].InputName != Outputs[Index].OutputName) + { + return false; + } + } + return true; +} +#endif // WITH_EDITOR + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FMaterialExpressionUnpackScope::FMaterialExpressionUnpackScope(UMaterialExpressionUnpack* Unpack, int32 OutputIndex) +{ + check(Unpack); + check(!QueuedElement.IsValid()); + + QueuedElement = MakeUnique(FElement{ Unpack, OutputIndex }); +} + +FMaterialExpressionUnpackScope::~FMaterialExpressionUnpackScope() +{ + // Might already have been reset + QueuedElement.Reset(); +} + +TUniquePtr FMaterialExpressionUnpackScope::QueuedElement; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelColorWheel.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelColorWheel.cpp new file mode 100644 index 00000000..18d09d26 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelColorWheel.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelColorWheel.h" +#include "Widgets/Colors/SColorWheel.h" + +TSharedRef UVoxelColorWheel::RebuildWidget() +{ + ColorWheel = SNew(SColorWheel) + .SelectedColor_UObject(this, &UVoxelColorWheel::GetColor) + .OnValueChanged(FOnLinearColorValueChanged::CreateUObject(this, &UVoxelColorWheel::OnValueChanged)); + + return ColorWheel.ToSharedRef(); +} + +void UVoxelColorWheel::ReleaseSlateResources(bool bReleaseChildren) +{ + Super::ReleaseSlateResources(bReleaseChildren); + + ColorWheel.Reset(); +} + +#if WITH_EDITOR +const FText UVoxelColorWheel::GetPaletteCategory() +{ + return NSLOCTEXT("Voxel", "Voxel", "Voxel"); +} +#endif + +void UVoxelColorWheel::OnValueChanged(FLinearColor NewValue) +{ + Color = NewValue.HSVToLinearRGB();; + OnColorChanged.Broadcast(Color); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersLibrary.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersLibrary.cpp new file mode 100644 index 00000000..5fd19e42 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersLibrary.cpp @@ -0,0 +1,54 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelHelpersLibrary.h" +#include "ProceduralMeshComponent.h" + +void UVoxelHelpersLibrary::CreateProcMeshPlane(UProceduralMeshComponent* Mesh, int32 SizeX, int32 SizeY, float Step) +{ + if (!Mesh) + { + return; + } + + TArray Indices; + TArray Positions; + TArray Normals; + TArray TextureCoordinates; + + const int32 NumVertices = (SizeX + 1) * (SizeY + 1); + + Positions.Reserve(NumVertices); + Normals.Reserve(NumVertices); + TextureCoordinates.Reserve(NumVertices); + + Indices.Reserve(SizeX * SizeY * 6); + + for (int32 X = 0; X <= SizeX; X++) + { + for (int32 Y = 0; Y <= SizeY; Y++) + { + Positions.Add(FVector(X, Y, 0) * Step); + Normals.Add(FVector(0, 0, 1)); + TextureCoordinates.Add(FVector2D(X / float(SizeX), Y / float(SizeY))); + + if (X < SizeX && Y < SizeY) + { + const auto Index = [&](int32 U, int32 V) { return V + (SizeY + 1) * U; }; + const int32 A = Index(X + 0, Y + 0); + const int32 B = Index(X + 1, Y + 0); + const int32 C = Index(X + 0, Y + 1); + const int32 D = Index(X + 1, Y + 1); + + Indices.Add(C); + Indices.Add(D); + Indices.Add(A); + + Indices.Add(D); + Indices.Add(B); + Indices.Add(A); + } + } + } + + Mesh->CreateMeshSection(0, Positions, Indices, Normals, TextureCoordinates, {}, {}, false); +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersModule.cpp new file mode 100644 index 00000000..a5b5a341 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Private/VoxelHelpersModule.cpp @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelHelpersModule.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FVoxelHelpers, VoxelHelpers) diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionBlendMaterialAttributesBarycentric.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionBlendMaterialAttributesBarycentric.h new file mode 100644 index 00000000..a7866528 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionBlendMaterialAttributesBarycentric.h @@ -0,0 +1,47 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "MaterialExpressionIO.h" +#include "Materials/MaterialExpression.h" +#include "MaterialExpressionBlendMaterialAttributesBarycentric.generated.h" + +// Returns A * AlphaA + B * AlphaB + C * (1 - AlphaA - AlphaB) +UCLASS(CollapseCategories, HideCategories = Object, MinimalAPI) +class UMaterialExpressionBlendMaterialAttributesBarycentric : public UMaterialExpression +{ + GENERATED_BODY() + +public: + UMaterialExpressionBlendMaterialAttributesBarycentric(); + + UPROPERTY() + FMaterialAttributesInput A; + + UPROPERTY() + FMaterialAttributesInput B; + + UPROPERTY() + FMaterialAttributesInput C; + + UPROPERTY() + FExpressionInput AlphaA; + + UPROPERTY() + FExpressionInput AlphaB; + + //~ Begin UMaterialExpression Interface +#if WITH_EDITOR + virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override; + virtual void GetCaption(TArray& OutCaptions) const override; + virtual const TArray GetInputs()override; + virtual FExpressionInput* GetInput(int32 InputIndex)override; + virtual FName GetInputName(int32 InputIndex) const override; + virtual bool IsInputConnectionRequired(int32 InputIndex) const override { return true; } + virtual bool IsResultMaterialAttributes(int32 OutputIndex) override { return true; } + virtual uint32 GetInputType(int32 InputIndex) override { return InputIndex > 2 ? MCT_Float1 : MCT_MaterialAttributes; } +#endif + //~ End UMaterialExpression Interface +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionPack.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionPack.h new file mode 100644 index 00000000..976f11ba --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/MaterialExpressionPack.h @@ -0,0 +1,109 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Materials/MaterialExpression.h" +#include "MaterialExpressionPack.generated.h" + +USTRUCT() +struct FMaterialPackInput +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category=PackInput) + FName InputName = "DEFAULT_DO_NOT_USE"; // Assign a default value to bypass copy paste bug when items are equal to default + + UPROPERTY() + FExpressionInput Input; +}; + +UCLASS(CollapseCategories, HideCategories = Object) +class UMaterialExpressionPack : public UMaterialExpression +{ + GENERATED_BODY() + +public: + UMaterialExpressionPack(); + + UPROPERTY(EditAnywhere, Category=MaterialExpressionPack) + TArray Inputs; + + FSimpleMulticastDelegate OnPostEditChangeProperty; + +#if WITH_EDITOR + //~ Begin UObject Interface. + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + //~ End UObject Interface. + + //~ Begin UMaterialExpression Interface + virtual int32 Compile(FMaterialCompiler* Compiler, int32 OutputIndex) override; + virtual void GetCaption(TArray& OutCaptions) const override; + virtual const TArray GetInputs() override; + virtual FExpressionInput* GetInput(int32 InputIndex) override; + virtual FName GetInputName(int32 InputIndex) const override; + virtual uint32 GetInputType(int32 InputIndex) override { return MCT_Unknown; } + virtual uint32 GetOutputType(int32 OutputIndex) override { return MCT_Unknown; } + virtual bool IsResultMaterialAttributes(int32 OutputIndex) override { return true; } + //~ End UMaterialExpression Interface +#endif +}; + +UCLASS(CollapseCategories, HideCategories = Object) +class UMaterialExpressionUnpack : public UMaterialExpression +{ + GENERATED_BODY() + +public: + UMaterialExpressionUnpack(); + + UPROPERTY() + FExpressionInput Input; + + UPROPERTY(EditAnywhere, Category=MaterialExpressionUnpack) + bool bRefresh = false; + +#if WITH_EDITOR + //~ Begin UObject Interface. + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + //~ End UObject Interface. + + //~ Begin UMaterialExpression Interface + virtual int32 Compile(FMaterialCompiler* Compiler, int32 OutputIndex) override; + virtual void GetCaption(TArray& OutCaptions) const override; + virtual uint32 GetInputType(int32 InputIndex) override { return MCT_Unknown; } + virtual uint32 GetOutputType(int32 OutputIndex) override { return MCT_Unknown; } + //~ End UMaterialExpression Interface +#endif + + void RefreshPack(); + UMaterialExpressionPack* GetPack_NotForCompile() const; + bool MatchesPack(UMaterialExpressionPack* Pack); +}; + +struct FMaterialExpressionUnpackScope +{ + struct FElement + { + UMaterialExpressionUnpack* Unpack = nullptr; + int32 OutputIndex = 0; + }; + + FMaterialExpressionUnpackScope(UMaterialExpressionUnpack* Unpack, int32 OutputIndex); + ~FMaterialExpressionUnpackScope(); + + static FElement* GetQueuedElement() + { + return QueuedElement.Get(); + } + static FElement Pop() + { + check(QueuedElement.IsValid()); + const auto Copy = *QueuedElement; + QueuedElement.Reset(); + return Copy; + } + +private: + static TUniquePtr QueuedElement; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelColorWheel.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelColorWheel.h new file mode 100644 index 00000000..ee57e9d0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelColorWheel.h @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Components/Widget.h" +#include "VoxelColorWheel.generated.h" + +class SColorWheel; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnVoxelColorWheelColorChangedEvent, const FLinearColor&, NewColor); + +UCLASS() +class VOXELHELPERS_API UVoxelColorWheel : public UWidget +{ + GENERATED_BODY() + +public: + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Voxel Color Wheel") + FLinearColor Color = FLinearColor::Red; + + UPROPERTY(BlueprintAssignable, Category="Voxel Color Wheel") + FOnVoxelColorWheelColorChangedEvent OnColorChanged; + +protected: + //~ Begin UWidget interface + virtual TSharedRef RebuildWidget() override; + virtual void ReleaseSlateResources(bool bReleaseChildren) override; +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + //~ End UWidget interface + +private: + TSharedPtr ColorWheel; + + void OnValueChanged(FLinearColor NewValue); + inline FLinearColor GetColor() const { return Color.LinearRGBToHSV(); } +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersLibrary.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersLibrary.h new file mode 100644 index 00000000..7f4c90e8 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersLibrary.h @@ -0,0 +1,19 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "VoxelHelpersLibrary.generated.h" + +class UProceduralMeshComponent; + +UCLASS() +class VOXELHELPERS_API UVoxelHelpersLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Voxel Helpers") + static void CreateProcMeshPlane(UProceduralMeshComponent* Mesh, int32 SizeX = 512, int32 SizeY = 512, float Step = 100); +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersModule.h new file mode 100644 index 00000000..cf5a973e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/Public/VoxelHelpersModule.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelHelpers : public IModuleInterface +{ +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/VoxelHelpers.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/VoxelHelpers.Build.cs new file mode 100644 index 00000000..2d49387e --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelHelpers/VoxelHelpers.Build.cs @@ -0,0 +1,34 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelHelpers : ModuleRules +{ + public VoxelHelpers(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "UMG", + "Slate", + "SlateCore", + "ProceduralMeshComponent" + } + ); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/NiagaraDataInterfaceVoxelDataAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/NiagaraDataInterfaceVoxelDataAsset.cpp new file mode 100644 index 00000000..e0bbb6ee --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/NiagaraDataInterfaceVoxelDataAsset.cpp @@ -0,0 +1,252 @@ +// Copyright 2020 Phyronnaz + +#include "NiagaraDataInterfaceVoxelDataAsset.h" +#include "NiagaraShader.h" +#include "ShaderParameterUtils.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelMinimal.h" + +bool FNDIVoxelDataAsset_InstanceData::Init(UNiagaraDataInterfaceVoxelDataAsset* Interface, FNiagaraSystemInstance* SystemInstance) +{ + if (Interface->Asset) + { + Data = Interface->Asset->GetData(); + + auto& AssetData = *Data; + for (int32 X = 0; X < AssetData.GetSize().X; X++) + { + for (int32 Y = 0; Y < AssetData.GetSize().Y; Y++) + { + for (int32 Z = 0; Z < AssetData.GetSize().Z; Z++) + { + if (!AssetData.GetValueUnsafe(X, Y, Z).IsEmpty()) + { + Positions.Emplace(X, Y, Z); + } + } + } + } + + return true; + } + else + { + return false; + } +} + +const FName UNiagaraDataInterfaceVoxelDataAsset::GetAssetValueName(TEXT("GetVoxelDataAssetValue")); +const FName UNiagaraDataInterfaceVoxelDataAsset::GetAssetColorName(TEXT("GetVoxelDataAssetColor")); +const FName UNiagaraDataInterfaceVoxelDataAsset::GetPositionFromAssetName(TEXT("GetPositionFromVoxelDataAsset")); +const FName UNiagaraDataInterfaceVoxelDataAsset::GetNumVoxelsName(TEXT("GetNumVoxels")); + +void UNiagaraDataInterfaceVoxelDataAsset::PostInitProperties() +{ + Super::PostInitProperties(); + + if (HasAnyFlags(RF_ClassDefaultObject)) + { + PRAGMA_DISABLE_DEPRECATION_WARNINGS + FNiagaraTypeRegistry::Register(FNiagaraTypeDefinition(GetClass()), true, false, false); + PRAGMA_ENABLE_DEPRECATION_WARNINGS + } +} + +bool UNiagaraDataInterfaceVoxelDataAsset::InitPerInstanceData(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance) +{ + FNDIVoxelDataAsset_InstanceData* Inst = new (PerInstanceData) FNDIVoxelDataAsset_InstanceData(); + return Inst->Init(this, SystemInstance); +} + +void UNiagaraDataInterfaceVoxelDataAsset::DestroyPerInstanceData(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance) +{ + FNDIVoxelDataAsset_InstanceData* Inst = (FNDIVoxelDataAsset_InstanceData*)PerInstanceData; + Inst->~FNDIVoxelDataAsset_InstanceData(); +} + +bool UNiagaraDataInterfaceVoxelDataAsset::CopyToInternal(UNiagaraDataInterface* Destination) const +{ + if (!Super::CopyToInternal(Destination)) + { + return false; + } + UNiagaraDataInterfaceVoxelDataAsset* DestinationTexture = CastChecked(Destination); + DestinationTexture->Asset = Asset; + + return true; +} + +bool UNiagaraDataInterfaceVoxelDataAsset::Equals(const UNiagaraDataInterface* Other) const +{ + if (!Super::Equals(Other)) + { + return false; + } + const UNiagaraDataInterfaceVoxelDataAsset* OtherTexture = CastChecked(Other); + return OtherTexture->Asset == Asset; +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetFunctions(TArray& OutFunctions) +{ + { + FNiagaraFunctionSignature Sig; + Sig.Name = GetAssetValueName; + Sig.bMemberFunction = true; + Sig.bRequiresContext = false; + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition(GetClass()), TEXT("Asset"))); + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetVec3Def(), TEXT("Position"))); + Sig.SetDescription(VOXEL_LOCTEXT("Get a voxel data asset value")); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("Value"))); + + OutFunctions.Add(Sig); + } + { + FNiagaraFunctionSignature Sig; + Sig.Name = GetAssetColorName; + Sig.bMemberFunction = true; + Sig.bRequiresContext = false; + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition(GetClass()), TEXT("Asset"))); + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetVec3Def(), TEXT("Position"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("R"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("G"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("B"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("A"))); + Sig.SetDescription(VOXEL_LOCTEXT("Color")); + + OutFunctions.Add(Sig); + } + { + FNiagaraFunctionSignature Sig; + Sig.Name = GetPositionFromAssetName; + Sig.bMemberFunction = true; + Sig.bRequiresContext = false; + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition(GetClass()), TEXT("Asset"))); + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetIntDef(), TEXT("Index"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("X"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("Y"))); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetFloatDef(), TEXT("Z"))); + Sig.SetDescription(VOXEL_LOCTEXT("Position")); + + OutFunctions.Add(Sig); + } + { + FNiagaraFunctionSignature Sig; + Sig.Name = GetNumVoxelsName; + Sig.bMemberFunction = true; + Sig.bRequiresContext = false; + Sig.Inputs.Add(FNiagaraVariable(FNiagaraTypeDefinition(GetClass()), TEXT("Asset"))); + Sig.SetDescription(VOXEL_LOCTEXT("Get a voxel position from a voxel data asset")); + Sig.Outputs.Add(FNiagaraVariable(FNiagaraTypeDefinition::GetIntDef(), TEXT("Num"))); + + OutFunctions.Add(Sig); + } +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetVMExternalFunction(const FVMExternalFunctionBindingInfo& BindingInfo, void* InstanceData, FVMExternalFunction &OutFunc) +{ + FNDIVoxelDataAsset_InstanceData* InstData = (FNDIVoxelDataAsset_InstanceData*)InstanceData; + if (!InstData || !InstData->Data.IsValid()) + { + OutFunc = FVMExternalFunction(); + } + + if (BindingInfo.Name == GetAssetValueName) + { + check(BindingInfo.GetNumInputs() == 4 && BindingInfo.GetNumOutputs() == 1); + OutFunc = FVMExternalFunction::CreateStatic(&UNiagaraDataInterfaceVoxelDataAsset::GetAssetValue); + } + else if (BindingInfo.Name == GetAssetColorName) + { + check(BindingInfo.GetNumInputs() == 4 && BindingInfo.GetNumOutputs() == 4); + OutFunc = FVMExternalFunction::CreateStatic(&UNiagaraDataInterfaceVoxelDataAsset::GetAssetColor); + } + else if (BindingInfo.Name == GetPositionFromAssetName) + { + check(BindingInfo.GetNumInputs() == 2 && BindingInfo.GetNumOutputs() == 3); + OutFunc = FVMExternalFunction::CreateStatic(&UNiagaraDataInterfaceVoxelDataAsset::GetPositionFromAsset); + } + else if (BindingInfo.Name == GetNumVoxelsName) + { + check(BindingInfo.GetNumInputs() == 1 && BindingInfo.GetNumOutputs() == 1); + OutFunc = FVMExternalFunction::CreateStatic(&UNiagaraDataInterfaceVoxelDataAsset::GetNumVoxels); + } +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetAssetValue(FVectorVMContext& Context) +{ + VectorVM::FUserPtrHandler InstData(Context); + VectorVM::FExternalFuncInputHandler XParam(Context); + VectorVM::FExternalFuncInputHandler YParam(Context); + VectorVM::FExternalFuncInputHandler ZParam(Context); + VectorVM::FExternalFuncRegisterHandler OutValue(Context); + + auto& Data = *InstData->Data; + for (int32 i = 0; i < Context.NumInstances; ++i) + { + const float X = XParam.GetAndAdvance(); + const float Y = YParam.GetAndAdvance(); + const float Z = ZParam.GetAndAdvance(); + *OutValue.GetDestAndAdvance() = Data.GetInterpolatedValue(X, Y, Z, FVoxelValue::Empty()); + } +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetAssetColor(FVectorVMContext& Context) +{ + VectorVM::FUserPtrHandler InstData(Context); + VectorVM::FExternalFuncInputHandler XParam(Context); + VectorVM::FExternalFuncInputHandler YParam(Context); + VectorVM::FExternalFuncInputHandler ZParam(Context); + VectorVM::FExternalFuncRegisterHandler OutR(Context); + VectorVM::FExternalFuncRegisterHandler OutG(Context); + VectorVM::FExternalFuncRegisterHandler OutB(Context); + VectorVM::FExternalFuncRegisterHandler OutA(Context); + + auto& Data = *InstData->Data; + for (int32 i = 0; i < Context.NumInstances; ++i) + { + const float X = XParam.GetAndAdvance(); + const float Y = YParam.GetAndAdvance(); + const float Z = ZParam.GetAndAdvance(); + const FLinearColor Color = Data.GetInterpolatedMaterial(X, Y, Z).GetLinearColor(); + *OutR.GetDestAndAdvance() = Color.R; + *OutG.GetDestAndAdvance() = Color.G; + *OutB.GetDestAndAdvance() = Color.B; + *OutA.GetDestAndAdvance() = Color.A; + } +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetPositionFromAsset(FVectorVMContext& Context) +{ + VectorVM::FUserPtrHandler InstData(Context); + VectorVM::FExternalFuncInputHandler IndexParam(Context); + VectorVM::FExternalFuncRegisterHandler OutX(Context); + VectorVM::FExternalFuncRegisterHandler OutY(Context); + VectorVM::FExternalFuncRegisterHandler OutZ(Context); + + auto& Positions = InstData->Positions; + for (int32 i = 0; i < Context.NumInstances; ++i) + { + const int32 Index = IndexParam.GetAndAdvance(); + FVector Position(0, 0, 0); + if (Positions.IsValidIndex(Index)) + { + Position = Positions[Index]; + } + *OutX.GetDestAndAdvance() = Position.X; + *OutY.GetDestAndAdvance() = Position.Y; + *OutZ.GetDestAndAdvance() = Position.Z; + } +} + +void UNiagaraDataInterfaceVoxelDataAsset::GetNumVoxels(FVectorVMContext& Context) +{ + VectorVM::FUserPtrHandler InstData(Context); + VectorVM::FExternalFuncRegisterHandler OutNum(Context); + + const int32 Num = InstData->Positions.Num(); + for (int32 i = 0; i < Context.NumInstances; ++i) + { + *OutNum.GetDestAndAdvance() = Num; + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/VoxelNiagaraModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/VoxelNiagaraModule.cpp new file mode 100644 index 00000000..bd1bc1f7 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Private/VoxelNiagaraModule.cpp @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNiagaraModule.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FVoxelNiagara, VoxelNiagara) diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/NiagaraDataInterfaceVoxelDataAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/NiagaraDataInterfaceVoxelDataAsset.h new file mode 100644 index 00000000..b0a4abef --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/NiagaraDataInterfaceVoxelDataAsset.h @@ -0,0 +1,60 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "NiagaraDataInterface.h" +#include "NiagaraDataInterfaceVoxelDataAsset.generated.h" + +class UVoxelDataAsset; +class UNiagaraDataInterfaceVoxelDataAsset; +struct FVoxelDataAssetData; + +struct FNDIVoxelDataAsset_InstanceData +{ + TArray Positions; + TVoxelSharedPtr Data; + + bool Init(UNiagaraDataInterfaceVoxelDataAsset* Interface, FNiagaraSystemInstance* SystemInstance); +}; + +/** Data Interface allowing sampling of a voxel data asset */ +UCLASS(EditInlineNew, Category = "Voxel", meta = (DisplayName = "Voxel Data Asset Sample")) +class VOXELNIAGARA_API UNiagaraDataInterfaceVoxelDataAsset : public UNiagaraDataInterface +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditAnywhere, Category = "Voxel") + UVoxelDataAsset* Asset; + + //UObject Interface + virtual void PostInitProperties() override; + //UObject Interface End + + //UNiagaraDataInterface Interface + virtual bool InitPerInstanceData(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance) override; + virtual void DestroyPerInstanceData(void* PerInstanceData, FNiagaraSystemInstance* SystemInstance) override; + virtual int32 PerInstanceDataSize() const override { return sizeof(FNDIVoxelDataAsset_InstanceData); } + + virtual void GetFunctions(TArray& OutFunctions) override; + virtual void GetVMExternalFunction(const FVMExternalFunctionBindingInfo& BindingInfo, void* InstanceData, FVMExternalFunction &OutFunc) override; + virtual bool CanExecuteOnTarget(ENiagaraSimTarget Target) const override { return Target == ENiagaraSimTarget::CPUSim; } + //UNiagaraDataInterface Interface End + + static void GetAssetValue(FVectorVMContext& Context); + static void GetAssetColor(FVectorVMContext& Context); + static void GetPositionFromAsset(FVectorVMContext& Context); + static void GetNumVoxels(FVectorVMContext& Context); + + virtual bool Equals(const UNiagaraDataInterface* Other) const override; +protected: + virtual bool CopyToInternal(UNiagaraDataInterface* Destination) const override; + + static const FName GetAssetValueName; + static const FName GetAssetColorName; + static const FName GetPositionFromAssetName; + static const FName GetNumVoxelsName; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/VoxelNiagaraModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/VoxelNiagaraModule.h new file mode 100644 index 00000000..26661268 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/Public/VoxelNiagaraModule.h @@ -0,0 +1,10 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelNiagara : public IModuleInterface +{ +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/VoxelNiagara.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/VoxelNiagara.Build.cs new file mode 100644 index 00000000..e1f17a30 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelNiagara/VoxelNiagara.Build.cs @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelNiagara : ModuleRules +{ + public VoxelNiagara(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "Niagara", + "NiagaraCore", + "Voxel" + } + ); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/OpenVDB7/FindActiveValues.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/OpenVDB7/FindActiveValues.h new file mode 100644 index 00000000..0a3c2be4 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/OpenVDB7/FindActiveValues.h @@ -0,0 +1,435 @@ +// Copyright 2020 Phyronnaz + +// Since Unreal is bundling openvdb 4.0, it doesn't have all the latest features of 7.0 +// To mitigate that we manually include more recent headers + +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Ken Museth +// +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +// +// Redistributions of source code must retain the above copyright +// and license notice and the following restrictions and disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE +// LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. +// +/////////////////////////////////////////////////////////////////////////// +// +/// @file FindActiveValues.h +/// +/// @brief Finds the active values in a tree which intersects a bounding box. +/// Two methods are provided, one that counts the number of active values +/// and one that simply tests if any active values intersect the bbox. +/// +/// @warning For repeated calls to the free-standing functions defined below +/// consider instead creating an instance of FindActiveValues +/// and then repeatedly call its member methods. This assumes the tree +/// to be constant between calls but is sightly faster. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED + +#include +#include // for OPENVDB_VERSION_NAME +#include +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Returns true if the bounding box intersects any of the active +/// values in a tree, i.e. either active voxels or active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call any(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline bool +anyActiveValues(const TreeT& tree, const CoordBBox &bbox); + +/// @brief Returns true if the bounding box intersects none of the active +/// values in a tree, i.e. neither active voxels or active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call none(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline bool +noActiveValues(const TreeT& tree, const CoordBBox &bbox); + +/// @brief Returns the number of active values that intersects a bounding box intersects, +/// i.e. the count includes both active voxels and virtual voxels in active tiles. +/// +/// @warning For repeated calls to this method consider instead creating an instance of +/// FindActiveValues and then repeatedly call count(). This assumes the tree +/// to be constant between calls but is slightly faster. +/// +/// @param tree const tree to be tested for active values. +/// @param bbox index bounding box which is intersected against the active values. +template +inline Index64 +countActiveValues(const TreeT& tree, const CoordBBox &bbox); + +////////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Finds the active values in a tree which intersects a bounding box. +/// +/// @details Two methods are provided, one that count the number of active values +/// and one that simply tests if any active values intersect the bbox. +/// +/// @warning Tree nodes are cached by this class so it's important that the tree is not +/// modified after this class is instantiated and before its methods are called. +template +class FindActiveValues +{ +public: + + /// @brief Constructor from a const tree, which is assumed not to be modified after construction. + FindActiveValues(const TreeT& tree); + + /// @brief Default destructor + ~FindActiveValues(); + + /// @brief Initiate this class with a new (or modified) tree. + void update(const TreeT& tree); + + /// @brief Returns true if the specified bounding box intersects any active values. + /// + /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially + /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are + /// calling this method concurrently use the default setting, useAccessor = false. + bool any(const CoordBBox &bbox, bool useAccessor = false) const; + + /// @brief Returns true if the specified bounding box does not intersect any active values. + /// + /// @warning Using a ValueAccessor (i.e. useAccessor = true) can improve performance for especially + /// small bounding boxes, but at the cost of no thread-safety. So if multiple threads are + /// calling this method concurrently use the default setting, useAccessor = false. + bool none(const CoordBBox &bbox, bool useAccessor = false) const { return !this->any(bbox, useAccessor); } + + /// @brief Returns the number of active voxels intersected by the specified bounding box. + Index64 count(const CoordBBox &bbox) const; + +private: + + // Cleans up internal data structures + void clear(); + + // builds internal data structures + void init(const TreeT &tree); + + template + typename NodeT::NodeMaskType getBBoxMask(const CoordBBox &bbox, const NodeT* node) const; + + // process leaf node + inline bool any(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; + + // process leaf node + inline Index64 count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const; + + // process internal node + template + bool any(const NodeT* node, const CoordBBox &bbox) const; + + // process internal node + template + Index64 count(const NodeT* node, const CoordBBox &bbox) const; + + using AccT = tree::ValueAccessor; + using RootChildT = typename TreeT::RootNodeType::ChildNodeType; + + struct NodePairT; + + AccT mAcc; + std::vector mRootTiles;// cache bbox of child nodes (faster to cache than access RootNode) + std::vector mRootNodes;// cache bbox of acive tiles (faster to cache than access RootNode) + +};// FindActiveValues class + +////////////////////////////////////////////////////////////////////////////////////////// + +template +FindActiveValues::FindActiveValues(const TreeT& tree) : mAcc(tree), mRootTiles(), mRootNodes() +{ + this->init(tree); +} + +template +FindActiveValues::~FindActiveValues() +{ + this->clear(); +} + +template +void FindActiveValues::update(const TreeT& tree) +{ + this->clear(); + mAcc = AccT(tree); + this->init(tree); +} + +template +void FindActiveValues::clear() +{ + mRootNodes.clear(); + mRootTiles.clear(); +} + +template +void FindActiveValues::init(const TreeT& tree) +{ + for (auto i = tree.root().cbeginChildOn(); i; ++i) { + mRootNodes.emplace_back(i.getCoord(), &*i); + } + for (auto i = tree.root().cbeginValueOn(); i; ++i) { + mRootTiles.emplace_back(CoordBBox::createCube(i.getCoord(), RootChildT::DIM)); + } +} + +template +bool FindActiveValues::any(const CoordBBox &bbox, bool useAccessor) const +{ + if (useAccessor) { + if (mAcc.isValueOn( (bbox.min() + bbox.max())>>1 )) return true; + } else { + if (mAcc.tree().isValueOn( (bbox.min() + bbox.max())>>1 )) return true; + } + + for (auto& tile : mRootTiles) { + if (tile.hasOverlap(bbox)) return true; + } + for (auto& node : mRootNodes) { + if (!node.bbox.hasOverlap(bbox)) { + continue; + } else if (node.bbox.isInside(bbox)) { + return this->any(node.child, bbox); + } else if (this->any(node.child, bbox)) { + return true; + } + } + return false; +} + +template +Index64 FindActiveValues::count(const CoordBBox &bbox) const +{ + Index64 count = 0; + for (auto& tile : mRootTiles) {//loop over active tiles only + if (!tile.hasOverlap(bbox)) { + continue;//ignore non-overlapping tiles + } else if (tile.isInside(bbox)) { + return bbox.volume();// bbox is completely inside the active tile + } else if (bbox.isInside(tile)) { + count += RootChildT::NUM_VOXELS; + } else { + auto tmp = tile; + tmp.intersect(bbox); + count += tmp.volume(); + } + } + for (auto &node : mRootNodes) {//loop over child nodes of the root node only + if ( !node.bbox.hasOverlap(bbox) ) { + continue;//ignore non-overlapping child nodes + } else if ( node.bbox.isInside(bbox) ) { + return this->count(node.child, bbox);// bbox is completely inside the child node + } else { + count += this->count(node.child, bbox); + } + } + return count; +} + +template +template +typename NodeT::NodeMaskType FindActiveValues::getBBoxMask(const CoordBBox &bbox, const NodeT* node) const +{ + typename NodeT::NodeMaskType mask; + auto b = node->getNodeBoundingBox(); + assert( bbox.hasOverlap(b) ); + if ( bbox.isInside(b) ) { + mask.setOn();//node is completely inside the bbox so early out + } else { + b.intersect(bbox); + b.min() &= NodeT::DIM-1u; + b.min() >>= NodeT::ChildNodeType::TOTAL; + b.max() &= NodeT::DIM-1u; + b.max() >>= NodeT::ChildNodeType::TOTAL; + assert( b.hasVolume() ); + auto it = CoordBBox::Iterator(b); + for (const Coord& x = *it; it; ++it) { + mask.setOn(x[2] + (x[1] << NodeT::LOG2DIM) + (x[0] << 2*NodeT::LOG2DIM)); + } + } + return mask; +} + +template +template +bool FindActiveValues::any(const NodeT* node, const CoordBBox &bbox) const +{ + // Generate a bit mask of the bbox coverage + auto mask = this->getBBoxMask(bbox, node); + + // Check active tiles + const auto tmp = mask & node->getValueMask();// prune active the tile mask with the bbox mask + if (!tmp.isOff()) return true; + + // Check child nodes + mask &= node->getChildMask();// prune the child mask with the bbox mask + const auto* table = node->getTable(); + bool test = false; + for (auto i = mask.beginOn(); !test && i; ++i) { + test = this->any(table[i.pos()].getChild(), bbox); + } + return test; +} + +template +inline bool FindActiveValues::any(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const +{ + bool test = leaf->getValueMask().isOn(); + + for (auto i = leaf->cbeginValueOn(); !test && i; ++i) { + test = bbox.isInside(i.getCoord()); + } + return test; +} + +template +inline Index64 FindActiveValues::count(const typename TreeT::LeafNodeType* leaf, const CoordBBox &bbox ) const +{ + Index64 count = 0; + if (leaf->getValueMask().isOn()) { + auto b = leaf->getNodeBoundingBox(); + b.intersect(bbox); + count = b.volume(); + } else { + for (auto i = leaf->cbeginValueOn(); i; ++i) { + if (bbox.isInside(i.getCoord())) ++count; + } + } + return count; +} + +template +template +Index64 FindActiveValues::count(const NodeT* node, const CoordBBox &bbox) const +{ + Index64 count = 0; + + // Generate a bit masks + auto mask = this->getBBoxMask(bbox, node); + const auto childMask = mask & node->getChildMask();// prune the child mask with the bbox mask + mask &= node->getValueMask();// prune active tile mask with the bbox mask + const auto* table = node->getTable(); + + {// Check child nodes + using ChildT = typename NodeT::ChildNodeType; + using RangeT = tbb::blocked_range::iterator>; + std::vector childNodes(childMask.countOn()); + int j=0; + for (auto i = childMask.beginOn(); i; ++i, ++j) childNodes[j] = table[i.pos()].getChild(); + count += tbb::parallel_reduce( RangeT(childNodes.begin(), childNodes.end()), 0, + [&](const RangeT& r, Index64 sum)->Index64 { + for ( auto i = r.begin(); i != r.end(); ++i ) sum += this->count(*i, bbox); + return sum; + }, []( Index64 a, Index64 b )->Index64 { return a+b; } + ); + } + + {// Check active tiles + std::vector coords(mask.countOn()); + using RangeT = tbb::blocked_range::iterator>; + int j=0; + for (auto i = mask.beginOn(); i; ++i, ++j) coords[j] = node->offsetToGlobalCoord(i.pos()); + count += tbb::parallel_reduce( RangeT(coords.begin(), coords.end()), 0, + [&bbox](const RangeT& r, Index64 sum)->Index64 { + for ( auto i = r.begin(); i != r.end(); ++i ) { + auto b = CoordBBox::createCube(*i, NodeT::ChildNodeType::DIM); + b.intersect(bbox); + sum += b.volume(); + } + return sum; + }, []( Index64 a, Index64 b )->Index64 { return a+b; } + ); + } + + return count; +} + +template +struct FindActiveValues::NodePairT +{ + const RootChildT* child; + const CoordBBox bbox; + NodePairT(const Coord& c = Coord(), const RootChildT* p = nullptr) + : child(p), bbox(CoordBBox::createCube(c, RootChildT::DIM)) + { + } +};// NodePairT struct + +////////////////////////////////////////////////////////////////////////////////////////// + +// Implementation of stand-alone function +template +inline bool +anyActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.any(bbox); +} + +// Implementation of stand-alone function +template +inline bool +noActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.none(bbox); +} + +// Implementation of stand-alone function +template +inline bool +countActiveValues(const TreeT& tree, const CoordBBox &bbox) +{ + FindActiveValues op(tree); + return op.count(bbox); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_FINDACTIVEVALUES_HAS_BEEN_INCLUDED + +// Copyright (c) Ken Museth +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBAsset.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBAsset.cpp new file mode 100644 index 00000000..545ac872 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBAsset.cpp @@ -0,0 +1,577 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelVDBAsset.h" +#include "VoxelVDBInclude.h" + +#include "VoxelMessages.h" +#include "VoxelObjectArchive.h" +#include "VoxelFeedbackContext.h" +#include "VoxelGenerators/VoxelGeneratorHelpers.h" +#include "VoxelGenerators/VoxelTransformableGeneratorHelper.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "VoxelGenerators/VoxelEmptyGenerator.h" + +#include "Serialization/BufferArchive.h" +#include "Serialization/MemoryReader.h" + +struct FVoxelVDBAssetDataChannel +{ +public: + const EVoxelVDBChannel Channel; + float Min = 0; + float Max = 1; + FVoxelIntBox Bounds; + + explicit FVoxelVDBAssetDataChannel(EVoxelVDBChannel Channel) + : Channel(Channel) + { + } + + bool IsValid() const { return bool(Grid); } + const openvdb::FloatGrid& GetGrid() const { return *Grid; } + const openvdb::FloatGrid::Ptr& GetGridPtr() const { return Grid; } + const openvdb::tools::FindActiveValues& GetFindActiveValues() const { return *FindActiveValues; } + +public: + void SetGrid(const openvdb::FloatGrid::Ptr& NewGrid) + { + Grid = NewGrid; + if (Grid) + { + FindActiveValues = MakeUnique>(Grid->tree()); + } + else + { + FindActiveValues = nullptr; + } + } + +public: + void Save(FArchive& Ar) + { + check(Ar.IsSaving()); + + EVoxelVDBChannel ChannelCopy = Channel; + Ar << ChannelCopy; + Ar << Min; + Ar << Max; + Ar << Bounds; + + std::ostringstream StringStream(std::ios_base::binary); + openvdb::io::Stream(StringStream).write({ Grid }); + + const std::string String = StringStream.str(); + int64 Size = String.size(); + + Ar << Size; + Ar.Serialize(const_cast(String.c_str()), Size); + } + static TUniquePtr Load(FArchive& Ar) + { + check(Ar.IsLoading()); + + EVoxelVDBChannel Channel; + Ar << Channel; + + auto Result = MakeUnique(Channel); + + Ar << Result->Min; + Ar << Result->Max; + Ar << Result->Bounds; + + int64 Size = 0; + Ar << Size; + + const std::string String(Size, 0); + Ar.Serialize(const_cast(String.c_str()), Size); + + std::istringstream StringStream(String, std::ios_base::binary); + + openvdb::io::Stream Stream(StringStream); + const auto Grids = Stream.getGrids(); + + check(Grids->size() == 1); + const auto FloatGrid = openvdb::gridPtrCast(Grids->at(0)); + check(FloatGrid); + + Result->SetGrid(FloatGrid); + + return MoveTemp(Result); + } + +private: + openvdb::FloatGrid::Ptr Grid; + TUniquePtr> FindActiveValues; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelVDBAssetData::FVoxelVDBAssetData() +{ + for (uint32 Index = 0; Index < Channels.Num(); Index++) + { + Channels[Index] = MakeUnique(EVoxelVDBChannel(Index)); + } +} + +FVoxelVDBAssetData::~FVoxelVDBAssetData() +{ +} + +bool FVoxelVDBAssetData::LoadVDB(const FString& Path, FString& OutError, const TFunction&)>& GetChannelConfigs) +{ + VOXEL_FUNCTION_COUNTER(); + + try + { + openvdb::io::File File(TCHAR_TO_UTF8(*Path)); + + File.open(); + + const auto Grids = File.getGrids(); + if (Grids->size() == 0) + { + OutError = "No grids"; + return false; + } + + TMap ChannelConfigs; + TMap NamesToGrids; + for (auto& Grid : *Grids) + { + FName Name; + + try + { + const auto StdName = Grid->metaValue("name"); + Name = *FString(StdName.c_str()); + } + catch (openvdb::LookupError&) + { + Name = TEXT("UNAMED GRID"); + } + catch (openvdb::TypeError&) + { + Name = TEXT("UNAMED GRID (Type Error)"); + } + + const auto FloatGrid = openvdb::gridPtrCast(Grid); + if (!FloatGrid) + { + LOG_VOXEL(Log, TEXT("Skipping grid named %s, as it's not a float grid"), *Name.ToString()); + continue; + } + + while (ChannelConfigs.Contains(Name)) + { + Name.SetNumber(Name.GetNumber() + 1); + } + + NamesToGrids.Add(Name, FloatGrid); + + LOG_VOXEL(Log, TEXT("Found float grid named %s"), *Name.ToString()); + ChannelConfigs.Add(Name); + } + + if (GetChannelConfigs && !GetChannelConfigs(ChannelConfigs)) + { + OutError = "Cancelled"; + return false; + } + + for (auto& It : ChannelConfigs) + { + const auto Grid = NamesToGrids.FindRef(It.Key); + if (!Grid) + { + OutError = "Missing channel from vdb: " + It.Key.ToString(); + return false; + } + + float Min = MAX_flt; + float Max = -MAX_flt; + FVoxelIntBoxWithValidity Bounds; + for (openvdb::FloatGrid::ValueOnCIter Iterator = Grid->cbeginValueOn(); Iterator.test(); ++Iterator) + { + const float& Value = *Iterator; + + Min = FMath::Min(Min, Value); + Max = FMath::Max(Max, Value); + + openvdb::CoordBBox BoundingBox; + Iterator.getBoundingBox(BoundingBox); + + Bounds += FVoxelIntBox + { + FIntVector + { + BoundingBox.min().x(), + BoundingBox.min().z(), + BoundingBox.min().y(), + }, + FIntVector + { + BoundingBox.max().x() + 1, + BoundingBox.max().z() + 1, + BoundingBox.max().y() + 1, + } + }; + } + + if (Bounds.IsValid()) // Else grid is empty + { + const auto& Channel = Channels[int32(It.Value.TargetChannel)]; + Channel->SetGrid(Grid); + Channel->Min = It.Value.bAutoMinMax ? Min : It.Value.Min; + Channel->Max = It.Value.bAutoMinMax ? Max : It.Value.Max; + Channel->Bounds = Bounds.GetBox(); + } + } + + File.close(); + + return true; + } + catch (openvdb::IoError& IoError) + { + OutError = IoError.what(); + return false; + } + catch (openvdb::LookupError& LookupError) + { + OutError = LookupError.what(); + return false; + } +} + +bool FVoxelVDBAssetData::SaveVDB(const FString& Path, FString& OutError) const +{ + VOXEL_FUNCTION_COUNTER(); + + try + { + openvdb::io::File File(TCHAR_TO_UTF8(*Path)); + + openvdb::GridCPtrVec Grids; + for (auto& Channel : Channels) + { + if (Channel->IsValid()) + { + const auto Grid = Channel->GetGridPtr(); + Grid->insertMeta("name", openvdb::StringMetadata(TCHAR_TO_UTF8(*StaticEnum()->GetNameStringByValue(int64(Channel->Channel))))); + Grids.push_back(Grid); + } + } + + File.write(Grids); + File.close(); + + return true; + } + catch (openvdb::IoError& IoError) + { + OutError = IoError.what(); + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FVoxelVDBAssetData::IsValid() const +{ + for (auto& Channel : Channels) + { + if (Channel->IsValid()) + { + return true; + } + } + return false; +} + +void FVoxelVDBAssetData::Save(TArray& OutData) const +{ + FMemoryWriter Writer(OutData); + + int32 NumChannels = 0; + for (auto& Channel : Channels) + { + if (Channel->IsValid()) + { + NumChannels++; + } + } + Writer << NumChannels; + + for (auto& Channel : Channels) + { + if (Channel->IsValid()) + { + Channel->Save(Writer); + } + } +} + +void FVoxelVDBAssetData::Load(const TArray& Data) +{ + FMemoryReader Reader(Data); + + int32 NumChannels = 0; + Reader << NumChannels; + + for (int32 Index = 0; Index < NumChannels; Index++) + { + auto Channel = FVoxelVDBAssetDataChannel::Load(Reader); + Channels[int32(Channel->Channel)] = MoveTemp(Channel); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBoxWithValidity FVoxelVDBAssetData::GetBounds() const +{ + FVoxelIntBoxWithValidity Bounds; + for (auto& Channel : Channels) + { + if (Channel->IsValid()) + { + Bounds += Channel->Bounds; + } + } + return Bounds; +} + +float FVoxelVDBAssetData::GetValue(double X, double Y, double Z) const +{ + const auto& DensityChannel = Channels[int32(EVoxelVDBChannel::Density)]; + if (!DensityChannel->IsValid()) + { + return 1.f; + } + + const auto& Tree = DensityChannel->GetGrid().constTree(); + + const openvdb::Vec3R Position(X, Z, Y); + + // Compute the value of the grid at ijk via nearest-neighbor (zero-order) + // interpolation. + //float v0 = openvdb::tools::PointSampler::sample(Tree, Position); + // Compute the value via triquadratic (second-order) interpolation. + //float v2 = openvdb::tools::QuadraticSampler::sample(Tree, Position); + + return openvdb::tools::BoxSampler::sample(Tree, Position); +} + +FVoxelMaterial FVoxelVDBAssetData::GetMaterial(double X, double Y, double Z) const +{ + const openvdb::Vec3R Position(X, Z, Y); + + FVoxelMaterial Material{ ForceInit }; + +#define CHANNEL(Name) \ + { \ + const auto& Channel = Channels[int32(EVoxelVDBChannel::Name)]; \ + if (Channel->IsValid()) \ + { \ + const auto& Tree = Channel->GetGrid().constTree(); \ + const float Value = openvdb::tools::BoxSampler::sample(Tree, Position); \ + Material.Set##Name##_AsFloat((Value - Channel->Min) / (Channel->Max - Channel->Min)); \ + } \ + } + + CHANNEL(R); + CHANNEL(G); + CHANNEL(B); + CHANNEL(A); + CHANNEL(U0); + CHANNEL(U1); + CHANNEL(U2); + CHANNEL(U3); + CHANNEL(V0); + CHANNEL(V1); + CHANNEL(V2); + CHANNEL(V3); + +#undef CHANNEL + + return Material; +} + +TVoxelRange FVoxelVDBAssetData::GetValueRange(const FVoxelIntBox& Bounds) const +{ + const auto& DensityChannel = Channels[int32(EVoxelVDBChannel::Density)]; + if (!DensityChannel->IsValid()) + { + return 1.f; + } + + const openvdb::CoordBBox Box( + { + Bounds.Min.X, + Bounds.Min.Z, + Bounds.Min.Y + }, + { + Bounds.Max.X, + Bounds.Max.Z, + Bounds.Max.Y + }); + + if (DensityChannel->GetFindActiveValues().any(Box)) + { + return { -1, 1 }; + } + else + { + return 1.f; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FVoxelVDBAssetInstance : public TVoxelGeneratorInstanceHelper +{ +public: + using Super = TVoxelGeneratorInstanceHelper; + + const TVoxelSharedPtr Data; + +public: + explicit FVoxelVDBAssetInstance(UVoxelVDBAsset& Asset) + : Super(&Asset) + , Data(Asset.GetData()) + { + } + + //~ Begin FVoxelGeneratorInstance Interface + v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return Data->GetValue(X, Y, Z); + } + + FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const + { + return Data->GetMaterial(X, Y, Z); + } + + TVoxelRange GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const + { + return TVoxelRange(Data->GetValueRange(Bounds)); + } + FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final + { + return FVector::UpVector; + } + //~ End FVoxelGeneratorInstance Interface +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelIntBox UVoxelVDBAsset::GetBounds() const +{ + return Bounds; +} + +TVoxelSharedRef UVoxelVDBAsset::GetInstance() +{ + return GetInstanceImpl(); +} + +TVoxelSharedRef UVoxelVDBAsset::GetTransformableInstance() +{ + const bool bSubtractiveAsset = false; + return MakeVoxelShared>(GetInstanceImpl(), bSubtractiveAsset); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TVoxelSharedRef UVoxelVDBAsset::GetData() +{ + TryLoad(); + return Data; +} + +void UVoxelVDBAsset::SetData(const TVoxelSharedRef& InData) +{ + Data = InData; + Save(); + + const auto DataBounds = Data->GetBounds(); + Bounds = DataBounds.IsValid() ? DataBounds.GetBox() : FVoxelIntBox(); + + MemorySizeInMB = CompressedData.Num() / double(1 << 20); +} + +TVoxelSharedRef UVoxelVDBAsset::GetInstanceImpl() +{ + return MakeVoxelShared(*this); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelVDBAsset::Save() +{ + VOXEL_FUNCTION_COUNTER(); + + FVoxelScopedSlowTask Saving(1.f); + + Modify(); + + VoxelCustomVersion = FVoxelVDBAssetDataVersion::LatestVersion; + Data->Save(CompressedData); +} + + +void UVoxelVDBAsset::Load() +{ + VOXEL_FUNCTION_COUNTER(); + + if (CompressedData.Num() == 0) + { + // Nothing to load + return; + } + + Data->Load(CompressedData); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelVDBAsset::TryLoad() +{ + if (!Data->IsValid()) + { + // Seems invalid, try to load + Load(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelVDBAsset::Serialize(FArchive& Ar) +{ + Super::Serialize(Ar); + + if ((Ar.IsLoading() || Ar.IsSaving()) && !Ar.IsTransacting()) + { + CompressedData.BulkSerialize(Ar); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBInclude.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBInclude.h new file mode 100644 index 00000000..71423b18 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBInclude.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelMinimal.h" + +THIRD_PARTY_INCLUDES_START +#pragma warning(push) +#pragma warning(disable: 4146) + +#include +#include +#include + +#include "OpenVDB7/FindActiveValues.h" + +#pragma warning(pop) +THIRD_PARTY_INCLUDES_END \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBModule.cpp new file mode 100644 index 00000000..13d91841 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Private/VoxelVDBModule.cpp @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelVDBModule.h" +#include "Modules/ModuleManager.h" +#include "VoxelVDBInclude.h" + +IMPLEMENT_MODULE(FVoxelVDB, VoxelVDB) + +void FVoxelVDB::StartupModule() +{ + openvdb::initialize(); +} diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBAsset.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBAsset.h new file mode 100644 index 00000000..ccfb76ea --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBAsset.h @@ -0,0 +1,168 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelRange.h" +#include "VoxelContainers/VoxelStaticArray.h" +#include "VoxelGenerators/VoxelGenerator.h" +#include "VoxelVDBAsset.generated.h" + +class UVoxelVDBAsset; +class FVoxelVDBAssetInstance; +struct FVoxelMaterial; +struct FVoxelVDBAssetDataChannel; + +UENUM() +enum class EVoxelVDBChannel +{ + Density, + R, + G, + B, + A, + U0, + U1, + U2, + U3, + V0, + V1, + V2, + V3, + Max UMETA(Hidden) +}; + +USTRUCT(BlueprintType) +struct FVoxelVDBImportChannelConfig +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + EVoxelVDBChannel TargetChannel = EVoxelVDBChannel::Density; + + // If true, will automatically assign the Min/Max used for normalization to the min & max of the input data + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + bool bAutoMinMax = false; + + // Min/Max, used to normalize the input data. Min = 0 and Max = 1 does nothing. + // Result = (Value - Min) / (Max - Min) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (EditCondition = "!bNormalize")) + float Min = 0.f; + + // Min/Max, used to normalize the input data. Min = 0 and Max = 1 does nothing. + // Result = (Value - Min) / (Max - Min) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel", meta = (EditCondition = "!bNormalize")) + float Max = 1.f; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +namespace FVoxelVDBAssetDataVersion +{ + enum Type : int32 + { + BeforeCustomVersionWasAdded, + SHARED_PlaceableItemsInSave, + SHARED_AssetItemsImportValueMaterials, + SHARED_DataAssetScale, + SHARED_RemoveVoxelGrass, + SHARED_DataAssetTransform, + SHARED_RemoveEnableVoxelSpawnedActorsEnableVoxelGrass, + SHARED_FoliagePaint, + SHARED_ValueConfigFlagAndSaveGUIDs, + SHARED_SingleValues, + SHARED_NoVoxelMaterialInHeightmapAssets, + SHARED_FixMissingMaterialsInHeightmapAssets, + SHARED_AddUserFlagsToSaves, + SHARED_StoreSpawnerMatricesRelativeToComponent, + SHARED_StoreMaterialChannelsIndividuallyAndRemoveFoliage, + + // ------------------------------------------------------ + VersionPlusOne, + LatestVersion = VersionPlusOne - 1 + }; +}; + +class VOXELVDB_API FVoxelVDBAssetData +{ +public: + FVoxelVDBAssetData(); + ~FVoxelVDBAssetData(); + +public: + bool LoadVDB(const FString& Path, FString& OutError, const TFunction&)>& GetChannelConfigs); + bool SaveVDB(const FString& Path, FString& OutError) const; + +public: + bool IsValid() const; + + void Save(TArray& OutData) const; + void Load(const TArray& Data); + + FVoxelIntBoxWithValidity GetBounds() const; + +public: + float GetValue(double X, double Y, double Z) const; + FVoxelMaterial GetMaterial(double X, double Y, double Z) const; + + TVoxelRange GetValueRange(const FVoxelIntBox& Bounds) const; + +private: + TVoxelStaticArray, int32(EVoxelVDBChannel::Max)> Channels{ ForceInit }; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UCLASS(HideDropdown, BlueprintType) +class VOXELVDB_API UVoxelVDBAsset : public UVoxelTransformableGeneratorWithBounds +{ + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Asset") + FVoxelIntBox Bounds; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Asset") + float MemorySizeInMB; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Import") + FString ImportPath; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Import") + TMap ChannelConfigs; + +public: + //~ Begin UVoxelGenerator Interface + virtual FVoxelIntBox GetBounds() const override; + virtual TVoxelSharedRef GetInstance() override; + virtual TVoxelSharedRef GetTransformableInstance() override final; + //~ End UVoxelGenerator Interface + +public: + TVoxelSharedRef GetData(); + void SetData(const TVoxelSharedRef& InData); + +protected: + void Save(); + void Load(); + + void TryLoad(); + + TVoxelSharedRef GetInstanceImpl(); + +protected: + virtual void Serialize(FArchive& Ar) override; + +private: + TVoxelSharedRef Data = MakeVoxelShared(); + +private: + UPROPERTY() + int32 VoxelCustomVersion; + + TArray CompressedData; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBModule.h new file mode 100644 index 00000000..5646700b --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/Public/VoxelVDBModule.h @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelVDB : public IModuleInterface +{ + virtual void StartupModule() override; +}; diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/VoxelVDB.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/VoxelVDB.Build.cs new file mode 100644 index 00000000..b92bceab --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDB/VoxelVDB.Build.cs @@ -0,0 +1,39 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelVDB : ModuleRules +{ + public VoxelVDB(ReadOnlyTargetRules Target) : base(Target) +{ + // OpenVDB/boost requires to have both of these on + // RTTI is a global setting in packaged games: as such, this module is editor only by default + // Set it to Runtime in Voxel.uplugin if you wish to use VDB at runtime + bUseRTTI = true; + bEnableExceptions = true; + + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Voxel", + "OpenVDB", + "UEOpenExr", + "Core", + "CoreUObject", + "Engine" + } + ); + } +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBEditorModule.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBEditorModule.cpp new file mode 100644 index 00000000..28fe7cd0 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBEditorModule.cpp @@ -0,0 +1,136 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelVDBEditorModule.h" +#include "VoxelMinimal.h" +#include "VoxelVDBAsset.h" +#include "VoxelEditorModule.h" + +#include "IAssetTools.h" +#include "AssetToolsModule.h" +#include "AssetTypeActions_Base.h" + +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Commands/UIAction.h" +#include "Framework/Application/SlateApplication.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Widgets/Notifications/SNotificationList.h" + +#include "HAL/FileManager.h" + +#include "EditorStyleSet.h" +#include "EditorReimportHandler.h" +#include "IDesktopPlatform.h" +#include "DesktopPlatformModule.h" + +class FAssetTypeActions_VoxelVDBAsset : public FAssetTypeActions_Base +{ +public: + virtual FText GetName() const override { return VOXEL_LOCTEXT("Voxel VDB Asset"); } + virtual FColor GetTypeColor() const override { return FColor(128, 0, 64); } + virtual UClass* GetSupportedClass() const override { return UVoxelVDBAsset::StaticClass(); } + virtual uint32 GetCategories() override + { + return FModuleManager::LoadModuleChecked("VoxelEditor").GetVoxelAssetTypeCategory(); + } + + virtual bool HasActions(const TArray& InObjects) const override { return true; } + virtual void GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) override + { + const auto Assets = GetTypedWeakObjectPtrs(InObjects); + + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Reimport"), + VOXEL_LOCTEXT("Reimport the selected asset(s)."), + FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.ReimportAsset"), + FUIAction( + FExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelVDBAsset::ExecuteReimport, Assets), + FCanExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelVDBAsset::CanExecuteReimport, Assets) + ) + ); + MenuBuilder.AddMenuEntry( + VOXEL_LOCTEXT("Export"), + VOXEL_LOCTEXT("Export the selected asset(s)."), + FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.OpenInExternalEditor"), + FUIAction( + FExecuteAction::CreateSP(this, &FAssetTypeActions_VoxelVDBAsset::ExecuteExport, Assets) + ) + ); + } + +private: + bool CanExecuteReimport(const TArray> Objects) const + { + for (auto& Object : Objects) + { + if (Object.IsValid() && !Object->ImportPath.IsEmpty()) + { + return true; + } + } + return false; + } + + void ExecuteReimport(const TArray> Objects) const + { + for (auto& Object : Objects) + { + if (Object.IsValid() && !Object->ImportPath.IsEmpty()) + { + FReimportManager::Instance()->Reimport(Object.Get(), /*bAskForNewFileIfMissing=*/true); + } + } + } + void ExecuteExport(const TArray> Objects) const + { + for (auto& Object : Objects) + { + if (Object.IsValid()) + { + const FString DefaultPath = FPaths::ProjectSavedDir() / "VDB"; + IFileManager::Get().MakeDirectory(*DefaultPath); + + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + check(DesktopPlatform); + + TArray OutFiles; + if (DesktopPlatform->SaveFileDialog( + nullptr, + "Destination", + DefaultPath, + Object->GetName() + ".vdb", + TEXT("VDB file (*.vdb)|*.vdb"), + EFileDialogFlags::None, + OutFiles)) + { + check(OutFiles.Num() == 1); + const FString Path = OutFiles[0]; + + FString Error; + const bool bSuccess = Object->GetData()->SaveVDB(Path, Error); + + const FString Text = bSuccess + ? "Successfully exported " + Path + : "Failed to export " + Path + ": " + Error; + + FNotificationInfo Info(FText::FromString(Text)); + Info.ExpireDuration = 10.f; + Info.CheckBoxState = bSuccess ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + FSlateNotificationManager::Get().AddNotification(Info); + } + } + } + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelVDBEditorModule::StartupModule() +{ + IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + + AssetTools.RegisterAssetTypeActions(MakeShared()); +} + +IMPLEMENT_MODULE(FVoxelVDBEditorModule, VoxelVDBEditor); \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.cpp b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.cpp new file mode 100644 index 00000000..dbe65128 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.cpp @@ -0,0 +1,218 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelVDBFactory.h" +#include "VoxelMessages.h" + +#include "Misc/Paths.h" + +#include "Widgets/SWindow.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SUniformGridPanel.h" +#include "Widgets/Layout/SScrollBox.h" + +#include "Editor.h" +#include "EditorStyleSet.h" +#include "PropertyEditorModule.h" +#include "DetailLayoutBuilder.h" + +#include "Modules/ModuleManager.h" + +UVoxelVDBFactory::UVoxelVDBFactory() +{ + bEditorImport = true; + SupportedClass = UVoxelVDBAsset::StaticClass(); + Formats.Add(TEXT("vdb;OpenVDB file")); +} + +bool UVoxelVDBFactory::FactoryCanImport(const FString& Filename) +{ + return FPaths::GetExtension(Filename) == TEXT("vdb"); +} + +UObject* UVoxelVDBFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) +{ + auto* NewAsset = NewObject(InParent, InName, Flags | RF_Transactional); + if (DoImport(*NewAsset, Filename, {})) + { + return NewAsset; + } + else + { + return nullptr; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelVDBFactory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + if (auto* Asset = Cast(Obj)) +{ + OutFilenames = { Asset->ImportPath }; + return true; + } + return false; +} + +void UVoxelVDBFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + auto* Asset = Cast(Obj); + if (Asset && NewReimportPaths.Num() > 0) + { + Asset->ImportPath = NewReimportPaths[0]; + } +} + +EReimportResult::Type UVoxelVDBFactory::Reimport(UObject* Obj) +{ + if (auto* Asset = Cast(Obj)) + { + if (DoImport(*Asset, Asset->ImportPath, Asset->ChannelConfigs)) + { + return EReimportResult::Succeeded; + } + else + { + return EReimportResult::Failed; + } + } + return EReimportResult::Failed; +} + +int32 UVoxelVDBFactory::GetPriority() const +{ + return ImportPriority; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool UVoxelVDBFactory::DoImport(UVoxelVDBAsset& Asset, const FString& Path, const TMap& ExistingChannelConfigs) +{ + const auto Data = MakeVoxelShared(); + + FString Error; + + const auto GetChannelConfigs = [&](TMap& ChannelConfigs) + { + SetFlags(RF_Transactional); + + Mappings.Reset(); + for (auto& It : ChannelConfigs) + { + auto Value = It.Value; + if (auto* ExistingValue = ExistingChannelConfigs.Find(It.Key)) + { + Value = *ExistingValue; + } + Mappings.Add({ It.Key, Value }); + } + + TSharedRef PickerWindow = SNew(SWindow) + .Title(VOXEL_LOCTEXT("Import Heightmap")) + .SizingRule(ESizingRule::UserSized) + .ClientSize(FVector2D(600, 400)); + + bool bSuccess = false; + + auto OnOkClicked = FOnClicked::CreateLambda([&]() + { + bSuccess = true; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }); + auto OnCancelClicked = FOnClicked::CreateLambda([&]() + { + bSuccess = false; + PickerWindow->RequestDestroyWindow(); + return FReply::Handled(); + }); + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea); + + auto DetailsPanel = PropertyEditorModule.CreateDetailView(DetailsViewArgs); + DetailsPanel->SetObject(this); + + auto Widget = + SNew(SBorder) + .Visibility(EVisibility::Visible) + .BorderImage(FEditorStyle::GetBrush("Menu.Background")) + [ + SNew(SBox) + .Visibility(EVisibility::Visible) + .WidthOverride(520.0f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .FillHeight(1.f) + [ + SNew(SScrollBox) + + SScrollBox::Slot() + [ + DetailsPanel + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + .VAlign(VAlign_Bottom) + .Padding(8) + [ + SNew(SUniformGridPanel) + .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding")) + + SUniformGridPanel::Slot(0, 0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Create")) + .HAlign(HAlign_Center) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnOkClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Success") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + + SUniformGridPanel::Slot(1,0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Cancel")) + .HAlign(HAlign_Center) + .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnCancelClicked) + .ButtonStyle(FEditorStyle::Get(), "FlatButton.Default") + .TextStyle(FEditorStyle::Get(), "FlatButton.DefaultTextStyle") + ] + ] + ] + ]; + + PickerWindow->SetContent(Widget); + + GEditor->EditorAddModalWindow(PickerWindow); + + for (auto& Mapping : Mappings) + { + if (auto* Value = ChannelConfigs.Find(Mapping.ChannelName)) + { + *Value = Mapping.ChannelConfig; + } + } + + Asset.ImportPath = Path; + Asset.ChannelConfigs = ChannelConfigs; + + return bSuccess; + }; + + if (!Data->LoadVDB(Path, Error, GetChannelConfigs)) + { + FVoxelMessages::Error("Failed to import VDB: " + Error); + return false; + } + + Asset.SetData(Data); + return true; +} \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.h new file mode 100644 index 00000000..c6dcf2d1 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Private/VoxelVDBFactory.h @@ -0,0 +1,48 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "EditorReimportHandler.h" +#include "VoxelVDBAsset.h" +#include "VoxelVDBFactory.generated.h" + +USTRUCT() +struct FVoxelVDBFactoryChannelMapping +{ + GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Category = "Voxel") + FName ChannelName; + + UPROPERTY(EditAnywhere, Category = "Voxel") + FVoxelVDBImportChannelConfig ChannelConfig; +}; + +UCLASS(HideCategories=Object, CollapseCategories) +class UVoxelVDBFactory : public UFactory, public FReimportHandler +{ + GENERATED_BODY() + +public: + UVoxelVDBFactory(); + + UPROPERTY(EditAnywhere, Category = "Import configuration") + TArray Mappings; + + // UFactory interface + virtual bool FactoryCanImport(const FString& Filename) override; + virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override; + // End of UFactory interface + + //~ Begin FReimportHandler Interface + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + virtual int32 GetPriority() const override; + //~ End FReimportHandler Interface + +private: + bool DoImport(UVoxelVDBAsset& Asset, const FString& Path, const TMap& ExistingChannelConfigs); +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Public/VoxelVDBEditorModule.h b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Public/VoxelVDBEditorModule.h new file mode 100644 index 00000000..9f105326 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/Public/VoxelVDBEditorModule.h @@ -0,0 +1,12 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +class FVoxelVDBEditorModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; +}; \ No newline at end of file diff --git a/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/VoxelVDBEditor.Build.cs b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/VoxelVDBEditor.Build.cs new file mode 100644 index 00000000..9fd34490 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/Source/VoxelVDBEditor/VoxelVDBEditor.Build.cs @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +using System.IO; +using UnrealBuildTool; + +public class VoxelVDBEditor : ModuleRules +{ + public VoxelVDBEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + bEnforceIWYU = true; + bLegacyPublicIncludePaths = false; + +#if UE_4_24_OR_LATER +#else +#endif + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PrivateDependencyModuleNames.AddRange( + new string[] { + "Core", + "CoreUObject", + "SlateCore", + "Slate", + "EditorStyle", + "Voxel", + "VoxelEditor", + "VoxelVDB", + "DesktopPlatform", + "UnrealEd" + }); + } +} diff --git a/voxel_cpp_test/Plugins/Voxel/VoxelPro.uplugin b/voxel_cpp_test/Plugins/Voxel/VoxelPro.uplugin new file mode 100644 index 00000000..8979b1f5 --- /dev/null +++ b/voxel_cpp_test/Plugins/Voxel/VoxelPro.uplugin @@ -0,0 +1,129 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.2.1-Marketplace-Pro-2021-08-21", + "FriendlyName": "Voxel Plugin Pro", + "Description": "Create fully volumetric, entirely destructible & infinite worlds", + "Category": "Terrain", + "CreatedBy": "voxelplugin.com", + "CreatedByURL": "https://voxelplugin.com", + "DocsURL": "https://wiki.voxelplugin.com/", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/b08e5581837e4bbca486b61cdf2751bb", + "SupportURL": "https://discord.voxelplugin.com", + "EngineVersion": "4.27.0", + "CanContainContent": true, + "Installed": true, + "Modules": [ + { + "Name": "Voxel", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Android", + "Mac" + ] + }, + { + "Name": "VoxelGraph", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Android", + "Mac" + ] + }, + { + "Name": "VoxelHelpers", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Android", + "Mac" + ] + }, + { + "Name": "VoxelVDB", + "Type": "Editor", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64" + ] + }, + { + "Name": "VoxelVDBEditor", + "Type": "Editor", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64" + ] + }, + { + "Name": "VoxelNiagara", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Android", + "Mac" + ] + }, + { + "Name": "VoxelEditor", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Mac" + ] + }, + { + "Name": "VoxelGraphEditor", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Mac" + ] + }, + { + "Name": "VoxelEditorDefault", + "Type": "Editor", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Mac" + ] + }, + { + "Name": "VoxelExamples", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "Android", + "Mac" + ] + } + ], + "Plugins": [ + { + "Name": "Niagara", + "Enabled": true + }, + { + "Name": "ProceduralMeshComponent", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/voxel_cpp_test/Saved/AutoScreenshot.png b/voxel_cpp_test/Saved/AutoScreenshot.png new file mode 100644 index 00000000..5bbd3115 Binary files /dev/null and b/voxel_cpp_test/Saved/AutoScreenshot.png differ diff --git a/voxel_cpp_test/Saved/Autosaves/PackageRestoreData.json b/voxel_cpp_test/Saved/Autosaves/PackageRestoreData.json new file mode 100644 index 00000000..a6c46436 Binary files /dev/null and b/voxel_cpp_test/Saved/Autosaves/PackageRestoreData.json differ diff --git a/voxel_cpp_test/Saved/Config/WorldState/2145691951.json b/voxel_cpp_test/Saved/Config/WorldState/2145691951.json new file mode 100644 index 00000000..8371c710 Binary files /dev/null and b/voxel_cpp_test/Saved/Config/WorldState/2145691951.json differ diff --git a/voxel_cpp_test/voxel_cpp_test.uproject b/voxel_cpp_test/voxel_cpp_test.uproject index 4cec1b94..87826145 100644 --- a/voxel_cpp_test/voxel_cpp_test.uproject +++ b/voxel_cpp_test/voxel_cpp_test.uproject @@ -9,5 +9,12 @@ "Type": "Runtime", "LoadingPhase": "Default" } + ], + "Plugins": [ + { + "Name": "VoxelPro", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/b08e5581837e4bbca486b61cdf2751bb" + } ] } \ No newline at end of file