diff --git a/CelticCraft.uproject b/CelticCraft.uproject new file mode 100644 index 0000000..19634e4 --- /dev/null +++ b/CelticCraft.uproject @@ -0,0 +1,15 @@ +{ + "FileVersion": 3, + "EngineAssociation": "5.1", + "Category": "", + "Description": "", + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + } + ] +} \ No newline at end of file diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 0000000..4ecb6eb --- /dev/null +++ b/Config/DefaultEditor.ini @@ -0,0 +1,8 @@ +[UnrealEd.SimpleMap] +SimpleMapName=/Game/TP_ThirdPerson/Maps/ThirdPersonExampleMap + +[EditoronlyBP] +bAllowClassAndBlueprintPinMatching=true +bReplaceBlueprintWithClass= true +bDontLoadBlueprintOutsideEditor= true +bBlueprintIsNotBlueprintType= true \ No newline at end of file diff --git a/Config/DefaultEditorPerProjectUserSettings.ini b/Config/DefaultEditorPerProjectUserSettings.ini new file mode 100644 index 0000000..d3d6cc0 --- /dev/null +++ b/Config/DefaultEditorPerProjectUserSettings.ini @@ -0,0 +1,2 @@ +[ContentBrowser] +ContentBrowserTab1.SelectedPaths=/Game/ThirdPersonBP \ No newline at end of file diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 0000000..8127502 --- /dev/null +++ b/Config/DefaultEngine.ini @@ -0,0 +1,76 @@ +[URL] +GameName=CelticCraft + +[/Script/EngineSettings.GameMapsSettings] +EditorStartupMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap +GameDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap +TransitionMap= +bUseSplitscreen=True +TwoPlayerSplitscreenLayout=Horizontal +ThreePlayerSplitscreenLayout=FavorTop +GlobalDefaultGameMode=/Game/ThirdPerson/Blueprints/BP_ThirdPersonGameMode.BP_ThirdPersonGameMode_C +GlobalDefaultServerGameMode=None + +[/Script/Engine.RendererSettings] +r.ReflectionMethod=1 +r.GenerateMeshDistanceFields=True +r.DynamicGlobalIlluminationMethod=1 +r.Lumen.TraceMeshSDFs=0 +r.Shadow.Virtual.Enable=1 +r.Mobile.EnableNoPrecomputedLightingCSMShader=1 +r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True +r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=true + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 +-D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM6 +-D3D11TargetedShaderFormats=PCD3D_SM5 ++D3D11TargetedShaderFormats=PCD3D_SM5 +Compiler=Default +AudioSampleRate=48000 +AudioCallbackBufferFrameSize=1024 +AudioNumBuffersToEnqueue=1 +AudioMaxChannels=0 +AudioNumSourceWorkers=4 +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) +CacheSizeKB=65536 +MaxChunkSizeOverrideKB=0 +bResampleForDevice=False +MaxSampleRate=48000.000000 +HighSampleRate=32000.000000 +MedSampleRate=24000.000000 +LowSampleRate=12000.000000 +MinSampleRate=8000.000000 +CompressionQualityModifier=1.000000 +AutoStreamingThreshold=0.000000 +SoundCueCookQualityIndex=-1 + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_ThirdPersonBP",NewGameName="/Script/CelticCraft") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_ThirdPersonBP",NewGameName="/Script/CelticCraft") + +[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] +bEnablePlugin=True +bAllowNetworkConnection=True +SecurityToken=AD1890BC452C7CDDA698AEB7E00BA404 +bIncludeInShipping=False +bAllowExternalStartInShipping=False +bCompileAFSProject=False +bUseCompression=False +bLogFiles=False +bReportStats=False +ConnectionType=USBOnly +bUseManualIPAddress=False +ManualIPAddress= + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 0000000..b96d840 --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,7 @@ +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=EE49771146245D5AE39F59ABD288372D +ProjectName=Third Person BP Game Template + +[StartupActions] +bAddPacks=True +InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent") diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini new file mode 100644 index 0000000..9b1348f --- /dev/null +++ b/Config/DefaultInput.ini @@ -0,0 +1,88 @@ + + +[/Script/Engine.InputSettings] +-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) ++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +bAltEnterTogglesFullscreen=True +bF11TogglesFullscreen=True +bUseMouseForTouch=False +bEnableMouseSmoothing=True +bEnableFOVScaling=True +bCaptureMouseOnLaunch=True +bEnableLegacyInputScales=True +bEnableMotionControls=True +bFilterInputByPlatformUser=False +bShouldFlushPressedKeysOnViewportFocusLost=True +bAlwaysShowTouchInterface=False +bShowConsoleOnFourFingerTap=True +bEnableGestureRecognizer=False +bUseAutocorrect=False +DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown +DefaultViewportMouseLockMode=LockOnCapture +FOVScale=0.011110 +DoubleClickTime=0.200000 +DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput +DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent +DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks +-ConsoleKeys=Tilde ++ConsoleKeys=Tilde ++ConsoleKeys=Caret + diff --git a/Plugins/VoxelFree/Config/BaseVoxelFree.ini b/Plugins/VoxelFree/Config/BaseVoxelFree.ini new file mode 100644 index 0000000..ad4e441 --- /dev/null +++ b/Plugins/VoxelFree/Config/BaseVoxelFree.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/Plugins/VoxelFree/Config/DefaultVoxelFree.ini b/Plugins/VoxelFree/Config/DefaultVoxelFree.ini new file mode 100644 index 0000000..ad4e441 --- /dev/null +++ b/Plugins/VoxelFree/Config/DefaultVoxelFree.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/Plugins/VoxelFree/Config/FilterPlugin.ini b/Plugins/VoxelFree/Config/FilterPlugin.ini new file mode 100644 index 0000000..efc58de --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Content/Blueprints/VoxelFunctionLibrary.uasset b/Plugins/VoxelFree/Content/Blueprints/VoxelFunctionLibrary.uasset new file mode 100644 index 0000000..5810b43 Binary files /dev/null and b/Plugins/VoxelFree/Content/Blueprints/VoxelFunctionLibrary.uasset differ diff --git a/Plugins/VoxelFree/Content/Blueprints/VoxelMacroLibrary.uasset b/Plugins/VoxelFree/Content/Blueprints/VoxelMacroLibrary.uasset new file mode 100644 index 0000000..37558a8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Blueprints/VoxelMacroLibrary.uasset differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_16x.png new file mode 100644 index 0000000..c70773d Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_64x.png new file mode 100644 index 0000000..91eb273 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/DataAsset_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_16x.png new file mode 100644 index 0000000..9395f6b Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.pdn b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.pdn new file mode 100644 index 0000000..c3db6e5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.pdn differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.png new file mode 100644 index 0000000..ef7af9c Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Generator_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_16x.png new file mode 100644 index 0000000..8852d36 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_64x.png new file mode 100644 index 0000000..8f05669 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Import_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_16x.png new file mode 100644 index 0000000..052c126 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_64x.png new file mode 100644 index 0000000..92d4069 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Landscape_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_16x.png new file mode 100644 index 0000000..a41027f Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_64x.png new file mode 100644 index 0000000..3238261 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/MaterialCollection_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_16x.png new file mode 100644 index 0000000..b81ff8f Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_64x.png new file mode 100644 index 0000000..2dd7a4b Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerConfig_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_16x.png new file mode 100644 index 0000000..19535ab Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_64x.png new file mode 100644 index 0000000..d28435d Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/SpawnerGroup_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_16x.png new file mode 100644 index 0000000..f48b1d1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_64x.png new file mode 100644 index 0000000..f592aa1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/Spawner_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_16x.png new file mode 100644 index 0000000..cb1b60e Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.pdn b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.pdn new file mode 100644 index 0000000..8b22293 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.pdn differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.png new file mode 100644 index 0000000..a4ecab1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelAsset_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_16x.png new file mode 100644 index 0000000..bc4709c Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_64x.png new file mode 100644 index 0000000..34efe9e Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelGraph_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png new file mode 100644 index 0000000..046d06a Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png new file mode 100644 index 0000000..2fdc36f Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/VoxelWorldSaveObject_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/World_16x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/World_16x.png new file mode 100644 index 0000000..90563bc Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/World_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/AssetIcons/World_64x.png b/Plugins/VoxelFree/Content/Editor/AssetIcons/World_64x.png new file mode 100644 index 0000000..310483e Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/AssetIcons/World_64x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_16x.png b/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_16x.png new file mode 100644 index 0000000..43d9107 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_16x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_40x.png b/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_40x.png new file mode 100644 index 0000000..357595b Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/InvertDataAsset_40x.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/FlattenTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/FlattenTool_40.png new file mode 100644 index 0000000..2178eed Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/FlattenTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/LevelTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/LevelTool_40.png new file mode 100644 index 0000000..2178eed Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/LevelTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/MeshTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/MeshTool_40.png new file mode 100644 index 0000000..38e7736 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/MeshTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/RevertTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/RevertTool_40.png new file mode 100644 index 0000000..36e693e Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/RevertTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SmoothTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SmoothTool_40.png new file mode 100644 index 0000000..c0b6cb5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SmoothTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SphereTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SphereTool_40.png new file mode 100644 index 0000000..3c59742 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SphereTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SurfaceTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SurfaceTool_40.png new file mode 100644 index 0000000..aa98669 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/SurfaceTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/TrimTool_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/TrimTool_40.png new file mode 100644 index 0000000..d1e2eb9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/Tools/TrimTool_40.png differ diff --git a/Plugins/VoxelFree/Content/Editor/UIIcons/mode_40.png b/Plugins/VoxelFree/Content/Editor/UIIcons/mode_40.png new file mode 100644 index 0000000..71297bb Binary files /dev/null and b/Plugins/VoxelFree/Content/Editor/UIIcons/mode_40.png differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController.uasset new file mode 100644 index 0000000..6a8a3ce Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset new file mode 100644 index 0000000..103c1d3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Mode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset new file mode 100644 index 0000000..8e4ca61 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_SaveExample.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset new file mode 100644 index 0000000..1578655 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_Tool.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI.uasset new file mode 100644 index 0000000..e513e49 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset new file mode 100644 index 0000000..f9547f8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexController_UI_Paint.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexGameMode.uasset b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexGameMode.uasset new file mode 100644 index 0000000..043e109 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ComplexController/VoxelComplexGameMode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset b/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset new file mode 100644 index 0000000..500fe7d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestructionGameMode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset b/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset new file mode 100644 index 0000000..5f89371 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/CubicDestruction/CubicDestruction_FirstPersonCharacter.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset b/Plugins/VoxelFree/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset new file mode 100644 index 0000000..4023163 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/CubicDestruction/OrangeFlatWorldGenerator.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset new file mode 100644 index 0000000..3b5ef5f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset new file mode 100644 index 0000000..ca63bfc Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_GraphConfig.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset new file mode 100644 index 0000000..63d0b0d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Material.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset new file mode 100644 index 0000000..89efa35 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/GraphColorOutputToTexture/VoxelExamples_ColorOutput_Plane_Blueprint.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset new file mode 100644 index 0000000..f94d235 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset new file mode 100644 index 0000000..cf78814 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_Color.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset new file mode 100644 index 0000000..82a10b5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_M_Chair_Emissive_UVs.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset new file mode 100644 index 0000000..2fdadb2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_SM_Chair.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset new file mode 100644 index 0000000..b0619fb Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_M.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset new file mode 100644 index 0000000..3ae74b8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/Chair/VoxelExample_T_Chair_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset new file mode 100644 index 0000000..5d035e3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_ChairMesh_Import2.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset new file mode 100644 index 0000000..c55ffad Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_LandscapeImport.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset new file mode 100644 index 0000000..ab29130 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_MagicaVoxelImport.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset new file mode 100644 index 0000000..cd278ef Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Importers/VoxelExample_RawVoxImport.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap b/Plugins/VoxelFree/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap new file mode 100644 index 0000000..621a3e3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/CubicDestruction/CubicDestruction.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap new file mode 100644 index 0000000..1cd14cb Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cave_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap new file mode 100644 index 0000000..9357466 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Cliffs_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap new file mode 100644 index 0000000..9552154 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Craters_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap new file mode 100644 index 0000000..6490bc4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Dunes_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap new file mode 100644 index 0000000..83fbed6 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Erosion_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap new file mode 100644 index 0000000..90bca15 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FastCraters_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap new file mode 100644 index 0000000..63b0b47 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_FloatingIslandOnion_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap new file mode 100644 index 0000000..f37af98 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_HollowPlanet_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap new file mode 100644 index 0000000..b0d4cc8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_IQNoise_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap new file mode 100644 index 0000000..97cda92 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_MultiIndex_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap new file mode 100644 index 0000000..caeb650 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Planet_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap new file mode 100644 index 0000000..fb2d581 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_Ravines_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap new file mode 100644 index 0000000..168da6a Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Generators/VoxelExample_RingWorld_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap b/Plugins/VoxelFree/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap new file mode 100644 index 0000000..fc42676 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Importers/VoxelExample_ImportersMap.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap b/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap new file mode 100644 index 0000000..c9a3f79 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_ManualMultiplayerMap.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap b/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap new file mode 100644 index 0000000..0417739 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Multiplayer/VoxelExample_TcpMultiplayerMap.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap b/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap new file mode 100644 index 0000000..e737cfa Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredPlanet.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap b/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap new file mode 100644 index 0000000..0532b24 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/PerlinWorms/VoxelExample_LayeredWorld.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap new file mode 100644 index 0000000..757806b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Flat_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap new file mode 100644 index 0000000..90054cd Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_HeightSpawners_Sphere_Map.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap new file mode 100644 index 0000000..45babd5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Spawners/VoxelExample_RaySpawnersMap.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Maps/Tools/HighResolutionDigging.umap b/Plugins/VoxelFree/Content/Examples/Maps/Tools/HighResolutionDigging.umap new file mode 100644 index 0000000..184d035 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Maps/Tools/HighResolutionDigging.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset b/Plugins/VoxelFree/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset new file mode 100644 index 0000000..5c8ffb8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/MS_VoxelPlanetTriplanar.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset b/Plugins/VoxelFree/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset new file mode 100644 index 0000000..edfc7fc Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/M_VoxelMaterial_MagicaVox.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel.uasset new file mode 100644 index 0000000..05a1c60 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset new file mode 100644 index 0000000..8db6615 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MCT_Quixel_Tess.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel.uasset new file mode 100644 index 0000000..faffe6a Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset new file mode 100644 index 0000000..7db5fa0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MC_Quixel_Tess.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset new file mode 100644 index 0000000..3b48852 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset new file mode 100644 index 0000000..250a525 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelCollection.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset new file mode 100644 index 0000000..5f7583c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelHeightBlend.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset new file mode 100644 index 0000000..a1fc0b8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MF_VoxelQuixelStaticSwitch.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset new file mode 100644 index 0000000..9dd8c02 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset new file mode 100644 index 0000000..b5c942d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixelCollection_Tess.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset new file mode 100644 index 0000000..8a4b0c0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset new file mode 100644 index 0000000..1306992 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Inst.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset new file mode 100644 index 0000000..8f84b64 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset new file mode 100644 index 0000000..adaf40a Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MI_VoxelQuixel_FiveWayBlend_Tess_Inst.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset new file mode 100644 index 0000000..9acd37d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset new file mode 100644 index 0000000..e4b132e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset new file mode 100644 index 0000000..7f75a3e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/Quixel/MS_VoxelQuixelCollection_Tess.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset b/Plugins/VoxelFree/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset new file mode 100644 index 0000000..b2c3cd5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/RGB/MI_VoxelMaterial_Colors_WithShift.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset b/Plugins/VoxelFree/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset new file mode 100644 index 0000000..e45e6b5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/RGB/MS_VoxelMaterial_Colors_WithShift.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset new file mode 100644 index 0000000..d713744 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset new file mode 100644 index 0000000..36500d5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_Parameter.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset new file mode 100644 index 0000000..28a1c98 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/RGB/M_VoxelMaterial_Colors_SurfaceNets.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Materials/T_Palette.uasset b/Plugins/VoxelFree/Content/Examples/Materials/T_Palette.uasset new file mode 100644 index 0000000..488e3e3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Materials/T_Palette.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset new file mode 100644 index 0000000..a6a636f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset new file mode 100644 index 0000000..7927cab Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_CircularBuffer_Library.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset new file mode 100644 index 0000000..10913f2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelManualMultiplayerController_State.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset new file mode 100644 index 0000000..61954da Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/Manual/VoxelWorldExample_ManualMultiplayer.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset new file mode 100644 index 0000000..6daadc2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/Tcp/VoxelWorldExample_Multiplayer_Tcp.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset new file mode 100644 index 0000000..9c786ec Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerCharacter.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset new file mode 100644 index 0000000..fbe56ac Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerController.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset new file mode 100644 index 0000000..7b4b603 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_MultiplayerGameMode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset new file mode 100644 index 0000000..e426b69 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelExample_Multiplayer_SurfaceTool.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset new file mode 100644 index 0000000..ab58f2f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Multiplayer/VoxelWorldExample_MultiplayerBase.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap new file mode 100644 index 0000000..1cc4887 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset new file mode 100644 index 0000000..ae154ea Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_CubeBP.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset new file mode 100644 index 0000000..5bb9427 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/MultiIndex/VoxelExample_ReadVoxelMaterialFromHit_MultiIndex_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap new file mode 100644 index 0000000..43dafce Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset new file mode 100644 index 0000000..35fa42b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_CubeBP.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset new file mode 100644 index 0000000..d2b5e9f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/ReadVoxelMaterialFromHit/RGB/VoxelExample_ReadVoxelMaterialFromHit_RGB_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/RuntimeCopyPaste.uasset b/Plugins/VoxelFree/Content/Examples/RuntimeCopyPaste.uasset new file mode 100644 index 0000000..d235acf Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/RuntimeCopyPaste.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/RuntimeMeshImportExample.uasset b/Plugins/VoxelFree/Content/Examples/RuntimeMeshImportExample.uasset new file mode 100644 index 0000000..e2c282b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/RuntimeMeshImportExample.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset new file mode 100644 index 0000000..1ccba87 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPersonFire_Montage.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset new file mode 100644 index 0000000..a7831ee Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_AnimBP.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset new file mode 100644 index 0000000..334fb50 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Fire.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset new file mode 100644 index 0000000..7b342a9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Idle.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset new file mode 100644 index 0000000..ffc34ae Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpEnd.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset new file mode 100644 index 0000000..6fbf25f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpLoop.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset new file mode 100644 index 0000000..535f567 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_JumpStart.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset new file mode 100644 index 0000000..8251ab1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Animations/FirstPerson_Run.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset new file mode 100644 index 0000000..24f2af0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Audio/FirstPersonTemplateWeaponFire02.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset new file mode 100644 index 0000000..9b27e32 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/M_UE4Man_Body.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset new file mode 100644 index 0000000..e72926f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 0000000..e90177c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 0000000..9e1a558 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset new file mode 100644 index 0000000..5417fb8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset new file mode 100644 index 0000000..2f3076d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset new file mode 100644 index 0000000..ad48d07 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 0000000..30c509d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 0000000..9923d78 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset new file mode 100644 index 0000000..af130e4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset new file mode 100644 index 0000000..f6e36d0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_PhysicsAsset.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset new file mode 100644 index 0000000..da423ed Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset new file mode 100644 index 0000000..19ae3c9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_LOGO_CARD.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset new file mode 100644 index 0000000..6a7a448 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset new file mode 100644 index 0000000..e643579 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Character/Textures/UE4_Mannequin__normals.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset new file mode 100644 index 0000000..cde0b1c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/M_FPGun.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset new file mode 100644 index 0000000..fb68fe9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset new file mode 100644 index 0000000..67cf190 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset new file mode 100644 index 0000000..f7e9577 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset new file mode 100644 index 0000000..0ea4af6 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset new file mode 100644 index 0000000..c6ba631 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset new file mode 100644 index 0000000..5bafc8e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset new file mode 100644 index 0000000..661cd5b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset new file mode 100644 index 0000000..737d18b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset new file mode 100644 index 0000000..1733c68 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset new file mode 100644 index 0000000..ead5a03 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset new file mode 100644 index 0000000..c4f452d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset new file mode 100644 index 0000000..637bb9e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset new file mode 100644 index 0000000..2cecc98 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset new file mode 100644 index 0000000..ec2c527 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_M.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset new file mode 100644 index 0000000..de4e2ff Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FPWeapon/Textures/T_FPGun_N.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset new file mode 100644 index 0000000..1421019 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/FirstPersonHUD.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset new file mode 100644 index 0000000..6f80e82 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/FirstPerson/Textures/FirstPersonCrosshair.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/M_Shovel.uasset b/Plugins/VoxelFree/Content/Examples/Shared/M_Shovel.uasset new file mode 100644 index 0000000..b852bf0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/M_Shovel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Shovel.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Shovel.uasset new file mode 100644 index 0000000..f4cd54c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Shovel.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset new file mode 100644 index 0000000..54bf0aa Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset new file mode 100644 index 0000000..1db6345 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultAlbedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset new file mode 100644 index 0000000..c84a2a2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHRA.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset new file mode 100644 index 0000000..7dbf074 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultHeightmap.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset new file mode 100644 index 0000000..a7c51d1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/Default/T_DefaultNormalmap.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset new file mode 100644 index 0000000..f1993c7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/MI_AerialGrassRock.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset new file mode 100644 index 0000000..1b398e5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset new file mode 100644 index 0000000..434ca03 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset new file mode 100644 index 0000000..f181fe2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset new file mode 100644 index 0000000..1f09e60 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/AerialGrassRock/T_AerialGrassRock_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset new file mode 100644 index 0000000..cf2bde2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/MI_BrownMudRocks.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset new file mode 100644 index 0000000..1c1ed5c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset new file mode 100644 index 0000000..dcecba8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Arm.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset new file mode 100644 index 0000000..b7c8799 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset new file mode 100644 index 0000000..bf62675 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/BrownMudRocks/T_BrownMudRocks_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset new file mode 100644 index 0000000..11e5234 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/MI_CastleWallSlates.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset new file mode 100644 index 0000000..88a56f3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset new file mode 100644 index 0000000..c411c73 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset new file mode 100644 index 0000000..9993853 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset new file mode 100644 index 0000000..90466cf Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CastleWallSlates/T_CastleWallSlates_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset new file mode 100644 index 0000000..344637b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/MI_ConcreteFloor.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset new file mode 100644 index 0000000..2ee151e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset new file mode 100644 index 0000000..722586e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset new file mode 100644 index 0000000..1041eba Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset new file mode 100644 index 0000000..651cfeb Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/ConcreteFloor/T_ConcreteFloor_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset new file mode 100644 index 0000000..5b66562 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/MI_CoralMud.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset new file mode 100644 index 0000000..9097429 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset new file mode 100644 index 0000000..54b541e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset new file mode 100644 index 0000000..6cc8662 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset new file mode 100644 index 0000000..7124213 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/CoralMud/T_CoralMud_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset new file mode 100644 index 0000000..481acad Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MI_AerialGrassRock_BrownMudRocks.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset new file mode 100644 index 0000000..94c2760 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/MI_MedievalBlocks.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset new file mode 100644 index 0000000..a4d1972 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset new file mode 100644 index 0000000..93a96ce Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset new file mode 100644 index 0000000..c7c7ac8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset new file mode 100644 index 0000000..5ac9866 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/MedievalBlocks/T_MedievalBlocks_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset new file mode 100644 index 0000000..e96b946 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/MI_Pavement.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset new file mode 100644 index 0000000..c58e91e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset new file mode 100644 index 0000000..bc45695 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset new file mode 100644 index 0000000..adf12a8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset new file mode 100644 index 0000000..1b7ae05 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Pavement/T_Pavement_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset new file mode 100644 index 0000000..ac284e7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/MI_Rock.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset new file mode 100644 index 0000000..133ba75 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset new file mode 100644 index 0000000..f203c1c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset new file mode 100644 index 0000000..e807660 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset new file mode 100644 index 0000000..a1811a8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Rock/T_Rock_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset new file mode 100644 index 0000000..2b42175 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/MI_RockGround.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset new file mode 100644 index 0000000..9e940ce Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset new file mode 100644 index 0000000..5a15c84 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset new file mode 100644 index 0000000..10aa2c3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset new file mode 100644 index 0000000..a72891e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/RockGround/T_RockGround_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset new file mode 100644 index 0000000..9fe7378 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/MI_Snow.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset new file mode 100644 index 0000000..3d47d2c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_ARM.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset new file mode 100644 index 0000000..d565e1c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Albedo.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset new file mode 100644 index 0000000..b8ce2d6 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Height.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset new file mode 100644 index 0000000..c5f3eaf Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/Textures/TextureHaven/Snow/T_Snow_Normal.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset b/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset new file mode 100644 index 0000000..9756b05 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SimpleColorMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset b/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset new file mode 100644 index 0000000..0c2bc6d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/VoxelExamples_SpectatorGameMode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Shared/fork.uasset b/Plugins/VoxelFree/Content/Examples/Shared/fork.uasset new file mode 100644 index 0000000..9aa3147 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Shared/fork.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleController.uasset b/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleController.uasset new file mode 100644 index 0000000..cf8813e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleController.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset b/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset new file mode 100644 index 0000000..543fb54 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/SimpleController/VoxelSimpleGameMode.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset new file mode 100644 index 0000000..d1d72f9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Config.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset new file mode 100644 index 0000000..169f5c6 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Flat/VoxelExample_HeightSpawners_Flat_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset new file mode 100644 index 0000000..5ba460b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Config.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset new file mode 100644 index 0000000..0ebccce Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/Sphere/VoxelExample_HeightSpawners_Sphere_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset new file mode 100644 index 0000000..8815b00 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/HeightSpawners/VoxelExample_HeightSpawnersGraphOutputs.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset new file mode 100644 index 0000000..481bfbf Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersConfig.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset new file mode 100644 index 0000000..788015e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset new file mode 100644 index 0000000..5d3773f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/RaySpawners/VoxelExample_RaySpawnersGraphOutputs.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset new file mode 100644 index 0000000..1fa253b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset new file mode 100644 index 0000000..d24f2c9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_CubeSpawner_Big.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset new file mode 100644 index 0000000..28f6d87 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset new file mode 100644 index 0000000..faaf05e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Spawners/VoxelExample_SphereSpawner_Big.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools.umap b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools.umap new file mode 100644 index 0000000..f72ce4a Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools.umap differ diff --git a/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset new file mode 100644 index 0000000..35e8074 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_SaveObject.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset new file mode 100644 index 0000000..0cb3c25 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tests/Tools/VoxelTest_Tools_VoxelWorld.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/BoxTool.uasset b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/BoxTool.uasset new file mode 100644 index 0000000..1636306 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/BoxTool.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/GraphTool.uasset b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/GraphTool.uasset new file mode 100644 index 0000000..4e876e7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/GraphTool.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/MySphereTool.uasset b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/MySphereTool.uasset new file mode 100644 index 0000000..0419169 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tools/CustomTools/MySphereTool.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset b/Plugins/VoxelFree/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset new file mode 100644 index 0000000..022508c Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/Tools/HighResolutionDigging/HighResolutionDigging_Shovel_Blueprint.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelDefaultBrushMask.uasset b/Plugins/VoxelFree/Content/Examples/VoxelDefaultBrushMask.uasset new file mode 100644 index 0000000..6ad50c8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelDefaultBrushMask.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosionBP.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosionBP.uasset new file mode 100644 index 0000000..e56672d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosionBP.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset new file mode 100644 index 0000000..3b5580d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_DummyRenderTarget.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset new file mode 100644 index 0000000..add82b4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_FinalGraph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset new file mode 100644 index 0000000..875622f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_HeightMapGraph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset new file mode 100644 index 0000000..56d2e3d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_PreviewMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset new file mode 100644 index 0000000..0ca7e9f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelErosion/VoxelErosion_RainMapGraph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset new file mode 100644 index 0000000..8417582 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cave/VoxelExample_Cave.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset new file mode 100644 index 0000000..393e500 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Cliffs/VoxelExample_Cliffs.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset new file mode 100644 index 0000000..82e9776 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset new file mode 100644 index 0000000..fd9bfcc Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VDI_Example_Crater_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset new file mode 100644 index 0000000..7015a4e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Craters/VG_Example_Craters.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset new file mode 100644 index 0000000..946b393 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Dunes/VG_Example_Dunes.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset new file mode 100644 index 0000000..5d96518 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Erosion/VG_Example_Erosion.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset new file mode 100644 index 0000000..6349969 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FastCraters/VG_Example_FastCraters.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset new file mode 100644 index 0000000..e5f0f30 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/FloatingIslandOnion/VoxelExample_FloatingIslandOnion.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset new file mode 100644 index 0000000..d68b257 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/VoxelExample_HeightmapComposition.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset new file mode 100644 index 0000000..fca5825 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y0.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset new file mode 100644 index 0000000..1dc4059 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x0_y1.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset new file mode 100644 index 0000000..c87c531 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y0.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset new file mode 100644 index 0000000..d444c8e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HeightmapComposition/heightmap_x1_y1.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset new file mode 100644 index 0000000..a1f1442 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/HollowPlanet/VoxelExample_HollowPlanet.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset new file mode 100644 index 0000000..f6739d9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/IQNoise/VoxelExample_IQNoise.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset new file mode 100644 index 0000000..2538e36 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/MultiIndex/VG_Example_MultiIndex.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset new file mode 100644 index 0000000..ae484c5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredPlanet.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset new file mode 100644 index 0000000..cd2561b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset new file mode 100644 index 0000000..3a5cb5b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/PerlinWorms/VoxelExample_LayeredWorld_Curve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset new file mode 100644 index 0000000..d7d259a Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset new file mode 100644 index 0000000..6e62f7f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_ColorCurve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset new file mode 100644 index 0000000..9b20549 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Planet/VoxelExample_Planet_Curve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset new file mode 100644 index 0000000..fe6ab48 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Ravines/VoxelExample_Ravines.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset new file mode 100644 index 0000000..ce3c607 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/MoutainsMaskCurve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset new file mode 100644 index 0000000..9753e8f Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/PlainsNoiseStrengthCurve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset new file mode 100644 index 0000000..d862f0b Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RingMainShapeCurve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset new file mode 100644 index 0000000..dba4a0d Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/RiverDepthCurve.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset new file mode 100644 index 0000000..967aa08 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/RingWorld/VoxelExample_RingWorld.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset new file mode 100644 index 0000000..8e21bfc Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/Tools/Tool_NoisyColors.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset new file mode 100644 index 0000000..83ff77e Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/VoxelGraphs/VoxelGraph_Mask_Cellular.uasset differ diff --git a/Plugins/VoxelFree/Content/Examples/alpha_brush.png b/Plugins/VoxelFree/Content/Examples/alpha_brush.png new file mode 100644 index 0000000..7aa9712 Binary files /dev/null and b/Plugins/VoxelFree/Content/Examples/alpha_brush.png differ diff --git a/Plugins/VoxelFree/Content/Macros/Difference.uasset b/Plugins/VoxelFree/Content/Macros/Difference.uasset new file mode 100644 index 0000000..bffa3f2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Difference.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/FastLerp.uasset b/Plugins/VoxelFree/Content/Macros/FastLerp.uasset new file mode 100644 index 0000000..45e42e3 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/FastLerp.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/FlowControl/FourWayFlowMerge.uasset b/Plugins/VoxelFree/Content/Macros/FlowControl/FourWayFlowMerge.uasset new file mode 100644 index 0000000..dd1e460 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/FlowControl/FourWayFlowMerge.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/GetSlopeFromDerivatives.uasset b/Plugins/VoxelFree/Content/Macros/GetSlopeFromDerivatives.uasset new file mode 100644 index 0000000..23ccdf0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/GetSlopeFromDerivatives.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphas.uasset b/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphas.uasset new file mode 100644 index 0000000..8cfcb52 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphas.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphasUsingDistances.uasset b/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphasUsingDistances.uasset new file mode 100644 index 0000000..0875ce1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/GetVoronoiAlphasUsingDistances.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/GraphAssetMax.uasset b/Plugins/VoxelFree/Content/Macros/GraphAssetMax.uasset new file mode 100644 index 0000000..2135a97 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/GraphAssetMax.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/GraphAssetMin.uasset b/Plugins/VoxelFree/Content/Macros/GraphAssetMin.uasset new file mode 100644 index 0000000..49d0d73 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/GraphAssetMin.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/IsPreview.uasset b/Plugins/VoxelFree/Content/Macros/IsPreview.uasset new file mode 100644 index 0000000..71fa4f5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/IsPreview.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/IsRangeAnalysis.uasset b/Plugins/VoxelFree/Content/Macros/IsRangeAnalysis.uasset new file mode 100644 index 0000000..2035f34 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/IsRangeAnalysis.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/LerpColors.uasset b/Plugins/VoxelFree/Content/Macros/LerpColors.uasset new file mode 100644 index 0000000..ac0a8c7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/LerpColors.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Map.uasset b/Plugins/VoxelFree/Content/Macros/Map.uasset new file mode 100644 index 0000000..d12cd91 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Map.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Math/DegreesToRadians.uasset b/Plugins/VoxelFree/Content/Macros/Math/DegreesToRadians.uasset new file mode 100644 index 0000000..bed36f0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Math/DegreesToRadians.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Math/RadiansToDegrees.uasset b/Plugins/VoxelFree/Content/Macros/Math/RadiansToDegrees.uasset new file mode 100644 index 0000000..a9ca234 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Math/RadiansToDegrees.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Misc/CaveLayer.uasset b/Plugins/VoxelFree/Content/Macros/Misc/CaveLayer.uasset new file mode 100644 index 0000000..7544dec Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Misc/CaveLayer.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Modulate.uasset b/Plugins/VoxelFree/Content/Macros/Modulate.uasset new file mode 100644 index 0000000..0bceb85 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Modulate.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset b/Plugins/VoxelFree/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset new file mode 100644 index 0000000..adcd23a Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/PlaceableItems/AddDefaultDataItems.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/PlotToPreview.uasset b/Plugins/VoxelFree/Content/Macros/PlotToPreview.uasset new file mode 100644 index 0000000..de4c266 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/PlotToPreview.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/PreviewSize.uasset b/Plugins/VoxelFree/Content/Macros/PreviewSize.uasset new file mode 100644 index 0000000..fc23e59 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/PreviewSize.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/PreviousGeneratorValueMaterial.uasset b/Plugins/VoxelFree/Content/Macros/PreviousGeneratorValueMaterial.uasset new file mode 100644 index 0000000..cf52782 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/PreviousGeneratorValueMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Ramp.uasset b/Plugins/VoxelFree/Content/Macros/Ramp.uasset new file mode 100644 index 0000000..4ce2fe5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Ramp.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/RandomColor.uasset b/Plugins/VoxelFree/Content/Macros/RandomColor.uasset new file mode 100644 index 0000000..5033405 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/RandomColor.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SCurve3.uasset b/Plugins/VoxelFree/Content/Macros/SCurve3.uasset new file mode 100644 index 0000000..f1477e7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SCurve3.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SDF/2DNoiseSDF.uasset b/Plugins/VoxelFree/Content/Macros/SDF/2DNoiseSDF.uasset new file mode 100644 index 0000000..3b23d90 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SDF/2DNoiseSDF.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SDF/Elongate.uasset b/Plugins/VoxelFree/Content/Macros/SDF/Elongate.uasset new file mode 100644 index 0000000..72e1b4e Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SDF/Elongate.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SDF/MirrorAxis.uasset b/Plugins/VoxelFree/Content/Macros/SDF/MirrorAxis.uasset new file mode 100644 index 0000000..2103d67 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SDF/MirrorAxis.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SDF/RoundSDF.uasset b/Plugins/VoxelFree/Content/Macros/SDF/RoundSDF.uasset new file mode 100644 index 0000000..1b7f5e9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SDF/RoundSDF.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SDF/Shell.uasset b/Plugins/VoxelFree/Content/Macros/SDF/Shell.uasset new file mode 100644 index 0000000..ff723e7 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SDF/Shell.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SafeLerp.uasset b/Plugins/VoxelFree/Content/Macros/SafeLerp.uasset new file mode 100644 index 0000000..3a5e17a Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SafeLerp.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Select.uasset b/Plugins/VoxelFree/Content/Macros/Select.uasset new file mode 100644 index 0000000..0688331 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Select.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SetHighQualityValue.uasset b/Plugins/VoxelFree/Content/Macros/SetHighQualityValue.uasset new file mode 100644 index 0000000..60a43c4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SetHighQualityValue.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SetSphereUpVector.uasset b/Plugins/VoxelFree/Content/Macros/SetSphereUpVector.uasset new file mode 100644 index 0000000..8c4b06d Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SetSphereUpVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothIntersection.uasset b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothIntersection.uasset new file mode 100644 index 0000000..904a71b Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothIntersection.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothSubtract.uasset b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothSubtract.uasset new file mode 100644 index 0000000..8b8cfc2 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothSubtract.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothUnion.uasset b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothUnion.uasset new file mode 100644 index 0000000..6f2ef0f Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SmoothOps/SmoothUnion.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SphereNormalizeWithPreview.uasset b/Plugins/VoxelFree/Content/Macros/SphereNormalizeWithPreview.uasset new file mode 100644 index 0000000..1468838 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SphereNormalizeWithPreview.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Step.uasset b/Plugins/VoxelFree/Content/Macros/Step.uasset new file mode 100644 index 0000000..cfca2bd Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Step.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/SwitchPreview.uasset b/Plugins/VoxelFree/Content/Macros/SwitchPreview.uasset new file mode 100644 index 0000000..85171a0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/SwitchPreview.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Terrace.uasset b/Plugins/VoxelFree/Content/Macros/Terrace.uasset new file mode 100644 index 0000000..2509e8b Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Terrace.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/AbsVector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AbsVector2.uasset new file mode 100644 index 0000000..d9c9206 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AbsVector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Float.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Float.uasset new file mode 100644 index 0000000..b17d832 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Float.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Vector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Vector2.uasset new file mode 100644 index 0000000..52c616d Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/AddVector2Vector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Float.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Float.uasset new file mode 100644 index 0000000..8f81704 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Float.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset new file mode 100644 index 0000000..dac8f60 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DivideVector2Vector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/DotProductVector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DotProductVector2.uasset new file mode 100644 index 0000000..3d89efc Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/DotProductVector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/LerpVector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/LerpVector2.uasset new file mode 100644 index 0000000..b949643 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/LerpVector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset new file mode 100644 index 0000000..d912184 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Float.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset new file mode 100644 index 0000000..9a038f9 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/MultiplyVector2Vector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/Normalize.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Normalize.uasset new file mode 100644 index 0000000..40b175a Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Normalize.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Float.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Float.uasset new file mode 100644 index 0000000..7ccf222 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Float.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset new file mode 100644 index 0000000..a4df0d8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SubtractVector2Vector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/SwitchVector2.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SwitchVector2.uasset new file mode 100644 index 0000000..4e6be91 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/SwitchVector2.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Distance.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Distance.uasset new file mode 100644 index 0000000..a2f37f4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Distance.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Length.uasset b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Length.uasset new file mode 100644 index 0000000..2634e17 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/Vector2Ops/Vector2Length.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/AbsVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/AbsVector.uasset new file mode 100644 index 0000000..ba88a05 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/AbsVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorFloat.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorFloat.uasset new file mode 100644 index 0000000..c22a96e Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorFloat.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorVector.uasset new file mode 100644 index 0000000..39c38ca Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/AddVectorVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/BreakVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/BreakVector.uasset new file mode 100644 index 0000000..9e5b27d Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/BreakVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/ClampVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/ClampVector.uasset new file mode 100644 index 0000000..00ed0d4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/ClampVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorFloat.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorFloat.uasset new file mode 100644 index 0000000..db38066 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorFloat.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorVector.uasset new file mode 100644 index 0000000..2d5d5b0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/DivideVectorVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/DotProductVector3.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/DotProductVector3.uasset new file mode 100644 index 0000000..70366b8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/DotProductVector3.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/LerpVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/LerpVector.uasset new file mode 100644 index 0000000..ebb11c5 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/LerpVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/MakeVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/MakeVector.uasset new file mode 100644 index 0000000..993e78d Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/MakeVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/MaxVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/MaxVector.uasset new file mode 100644 index 0000000..d1c3570 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/MaxVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/MinVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/MinVector.uasset new file mode 100644 index 0000000..f01d77d Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/MinVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorFloat.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorFloat.uasset new file mode 100644 index 0000000..4db88aa Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorFloat.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorVector.uasset new file mode 100644 index 0000000..953f7ae Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/MultiplyVectorVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/Normalize.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/Normalize.uasset new file mode 100644 index 0000000..9ecc84f Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/Normalize.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorFloat.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorFloat.uasset new file mode 100644 index 0000000..4000694 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorFloat.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorVector.uasset new file mode 100644 index 0000000..0a631b0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/SubtractVectorVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/SwitchVector.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/SwitchVector.uasset new file mode 100644 index 0000000..603b4e1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/SwitchVector.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VectorOps/XYZ.uasset b/Plugins/VoxelFree/Content/Macros/VectorOps/XYZ.uasset new file mode 100644 index 0000000..219bfc4 Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VectorOps/XYZ.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VoronoiLerp.uasset b/Plugins/VoxelFree/Content/Macros/VoronoiLerp.uasset new file mode 100644 index 0000000..2ed708c Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VoronoiLerp.uasset differ diff --git a/Plugins/VoxelFree/Content/Macros/VoronoiLerp_2Data.uasset b/Plugins/VoxelFree/Content/Macros/VoronoiLerp_2Data.uasset new file mode 100644 index 0000000..dc3aafc Binary files /dev/null and b/Plugins/VoxelFree/Content/Macros/VoronoiLerp_2Data.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset new file mode 100644 index 0000000..37169cd Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Debug_UsePerInstanceRandomAsColor.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset new file mode 100644 index 0000000..b9d6a59 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/GetColorFromInstanceRandom.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset new file mode 100644 index 0000000..fb5a059 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelMaterialFromInstanceRandom.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset new file mode 100644 index 0000000..26f48c1 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSpawnerActorPerInstanceRandom.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset new file mode 100644 index 0000000..16e7055 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/GetVoxelSurfaceNetsWorldOffset.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/MeshImporterMaterial.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/MeshImporterMaterial.uasset new file mode 100644 index 0000000..b03ad9d Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/MeshImporterMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/VoxelOpacity.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/VoxelOpacity.uasset new file mode 100644 index 0000000..15e2a2c Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/VoxelOpacity.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset new file mode 100644 index 0000000..0290c5b Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexBlends.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset new file mode 100644 index 0000000..c73fd1a Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetMultiIndexWetness.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset new file mode 100644 index 0000000..9a74d9f Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_GetSingleIndex.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_HeightBlend.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_HeightBlend.uasset new file mode 100644 index 0000000..a3089cc Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_HeightBlend.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalNormal.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalNormal.uasset new file mode 100644 index 0000000..801c126 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalNormal.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalPosition.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalPosition.uasset new file mode 100644 index 0000000..ca52165 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_LocalPosition.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_PlanetBlend.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_PlanetBlend.uasset new file mode 100644 index 0000000..17f8f00 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_PlanetBlend.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset new file mode 100644 index 0000000..79c48e8 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_TessellationMultiplier.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset new file mode 100644 index 0000000..ea30153 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedAlphas.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset new file mode 100644 index 0000000..60860c9 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedNormals.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset new file mode 100644 index 0000000..7eefc22 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedTextures.uasset differ diff --git a/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset new file mode 100644 index 0000000..3527d02 Binary files /dev/null and b/Plugins/VoxelFree/Content/MaterialHelpers/Voxel_WorldAlignedUVs.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/CA_Gradient.uasset b/Plugins/VoxelFree/Content/Preview/CA_Gradient.uasset new file mode 100644 index 0000000..3293d11 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/CA_Gradient.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/C_GradientNoWater.uasset b/Plugins/VoxelFree/Content/Preview/C_GradientNoWater.uasset new file mode 100644 index 0000000..c142f9b Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/C_GradientNoWater.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/C_GradientWater.uasset b/Plugins/VoxelFree/Content/Preview/C_GradientWater.uasset new file mode 100644 index 0000000..fe16ae1 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/C_GradientWater.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/DefaultVoxelPreview.uasset b/Plugins/VoxelFree/Content/Preview/DefaultVoxelPreview.uasset new file mode 100644 index 0000000..f9c812f Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/DefaultVoxelPreview.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/M_2DPreviewMaterial.uasset b/Plugins/VoxelFree/Content/Preview/M_2DPreviewMaterial.uasset new file mode 100644 index 0000000..bd242e0 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/M_2DPreviewMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/M_PreviewMaterial.uasset b/Plugins/VoxelFree/Content/Preview/M_PreviewMaterial.uasset new file mode 100644 index 0000000..838ccf8 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/M_PreviewMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/Plane.fbx b/Plugins/VoxelFree/Content/Preview/Plane.fbx new file mode 100644 index 0000000..4321d5f Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/Plane.fbx differ diff --git a/Plugins/VoxelFree/Content/Preview/SM_Plane.uasset b/Plugins/VoxelFree/Content/Preview/SM_Plane.uasset new file mode 100644 index 0000000..0ac6e30 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/SM_Plane.uasset differ diff --git a/Plugins/VoxelFree/Content/Preview/VoxelRayMarchHeightMap.uasset b/Plugins/VoxelFree/Content/Preview/VoxelRayMarchHeightMap.uasset new file mode 100644 index 0000000..77b1525 Binary files /dev/null and b/Plugins/VoxelFree/Content/Preview/VoxelRayMarchHeightMap.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial.uasset new file mode 100644 index 0000000..6cf9084 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Level.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Level.uasset new file mode 100644 index 0000000..f4b0dae Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Level.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset new file mode 100644 index 0000000..660a3c2 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Mesh.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset new file mode 100644 index 0000000..908087b Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolMeshMaterial_Sphere.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial.uasset new file mode 100644 index 0000000..ed1079e Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset new file mode 100644 index 0000000..23045d9 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Flatten.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset new file mode 100644 index 0000000..dcbe368 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Smooth.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset new file mode 100644 index 0000000..1f92e98 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Sphere.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset new file mode 100644 index 0000000..d2be231 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Surface.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset new file mode 100644 index 0000000..fda0f03 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ToolRenderingMaterial_Trim.uasset differ diff --git a/Plugins/VoxelFree/Content/ToolMaterials/ViewportPlaneMaterial.uasset b/Plugins/VoxelFree/Content/ToolMaterials/ViewportPlaneMaterial.uasset new file mode 100644 index 0000000..8432ab2 Binary files /dev/null and b/Plugins/VoxelFree/Content/ToolMaterials/ViewportPlaneMaterial.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule.uasset new file mode 100644 index 0000000..2fcc4cc Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule_Graph.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule_Graph.uasset new file mode 100644 index 0000000..22e548d Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Capsule_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Ravine_Graph.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Ravine_Graph.uasset new file mode 100644 index 0000000..e16653c Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Ravine_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere.uasset new file mode 100644 index 0000000..f4aad72 Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere_Graph.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere_Graph.uasset new file mode 100644 index 0000000..9cfc563 Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VDI_Sphere_Graph.uasset differ diff --git a/Plugins/VoxelFree/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset b/Plugins/VoxelFree/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset new file mode 100644 index 0000000..5e83149 Binary files /dev/null and b/Plugins/VoxelFree/Content/VoxelDataItems/VoxelDataItemsMacroLibrary.uasset differ diff --git a/Plugins/VoxelFree/Content/palette.tga b/Plugins/VoxelFree/Content/palette.tga new file mode 100644 index 0000000..dda09ad Binary files /dev/null and b/Plugins/VoxelFree/Content/palette.tga differ diff --git a/Plugins/VoxelFree/Resources/Icon128.png b/Plugins/VoxelFree/Resources/Icon128.png new file mode 100644 index 0000000..20a4d52 Binary files /dev/null and b/Plugins/VoxelFree/Resources/Icon128.png differ diff --git a/Plugins/VoxelFree/Shaders/Private/DistanceField.usf b/Plugins/VoxelFree/Shaders/Private/DistanceField.usf new file mode 100644 index 0000000..8ca5782 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Shaders/Private/Erosion.usf b/Plugins/VoxelFree/Shaders/Private/Erosion.usf new file mode 100644 index 0000000..0eaf968 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel.natvis b/Plugins/VoxelFree/Source/Voxel.natvis new file mode 100644 index 0000000..d7790b5 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel.natvis @@ -0,0 +1,15 @@ + + + + + + Num={$T2} + + + $T2 + (TVoxelStaticArray<$T1,$T2,$T3>::ElementType*)Data + + + + + diff --git a/Plugins/VoxelFree/Source/Voxel/Private/FastNoise/LICENSE b/Plugins/VoxelFree/Source/Voxel/Private/FastNoise/LICENSE new file mode 100644 index 0000000..5ee8678 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/FastNoise/VoxelFastNoiseLUT.cpp b/Plugins/VoxelFree/Source/Voxel/Private/FastNoise/VoxelFastNoiseLUT.cpp new file mode 100644 index 0000000..b30d068 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/IVoxelPool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/IVoxelPool.cpp new file mode 100644 index 0000000..ac231ac --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelDataAsset.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelDataAsset.cpp new file mode 100644 index 0000000..5c2342b --- /dev/null +++ b/Plugins/VoxelFree/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->GetPlatformData()->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/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelDataAssetData.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelDataAssetData.cpp new file mode 100644 index 0000000..440acbb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelHeightmapAsset.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelAssets/VoxelHeightmapAsset.cpp new file mode 100644 index 0000000..ce901d9 --- /dev/null +++ b/Plugins/VoxelFree/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->GetPlatformData()->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/Plugins/VoxelFree/Source/Voxel/Private/VoxelAsyncWork.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelAsyncWork.cpp new file mode 100644 index 0000000..55745c2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelInvokerComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelInvokerComponent.cpp new file mode 100644 index 0000000..1c60d58 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelNoClippingComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelNoClippingComponent.cpp new file mode 100644 index 0000000..965ccc7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelPhysicsRelevancyComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelComponents/VoxelPhysicsRelevancyComponent.cpp new file mode 100644 index 0000000..ed037b5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelData.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelData.cpp new file mode 100644 index 0000000..63189a8 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelData.cpp @@ -0,0 +1,1068 @@ +// 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(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(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataAccelerator.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataAccelerator.cpp new file mode 100644 index 0000000..97dc05c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataOctree.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataOctree.cpp new file mode 100644 index 0000000..727ca4d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataOctreeLeafUndoRedo.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelDataOctreeLeafUndoRedo.cpp new file mode 100644 index 0000000..9ce0534 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelSave.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelSave.cpp new file mode 100644 index 0000000..75d8ada --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelSaveUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelData/VoxelSaveUtilities.cpp new file mode 100644 index 0000000..bea2ffe --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelDebugManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelDebugManager.cpp new file mode 100644 index 0000000..c597b6c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelDebugUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelDebugUtilities.cpp new file mode 100644 index 0000000..941072a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelLineBatchComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDebug/VoxelLineBatchComponent.cpp new file mode 100644 index 0000000..a2edc42 --- /dev/null +++ b/Plugins/VoxelFree/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 FVector3f const PosX(1.f, 0, 0); + static FVector3f const PosY(0, 1.f, 0); + static FVector3f 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(FVector3f(Mesh.MeshVerts[VertIdx]), FVector2f::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.bSeparateTranslucency = true; + ViewRelevance.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/Plugins/VoxelFree/Source/Voxel/Private/VoxelDefaultPool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDefaultPool.cpp new file mode 100644 index 0000000..85da2cb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelDefinitions.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDefinitions.cpp new file mode 100644 index 0000000..a03f7f3 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelDefinitions.cpp @@ -0,0 +1,138 @@ +// 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 + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FVoxelRangeFailStatus& FVoxelRangeFailStatus::Get() +{ + return TThreadSingleton::Get(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelEvents/VoxelEventManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelEvents/VoxelEventManager.cpp new file mode 100644 index 0000000..8725f74 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp new file mode 100644 index 0000000..6c90f2e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGenerator.cpp @@ -0,0 +1,133 @@ +// 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 (Property->GetMetaDataMap()) + { + MetaData = *Property->GetMetaDataMap(); + } +#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 = FindFProperty (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/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorCache.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorCache.cpp new file mode 100644 index 0000000..16f69b9 --- /dev/null +++ b/Plugins/VoxelFree/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 +{ + TObjectPtr& Instance = Cache.FindOrAdd(Picker); + if (!Instance) + { + Instance = UVoxelGeneratorTools::MakeGeneratorInstance(Picker, GeneratorInit); + } + return Instance; +} + +UVoxelTransformableGeneratorInstanceWrapper* UVoxelGeneratorCache::MakeTransformableGeneratorInstance(FVoxelTransformableGeneratorPicker Picker) const +{ + TObjectPtr& Instance = TransformableCache.FindOrAdd(Picker); + if (!Instance) + { + Instance = UVoxelGeneratorTools::MakeTransformableGeneratorInstance(Picker, GeneratorInit); + } + return Instance; +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorParameters.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorParameters.cpp new file mode 100644 index 0000000..6e0c9a7 --- /dev/null +++ b/Plugins/VoxelFree/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 = CastField(&Property); + PropertyClass = ObjectProperty->PropertyClass->GetFName(); + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Object; + + auto* ObjectProperty = CastField(&Property); + PropertyClass = ObjectProperty->PropertyClass->GetFName(); + } + else if (Property.IsA()) + { + PropertyType = EVoxelGeneratorParameterPropertyType::Struct; + + auto* ObjectProperty = CastField(&Property); + PropertyClass = ObjectProperty->Struct->GetFName(); + } + else if (Property.IsA()) + { + auto* ArrayProperty = 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 = 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 = 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorPicker.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorPicker.cpp new file mode 100644 index 0000000..926fbaf --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelGenerators/VoxelGeneratorTools.cpp new file mode 100644 index 0000000..f9a579b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/SDFGen/README.md b/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/SDFGen/README.md new file mode 100644 index 0000000..ed85c98 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/VoxelLandscapeImporter.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/VoxelLandscapeImporter.cpp new file mode 100644 index 0000000..54795be --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp new file mode 100644 index 0000000..dbc642d --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelImporters/VoxelMeshImporter.cpp @@ -0,0 +1,412 @@ +// 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 "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->GetRenderData()) || !ensure(InMesh->GetRenderData()->LODResources.IsValidIndex(LODIndex))) return; + + const FStaticMeshLODResources& LODResources = InMesh->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) = FVector(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) = FVector2D(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.LockBuffer(PositionVertexBuffer.VertexBufferRHI, 0, NumBytes, EResourceLockMode::RLM_ReadOnly); + FMemory::Memcpy(Vertices.GetData(), BufferData, NumBytes); + RHICmdList.UnlockBuffer(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.LockBuffer(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.UnlockBuffer(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.LockBuffer(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.UnlockBuffer(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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Converting meshes to voxels require Voxel Plugin Pro")); + return false; +} + +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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Converting meshes to voxels require Voxel Plugin Pro")); +} + +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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Converting meshes to voxels require Voxel Plugin Pro")); +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialBuilder.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialBuilder.cpp new file mode 100644 index 0000000..20cd278 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialInterface.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialInterface.cpp new file mode 100644 index 0000000..4dd53b0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialUtilities.cpp new file mode 100644 index 0000000..0286bf7 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMaterialUtilities.cpp @@ -0,0 +1,33 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelUtilities/VoxelMaterialUtilities.h" + +#include "Engine/EngineTypes.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceDynamic.h" + +bool FVoxelUtilities::IsMaterialTessellated(UMaterialInterface* Material) +{ + return false; +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelMessages.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMessages.cpp new file mode 100644 index 0000000..8e0f3ec --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelModule.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelModule.cpp new file mode 100644 index 0000000..b0d201e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelModule.cpp @@ -0,0 +1,186 @@ +// 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); + + } + + FTSTicker::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); + } + + { + FVoxelMessages::FNotification Notification; + Notification.UniqueId = OBJECT_LINE_ID(); + Notification.Message = "Thanks for using Voxel Plugin Free!"; + Notification.Duration = 1e6f; + + { + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Docs"; + Button.Tooltip = "Open the plugin docs"; + Button.bCloseOnClick = false; + Button.OnClick = FSimpleDelegate::CreateLambda([=]() + { + OpenLink("https://wiki.voxelplugin.com"); + }); + } + + { + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Discord"; + Button.Tooltip = "Open the plugin discord, where you can interact with an awesome community!"; + Button.bCloseOnClick = false; + Button.OnClick = FSimpleDelegate::CreateLambda([=]() + { + OpenLink("https://discord.voxelplugin.com"); + }); + } + + { + auto& Button = Notification.Buttons.Emplace_GetRef(); + Button.Text = "Pro"; + Button.Tooltip = "Open the Voxel Plugin Pro marketplace page"; + Button.bCloseOnClick = false; + Button.OnClick = FSimpleDelegate::CreateLambda([=]() + { + OpenLink("https://pro.voxelplugin.com"); + }); + } + + FVoxelMessages::ShowNotification(Notification); + } + + return false; + })); +} + +void FVoxelModule::ShutdownModule() +{ + IVoxelPool::Shutdown(); +} + +IMPLEMENT_MODULE(FVoxelModule, Voxel) \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp new file mode 100644 index 0000000..8fe0bfc --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelMultiplayer/VoxelMultiplayerTcp.cpp @@ -0,0 +1,31 @@ +// 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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Multiplayer with TCP sockets is only available in Voxel Plugin Pro")); + return false; +} + +bool UVoxelMultiplayerTcpInterface::StartServer(FString& OutError, const FString& Ip, int32 Port) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Multiplayer with TCP sockets is only available in Voxel Plugin Pro")); + return false; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelObjectArchive.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelObjectArchive.cpp new file mode 100644 index 0000000..92b9dab --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelAssetActor.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelAssetActor.cpp new file mode 100644 index 0000000..c57645e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDataItemActor.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDataItemActor.cpp new file mode 100644 index 0000000..fb31f1f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.cpp new file mode 100644 index 0000000..8784fe3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp new file mode 100644 index 0000000..52a24ef --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.cpp @@ -0,0 +1,160 @@ +// 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 "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 + } + + } + + PlaceableItemManager.DrawDebug(VoxelWorld, VoxelWorld.GetLineBatchComponent()); +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItem.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItem.cpp new file mode 100644 index 0000000..1b7491c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemManager.cpp new file mode 100644 index 0000000..4b7c59b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.cpp new file mode 100644 index 0000000..09d2fe0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/IVoxelLODManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/IVoxelLODManager.cpp new file mode 100644 index 0000000..811f144 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/IVoxelRenderer.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/IVoxelRenderer.cpp new file mode 100644 index 0000000..d69cf91 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.cpp new file mode 100644 index 0000000..8c4bc98 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelDefaultLODManager.h new file mode 100644 index 0000000..46eacd4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.cpp new file mode 100644 index 0000000..81f8000 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelFixedResolutionLODManager.h new file mode 100644 index 0000000..3c90764 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.cpp new file mode 100644 index 0000000..26a2fd5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/LODManager/VoxelRenderOctree.h new file mode 100644 index 0000000..43bae68 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.cpp new file mode 100644 index 0000000..03b3997 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.cpp new file mode 100644 index 0000000..2628090 --- /dev/null +++ b/Plugins/VoxelFree/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 }; + TObjectPtr& 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.cpp new file mode 100644 index 0000000..05f5270 --- /dev/null +++ b/Plugins/VoxelFree/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.EditorOnly.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++) + { + UMaterialInstance* 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp new file mode 100644 index 0000000..8bd6ddc --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.cpp @@ -0,0 +1,333 @@ +// 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.Get(), [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 +{ + UMaterialInstanceConstant* 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; + { + TSet AddedLayers; + for (int32 Index = 0; Index < 6; Index++) + { + const auto Name = Permutation.Names[Index]; + if (Name.IsNone()) + { + continue; + } + AddedLayers.Add(Name); + + FStaticTerrainLayerWeightParameter Parameter; + Parameter.LayerName = Name; + // Pass the layer to use to the voxel expression + // Add 1 000 000 to detect voxel vs landscape indices + Parameter.WeightmapIndex = 1000000 + Index; + StaticParameters.EditorOnly.TerrainLayerWeightParameters.Add(Parameter); + } + + for (auto& Layer : Layers) + { + if (AddedLayers.Contains(Layer.Name)) + { + continue; + } + + FStaticTerrainLayerWeightParameter Parameter; + Parameter.LayerName = Layer.Name; + // 1 000 006 is used to set Default + Parameter.WeightmapIndex = 1000006; + StaticParameters.EditorOnly.TerrainLayerWeightParameters.Add(Parameter); + } + } + + Instance->UpdateStaticPermutation(StaticParameters); + Instance->PostEditChange(); + + return Instance; +} + +void UVoxelLandscapeMaterialCollection::ForeachMaterialParameterName(TFunctionRef Lambda) const +{ + if (!ensure(Material)) + { + return; + } + + UMaterial* ActualMaterial = Material->GetMaterial(); + if (!ensure(ActualMaterial)) + { + return; + } + + if (!ensure(!NeedsToBeConvertedToVoxel())) + { + return; + } + + TSet Names; + for (const TObjectPtr& Expression : ActualMaterial->GetExpressions()) + { + if (const UMaterialExpressionLandscapeLayerWeight* Weight = Cast(Expression)) + { + Names.Add(Weight->ParameterName); + } + if (const UMaterialExpressionLandscapeLayerSwitch* Switch = Cast(Expression)) + { + Names.Add(Switch->ParameterName); + } + if (const UMaterialExpressionLandscapeLayerSample* Sample = Cast(Expression)) + { + Names.Add(Sample->ParameterName); + } + if (const UMaterialExpressionLandscapeLayerBlend* Blend = Cast(Expression)) + { + for (const FLayerBlendInput& Layer : Blend->Layers) + { + Names.Add(Layer.LayerName); + } + } + + // Note: don't check landscape visibility parameter name, it just returns __LANDSCAPE_VISIBILITY__ + } + + // Make sure names are unique before iterating + for (const FName Name : Names) + { + Lambda(Name); + } +} + +bool UVoxelLandscapeMaterialCollection::NeedsToBeConvertedToVoxel() const +{ + if (!ensure(Material)) + { + return false; + } + + UMaterial* ActualMaterial = Material->GetMaterial(); + if (!ensure(ActualMaterial)) + { + return false; + } + + return FVoxelMaterialExpressionUtilities::NeedsToBeConvertedToVoxel(ActualMaterial->GetEditorOnlyData()->ExpressionCollection.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(); + ForeachMaterialParameterName([&](FName Name) + { + auto* ExistingIndex = ExistingIndices.Find(Name); + const uint8 Index = ExistingIndex ? *ExistingIndex : GetUniqueIndex(0); + + Layers.Add(FVoxelLandscapeMaterialCollectionLayer{ 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/Transvoxel.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/Transvoxel.h new file mode 100644 index 0000000..5575c8f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.cpp new file mode 100644 index 0000000..cf49f81 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelCubicMesher.h new file mode 100644 index 0000000..58b23c6 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.cpp new file mode 100644 index 0000000..b04cd9a --- /dev/null +++ b/Plugins/VoxelFree/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(double(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMarchingCubeMesher.h new file mode 100644 index 0000000..09f2f78 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.cpp new file mode 100644 index 0000000..44d50c9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesher.h new file mode 100644 index 0000000..dadd8ee --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.cpp new file mode 100644 index 0000000..11f8eb8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelMesherUtilities.h new file mode 100644 index 0000000..59bb85e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.cpp new file mode 100644 index 0000000..d6cee54 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Meshers/VoxelSurfaceNetMesher.h new file mode 100644 index 0000000..7b33752 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp new file mode 100644 index 0000000..f2b023b --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.cpp @@ -0,0 +1,110 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.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); + return new FVoxelAsyncPhysicsCooker_Chaos(Component); +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker.h new file mode 100644 index 0000000..3fb180b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp new file mode 100644 index 0000000..d48b1aa --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.cpp @@ -0,0 +1,159 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h" +#include "VoxelRender/VoxelProcMeshBuffers.h" +#include "VoxelUtilities/VoxelMathUtilities.h" + +#include "PhysicsEngine/BodySetup.h" + +#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); + } +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h new file mode 100644 index 0000000..b1a31fd --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/PhysicsCooker/VoxelAsyncPhysicsCooker_Chaos.h @@ -0,0 +1,36 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelAsyncPhysicsCooker.h" + +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; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.cpp new file mode 100644 index 0000000..96be002 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelDefaultRenderer.h new file mode 100644 index 0000000..9509a02 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelMesherAsyncWork.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelMesherAsyncWork.cpp new file mode 100644 index 0000000..8b39562 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.cpp new file mode 100644 index 0000000..318728e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererBasicMeshHandler.h new file mode 100644 index 0000000..60a95d3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.cpp new file mode 100644 index 0000000..49ef56e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererClusteredMeshHandler.h new file mode 100644 index 0000000..c1ecb13 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp new file mode 100644 index 0000000..67cbc7c --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.cpp @@ -0,0 +1,462 @@ +// 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 :( + Mesh.SetUsingAbsoluteLocation(true); + Mesh.SetWorldLocationAndRotationNoPhysics(FVector::ZeroVector, FRotator::ZeroRotator); + Mesh.SetUsingAbsoluteLocation(false); + } + + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMeshHandler.h new file mode 100644 index 0000000..0c72858 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.cpp new file mode 100644 index 0000000..fb4159c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/Renderers/VoxelRendererMixedMeshHandler.h new file mode 100644 index 0000000..d3d1a30 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp new file mode 100644 index 0000000..700682c --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelChunkMesh.cpp @@ -0,0 +1,145 @@ +// 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) +{ +} diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp new file mode 100644 index 0000000..b454614 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelMaterialExpressions.cpp @@ -0,0 +1,224 @@ +// 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().EditorOnly.TerrainLayerWeightParameters; + + TArray VoxelIndices; + int32 ParameterVoxelIndex = -1; + for (auto& Parameter : TerrainLayerWeightParameters) + { + const int32 WeightmapIndex = Parameter.WeightmapIndex - 1000000; + if (WeightmapIndex >= 0) + { + VoxelIndices.Add(WeightmapIndex); + + if (Parameter.LayerName == 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 (!ensureVoxelSlow(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 (!ensureVoxelSlow(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(); + } + + +}; + +#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->GetEditorOnlyData()->ExpressionCollection.Expressions, 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProcMeshBuffers.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProcMeshBuffers.cpp new file mode 100644 index 0000000..dd0ed7c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp new file mode 100644 index 0000000..87f9723 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshComponent.cpp @@ -0,0 +1,751 @@ +// 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; +} + +UVoxelProceduralMeshComponent::UVoxelProceduralMeshComponent() +{ + 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] = FVector(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp new file mode 100644 index 0000000..e947859 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.cpp @@ -0,0 +1,638 @@ +// 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 "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; + + FRayTracingGeometrySegment Segment; + Segment.VertexBuffer = VertexBuffers.PositionVertexBuffer.VertexBufferRHI; + Segment.NumPrimitives = Initializer.TotalPrimitiveCount; + Segment.MaxVertices = VertexBuffers.PositionVertexBuffer.GetNumVertices(); + Initializer.Segments.Add(Segment); + + 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 = false; + 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() +{ + 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()); + } + } +} + +void FVoxelProceduralMeshSceneProxy::DestroyRenderThreadResources() +{ + VOXEL_RENDER_FUNCTION_COUNTER(); + check(IsInRenderingThread()); + + for (auto& Section : Sections) + { + Section.RenderData.Reset(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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.Get() + : GEngine->LevelColorationLitMaterial.Get(); + + 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(GetScene().GetFeatureLevel()); + 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.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(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 = false; + + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h new file mode 100644 index 0000000..6c2d7a4 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelProceduralMeshSceneProxy.h @@ -0,0 +1,104 @@ +// 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; + virtual void DestroyRenderThreadResources() 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; + //~ 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRawStaticIndexBuffer.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRawStaticIndexBuffer.cpp new file mode 100644 index 0000000..863fd98 --- /dev/null +++ b/Plugins/VoxelFree/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(TEXT("INDEX"), &IndexStorage); + IndexBufferRHI = RHICreateIndexBuffer(IndexStride,SizeInBytes,BUF_Static,CreateInfo); + } +} + +void FVoxelRawStaticIndexBuffer::Serialize(FArchive& Ar, bool bNeedsCPUAccess) +{ + IndexStorage.SetAllowCPUAccess(bNeedsCPUAccess); + + if (Ar.UEVer() < 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.cpp new file mode 100644 index 0000000..b826aad --- /dev/null +++ b/Plugins/VoxelFree/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) = FVector3f(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, FVector3f(Tangent.TangentX), FVector3f(Tangent.GetY(Normal)), FVector3f(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, FVector2f(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) = FVector3f(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelRender/VoxelRenderUtilities.h new file mode 100644 index 0000000..8cb9b10 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSerializationUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSerializationUtilities.cpp new file mode 100644 index 0000000..daf9411 --- /dev/null +++ b/Plugins/VoxelFree/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/1.2.12/include/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSettings.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSettings.cpp new file mode 100644 index 0000000..0c7a324 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp new file mode 100644 index 0000000..f80a1ff --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelDistanceFieldShader.cpp @@ -0,0 +1,164 @@ +// 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); +} + +void FVoxelDistanceFieldBaseCS::SetBuffers( + FRHICommandList& RHICmdList, + const FRWBuffer& SrcBuffer, + const FRWBuffer& DstBuffer) const +{ + Src.SetBuffer(RHICmdList, RHICmdList.GetBoundComputeShader(), SrcBuffer); + Dst.SetBuffer(RHICmdList, RHICmdList.GetBoundComputeShader(), DstBuffer); +} + +void FVoxelDistanceFieldBaseCS::SetUniformBuffers(FRHICommandList& RHICmdList, const FVoxelDistanceFieldParameters& Parameters) const +{ + const FVoxelDistanceFieldParametersRef ParametersBuffer = FVoxelDistanceFieldParametersRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_MultiFrame); + SetUniformBufferParameter(RHICmdList, 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, + FVector3f* 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(TEXT("DEBUG"), sizeof(float), 3 * Num, PF_R32_FLOAT); + DstBuffer.Initialize(TEXT("DEBUG"), sizeof(float), 3 * Num, PF_R32_FLOAT); + } + + { + VOXEL_RENDER_SCOPE_COUNTER("Copy Data To Buffers"); + void* BufferData = RHICmdList.LockBuffer(SrcBuffer.Buffer, 0, SrcBuffer.NumBytes, EResourceLockMode::RLM_WriteOnly); + FMemory::Memcpy(BufferData, Data, SrcBuffer.NumBytes); + RHICmdList.UnlockBuffer(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 + RHICmdList.Transition(FRHITransitionInfo(DstBuffer.UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute)); // TODO not unknown? + + { + VOXEL_RENDER_SCOPE_COUNTER("Copy Data From Buffers"); + void* BufferData = RHICmdList.LockBuffer(SrcBuffer.Buffer, 0, SrcBuffer.NumBytes, EResourceLockMode::RLM_ReadOnly); + FMemory::Memcpy(Data, BufferData, SrcBuffer.NumBytes); + RHICmdList.UnlockBuffer(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(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); + + RHICmdList.Transition(FRHITransitionInfo(SrcBuffer.UAV, ERHIAccess::UAVCompute, ERHIAccess::UAVCompute)); + RHICmdList.Transition(FRHITransitionInfo(DstBuffer.UAV, ERHIAccess::UAVCompute, ERHIAccess::UAVCompute)); + + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp new file mode 100644 index 0000000..c83bb32 --- /dev/null +++ b/Plugins/VoxelFree/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(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(); +} + +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(); +} + +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(TEXT("Name")); + const 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp new file mode 100644 index 0000000..aa545bc --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.cpp @@ -0,0 +1,87 @@ +// 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); +} + +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, 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, RHICmdList.GetBoundComputeShader(), GetUniformBufferParameter(), ParametersBuffer); +} + +/* Unbinds buffers that will be used elsewhere */ +void FVoxelErosionCS::UnbindBuffers(FRHICommandList& RHICmdList) +{ +#define PROCESS_SURFACE(Name) RHICmdList.SetUAVParameter(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h new file mode 100644 index 0000000..7dffdc7 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosionShader.h @@ -0,0 +1,118 @@ +// 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); + + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshSettings.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelInstancedMeshSettings.cpp new file mode 100644 index 0000000..7bb014a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp new file mode 100644 index 0000000..ffce986 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelMeshSpawner.cpp @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelMeshSpawner.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 + + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp new file mode 100644 index 0000000..9f3dbf9 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawner.cpp @@ -0,0 +1,24 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawner.h" +#include "VoxelSpawners/VoxelMeshSpawner.h" +#include "VoxelSpawners/VoxelAssetSpawner.h" +#include "VoxelSpawners/VoxelSpawnerGroup.h" + + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp new file mode 100644 index 0000000..4b7483b --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerActor.cpp @@ -0,0 +1,71 @@ +// 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); + Resource->CacheUniformExpressions(false); + }); +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerConfig.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerConfig.cpp new file mode 100644 index 0000000..520d573 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp new file mode 100644 index 0000000..63cb119 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelSpawners/VoxelSpawnerGroup.cpp @@ -0,0 +1,55 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelSpawners/VoxelSpawnerGroup.h" +#include "VoxelMessages.h" + +#include "Logging/MessageLog.h" +#include "Misc/UObjectToken.h" + + +#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/Plugins/VoxelFree/Source/Voxel/Private/VoxelStaticWorld.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelStaticWorld.cpp new file mode 100644 index 0000000..8474fd8 --- /dev/null +++ b/Plugins/VoxelFree/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 (UStaticMeshComponent* Mesh : Meshes) + { + auto* StaticMesh = Mesh->GetStaticMesh().Get(); + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTexture.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTexture.cpp new file mode 100644 index 0000000..a5fdad2 --- /dev/null +++ b/Plugins/VoxelFree/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->GetPlatformData()->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->MarkAsGarbage(); +} + +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->GetPlatformData() || + InOutTexture->GetPlatformData()->Mips.Num() == 0 || + InOutTexture->GetPlatformData()->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->GetPlatformData()->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->GetPlatformData() || + InOutTexture->GetPlatformData()->Mips.Num() == 0 || + InOutTexture->GetPlatformData()->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->GetPlatformData()->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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTextureUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTextureUtilities.cpp new file mode 100644 index 0000000..6a7bc85 --- /dev/null +++ b/Plugins/VoxelFree/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->GetPlatformData()->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/Plugins/VoxelFree/Source/Voxel/Private/VoxelThreadPool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelThreadPool.cpp new file mode 100644 index 0000000..067bb43 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelThreadPool.cpp @@ -0,0 +1,452 @@ +// 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) +{ + UE::Trace::ThreadGroupBegin(TEXT("VoxelThreadPool")); + ON_SCOPE_EXIT + { + UE::Trace::ThreadGroupEnd(); + }; + + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelBoxTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelBoxTools.cpp new file mode 100644 index 0000000..8d8f25d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelGeneratedTools.h b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelGeneratedTools.h new file mode 100644 index 0000000..29f3f47 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelLevelTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelLevelTools.cpp new file mode 100644 index 0000000..ed19cb2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelSphereTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelSphereTools.cpp new file mode 100644 index 0000000..8fc9d68 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelSurfaceEditTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Gen/VoxelSurfaceEditTools.cpp new file mode 100644 index 0000000..412aed5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Impl/VoxelToolsBaseImpl.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Impl/VoxelToolsBaseImpl.cpp new file mode 100644 index 0000000..901b1ca --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelFlattenTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelFlattenTool.cpp new file mode 100644 index 0000000..a336b12 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelLevelTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelLevelTool.cpp new file mode 100644 index 0000000..2fb4a26 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp new file mode 100644 index 0000000..f9e79b8 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelMeshTool.cpp @@ -0,0 +1,114 @@ +// 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() +{ + FVoxelMessages::Info("Using the mesh tool requires the Pro version of Voxel Plugin"); + return {}; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const UVoxelMeshTool::FMeshData* UVoxelMeshTool::GetMeshData() +{ + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelRevertTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelRevertTool.cpp new file mode 100644 index 0000000..1839aff --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSmoothTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSmoothTool.cpp new file mode 100644 index 0000000..1280e3a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereTool.cpp new file mode 100644 index 0000000..5b2169b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereToolBase.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSphereToolBase.cpp new file mode 100644 index 0000000..5dd16bb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp new file mode 100644 index 0000000..39d96a9 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelSurfaceTool.cpp @@ -0,0 +1,249 @@ +// 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()) + { + } + + 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()) + { + FVoxelMessages::Info("Using masks requires the Pro version of Voxel Plugin"); + } + + 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::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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelTool.cpp new file mode 100644 index 0000000..f94ae2d --- /dev/null +++ b/Plugins/VoxelFree/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.GetActor()); + 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.Get(); + 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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp new file mode 100644 index 0000000..99a5076 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelToolBase.cpp @@ -0,0 +1,632 @@ +// 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->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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelToolLibrary.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelToolLibrary.cpp new file mode 100644 index 0000000..347edfe --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelTrimTool.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/Tools/VoxelTrimTool.cpp new file mode 100644 index 0000000..5097bab --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelAssetTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelAssetTools.cpp new file mode 100644 index 0000000..897a6ad --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp new file mode 100644 index 0000000..111c8d8 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelBlueprintLibrary.cpp @@ -0,0 +1,1205 @@ +// 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/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 false; +} + +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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); +} + +AVoxelSpawnerActor* UVoxelBlueprintLibrary::SpawnVoxelSpawnerActorByInstanceIndex( + AVoxelWorld* World, + UVoxelHierarchicalInstancedStaticMeshComponent* Component, + int32 InstanceIndex) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); + return nullptr; +} + +void UVoxelBlueprintLibrary::AddInstances( + AVoxelWorld* const World, + UStaticMesh* const Mesh, + const TArray& Transforms, + const TArray& Colors, + FVoxelInstancedMeshSettings InstanceSettings, + FVoxelSpawnerActorSettings ActorSettings, + const FVector FloatingDetectionOffset) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void UVoxelBlueprintLibrary::RegenerateSpawners(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); +} + +void UVoxelBlueprintLibrary::MarkSpawnersDirty(AVoxelWorld* World, FVoxelIntBox Bounds) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); +} + +FVoxelSpawnersSave UVoxelBlueprintLibrary::GetSpawnersSave(AVoxelWorld* World) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); + return {}; +} + +void UVoxelBlueprintLibrary::LoadFromSpawnersSave(AVoxelWorld* World, const FVoxelSpawnersSave& Save) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Spawners require Voxel Plugin Pro")); + return false; +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelDataTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelDataTools.cpp new file mode 100644 index 0000000..dc12afe --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelHardnessHandler.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelHardnessHandler.cpp new file mode 100644 index 0000000..49b5699 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelMathLibrary.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelMathLibrary.cpp new file mode 100644 index 0000000..d2b2d2b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPaintMaterial.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPaintMaterial.cpp new file mode 100644 index 0000000..f764905 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp new file mode 100644 index 0000000..28e4412 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysics.cpp @@ -0,0 +1,31 @@ +// 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" + + +void UVoxelPhysicsTools::ApplyVoxelPhysics( + UObject* WorldContextObject, + FLatentActionInfo LatentInfo, + TArray>& OutResults, + AVoxelWorld* World, + FVoxelIntBox Bounds, + TScriptInterface PartSpawner, + int32 MinParts, + bool bDebug, + bool bHideLatentWarnings) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Voxel Physics require Voxel Plugin Pro")); +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp new file mode 100644 index 0000000..95aeb2b --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelPhysicsPartSpawner.cpp @@ -0,0 +1,36 @@ +// 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" + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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'")); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelProjectionTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelProjectionTools.cpp new file mode 100644 index 0000000..191c597 --- /dev/null +++ b/Plugins/VoxelFree/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.GetActor() == 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, + TArray>(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp new file mode 100644 index 0000000..f4a7ca0 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelSurfaceTools.cpp @@ -0,0 +1,554 @@ +// 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) + FVector(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) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Masks require Voxel Plugin Pro")); + + return + { + "ApplyStrengthMask", + EVoxelSurfaceEditsStackElementFlags::None, + [=](auto& Info, auto& Voxels) {} + }; +} + +void UVoxelSurfaceTools::GetStrengthMaskScale( + float& ScaleX, + float& ScaleY, + AVoxelWorld* World, + FVoxelFloatTexture Mask, + float SizeX, + float SizeY, + bool bConvertToVoxelSpace) +{ + FVoxelMessages::Info(FUNCTION_ERROR("Masks require Voxel Plugin Pro")); +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelTestLibrary.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelTestLibrary.cpp new file mode 100644 index 0000000..ff018e7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelTextureTools.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelTextureTools.cpp new file mode 100644 index 0000000..48075f8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolHelpers.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolHelpers.cpp new file mode 100644 index 0000000..f2174a2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolManager.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolManager.cpp new file mode 100644 index 0000000..3560737 --- /dev/null +++ b/Plugins/VoxelFree/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.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); + + 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 (UVoxelTool* Tool : Tools) + { + if (Tool->GetClass() == NewActiveTool) + { + SetActiveTool(Tool); + return; + } + } +} + +void UVoxelToolManager::SetActiveToolByName(FName NewActiveTool) +{ + for (UVoxelTool* Tool : Tools) + { + if (Tool->ToolName == NewActiveTool) + { + SetActiveTool(Tool); + return; + } + } +} diff --git a/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolsBase.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelTools/VoxelToolsBase.cpp new file mode 100644 index 0000000..ebc2d2e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelUtilities/VoxelConfigUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelUtilities/VoxelConfigUtilities.cpp new file mode 100644 index 0000000..5c17c31 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Private/VoxelUtilities/VoxelDistanceFieldUtilities.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelUtilities/VoxelDistanceFieldUtilities.cpp new file mode 100644 index 0000000..6e6d06e --- /dev/null +++ b/Plugins/VoxelFree/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 FVector3f SurfacePosition = FVoxelUtilities::Get3D(SurfacePositions, Size, X, Y, Z); + ensureVoxelSlow(IsSurfacePositionValid(SurfacePosition)); + + // Keep sign + Distance = FVector3f::Distance(FVector3f(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; + FVector3f 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++) + { + FVector3f NeighborSurfacePosition = FVoxelUtilities::Get3D(InSurfacePositions, Size, HighX, HighY, HighZ); + + if (IsSurfacePositionValid(NeighborSurfacePosition)) + { + // Make sure to / Divisor AFTER IsSurfacePositionValid + NeighborSurfacePosition /= Divisor; + const float Distance = (NeighborSurfacePosition - FVector3f(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; + FVector3f 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 FVector3f NeighborSurfacePosition = FVoxelUtilities::Get3D(InData, Size, NeighborPosition); + + if (IsSurfacePositionValid(NeighborSurfacePosition)) + { + const float Distance = (NeighborSurfacePosition - FVector3f(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/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorld.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorld.cpp new file mode 100644 index 0000000..c26e7e2 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorld.cpp @@ -0,0 +1,1639 @@ +// 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/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 "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) + { + }; + + 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(); +} + +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() +{ + FVoxelMessages::Info(FUNCTION_ERROR("Multiplayer with TCP require Voxel Plugin Pro")); + return nullptr; +} + +UVoxelMultiplayerInterface* AVoxelWorld::GetMultiplayerInterfaceInstance() const +{ + FVoxelMessages::Info(FUNCTION_ERROR("Multiplayer with TCP require Voxel Plugin Pro")); + return nullptr; +} + +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(); + } +} + +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(float(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(); +} + + +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(); + + if (SpawnerConfig) + { + FVoxelMessages::Info("Spawners are only available in Voxel Plugin Pro", this); + } + + if (bEnableMultiplayer) + { + FVoxelMessages::Info("TCP Multiplayer is only available in Voxel Plugin Pro", this); + } + + 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); + + + WorldOffset = MakeVoxelShared(FIntVector::ZeroValue); + + GameThreadTasks->Flush(); + GameThreadTasks.Reset(); + + // Clear generator cache to avoid keeping instances alive + if (ensure(GeneratorCache)) + { + GeneratorCache->ClearCache(); + GeneratorCache->MarkAsGarbage(); + GeneratorCache = nullptr; + } + + if (PlaceableItemActorHelper) + { + PlaceableItemActorHelper->MarkAsGarbage(); + 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(); + + + LODManager->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), LODManager); + + Renderer->Destroy(); + FVoxelUtilities::DeleteTickable(GetWorld(), Renderer); + + DestroyVoxelComponents(); + + Renderer = CreateRenderer(); + LODManager = CreateLODManager(); + +} + +void AVoxelWorld::RecreateSpawners() +{ + if (SpawnerConfig) + { + FVoxelMessages::Info("Spawners are only available in Voxel Plugin Pro", this); + } +} + +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/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorldRootComponent.cpp b/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorldRootComponent.cpp new file mode 100644 index 0000000..66586e7 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Private/VoxelWorldRootComponent.cpp @@ -0,0 +1,376 @@ +// 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); + }); + } +} + +class UMRMeshComponent +{ +public: + static void FinishCreatingPhysicsMeshes(UBodySetup* Body, const TArray& ConvexMeshes, const TArray& ConvexMeshesNegX, const TArray& TriMeshes) + { + Body->FinishCreatingPhysicsMeshes_PhysX(ConvexMeshes, ConvexMeshesNegX, TriMeshes); + } +}; + +void UVoxelWorldRootComponent::SetCookedTriMeshes(const TArray& TriMeshes) +{ + VOXEL_FUNCTION_COUNTER(); + + // Create body setup + GetBodySetup(); + + UMRMeshComponent::FinishCreatingPhysicsMeshes(BodySetup, {}, {}, TriMeshes); +} +#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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise.h new file mode 100644 index 0000000..8a6ef67 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/CrossPlatformSTD.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/CrossPlatformSTD.h new file mode 100644 index 0000000..103733b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise.h new file mode 100644 index 0000000..d6b981a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise.inl new file mode 100644 index 0000000..dbb41a0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.h new file mode 100644 index 0000000..b0d6ced --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseBase.inl new file mode 100644 index 0000000..15af81f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.h new file mode 100644 index 0000000..9fce6f3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseLUT.inl new file mode 100644 index 0000000..679b978 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.h new file mode 100644 index 0000000..566e18e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseMath.inl new file mode 100644 index 0000000..be7bd24 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseTest.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoiseTest.h new file mode 100644 index 0000000..603bc69 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.h new file mode 100644 index 0000000..3b5d91a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CellularNoise.inl new file mode 100644 index 0000000..c48b3da --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.h new file mode 100644 index 0000000..fdcb3d1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_CubicNoise.inl new file mode 100644 index 0000000..dae1614 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.h new file mode 100644 index 0000000..bf67f92 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_GradientPerturb.inl new file mode 100644 index 0000000..10bcf96 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.h new file mode 100644 index 0000000..d2d64e6 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_PerlinNoise.inl new file mode 100644 index 0000000..c0d88d1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.h new file mode 100644 index 0000000..cccf770 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_SimplexNoise.inl new file mode 100644 index 0000000..0a34ef2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.h new file mode 100644 index 0000000..0477401 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_ValueNoise.inl new file mode 100644 index 0000000..2531a26 --- /dev/null +++ b/Plugins/VoxelFree/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(FVector2D{ double(x), double(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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.h b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.h new file mode 100644 index 0000000..2929ef1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.inl b/Plugins/VoxelFree/Source/Voxel/Public/FastNoise/VoxelFastNoise_WhiteNoise.inl new file mode 100644 index 0000000..4c081c9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/IVoxelPool.h b/Plugins/VoxelFree/Source/Voxel/Public/IVoxelPool.h new file mode 100644 index 0000000..d10a000 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAsset.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAsset.h new file mode 100644 index 0000000..8ac22b6 --- /dev/null +++ b/Plugins/VoxelFree/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) + TObjectPtr 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() + TObjectPtr VoxelWorldTemplate; +#endif +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.h new file mode 100644 index 0000000..c34fd96 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetData.inl new file mode 100644 index 0000000..ac12cb4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetInstance.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelDataAssetInstance.h new file mode 100644 index 0000000..4a1123d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAsset.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAsset.h new file mode 100644 index 0000000..f12dfb9 --- /dev/null +++ b/Plugins/VoxelFree/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) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.h new file mode 100644 index 0000000..44d45f7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetData.inl new file mode 100644 index 0000000..34367ee --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h new file mode 100644 index 0000000..8beaaf8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h new file mode 100644 index 0000000..1695d10 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelAsyncWork.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelAsyncWork.h new file mode 100644 index 0000000..22d27b9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelBoolVector.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelBoolVector.h new file mode 100644 index 0000000..5c2459e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelCancelCounter.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelCancelCounter.h new file mode 100644 index 0000000..4dc4554 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelCharacter.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelCharacter.h new file mode 100644 index 0000000..f2e3cb0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelInvokerComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelInvokerComponent.h new file mode 100644 index 0000000..01401a0 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelNoClippingComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelNoClippingComponent.h new file mode 100644 index 0000000..863dc74 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelPhysicsRelevancyComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelComponents/VoxelPhysicsRelevancyComponent.h new file mode 100644 index 0000000..a48c63d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/NoGrowArray.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/NoGrowArray.h new file mode 100644 index 0000000..d1ae27a --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/NoGrowArray.h @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +template +using TNoGrowArray = TArray; + +template +using TNoGrowArray64 = TArray; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelArray3.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelArray3.h new file mode 100644 index 0000000..62d9d50 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelArrayView.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelArrayView.h new file mode 100644 index 0000000..c73a657 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelSparseArray.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelSparseArray.h new file mode 100644 index 0000000..71e74c9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelStaticArray.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelContainers/VoxelStaticArray.h new file mode 100644 index 0000000..c83525e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/IVoxelData.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/IVoxelData.h new file mode 100644 index 0000000..a03fb80 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelData.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelData.h new file mode 100644 index 0000000..2927123 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelData.h @@ -0,0 +1,398 @@ +// 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: + /** + * 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelData.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelData.inl new file mode 100644 index 0000000..f323328 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.h new file mode 100644 index 0000000..6ea98f4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataAccelerator.inl new file mode 100644 index 0000000..5b17205 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataImpl.h new file mode 100644 index 0000000..b3c3aca --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataImpl.inl new file mode 100644 index 0000000..e557ff3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataIncludes.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataIncludes.h new file mode 100644 index 0000000..4ec25ec --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataLock.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataLock.h new file mode 100644 index 0000000..392c2ae --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctree.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctree.h new file mode 100644 index 0000000..8ff5330 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafData.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafData.h new file mode 100644 index 0000000..5ac8e7f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafMultiplayer.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafMultiplayer.h new file mode 100644 index 0000000..39e9c60 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafUndoRedo.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataOctreeLeafUndoRedo.h new file mode 100644 index 0000000..b32a294 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataUtilities.h new file mode 100644 index 0000000..d850106 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataUtilities.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelDataUtilities.inl new file mode 100644 index 0000000..df054ca --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelSave.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelSave.h new file mode 100644 index 0000000..45358f5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelSaveUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelData/VoxelSaveUtilities.h new file mode 100644 index 0000000..9051d7e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug.h new file mode 100644 index 0000000..d6da3b4 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug.h @@ -0,0 +1,26 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelEngineVersionHelpers.h" + +struct VOXEL_API FVoxelDebug +{ + template + using TDelegate = TMulticastDelegate)>; + + 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelDebugManager.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelDebugManager.h new file mode 100644 index 0000000..4f01e33 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelDebugUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelDebugUtilities.h new file mode 100644 index 0000000..191e7f5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelLineBatchComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDebug/VoxelLineBatchComponent.h new file mode 100644 index 0000000..6052e4f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDefaultPool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDefaultPool.h new file mode 100644 index 0000000..7d73354 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDefinitions.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDefinitions.h new file mode 100644 index 0000000..c0e407d --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDefinitions.h @@ -0,0 +1,131 @@ +// 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 1 +#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 + +// 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDelegateHelpers.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDelegateHelpers.h new file mode 100644 index 0000000..1b7b66e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDelegateHelpers.h @@ -0,0 +1,252 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSharedPtr.h" +#include "VoxelEngineVersionHelpers.h" + +/** + * Delegate impl for weak lambda on shared pointers + */ + +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; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +template +struct TDelegateFromLambda : TDelegateFromLambda +{ +}; + +template +struct TDelegateFromLambda : TDelegateFromLambda +{ + +}; + +template +struct TDelegateFromLambda +{ + using Type = TDelegate; + + 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDiff.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDiff.h new file mode 100644 index 0000000..19c13a0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelDirection.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelDirection.h new file mode 100644 index 0000000..e1d6761 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelEditorDelegates.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEditorDelegates.h new file mode 100644 index 0000000..928ac01 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelEditorDelegatesInterface.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEditorDelegatesInterface.h new file mode 100644 index 0000000..932d8e0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelEngineVersionHelpers.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEngineVersionHelpers.h new file mode 100644 index 0000000..018bb56 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEngineVersionHelpers.h @@ -0,0 +1,6 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Launch/Resources/Version.h" \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelEnums.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEnums.h new file mode 100644 index 0000000..9de5310 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelEvents/VoxelEventManager.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelEvents/VoxelEventManager.h new file mode 100644 index 0000000..e0ddc65 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelFeedbackContext.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelFeedbackContext.h new file mode 100644 index 0000000..7870461 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelEmptyGenerator.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelEmptyGenerator.h new file mode 100644 index 0000000..f0a0adc --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelFlatGenerator.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelFlatGenerator.h new file mode 100644 index 0000000..e77a7c3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGenerator.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGenerator.h new file mode 100644 index 0000000..83d3534 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorCache.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorCache.h new file mode 100644 index 0000000..85fdc42 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorHelpers.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorHelpers.h new file mode 100644 index 0000000..728ec98 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInit.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInit.h new file mode 100644 index 0000000..8eb1bb5 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr MaterialCollection = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.h new file mode 100644 index 0000000..fbd8bf3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstance.inl new file mode 100644 index 0000000..927c78a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstanceWrapper.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorInstanceWrapper.h new file mode 100644 index 0000000..1f35a0b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorParameters.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorParameters.h new file mode 100644 index 0000000..2f9c2fc --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorPicker.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorPicker.h new file mode 100644 index 0000000..122edc2 --- /dev/null +++ b/Plugins/VoxelFree/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; + TObjectPtr Object = nullptr; + TMap Parameters; +#if WITH_EDITORONLY_DATA + TObjectPtr 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")) + TObjectPtr Object = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TMap Parameters; + +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient) + TObjectPtr 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")) + TObjectPtr Object = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TMap Parameters; + +#if WITH_EDITORONLY_DATA + UPROPERTY(Transient) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelGeneratorTools.h new file mode 100644 index 0000000..f2d175a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelTransformableGeneratorHelper.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelGenerators/VoxelTransformableGeneratorHelper.h new file mode 100644 index 0000000..df6f797 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelImporters/VoxelLandscapeImporter.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelImporters/VoxelLandscapeImporter.h new file mode 100644 index 0000000..7151e88 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelImporters/VoxelMeshImporter.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelImporters/VoxelMeshImporter.h new file mode 100644 index 0000000..357bbff --- /dev/null +++ b/Plugins/VoxelFree/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) + TObjectPtr ColorsRenderTarget = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + TObjectPtr UVsRenderTarget = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + TObjectPtr LastRenderedColorsMaterial = nullptr; + + UPROPERTY(Category = "Cache", BlueprintReadOnly, VisibleAnywhere, Transient) + TObjectPtr 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")) + TObjectPtr 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)) + TObjectPtr 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") + TObjectPtr 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() + TObjectPtr MeshComponent; + + UPROPERTY(Transient) + TObjectPtr MaterialInstance; + + UPROPERTY(Transient) + FBox CachedBox; + + UPROPERTY(Transient) + TObjectPtr CachedStaticMesh; + + UPROPERTY(Transient) + TArray CachedVertices; + + UPROPERTY(Transient) + FTransform CachedTransform; + + void InitMaterialInstance(); + void UpdateSizes(); +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelIntBox.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelIntBox.h new file mode 100644 index 0000000..b8c786a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelIntBoxLibrary.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelIntBoxLibrary.h new file mode 100644 index 0000000..4128915 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelInterpolator.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelInterpolator.h new file mode 100644 index 0000000..4200309 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelInvokerSettings.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelInvokerSettings.h new file mode 100644 index 0000000..3238eb7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelItemStack.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelItemStack.h new file mode 100644 index 0000000..f36aabc --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelLog.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelLog.h new file mode 100644 index 0000000..dc0c9fd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMacros.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMacros.h new file mode 100644 index 0000000..02d8fd2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMaterial.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMaterial.h new file mode 100644 index 0000000..99ca260 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMaterialBuilder.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMaterialBuilder.h new file mode 100644 index 0000000..ee7a1cb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMessages.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMessages.h new file mode 100644 index 0000000..ac96614 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMinimal.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMinimal.h new file mode 100644 index 0000000..49e8925 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelModule.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelModule.h new file mode 100644 index 0000000..f6af744 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h new file mode 100644 index 0000000..2f54033 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterface.h @@ -0,0 +1,24 @@ +// 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: +}; + diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h new file mode 100644 index 0000000..5bcb2e0 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerInterfaceWithSocket.h @@ -0,0 +1,7 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMultiplayer/VoxelMultiplayerInterface.h" + diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h new file mode 100644 index 0000000..482df9e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelMultiplayer/VoxelMultiplayerTcp.h @@ -0,0 +1,39 @@ +// 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); + +}; + diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelObjectArchive.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelObjectArchive.h new file mode 100644 index 0000000..79a55f3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelOctree.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelOctree.h new file mode 100644 index 0000000..5ad43d7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelOctreeId.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelOctreeId.h new file mode 100644 index 0000000..170b1cf --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelAssetActor.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelAssetActor.h new file mode 100644 index 0000000..83fa5d1 --- /dev/null +++ b/Plugins/VoxelFree/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() + TObjectPtr Root; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + TObjectPtr PrimitiveComponent; + + UPROPERTY() + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDataItemActor.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDataItemActor.h new file mode 100644 index 0000000..4db269b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelDisableEditsBox.h new file mode 100644 index 0000000..a58b536 --- /dev/null +++ b/Plugins/VoxelFree/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() + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActor.h new file mode 100644 index 0000000..e0ac692 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/Actors/VoxelPlaceableItemActorHelper.h new file mode 100644 index 0000000..2bacb2b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItem.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItem.h new file mode 100644 index 0000000..faafbe0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemManager.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemManager.h new file mode 100644 index 0000000..41b92e1 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPlaceableItems/VoxelPlaceableItemsUtilities.h new file mode 100644 index 0000000..5e7cce6 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelPriorityHandler.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelPriorityHandler.h new file mode 100644 index 0000000..cce6057 --- /dev/null +++ b/Plugins/VoxelFree/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(float(Distance))); + } +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueryZone.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueryZone.h new file mode 100644 index 0000000..cf78018 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueueWithNum.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueueWithNum.h new file mode 100644 index 0000000..d3d20cd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueuedWork.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelQueuedWork.h new file mode 100644 index 0000000..0dd755d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRange.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRange.h new file mode 100644 index 0000000..62ca38b --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRange.h @@ -0,0 +1,704 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include + +#include "CoreMinimal.h" +#include "VoxelValue.h" +#include "VoxelMinimal.h" +#include "VoxelUtilities/VoxelBaseUtilities.h" +#include "VoxelRange.generated.h" + +class VOXEL_API FVoxelRangeFailStatus : private TThreadSingleton +{ +public: + static FVoxelRangeFailStatus& Get(); + + 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; + + 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); + } + T GetSign_NotZero() const + { + ensureVoxelSlow(IsSingleSign()); + ensureVoxelSlow(Min != 0 && Max != 0); + ensureVoxelSlow((Min < 0) == (Max < 0)); + return Min < 0 ? -1 : 1; + } + 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()) + { + ensureVoxelSlow(Other.IsSingleSign()); // Does not contain 0 + ensureVoxelSlow(Other.GetSign() != 0); // Else wouldn't be infinity, and does not contain 0 + const auto Inf = ExtendToInfinity(); + return TVoxelRange::FromList(Inf.Min / Other.GetSign_NotZero(), Inf.Max / Other.GetSign_NotZero()); + } + return TVoxelRange::FromList(Min / Other.Min, Min / Other.Max, Max / Other.Min, Max / Other.Max); + } + else + { + if (Other.IsSingleSign()) + { + ensureVoxelSlow(Other.GetSign() != 0); // Else would be a single value + const auto Inf = ExtendToInfinity(); + return TVoxelRange::FromList(Inf.Min / Other.GetSign_NotZero(), Inf.Max / Other.GetSign_NotZero()); + } + 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelLODManager.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelLODManager.h new file mode 100644 index 0000000..e262b17 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelProceduralMeshComponent_PhysicsCallbackHandler.h new file mode 100644 index 0000000..d362cdc --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h new file mode 100644 index 0000000..e233b3a --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/IVoxelRenderer.h @@ -0,0 +1,188 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "BodySetupEnums.h" +#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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelBasicMaterialCollection.h new file mode 100644 index 0000000..cf6409f --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelCachedMaterialCollection.h new file mode 100644 index 0000000..8c73d2f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelInstancedMaterialCollection.h new file mode 100644 index 0000000..80901d9 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr Template = nullptr; + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr Template1x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr Template2x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr Template3x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr Template4x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr Template5x = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Generated Templates") + TObjectPtr 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") + TObjectPtr 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") + TObjectPtr 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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelLandscapeMaterialCollection.h new file mode 100644 index 0000000..7c54fba --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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::Clamp(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 ForeachMaterialParameterName(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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/MaterialCollections/VoxelMaterialCollectionBase.h new file mode 100644 index 0000000..50ee6c1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkMaterials.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkMaterials.h new file mode 100644 index 0000000..0474b67 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkMesh.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkMesh.h new file mode 100644 index 0000000..12bde3b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkToUpdate.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelChunkToUpdate.h new file mode 100644 index 0000000..aeeeb52 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelLODMaterials.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelLODMaterials.h new file mode 100644 index 0000000..0e8b6f4 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr Material = nullptr; +}; + +USTRUCT(BlueprintType) +struct FVoxelLODMaterialCollections : public FVoxelLODMaterialsBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TObjectPtr MaterialCollection = nullptr; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialExpressions.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialExpressions.h new file mode 100644 index 0000000..2b7ffd8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialIndices.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialIndices.h new file mode 100644 index 0000000..e33b7b2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h new file mode 100644 index 0000000..22a0ec0 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMaterialInterface.h @@ -0,0 +1,110 @@ +// 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; + virtual FString GetReferencerName() const override { return "FVoxelMaterialInterfaceManager"; } + //~ 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMeshConfig.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMeshConfig.h new file mode 100644 index 0000000..7565f8d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMesherAsyncWork.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelMesherAsyncWork.h new file mode 100644 index 0000000..142c80a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshBuffers.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshBuffers.h new file mode 100644 index 0000000..795aeed --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshSectionSettings.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshSectionSettings.h new file mode 100644 index 0000000..d4d551d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshTangent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProcMeshTangent.h new file mode 100644 index 0000000..e44f160 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h new file mode 100644 index 0000000..3ee8901 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelProceduralMeshComponent.h @@ -0,0 +1,198 @@ +// 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 : public UPrimitiveComponent +{ + 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) + TObjectPtr BodySetup; + UPROPERTY(Transient) + TObjectPtr 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; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelRawStaticIndexBuffer.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelRawStaticIndexBuffer.h new file mode 100644 index 0000000..347056d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelToolRendering.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelRender/VoxelToolRendering.h new file mode 100644 index 0000000..824682b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSaveStruct.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSaveStruct.h new file mode 100644 index 0000000..6575946 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSettings.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSettings.h new file mode 100644 index 0000000..4455923 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h new file mode 100644 index 0000000..fdd874e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelShaders/VoxelDistanceFieldShader.h @@ -0,0 +1,103 @@ +// 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); + + 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, + FVector3f* 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelShaders/VoxelErosion.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelShaders/VoxelErosion.h new file mode 100644 index 0000000..5a43ad9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSharedMutex.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSharedMutex.h new file mode 100644 index 0000000..a35650e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSharedPtr.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSharedPtr.h new file mode 100644 index 0000000..5a37837 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSimpleOctree.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSimpleOctree.h new file mode 100644 index 0000000..163cbf0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h new file mode 100644 index 0000000..795bd2d --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelAssetSpawner.h @@ -0,0 +1,55 @@ +// 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; + + +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 +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h new file mode 100644 index 0000000..9dc5090 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelBasicSpawner.h @@ -0,0 +1,125 @@ +// 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; +}; + diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h new file mode 100644 index 0000000..d0e0775 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelHierarchicalInstancedStaticMeshComponent.h @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" +#include "VoxelEnums.h" +#include "VoxelIntBox.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() + TObjectPtr Voxel_DebugMaterial; + +public: + UVoxelHierarchicalInstancedStaticMeshComponent(const FObjectInitializer& ObjectInitializer); + ~UVoxelHierarchicalInstancedStaticMeshComponent(); + +}; + +inline UVoxelHierarchicalInstancedStaticMeshComponent::UVoxelHierarchicalInstancedStaticMeshComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} +inline UVoxelHierarchicalInstancedStaticMeshComponent::~UVoxelHierarchicalInstancedStaticMeshComponent() +{ +} diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshSettings.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelInstancedMeshSettings.h new file mode 100644 index 0000000..7e58362 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h new file mode 100644 index 0000000..a058409 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelMeshSpawner.h @@ -0,0 +1,110 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawners/VoxelBasicSpawner.h" +#include "VoxelSpawners/VoxelInstancedMeshSettings.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; + + +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") + TObjectPtr 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 +}; + +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 +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h new file mode 100644 index 0000000..c58de51 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawner.h @@ -0,0 +1,101 @@ +// 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; +}; + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 + +}; + +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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerActor.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerActor.h new file mode 100644 index 0000000..5472f71 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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") + TObjectPtr PhysicsRelevancyComponent; + + AVoxelMeshWithPhysicsRelevancySpawnerActor(); +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerConfig.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerConfig.h new file mode 100644 index 0000000..fd5f557 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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() + TObjectPtr Spawner = nullptr; + + UPROPERTY() + FVoxelSpawnerDensity Density; + + UPROPERTY() + FVoxelSpawnerOutputName DensityGraphOutputName_DEPRECATED; + + UPROPERTY() + FVoxelSpawnerConfigElementAdvanced_Height Advanced; +}; + +USTRUCT() +struct FVoxelSpawnerConfigElement_Ray +{ + GENERATED_BODY() + + UPROPERTY() + TObjectPtr 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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h new file mode 100644 index 0000000..860c1f6 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerGroup.h @@ -0,0 +1,44 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelSpawner.h" +#include "VoxelSpawnerGroup.generated.h" + +class UVoxelSpawnerGroup; + + +USTRUCT() +struct FVoxelSpawnerGroupChild +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = "Config") + TObjectPtr 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; + + +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/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerOutputsConfig.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelSpawners/VoxelSpawnerOutputsConfig.h new file mode 100644 index 0000000..adbb31c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelStaticWorld.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelStaticWorld.h new file mode 100644 index 0000000..0c95ada --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelStats.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelStats.h new file mode 100644 index 0000000..73e39d5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTexture.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTexture.h new file mode 100644 index 0000000..a94f7fd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelThreadPool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelThreadPool.h new file mode 100644 index 0000000..1968dc8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTickable.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTickable.h new file mode 100644 index 0000000..5882a79 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelBoxTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelBoxTools.h new file mode 100644 index 0000000..0f1f0b8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelLevelTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelLevelTools.h new file mode 100644 index 0000000..8ffebcf --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelSphereTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelSphereTools.h new file mode 100644 index 0000000..74c2f3b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelSurfaceEditTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelSurfaceEditTools.h new file mode 100644 index 0000000..671f541 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelToolsBase.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Gen/VoxelToolsBase.h new file mode 100644 index 0000000..1a4ad3d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.h new file mode 100644 index 0000000..7717e26 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelBoxToolsImpl.inl new file mode 100644 index 0000000..e3cc49d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.h new file mode 100644 index 0000000..729ad88 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelLevelToolsImpl.inl new file mode 100644 index 0000000..176c1d5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.h new file mode 100644 index 0000000..9af4cfa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSphereToolsImpl.inl new file mode 100644 index 0000000..b4d174b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.h new file mode 100644 index 0000000..90dfebd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelSurfaceEditToolsImpl.inl new file mode 100644 index 0000000..ffa4c92 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.h new file mode 100644 index 0000000..d8b57c7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Impl/VoxelToolsBaseImpl.inl new file mode 100644 index 0000000..1b1a2bf --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelFlattenTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelFlattenTool.h new file mode 100644 index 0000000..3e83db5 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelLevelTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelLevelTool.h new file mode 100644 index 0000000..13e8d8b --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr ToolMaterial = nullptr; + + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelMeshTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelMeshTool.h new file mode 100644 index 0000000..3310d1e --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr ToolMaterial = nullptr; + +public: + UPROPERTY(Category = "Tool Settings", EditAnywhere, BlueprintReadWrite) + TObjectPtr 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")) + TObjectPtr 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")) + TObjectPtr 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")) + TObjectPtr UVsRenderTarget = nullptr; + + // For debug + UPROPERTY(Category = "Paint Settings", AdvancedDisplay, VisibleAnywhere, BlueprintReadOnly, Transient, meta = (ShowForMaterialConfigs = "RGB, SingleIndex")) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelRevertTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelRevertTool.h new file mode 100644 index 0000000..842f678 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSmoothTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSmoothTool.h new file mode 100644 index 0000000..e23ad10 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereTool.h new file mode 100644 index 0000000..c7eddb3 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereToolBase.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSphereToolBase.h new file mode 100644 index 0000000..9b2efe7 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr ToolMaterial = nullptr; + + UPROPERTY(Category = "Tool Preview Settings", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h new file mode 100644 index 0000000..3674b5d --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelSurfaceTool.h @@ -0,0 +1,167 @@ +// 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) + TObjectPtr 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) + TObjectPtr 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)) + TObjectPtr 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) + TObjectPtr MaskGeneratorCache_RenderTexture = nullptr; + + bool ShouldUseMask() const; + + void GetStrengths(float& OutSignedSculptStrength, float& OutSignedPaintStrength) const; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelTool.h new file mode 100644 index 0000000..9ce2c4b --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr PlaneMesh = nullptr; + + UPROPERTY(Category = "Shared Config", EditAnywhere, BlueprintReadWrite, meta = (HideInPanel)) + TObjectPtr 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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolBase.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolBase.h new file mode 100644 index 0000000..e63f349 --- /dev/null +++ b/Plugins/VoxelFree/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) + TObjectPtr OverlayMaterial = nullptr; + + UPROPERTY(Category = "Materials", EditAnywhere, BlueprintReadWrite) + TObjectPtr 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) + TObjectPtr 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) + TObjectPtr ToolOverlayMaterialInstance = nullptr; + UPROPERTY(Transient) + TObjectPtr ToolMeshMaterialInstance = nullptr; + UPROPERTY(Transient) + TObjectPtr PlaneMeshMaterialInstance = nullptr; + + void ClearVoxelWorld(); + void ApplyPendingFrameBounds(); +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolLibary.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolLibary.h new file mode 100644 index 0000000..ced0ecf --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolWithAlignment.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelToolWithAlignment.h new file mode 100644 index 0000000..a6778a6 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelTrimTool.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/Tools/VoxelTrimTool.h new file mode 100644 index 0000000..90972ca --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelAssetTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelAssetTools.h new file mode 100644 index 0000000..781cbe7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl new file mode 100644 index 0000000..f90467a --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelAssetTools.inl @@ -0,0 +1,7 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "VoxelAssetTools.h" +#include "VoxelTools/VoxelToolHelpers.h" + diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h new file mode 100644 index 0000000..d6ec7da --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelBlueprintLibrary.h @@ -0,0 +1,805 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/Texture2D.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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelDataTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelDataTools.h new file mode 100644 index 0000000..d560da7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelDataTools.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelDataTools.inl new file mode 100644 index 0000000..fc3eb3f --- /dev/null +++ b/Plugins/VoxelFree/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 FVector3f SurfacePosition = FVoxelUtilities::Get3D(SurfacePositions, Size, X, Y, Z, Bounds.Min); + const auto Result = FindClosestNonEmptyVoxelImpl(Data, FVoxelVector(Bounds.Min) + FVector(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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelHardnessHandler.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelHardnessHandler.h new file mode 100644 index 0000000..f06d262 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelMathLibrary.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelMathLibrary.h new file mode 100644 index 0000000..a128ba9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPaintMaterial.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPaintMaterial.h new file mode 100644 index 0000000..8953ad4 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysics.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysics.h new file mode 100644 index 0000000..5d7f02d --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysics.h @@ -0,0 +1,45 @@ +// 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; + + +UCLASS() +class VOXEL_API UVoxelPhysicsTools : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + +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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h new file mode 100644 index 0000000..4273217 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawner.h @@ -0,0 +1,106 @@ +// 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") + TObjectPtr 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; + +}; + +/////////////////////////////////////////////////////////////////////////////// + +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") + TObjectPtr Material; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + TObjectPtr CubeMesh; + + // Spawn probability for each cube. Use this to reduce the amount of cubes spawned. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel") + float SpawnProbability = 1.f; + +}; + +/////////////////////////////////////////////////////////////////////////////// + +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() + +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h new file mode 100644 index 0000000..a859867 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelPhysicsPartSpawnerInterface.h @@ -0,0 +1,56 @@ +// 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: +}; diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelProjectionTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelProjectionTools.h new file mode 100644 index 0000000..5109192 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceEdits.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceEdits.h new file mode 100644 index 0000000..1b734ae --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceTools.h new file mode 100644 index 0000000..ac4aa78 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h new file mode 100644 index 0000000..bd29d67 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h @@ -0,0 +1,138 @@ +// 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 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelTestLibrary.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelTestLibrary.h new file mode 100644 index 0000000..bbdace4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelTextureTools.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelTextureTools.h new file mode 100644 index 0000000..7955381 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelToolHelpers.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelToolHelpers.h new file mode 100644 index 0000000..86fe2fd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelToolManager.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelToolManager.h new file mode 100644 index 0000000..0ab9725 --- /dev/null +++ b/Plugins/VoxelFree/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() + TObjectPtr 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) + TObjectPtr ActiveTool = nullptr; + + UPROPERTY(Transient) + TArray> Tools; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelUniqueError.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUniqueError.h new file mode 100644 index 0000000..51aba49 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUserDefinitions.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUserDefinitions.h new file mode 100644 index 0000000..06e7110 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelBaseUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelBaseUtilities.h new file mode 100644 index 0000000..08b26f3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelConfigUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelConfigUtilities.h new file mode 100644 index 0000000..b5a3fff --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDataItemUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDataItemUtilities.h new file mode 100644 index 0000000..9877e32 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.h new file mode 100644 index 0000000..11395b9 --- /dev/null +++ b/Plugins/VoxelFree/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 FVector3f& P) + { + return P.X < 1e9; + } + FORCEINLINE static FVector3f MakeInvalidSurfacePosition() + { + return FVector3f(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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelDistanceFieldUtilities.inl new file mode 100644 index 0000000..91071f9 --- /dev/null +++ b/Plugins/VoxelFree/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(FVector3f(Position), FVector3f(MaxNeighborPosition), Alpha); + } + }; + + if (Value <= 0) + { + Lambda(FVoxelUtilities::FTrueType()); + } + else + { + Lambda(FVoxelUtilities::FFalseType()); + } + } + } + } +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelExampleUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelExampleUtilities.h new file mode 100644 index 0000000..46634d1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelGeneratorUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelGeneratorUtilities.h new file mode 100644 index 0000000..c9765c2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelIntVectorUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelIntVectorUtilities.h new file mode 100644 index 0000000..a0e3873 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelLambdaUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelLambdaUtilities.h new file mode 100644 index 0000000..2c10991 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMaterialUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMaterialUtilities.h new file mode 100644 index 0000000..ca0db1a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMathUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMathUtilities.h new file mode 100644 index 0000000..a4b6070 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMiscUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelMiscUtilities.h new file mode 100644 index 0000000..7e820b7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelOctreeUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelOctreeUtilities.h new file mode 100644 index 0000000..56e610c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelRangeUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelRangeUtilities.h new file mode 100644 index 0000000..19fb172 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelRichCurveUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelRichCurveUtilities.h new file mode 100644 index 0000000..3b9476d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFRangeUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFRangeUtilities.h new file mode 100644 index 0000000..ec86819 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilities.h new file mode 100644 index 0000000..3bb9b8d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilitiesImpl.inl b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSDFUtilitiesImpl.inl new file mode 100644 index 0000000..bc1fa86 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSerializationUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelSerializationUtilities.h new file mode 100644 index 0000000..1412e91 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h new file mode 100644 index 0000000..73f1026 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelStatsUtilities.h @@ -0,0 +1,11 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelMinimal.h" + +namespace FVoxelUtilities +{ +#define VOXEL_SCOPE_COUNTER_FORMAT(Format, ...) VOXEL_ASYNC_SCOPE_COUNTER(Format) +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelTextureUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelTextureUtilities.h new file mode 100644 index 0000000..6f8394d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h new file mode 100644 index 0000000..3d6b8e4 --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelThreadingUtilities.h @@ -0,0 +1,95 @@ +// 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()); + 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelVectorUtilities.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelUtilities/VoxelVectorUtilities.h new file mode 100644 index 0000000..80aaffa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelValue.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelValue.h new file mode 100644 index 0000000..e58f28f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelVector.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelVector.h new file mode 100644 index 0000000..c97bbf3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorld.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorld.h new file mode 100644 index 0000000..517750b --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorld.h @@ -0,0 +1,863 @@ +// 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) + TObjectPtr WorldRoot; + + UPROPERTY() + TObjectPtr 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)) + TObjectPtr 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") + TObjectPtr 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)) + TObjectPtr 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)) + TObjectPtr VoxelMaterial = nullptr; + + // The material collection to use in Single Index or Double Index material config + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel - Materials", meta = (UpdateRenderer)) + TObjectPtr 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)) + TObjectPtr 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)) + TObjectPtr 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; } + + 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) + TObjectPtr VoxelWorldEditor; +#endif + + UPROPERTY(Transient) + mutable TObjectPtr MultiplayerInterfaceInstance; + + UPROPERTY(Transient) + TObjectPtr GeneratorCache = nullptr; + + UPROPERTY(Transient) + TObjectPtr 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; + + 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; + + 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/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldCreateInfo.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldCreateInfo.h new file mode 100644 index 0000000..b81a53b --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr DataOverride = nullptr; + + TVoxelSharedPtr DataOverride_Raw; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldInterface.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldInterface.h new file mode 100644 index 0000000..b73d170 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldRootComponent.h b/Plugins/VoxelFree/Source/Voxel/Public/VoxelWorldRootComponent.h new file mode 100644 index 0000000..47ab519 --- /dev/null +++ b/Plugins/VoxelFree/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) + TObjectPtr 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/Plugins/VoxelFree/Source/Voxel/Voxel.Build.cs b/Plugins/VoxelFree/Source/Voxel/Voxel.Build.cs new file mode 100644 index 0000000..2d1dc2e --- /dev/null +++ b/Plugins/VoxelFree/Source/Voxel/Voxel.Build.cs @@ -0,0 +1,77 @@ +// 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", +#if UE_4_26_OR_LATER + "DeveloperSettings", + "TraceLog", +#endif + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "nvTessLib", + "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(\"VoxelFree\")"); + } +} diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelBase.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelBase.h new file mode 100644 index 0000000..644e7c7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.cpp new file mode 100644 index 0000000..5d48cb4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelDataAsset.h new file mode 100644 index 0000000..c02389c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphDataItemConfig.h new file mode 100644 index 0000000..7b8c7be --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.cpp new file mode 100644 index 0000000..af4053c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphGenerator.h new file mode 100644 index 0000000..42c852f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.cpp new file mode 100644 index 0000000..d34b207 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphMacro.h new file mode 100644 index 0000000..a5522d8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelGraphOutputsConfig.h new file mode 100644 index 0000000..0fce375 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.cpp new file mode 100644 index 0000000..77d5c5e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelHeightmapAsset.h new file mode 100644 index 0000000..ec91f25 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelMaterialCollection.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelMaterialCollection.h new file mode 100644 index 0000000..a5e55d5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawnerConfig.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawnerConfig.h new file mode 100644 index 0000000..2a63a5f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawners.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelSpawners.h new file mode 100644 index 0000000..86d7423 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelWorldSaveObject.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/AssetTools/AssetTypeActions_VoxelWorldSaveObject.h new file mode 100644 index 0000000..fde8f93 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/IVoxelDataAssetEditor.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/IVoxelDataAssetEditor.h new file mode 100644 index 0000000..32eb87e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.cpp new file mode 100644 index 0000000..11ef9a5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/SVoxelDataAssetEditorViewport.h new file mode 100644 index 0000000..9f22cae --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorCommands.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorCommands.h new file mode 100644 index 0000000..fa5e306 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.cpp new file mode 100644 index 0000000..c8d8daa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h new file mode 100644 index 0000000..648925c --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorManager.h @@ -0,0 +1,37 @@ +// 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; + virtual FString GetReferencerName() const override { return "FVoxelDataAssetEditorManager"; } + //~ 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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.cpp new file mode 100644 index 0000000..5a4115f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h new file mode 100644 index 0000000..7a59599 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorToolkit.h @@ -0,0 +1,113 @@ +// 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; + virtual FString GetReferencerName() const override { return "FVoxelDataAssetEditorToolkit"; } + //~ 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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp new file mode 100644 index 0000000..b3c1946 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.cpp @@ -0,0 +1,263 @@ +// 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" +#include "UnrealWidget.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/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/DataAssetEditor/VoxelDataAssetEditorViewportClient.h new file mode 100644 index 0000000..2c9d3b6 --- /dev/null +++ b/Plugins/VoxelFree/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 UE::Widget::EWidgetMode GetWidgetMode() const override { return UE::Widget::EWidgetMode::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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.cpp new file mode 100644 index 0000000..bfef758 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/RangeAnalysisDebuggerDetails.h new file mode 100644 index 0000000..facf8e8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.cpp new file mode 100644 index 0000000..04a898d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelAssetActorDetails.h new file mode 100644 index 0000000..0b6a967 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.cpp new file mode 100644 index 0000000..1fc3af1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBasicSpawnerScaleSettingsCustomization.h new file mode 100644 index 0000000..d1f03d9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.cpp new file mode 100644 index 0000000..783d273 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelBoolVectorCustomization.h new file mode 100644 index 0000000..331a9ff --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.cpp new file mode 100644 index 0000000..ca4097c --- /dev/null +++ b/Plugins/VoxelFree/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(FAppStyle::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, 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); + } + + UObject* 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(EPropertyChangeType::Unspecified); + })); + + { + 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, 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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGeneratorPickerCustomization.h new file mode 100644 index 0000000..bbbf4e3 --- /dev/null +++ b/Plugins/VoxelFree/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() + TObjectPtr GeneratorObject = nullptr; + + UPROPERTY() + TArray Parameters; + + UPROPERTY() + TObjectPtr Blueprint = nullptr; + + UPROPERTY() + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.cpp new file mode 100644 index 0000000..ca2b9dd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelGraphOutputCustomization.h new file mode 100644 index 0000000..64bfee3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.cpp new file mode 100644 index 0000000..e00120c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelInt32IntervalCustomization.h new file mode 100644 index 0000000..cf866f1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.cpp new file mode 100644 index 0000000..5ada11a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelLandscapeImporterDetails.h new file mode 100644 index 0000000..e5f1c73 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.cpp new file mode 100644 index 0000000..60d807d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshImporterDetails.h new file mode 100644 index 0000000..2305446 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.cpp new file mode 100644 index 0000000..8b2f0a1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelMeshSpawnerBaseDetails.h new file mode 100644 index 0000000..9040b37 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.cpp new file mode 100644 index 0000000..afa593f --- /dev/null +++ b/Plugins/VoxelFree/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( FAppStyle::Get(), "PropertyEditor.AssetComboStyle" ) + .ForegroundColor(FAppStyle::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( FAppStyle::Get(), "PropertyEditor.AssetClass" ) + .Font( FAppStyle::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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelPaintMaterialCustomization.h new file mode 100644 index 0000000..0acc04f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.cpp new file mode 100644 index 0000000..e74b388 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerConfigSpawnerCustomization.h new file mode 100644 index 0000000..3c34543 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.cpp new file mode 100644 index 0000000..72df1f8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerDensityCustomization.h new file mode 100644 index 0000000..abc594f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.cpp new file mode 100644 index 0000000..2d1bfe7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelSpawnerOutputNameCustomization.h new file mode 100644 index 0000000..51c7077 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp new file mode 100644 index 0000000..94106dd --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelWorldDetails.cpp @@ -0,0 +1,517 @@ +// 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(); + })); +} +} + +void FVoxelWorldDetails::BakeWorld(AVoxelWorld& World) +{ + FVoxelMessages::Info("Baking to static mesh requires Voxel Plugin Pro"); +} diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelWorldDetails.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Details/VoxelWorldDetails.h new file mode 100644 index 0000000..1850115 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdMode.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdMode.cpp new file mode 100644 index 0000000..e380c9f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdMode.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdMode.h new file mode 100644 index 0000000..562a17b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.cpp new file mode 100644 index 0000000..7eb5dd7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/EdMode/VoxelEdModeToolkit.h new file mode 100644 index 0000000..8fbe650 --- /dev/null +++ b/Plugins/VoxelFree/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 override; + virtual void BuildToolPalette(FName PaletteName, FToolBarBuilder& ToolbarBuilder) override; + virtual void OnToolPaletteChanged(FName PaletteName) override; + + /** Modes Panel Header Information **/ + virtual FText GetActiveToolDisplayName() const override; + virtual FText GetActiveToolMessage() const override; + +private: + FVoxelEditorToolsPanel& GetPanel() const; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp new file mode 100644 index 0000000..c0d972c --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.cpp @@ -0,0 +1,261 @@ +// Copyright 2020 Phyronnaz + +#include "Factories/VoxelDataAssetFactory.h" +#include "VoxelAssets/VoxelDataAsset.h" +#include "VoxelAssets/VoxelDataAssetData.inl" +#include "VoxelImporters/VoxelMeshImporter.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) +{ + FVoxelMessages::Info("Converting meshes to voxels requires Voxel Plugin Pro"); + 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(FAppStyle::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(FAppStyle::GetMargin("StandardDialog.SlotPadding")) + + SUniformGridPanel::Slot(0, 0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Create")) + .HAlign(HAlign_Center) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnOkClicked) + .ButtonStyle(FAppStyle::Get(), "FlatButton.Success") + .TextStyle(FAppStyle::Get(), "FlatButton.DefaultTextStyle") + ] + +SUniformGridPanel::Slot(1,0) + [ + SNew(SButton) + .Text(VOXEL_LOCTEXT("Cancel")) + .HAlign(HAlign_Center) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(OnCancelClicked) + .ButtonStyle(FAppStyle::Get(), "FlatButton.Default") + .TextStyle(FAppStyle::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) +{ + FVoxelMessages::Info("Importing MagicaVox assets requires Voxel Plugin Pro"); + 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) +{ + FVoxelMessages::Info("Converting meshes to voxels requires Voxel Plugin Pro"); + 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) +{ + FVoxelMessages::Info("Importing RawVox assets requires Voxel Plugin Pro"); + 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) +{ + FVoxelMessages::Info("Importing RawVox assets requires Voxel Plugin Pro"); + return EReimportResult::Failed; +} + +int32 UVoxelDataAssetFromRawVoxFactory::GetPriority() const +{ + return ImportPriority; +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelDataAssetFactory.h new file mode 100644 index 0000000..25848fc --- /dev/null +++ b/Plugins/VoxelFree/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() + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphDataItemConfigFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphDataItemConfigFactory.h new file mode 100644 index 0000000..83d4645 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphGeneratorAssetFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphGeneratorAssetFactory.h new file mode 100644 index 0000000..28e4702 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphMacroAssetFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphMacroAssetFactory.h new file mode 100644 index 0000000..067d714 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphOutputsConfigFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelGraphOutputsConfigFactory.h new file mode 100644 index 0000000..be303a3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp new file mode 100644 index 0000000..2ec7946 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.cpp @@ -0,0 +1,299 @@ +// 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" + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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) +{ + FVoxelMessages::Info("Importing Landscapes requires Voxel Plugin Pro"); + return nullptr; +} + +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() +{ + FVoxelMessages::Info("Importing Heightmaps requires Voxel Plugin Pro"); + return false; +} + +bool UVoxelHeightmapAssetUINT16Factory::DoImport(UVoxelHeightmapAssetUINT16* Asset) +{ + FVoxelMessages::Info("Importing Heightmaps requires Voxel Plugin Pro"); + return false; +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelHeightmapAssetFactory.h new file mode 100644 index 0000000..8e04c2b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelMaterialCollectionFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelMaterialCollectionFactory.h new file mode 100644 index 0000000..5f57d7f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelSpawnerConfigFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelSpawnerConfigFactory.h new file mode 100644 index 0000000..18c7d9d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelSpawnersFactories.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelSpawnersFactories.h new file mode 100644 index 0000000..c90d848 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelWorldSaveObjectFactory.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Factories/VoxelWorldSaveObjectFactory.h new file mode 100644 index 0000000..3727a6c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.cpp new file mode 100644 index 0000000..08f47db --- /dev/null +++ b/Plugins/VoxelFree/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.Get())); + } + + 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_Real, 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/Plugins/VoxelFree/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/K2Nodes/K2Node_AddDataItem.h new file mode 100644 index 0000000..11ec311 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelDataAssetThumbnailRenderer.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelDataAssetThumbnailRenderer.h new file mode 100644 index 0000000..8fbc73c --- /dev/null +++ b/Plugins/VoxelFree/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, bool bAdditionalViewFamily) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetThumbnail(), X, Y, Width, Height, Target, Canvas, bAdditionalViewFamily); + } +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelGraphGeneratorThumbnailRenderer.h new file mode 100644 index 0000000..b703b75 --- /dev/null +++ b/Plugins/VoxelFree/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, bool bAdditionalViewFamily) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetPreviewTexture(), X, Y, Width, Height, Target, Canvas, bAdditionalViewFamily); + } +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelHeightmapAssetThumbnailRenderer.h new file mode 100644 index 0000000..218fd6d --- /dev/null +++ b/Plugins/VoxelFree/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, bool bAdditionalViewFamily) override + { + UTextureThumbnailRenderer::Draw(CastChecked(Object)->GetThumbnail(), X, Y, Width, Height, Target, Canvas, bAdditionalViewFamily); + } +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.cpp new file mode 100644 index 0000000..af823f6 --- /dev/null +++ b/Plugins/VoxelFree/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 , bool bAdditionalViewFamily) +{ + UStaticMeshThumbnailRenderer::Draw(CastChecked(Object)->Mesh, X, Y, Width, Height, Target, Canvas , 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 , 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 , 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 , 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)) + { + UVoxelSpawner* 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 , 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 , 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)) + { + UStaticMesh* Mesh = Meshes[Index]; + if (Mesh) + { + // TODO proper bAdditionalViewFamily + UStaticMeshThumbnailRenderer::Draw(Mesh, NewX, NewY, NewWidth, NewHeight, Target, Canvas , bAdditionalViewFamily); + } + } + }); +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/Thumbnails/VoxelSpawnersThumbnailRenderer.h new file mode 100644 index 0000000..eb99376 --- /dev/null +++ b/Plugins/VoxelFree/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, 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, 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, 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, bool bAdditionalViewFamily) override; +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.cpp new file mode 100644 index 0000000..d656530 --- /dev/null +++ b/Plugins/VoxelFree/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->GetEditorOnlyData()->ExpressionCollection.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->GetEditorOnlyData()->ExpressionCollection.Expressions, 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)->GetEditorOnlyData()->ExpressionCollection.Expressions : CastChecked(Owner)->GetEditorOnlyData()->ExpressionCollection.Expressions; + 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelConvertLandscapeMaterial.h new file mode 100644 index 0000000..f4d6395 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelDebugEditor.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelDebugEditor.cpp new file mode 100644 index 0000000..e4638a0 --- /dev/null +++ b/Plugins/VoxelFree/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++) + { + FVector3f Position; + float Distance; + if (FVector3f(X - Size.X / 2.f, Y - Size.Y / 2.f, Z - Size.Z / 2.f).Size() < 16) + { + Position = FVector3f(X, Y, Z); + Distance = 0.f; + } + else + { + Position = FVector3f(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 FVector3f 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelDebugEditor.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelDebugEditor.h new file mode 100644 index 0000000..f5dce73 --- /dev/null +++ b/Plugins/VoxelFree/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")) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.cpp new file mode 100644 index 0000000..b3258b7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsIncludes.h new file mode 100644 index 0000000..754d7ad --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.cpp new file mode 100644 index 0000000..4901913 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorDetailsUtilities.h new file mode 100644 index 0000000..cc5fea1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorModule.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorModule.cpp new file mode 100644 index 0000000..c5c7e0a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp new file mode 100644 index 0000000..b17bd73 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.cpp @@ -0,0 +1,735 @@ +// 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" +#include "VariablePrecisionNumericInterface.h" +#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) + { + const auto& ParentProperties = PropertyAndParent.ParentProperties; + 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(FAppStyle::GetBrush("DetailsView.AdvancedDropdownBorder")) + .Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f)) + [ + SAssignNew(ExpanderButton, SButton) + .ButtonStyle(FAppStyle::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 ? FAppStyle::GetBrush("DetailsView.PulldownArrow.Up.Hovered") : FAppStyle::GetBrush("DetailsView.PulldownArrow.Down.Hovered"); + } + else + { + return bShowCustomTools ? FAppStyle::GetBrush("DetailsView.PulldownArrow.Up") : FAppStyle::GetBrush("DetailsView.PulldownArrow.Down"); + } + }) + ] + ] + ]; + + CustomToolBarsVerticalBox->AddSlot() + .AutoHeight() + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle")) + .Padding(FMargin(0.0f, 3.0f, 16.f, 0.0f)) + [ + SNew(SImage) + .Image(FAppStyle::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) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h new file mode 100644 index 0000000..960a572 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelEditorToolsPanel.h @@ -0,0 +1,105 @@ +// 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; + virtual FString GetReferencerName() const override { return "FVoxelEditorToolsPanel"; } + //~ 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp new file mode 100644 index 0000000..40d1514 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelMessagesEditor.cpp @@ -0,0 +1,198 @@ +// 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()); + } + } + } + }; + + const TArray& ScriptStack = FBlueprintContextTracker::Get().GetScriptStack(); + + 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelMessagesEditor.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelMessagesEditor.h new file mode 100644 index 0000000..f0c7a31 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp new file mode 100644 index 0000000..1aef239 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.cpp @@ -0,0 +1,102 @@ +// 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 "Framework/MultiBox/MultiBoxBuilder.h" + +void UVoxelOpenAssetsOnStartup::Init() +{ + FTSTicker::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; + } + + GEditor->GetEditorSubsystem()->OpenEditorForAsset(It.Key.ToString()); + } + } + + 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 = 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelOpenAssetsOnStartup.h new file mode 100644 index 0000000..eabf133 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp new file mode 100644 index 0000000..889a35e --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelScopedTransaction.cpp @@ -0,0 +1,117 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelScopedTransaction.h" +#include "VoxelTools/VoxelBlueprintLibrary.h" +#include "VoxelWorld.h" +#include "Editor.h" +#include "Misc/ITransaction.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)); + } + } + + 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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelToolsCommands.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelToolsCommands.cpp new file mode 100644 index 0000000..dfe313e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelToolsCommands.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelToolsCommands.h new file mode 100644 index 0000000..e68f5cb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelWorldEditorControls.cpp b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelWorldEditorControls.cpp new file mode 100644 index 0000000..e1f867e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelWorldEditorControls.h b/Plugins/VoxelFree/Source/VoxelEditor/Private/VoxelWorldEditorControls.h new file mode 100644 index 0000000..e84a83e --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelEditor/Public/VoxelEditorModule.h b/Plugins/VoxelFree/Source/VoxelEditor/Public/VoxelEditorModule.h new file mode 100644 index 0000000..9fa8116 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/Public/VoxelScopedTransaction.h b/Plugins/VoxelFree/Source/VoxelEditor/Public/VoxelScopedTransaction.h new file mode 100644 index 0000000..ed5e0ff --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditor/VoxelEditor.Build.cs b/Plugins/VoxelFree/Source/VoxelEditor/VoxelEditor.Build.cs new file mode 100644 index 0000000..a32b3d3 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditor/VoxelEditor.Build.cs @@ -0,0 +1,71 @@ +// 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", + "EditorFramework", +#if UE_4_26_OR_LATER + "DeveloperSettings", +#endif + }); + + PrivateIncludePathModuleNames.AddRange( + new string[] { + "VoxelGraphEditor" + }); + } +} diff --git a/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/ActorFactoryVoxelPlaceableItems.cpp b/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/ActorFactoryVoxelPlaceableItems.cpp new file mode 100644 index 0000000..9a5bedd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp b/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp new file mode 100644 index 0000000..05ea8a1 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/ActorFactoryVoxelWorld.cpp @@ -0,0 +1,32 @@ +// 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")); + if (!VoxelWorld->IsCreated()) + { + VoxelWorld->Toggle(); + } +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/VoxelEditorDefaultModule.cpp b/Plugins/VoxelFree/Source/VoxelEditorDefault/Private/VoxelEditorDefaultModule.cpp new file mode 100644 index 0000000..fbc85b7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelMeshImporter.h b/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelMeshImporter.h new file mode 100644 index 0000000..2f81ff1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelPlaceableItems.h b/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelPlaceableItems.h new file mode 100644 index 0000000..cb1885f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelWorld.h b/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/ActorFactoryVoxelWorld.h new file mode 100644 index 0000000..3a84bd5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/VoxelEditorDefaultModule.h b/Plugins/VoxelFree/Source/VoxelEditorDefault/Public/VoxelEditorDefaultModule.h new file mode 100644 index 0000000..1cf8dd4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelEditorDefault/VoxelEditorDefault.Build.cs b/Plugins/VoxelFree/Source/VoxelEditorDefault/VoxelEditorDefault.Build.cs new file mode 100644 index 0000000..9c9a14b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelExamples/Private/VoxelExamplesModule.cpp b/Plugins/VoxelFree/Source/VoxelExamples/Private/VoxelExamplesModule.cpp new file mode 100644 index 0000000..e02e989 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelExamples/Private/VoxelGeneratorExample.cpp b/Plugins/VoxelFree/Source/VoxelExamples/Private/VoxelGeneratorExample.cpp new file mode 100644 index 0000000..493e2ff --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelExamples/Public/VoxelExamplesModule.h b/Plugins/VoxelFree/Source/VoxelExamples/Public/VoxelExamplesModule.h new file mode 100644 index 0000000..8b0878a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelExamples/Public/VoxelGeneratorExample.h b/Plugins/VoxelFree/Source/VoxelExamples/Public/VoxelGeneratorExample.h new file mode 100644 index 0000000..8d9d89c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelExamples/VoxelExamples.Build.cs b/Plugins/VoxelFree/Source/VoxelExamples/VoxelExamples.Build.cs new file mode 100644 index 0000000..a045a8a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp new file mode 100644 index 0000000..00f3664 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/CppTranslation/VoxelVariables.cpp @@ -0,0 +1,46 @@ +// Copyright 2020 Phyronnaz + +#include "CppTranslation/VoxelVariables.h" +#include "VoxelNodes/VoxelExposedNodes.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; + } +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.cpp new file mode 100644 index 0000000..54780bc --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Capsule_Graph.h new file mode 100644 index 0000000..d0accfb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.cpp new file mode 100644 index 0000000..1126b45 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Example_Crater_Graph.h new file mode 100644 index 0000000..d6f991a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.cpp new file mode 100644 index 0000000..f61c7b8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Ravine_Graph.h new file mode 100644 index 0000000..3fe3425 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.cpp new file mode 100644 index 0000000..d83128b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VDI_Sphere_Graph.h new file mode 100644 index 0000000..29d5150 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Craters.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Craters.cpp new file mode 100644 index 0000000..efa8730 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Craters.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Craters.h new file mode 100644 index 0000000..279414f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.cpp new file mode 100644 index 0000000..2fd219f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Dunes.h new file mode 100644 index 0000000..d2b82b3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.cpp new file mode 100644 index 0000000..fa43fb9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_Erosion.h new file mode 100644 index 0000000..802d9b3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.cpp new file mode 100644 index 0000000..86a55eb --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_FastCraters.h new file mode 100644 index 0000000..6676356 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.cpp new file mode 100644 index 0000000..b67ec2e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VG_Example_MultiIndex.h new file mode 100644 index 0000000..1148f97 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.cpp new file mode 100644 index 0000000..5f247e1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cave.h new file mode 100644 index 0000000..f285b90 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.cpp new file mode 100644 index 0000000..d7e8b4b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Cliffs.h new file mode 100644 index 0000000..35834d9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.cpp new file mode 100644 index 0000000..76bcd96 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_FloatingIslandOnion.h new file mode 100644 index 0000000..b616c62 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.cpp new file mode 100644 index 0000000..334ac78 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HeightmapComposition.h new file mode 100644 index 0000000..be1c84a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.cpp new file mode 100644 index 0000000..6b6d385 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_HollowPlanet.h new file mode 100644 index 0000000..7e5aba5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.cpp new file mode 100644 index 0000000..7f61e14 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_IQNoise.h new file mode 100644 index 0000000..0b1a15a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.cpp new file mode 100644 index 0000000..58ca783 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredPlanet.h new file mode 100644 index 0000000..445302a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.cpp new file mode 100644 index 0000000..ef36fab --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_LayeredWorld.h new file mode 100644 index 0000000..367c6ed --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.cpp new file mode 100644 index 0000000..fe76cba --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Planet.h new file mode 100644 index 0000000..b523973 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.cpp new file mode 100644 index 0000000..b66d04a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Ravines.h new file mode 100644 index 0000000..90982a8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.cpp new file mode 100644 index 0000000..1b0452f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_RingWorld.h new file mode 100644 index 0000000..bcc8800 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.cpp new file mode 100644 index 0000000..834cd4c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.h b/Plugins/VoxelFree/Source/VoxelGraph/Private/Examples/VoxelExample_Tool_NoisyColors.h new file mode 100644 index 0000000..d30c6da --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/NodeFunctions/VoxelNodeFunctions.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/NodeFunctions/VoxelNodeFunctions.cpp new file mode 100644 index 0000000..4cfbc34 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelAxisDependencies.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelAxisDependencies.cpp new file mode 100644 index 0000000..812bdbd --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelContext.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelContext.cpp new file mode 100644 index 0000000..b2395d0 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp new file mode 100644 index 0000000..fd8870a --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphErrorReporter.cpp @@ -0,0 +1,265 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphErrorReporter.h" +#include "VoxelNode.h" +#include "VoxelGraphGenerator.h" +#include "IVoxelGraphEditor.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 (UVoxelGraphNodeInterface* GraphNode = Node->GraphNode) + { + AddMessageToNodeInternal(Node, MessageWithPrefix, Severity); + GraphsToRefresh.Add(GraphNode->GetGraph()); + } +#endif +} + +void FVoxelGraphErrorReporter::AddNodeToSelect(const UVoxelNode* Node) +{ +#if WITH_EDITORONLY_DATA + if (Node && Node->GraphNode) + { + NodesToSelect.Add(Node->GraphNode); + } +#endif +} + +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())) + { + UVoxelGraphMacro* 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 (UVoxelGraphNodeInterface* 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; + } +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp new file mode 100644 index 0000000..9646a47 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphGenerator.cpp @@ -0,0 +1,306 @@ +// 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 "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->GetPlatformData()->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 +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +void UVoxelGraphGenerator::ApplyParameters(const TMap& Parameters) +{ + for (UVoxelNode* Node : AllNodes) + { + Node->ApplyParameters(Parameters); + } +} + +void UVoxelGraphGenerator::GetParameters(TArray& OutParameters) const +{ + for (UVoxelNode* 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) +{ + FVoxelMessages::Info("Running Voxel Graphs require Voxel Plugin Pro"); + 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(); + } + } +} + +///////////////////////////////////////////////////////////////////////////////// + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphModule.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphModule.cpp new file mode 100644 index 0000000..8a96a8f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp new file mode 100644 index 0000000..230ec93 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphOutputs.cpp @@ -0,0 +1,72 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphOutputs.h" +#include "VoxelGraphConstants.h" + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphOutputsConfig.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphOutputsConfig.cpp new file mode 100644 index 0000000..e75bc73 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphPreviewSettings.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelGraphPreviewSettings.cpp new file mode 100644 index 0000000..3a420b7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNode.cpp new file mode 100644 index 0000000..586f42c --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNode.cpp @@ -0,0 +1,191 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNode.h" +#include "IVoxelGraphEditor.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.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 +} + + +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 = 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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp new file mode 100644 index 0000000..726030f --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBinaryNodes.cpp @@ -0,0 +1,20 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBinaryNodes.h" +#include "Runtime/VoxelNodeType.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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp new file mode 100644 index 0000000..c0d2083 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMapNode.cpp @@ -0,0 +1,82 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBiomeMapNode.h" +#include "VoxelNodes/VoxelTextureSamplerNode.h" +#include "CppTranslation/VoxelVariables.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()); +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp new file mode 100644 index 0000000..aa05f83 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelBiomeMergeNode.cpp @@ -0,0 +1,105 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelBiomeMergeNode.h" +#include "VoxelNodes/VoxelNodeHelperMacros.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; +} + + +#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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp new file mode 100644 index 0000000..1c12d37 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelConstantNodes.cpp @@ -0,0 +1,59 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelConstantNodes.h" +#include "CppTranslation/VoxelVariables.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); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_WorldSize::UVoxelNode_WorldSize() +{ + SetOutputs(EC::Int); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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; +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp new file mode 100644 index 0000000..c09fda5 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCoordinatesNodes.cpp @@ -0,0 +1,199 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelCoordinatesNodes.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "Runtime/VoxelNodeType.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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp new file mode 100644 index 0000000..b53bf41 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelCurveNodes.cpp @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelCurveNodes.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); +} + + +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" }); +} + + +FText UVoxelNode_CurveColor::GetTitle() const +{ + return FText::Format(VOXEL_LOCTEXT("Color Curve: {0}"), Super::GetTitle()); +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp new file mode 100644 index 0000000..193e9ad --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelDataAssetSamplerNode.cpp @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelDataAssetSamplerNode.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" }); +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp new file mode 100644 index 0000000..6ee34ac --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExecNodes.cpp @@ -0,0 +1,263 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelExecNodes.h" + +#include "VoxelNodes/VoxelNodeColors.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); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetSingleIndex::UVoxelNode_SetSingleIndex() +{ + SetInputs( + EC::Exec, + { "Index", EC::Int, "Index between 0 and 255", "", {0, 255} }); + SetOutputs(EC::Exec); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetMultiIndexWetness::UVoxelNode_SetMultiIndexWetness() +{ + SetInputs( + EC::Exec, + { "Wetness", EC::Float, "Wetness between 0 and 1", "", {0, 1 } }); + SetOutputs(EC::Exec); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SetNode::UVoxelNode_SetNode() +{ + SetInputs(EC::Exec, EC::Exec); + SetOutputs(EC::Exec); +} + + +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); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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(); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp new file mode 100644 index 0000000..0b9a9e3 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelExposedNodes.cpp @@ -0,0 +1,238 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelExposedNodes.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "CppTranslation/VoxelVariables.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 (UVoxelNode* 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 = 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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp new file mode 100644 index 0000000..2e8def7 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGavoronoiNoiseNode.cpp @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGavoronoiNoiseNode.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); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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)); +} + + +#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 + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp new file mode 100644 index 0000000..599b221 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorMergeNode.cpp @@ -0,0 +1,107 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGeneratorMergeNode.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphErrorReporter.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGenerators/VoxelGeneratorInstance.h" +#include "VoxelGraphOutputsConfig.h" +#include "NodeFunctions/VoxelNodeFunctions.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; +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp new file mode 100644 index 0000000..82f0fc0 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGeneratorSamplerNodes.cpp @@ -0,0 +1,80 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGeneratorSamplerNodes.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelGenerators/VoxelFlatGenerator.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGenerators/VoxelGeneratorInstance.inl" +#include "NodeFunctions/VoxelNodeFunctions.h" + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorValue::UVoxelNode_GetGeneratorValue() +{ + SetOutputs({"", EC::Float, "Value"}); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorMaterial::UVoxelNode_GetGeneratorMaterial() +{ + SetOutputs({ "", EC::Material, "Material" }); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_GetGeneratorCustomOutput::UVoxelNode_GetGeneratorCustomOutput() +{ + SetOutputs({ "", EC::Float, "Custom Output Value" }); +} + +FText UVoxelNode_GetGeneratorCustomOutput::GetTitle() const +{ + return FText::FromString("Get Generator Custom Output: " + OutputName.ToString()); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp new file mode 100644 index 0000000..a204bf7 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGetMaterialCollectionIndexNode.cpp @@ -0,0 +1,54 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGetMaterialCollectionIndexNode.h" +#include "CppTranslation/VoxelVariables.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; +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp new file mode 100644 index 0000000..5b3f06e --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGradientPerturbNodes.cpp @@ -0,0 +1,62 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGradientPerturbNodes.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" }); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp new file mode 100644 index 0000000..c7a8772 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphAssetNodes.cpp @@ -0,0 +1,91 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGraphAssetNodes.h" +#include "VoxelNodes/VoxelGeneratorSamplerNodes.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(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 + ); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 + ); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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()); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_EditGetHardness::UVoxelNode_EditGetHardness() +{ + SetInputs( + EC::Material + ); + SetOutputs( + EC::Float + ); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp new file mode 100644 index 0000000..f24c517 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelGraphMacro.cpp @@ -0,0 +1,246 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelGraphMacro.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "VoxelNodes/VoxelLocalVariables.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; +} + + +#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); +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp new file mode 100644 index 0000000..cbff366 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightSplitterNode.cpp @@ -0,0 +1,78 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelHeightSplitterNode.h" +#include "Runtime/VoxelNodeType.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); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp new file mode 100644 index 0000000..7abb4ff --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelHeightmapSamplerNode.cpp @@ -0,0 +1,37 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelHeightmapSamplerNode.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" }); +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp new file mode 100644 index 0000000..8fc36ec --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelIfNode.cpp @@ -0,0 +1,14 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelIfNode.h" +#include "VoxelNodes/VoxelNodeColors.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); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp new file mode 100644 index 0000000..59fb0b6 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelLocalVariables.cpp @@ -0,0 +1,327 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelLocalVariables.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 (UVoxelNode* 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(); +} + + +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(); +} + + +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 IsValid(Declaration); +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp new file mode 100644 index 0000000..fc1f06c --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMaterialNodes.cpp @@ -0,0 +1,55 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelMaterialNodes.h" +#include "Runtime/VoxelNodeType.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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp new file mode 100644 index 0000000..2429025 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelMathNodes.cpp @@ -0,0 +1,886 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelMathNodes.h" +#include "Runtime/VoxelNodeType.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); +} +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); +} +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); +} +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 +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeColors.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeColors.cpp new file mode 100644 index 0000000..47bf70f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp new file mode 100644 index 0000000..610cc6e --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNodeHelper.cpp @@ -0,0 +1,81 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNodeHelper.h" +#include "VoxelNodes/VoxelNodeColors.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); +} + + +uint8 UVoxelNodeWithContext::GetNodeDependencies() const +{ + return EVoxelAxisDependenciesFlags::X; +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp new file mode 100644 index 0000000..ff3eaef --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelNoiseNodes.cpp @@ -0,0 +1,318 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelNoiseNodes.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; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// + +#if WITH_EDITOR +void UVoxelNode_NoiseNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + if (PropertyChangedEvent.MemberProperty && + PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) + { + } +} + +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) + { + } +} + +void UVoxelNode_NoiseNode::PostInitProperties() +{ + Super::PostInitProperties(); + if (OutputRanges.Num() == 0 && !HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad)) + { + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp new file mode 100644 index 0000000..ca785ce --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelOptimizationNodes.cpp @@ -0,0 +1,151 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelOptimizationNodes.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)); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_Sleep::UVoxelNode_Sleep() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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"); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmartMin::UVoxelNode_SmartMin() +{ + SetInputs(EC::Float); + SetOutputs(EC::Float); + SetInputsCount(3, MAX_VOXELNODE_PINS); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_SmartMax::UVoxelNode_SmartMax() +{ + SetInputs(EC::Exec, EC::Float); + SetOutputs(EC::Exec, EC::Float); + SetInputsCount(3, MAX_VOXELNODE_PINS); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp new file mode 100644 index 0000000..8830741 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelParameterNodes.cpp @@ -0,0 +1,41 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelParameterNodes.h" +#include "CppTranslation/VoxelVariables.h" + + +UVoxelNode_FloatParameter::UVoxelNode_FloatParameter() +{ + SetOutputs(EC::Float); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_IntParameter::UVoxelNode_IntParameter() +{ + SetOutputs(EC::Int); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_ColorParameter::UVoxelNode_ColorParameter() +{ + SetOutputs(EC::Color); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_BoolParameter::UVoxelNode_BoolParameter() +{ + SetOutputs(EC::Boolean); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp new file mode 100644 index 0000000..c282a86 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelPlaceableItemsNodes.cpp @@ -0,0 +1,79 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelPlaceableItemsNodes.h" +#include "Runtime/VoxelNodeType.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); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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; +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp new file mode 100644 index 0000000..c0ff63c --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelRandomNodes.cpp @@ -0,0 +1,35 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelRandomNodes.h" +#include "Runtime/VoxelNodeType.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)); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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)); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp new file mode 100644 index 0000000..126dd15 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSDFNodes.cpp @@ -0,0 +1,441 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelSDFNodes.h" +#include "Runtime/VoxelNodeType.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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp new file mode 100644 index 0000000..af6dfe7 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelSeedNodes.cpp @@ -0,0 +1,57 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelSeedNodes.h" +#include "VoxelNodes/VoxelNodeColors.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelGenerators/VoxelGeneratorInit.h" +#include "VoxelGraphGlobals.h" + +UVoxelSeedNode::UVoxelSeedNode() +{ + SetColor(FVoxelNodeColors::SeedNode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_Seed::UVoxelNode_Seed() +{ + SetOutputs(EC::Seed); + SetColor(FVoxelNodeColors::SeedNode); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UVoxelNode_MakeSeeds::UVoxelNode_MakeSeeds() +{ + SetInputs(EC::Seed); + SetOutputs(EC::Seed); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp new file mode 100644 index 0000000..94d07bb --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelTextureSamplerNode.cpp @@ -0,0 +1,80 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelTextureSamplerNode.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" }); +} + + +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); +} + + +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/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp new file mode 100644 index 0000000..76b6956 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelVoronoiNoiseNodes.cpp @@ -0,0 +1,129 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelVoronoiNoiseNodes.h" +#include "CppTranslation/VoxelVariables.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; + } +} + + +UVoxelNode_2DVoronoiNoise::UVoxelNode_2DVoronoiNoise() +{ + Dimension = 2; +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp new file mode 100644 index 0000000..9e01d49 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelNodes/VoxelWhiteNoiseNodes.cpp @@ -0,0 +1,28 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelNodes/VoxelWhiteNoiseNodes.h" +#include "CppTranslation/VoxelVariables.h" +#include "VoxelGraphGenerator.h" +#include "FastNoise/VoxelFastNoise.inl" + + +UVoxelNode_2DWhiteNoise::UVoxelNode_2DWhiteNoise() +{ + SetInputs( + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Float); +} + + +UVoxelNode_3DWhiteNoise::UVoxelNode_3DWhiteNoise() +{ + SetInputs( + {"X", EC::Float, "X"}, + {"Y", EC::Float, "Y"}, + {"Z", EC::Float, "Z"}, + {"Seed", EC::Seed, "Seed"}); + SetOutputs(EC::Float); +} + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelPinCategory.cpp b/Plugins/VoxelFree/Source/VoxelGraph/Private/VoxelPinCategory.cpp new file mode 100644 index 0000000..a794e89 --- /dev/null +++ b/Plugins/VoxelFree/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 || String == "real") + { + 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/Plugins/VoxelFree/Source/VoxelGraph/Public/Compilation/VoxelCompilationEnums.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/Compilation/VoxelCompilationEnums.h new file mode 100644 index 0000000..4fb5390 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h new file mode 100644 index 0000000..ab7f92d --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/CppTranslation/VoxelVariables.h @@ -0,0 +1,15 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +class UVoxelExposedNode; + +class VOXELGRAPH_API FVoxelVariable +{ +public: + static FString SanitizeName(const FString& Name); + +}; + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/IVoxelGraphEditor.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/IVoxelGraphEditor.h new file mode 100644 index 0000000..b834462 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelDeprecatedNodeFunctions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelDeprecatedNodeFunctions.h new file mode 100644 index 0000000..cca74ba --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelMathNodeFunctions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelMathNodeFunctions.h new file mode 100644 index 0000000..3e7de1c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelNodeFunctions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelNodeFunctions.h new file mode 100644 index 0000000..469e8ca --- /dev/null +++ b/Plugins/VoxelFree/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 { int32(FMath::FloorToInt(Value.Min)), int32(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 { int32(FMath::CeilToInt(A.Min)), int32(FMath::CeilToInt(A.Max)) }; + } + + inline int32 FloorToInt(v_flt A) + { + return FMath::FloorToInt(A); + } + inline TVoxelRange FloorToInt(const TVoxelRange& A) + { + return { int32(FMath::FloorToInt(A.Min)), int32(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/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelPlaceableItemsNodeFunctions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelPlaceableItemsNodeFunctions.h new file mode 100644 index 0000000..5d9f76e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelSDFNodeFunctions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/NodeFunctions/VoxelSDFNodeFunctions.h new file mode 100644 index 0000000..a41449a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/Runtime/VoxelNodeType.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/Runtime/VoxelNodeType.h new file mode 100644 index 0000000..c979c65 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelAxisDependencies.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelAxisDependencies.h new file mode 100644 index 0000000..8e75b78 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelContext.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelContext.h new file mode 100644 index 0000000..7bbb6ca --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsDefinitions.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsDefinitions.h new file mode 100644 index 0000000..17482c4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsIncludes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsIncludes.h new file mode 100644 index 0000000..8264f3a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsPCH.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGeneratedWorldGeneratorsPCH.h new file mode 100644 index 0000000..3e04547 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphConstants.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphConstants.h new file mode 100644 index 0000000..a2c33fa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphDataItemConfig.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphDataItemConfig.h new file mode 100644 index 0000000..209f6aa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h new file mode 100644 index 0000000..d4bb6d5 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphErrorReporter.h @@ -0,0 +1,83 @@ +// 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 AddNodeToSelect(const UVoxelNode* 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; +}; + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGenerator.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGenerator.h new file mode 100644 index 0000000..351373d --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGenerator.h @@ -0,0 +1,195 @@ +// 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") + TObjectPtr 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() + TObjectPtr FirstNode; + + UPROPERTY() + FGuid FirstNodePinId; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + TObjectPtr VoxelGraph; + + UPROPERTY() + TObjectPtr VoxelDebugGraph; + + FEdGraphPinReference PreviewedPin; +#endif + + UPROPERTY() + TObjectPtr PreviewSettings; + +public: + TMap TransientParameters; + +public: + + //~ 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) + TObjectPtr PreviewTexture; +#endif + +private: + void UpdateSetterNodes(); + void BindUpdateSetterNodes(); +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGeneratorHelpers.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGeneratorHelpers.h new file mode 100644 index 0000000..df9ed2e --- /dev/null +++ b/Plugins/VoxelFree/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(InPlace, 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(InPlace, 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGlobals.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphGlobals.h new file mode 100644 index 0000000..2e96d90 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphModule.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphModule.h new file mode 100644 index 0000000..2b31e8a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphOutputs.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphOutputs.h new file mode 100644 index 0000000..b16efea --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphOutputs.h @@ -0,0 +1,55 @@ +// 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; + + + 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphOutputsConfig.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphOutputsConfig.h new file mode 100644 index 0000000..fda47be --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphPreviewSettings.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelGraphPreviewSettings.h new file mode 100644 index 0000000..00b0964 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr MaterialCollection = nullptr; + + // Used to preview placeable items + UPROPERTY(EditAnywhere, Category = "Voxel World Settings", Instanced, meta = (Automatic, UpdateItems)) + TObjectPtr 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() + TObjectPtr Mesh = nullptr; + + UPROPERTY() + TObjectPtr HeightmapMaterial = nullptr; + + UPROPERTY() + TObjectPtr 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() + TObjectPtr 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(double X, double 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNode.h new file mode 100644 index 0000000..dbe879e --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNode.h @@ -0,0 +1,182 @@ +// 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() + TObjectPtr Graph; + +#if WITH_EDITORONLY_DATA + UPROPERTY() + TObjectPtr 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 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelAssetPickerNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelAssetPickerNode.h new file mode 100644 index 0000000..ab06f98 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBinaryNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBinaryNodes.h new file mode 100644 index 0000000..bf53b0d --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMapNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMapNode.h new file mode 100644 index 0000000..dda91a3 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h new file mode 100644 index 0000000..2ea00e3 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelBiomeMergeNode.h @@ -0,0 +1,41 @@ +// 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; + + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h new file mode 100644 index 0000000..a3bc1d1 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelConstantNodes.h @@ -0,0 +1,62 @@ +// 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; + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelCoordinatesNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelCoordinatesNodes.h new file mode 100644 index 0000000..8f60a37 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelCurveNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelCurveNodes.h new file mode 100644 index 0000000..4f37ce8 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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)) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelDataAssetSamplerNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelDataAssetSamplerNode.h new file mode 100644 index 0000000..5b48310 --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelDeprecatedNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelDeprecatedNodes.h new file mode 100644 index 0000000..0cf605b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h new file mode 100644 index 0000000..38ee7e6 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelExecNodes.h @@ -0,0 +1,177 @@ +// 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 + //~ 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; + + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelExposedNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelExposedNodes.h new file mode 100644 index 0000000..191fcc8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h new file mode 100644 index 0000000..1ca59a4 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGavoronoiNoiseNode.h @@ -0,0 +1,78 @@ +// 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; } + //~ 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; } + //~ 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; } + //~ 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorMergeNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorMergeNode.h new file mode 100644 index 0000000..14b32df --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h new file mode 100644 index 0000000..67bc7b2 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGeneratorSamplerNodes.h @@ -0,0 +1,76 @@ +// 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 + //~ 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGetMaterialCollectionIndexNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGetMaterialCollectionIndexNode.h new file mode 100644 index 0000000..e97d00e --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGradientPerturbNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGradientPerturbNodes.h new file mode 100644 index 0000000..9e978a5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphAssetNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphAssetNodes.h new file mode 100644 index 0000000..26a52f7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h new file mode 100644 index 0000000..c82f6e9 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelGraphMacro.h @@ -0,0 +1,159 @@ +// 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() + TObjectPtr 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; + + +#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() + TObjectPtr InputNode; + + UPROPERTY() + TObjectPtr OutputNode; + + FText GetMacroName() const; + FText GetMacroCategory() const; +}; + +UCLASS(NotPlaceable) +class VOXELGRAPH_API UVoxelGraphMacroNode : public UVoxelNode +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, Category = "Voxel", meta = (ReconstructNode)) + TObjectPtr Macro; + + + 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightSplitterNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightSplitterNode.h new file mode 100644 index 0000000..01a14d5 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightmapSamplerNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelHeightmapSamplerNode.h new file mode 100644 index 0000000..a445928 --- /dev/null +++ b/Plugins/VoxelFree/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")) + TObjectPtr HeightmapFloat; + + UPROPERTY(EditAnywhere, Category = "Heightmap settings", meta = (DisplayName = "Heightmap (uint16)", EditCondition = "!bFloatHeightmap")) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h new file mode 100644 index 0000000..0a239b9 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelIfNode.h @@ -0,0 +1,61 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.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(); + +}; + +// 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; + } +}; + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h new file mode 100644 index 0000000..6e67c34 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelLocalVariables.h @@ -0,0 +1,141 @@ +// 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 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() + TObjectPtr 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 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelMaterialNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelMaterialNodes.h new file mode 100644 index 0000000..79bcf50 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h new file mode 100644 index 0000000..56294c9 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelMathNodes.h @@ -0,0 +1,750 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.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(); +}; + +// 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(); +}; + +// 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(); +}; + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeColors.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeColors.h new file mode 100644 index 0000000..b222372 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h new file mode 100644 index 0000000..c78adb0 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelper.h @@ -0,0 +1,422 @@ +// 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 UVoxelSetterNode Interface + virtual int32 GetOutputIndex() const { unimplemented(); return -1; }; + //~ End UVoxelSetterNode Interface +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelPureNode : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: +}; + +UCLASS(Abstract) +class VOXELGRAPH_API UVoxelNodeWithDependencies : public UVoxelNodeHelper +{ + GENERATED_BODY() + +public: + + //~ 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h new file mode 100644 index 0000000..8965881 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeHelperMacros.h @@ -0,0 +1,168 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" + +using EC = EVoxelPinCategory; + +#define GENERATED_VOXELNODE_BODY() +#define GENERATED_VOXELNODE_IMPL_IMPL(Name, ...) +#define GENERATED_DATA_COMPUTE_NODE_BODY() +#define GENERATED_COMPUTE(InputsDef, OutputsDef, ...) +#define GENERATED_COMPUTENODE(InputsDef, OutputsDef, ...) +#define GENERATED_VOXELNODE_IMPL(Name, InputsDef, OutputsDef, Code) +#define GENERATED_COMPUTENODE_PREFIXOPLOOP(Op, Type) +#define GENERATED_VOXELNODE_IMPL_PREFIXOPLOOP(Name, Op, Type) +#define GENERATED_COMPUTENODE_INFIXOPLOOP(Op, Type) + +#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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeStructs.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNodeStructs.h new file mode 100644 index 0000000..afe4450 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h new file mode 100644 index 0000000..bcf9b7f --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodes.h @@ -0,0 +1,282 @@ +// 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; +}; + + +// 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; +}; + + +// 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h new file mode 100644 index 0000000..43dd664 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesBase.h @@ -0,0 +1,192 @@ +// 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 "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; + //~ 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; } + //~ 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; } +}; + + +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 +}; + + +////////////////////////////////////////////////////////////////////////////////////// + +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; +}; + + +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 }; +}; + diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h new file mode 100644 index 0000000..f129021 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelNoiseNodesMacros.h @@ -0,0 +1,32 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "FastNoise/VoxelFastNoise.inl" + +#define GENERATED_NOISENODE_BODY_IMPL(Dimension, FunctionName, Body, Parent) \ + virtual uint32 GetDimension() const override final { return Dimension; } + +#define GENERATED_NOISENODE_BODY_DIM_IMPL(Dimension, Derivative, Fractal, FunctionName, Parent) \ + virtual uint32 GetDimension() const override final { return Dimension; } + +#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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h new file mode 100644 index 0000000..bff312b --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelOptimizationNodes.h @@ -0,0 +1,221 @@ +// 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; + //~ 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 + //~ 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 + //~ 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 + //~ End UVoxelNode Interface +}; \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelParameterNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelParameterNodes.h new file mode 100644 index 0000000..60d760a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelPlaceableItemsNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelPlaceableItemsNodes.h new file mode 100644 index 0000000..32074bf --- /dev/null +++ b/Plugins/VoxelFree/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)) + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelRandomNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelRandomNodes.h new file mode 100644 index 0000000..6b3de4f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h new file mode 100644 index 0000000..ef27a30 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelSDFNodes.h @@ -0,0 +1,343 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNodeHelper.h" +#include "VoxelNodeHelperMacros.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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelSeedNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelSeedNodes.h new file mode 100644 index 0000000..0db0bda --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelTextureSamplerNode.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelTextureSamplerNode.h new file mode 100644 index 0000000..95edbb5 --- /dev/null +++ b/Plugins/VoxelFree/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") + TObjectPtr 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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelVoronoiNoiseNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelVoronoiNoiseNodes.h new file mode 100644 index 0000000..62c4d4a --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelWhiteNoiseNodes.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelNodes/VoxelWhiteNoiseNodes.h new file mode 100644 index 0000000..af55b83 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelPinCategory.h b/Plugins/VoxelFree/Source/VoxelGraph/Public/VoxelPinCategory.h new file mode 100644 index 0000000..809f79c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraph/VoxelGraph.Build.cs b/Plugins/VoxelFree/Source/VoxelGraph/VoxelGraph.Build.cs new file mode 100644 index 0000000..b28b730 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/DummyObject.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/DummyObject.h new file mode 100644 index 0000000..82c11e3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.cpp new file mode 100644 index 0000000..70df602 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreview.h new file mode 100644 index 0000000..b0b8033 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp new file mode 100644 index 0000000..d8e26cd --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.cpp @@ -0,0 +1,187 @@ +// 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" +#include "UnrealWidget.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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/SVoxelGraphPreviewViewport.h new file mode 100644 index 0000000..b741208 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp new file mode 100644 index 0000000..d331d50 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.cpp @@ -0,0 +1,211 @@ +// 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 "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) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +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 +{ +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/Preview/VoxelGraphPreview.h new file mode 100644 index 0000000..75170ea --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/SVoxelPalette.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/SVoxelPalette.cpp new file mode 100644 index 0000000..e26c701 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/SVoxelPalette.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/SVoxelPalette.h new file mode 100644 index 0000000..ee49011 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h new file mode 100644 index 0000000..c249d72 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelDebugGraphUtils.h @@ -0,0 +1,18 @@ +// Copyright 2020 Phyronnaz + +#pragma once + +#include "CoreMinimal.h" +#include "VoxelNode.h" +#include "VoxelDebugGraphUtils.generated.h" + +class FVoxelGraphCompiler; +class UVoxelGraphGenerator; + +UCLASS(NotPlaceable) +class UVoxelDebugNode : public UVoxelNode +{ + GENERATED_BODY() + +}; + diff --git a/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelEdGraph.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelEdGraph.cpp new file mode 100644 index 0000000..5d601c1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelEdGraph.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelEdGraph.h new file mode 100644 index 0000000..110be45 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.cpp new file mode 100644 index 0000000..82960b0 --- /dev/null +++ b/Plugins/VoxelFree/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 = FAppStyle::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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphConnectionDrawingPolicy.h new file mode 100644 index 0000000..d110067 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditor.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditor.cpp new file mode 100644 index 0000000..dbd5df7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditor.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditor.h new file mode 100644 index 0000000..cb51ad9 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorCommands.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorCommands.h new file mode 100644 index 0000000..56d0ff4 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp new file mode 100644 index 0000000..aba7539 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorModule.cpp @@ -0,0 +1,140 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditorModule.h" + +#include "VoxelGraphEditor.h" +#include "VoxelGraphGenerator.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([=]() + { + }))); + })); + + 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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp new file mode 100644 index 0000000..c4aa36e --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.cpp @@ -0,0 +1,1731 @@ +// Copyright 2020 Phyronnaz + +#include "VoxelGraphEditorToolkit.h" +#include "VoxelGraphEditorUtilities.h" +#include "VoxelGraphGenerator.h" +#include "VoxelGraphShortcuts.h" +#include "VoxelGraphPreviewSettings.h" +#include "VoxelGraphSchema.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) +{ + FVoxelMessages::Info("You can view and edit Voxel Graphs, but running them requires Voxel Plugin Pro"); + + 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) +{ +} + +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) + { + } + + // 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 (UVoxelNode* 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() +{ + FVoxelMessages::Info("Compiling graphs to C++ requires Voxel Plugin Pro"); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FVoxelGraphEditorToolkit::ToggleAutomaticPreview() +{ + Generator->Modify(); + Generator->bAutomaticPreview = !Generator->bAutomaticPreview; +} + +bool FVoxelGraphEditorToolkit::IsToggleAutomaticPreviewChecked() const +{ + return Generator->bAutomaticPreview; +} + +void FVoxelGraphEditorToolkit::UpdatePreview(EVoxelGraphPreviewFlags Flags) +{ + PreviewHandler->Update(Flags); + + if (EnumHasAnyFlags(Flags, EVoxelGraphPreviewFlags::ManualPreview)) + { + FVoxelMessages::Info("You can view and edit Voxel Graphs, but running and previewing them requires Voxel Plugin Pro"); + } +} + +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); + +} + +/////////////////////////////////////////////////////////////////////////////// + +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 (ensure(GEditor->GetEditorSubsystem()->OpenEditorForAsset(Node->Graph))) + { + auto NewEditor = FVoxelGraphEditorUtilities::GetIVoxelEditorForGraph(Node->Graph->VoxelGraph); + if (ensure(NewEditor.IsValid())) + { + NewEditor->SelectNodesAndZoomToFit({ Node->GraphNode }); + } + } + } + } +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h new file mode 100644 index 0000000..23014ee --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorToolkit.h @@ -0,0 +1,262 @@ +// 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; + virtual FString GetReferencerName() const override { return "FVoxelGraphEditorToolkit"; } + //~ 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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.cpp new file mode 100644 index 0000000..0123f91 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphEditorUtilities.h new file mode 100644 index 0000000..61eac36 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SGraphNodeDefaultVoxel.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SGraphNodeDefaultVoxel.h new file mode 100644 index 0000000..b508072 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.cpp new file mode 100644 index 0000000..629067d --- /dev/null +++ b/Plugins/VoxelFree/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(FAppStyle::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( FAppStyle::GetBrush("Graph.Node.TitleGloss") ) + .ColorAndOpacity( this, &SGraphNode::GetNodeTitleIconColor ) + ] + +SOverlay::Slot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + [ + SNew(SBorder) + .BorderImage( FAppStyle::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( FAppStyle::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( FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::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(FAppStyle::GetBrush("Graph.VarNode.Body")) + .ColorAndOpacity(this, &SVoxelGraphNode::GetNodeBodyColor) + ] + + SOverlay::Slot() + [ + SNew(SImage) + .Image(FAppStyle::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 = FAppStyle::GetColor("InfoReporting.BackgroundColor"); + WarningColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor"); + ErrorColor = FAppStyle::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( FAppStyle::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( FAppStyle::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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/SVoxelGraphNode.h new file mode 100644 index 0000000..e7a7776 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp new file mode 100644 index 0000000..6533da0 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.cpp @@ -0,0 +1,716 @@ +// 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 "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->MarkAsGarbage(); + 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); + } +} + +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)) + { + GEditor->GetEditorSubsystem()->OpenEditorForAsset(Macro->Macro); + } + 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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h new file mode 100644 index 0000000..e7a0504 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode.h @@ -0,0 +1,81 @@ +// 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() + TObjectPtr 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; + 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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNodeFactory.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNodeFactory.h new file mode 100644 index 0000000..43c5c96 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.cpp new file mode 100644 index 0000000..fb46d39 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Base.h new file mode 100644 index 0000000..0e8ca79 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.cpp new file mode 100644 index 0000000..71bc17c --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Knot.h new file mode 100644 index 0000000..1b2c9fa --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Root.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphNodes/VoxelGraphNode_Root.h new file mode 100644 index 0000000..f8bf4f7 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphPanelPinFactory.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphPanelPinFactory.h new file mode 100644 index 0000000..d393fef --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp new file mode 100644 index 0000000..a34be27 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.cpp @@ -0,0 +1,1150 @@ +// 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" + +#include "ToolMenu.h" +#include "ToolMenuSection.h" + +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 (UVoxelNode* 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, + bool bMarkAsModified +) 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); +} + +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 (UVoxelNode* 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 (UVoxelNode* 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(); + })); + } + } +} + +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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h new file mode 100644 index 0000000..ec8d934 --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphSchema.h @@ -0,0 +1,160 @@ +// 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() + TObjectPtr 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() + TObjectPtr 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() + TObjectPtr 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, + bool bMarkAsModified + ) 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; + virtual void GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const override; + 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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.cpp b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.cpp new file mode 100644 index 0000000..cb9a51f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Private/VoxelGraphShortcuts.h new file mode 100644 index 0000000..65fe75f --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Public/IVoxelGraphEditorToolkit.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Public/IVoxelGraphEditorToolkit.h new file mode 100644 index 0000000..cad0827 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/Public/VoxelGraphEditorModule.h b/Plugins/VoxelFree/Source/VoxelGraphEditor/Public/VoxelGraphEditorModule.h new file mode 100644 index 0000000..68b99f3 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs b/Plugins/VoxelFree/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs new file mode 100644 index 0000000..8c1c35f --- /dev/null +++ b/Plugins/VoxelFree/Source/VoxelGraphEditor/VoxelGraphEditor.Build.cs @@ -0,0 +1,61 @@ +// 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", + "EditorFramework", +#if UE_4_24_OR_LATER + "ToolMenus" +#endif + }); + + PrivateIncludePathModuleNames.AddRange( + new string[] { + "VoxelEditor" + }); + } +} \ No newline at end of file diff --git a/Plugins/VoxelFree/Source/VoxelHelpers/Private/MaterialExpressionBlendMaterialAttributesBarycentric.cpp b/Plugins/VoxelFree/Source/VoxelHelpers/Private/MaterialExpressionBlendMaterialAttributesBarycentric.cpp new file mode 100644 index 0000000..3f7483e --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Private/MaterialExpressionPack.cpp b/Plugins/VoxelFree/Source/VoxelHelpers/Private/MaterialExpressionPack.cpp new file mode 100644 index 0000000..5d2b699 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelColorWheel.cpp b/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelColorWheel.cpp new file mode 100644 index 0000000..628d6f8 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelHelpersLibrary.cpp b/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelHelpersLibrary.cpp new file mode 100644 index 0000000..0c41690 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelHelpersModule.cpp b/Plugins/VoxelFree/Source/VoxelHelpers/Private/VoxelHelpersModule.cpp new file mode 100644 index 0000000..1baa40b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Public/MaterialExpressionBlendMaterialAttributesBarycentric.h b/Plugins/VoxelFree/Source/VoxelHelpers/Public/MaterialExpressionBlendMaterialAttributesBarycentric.h new file mode 100644 index 0000000..2610760 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Public/MaterialExpressionPack.h b/Plugins/VoxelFree/Source/VoxelHelpers/Public/MaterialExpressionPack.h new file mode 100644 index 0000000..7216a5b --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelColorWheel.h b/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelColorWheel.h new file mode 100644 index 0000000..63684e1 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelHelpersLibrary.h b/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelHelpersLibrary.h new file mode 100644 index 0000000..4bf4216 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelHelpersModule.h b/Plugins/VoxelFree/Source/VoxelHelpers/Public/VoxelHelpersModule.h new file mode 100644 index 0000000..12dd0b2 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/Source/VoxelHelpers/VoxelHelpers.Build.cs b/Plugins/VoxelFree/Source/VoxelHelpers/VoxelHelpers.Build.cs new file mode 100644 index 0000000..7dfe900 --- /dev/null +++ b/Plugins/VoxelFree/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/Plugins/VoxelFree/VoxelFree.uplugin b/Plugins/VoxelFree/VoxelFree.uplugin new file mode 100644 index 0000000..6a517f8 --- /dev/null +++ b/Plugins/VoxelFree/VoxelFree.uplugin @@ -0,0 +1,84 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "Free-UE5", + "FriendlyName": "Voxel Plugin Free", + "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/c61d1941c8664436be42d649404a10e0", + "SupportURL": "https://discord.voxelplugin.com", + "EngineVersion": "5.1.0", + "CanContainContent": true, + "Installed": true, + "Modules": [ + { + "Name": "Voxel", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelGraph", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelHelpers", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelEditor", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelGraphEditor", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelEditorDefault", + "Type": "Editor", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64" + ] + }, + { + "Name": "VoxelExamples", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64" + ] + } + ], + "Plugins": [ + { + "Name": "Niagara", + "Enabled": true + }, + { + "Name": "ProceduralMeshComponent", + "Enabled": true + } + ] +} \ No newline at end of file