/******************************************************************************/ /* Project - MudBun Publisher - Long Bunny Labs http://LongBunnyLabs.com Author - Ming-Lun "Allen" Chou http://AllenChou.net */ /******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; using UnityEngine.XR; namespace MudBun { [ExecuteInEditMode] public abstract class MudRendererBase : MonoBehaviour { #region Events public virtual void InvokeOnMeshGenerated(Mesh mesh) { } #endregion //------------------------------------------------------------------------- #region Enums & Structs public enum HardwareModeEnum { [InspectorName("GPU")] Gpu, // [InspectorName("CPU")] Cpu, } public enum RenderModeEnum { FlatMesh = 0, SmoothMesh = 1, CircleSplats = 2, QuadSplats = 3, Decal = 4, /* [HideInInspector] RayMarchedSurface = 5, [HideInInspector] RayTracedVoxels = 6, */ } public enum RenderMaterialModeEnum { Static, Dynamic, } public enum RayTracedVoxelModeEnum { FlatCubes, FacetedCubes, FlatSpheres, SmoothSpheres, Custom = 100, } public enum RayTracedVoxelPaddingModeEnum { None, ByDistance, Full, } public enum RenderModeCategoryEnum { Unknown = -1, Mesh, Splats, Decal, /* RayMarchedSurface, RayTracedVoxels, */ } public enum MeshingModeEnum { MarchingCubes, DualQuads, SurfaceNets, DualContouring, } public enum ComputeModeEnum { Auto, Manual, ManualNoRender, EveryFrame, TimeSliced } public enum TimeSliceModeEnum { ByFramesAutoOffset, ByFramesManualOffset, ByPeriodAutoOffset, ByPeriodManualOffset, } public struct Const { public struct KernelIndex { public int ClearVoxelHashTable; public int ClearAutoSmoothVertDataTable; public int ClearVoxelCache; public int RegisterTopNodes; public int UpdateBranchingIndirectDispatchArgs; public int AllocateChildNodes; public int UpdateVoxelIndirectDispatchArgs; public int GenerateFlatMarchingCubesMesh; public int GenerateSmoothMarchingCubesMesh; public int GenerateMarchingCubesSplats; public int GenerateFlatMarchingCubesMesh2d; public int GenerateSmoothMarchingCubesMesh2d; public int GenerateMarchingCubesSplats2d; public int UpdateMarchingCubesAutoSmoothIndirectDispatchArgs; public int MarchingCubesUpdateAutoSmooth; public int MarchingCubesComputeAutoSmooth; public int GenerateDualQuads; public int GenerateDualQuads2d; public int UpdateDualMeshingIndirectDispatchArgs; public int DualMeshingFlatMeshNormal; public int DualMeshingSmoothMeshNormal; public int DualMeshingFlatMeshNormal2d; public int DualMeshingSmoothMeshNormal2d; public int DualMeshingUpdateAutoSmooth; public int DualMeshingComputeAutoSmooth; public int DualMeshingUpdateSmoothCornerIndirectDispatchArgs; public int DualMeshingSmoothCorner; public int UpdateDualMeshingSplatsIndirectArgs; public int ConvertDualMeshingSplats; public int SurfaceNetsMovePoint; public int SurfaceNetsMovePoint2d; public int DualContouringMovePoint; public int DualContouringMovePoint2d; public int UpdateRayTracedVoxelIndirectDispatchArgs; public int ComputeRayTracedVoxelGenPoints; public int ComputeRayTracedVoxelGenPointsWithNormals; public int GenerateNoiseCache; public int RigBones; public int GenerateSdf; } public static KernelIndex Kernel; public static int TriTable; public static int VertTable; public static int TriTable2d; public static int Brushes; public static int BrushMaterials; public static int NumBrushes; public static int SurfaceShift; public static int RenderMode; public static int MeshingMode; public static int RayTracedVoxelMode; public static int RayTracedVoxelSizeMultiplier; public static int RayTracedVoxelSmoothCubeNormal; public static int RayTracedVoxelRadius; public static int RayTracedVoxelPaddingMode; public static int RayTracedVoxelInternalPaddingDistance; public static int RayTracedVoxelSizeFadeDistance; public static int NormalDifferentiationStep; public static int NormalQuantization; public static int Normal2dFadeDist; public static int Normal2dStrength; public static int EnableAutoSmooth; public static int AutoSmoothMaxAngle; public static int AutoSmoothVertDataTable; public static int AutoSmoothVertDataPoolSize; public static int EnableSmoothCorner; public static int SmoothCornerSubdivision; public static int SmoothCornerNormalBlur; public static int SmoothCornerFade; public static int InvertNormals; public static int SplatSize; public static int SplatSizeJitter; public static int SplatNormalShift; public static int SplatNormalShiftJitter; public static int SplatColorJitter; public static int SplatPositionJitter; public static int SplatRotationJitter; public static int SplatOrientationJitter; public static int SplatOriginalNormalBlend; public static int SplatJitterNoisiness; public static int SplatCameraFacing; public static int SplatNormalsMatchCameraFacing; public static int SplatShadowsMatchCameraFacing; public static int SplatScreenSpaceFlattening; //public static int SplatSmoothNormalBlend; public static int SurfaceNetsDualQuadsBlend; public static int SurfaceNetsBinarySearchIterations; public static int SurfaceNetsGradientDescentIterations; public static int SurfaceNetsGradientDescentFactor; public static int DualContouringDualQuadsBlend; public static int DualContouringRelaxation; public static int DualContouringSolverIterations; public static int DualContouringBinarySearchIterations; public static int DualContouringGradientDescentIterations; public static int DualContouringGradientDescentFactor; public static int AabbTree; public static int AabbRoot; public static int Enable2dMode; public static int ForceAllBrushes; public static int NumAllocations; // general allocation counters public static int NodeHashTable; public static int NodeHashTableSize; public static int NodePool; public static int NodePoolSize; public static int NumNodesAllocated; public static int UseVoxelCache; public static int VoxelCacheIdTable; public static int VoxelCache; public static int VoxelCacheSize; public static int BrushMaskPool; public static int BrushMaskPoolSize; public static int IndirectDispatchArgs; public static int CurrentNodeDepth; public static int CurrentNodeBranchingFactor; public static int CurrentNodeSize; public static int VoxelSize; public static int VoxelTreeBranchingFactorsCompressed; public static int VoxelNodeSizes; public static int MaxNodeDepth; public static int ChunkVoxelDensity; public static int GenPoints; public static int MaxGenPoints; public static int IndirectDrawArgs; public static int MasterColor; public static int MasterEmission; public static int MasterMetallic; public static int MasterSmoothness; public static int ScaleSign; public static int LocalToWorld; public static int LocalToWorldIt; public static int LocalToWorldScale; public static int WorldToLocal; public static int WorldToLocalIt; public static int NoiseCache; public static int NoiseCacheDimension; public static int NoiseCacheDensity; public static int NoiseCachePeriod; public static int SdfOutput; public static int SdfOutputSize; public static int SdfCenter; public static int SdfDimension; public static int MaxRayMarchSteps; public static int RayMarchHitDistance; public static int RayMarchMaxRayDistance; public static int MeshGenerationAutoRiggingAlgorithm; /* public static int NumLightMarchSteps; public static int RayMarchStepSize; public static int RayMarchVolumeDensity; public static int RayMarchVolumeBorderFade; public static int RayMarchLightPositionType; public static int RayMarchLightDirection; public static int RayMarchAbsorption; public static int RayMarchDarknesThreshold; public static int RayMarchTransmittanceCurve; public static int RayMarchNoiseEdgeFade; public static int RayMarchNoiseThreshold; public static int RayMarchNoiseScrollSpeed; public static int RayMarchNoiseBaseOctaveSize; public static int RayMarchNoiseNumOctaves; public static int RayMarchNoiseOctaveOffsetFactor; */ public static int IsMeshRenderMaterial; public static int IsSplatRenderMaterial; public static int MaterialNeedsSdfProperties; public static int MaterialNeedsRayMarchingProperties; } #endregion // end: Enums & Structs //------------------------------------------------------------------------- #region Global Consts public static readonly int ThreadGroupExtent = 4; public static readonly int ThreadGroupSize = ThreadGroupExtent * ThreadGroupExtent * ThreadGroupExtent; public static readonly int ClearThreadGroupSize = 256; private static int[] s_aVoxelTreeBranchingFactor = new int[] { 8, 8, 4 }; public static int[] VoxelTreeBranchingFactors => s_aVoxelTreeBranchingFactor; public static int VoxelTreeBranchingFactorsComrpessed = (int) Codec.Pack8888((uint)s_aVoxelTreeBranchingFactor[0], (uint)s_aVoxelTreeBranchingFactor[1], (uint)s_aVoxelTreeBranchingFactor[2], 0); public static int VoxelNodeDepth => VoxelTreeBranchingFactors.Length; private static int s_chunkVoxelDensity = -1; public static int ChunkVoxelDensity { get { if (s_chunkVoxelDensity < 0) s_chunkVoxelDensity = VoxelTreeBranchingFactors.Aggregate((x, y) => x * y); return s_chunkVoxelDensity; } } // maximum allowed number of brushes per renderer // NOTE: MaxBrushes must be a multiple of 32 (8 * sizeof(int)) // (MaxBrushes / 32) must be less than or equal to kMaxBrushMaskInts in BrushMaskDefs.cginc // MaxBrushes must *NOT* be larger than 2^kAabbTreeNodeStackSize in AabbTreeDefs.cginc public static readonly int MaxBrushes = 1024; public static int MaxBrushMaskInts => MaxBrushes / 32; public static int MaxBrushGroupDepth = 6; private static int[] s_noiseCacheDimensionInts = new int[] { 256, 128, 256 }; public static int[] NoiseCacheDimensionInts => s_noiseCacheDimensionInts; private static float[] s_noiseCacheDimensionFloats; public static float[] NoiseCacheDimensionFloats { get { if (s_noiseCacheDimensionFloats == null) s_noiseCacheDimensionFloats = NoiseCacheDimensionInts.Select(x => (float)x).ToArray(); return s_noiseCacheDimensionFloats; } } public static readonly float NoiseCacheDensity = 32.0f; private static float[] s_noiseCachePeriod; public static float[] NoiseCachePeriod { get { if (s_noiseCachePeriod == null) s_noiseCachePeriod = NoiseCacheDimensionInts.Select(x => x / NoiseCacheDensity).ToArray(); return s_noiseCachePeriod; } } public int VoxelToVertexFactor { get { switch (RenderMode) { case RenderModeEnum.FlatMesh: case RenderModeEnum.SmoothMesh: return 3; case RenderModeEnum.CircleSplats: return 2; case RenderModeEnum.QuadSplats: return 3; } return 3; } } #endregion // end: Global Consts //------------------------------------------------------------------------- #region Global Resources private static bool s_globalResourcesValid = false; protected static HashSet s_renderers = new HashSet(); protected static Dictionary s_brushMap = new Dictionary(); private static ComputeShader s_computeVoxelGen; private static ComputeShader s_computeMarchingCubes; private static ComputeShader s_computeDualMeshing; private static ComputeShader s_computeSurfaceNets; private static ComputeShader s_computeDualContouring; private static ComputeShader s_computeRayTracedVoxels; private static ComputeShader s_computeNoiseCache; private static ComputeShader s_computeMeshLock; private static ComputeShader s_computeSdfGen; private static ComputeBuffer s_triTableBuffer; private static ComputeBuffer s_vertTableBuffer; private static ComputeBuffer s_triTable2dBuffer; private static ComputeBuffer s_brushesBuffer; private static ComputeBuffer s_brushMaterialBuffer; private static ComputeBuffer s_aabbTreeBuffer; private static ComputeBuffer s_dummyBuffer; private static RenderTexture s_noiseCache; public static RenderTexture NoiseCache => s_noiseCache; /* private static Texture s_noiseCache; public static Texture NoiseCache { get { if (s_noiseCache != null) return s_noiseCache; s_noiseCache = ResourcesUtil.NoiseTexture; return s_noiseCache; } } */ public static ResourcesUtil.RenderPipelineEnum RenderPipeline => ResourcesUtil.RenderPipeline; public static int GlobalResourceGpuMemoryAllocated { get { int bytes = 0; if (s_triTableBuffer != null) bytes += s_triTableBuffer.stride * s_triTableBuffer.count; if (s_vertTableBuffer != null) bytes += s_vertTableBuffer.stride * s_vertTableBuffer.count; if (s_triTable2dBuffer != null) bytes += s_triTable2dBuffer.stride * s_triTable2dBuffer.count; if (s_brushesBuffer != null) bytes += s_brushesBuffer.stride * s_brushesBuffer.count; if (s_brushMaterialBuffer != null) bytes += s_brushMaterialBuffer.stride * s_brushMaterialBuffer.count; if (s_aabbTreeBuffer != null) bytes += s_aabbTreeBuffer.stride * s_aabbTreeBuffer.count; if (s_noiseCache != null) bytes += s_noiseCache.width * s_noiseCache.height * s_noiseCache.volumeDepth * sizeof(float); if (s_dummyBuffer != null) bytes += s_dummyBuffer.stride * s_dummyBuffer.count; return bytes; } } public static void ReloadAllShaders() { foreach (var renderer in s_renderers) { renderer.ReloadShaders(); } } #endregion // end: Global Resources //------------------------------------------------------------------------- #region Local Resources private bool m_localResourcesValid = false; protected NativeArray m_aSdfBrush; protected NativeArray m_aSdfBrushMaterial; protected Dictionary m_sdfBrushMaterialIndexMap; //[Header("Budgets")] // base budgets [Range(1, 2048)] public int MaxVoxelsK = 256; [Range(16, 1024)] public int MaxChunks = 64; private bool UseVoxelCache = false; // profiler says this doesn't really help with performance // derived budgets public int MaxVoxels => 1024 * MaxVoxelsK; public int MaxVoxelNodes => 1024 * (MaxChunks + MaxVoxelsK); public int MaxBrushMasks => 256 * MaxChunks; public int MaxGenPoints => VoxelToVertexFactor * MaxVoxelNodes; // only works in editor public bool ShowGpuMemoryUsage = false; public bool AutoAdjustBudgetsToHighWaterMarks = false; [Range(0, 100)] public int AutoAdjustBudgetsToHighWaterMarksMarginPercent = 15; private bool m_autoAdjustBudgetsToHighWaterMarks = false; //[Header("Render")] // local shader resources [Range(0.1f, 100.0f)] public float VoxelDensity = 8.0f; public float TopVoxelNodeSize { get { float size = ChunkVoxelDensity / VoxelDensity; if (LockMeshIntermediateState != LockMeshIntermediateStateEnum.Idle) size = ChunkVoxelDensity / MeshGenerationVoxelDensity; return size * 1.0001f; } } private float[] m_aNodeSize; public float[] NodeSizes { get { if (m_aNodeSize == null || m_aNodeSize.Length != VoxelNodeDepth + 1) m_aNodeSize = new float[VoxelNodeDepth + 1]; float nodeSize = TopVoxelNodeSize; for (int depth = 0; depth < m_aNodeSize.Length; ++depth) { m_aNodeSize[depth] = nodeSize; if (depth < VoxelNodeDepth) nodeSize /= VoxelTreeBranchingFactors[depth]; } return m_aNodeSize; } } public Vector4 NodeSizesVector { get { var aNodeSize = NodeSizes; int maxDepth = aNodeSize.Length; Vector4 vec = Vector4.zero; if (maxDepth > 0) vec.x = aNodeSize[0]; if (maxDepth > 1) vec.y = aNodeSize[1]; if (maxDepth > 2) vec.z = aNodeSize[2]; if (maxDepth > 3) vec.w = aNodeSize[3]; return vec; } } public float VoxelSize { get { switch (RenderModeCategory) { case RenderModeCategoryEnum.Decal: //case RenderModeCategoryEnum.RayMarchedSurface: return 1e-5f; } return NodeSizes[VoxelNodeDepth]; } } private bool AllowSharedRWBuffers { get { if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) // Metal is weird return false; switch (RenderModeCategory) { case RenderModeCategoryEnum.Decal: //case RenderModeCategoryEnum.RayMarchedSurface: //case RenderModeCategoryEnum.RayTracedVoxels: return false; case RenderModeCategoryEnum.Splats: /* if (SplatSmoothNormalBlend > 0.0f) return false; */ break; } return true; } } private bool m_initialized = false; private ComputeBuffer m_brushesBuffer; private ComputeBuffer m_brushMaterialBuffer; private ComputeBuffer m_aabbTreeBuffer; private int m_aabbTreeBufferRoot; private ComputeBuffer m_nodeHashTableBuffer; protected ComputeBuffer m_nodePoolBuffer; protected ComputeBuffer m_numNodesAllocatedBuffer; private ComputeBuffer m_numAllocationsBuffer; private ComputeBuffer m_voxelCacheIdTableBuffer; private ComputeBuffer m_voxelCacheBuffer; private ComputeBuffer m_brushMaskPoolBuffer; private ComputeBuffer m_indirectDispatchArgsBuffer; private ComputeBuffer m_autoSmoothVertDataTableBuffer; protected ComputeBuffer m_genPointsBufferDefault; protected ComputeBuffer m_indirectDrawArgsBufferDefault; protected ComputeBuffer m_genPointsBufferOverride; protected ComputeBuffer m_indirectDrawArgsBufferOverride; protected ComputeBuffer m_genPointsBufferUsedForCompute; protected ComputeBuffer m_indirectDrawArgsBufferUsedForCompute; private NativeArray m_indirectDrawArgsInitData; private NativeArray IndirectDrawArgsInitData { get { bool usingXr = false; var aXrDisplaySubsystem = new List(); SubsystemManager.GetInstances(aXrDisplaySubsystem); foreach (var xrDisplay in aXrDisplaySubsystem) { if (xrDisplay.running) { usingXr = true; break; } } int numInstances = usingXr ? 2 : 1; if (!m_indirectDrawArgsInitData.IsCreated) { m_indirectDrawArgsInitData = new NativeArray(new int[] { 0, numInstances, 0, 0, 0, }, Allocator.Persistent); } m_indirectDrawArgsInitData[1] = numInstances; return m_indirectDrawArgsInitData; } } private static NativeArray s_numAllocatoinsBufferInitData; private static NativeArray NumAllocatoinsBufferInitData { get { int desiredLength = VoxelTreeBranchingFactors.Length; if (s_numAllocatoinsBufferInitData.IsCreated) { if (s_numAllocatoinsBufferInitData.Length == desiredLength) return s_numAllocatoinsBufferInitData; else s_numAllocatoinsBufferInitData.Dispose(); } s_numAllocatoinsBufferInitData = new NativeArray(desiredLength, Allocator.Persistent, NativeArrayOptions.ClearMemory); return s_numAllocatoinsBufferInitData; } } private static NativeArray s_unitIndirectDispatchArgsInitData; private static NativeArray UnitIndirectDispatchArgsInitData { get { if (s_unitIndirectDispatchArgsInitData.IsCreated) return s_unitIndirectDispatchArgsInitData; s_unitIndirectDispatchArgsInitData = new NativeArray(new int[] { 1, 1, 1 }, Allocator.Persistent); return s_unitIndirectDispatchArgsInitData; } } private int[] m_numAllocationsBufferInitData; private static readonly int NodeHashTableAllocationMultiplier = 2; public int NodeHashTableSize { get { /* switch (RenderMode) { case RenderModeEnum.RayTracedVoxels: return MaxVoxels * NodeHashTableAllocationMultiplier; } */ return MaxChunks * NodeHashTableAllocationMultiplier; } } private static readonly int AutoSmoothVertDataAllocationMultiplier = 2; public int AutoSmoothVertDataTableSize => MaxVoxels * AutoSmoothVertDataAllocationMultiplier; public enum NumAllcationIndex { BrushMask, VoxelCache, VoxelHash, AutoSmoothVertData, } private static bool s_warnedResourceAccessPerformanceImpact = false; public long LocalResourceGpuMemoryAllocated { get { long bytes = 0; if (!AllowSharedRWBuffers) { if (m_brushesBuffer != null) bytes += m_brushesBuffer.stride * m_brushesBuffer.count; if (m_brushMaterialBuffer != null) bytes += m_brushMaterialBuffer.stride * m_brushMaterialBuffer.count; if (m_aabbTreeBuffer != null) bytes += m_aabbTreeBuffer.stride * m_aabbTreeBuffer.count; } if (m_nodePoolBuffer != null) bytes += m_nodePoolBuffer.stride * ((long) m_nodePoolBuffer.count); if (m_nodeHashTableBuffer != null) bytes += m_nodeHashTableBuffer.stride * m_nodeHashTableBuffer.count; if (m_numNodesAllocatedBuffer != null) bytes += m_numNodesAllocatedBuffer.stride * m_numNodesAllocatedBuffer.count; if (m_numAllocationsBuffer != null) bytes += m_numAllocationsBuffer.stride * m_numAllocationsBuffer.count; if (m_voxelCacheIdTableBuffer != null) bytes += m_voxelCacheIdTableBuffer.stride * m_voxelCacheIdTableBuffer.count; if (m_voxelCacheBuffer != null) bytes += m_voxelCacheBuffer.stride * m_voxelCacheBuffer.count; if (m_brushMaskPoolBuffer != null) bytes += m_brushMaskPoolBuffer.stride * m_brushMaskPoolBuffer.count; if (m_indirectDispatchArgsBuffer != null) bytes += m_indirectDispatchArgsBuffer.stride * m_indirectDispatchArgsBuffer.count; if (m_genPointsBufferDefault != null) bytes += m_genPointsBufferDefault.stride * ((long)m_genPointsBufferDefault.count); if (m_indirectDrawArgsBufferDefault != null) bytes += m_indirectDrawArgsBufferDefault.stride * m_indirectDrawArgsBufferDefault.count; if (m_autoSmoothVertDataTableBuffer != null) bytes += m_autoSmoothVertDataTableBuffer.stride * m_autoSmoothVertDataTableBuffer.count; if (!s_warnedResourceAccessPerformanceImpact && Application.isPlaying) { Debug.LogWarning("MudBun: Accessing resource usage impacts performance!"); s_warnedResourceAccessPerformanceImpact = true; } return bytes; } } public long LocalResourceGpuMemoryUsed { get { long bytes = 0; if (!AllowSharedRWBuffers) { if (m_brushesBuffer != null) bytes += m_brushesBuffer.stride * m_brushesBuffer.count; if (m_brushMaterialBuffer != null) bytes += m_brushMaterialBuffer.stride * m_brushMaterialBuffer.count; if (m_aabbTreeBuffer != null) bytes += m_aabbTreeBuffer.stride * m_aabbTreeBuffer.count; } if (m_numNodesAllocatedBuffer != null) { bytes += m_numNodesAllocatedBuffer.stride * ((long)m_numNodesAllocatedBuffer.count); var aNumNodesAllocated = new int[m_numNodesAllocatedBuffer.count]; m_numNodesAllocatedBuffer.GetData(aNumNodesAllocated); int numTotalNodes = aNumNodesAllocated[0]; bytes += numTotalNodes * m_nodePoolBuffer.stride; if (m_genPointsBufferDefault != null) bytes += 6 * numTotalNodes * m_genPointsBufferDefault.stride; } int [] aNumAllocated = null; if (m_numAllocationsBuffer != null) { aNumAllocated = new int[m_numAllocationsBuffer.count]; m_numAllocationsBuffer.GetData(aNumAllocated); } if (aNumAllocated != null) { if (m_nodeHashTableBuffer != null) { int numTotalHashes = aNumAllocated[(int) NumAllcationIndex.VoxelHash]; bytes += NodeHashTableAllocationMultiplier * numTotalHashes * m_nodeHashTableBuffer.stride; } if (m_autoSmoothVertDataTableBuffer != null) { int numSmoothVertData = aNumAllocated[(int) NumAllcationIndex.AutoSmoothVertData]; bytes += numSmoothVertData * m_autoSmoothVertDataTableBuffer.count; } } if (m_numAllocationsBuffer != null) bytes += m_numAllocationsBuffer.stride * m_numAllocationsBuffer.count; if (m_voxelCacheIdTableBuffer != null) bytes += m_voxelCacheIdTableBuffer.stride * m_voxelCacheIdTableBuffer.count; if (m_voxelCacheBuffer != null) bytes += m_voxelCacheBuffer.stride * m_voxelCacheBuffer.count; if (m_brushMaskPoolBuffer != null) bytes += m_brushMaskPoolBuffer.stride * m_brushMaskPoolBuffer.count; if (m_indirectDispatchArgsBuffer != null) bytes += m_indirectDispatchArgsBuffer.stride * m_indirectDispatchArgsBuffer.count; if (m_indirectDrawArgsBufferDefault != null) bytes += m_indirectDrawArgsBufferDefault.stride * m_indirectDrawArgsBufferDefault.count; if (!s_warnedResourceAccessPerformanceImpact && Application.isPlaying) { Debug.LogWarning("MudBun: Accessing resource usage impacts performance!"); s_warnedResourceAccessPerformanceImpact = true; } return bytes; } } public int NumVerticesAllocated => VoxelToVertexFactor * NumVoxelsAllocated; public int NumVerticesGenerated { get { if (m_indirectDispatchArgsBuffer == null) return 0; int[] aIndirectDrawArgs = new int[5]; m_indirectDrawArgsBufferDefault.GetData(aIndirectDrawArgs); if (!s_warnedResourceAccessPerformanceImpact && Application.isPlaying) { Debug.LogWarning("MudBun: Accessing resource usage impacts performance!"); s_warnedResourceAccessPerformanceImpact = true; } return aIndirectDrawArgs[0]; } } public int NumVoxelsAllocated => (m_nodePoolBuffer != null) ? MaxVoxels : 0; public int NumChunksAllocated => (m_nodeHashTableBuffer != null) ? MaxChunks : 0; public int NumVoxelsUsed { get { if (m_numNodesAllocatedBuffer == null) return 0; var aNumAllocated = new int[m_numNodesAllocatedBuffer.count]; m_numNodesAllocatedBuffer.GetData(aNumAllocated); if (!s_warnedResourceAccessPerformanceImpact && Application.isPlaying) { Debug.LogWarning("MudBun: Accessing resource usage impacts performance!"); s_warnedResourceAccessPerformanceImpact = true; } return aNumAllocated[0]; } } public int NumChunksUsed { get { if (m_numAllocationsBuffer == null) return 0; var aNumAllocated = new int[m_numAllocationsBuffer.count]; m_numAllocationsBuffer.GetData(aNumAllocated); int numTotalHashes = aNumAllocated[(int)NumAllcationIndex.VoxelHash]; if (!s_warnedResourceAccessPerformanceImpact && Application.isPlaying) { Debug.LogWarning("MudBun: Accessing resource usage impacts performance!"); s_warnedResourceAccessPerformanceImpact = true; } return numTotalHashes; } } public bool ForceEvaluateAllBrushes = false; private bool ShouldForceAllBrushes() { if (ForceEvaluateAllBrushes) return true; // trial version can't reference MudSolid // should probably manually force this with the manual option anyway /* if (Enable2dMode) { if (Normal2dFade > MathUtil.Epsilon) { foreach (var b in m_aBrush) { var sb = (MudSolid) b; if (sb.Operator == SdfBrush.OperatorEnum.Subtract) return true; } } } */ return false; } public bool Enable2dMode = false; [Range(-1.0f, 1.0f)] public float SurfaceShift = 0.0f; public HardwareModeEnum HardwareMode = HardwareModeEnum.Gpu; public RenderModeEnum RenderMode = RenderModeEnum.SmoothMesh; public RenderModeCategoryEnum RenderModeCategory { get { switch (RenderMode) { case RenderModeEnum.FlatMesh: case RenderModeEnum.SmoothMesh: return RenderModeCategoryEnum.Mesh; case RenderModeEnum.CircleSplats: case RenderModeEnum.QuadSplats: return RenderModeCategoryEnum.Splats; case RenderModeEnum.Decal: return RenderModeCategoryEnum.Decal; /* case RenderModeEnum.RayMarchedSurface: return RenderModeCategoryEnum.RayMarchedSurface; case RenderModeEnum.RayTracedVoxels: return RenderModeCategoryEnum.RayTracedVoxels; */ } return RenderModeCategoryEnum.Mesh; } } public MeshingModeEnum MeshingMode = MeshingModeEnum.MarchingCubes; public RayTracedVoxelModeEnum RayTracedVoxelMode = RayTracedVoxelModeEnum.FlatCubes; [Range(0.0f, 1.0f)] public float RayTracedVoxelSizeMultiplier = 1.0f; [Range(0.0f, 1.0f)] public float RayTracedVoxelSmoothCubeNormal = 0.0f; [Range(0.0f, 1.0f)] public float RayTracedVoxelSphereFullness = 0.0f; public RayTracedVoxelPaddingModeEnum RayTracedVoxelPaddingMode = RayTracedVoxelPaddingModeEnum.ByDistance; public float RayTracedVoxelInternalPaddingDistance = 0.0f; public float RayTracedVoxelSizeFadeDistance = 0.0f; private static RenderModeCategoryEnum GetMaterialRenderModeCategory(Material material) { if (material == null) return RenderModeCategoryEnum.Unknown; if (material.HasProperty(Const.IsMeshRenderMaterial)) return RenderModeCategoryEnum.Mesh; if (material.HasProperty(Const.IsSplatRenderMaterial)) return RenderModeCategoryEnum.Splats; return RenderModeCategoryEnum.Unknown; } private static bool MaterialNeedsSdfProperties(Material material) { if (material == null) return false; return material.HasProperty(Const.MaterialNeedsSdfProperties); } private static bool MaterialNeedsRayMarchingProperties(Material material) { if (material == null) return false; return material.HasProperty(Const.MaterialNeedsRayMarchingProperties); } private enum RenderGeometryTypeEnum { Unknown = -1, Mesh, BoxProxy, Chunks, } private RenderGeometryTypeEnum RenderGeometryType { get { switch (RenderModeCategory) { case RenderModeCategoryEnum.Mesh: case RenderModeCategoryEnum.Splats: return RenderGeometryTypeEnum.Mesh; case RenderModeCategoryEnum.Decal: //case RenderModeCategoryEnum.RayMarchedSurface: return RenderGeometryTypeEnum.BoxProxy; /* case RenderModeCategoryEnum.RayTracedVoxels: return RenderGeometryTypeEnum.Chunks; */ } return RenderGeometryTypeEnum.Unknown; } } private bool RenderMaterialNeedsSdfProperties { get { if (MaterialNeedsSdfProperties(m_materialUsed)) return true; return false; } } private RenderGeometryTypeEnum m_prevRenderGeometryType = RenderGeometryTypeEnum.Unknown; public bool ShowAdvancedNormalOptions = false; [Range(0.0f, 1.0f)] public float SmoothNormalBlurRelative = 0.05f; [Range(0.0f, 0.2f)] public float SmoothNormalBlurAbsolute = 0.0f; public float NormalDifferentialStep => Mathf.Max(0.01f * VoxelSize, SmoothNormalBlurRelative * VoxelSize + SmoothNormalBlurAbsolute); [Range(0.0f, 1.0f)] public float NormalQuantization = 0.0f; [Range(0.0f, 1.0f)] public float Normal2dFade = 0.0f; [Range(0.0f, 1.0f)] public float Normal2dStrength = 1.0f; public bool EnableAutoSmoothing = false; [Range(0.0f, 180.0f)] public float AutoSmoothingMaxAngle = 30.0f; public bool EnableSmoothCorner = false; [Range(1, 4)] public int SmoothCornerSubdivision = 2; [Range(0.001f, 0.1f)] public float SmoothCornerNormalBlur = 0.02f; [Range(0.0f, 1.0f)] public float SmoothCornerFade = 0.0f; public bool InvertNormals = false; private bool ShouldDoAutoSmoothing { get { if (!EnableAutoSmoothing) return false; if (Enable2dMode) return false; switch (MeshingMode) { case MeshingModeEnum.MarchingCubes: case MeshingModeEnum.SurfaceNets: case MeshingModeEnum.DualContouring: switch (RenderMode) { case RenderModeEnum.FlatMesh: case RenderModeEnum.SmoothMesh: return true; } break; } return false; } } public bool ShowAdvancedSplatOptions = false; [Range(0.0f, 5.0f)] public float SplatSize = 1.0f; [Range(0.0f, 1.0f)] public float SplatSizeJitter = 0.0f; [Range(-1.0f, 1.0f)] public float SplatNormalShift = 0.0f; [Range(0.0f, 1.0f)] public float SplatNormalShiftJitter = 1.0f; [Range(0.0f, 1.0f)] public float SplatColorJitter = 0.0f; [Range(0.0f, 1.0f)] public float SplatPositionJitter = 0.0f; [Range(0.0f, 1.0f)] public float SplatRotationJitter = 0.0f; [Range(0.0f, 1.0f)] public float SplatOrientationJitter = 0.0f; [Range(0.0f, 1.0f)] public float SplatOriginalNormalBlend = 1.0f; [Range(0.01f, 1.0f)] public float SplatJitterNoisiness = 1.0f; [Range(0.0f, 1.0f)] public float SplatCameraFacing = 0.0f; public bool SplatNormalsMatchCameraFacing = false; public bool SplatShadowsMatchCameraFacing = false; [Range(0.0f, 1.0f)] public float SplatScreenSpaceFlattening = 1.0f; //[Range(0.0f, 1.0f)] public float SplatSmoothNormalBlend = 0.0f; [Range(0.0f, 1.0f)] public float SurfaceNetsDualQuadsBlend = 0.0f; public bool ShowAdvancedGeometryOptions = false; //[Range(0, 10)] public int SurfaceNetsBinarySearchIterations = 0; //[Range(0, 5)] public int SurfaceNetsGradientDescentIterations = 0; //[Range(0.0f, 1.0f)] public float SurfaceNetsGradientDescentFactor = 1.0f; public bool SurfaceNetsHighAccuracyMode = false; [Range(0.0f, 1.0f)] public float DualContouringDualQuadsBlend = 0.0f; [Range(0.0f, 1.0f)] public float DualContouringRelaxation = 0.0f; [Range(0, 20)] public int DualContouringSolverIterations = 5; //[Range(0, 10)] public int DualContouringBinarySearchIterations = 0; //[Range(0, 5)] public int DualContouringGradientDescentIterations = 0; //[Range(0.0f, 1.0f)] public float DualContouringGradientDescentFactor = 1.0f; public bool DualContouringHighAccuracyMode = false; public ShadowCastingMode CastShadows = ShadowCastingMode.On; public bool ReceiveShadows = true; public IList Brushes => m_aBrush; private List m_aBrush = new List(); private List m_aBrushToProcess = new List(); private bool m_needRescanBrushes = false; private static readonly float AabbTreeFatBoundsRadius = 0.25f; internal AabbTree m_aabbTree; private void ValidateAabbTree() { if (m_aabbTree != null) return; m_aabbTree = new AabbTree(AabbTreeFatBoundsRadius); } //[Header("Material")] private MudSharedMaterialBase m_usedSharedMaterial; public MudSharedMaterialBase SharedMaterial; [SerializeField] private Color m_masterColor = Color.white; [SerializeField] private Color m_masterEmission = Color.white; [SerializeField] [Range(0.0f, 1.0f)] private float m_masterMetallic = 1.0f; [SerializeField] [Range(0.0f, 1.0f)] private float m_masterSmoothness = 1.0f; public Color MasterColor { get => m_usedSharedMaterial ? m_usedSharedMaterial.Color : m_masterColor; set { m_masterColor = value; } } public Color MasterEmission { get => m_usedSharedMaterial ? m_usedSharedMaterial.Emission : m_masterEmission; set { m_masterEmission = value; } } public float MasterMetallic { get => m_usedSharedMaterial ? m_usedSharedMaterial.Metallic : m_masterMetallic; set { m_masterMetallic = value; } } public float MasterSmoothness { get => m_usedSharedMaterial ? m_usedSharedMaterial.Smoothness : m_masterSmoothness; set { m_masterSmoothness = value; } } public Material RenderMaterialMesh; public Material RenderMaterialSplats; public Material RenderMaterialDecal; //public Material RenderMaterialRayMarchedSurface; //public Material RenderMaterialRayTracedVoxels; public Material RenderMaterial { get { switch (RenderModeCategory) { case RenderModeCategoryEnum.Mesh: return RenderMaterialMesh; case RenderModeCategoryEnum.Splats: return RenderMaterialSplats; case RenderModeCategoryEnum.Decal: return RenderMaterialDecal; /* case RenderModeCategoryEnum.RayMarchedSurface: return RenderMaterialRayMarchedSurface; case RenderModeCategoryEnum.RayTracedVoxels: return RenderMaterialRayTracedVoxels; */ } return RenderMaterialMesh; } } private Material m_materialCloned; private Material m_materialUsed; private MaterialPropertyBlock m_materialProps; public MaterialPropertyBlock RenderMaterialPropertyBlock { get { if (m_materialProps != null) return m_materialProps; m_materialProps = new MaterialPropertyBlock(); return m_materialProps; } } [Range(8, 256)] public int MaxRayMarchSteps = 64; [Range(0.0f, 1.0f)] public float RayMarchAccuracy = 0.5f; [Range(1.0f, 2e3f)] public float RayMarchMaxRayDistance = 1e3f; [Range(0.1f, 10.0f)] public float RayMarchStepSize = 0.5f; [Range(1, 16)] public int NumLightMarchSteps = 8; //[Range(0.1f, 100.0f)] public float RayMarchDistance = 20.0f; [Range(0.0f, 20.0f)] public float RayMarchVolumeDensity = 5.0f; public Light RayMarchLight; [Range(0.0f, 10.0f)] public float RayMarchVolumeAbsorption = 1.0f; [Range(0.0f, 10.0f)] public float RayMarchLightAbsorption = 1.0f; [Range(0.0f, 1.0f)] public float RayMarchDarknesThreshold = 0.1f; [Range(0.0f, 10.0f)] public float RayMarchTransmittanceCurve = 0.0f; public bool UseRayMarchNoise = false; [Range(0.0f, 1.0f)] public float RayMarchNoiseThreshold = 0.2f; [Range(0.0f, 10.0f)] public float RayMarchNoiseEdgeFade = 2.0f; public Vector3 RayMarchNoiseScrollSpeed = Vector3.zero; public Vector3 RayMarchNoiseBaseOctaveSize = Vector3.one; [Range(1, 3)] public int RayMarchNoiseNumOctaves = 2; public float RayMarchNoiseOctaveOffsetFactor = 0.5f; private Sdf.EvalJob[] m_aEvalJob; //[Header("Editor")] public enum ClickSelectionEnum { None, Gizmos, Raycast, [InspectorName("Raycast (Forced Zero-Blend Union)")] RaycastForcedZeroBlendUnion, } public ClickSelectionEnum ClickSelection = ClickSelectionEnum.Raycast; //[Header("Debug")] public bool AlwaysDrawGizmos = false; public bool DrawRawBrushBounds = false; public bool DrawComputeBrushBounds = false; public bool DrawRenderBounds = false; public bool DrawVoxelNodes = false; [Range(-1, 3)] public int DrawVoxelNodesDepth = -1; [Range(0.0f, 1.0f)] public float DrawVoxelNodesScale = 1.0f; public Aabb RenderBounds { get { var bounds = RenderBoundsRs; if (!bounds.IsEmpty) // don't expand if empty, or it might crash the GPU! { bounds.Expand(SurfaceShift); bounds.Transform(transform); } return bounds; } } public Aabb RenderBoundsRs => m_aabbTree.Bounds; [SerializeField] [HideInInspector] private string m_firstTrackedVersion = ""; [SerializeField] [HideInInspector] private string m_previousTrackedVersion = ""; [SerializeField] [HideInInspector] private string m_currentTrackedVersion = ""; public string FirstTrackedVersion => m_firstTrackedVersion; public string PreviousTrackedVersion => m_previousTrackedVersion; public string CurrentTrackedVersion => m_currentTrackedVersion; #endregion // end: Local Resources //------------------------------------------------------------------------- #region Mesh Utilities public static readonly float MaxMeshGenerationVoxelDensityFreeVersion = 8.0f; public static readonly int MaxMeshGenerationTrianglesFreeVersion = 4096; public float MeshGenerationVoxelDensity { get { #if MUDBUN_FREE return Mathf.Min(MaxMeshGenerationVoxelDensityFreeVersion, VoxelDensity); #else return VoxelDensity; #endif } } public enum RenderableMeshMode { None, Procedural, MeshRenderer, } public enum AutoRiggingAlgorithm { New, // not ready yet Default, // will be Legacy once New is ready } public bool MeshGenerationCreateNewObject = false; public bool MeshGenerationCreateCollider = false; public bool MeshGenerationForceConvexCollider = false; [Range(-1.0f, 1.0f)] public float MeshGenerationColliderSurfaceShift = 0.0f; public bool MeshGenerationCreateRigidBody = false; public bool GenerateColliderMeshAssetByEditor = true; // editor only public string GenerateColliderMeshAssetByEditorName = ""; // editor only #if MUDBUN_FREE [Range(0.0f, 8.0f)] public float MeshGenerationColliderVoxelDensity = 4.0f; #else [Range(0.0f, 128.0f)] public float MeshGenerationColliderVoxelDensity = 4.0f; #endif public RenderableMeshMode MeshGenerationRenderableMeshMode = RenderableMeshMode.Procedural; public bool MeshGenerationAutoRigging = false; public AutoRiggingAlgorithm MeshGenerationAutoRiggingAlgorithm = AutoRiggingAlgorithm.Default; [Range(1, 4)] public int MeshGenerationAutoRiggingMaxBonesPerVertex = 4; public bool MeshGenerationGenerateTextureUV = false; public bool MeshGenerationGenerateLightMapUV = false; public bool MeshGenerationWeldVertices = false; public bool MeshGenerationLockOnStart = false; public bool GenerateMeshAssetByEditor = true; // editor only public string GenerateMeshAssetByEditorName = ""; // editor only public bool RecursiveLockMeshByEditor = true; // editor only public bool RememberLockedMeshMaterialByEditor = true; // editor only [SerializeField] [HideInInspector] public bool MeshGenerationLockOnStartByEditor = false; // editor only [SerializeField] protected Material m_lastLockedMeshMaterial; [Serializable] private class TransformCache { public Transform Transform; public Transform Parent; public Vector3 GlobalPosition; public Quaternion GlobalRotation; public Vector3 LocalPosition; public Quaternion LocalRotation; public Vector3 LocalScale; public bool HasBrushDescendants; } // order: parent->child [SerializeField] [HideInInspector] private List m_aBrushTransformCache; [SerializeField] [HideInInspector] private List m_aNestedRendereTransformCache; private bool HashNonMudBunObjectInHierarchy(Transform t) { if (t == null) return false; if (t.TryGetComponent(out Collider _) || t.TryGetComponent(out Collider2D _)) return true; if (t.TryGetComponent(out MeshRenderer _) || t.TryGetComponent(out SkinnedMeshRenderer _)) { if (!t.TryGetComponent(out MudRendererBase r) || !r.MeshLocked) { if (r != null) return true; } } for (int i = 0; i < t.childCount; ++i) { if (HashNonMudBunObjectInHierarchy(t.GetChild(i))) return true; } return false; } private bool HasBrushInHierarchy(Transform t) { if (t == null) return false; if (t.TryGetComponent(out MudBrushBase _)) return true; for (int i = 0; i < t.childCount; ++i) { if (HasBrushInHierarchy(t.GetChild(i))) return true; } return false; } private void DetectMixedHierarchy(Transform t) { if (!HasBrushInHierarchy(t) || !HashNonMudBunObjectInHierarchy(t)) return; Debug.LogWarning ( "WARNING: Mixed MudBun objects and non-MudBun objects in renderer hierarchy detected during auto-rigging. " + "This can cause issues when normalizing bones to a unit scale of (1, 1, 1).\n" + "Please follow this guideline: " + "Objects with mixed MudBun objects and non-MudBun objects in their hierarchy should have a unit scale of (1, 1, 1)." ); } private void CacheBoneTransforms() { m_aBrushTransformCache = new List(); m_aNestedRendereTransformCache = new List(); CacheBoneTransformsRecursive(transform); DetectMixedHierarchy(transform); } private void NormalizeBoneTransforms() { if (m_aBrushTransformCache == null) return; // move nested renderers out of the way foreach (var cache in m_aNestedRendereTransformCache) { cache.Transform.SetParent(null, true); } // set each bone's local scale to unit scale to avoid shearing in child bones foreach (var cache in m_aBrushTransformCache) { if (cache.HasBrushDescendants) { cache.Transform.localScale = Vector3.one; cache.Transform.position = cache.GlobalPosition; cache.Transform.rotation = cache.GlobalRotation; } else { cache.Transform.position = cache.GlobalPosition; cache.Transform.rotation = cache.GlobalRotation; } } // put nested renderers back in hierarchy foreach (var cache in m_aNestedRendereTransformCache) { cache.Transform.SetParent(cache.Parent, true); cache.Transform.position = cache.GlobalPosition; cache.Transform.rotation = cache.GlobalRotation; } } private void CacheBoneTransformsRecursive(Transform t) { if (t == null) return; var cache = new TransformCache() { Transform = t, Parent = t.parent, GlobalPosition = t.position, GlobalRotation = t.rotation, LocalPosition = t.localPosition, LocalRotation = t.localRotation, LocalScale = t.localScale, HasBrushDescendants = HasBrushInHierarchy(t), }; if (t != transform) { if (t.GetComponent() == null) { m_aBrushTransformCache.Add(cache); } else { // renderer blocks recursion m_aNestedRendereTransformCache.Add(cache); return; } } for (int i = 0; i < t.childCount; ++i) { var child = t.GetChild(i); CacheBoneTransformsRecursive(t.GetChild(i)); } } private void RestoreBoneTransforms() { if (m_aBrushTransformCache == null) return; // move nested renderers out of the way foreach (var cache in m_aNestedRendereTransformCache) { try { cache.Transform.SetParent(null, true); } catch (Exception) { } } // restore brush transforms foreach (var cache in m_aBrushTransformCache) { try { cache.Transform.localScale = cache.LocalScale; cache.Transform.localPosition = cache.LocalPosition; cache.Transform.localRotation = cache.LocalRotation; } catch (Exception) { } } // restore nested renderer transforms foreach (var cache in m_aNestedRendereTransformCache) { try { cache.Transform.SetParent(cache.Parent, true); cache.Transform.localScale = cache.LocalScale; cache.Transform.localPosition = cache.LocalPosition; cache.Transform.localRotation = cache.LocalRotation; } catch (Exception) { } } m_aBrushTransformCache = null; m_aNestedRendereTransformCache = null; } public enum GeneratedMeshType { Standard, Compute, Collider, } private class PendingMeshData { public Mesh Mesh; public GeneratedMeshType MeshType; public Transform RootBone; public List Bones; public bool DoRigging; public bool GenerateTextureUV; public bool GenerateLightMapUV; public bool WeldVertices; public bool OptimizeMeshForRendering; public bool Async; public ComputeBuffer IndirectDrawArgsBuffer; public ComputeBuffer GenPointsBuffer; public void Dispose() { if (IndirectDrawArgsBuffer != null) { //IndirectDrawArgsBuffer.Release(); Janitor.Dispose(IndirectDrawArgsBuffer); IndirectDrawArgsBuffer = null; } if (GenPointsBuffer != null) { //GenPointsBuffer.Release(); Janitor.Dispose(GenPointsBuffer); GenPointsBuffer = null; } } } private Dictionary m_pendingMeshTable = new Dictionary(); public bool IsAnyMeshGenerationPending => m_pendingMeshTable != null && m_pendingMeshTable.Count > 0; public bool IsMeshGenerationPending(Mesh mesh) { foreach (var pair in m_pendingMeshTable) if (pair.Value.Mesh == mesh) return true; return false; } public void WaitForMeshGeneration(Mesh mesh) { foreach (var pair in m_pendingMeshTable) { if (pair.Value.Mesh != mesh) continue; pair.Key.WaitForCompletion(); break; } } public Mesh GenerateMesh ( GeneratedMeshType meshType, bool async, Mesh mesh = null, bool generateTextureUV = false, bool generateLightMapUV = false, bool weldVertices = false, bool optimizeMeshForRendering = false ) { Transform [] aBone; return GenerateMesh(meshType, null, out aBone, async, mesh, generateTextureUV, generateLightMapUV, weldVertices, optimizeMeshForRendering); } public Mesh GenerateMesh ( GeneratedMeshType meshType, Transform rootBone, out Transform [] aBone, bool async, Mesh mesh = null, bool genreateTextureUV = false, bool generateLightMapUV = false, bool weldVertices = false, bool optimizeMeshForRendering =false ) { aBone = null; if (meshType == GeneratedMeshType.Compute) return null; UpdateComputeData(); ValidateLocalResources(); var prevRenderMode = RenderMode; var prevMeshingMode = MeshingMode; float prevVoxelDensity = VoxelDensity; int prevMaxVoxelsK = MaxVoxelsK; int prevMaxChunks = MaxChunks; float prevSurfaceShift = SurfaceShift; if (meshType == GeneratedMeshType.Collider) { RenderMode = RenderModeEnum.FlatMesh; VoxelDensity = MeshGenerationColliderVoxelDensity; SurfaceShift += MeshGenerationColliderSurfaceShift; } else { VoxelDensity = MeshGenerationVoxelDensity; } switch (RenderModeCategory) { case RenderModeCategoryEnum.Splats: RenderMode = RenderModeEnum.FlatMesh; break; /* case RenderModeCategoryEnum.RayMarchedSurface: RenderMode = RenderModeEnum.FlatMesh; MeshingMode = MeshingModeEnum.DualQuads; break; */ } // always override buffers to work in Metal m_indirectDrawArgsBufferOverride = new ComputeBuffer(5, sizeof(int), ComputeBufferType.IndirectArguments); m_genPointsBufferOverride = new ComputeBuffer(MaxGenPoints, GenPoint.Stride); var indirectDrawArgsBuffer = m_indirectDrawArgsBufferOverride; var genPointsBuffer = m_genPointsBufferOverride; indirectDrawArgsBuffer.SetData(IndirectDrawArgsInitData); ForceCompute(); RenderMode = prevRenderMode; MeshingMode = prevMeshingMode; VoxelDensity = prevVoxelDensity; MaxVoxelsK = prevMaxVoxelsK; MaxChunks = prevMaxChunks; SurfaceShift = prevSurfaceShift; MarkNeedsCompute(); if (indirectDrawArgsBuffer == null) return null; if (genPointsBuffer == null) return null; if (mesh == null) mesh = new Mesh(); var pendingMeshData = new PendingMeshData() { Mesh = mesh, MeshType = meshType, RootBone = rootBone, Bones = m_aBone, DoRigging = m_doRigging, GenerateTextureUV = genreateTextureUV, GenerateLightMapUV = generateLightMapUV, WeldVertices = weldVertices, OptimizeMeshForRendering = optimizeMeshForRendering, Async = async, IndirectDrawArgsBuffer = indirectDrawArgsBuffer, GenPointsBuffer = genPointsBuffer, }; if (m_aBone != null) { aBone = m_aBone.ToArray(); m_aBone = null; } var request = AsyncGPUReadback.Request(pendingMeshData.IndirectDrawArgsBuffer, OnIndirectDrawArgsBufferRead); m_pendingMeshTable.Add(request, pendingMeshData); if (!async) request.WaitForCompletion(); return mesh; } private void OnIndirectDrawArgsBufferRead(AsyncGPUReadbackRequest request) { PendingMeshData pendingMeshData; bool dataFound = m_pendingMeshTable.TryGetValue(request, out pendingMeshData); m_pendingMeshTable.Remove(request); var aDrawArgs = request.GetData(); if (!dataFound || aDrawArgs.Length <= 0 || request.hasError) { if (pendingMeshData != null) pendingMeshData.Dispose(); return; } int numVerts = aDrawArgs[0]; if (MudBun.IsFreeVersion) numVerts = Mathf.Min(numVerts, 3 * MaxMeshGenerationTrianglesFreeVersion); if (numVerts <= 0) { pendingMeshData.Dispose(); return; } var newRequest = AsyncGPUReadback.Request(pendingMeshData.GenPointsBuffer, numVerts * pendingMeshData.GenPointsBuffer.stride, 0, OnGenPointsBufferRead); m_pendingMeshTable.Add(newRequest, pendingMeshData); if (!pendingMeshData.Async) newRequest.WaitForCompletion(); } private void OnGenPointsBufferRead(AsyncGPUReadbackRequest request) { PendingMeshData pendingMeshData; bool dataFound = m_pendingMeshTable.TryGetValue(request, out pendingMeshData); m_pendingMeshTable.Remove(request); if (!dataFound || request.hasError) { if (pendingMeshData != null) pendingMeshData.Dispose(); return; } var aGenPoint = request.GetData(); BuildMesh ( pendingMeshData.Mesh, pendingMeshData.MeshType, aGenPoint, pendingMeshData.RootBone, pendingMeshData.Bones, pendingMeshData.DoRigging, pendingMeshData.GenerateTextureUV, pendingMeshData.GenerateLightMapUV, pendingMeshData.WeldVertices, pendingMeshData.OptimizeMeshForRendering ); if (pendingMeshData.Async && pendingMeshData.IndirectDrawArgsBuffer == m_indirectDrawArgsBufferDefault && pendingMeshData.GenPointsBuffer == m_genPointsBufferDefault && m_isMeshLocked) { DisposeLocalResources(); } InvokeOnMeshGenerated(pendingMeshData.Mesh); pendingMeshData.Dispose(); } private void BuildMesh ( Mesh mesh, GeneratedMeshType meshType, NativeArray aGenPoint, Transform rootBone, List bones, bool doRigging, bool generateTextureUV, bool generateLightMapUV, bool weldVertices, bool optimizeMeshForRendering ) { int numVerts = aGenPoint.Length; var aVertex = new NativeArray(numVerts, Allocator.Temp); var aNormal = new NativeArray(numVerts, Allocator.Temp); var aTangent = new NativeArray(numVerts, Allocator.Temp); var aVertIndex = new NativeArray(numVerts, Allocator.Temp); bool invertNormals = InvertNormals && !Enable2dMode; for (int i = 0; i < numVerts; ++i) { aVertex[i] = aGenPoint[i].PosNorm; aNormal[i] = Codec.UnpackNormal(aGenPoint[i].PosNorm.w); aTangent[i] = VectorUtil.FindOrthogonal(aNormal[i]); aVertIndex[i] = invertNormals ? (3 * (i / 3) + 2 - (i % 3) ) : i; } mesh.Clear(); mesh.SetVertices(aVertex); mesh.SetNormals(aNormal); mesh.SetTangents(aTangent); mesh.indexFormat = numVerts > 65535 ? IndexFormat.UInt32 : IndexFormat.UInt16; mesh.SetIndices(aVertIndex, MeshTopology.Triangles, 0); if (meshType == GeneratedMeshType.Standard) { Color [] aColor = new Color[numVerts]; Vector4 [] aEmissionHash = new Vector4[numVerts]; Vector2 [] aMetallicSmoothness = new Vector2[numVerts]; Vector4 [] aTextureWeight = new Vector4[numVerts]; BoneWeight [] aBoneWeight = null; if (doRigging) { CacheBoneTransforms(); aBoneWeight = new BoneWeight[numVerts]; } for (int i = 0; i < numVerts; ++i) { Color c = Codec.UnpackRgba(aGenPoint[i].Material.Color); Color et = Codec.UnpackRgba(aGenPoint[i].Material.EmissionTightness); /* if (QualitySettings.activeColorSpace == ColorSpace.Linear) { float gamma = 2.2f; aColor[i] = new Color ( Mathf.Pow(c.r * MasterColor.r, gamma), Mathf.Pow(c.g * MasterColor.g, gamma), Mathf.Pow(c.b * MasterColor.b, gamma), c.a * MasterColor.a ); aEmissionHash[i] = new Vector4 ( Mathf.Pow(et.r * MasterEmission.r, gamma), Mathf.Pow(et.g * MasterEmission.g, gamma), Mathf.Pow(et.b * MasterEmission.b, gamma), aGenPoint[i].Material.Hash ); } else */ { aColor[i] = c * MasterColor; aEmissionHash[i] = new Vector4 ( et.r * MasterEmission.r, et.g * MasterEmission.g, et.b * MasterEmission.b, aGenPoint[i].Material.Hash ); } Vector2 ms = Codec.UnpackSaturated(aGenPoint[i].Material.MetallicSmoothness); aMetallicSmoothness[i] = new Vector2(ms.x * MasterMetallic, ms.y * MasterSmoothness); aTextureWeight[i] = Codec.UnpackRgba(aGenPoint[i].Material.TextureWeight); if (aBoneWeight != null) { aBoneWeight[i].boneIndex0 = aGenPoint[i].BoneIndex0; aBoneWeight[i].boneIndex1 = aGenPoint[i].BoneIndex1; aBoneWeight[i].boneIndex2 = aGenPoint[i].BoneIndex2; aBoneWeight[i].boneIndex3 = aGenPoint[i].BoneIndex3; Vector4 boneWeight = Codec.UnpackRgba(aGenPoint[i].BoneWeight); for (int iWeightComp = 4; iWeightComp > MeshGenerationAutoRiggingMaxBonesPerVertex && iWeightComp >= 0; --iWeightComp) boneWeight[iWeightComp - 1] = 0.0f; if (aBoneWeight[i].boneIndex0 < 0) { aBoneWeight[i].boneIndex0 = 0; boneWeight.x = 0.0f; } if (aBoneWeight[i].boneIndex1 < 0) { aBoneWeight[i].boneIndex1 = 0; boneWeight.y = 0.0f; } if (aBoneWeight[i].boneIndex2 < 0) { aBoneWeight[i].boneIndex2 = 0; boneWeight.z = 0.0f; } if (aBoneWeight[i].boneIndex3 < 0) { aBoneWeight[i].boneIndex3 = 0; boneWeight.w = 0.0f; } boneWeight /= Mathf.Max(MathUtil.Epsilon, Vector4.Dot(boneWeight, Vector4.one)); aBoneWeight[i].weight0 = MathUtil.Saturate(boneWeight.x); aBoneWeight[i].weight1 = MathUtil.Saturate(boneWeight.y); aBoneWeight[i].weight2 = MathUtil.Saturate(boneWeight.z); aBoneWeight[i].weight3 = MathUtil.Saturate(boneWeight.w); } } mesh.SetColors(aColor); mesh.SetUVs(MeshUtil.EmissionHashUvIndex, aEmissionHash); mesh.SetUVs(MeshUtil.MetallicSmoothnessUvIndex, aMetallicSmoothness); mesh.SetUVs(MeshUtil.TextureWeightIndex, aTextureWeight); if (aBoneWeight != null && rootBone != null && bones != null) { NormalizeBoneTransforms(); mesh.boneWeights = aBoneWeight; mesh.bindposes = bones.Select(x => x.worldToLocalMatrix * rootBone.localToWorldMatrix).ToArray(); } bool uvGenerated = GenerateUV(mesh, generateTextureUV, generateLightMapUV); if (weldVertices) { MeshUtil.Weld(mesh, (uvGenerated ? 0 : -1)); } if (optimizeMeshForRendering) { mesh.Optimize(); } } } public virtual void RectifyNonUnitScaledParents() { } virtual protected bool GenerateUV(Mesh mesh, bool generateTextureUV, bool generateLightMapUV) { return false; } [SerializeField] [HideInInspector] private bool m_isMeshLocked = false; public bool MeshLocked => m_isMeshLocked; protected enum LockMeshIntermediateStateEnum { Idle, PreLock, PostLock, PreUnlock, } protected virtual LockMeshIntermediateStateEnum LockMeshIntermediateState => LockMeshIntermediateStateEnum.Idle; protected bool m_doRigging = false; public virtual Mesh AddCollider ( GameObject go, bool async, Mesh mesh = null, bool forceConvexCollider = false, bool makeRigidBody = false ) { return null; } public virtual Mesh AddLockedStandardMesh ( GameObject go, bool autoRigging, bool async, Mesh mesh = null, bool generateTextureUV = false, bool generateLightMapUV = false, bool weldVertices = false, bool optimizeMeshForRendering = false ) { return null; } public virtual void LockMesh ( bool autoRigging, bool async, Mesh mesh = null, bool generateTextureUV = false, bool generateLightMapUV = false, bool weldVertices = false, bool optimizeMeshForRendering = false ) { m_isMeshLocked = true; } public void RememberLockedMeshMaterial() { var meshRenderer = GetComponent(); if (meshRenderer != null) { m_lastLockedMeshMaterial = meshRenderer.sharedMaterial; } else { var skinnedMeshRenderer = GetComponent(); if (skinnedMeshRenderer != null) m_lastLockedMeshMaterial = skinnedMeshRenderer.sharedMaterial; } } public virtual void UnlockMesh() { m_isMeshLocked = false; MarkNeedsCompute(); RestoreBoneTransforms(); MarkNeedsCompute(); } #endregion // end: Mesh Utilities //------------------------------------------------------------------------- #region SDF public bool DrawGenerateSdfGizmos = false; // editor only public string GenerateSdfByEditorName = ""; // editor only public Vector3 GenerateSdfCenter = Vector3.zero; public Vector3 GenerateSdfDimension = Vector3.one; public Vector3Int GenerateSdfTextureSize = new Vector3Int(64, 64, 64); [NonSerialized] protected List m_jobQueue = new List(); [NonSerialized] protected List m_jobCompleteQueue = new List(); public void ValidateAssetNames() { if (GenerateColliderMeshAssetByEditorName.Equals("")) GenerateColliderMeshAssetByEditorName = $"{gameObject.name} Collider Mesh {(uint)gameObject.GetInstanceID()}"; if (GenerateMeshAssetByEditorName.Equals("")) GenerateMeshAssetByEditorName = $"{gameObject.name} Mesh {(uint) gameObject.GetInstanceID()}"; if (GenerateSdfByEditorName.Equals("")) GenerateSdfByEditorName = $"{gameObject.name} SDF {(uint) gameObject.GetInstanceID()}"; } public virtual void GenerateSdf ( RenderTexture sdf, Vector3 origin, Vector3 dimension ) { if (sdf == null) return; ValidateResources(); SetUpResources(); ComputeManager.SetTexture(Const.SdfOutput, sdf); ComputeManager.SetInts(Const.SdfOutputSize, new int[] { sdf.width, sdf.height, sdf.volumeDepth }); ComputeManager.SetVector(Const.SdfCenter, origin); ComputeManager.SetVector(Const.SdfDimension, dimension); ComputeManager.Dispatch ( s_computeSdfGen, Const.Kernel.GenerateSdf, (sdf.width + ThreadGroupExtent - 1) / ThreadGroupExtent, (sdf.height + ThreadGroupExtent - 1) / ThreadGroupExtent, (sdf.volumeDepth + ThreadGroupExtent - 1) / ThreadGroupExtent ); } public virtual void GenerateSdf ( Texture3D sdf, Vector3 origin, Vector3 dimension ) { if (sdf == null) return; var rt = new RenderTexture(sdf.width, sdf.height, 0, RenderTextureFormat.RFloat); rt.dimension = TextureDimension.Tex3D; rt.volumeDepth = sdf.depth; rt.enableRandomWrite = true; rt.Create(); GenerateSdf(rt, origin, dimension); TextureUtil.RenderTextureToTexture3D(sdf, rt); rt.Release(); } internal void UpdateComputeData() { Assert.True(m_initialized, "Renderer not initialized. Either an automatic call to OnEnable() or a manual call to Init() needs to be done before any computation."); if (m_needRescanBrushes) { RescanBrushesImmediate(); m_needRescanBrushes = false; } UpdateNeedsCompute(); UpdateBrushData(); UpdateAabbTreeData(); } public static MudBrushBase LookupBrush(float hash) { return s_brushMap.TryGetValue(hash, out var brush) ? brush : null; } #endregion // end: SDf //------------------------------------------------------------------------- #region Optimization public bool UseCutoffVolume = false; public Transform CutoffVolumeCenter; public Vector3 CutoffVolumeSize = Vector3.one; /* public Transform OptimizationRendererReference; public Transform OptimizationCameraRefernce; public bool EnableDistanceLod = false; public float DistanceLodStartDistance = 10.0f; public float DistanceLodFadeDistance = 5.0f; public float DistanceLodFarVoxelDensity = 4.0f; public bool EnableDistanceFade = false; public float DistanceFadeFarStartDistance = 10.0f; public float DistanceFadeFarFadeDistance = 5.0f; public float DistanceFadeNearStartDistance = 1.0f; public float DistanceFadeNearEndDistance = 1.0f; */ #endregion // end: Optimization //------------------------------------------------------------------------- #region Callbacks protected virtual void OnSharedMaterialChanged(UnityEngine.Object material) { } protected virtual void InitBeforeFirstRenderer() { Sdf.InitEvalMap(); } protected virtual void CleanUpAfterLastRenderer() { DisposeGlobalResources(); Sdf.DisposeEvalMap(); } private void Start() { if ((MeshGenerationLockOnStart || (MeshGenerationLockOnStartByEditor && MeshGenerationRenderableMeshMode == RenderableMeshMode.Procedural)) && Application.isPlaying && !m_isMeshLocked) { if (MeshGenerationCreateCollider) AddCollider(gameObject, false, null, MeshGenerationForceConvexCollider, MeshGenerationCreateRigidBody); MarkNeedsCompute(); LockMesh(MeshGenerationAutoRigging, false); } } protected virtual void OnEnable() { Init(); } protected virtual void OnDisable() { ShutDown();} public void Init() { if (m_initialized) return; Janitor.Init(); if (s_renderers.Count == 0) { InitBeforeFirstRenderer(); } s_renderers.Add(this); m_rendererIndex = s_renderers.Count - 1; m_needRescanBrushes = true; MarkNeedsCompute(); m_aSdfBrush = new NativeArray(MaxBrushes, Allocator.Persistent); m_aSdfBrushMaterial = new NativeArray(MaxBrushes, Allocator.Persistent); m_sdfBrushMaterialIndexMap = new Dictionary(); m_aabbTree = new AabbTree(AabbTreeFatBoundsRadius); MarkNeedsCompute(); MudSharedMaterialBase.OnSharedMaterialChanged += OnSharedMaterialChanged; m_previousTrackedVersion = m_currentTrackedVersion; m_currentTrackedVersion = MudBun.Version; if (m_firstTrackedVersion.Equals("")) m_firstTrackedVersion = m_currentTrackedVersion; if (m_previousTrackedVersion.Equals("")) m_previousTrackedVersion = m_currentTrackedVersion; m_initialized = true; } public void ShutDown() { if (!m_initialized) return; m_initialized = false; // complete lingering jobs for (int i = 0; i < m_jobCompleteQueue.Count; ++i) { m_jobCompleteQueue[i].Complete(); } m_jobCompleteQueue.Clear(); ClearBrushes(); DisposeLocalResources(); if (m_aSdfBrush.IsCreated) m_aSdfBrush.Dispose(); if (m_aSdfBrushMaterial.IsCreated) m_aSdfBrushMaterial.Dispose(); m_sdfBrushMaterialIndexMap = null; m_aabbTree.Dispose(); m_aabbTree = null; MudSharedMaterialBase.OnSharedMaterialChanged -= OnSharedMaterialChanged; s_renderers.Remove(this); m_rendererIndex = -1; if (s_renderers.Count == 0) { CleanUpAfterLastRenderer(); } } protected virtual void OnValidate() { SanitizeParameters(); m_numVoxelsHighWaterMark = 0; m_numChunksHighWaterMark = 0; if (!m_isMeshLocked) MarkNeedsCompute(); } internal void OnBrushDisabled(MudBrushBase brush) { RemoveBrush(brush); } // SDF evaluation interface would need brush & AABB tree data ready before Compute() // don't update it again for Compute() if they are already updated private bool m_brushDataDirty = false; private bool m_aabbTreeDirty = false; public void MarkNeedsCompute() { m_needsCompute = true; m_brushDataDirty = true; m_aabbTreeDirty = true; } private bool m_needsCompute = false; private bool UpdateNeedsCompute() { if (MeshLocked) { bool needsCompute = false; if (MeshGenerationRenderableMeshMode == RenderableMeshMode.Procedural) needsCompute = m_needsCompute; m_needsCompute = false; return needsCompute; } switch (ComputeMode) { case ComputeModeEnum.Auto: break; case ComputeModeEnum.Manual: case ComputeModeEnum.ManualNoRender: return m_needsCompute; case ComputeModeEnum.EveryFrame: MarkNeedsCompute(); return true; case ComputeModeEnum.TimeSliced: switch (TimeSliceMode) { case TimeSliceModeEnum.ByFramesAutoOffset: if (((Time.frameCount + m_rendererIndex) % TimeSliceFrames) != 0) return false; break; case TimeSliceModeEnum.ByFramesManualOffset: if (((Time.frameCount + TimeSliceFramesOffset) % TimeSliceFrames) != 0) return false; break; case TimeSliceModeEnum.ByPeriodAutoOffset: { int q = (int) Mathf.Floor((Time.time + Mathf.Repeat(m_rendererIndex * Time.deltaTime, TimeSlicePeriod)) / TimeSlicePeriod); float qT = q * TimeSlicePeriod; if (qT <= m_lastUpdateTimeSliceTime) return false; m_lastUpdateTimeSliceTime = qT; break; } case TimeSliceModeEnum.ByPeriodManualOffset: { int q = (int) Mathf.Floor((Time.time + Mathf.Repeat(TimeSliceTimeOffset, TimeSlicePeriod)) / TimeSlicePeriod); float qT = q * TimeSlicePeriod; if (qT <= m_lastUpdateTimeSliceTime) return false; m_lastUpdateTimeSliceTime = qT; break; } } break; } if (m_needsCompute) return true; Profiler.BeginSample("UpdateNeedsCompute"); // check need for rendering geometry type change if (m_needsCompute) { var currRenderGeometryType = RenderGeometryType; bool renderGeometryTypeChanged = (currRenderGeometryType != m_prevRenderGeometryType); m_prevRenderGeometryType = currRenderGeometryType; if (renderGeometryTypeChanged) m_needsCompute = true; } // check dirty brushes if (!m_needsCompute) { foreach (var b in m_aBrush) { if (!b.m_dirty && !b.transform.hasChanged) continue; MarkNeedsCompute(); break; } } // check animation if (!m_needsCompute) { if (TryGetComponent(out Animation animation) && animation.isPlaying) { MarkNeedsCompute(); } } // check animator if (!m_needsCompute) { if (TryGetComponent(out Animator animator) && animator.layerCount > 0) { var animState = animator.GetCurrentAnimatorStateInfo(0); if (animState.length > 0.0f && animState.speed > 0.0f && animState.speedMultiplier > 0.0f) MarkNeedsCompute(); } } if (m_needsCompute) { Profiler.BeginSample("Clear Transform Change Flags"); foreach (var b in m_aBrush) { b.m_dirty = false; b.transform.hasChanged = false; } Profiler.EndSample(); } Profiler.EndSample(); return m_needsCompute; } public RenderMaterialModeEnum RenderMaterialMode = RenderMaterialModeEnum.Static; public void MarkRenderMaterialDirty() { m_renderMaterialDirty = true; } private bool m_renderMaterialDirty = true; protected virtual bool IsEditorBusy() { return false; } private int m_numVoxelsHighWaterMark = 0; private int m_numChunksHighWaterMark = 0; private void TryAutoAdjustBudgets() { if (!Application.isEditor) return; if (!ShowGpuMemoryUsage) { m_autoAdjustBudgetsToHighWaterMarks = false; return; } if (!AutoAdjustBudgetsToHighWaterMarks) { m_autoAdjustBudgetsToHighWaterMarks = false; return; } Profiler.BeginSample("TryAutoAdjustBudgets"); if (AutoAdjustBudgetsToHighWaterMarks && !m_autoAdjustBudgetsToHighWaterMarks) { m_numVoxelsHighWaterMark = 0; m_numChunksHighWaterMark = 0; } m_autoAdjustBudgetsToHighWaterMarks = AutoAdjustBudgetsToHighWaterMarks; m_numVoxelsHighWaterMark = Mathf.Max(NumVoxelsUsed, m_numVoxelsHighWaterMark); m_numChunksHighWaterMark = Mathf.Max(NumChunksUsed, m_numChunksHighWaterMark); int effectiveNumVoxelsHighWaterMark = m_numVoxelsHighWaterMark; if (ShouldDoAutoSmoothing && EnableSmoothCorner) { effectiveNumVoxelsHighWaterMark = Mathf.Max(effectiveNumVoxelsHighWaterMark, NumVerticesGenerated / VoxelToVertexFactor); } MaxVoxelsK = Mathf.Max(1, (int) Mathf.Ceil(effectiveNumVoxelsHighWaterMark * (1.0f + AutoAdjustBudgetsToHighWaterMarksMarginPercent / 100.0f) / 1024.0f)); MaxChunks = Mathf.Max(16, (int) Mathf.Ceil(m_numChunksHighWaterMark * (1.0f + AutoAdjustBudgetsToHighWaterMarksMarginPercent / 100.0f))); Profiler.EndSample(); } protected virtual bool PreUpdateValidate() { return true; } protected void LateUpdate() { UpdateComputeData(); if (m_jobQueue.Count > 0) { // complete lingering jobs for (int i = 0; i < m_jobCompleteQueue.Count; ++i) { m_jobCompleteQueue[i].Complete(); } m_jobCompleteQueue.Clear(); // schedule queued jobs for (int i = 0; i < m_jobQueue.Count; ++i) { m_jobQueue[i].Schedule(true); m_jobCompleteQueue.Add(m_jobQueue[i]); } JobHandle.ScheduleBatchedJobs(); m_jobQueue.Clear(); } if (!PreUpdateValidate()) return; if (m_isMeshLocked) { switch (MeshGenerationRenderableMeshMode) { case RenderableMeshMode.Procedural: // keep on rendering in procedural mode break; default: return; } } TryCompute(); switch (ComputeMode) { case ComputeModeEnum.ManualNoRender: break; default: Render(); break; } } public void ForceCompute() { TryCompute(true); } public virtual void ReloadShaders() { DisposeGlobalResources(); DisposeLocalResources(); MarkNeedsCompute(); } private void TryCompute(bool forceCompute = false) { if (IsEditorBusy()) return; TryAutoAdjustBudgets(); SanitizeParameters(); if (!ValidateResources()) return; if (!s_globalResourcesValid || !m_localResourcesValid) return; if (forceCompute || m_needsCompute) { switch (HardwareMode) { case HardwareModeEnum.Gpu: ComputeGpu(); break; /* case HardwareModeEnum.Cpu: ComputeCpu(); break; */ } m_needsCompute = false; } } private void SanitizeParameters() { Validate.Range(-1, VoxelNodeDepth, ref DrawVoxelNodesDepth); Validate.NonNegative(ref RayTracedVoxelInternalPaddingDistance); Validate.NonNegative(ref RayTracedVoxelSizeFadeDistance); } #if MUDBUN_FREE private static readonly int s_watermarkWidth = 300; private static readonly int s_watermariHeight = 100; private static readonly string s_watermarkStr = "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"; private static Texture2D s_watermark; private void OnGUI() { switch (Event.current.type) { case EventType.Repaint: //if (MudBun.IsFreeVersion) { if (s_watermark == null) { s_watermark = new Texture2D(s_watermarkWidth, s_watermariHeight); s_watermark.LoadImage(Convert.FromBase64String(s_watermarkStr)); } // just let one renderer draw var itRenderer = s_renderers.GetEnumerator(); if (!itRenderer.MoveNext() || itRenderer.Current != this) return; Graphics.DrawTexture(new Rect(0.0f, Screen.height - s_watermark.height, s_watermark.width, s_watermark.height), s_watermark); } break; } } #endif #endregion // end: Callbacks //------------------------------------------------------------------------- #region Resources private bool ValidateResources() { Profiler.BeginSample("ValidateResources"); if (m_needRescanBrushes) { RescanBrushesImmediate(); m_needRescanBrushes = false; } if (!ValidateGlobalResources()) return false; if (!ValidateLocalResources()) return false; /* foreach (var b in m_aBrush) b.ValidateMaterial(); */ Profiler.EndSample(); return true; } private static bool s_shaderConstantIdPopulated = false; public static void ValidateShaderConstantId() { if (s_shaderConstantIdPopulated) return; Const.TriTable = Shader.PropertyToID("triTable"); Const.VertTable = Shader.PropertyToID("vertTable"); Const.TriTable2d = Shader.PropertyToID("triTable2d"); Const.Brushes = Shader.PropertyToID("aBrush"); Const.BrushMaterials = Shader.PropertyToID("aBrushMaterial"); Const.NumBrushes = Shader.PropertyToID("numBrushes"); Const.SurfaceShift = Shader.PropertyToID("surfaceShift"); Const.RenderMode = Shader.PropertyToID("renderMode"); Const.MeshingMode = Shader.PropertyToID("meshingMode"); Const.RayTracedVoxelMode = Shader.PropertyToID("rayTracedVoxelMode"); Const.RayTracedVoxelSizeMultiplier = Shader.PropertyToID("rayTracedVoxelSizeMultiplier"); Const.RayTracedVoxelSmoothCubeNormal = Shader.PropertyToID("rayTracedVoxelSmoothCubeNormal"); Const.RayTracedVoxelRadius = Shader.PropertyToID("rayTracedVoxelRadius"); Const.RayTracedVoxelPaddingMode = Shader.PropertyToID("rayTracedVoxelPaddingMode"); Const.RayTracedVoxelInternalPaddingDistance = Shader.PropertyToID("rayTracedVoxelInternalPaddingDistance"); Const.RayTracedVoxelSizeFadeDistance = Shader.PropertyToID("rayTracedVoxelSizeFadeDistance"); Const.NormalDifferentiationStep = Shader.PropertyToID("normalDifferentiationStep"); Const.NormalQuantization = Shader.PropertyToID("normalQuantization"); Const.Normal2dFadeDist = Shader.PropertyToID("normal2dFadeDist"); Const.Normal2dStrength = Shader.PropertyToID("normal2dStrength"); Const.EnableAutoSmooth = Shader.PropertyToID("enableAutoSmooth"); Const.AutoSmoothMaxAngle = Shader.PropertyToID("autoSmoothMaxAngle"); Const.AutoSmoothVertDataTable = Shader.PropertyToID("autoSmoothVertDataTable"); Const.AutoSmoothVertDataPoolSize = Shader.PropertyToID("autoSmoothVertDataPoolSize"); Const.EnableSmoothCorner = Shader.PropertyToID("enableSmoothCorner"); Const.SmoothCornerSubdivision = Shader.PropertyToID("smoothCornerSubdivision"); Const.SmoothCornerNormalBlur = Shader.PropertyToID("smoothCornerNormalBlur"); Const.SmoothCornerFade = Shader.PropertyToID("smoothCornerFade"); Const.InvertNormals = Shader.PropertyToID("invertNormals"); Const.SplatSize = Shader.PropertyToID("splatSize"); Const.SplatSizeJitter = Shader.PropertyToID("splatSizeJitter"); Const.SplatNormalShift = Shader.PropertyToID("splatNormalShift"); Const.SplatNormalShiftJitter = Shader.PropertyToID("splatNormalShiftJitter"); Const.SplatPositionJitter = Shader.PropertyToID("splatPositionJitter"); Const.SplatColorJitter = Shader.PropertyToID("splatColorJitter"); Const.SplatRotationJitter = Shader.PropertyToID("splatRotationJitter"); Const.SplatOrientationJitter = Shader.PropertyToID("splatOrientationJitter"); Const.SplatOriginalNormalBlend = Shader.PropertyToID("splatOriginalNormalBlend"); Const.SplatJitterNoisiness = Shader.PropertyToID("splatJitterNoisiness"); Const.SplatCameraFacing = Shader.PropertyToID("splatCameraFacing"); Const.SplatNormalsMatchCameraFacing = Shader.PropertyToID("splatNormalsMatchCameraFacing"); Const.SplatShadowsMatchCameraFacing = Shader.PropertyToID("splatShadowsMatchCameraFacing"); Const.SplatScreenSpaceFlattening = Shader.PropertyToID("splatScreenSpaceFlattening"); //Const.SplatSmoothNormalBlend = Shader.PropertyToID("splatSmoothNormalBlend"); Const.SurfaceNetsDualQuadsBlend = Shader.PropertyToID("surfaceNetsDualQuadsBlend"); Const.SurfaceNetsBinarySearchIterations = Shader.PropertyToID("surfaceNetsBinarySearchIterations"); Const.SurfaceNetsGradientDescentIterations = Shader.PropertyToID("surfaceNetsGradientDescentIterations"); Const.SurfaceNetsGradientDescentFactor = Shader.PropertyToID("surfaceNetsGradientDescentFactor"); Const.DualContouringDualQuadsBlend = Shader.PropertyToID("dualContouringDualQuadsBlend"); Const.DualContouringRelaxation = Shader.PropertyToID("dualContouringRelaxation"); Const.DualContouringSolverIterations = Shader.PropertyToID("dualContouringSolverIterations"); Const.DualContouringBinarySearchIterations = Shader.PropertyToID("dualContouringBinarySearchIterations"); Const.DualContouringGradientDescentIterations = Shader.PropertyToID("dualContouringGradientDescentIterations"); Const.DualContouringGradientDescentFactor = Shader.PropertyToID("dualContouringGradientDescentFactor"); Const.AabbTree = Shader.PropertyToID("aabbTree"); Const.AabbRoot = Shader.PropertyToID("aabbRoot"); Const.Enable2dMode = Shader.PropertyToID("enable2dMode"); Const.ForceAllBrushes = Shader.PropertyToID("forceAllBrushes"); Const.NumAllocations = Shader.PropertyToID("aNumAllocation"); Const.NodeHashTable = Shader.PropertyToID("nodeHashTable"); Const.NodeHashTableSize = Shader.PropertyToID("nodeHashTableSize"); Const.NodePool = Shader.PropertyToID("nodePool"); Const.NodePoolSize = Shader.PropertyToID("nodePoolSize"); Const.NumNodesAllocated = Shader.PropertyToID("aNumNodesAllocated"); Const.UseVoxelCache = Shader.PropertyToID("useVoxelCache"); Const.VoxelCacheIdTable = Shader.PropertyToID("voxelCacheIdTable"); Const.VoxelCache = Shader.PropertyToID("voxelCache"); Const.VoxelCacheSize = Shader.PropertyToID("voxelCacheSize"); Const.BrushMaskPool = Shader.PropertyToID("brushMaskPool"); Const.BrushMaskPoolSize = Shader.PropertyToID("brushMaskPoolSize"); Const.IndirectDispatchArgs = Shader.PropertyToID("indirectDispatchArgs"); Const.CurrentNodeDepth = Shader.PropertyToID("currentNodeDepth"); Const.CurrentNodeBranchingFactor = Shader.PropertyToID("currentNodeBranchingFactor"); Const.CurrentNodeSize = Shader.PropertyToID("currentNodeSize"); Const.VoxelSize = Shader.PropertyToID("voxelSize"); Const.VoxelTreeBranchingFactorsCompressed = Shader.PropertyToID("voxelTreeBranchingFactorsCompressed"); Const.VoxelNodeSizes = Shader.PropertyToID("voxelNodeSizes"); Const.MaxNodeDepth = Shader.PropertyToID("maxNodeDepth"); Const.ChunkVoxelDensity = Shader.PropertyToID("chunkVoxelDensity"); Const.GenPoints = Shader.PropertyToID("aGenPoint"); Const.MaxGenPoints = Shader.PropertyToID("maxGenPoints"); Const.IndirectDrawArgs = Shader.PropertyToID("indirectDrawArgs"); Const.MasterColor = Shader.PropertyToID("_Color"); Const.MasterEmission = Shader.PropertyToID("_Emission"); Const.MasterMetallic = Shader.PropertyToID("_Metallic"); Const.MasterSmoothness = Shader.PropertyToID("_Smoothness"); Const.ScaleSign = Shader.PropertyToID("scaleSign"); Const.LocalToWorld = Shader.PropertyToID("localToWorld"); Const.LocalToWorldIt = Shader.PropertyToID("localToWorldIt"); Const.LocalToWorldScale = Shader.PropertyToID("localToWorldScale"); Const.WorldToLocal = Shader.PropertyToID("worldToLocal"); Const.WorldToLocalIt = Shader.PropertyToID("worldToLocalIt"); Const.NoiseCache = Shader.PropertyToID("noiseCache"); Const.NoiseCacheDimension = Shader.PropertyToID("noiseCacheDimension"); Const.NoiseCacheDensity = Shader.PropertyToID("noiseCacheDensity"); Const.NoiseCachePeriod = Shader.PropertyToID("noiseCachePeriod"); Const.SdfOutput = Shader.PropertyToID("sdfOutput"); Const.SdfOutputSize = Shader.PropertyToID("sdfOutputSize"); Const.SdfCenter = Shader.PropertyToID("sdfCenter"); Const.SdfDimension = Shader.PropertyToID("sdfDimension"); Const.MaxRayMarchSteps = Shader.PropertyToID("maxRayMarchSteps"); Const.RayMarchHitDistance = Shader.PropertyToID("rayMarchHitDistance"); Const.RayMarchMaxRayDistance = Shader.PropertyToID("rayMarchMaxRayDistance"); Const.MeshGenerationAutoRiggingAlgorithm = Shader.PropertyToID("autoRiggingAlgorithm"); /* Const.NumLightMarchSteps = Shader.PropertyToID("numLightMarchSteps"); Const.RayMarchStepSize = Shader.PropertyToID("rayMarchStepSize"); Const.RayMarchVolumeDensity = Shader.PropertyToID("rayMarchVolumeDensity"); Const.RayMarchLightPositionType = Shader.PropertyToID("rayMarchLightPositionType"); Const.RayMarchLightDirection = Shader.PropertyToID("rayMarchLightDirection"); Const.RayMarchAbsorption = Shader.PropertyToID("rayMarchAbsorption"); Const.RayMarchDarknesThreshold = Shader.PropertyToID("rayMarchDarknesThreshold"); Const.RayMarchTransmittanceCurve = Shader.PropertyToID("rayMarchTransmittanceCurve"); Const.RayMarchNoiseEdgeFade = Shader.PropertyToID("rayMarchNoiseEdgeFade"); Const.RayMarchNoiseThreshold = Shader.PropertyToID("rayMarchNoiseThreshold"); Const.RayMarchNoiseScrollSpeed = Shader.PropertyToID("rayMarchNoiseScrollSpeed"); Const.RayMarchNoiseBaseOctaveSize = Shader.PropertyToID("rayMarchNoiseBaseOctaveSize"); Const.RayMarchNoiseNumOctaves = Shader.PropertyToID("rayMarchNoiseNumOctaves"); Const.RayMarchNoiseOctaveOffsetFactor = Shader.PropertyToID("rayMarchNoiseOctaveOffsetFactor"); */ Const.IsMeshRenderMaterial = Shader.PropertyToID("_IsMeshRenderMaterial"); Const.IsSplatRenderMaterial = Shader.PropertyToID("_IsSplatRenderMaterial"); Const.MaterialNeedsSdfProperties= Shader.PropertyToID("_MaterialNeedsSdfProperties"); Const.MaterialNeedsRayMarchingProperties = Shader.PropertyToID("_MaterialNeedsRayMarchingProperties"); s_shaderConstantIdPopulated = true; } private static void RegisterCommonMeshingConstants(ComputeShader shader) { ComputeManager.RegisterConstantId(shader, Const.RenderMode); ComputeManager.RegisterConstantId(shader, Const.MeshingMode); ComputeManager.RegisterConstantId(shader, Const.NormalDifferentiationStep); ComputeManager.RegisterConstantId(shader, Const.NormalQuantization); ComputeManager.RegisterConstantId(shader, Const.Normal2dFadeDist); ComputeManager.RegisterConstantId(shader, Const.Normal2dStrength); ComputeManager.RegisterConstantId(shader, Const.EnableAutoSmooth); ComputeManager.RegisterConstantId(shader, Const.AutoSmoothMaxAngle); ComputeManager.RegisterConstantId(shader, Const.AutoSmoothVertDataTable); ComputeManager.RegisterConstantId(shader, Const.AutoSmoothVertDataPoolSize); ComputeManager.RegisterConstantId(shader, Const.EnableSmoothCorner); ComputeManager.RegisterConstantId(shader, Const.SmoothCornerSubdivision); ComputeManager.RegisterConstantId(shader, Const.SmoothCornerNormalBlur); ComputeManager.RegisterConstantId(shader, Const.SmoothCornerFade); ComputeManager.RegisterConstantId(shader, Const.InvertNormals); ComputeManager.RegisterConstantId(shader, Const.NodeHashTableSize); ComputeManager.RegisterConstantId(shader, Const.NodePoolSize); ComputeManager.RegisterConstantId(shader, Const.UseVoxelCache); ComputeManager.RegisterConstantId(shader, Const.VoxelCacheSize); ComputeManager.RegisterConstantId(shader, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(shader, Const.Brushes); ComputeManager.RegisterConstantId(shader, Const.NumBrushes); ComputeManager.RegisterConstantId(shader, Const.BrushMaterials); ComputeManager.RegisterConstantId(shader, Const.SurfaceShift); ComputeManager.RegisterConstantId(shader, Const.AabbTree); ComputeManager.RegisterConstantId(shader, Const.AabbRoot); ComputeManager.RegisterConstantId(shader, Const.Enable2dMode); ComputeManager.RegisterConstantId(shader, Const.ForceAllBrushes); ComputeManager.RegisterConstantId(shader, Const.NodeHashTable); ComputeManager.RegisterConstantId(shader, Const.NodePool); ComputeManager.RegisterConstantId(shader, Const.NumNodesAllocated); ComputeManager.RegisterConstantId(shader, Const.NumAllocations); ComputeManager.RegisterConstantId(shader, Const.VoxelCacheIdTable); ComputeManager.RegisterConstantId(shader, Const.VoxelCache); ComputeManager.RegisterConstantId(shader, Const.BrushMaskPool); ComputeManager.RegisterConstantId(shader, Const.IndirectDispatchArgs); ComputeManager.RegisterConstantId(shader, Const.GenPoints); ComputeManager.RegisterConstantId(shader, Const.IndirectDrawArgs); ComputeManager.RegisterConstantId(shader, Const.VoxelSize); ComputeManager.RegisterConstantId(shader, Const.VoxelTreeBranchingFactorsCompressed); ComputeManager.RegisterConstantId(shader, Const.VoxelNodeSizes); ComputeManager.RegisterConstantId(shader, Const.MaxNodeDepth); ComputeManager.RegisterConstantId(shader, Const.ChunkVoxelDensity); ComputeManager.RegisterConstantId(shader, Const.NoiseCache); ComputeManager.RegisterConstantId(shader, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(shader, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(shader, Const.CurrentNodeDepth); ComputeManager.RegisterConstantId(shader, Const.CurrentNodeBranchingFactor); ComputeManager.RegisterConstantId(shader, Const.CurrentNodeSize); } public static bool ValidateComputeShaders() { if (s_computeVoxelGen != null && s_computeMarchingCubes != null && s_computeDualMeshing != null && s_computeNoiseCache != null && s_computeMeshLock != null) return true; ComputeManager.Reset(); ValidateShaderConstantId(); // voxel gen { s_computeVoxelGen = ResourcesUtil.VoxelGen; if (s_computeVoxelGen == null) return false; ComputeManager.RegisterShader(s_computeVoxelGen); Const.Kernel.ClearVoxelHashTable = ComputeManager.RegisterKernel(s_computeVoxelGen, "clear_voxel_hash_table"); Const.Kernel.ClearAutoSmoothVertDataTable = ComputeManager.RegisterKernel(s_computeVoxelGen, "clear_auto_smooth_vert_data_table"); Const.Kernel.ClearVoxelCache = ComputeManager.RegisterKernel(s_computeVoxelGen, "clear_voxel_cache"); Const.Kernel.RegisterTopNodes = ComputeManager.RegisterKernel(s_computeVoxelGen, "register_top_nodes"); Const.Kernel.UpdateBranchingIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeVoxelGen, "update_branching_indirect_dispatch_args"); Const.Kernel.AllocateChildNodes = ComputeManager.RegisterKernel(s_computeVoxelGen, "allocate_child_nodes"); Const.Kernel.UpdateVoxelIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeVoxelGen, "update_voxel_indirect_dispatch_args"); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RenderMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RayTracedVoxelMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RayTracedVoxelPaddingMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RayTracedVoxelInternalPaddingDistance); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RayTracedVoxelSizeFadeDistance); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumBrushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodeHashTableSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodePoolSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.Brushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaterials); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.SurfaceShift); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AabbTree); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AabbRoot); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.Enable2dMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.ForceAllBrushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodeHashTable); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodePool); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumNodesAllocated); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumAllocations); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaskPool); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.IndirectDispatchArgs); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.GenPoints); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.IndirectDrawArgs); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelTreeBranchingFactorsCompressed); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelNodeSizes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.MaxNodeDepth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.ChunkVoxelDensity); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeDepth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeBranchingFactor); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NormalDifferentiationStep); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.EnableAutoSmooth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothMaxAngle); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothVertDataTable); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothVertDataPoolSize); } // end: voxel gen // marching cubes { s_computeMarchingCubes = ResourcesUtil.MarchingCubes; if (s_computeMarchingCubes == null) return false; ComputeManager.RegisterShader(s_computeMarchingCubes); Const.Kernel.GenerateFlatMarchingCubesMesh = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_flat_marching_cubes_mesh"); Const.Kernel.GenerateSmoothMarchingCubesMesh = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_smooth_marching_cubes_mesh"); Const.Kernel.GenerateMarchingCubesSplats = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_marching_splats"); Const.Kernel.GenerateFlatMarchingCubesMesh2d = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_flat_marching_cubes_mesh_2d"); Const.Kernel.GenerateSmoothMarchingCubesMesh2d = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_smooth_marching_cubes_mesh_2d"); Const.Kernel.GenerateMarchingCubesSplats2d = ComputeManager.RegisterKernel(s_computeMarchingCubes, "generate_marching_splats_2d"); Const.Kernel.UpdateMarchingCubesAutoSmoothIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeMarchingCubes, "update_marching_cubes_auto_smooth_indirect_dispatch_args"); Const.Kernel.MarchingCubesUpdateAutoSmooth = ComputeManager.RegisterKernel(s_computeMarchingCubes, "marching_cubes_update_auto_smooth"); Const.Kernel.MarchingCubesComputeAutoSmooth = ComputeManager.RegisterKernel(s_computeMarchingCubes, "marching_cubes_compute_auto_smooth"); RegisterCommonMeshingConstants(s_computeMarchingCubes); ComputeManager.RegisterConstantId(s_computeMarchingCubes, Const.TriTable); ComputeManager.RegisterConstantId(s_computeMarchingCubes, Const.VertTable); ComputeManager.RegisterConstantId(s_computeMarchingCubes, Const.TriTable2d); } // dual meshing { s_computeDualMeshing = ResourcesUtil.DualMeshing; if (s_computeDualMeshing == null) return false; ComputeManager.RegisterShader(s_computeDualMeshing); Const.Kernel.GenerateDualQuads = ComputeManager.RegisterKernel(s_computeDualMeshing, "generate_dual_quads"); Const.Kernel.GenerateDualQuads2d = ComputeManager.RegisterKernel(s_computeDualMeshing, "generate_dual_quads_2d"); Const.Kernel.UpdateDualMeshingIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeDualMeshing, "update_dual_meshing_indirect_dispatch_args"); Const.Kernel.DualMeshingFlatMeshNormal = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_flat_mesh_normal"); Const.Kernel.DualMeshingSmoothMeshNormal = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_smooth_mesh_normal"); Const.Kernel.DualMeshingFlatMeshNormal2d = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_flat_mesh_normal_2d"); Const.Kernel.DualMeshingSmoothMeshNormal2d = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_smooth_mesh_normal_2d"); Const.Kernel.DualMeshingUpdateAutoSmooth = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_update_auto_smooth"); Const.Kernel.DualMeshingComputeAutoSmooth = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_compute_auto_smooth"); Const.Kernel.DualMeshingUpdateSmoothCornerIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_udpate_auto_smooth_SmoothCorner_indirect_dispatch_args"); Const.Kernel.DualMeshingSmoothCorner = ComputeManager.RegisterKernel(s_computeDualMeshing, "dual_meshing_auto_smooth_smooth_corner"); Const.Kernel.UpdateDualMeshingSplatsIndirectArgs = ComputeManager.RegisterKernel(s_computeDualMeshing, "update_dual_meshing_splats_indirect_args"); Const.Kernel.ConvertDualMeshingSplats = ComputeManager.RegisterKernel(s_computeDualMeshing, "convert_dual_meshing_splats"); RegisterCommonMeshingConstants(s_computeDualMeshing); } // surface nets { s_computeSurfaceNets = ResourcesUtil.SurfaceNets; if (s_computeSurfaceNets == null) return false; ComputeManager.RegisterShader(s_computeSurfaceNets); Const.Kernel.SurfaceNetsMovePoint = ComputeManager.RegisterKernel(s_computeSurfaceNets, "surface_nets_move_point"); Const.Kernel.SurfaceNetsMovePoint2d = ComputeManager.RegisterKernel(s_computeSurfaceNets, "surface_nets_move_point_2d"); RegisterCommonMeshingConstants(s_computeSurfaceNets); ComputeManager.RegisterConstantId(s_computeSurfaceNets, Const.SurfaceNetsDualQuadsBlend); ComputeManager.RegisterConstantId(s_computeSurfaceNets, Const.SurfaceNetsBinarySearchIterations); ComputeManager.RegisterConstantId(s_computeSurfaceNets, Const.SurfaceNetsGradientDescentIterations); ComputeManager.RegisterConstantId(s_computeSurfaceNets, Const.SurfaceNetsGradientDescentFactor); } // dual contouring { s_computeDualContouring = ResourcesUtil.DualContouring; if (s_computeDualContouring == null) return false; ComputeManager.RegisterShader(s_computeDualContouring); Const.Kernel.DualContouringMovePoint = ComputeManager.RegisterKernel(s_computeDualContouring, "dual_contouring_move_point"); Const.Kernel.DualContouringMovePoint2d = ComputeManager.RegisterKernel(s_computeDualContouring, "dual_contouring_move_point_2d"); RegisterCommonMeshingConstants(s_computeDualContouring); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringDualQuadsBlend); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringRelaxation); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringSolverIterations); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringBinarySearchIterations); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringGradientDescentIterations); ComputeManager.RegisterConstantId(s_computeDualContouring, Const.DualContouringGradientDescentFactor); } // ray-traced voxels { s_computeRayTracedVoxels = ResourcesUtil.RayTracedVoxels; if (s_computeRayTracedVoxels == null) return false; ComputeManager.RegisterShader(s_computeRayTracedVoxels); Const.Kernel.UpdateRayTracedVoxelIndirectDispatchArgs = ComputeManager.RegisterKernel(s_computeRayTracedVoxels, "update_ray_traced_voxel_indirect_dispatch_args"); Const.Kernel.ComputeRayTracedVoxelGenPoints = ComputeManager.RegisterKernel(s_computeRayTracedVoxels, "compute_ray_traced_voxel_gen_points"); Const.Kernel.ComputeRayTracedVoxelGenPointsWithNormals = ComputeManager.RegisterKernel(s_computeRayTracedVoxels, "compute_ray_traced_voxel_gen_points_with_normals"); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RenderMode); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelMode); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelSizeMultiplier); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelSmoothCubeNormal); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelRadius); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelPaddingMode); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelInternalPaddingDistance); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.RayTracedVoxelSizeFadeDistance); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NumBrushes); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NodeHashTableSize); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NodePoolSize); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.Brushes); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.BrushMaterials); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.SurfaceShift); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.AabbTree); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.AabbRoot); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.ForceAllBrushes); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NodeHashTable); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NodePool); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NumNodesAllocated); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NumAllocations); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.BrushMaskPool); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.IndirectDispatchArgs); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.GenPoints); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.IndirectDrawArgs); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.VoxelSize); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.VoxelTreeBranchingFactorsCompressed); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.VoxelNodeSizes); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.MaxNodeDepth); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.ChunkVoxelDensity); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.CurrentNodeDepth); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.CurrentNodeBranchingFactor); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.CurrentNodeSize); ComputeManager.RegisterConstantId(s_computeRayTracedVoxels, Const.NormalDifferentiationStep); } // noise cache { s_computeNoiseCache = ResourcesUtil.NoiseCache; if (s_computeNoiseCache == null) return false; // switched to using based noise texture ComputeManager.RegisterShader(s_computeNoiseCache); Const.Kernel.GenerateNoiseCache = ComputeManager.RegisterKernel(s_computeNoiseCache, "generate_noise_cache"); ComputeManager.RegisterConstantId(s_computeNoiseCache, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeNoiseCache, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeNoiseCache, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(s_computeNoiseCache, Const.NoiseCachePeriod); } // end: noise cache // mesh lock { s_computeMeshLock = ResourcesUtil.MeshLock; if (s_computeMeshLock == null) return false; ComputeManager.RegisterShader(s_computeMeshLock); Const.Kernel.RigBones = ComputeManager.RegisterKernel(s_computeMeshLock, "rig_bones"); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NumBrushes); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.Brushes); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.BrushMaterials); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.AabbTree); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.AabbRoot); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NodeHashTable); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NodePool); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NumNodesAllocated); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NumAllocations); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.VoxelCacheIdTable); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.VoxelCache); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.BrushMaskPool); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.IndirectDispatchArgs); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.GenPoints); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.MaxGenPoints); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.IndirectDrawArgs); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.VoxelSize); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.MaxNodeDepth); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.ChunkVoxelDensity); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.CurrentNodeDepth); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.CurrentNodeBranchingFactor); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.CurrentNodeSize); ComputeManager.RegisterConstantId(s_computeMeshLock, Const.MeshGenerationAutoRiggingAlgorithm); } // end: mesh lock // SDF gen { s_computeSdfGen = ResourcesUtil.SdfGen; if (s_computeSdfGen == null) return false; ComputeManager.RegisterShader(s_computeSdfGen); Const.Kernel.GenerateSdf = ComputeManager.RegisterKernel(s_computeSdfGen, "generate_sdf"); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.SdfOutput); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.SdfOutputSize); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.SdfCenter); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.SdfDimension); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.Brushes); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.NumBrushes); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.Brushes); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.BrushMaterials); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.AabbTree); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.AabbRoot); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeSdfGen, Const.NoiseCacheDensity); } // end: SDF gen ComputeManager.ActivateAllShaders(); // generate noise cache { if (s_noiseCache != null) { s_noiseCache.Release(); s_noiseCache = null; } s_noiseCache = new RenderTexture(NoiseCacheDimensionInts[0], NoiseCacheDimensionInts[1], 0, RenderTextureFormat.RFloat); s_noiseCache.dimension = TextureDimension.Tex3D; s_noiseCache.volumeDepth = NoiseCacheDimensionInts[2]; s_noiseCache.enableRandomWrite = true; s_noiseCache.Create(); ComputeManager.SetTexture(Const.NoiseCache, s_noiseCache); //ComputeManager.SetTexture(Const.NoiseCache, ResourcesUtil.NoiseTexture); ComputeManager.SetInts(Const.NoiseCacheDimension, NoiseCacheDimensionInts); ComputeManager.SetFloat(Const.NoiseCacheDensity, NoiseCacheDensity); ComputeManager.SetFloats(Const.NoiseCachePeriod, NoiseCachePeriod); ComputeManager.Dispatch ( s_computeNoiseCache, Const.Kernel.GenerateNoiseCache, (s_noiseCache.width + ThreadGroupExtent - 1) / ThreadGroupExtent, (s_noiseCache.height + ThreadGroupExtent - 1) / ThreadGroupExtent, (s_noiseCache.volumeDepth + ThreadGroupExtent - 1) / ThreadGroupExtent ); ComputeManager.Deactivate(s_computeNoiseCache); } // end: generate noise cache return true; } private bool ValidateGlobalResources() { Profiler.BeginSample("ValidateGlobalResources"); if (!ValidateComputeShaders()) return false; if (s_triTableBuffer == null) { s_triTableBuffer = new ComputeBuffer(16 * 256, sizeof(int)); s_triTableBuffer.SetData(MarchingCubes.TriTable); ComputeManager.SetBuffer(Const.TriTable, s_triTableBuffer); m_needsCompute = true; } if (s_vertTableBuffer == null) { s_vertTableBuffer = new ComputeBuffer(2 * 12, sizeof(int)); s_vertTableBuffer.SetData(MarchingCubes.VertTable); ComputeManager.SetBuffer(Const.VertTable, s_vertTableBuffer); m_needsCompute = true; } if (s_triTable2dBuffer == null) { s_triTable2dBuffer = new ComputeBuffer(12 * 16, sizeof(int)); s_triTable2dBuffer.SetData(MarchingCubes.TriTable2d); ComputeManager.SetBuffer(Const.TriTable2d, s_triTable2dBuffer); m_needsCompute = true; } if (AllowSharedRWBuffers) { if (s_brushesBuffer == null) { s_brushesBuffer = new ComputeBuffer(MaxBrushes, SdfBrush.Stride); m_needsCompute = true; } if (s_brushMaterialBuffer == null) { s_brushMaterialBuffer = new ComputeBuffer(MaxBrushes, SdfBrushMaterial.Stride); m_needsCompute = true; } } if (s_dummyBuffer == null) { s_dummyBuffer = new ComputeBuffer(4, sizeof(int)); } Profiler.EndSample(); s_globalResourcesValid = true; return true; } protected virtual bool ValidateLocalResources() { Profiler.BeginSample("ValidateLocalResources (Renderer Base)"); if (AllowSharedRWBuffers) { if (m_brushesBuffer != null && m_brushesBuffer != s_brushesBuffer) { //m_brushesBuffer.Release(); Janitor.Dispose(m_brushesBuffer); m_needsCompute = true; } if (m_brushMaterialBuffer != null && m_brushMaterialBuffer != s_brushMaterialBuffer) { //m_brushMaterialBuffer.Release(); Janitor.Dispose(m_brushMaterialBuffer); m_needsCompute = true; } if (m_aabbTreeBuffer != null && m_aabbTreeBuffer != s_aabbTreeBuffer) { //m_aabbTreeBuffer.Release(); Janitor.Dispose(m_aabbTreeBuffer); m_needsCompute = true; } m_brushesBuffer = s_brushesBuffer; m_brushMaterialBuffer = s_brushMaterialBuffer; m_aabbTreeBuffer = s_aabbTreeBuffer; } else { if (m_brushesBuffer == null || m_brushesBuffer == s_brushesBuffer) { m_brushesBuffer = new ComputeBuffer(MaxBrushes, SdfBrush.Stride); m_needsCompute = true; } if (m_brushMaterialBuffer == null || m_brushMaterialBuffer == s_brushMaterialBuffer) { m_brushMaterialBuffer = new ComputeBuffer(MaxBrushes, SdfBrushMaterial.Stride); m_needsCompute = true; } if (m_aabbTreeBuffer == s_aabbTreeBuffer) { m_aabbTreeBuffer = null; } } if (m_nodeHashTableBuffer == null || m_nodeHashTableBuffer.count != NodeHashTableSize) { if (m_nodeHashTableBuffer != null) { //m_nodeHashTableBuffer.Release(); Janitor.Dispose(m_nodeHashTableBuffer); } m_nodeHashTableBuffer = new ComputeBuffer(NodeHashTableSize, VoxelHashEntry.Stride); m_needsCompute = true; } if (m_nodePoolBuffer == null || m_nodePoolBuffer.count != MaxVoxels) { if (m_nodePoolBuffer != null) { //m_nodePoolBuffer.Release(); Janitor.Dispose(m_nodePoolBuffer); } m_nodePoolBuffer = new ComputeBuffer(MaxVoxels, VoxelNode.Stride); m_needsCompute = true; } int voxelCacheSize = MaxVoxels * 2; if (UseVoxelCache) { if (m_voxelCacheIdTableBuffer == null || m_voxelCacheIdTableBuffer.count != voxelCacheSize) { if (m_voxelCacheIdTableBuffer != null) { //m_voxelCacheIdTableBuffer.Release(); //m_voxelCacheBuffer.Release(); Janitor.Dispose(m_voxelCacheIdTableBuffer); Janitor.Dispose(m_voxelCacheBuffer); } m_voxelCacheIdTableBuffer = new ComputeBuffer(voxelCacheSize, sizeof(int)); m_voxelCacheBuffer = new ComputeBuffer(voxelCacheSize, 4 * sizeof(float)); m_needsCompute = true; } } else if (m_voxelCacheBuffer != null) { //m_voxelCacheIdTableBuffer.Release(); //m_voxelCacheBuffer.Release(); Janitor.Dispose(m_voxelCacheIdTableBuffer); Janitor.Dispose(m_voxelCacheBuffer); m_voxelCacheIdTableBuffer = null; m_voxelCacheBuffer = null; } if (m_brushMaskPoolBuffer == null || m_brushMaskPoolBuffer.count != MaxBrushMasks * MaxBrushMaskInts) { if (m_brushMaskPoolBuffer != null) { //m_brushMaskPoolBuffer.Release(); Janitor.Dispose(m_brushMaskPoolBuffer); } m_brushMaskPoolBuffer = new ComputeBuffer(MaxBrushMasks * MaxBrushMaskInts, sizeof(int)); m_needsCompute = true; } int maxGenPoints = MaxGenPoints; if (m_genPointsBufferDefault == null || m_genPointsBufferDefault.count != maxGenPoints) { if (m_genPointsBufferDefault != null) { //m_numNodesAllocatedBuffer.Release(); //m_numAllocationsBuffer.Release(); //m_indirectDispatchArgsBuffer.Release(); //m_genPointsBufferDefault.Release(); //m_indirectDrawArgsBufferDefault.Release(); Janitor.Dispose(m_numNodesAllocatedBuffer); Janitor.Dispose(m_numAllocationsBuffer); Janitor.Dispose(m_indirectDispatchArgsBuffer); Janitor.Dispose(m_genPointsBufferDefault); Janitor.Dispose(m_indirectDrawArgsBufferDefault); } m_numNodesAllocatedBuffer = new ComputeBuffer(VoxelNodeDepth + 2, sizeof(int)); m_numAllocationsBuffer = new ComputeBuffer(4, sizeof(int)); m_indirectDispatchArgsBuffer = new ComputeBuffer(3, sizeof(int), ComputeBufferType.IndirectArguments); m_genPointsBufferDefault = new ComputeBuffer(maxGenPoints, GenPoint.Stride); m_indirectDrawArgsBufferDefault = new ComputeBuffer(5, sizeof(int), ComputeBufferType.IndirectArguments); m_needsCompute = true; } if (ShouldDoAutoSmoothing) { if (m_autoSmoothVertDataTableBuffer != null && m_autoSmoothVertDataTableBuffer.count != AutoSmoothVertDataTableSize) { //m_autoSmoothVertDataTableBuffer.Release(); Janitor.Dispose(m_autoSmoothVertDataTableBuffer); m_autoSmoothVertDataTableBuffer = null; } if (m_autoSmoothVertDataTableBuffer == null) { m_autoSmoothVertDataTableBuffer = new ComputeBuffer(AutoSmoothVertDataTableSize, AutoSmoothVertData.Stride); } } else { if (m_autoSmoothVertDataTableBuffer != null) { //m_autoSmoothVertDataTableBuffer.Release(); Janitor.Dispose(m_autoSmoothVertDataTableBuffer); m_autoSmoothVertDataTableBuffer = null; } } if (m_usedSharedMaterial != SharedMaterial) { m_usedSharedMaterial = SharedMaterial; m_needsCompute = true; } Profiler.EndSample(); m_localResourcesValid = true; return true; } protected static void DisposeGlobalResources() { s_globalResourcesValid = false; s_computeVoxelGen = null; if (s_triTableBuffer != null) { //s_triTableBuffer.Release(); Janitor.Dispose(s_triTableBuffer); s_triTableBuffer = null; } if (s_vertTableBuffer != null) { //s_vertTableBuffer.Release(); Janitor.Dispose(s_vertTableBuffer); s_vertTableBuffer = null; } if (s_triTable2dBuffer != null) { //s_triTable2dBuffer.Release(); Janitor.Dispose(s_triTable2dBuffer); s_triTable2dBuffer = null; } if (s_brushesBuffer != null) { //s_brushesBuffer.Release(); Janitor.Dispose(s_brushesBuffer); s_brushesBuffer = null; } if (s_brushMaterialBuffer != null) { //s_brushMaterialBuffer.Release(); Janitor.Dispose(s_brushMaterialBuffer); s_brushMaterialBuffer = null; } if (s_aabbTreeBuffer != null) { //s_aabbTreeBuffer.Release(); Janitor.Dispose(s_aabbTreeBuffer); s_aabbTreeBuffer = null; } if (s_dummyBuffer != null) { //s_dummyBuffer.Release(); Janitor.Dispose(s_dummyBuffer); s_dummyBuffer = null; } if (s_numAllocatoinsBufferInitData.IsCreated) s_numAllocatoinsBufferInitData.Dispose(); if (s_unitIndirectDispatchArgsInitData.IsCreated) s_unitIndirectDispatchArgsInitData.Dispose(); } protected void DisposeLocalResources() { m_localResourcesValid = false; if (m_brushesBuffer != null && m_brushesBuffer != s_brushesBuffer) { //m_brushesBuffer.Release(); Janitor.Dispose(m_brushesBuffer); m_brushesBuffer = null; } if (m_brushMaterialBuffer != null && m_brushMaterialBuffer != s_brushMaterialBuffer) { //m_brushMaterialBuffer.Release(); Janitor.Dispose(m_brushMaterialBuffer); m_brushMaterialBuffer = null; } if (m_aabbTreeBuffer != null && m_aabbTreeBuffer != s_aabbTreeBuffer) { //m_aabbTreeBuffer.Release(); Janitor.Dispose(m_aabbTreeBuffer); m_aabbTreeBuffer = null; } if (m_nodeHashTableBuffer != null) { //m_nodeHashTableBuffer.Release(); Janitor.Dispose(m_nodeHashTableBuffer); m_nodeHashTableBuffer = null; } if (m_nodePoolBuffer != null) { //m_nodePoolBuffer.Release(); Janitor.Dispose(m_nodePoolBuffer); m_nodePoolBuffer = null; } if (m_voxelCacheIdTableBuffer != null) { //m_voxelCacheIdTableBuffer.Release(); //m_voxelCacheBuffer.Release(); Janitor.Dispose(m_voxelCacheIdTableBuffer); Janitor.Dispose(m_voxelCacheBuffer); m_voxelCacheIdTableBuffer = null; m_voxelCacheBuffer = null; } if (m_brushMaskPoolBuffer != null) { //m_brushMaskPoolBuffer.Release(); Janitor.Dispose(m_brushMaskPoolBuffer); m_brushMaskPoolBuffer = null; } if (m_genPointsBufferDefault != null) { //m_numNodesAllocatedBuffer.Release(); //m_numAllocationsBuffer.Release(); //m_indirectDispatchArgsBuffer.Release(); //m_genPointsBufferDefault.Release(); //m_indirectDrawArgsBufferDefault.Release(); Janitor.Dispose(m_numNodesAllocatedBuffer); Janitor.Dispose(m_numAllocationsBuffer); Janitor.Dispose(m_indirectDispatchArgsBuffer); Janitor.Dispose(m_genPointsBufferDefault); Janitor.Dispose(m_indirectDrawArgsBufferDefault); m_numNodesAllocatedBuffer = null; m_numAllocationsBuffer = null; m_indirectDispatchArgsBuffer = null; m_genPointsBufferDefault = null; m_indirectDrawArgsBufferDefault = null; } if (m_autoSmoothVertDataTableBuffer != null) { //m_autoSmoothVertDataTableBuffer.Release(); Janitor.Dispose(m_autoSmoothVertDataTableBuffer); m_autoSmoothVertDataTableBuffer = null; } m_materialCloned = null; m_materialUsed = null; if (m_pendingMeshTable != null) { foreach (var pair in m_pendingMeshTable) pair.Value.Dispose(); m_pendingMeshTable.Clear(); } if (m_indirectDrawArgsInitData.IsCreated) m_indirectDrawArgsInitData.Dispose(); } #endregion // end: Resources //------------------------------------------------------------------------- #region Brushes private static readonly string MaxBrushWarningMessage = $"Maximum of {MaxBrushes} compute brushes per renderer exceeded (some brushes can count as more than one compute brushes).\n" + "You may try bumping the maximum compute brushes by following the instructions in comments above where MudRendererBase.MaxBrushes is declared, but beware of performance."; public void AddBrush(MudBrushBase brush) { if (brush.m_renderer != null) brush.m_renderer.RemoveBrush(brush); brush.m_renderer = this; ValidateAabbTree(); brush.UpdateProxies(m_aabbTree, brush.BoundsRs); m_aabbTreeDirty = true; m_aBrush.Add(brush); m_aBrushToProcess.Add(brush); s_brushMap[brush.GetFloatHash()] = brush; Assert.True(m_aBrushToProcess.Count <= MaxBrushes, MaxBrushWarningMessage); RescanBrushes(); } public void RemoveBrush(MudBrushBase brush) { #if MUDBUN_DEV Assert.True(brush.m_renderer != null, "Brush was never added."); Assert.True(brush.m_renderer == this, "Brush was not added to this renderer."); #endif ValidateAabbTree(); brush.DestroyProxies(m_aabbTree); m_aabbTreeDirty = true; brush.m_renderer = null; m_aBrush.Remove(brush); m_aBrushToProcess.Remove(brush); if (brush.IsBrushGroup) m_aBrushToProcess.Remove(brush); s_brushMap.Remove(brush.GetFloatHash()); RescanBrushes(); } public void DestroyAllBrushes() { var aBrushCopy = Brushes.ToArray(); foreach (var b in aBrushCopy) { var m = b.GetComponent(); Destroy(b); Destroy(m); } } public void DestroyAllBrushesImmediate() { var aBrushCopy = Brushes.ToArray(); foreach (var b in aBrushCopy) { var m = b.GetComponent(); DestroyImmediate(b); DestroyImmediate(m); } } public virtual void NotifyHierarchyChange() { RescanBrushes(); } private void ClearBrushes() { ValidateAabbTree(); foreach (var brush in m_aBrush) { brush.m_renderer = null; brush.DestroyProxies(m_aabbTree); } m_aBrush.Clear(); m_aBrushToProcess.Clear(); RescanBrushes(); } public void RescanBrushes() { m_needRescanBrushes = true; } public void RescanBrushesImmediate() { ClearBrushes(); ScanHierarchyRecursive(transform); ValidateBrushSetHash(); } private int ComputeBrushListHash() { int hash = 0; for (int iBrush = 0, numBrushes = m_aBrushToProcess.Count; iBrush < numBrushes; ++iBrush) { var brush = m_aBrushToProcess[iBrush]; hash = Codec.HashConcat(hash, brush.GetInstanceID()); } return hash; } private int m_brushSetHash = -1; private void ValidateBrushSetHash() { int brushListHash = ComputeBrushListHash(); if (brushListHash != m_brushSetHash) { MarkNeedsCompute(); m_brushSetHash = brushListHash; m_brushDataDirty = true; } } private int m_brushGroupDepth = 0; private void ScanHierarchyRecursive(Transform parent) { if (parent == null) return; parent.TryGetComponent(out MudBrushBase parentBrush); bool parentIsGroup = (parentBrush != null && parentBrush.IsBrushGroup && parentBrush.isActiveAndEnabled); if (parentIsGroup) { ++m_brushGroupDepth; if (m_brushGroupDepth >= MaxBrushGroupDepth) Assert.Warn($"MudBun: Exceeded maximum group depth of {MaxBrushGroupDepth}!"); } for (int iChild = 0; iChild < parent.childCount; ++iChild) { var child = parent.GetChild(iChild); // renderer blocks recursion if (child.TryGetComponent(out MudRendererBase _)) continue; if (child.TryGetComponent(out MudBrushBase brush) && brush.isActiveAndEnabled) AddBrush(brush); ScanHierarchyRecursive(child); } if (parentIsGroup) { m_aBrushToProcess.Add(parentBrush); --m_brushGroupDepth; } } private void SetUpResources() { Profiler.BeginSample("SetUpResources"); WriteResources(); BindComputeResources(); Profiler.EndSample(); } private void UpdateActivePreCompute() { if (m_doRigging) ComputeManager.Activate(s_computeMeshLock); else ComputeManager.Deactivate(s_computeMeshLock); switch (MeshingMode) { case MeshingModeEnum.MarchingCubes: ComputeManager.Activate(s_computeMarchingCubes); ComputeManager.Deactivate(s_computeDualMeshing); ComputeManager.Deactivate(s_computeSurfaceNets); ComputeManager.Deactivate(s_computeDualContouring); break; case MeshingModeEnum.DualQuads: ComputeManager.Activate(s_computeDualMeshing); ComputeManager.Deactivate(s_computeMarchingCubes); ComputeManager.Deactivate(s_computeSurfaceNets); ComputeManager.Deactivate(s_computeDualContouring); break; case MeshingModeEnum.SurfaceNets: ComputeManager.Activate(s_computeDualMeshing); ComputeManager.Activate(s_computeSurfaceNets); ComputeManager.Deactivate(s_computeMarchingCubes); ComputeManager.Deactivate(s_computeDualContouring); break; case MeshingModeEnum.DualContouring: ComputeManager.Activate(s_computeDualMeshing); ComputeManager.Activate(s_computeDualContouring); ComputeManager.Deactivate(s_computeMarchingCubes); ComputeManager.Deactivate(s_computeSurfaceNets); break; } /* if (RenderModeCategory == RenderModeCategoryEnum.RayTracedVoxels) ComputeManager.Activate(s_computeRayTracedVoxels); else */ ComputeManager.Deactivate(s_computeRayTracedVoxels); } private void UpdateActivePostCompute() { if (m_doRigging) ComputeManager.Deactivate(s_computeMeshLock); } private bool ValidateAabbTreeBuffer(ref ComputeBuffer aabbTreeBuffer) { if (m_aabbTreeBuffer != null && m_aabbTreeBuffer.count >= m_aabbTree.Capacity) return false; if (aabbTreeBuffer != null) aabbTreeBuffer.Release(); aabbTreeBuffer = new ComputeBuffer(m_aabbTree.Capacity, AabbTree.NodePod.Stride); return true; } private void ValidateRenderMaterial() { Material material = null; switch (RenderModeCategory) { case RenderModeCategoryEnum.Mesh: material = RenderMaterialMesh; break; case RenderModeCategoryEnum.Splats: material = RenderMaterialSplats; break; case RenderModeCategoryEnum.Decal: material = RenderMaterialDecal; break; /* case RenderModeCategoryEnum.RayMarchedSurface: material = RenderMaterialRayMarchedSurface; break; case RenderModeCategoryEnum.RayTracedVoxels: material = RenderMaterialRayTracedVoxels; break; */ } { Profiler.BeginSample("Copy Render Material Properties"); if (m_materialCloned != material) { m_materialUsed = new Material(material); m_materialCloned = material; if (!Application.isEditor) { m_renderMaterialDirty = true; } } if (Application.isEditor) { // need to do this constantly in editor because the user can change the referenced material // TODO: timeslice this when in play mode m_renderMaterialDirty = true; } if (RenderMaterialMode == RenderMaterialModeEnum.Dynamic) { m_renderMaterialDirty = true; } if (m_renderMaterialDirty) { m_materialUsed.CopyPropertiesFromMaterial(m_materialCloned); m_materialUsed.enableInstancing = true; m_renderMaterialDirty = false; } Profiler.EndSample(); } m_materialUsed.EnableKeyword("MUDBUN_PROCEDURAL"); switch (RenderMode) { case RenderModeEnum.QuadSplats: m_materialUsed.EnableKeyword("MUDBUN_QUAD_SPLATS"); break; default: m_materialUsed.DisableKeyword("MUDBUN_QUAD_SPLATS"); break; } } protected virtual bool ShouldHighlightBrushFromSelection(MudBrushBase brush) { return false; } private void UpdateBrushData() { if (!m_brushDataDirty && !m_doRigging) return; // need to complete queued jobs before writing to native arrays again for (int i = 0; i < m_jobCompleteQueue.Count; ++i) { m_jobCompleteQueue[i].Complete(); } m_jobCompleteQueue.Clear(); Profiler.BeginSample("UpdateBrushData"); m_numSdfBrushes = 0; m_numSdfMaterials = 0; m_sdfBrushMaterialIndexMap.Clear(); m_aBone = (m_doRigging ? new List() : null); for (int iBrush = 0, numBrushes = m_aBrushToProcess.Count; iBrush < numBrushes; ++iBrush) { var brush = m_aBrushToProcess[iBrush]; Profiler.BeginSample("Each Brush"); brush.m_iSdfBrush = m_numSdfBrushes; Profiler.BeginSample("Fill Material"); int materialIndex = -1; bool selectionHighlight = ShouldHighlightBrushFromSelection(brush); /* if (selectionHighlight) { materialIndex = m_numSdfMaterials; m_sdfBrushMaterialIndexMap[0] = materialIndex; var mat = m_aSdfBrushMaterial[m_numSdfMaterials]; brush.FillBrushMaterialData(ref mat); var color = mat.Color; color.r = Mathf.Min(1.0f, color.r + 0.1f); color.g = Mathf.Min(1.0f, color.g + 0.1f); color.b = Mathf.Min(1.0f, color.b + 0.1f); mat.Color = color; var emissionHash = mat.EmissionHash; emissionHash.r = Mathf.Min(1.0f, emissionHash.r + 0.1f); emissionHash.g = Mathf.Min(1.0f, emissionHash.g + 0.1f); emissionHash.b = Mathf.Min(1.0f, emissionHash.b + 0.1f); mat.EmissionHash = emissionHash; m_aSdfBrushMaterial[m_numSdfMaterials] = mat; ++m_numSdfMaterials; } else */ { if (brush.UsesMaterial && !m_sdfBrushMaterialIndexMap.TryGetValue(brush.MaterialHash, out materialIndex)) { materialIndex = m_numSdfMaterials; m_sdfBrushMaterialIndexMap.Add(brush.MaterialHash, materialIndex); var mat = m_aSdfBrushMaterial[m_numSdfMaterials]; brush.FillBrushMaterialData(ref mat); m_aSdfBrushMaterial[m_numSdfMaterials] = mat; ++m_numSdfMaterials; } } #if MUDBUN_DEV Assert.True(!brush.UsesMaterial || materialIndex >= 0); #endif Profiler.EndSample(); Profiler.BeginSample("Fill Compute Data"); int numNewBrushes = (!brush.IsBrushGroup || !brush.m_preChildrenFlag) ? brush.FillComputeData(m_aSdfBrush, m_numSdfBrushes, (brush.CountAsBone ? m_aBone : null)) : brush.FillComputeDataPostChildren(m_aSdfBrush, m_numSdfBrushes); Profiler.EndSample(); Profiler.BeginSample("Fill Brush Data"); float hash = brush.GetFloatHash(); for (int iNewBrush = 0; iNewBrush < numNewBrushes; ++iNewBrush) { int iSdfBrush = m_numSdfBrushes + iNewBrush; var b = m_aSdfBrush[iSdfBrush]; b.Index = iSdfBrush; if (!brush.IsBrushGroup || !brush.m_preChildrenFlag) brush.FillBrushData(ref b, iSdfBrush); else brush.FillBrushDataPostChildren(ref b, iSdfBrush); b.MaterialIndex = materialIndex; b.Hash = selectionHighlight ? -hash // encode selection highlight in negative hash sign : hash; m_aSdfBrush[iSdfBrush] = b; } Profiler.EndSample(); m_numSdfBrushes += numNewBrushes; if (brush.IsBrushGroup) brush.m_preChildrenFlag = !brush.m_preChildrenFlag; Profiler.EndSample(); } m_brushDataDirty = false; Profiler.EndSample(); } private void UpdateAabbTreeData() { if (!m_aabbTreeDirty) return; Profiler.BeginSample("UpdateAabbTree"); Aabb successorModifierBounds = Aabb.Empty; Aabb accumulatedBounds = Aabb.Empty; Aabb groupBounds = Aabb.Empty; var groupSymmetry = MudSolid.SymmetryMode.None; foreach (var brush in m_aBrushToProcess) { Aabb opBounds = brush.BoundsRs; if (brush.IsBrushGroup) { if (!brush.m_preChildrenFlag) { // begin group m_brushGroupBoundsStack.Push(groupBounds); m_accumulatedBoundsStack.Push(accumulatedBounds); m_brushGroupSymmetryStack.Push(groupSymmetry); groupBounds = Aabb.Empty; accumulatedBounds = Aabb.Empty; groupSymmetry = ((MudBrushGroup) brush).Symmetry; } else { // end group opBounds = groupBounds; opBounds.Expand(m_aabbTree.FatBoundsRadius); groupBounds = m_brushGroupBoundsStack.Pop(); accumulatedBounds = m_accumulatedBoundsStack.Pop(); groupSymmetry = m_brushGroupSymmetryStack.Pop(); } brush.m_preChildrenFlag = !brush.m_preChildrenFlag; } if (groupSymmetry != MudSolid.SymmetryMode.None) opBounds = MudSolid.SymmetryBounds(groupSymmetry, opBounds); if (opBounds.IsEmpty) { if (brush.IsBrushGroup) brush.UpdateProxies(m_aabbTree, opBounds); continue; } if (brush.IsSuccessorModifier) successorModifierBounds.Include(opBounds); opBounds.Include(successorModifierBounds); accumulatedBounds.Include(opBounds); groupBounds.Include(opBounds); if (brush.ShouldUseAccumulatedBounds) opBounds = accumulatedBounds; opBounds.Expand(brush.BoundsRsPadding); accumulatedBounds.Include(opBounds); groupBounds.Include(opBounds); brush.UpdateProxies(m_aabbTree, opBounds); } m_aabbTree.UpdatePods(); m_aabbTreeDirty = false; Profiler.EndSample(); } protected int m_numSdfBrushes; protected int m_numSdfMaterials; protected List m_aBone; private Stack m_brushGroupBoundsStack = new Stack(); private Stack m_accumulatedBoundsStack = new Stack(); private Stack m_brushGroupSymmetryStack = new Stack(); private void WriteResources() { Profiler.BeginSample("WriteResources"); Profiler.BeginSample("Brushes"); if (m_materialUsed == null) ValidateRenderMaterial(); float effectiveSurfaceShift = SurfaceShift + 1e-5f; // tiny offset to fix voxel edges perfectly lining up with zero isosurface if (RenderModeCategory == RenderModeCategoryEnum.Decal) effectiveSurfaceShift += 0.5f * VoxelSize; Profiler.BeginSample("Set Brush Data"); m_brushesBuffer.SetData(m_aSdfBrush, 0, 0, m_numSdfBrushes); Profiler.EndSample(); Profiler.BeginSample("Set Material Data"); m_brushMaterialBuffer.SetData(m_aSdfBrushMaterial, 0, 0, m_numSdfMaterials); Profiler.EndSample(); ComputeManager.SetInt(Const.NumBrushes, m_numSdfBrushes); ComputeManager.SetFloat(Const.SurfaceShift, effectiveSurfaceShift); ComputeManager.SetInt(Const.RenderMode, (int) RenderMode); ComputeManager.SetInt(Const.MeshingMode, (int) MeshingMode); ComputeManager.SetInt(Const.RayTracedVoxelMode, (int) RayTracedVoxelMode); ComputeManager.SetFloat(Const.RayTracedVoxelSizeMultiplier, RayTracedVoxelSizeMultiplier); ComputeManager.SetInt(Const.RayTracedVoxelPaddingMode, (int) RayTracedVoxelPaddingMode); ComputeManager.SetFloat(Const.RayTracedVoxelSmoothCubeNormal, RayTracedVoxelSmoothCubeNormal); ComputeManager.SetFloat(Const.RayTracedVoxelRadius, 1.0f + 0.733f * RayTracedVoxelSphereFullness); ComputeManager.SetInt(Const.RayTracedVoxelPaddingMode, (int) RayTracedVoxelPaddingMode); ComputeManager.SetFloat(Const.RayTracedVoxelInternalPaddingDistance, RayTracedVoxelInternalPaddingDistance); ComputeManager.SetFloat(Const.RayTracedVoxelSizeFadeDistance, RayTracedVoxelSizeFadeDistance); ComputeManager.SetFloat(Const.NormalDifferentiationStep, NormalDifferentialStep); ComputeManager.SetFloat(Const.NormalQuantization, NormalQuantization); ComputeManager.SetFloat(Const.Normal2dFadeDist, Normal2dFade); ComputeManager.SetFloat(Const.Normal2dStrength, Normal2dStrength); bool shouldDoAutoSmoothing = ShouldDoAutoSmoothing; ComputeManager.SetBool(Const.EnableAutoSmooth, shouldDoAutoSmoothing); ComputeManager.SetFloat(Const.AutoSmoothMaxAngle, AutoSmoothingMaxAngle * MathUtil.Deg2Rad); ComputeManager.SetBuffer(Const.AutoSmoothVertDataTable, (shouldDoAutoSmoothing && m_autoSmoothVertDataTableBuffer != null) ? m_autoSmoothVertDataTableBuffer : s_dummyBuffer); ComputeManager.SetInt(Const.AutoSmoothVertDataPoolSize, (shouldDoAutoSmoothing && m_autoSmoothVertDataTableBuffer != null) ? m_autoSmoothVertDataTableBuffer.count : 0); ComputeManager.SetBool(Const.EnableSmoothCorner, EnableSmoothCorner); ComputeManager.SetInt(Const.SmoothCornerSubdivision, SmoothCornerSubdivision); ComputeManager.SetFloat(Const.SmoothCornerNormalBlur, 0.05f * VoxelSize + SmoothCornerNormalBlur); ComputeManager.SetFloat(Const.SmoothCornerFade, SmoothCornerFade); ComputeManager.SetInt(Const.InvertNormals, (InvertNormals && !Enable2dMode ? 1 : 0)); Profiler.EndSample(); Profiler.BeginSample("BVH"); bool needComputeFromAabbTree = false; if (AllowSharedRWBuffers) { needComputeFromAabbTree = ValidateAabbTreeBuffer(ref s_aabbTreeBuffer); m_aabbTreeBuffer = s_aabbTreeBuffer; } else { needComputeFromAabbTree = ValidateAabbTreeBuffer(ref m_aabbTreeBuffer); } if (needComputeFromAabbTree) { m_needsCompute = true; } m_aabbTree.FillComputeBuffer(m_aabbTreeBuffer); m_aabbTreeBufferRoot = m_aabbTree.Root; Profiler.EndSample(); Profiler.BeginSample("Voxel Tree"); ComputeManager.SetInt(Const.NodeHashTableSize, m_nodeHashTableBuffer.count); // general allocation counters m_numAllocationsBuffer.SetData(NumAllocatoinsBufferInitData); // node pool ComputeManager.SetInt(Const.NodePoolSize, m_nodePoolBuffer.count); if (m_numAllocationsBufferInitData == null || m_numAllocationsBufferInitData.Length != m_numNodesAllocatedBuffer.count) m_numAllocationsBufferInitData = new int[m_numNodesAllocatedBuffer.count]; for (int depth = 0; depth < m_numAllocationsBufferInitData.Length; ++depth) m_numAllocationsBufferInitData[depth] = 0; m_numNodesAllocatedBuffer.SetData(m_numAllocationsBufferInitData); // voxel cache ComputeManager.SetBool(Const.UseVoxelCache, UseVoxelCache); ComputeManager.SetInt(Const.VoxelCacheSize, (m_voxelCacheBuffer != null ? m_voxelCacheBuffer.count : 0)); // brush bit masks ComputeManager.SetInt(Const.BrushMaskPoolSize, MaxBrushMasks); // dispatch args m_indirectDispatchArgsBuffer.SetData(UnitIndirectDispatchArgsInitData); Profiler.EndSample(); Profiler.BeginSample("Indirect Draw Args"); // indirect draw args m_indirectDrawArgsBufferDefault.SetData(IndirectDrawArgsInitData); if (m_indirectDrawArgsBufferOverride != null) m_indirectDrawArgsBufferOverride.SetData(IndirectDrawArgsInitData); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.RenderMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumBrushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodeHashTableSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodePoolSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaskPoolSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.Brushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaterials); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.SurfaceShift); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AabbTree); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AabbRoot); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.Enable2dMode); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.ForceAllBrushes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodeHashTable); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NodePool); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumNodesAllocated); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NumAllocations); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.BrushMaskPool); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.IndirectDispatchArgs); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.GenPoints); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.IndirectDrawArgs); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelTreeBranchingFactorsCompressed); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.VoxelNodeSizes); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.MaxNodeDepth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.ChunkVoxelDensity); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCache); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCacheDimension); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NoiseCacheDensity); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeDepth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeBranchingFactor); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.CurrentNodeSize); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.NormalDifferentiationStep); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.EnableAutoSmooth); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothMaxAngle); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothVertDataTable); ComputeManager.RegisterConstantId(s_computeVoxelGen, Const.AutoSmoothVertDataPoolSize); Profiler.EndSample(); Profiler.EndSample(); } private void BindComputeResources() { Profiler.BeginSample("BindComputeResources"); m_genPointsBufferUsedForCompute = m_genPointsBufferOverride != null ? m_genPointsBufferOverride : m_genPointsBufferDefault; m_indirectDrawArgsBufferUsedForCompute = m_indirectDrawArgsBufferOverride != null ? m_indirectDrawArgsBufferOverride : m_indirectDrawArgsBufferDefault; ComputeManager.SetBuffer(Const.Brushes, m_brushesBuffer); ComputeManager.SetBuffer(Const.BrushMaterials, m_brushMaterialBuffer); ComputeManager.SetBuffer(Const.AabbTree, m_aabbTreeBuffer); ComputeManager.SetInt(Const.AabbRoot, m_aabbTreeBufferRoot); ComputeManager.SetBuffer(Const.NodeHashTable, m_nodeHashTableBuffer); ComputeManager.SetBuffer(Const.NodePool, m_nodePoolBuffer); ComputeManager.SetBuffer(Const.NumNodesAllocated, m_numNodesAllocatedBuffer); ComputeManager.SetBuffer(Const.NumAllocations, m_numAllocationsBuffer); ComputeManager.SetBuffer(Const.VoxelCacheIdTable, UseVoxelCache ? m_voxelCacheIdTableBuffer : m_numAllocationsBuffer); ComputeManager.SetBuffer(Const.VoxelCache, UseVoxelCache ? m_voxelCacheBuffer : m_numAllocationsBuffer); ComputeManager.SetBuffer(Const.BrushMaskPool, m_brushMaskPoolBuffer); ComputeManager.SetBuffer(Const.IndirectDispatchArgs, m_indirectDispatchArgsBuffer); ComputeManager.SetBuffer(Const.GenPoints, m_genPointsBufferUsedForCompute); ComputeManager.SetBuffer(Const.IndirectDrawArgs, m_indirectDrawArgsBufferUsedForCompute); ComputeManager.SetBool(Const.Enable2dMode, Enable2dMode); ComputeManager.SetBool(Const.ForceAllBrushes, ShouldForceAllBrushes()); ComputeManager.SetFloat(Const.VoxelSize, VoxelSize); ComputeManager.SetInt(Const.VoxelTreeBranchingFactorsCompressed, VoxelTreeBranchingFactorsComrpessed); ComputeManager.SetVector(Const.VoxelNodeSizes, NodeSizesVector); ComputeManager.SetInt(Const.MaxNodeDepth, VoxelNodeDepth); ComputeManager.SetInt(Const.ChunkVoxelDensity, ChunkVoxelDensity); ComputeManager.SetInt(Const.MaxGenPoints, MaxGenPoints); ComputeManager.SetTexture(Const.NoiseCache, s_noiseCache); //ComputeManager.SetTexture(Const.NoiseCache, NoiseCache); ComputeManager.SetFloats(Const.NoiseCacheDimension, NoiseCacheDimensionFloats); ComputeManager.SetFloat(Const.NoiseCacheDensity, NoiseCacheDensity); ComputeManager.SetInt(Const.MeshGenerationAutoRiggingAlgorithm, (int) MeshGenerationAutoRiggingAlgorithm); switch (MeshingMode) { case MeshingModeEnum.SurfaceNets: ComputeManager.SetFloat(Const.SurfaceNetsDualQuadsBlend, SurfaceNetsDualQuadsBlend); /* ComputeManager.SetInt(Const.SurfaceNetsBinarySearchIterations, SurfaceNetsBinarySearchIterations); ComputeManager.SetInt(Const.SurfaceNetsGradientDescentIterations, SurfaceNetsGradientDescentIterations); ComputeManager.SetFloat(Const.SurfaceNetsGradientDescentFactor, SurfaceNetsGradientDescentFactor); */ /* if (SurfaceNetsHighAccuracyMode) { ComputeManager.SetInt(Const.SurfaceNetsBinarySearchIterations, 6); ComputeManager.SetInt(Const.SurfaceNetsGradientDescentIterations, 1); ComputeManager.SetFloat(Const.SurfaceNetsGradientDescentFactor, 1.0f); } else { ComputeManager.SetInt(Const.SurfaceNetsBinarySearchIterations, 0); ComputeManager.SetInt(Const.SurfaceNetsGradientDescentIterations, 0); ComputeManager.SetFloat(Const.SurfaceNetsGradientDescentFactor, 0.0f); } */ break; case MeshingModeEnum.DualContouring: ComputeManager.SetFloat(Const.DualContouringDualQuadsBlend, DualContouringDualQuadsBlend); ComputeManager.SetFloat(Const.DualContouringRelaxation, 0.15f + 0.85f * DualContouringRelaxation); //ComputeManager.SetInt(Const.DualContouringSolverIterations, DualContouringSolverIterations); /* ComputeManager.SetInt(Const.DualContouringBinarySearchIterations, DualContouringBinarySearchIterations); ComputeManager.SetInt(Const.DualContouringGradientDescentIterations, DualContouringGradientDescentIterations); ComputeManager.SetFloat(Const.DualContouringGradientDescentFactor, DualContouringGradientDescentFactor); */ if (DualContouringHighAccuracyMode) { ComputeManager.SetInt(Const.DualContouringSolverIterations, 0); ComputeManager.SetInt(Const.DualContouringBinarySearchIterations, 10); ComputeManager.SetInt(Const.DualContouringGradientDescentIterations, 1); ComputeManager.SetFloat(Const.DualContouringGradientDescentFactor, 1.0f); } else { ComputeManager.SetInt(Const.DualContouringSolverIterations, 0); ComputeManager.SetInt(Const.DualContouringBinarySearchIterations, 0); ComputeManager.SetInt(Const.DualContouringGradientDescentIterations, 0); ComputeManager.SetFloat(Const.DualContouringGradientDescentFactor, 0.0f); } break; } Profiler.EndSample(); } private void BindRenderResources() { Profiler.BeginSample("BindRenderResources"); if (m_materialProps == null) { m_materialProps = new MaterialPropertyBlock(); m_needsCompute = true; } m_materialProps.SetBuffer(Const.GenPoints, m_genPointsBufferDefault); m_materialProps.SetVector(Const.MasterColor, MasterColor); m_materialProps.SetVector(Const.MasterEmission, MasterEmission); m_materialProps.SetFloat(Const.MasterMetallic, MasterMetallic); m_materialProps.SetFloat(Const.MasterSmoothness, MasterSmoothness); m_materialProps.SetInt(Const.InvertNormals, (InvertNormals && !Enable2dMode ? 1 : 0)); m_materialProps.SetInt(Const.Enable2dMode, Enable2dMode ? 1 : 0); m_materialProps.SetFloat(Const.VoxelSize, VoxelSize); m_materialProps.SetInt(Const.VoxelTreeBranchingFactorsCompressed, VoxelTreeBranchingFactorsComrpessed); m_materialProps.SetVector(Const.VoxelNodeSizes, NodeSizesVector); m_materialProps.SetFloat(Const.SplatSize, SplatSize * (1.5f / VoxelDensity)); m_materialProps.SetFloat(Const.SplatSizeJitter, SplatSizeJitter); m_materialProps.SetFloat(Const.SplatNormalShift, SplatNormalShift); m_materialProps.SetFloat(Const.SplatNormalShiftJitter, SplatNormalShiftJitter); m_materialProps.SetFloat(Const.SplatColorJitter, SplatColorJitter); m_materialProps.SetFloat(Const.SplatPositionJitter, SplatPositionJitter); m_materialProps.SetFloat(Const.SplatRotationJitter, SplatRotationJitter); m_materialProps.SetFloat(Const.SplatOrientationJitter, SplatOrientationJitter); m_materialProps.SetFloat(Const.SplatOriginalNormalBlend, SplatOriginalNormalBlend); m_materialProps.SetFloat(Const.SplatJitterNoisiness, SplatJitterNoisiness); m_materialProps.SetFloat(Const.SplatCameraFacing, SplatCameraFacing); m_materialProps.SetInt(Const.SplatNormalsMatchCameraFacing, SplatNormalsMatchCameraFacing ? 1 : 0); m_materialProps.SetInt(Const.SplatShadowsMatchCameraFacing, SplatShadowsMatchCameraFacing ? 1 : 0); m_materialProps.SetFloat(Const.SplatScreenSpaceFlattening, SplatScreenSpaceFlattening); //m_materialProps.SetFloat(Const.SplatSmoothNormalBlend, SplatSmoothNormalBlend); Vector3 scale = transform.localToWorldMatrix.lossyScale; m_materialProps.SetInt(Const.ScaleSign, (scale.x * scale.y * scale.z) >= 0.0f ? 1 : -1); m_materialProps.SetMatrix(Const.LocalToWorld, transform.localToWorldMatrix); m_materialProps.SetMatrix(Const.LocalToWorldIt, transform.localToWorldMatrix.inverse.transpose); m_materialProps.SetVector(Const.LocalToWorldScale, transform.localScale); m_materialProps.SetMatrix(Const.WorldToLocal, transform.worldToLocalMatrix); m_materialProps.SetMatrix(Const.WorldToLocalIt, transform.worldToLocalMatrix.inverse.transpose); m_materialProps.SetInt(Const.RenderMode, (int) RenderMode); m_materialProps.SetInt(Const.MeshingMode, (int) MeshingMode); m_materialProps.SetInt(Const.RayTracedVoxelMode, (int) RayTracedVoxelMode); m_materialProps.SetFloat(Const.RayTracedVoxelSizeMultiplier, RayTracedVoxelSizeMultiplier); m_materialProps.SetFloat(Const.RayTracedVoxelSmoothCubeNormal, RayTracedVoxelSmoothCubeNormal); m_materialProps.SetFloat(Const.RayTracedVoxelRadius, 1.0f + 0.733f * RayTracedVoxelSphereFullness); m_materialProps.SetInt(Const.RayTracedVoxelPaddingMode, (int) RayTracedVoxelPaddingMode); m_materialProps.SetInt(Const.RayTracedVoxelPaddingMode, (int)RayTracedVoxelPaddingMode); m_materialProps.SetFloat(Const.RayTracedVoxelInternalPaddingDistance, RayTracedVoxelInternalPaddingDistance); m_materialProps.SetFloat(Const.RayTracedVoxelSizeFadeDistance, RayTracedVoxelSizeFadeDistance); m_materialProps.SetFloat(Const.NormalQuantization, NormalQuantization); m_materialProps.SetFloat(Const.Normal2dFadeDist, Normal2dFade); m_materialProps.SetFloat(Const.Normal2dStrength, Normal2dStrength); m_materialProps.SetFloat(Const.AutoSmoothMaxAngle, EnableAutoSmoothing ? AutoSmoothingMaxAngle * MathUtil.Deg2Rad : -1.0f); //if (MaterialNeedsSdfProperties(m_materialUsed)) switch (RenderModeCategory) { case RenderModeCategoryEnum.Decal: //case RenderModeCategoryEnum.RayMarchedSurface: //case RenderModeCategoryEnum.RayTracedVoxels: m_materialProps.SetBuffer(Const.NodeHashTable, m_nodeHashTableBuffer); m_materialProps.SetInt(Const.NodeHashTableSize, m_nodeHashTableBuffer.count); m_materialProps.SetBuffer(Const.NodePool, m_nodePoolBuffer); m_materialProps.SetBuffer(Const.VoxelCacheIdTable, UseVoxelCache ? m_voxelCacheIdTableBuffer : m_numAllocationsBuffer); m_materialProps.SetBuffer(Const.VoxelCache, UseVoxelCache ? m_voxelCacheBuffer : m_numAllocationsBuffer); m_materialProps.SetBuffer(Const.BrushMaskPool, m_brushMaskPoolBuffer); m_materialProps.SetBuffer(Const.Brushes, m_brushesBuffer); m_materialProps.SetBuffer(Const.BrushMaterials, m_brushMaterialBuffer); m_materialProps.SetInt(Const.NumBrushes, m_numSdfBrushes); m_materialProps.SetBuffer(Const.AabbTree, m_aabbTreeBuffer); m_materialProps.SetInt(Const.AabbRoot, m_aabbTreeBufferRoot); m_materialProps.SetTexture(Const.NoiseCache, NoiseCache); m_materialProps.SetVector(Const.NoiseCacheDimension, new Vector4(NoiseCacheDimensionFloats[0], NoiseCacheDimensionFloats[1], NoiseCacheDimensionFloats[2])); m_materialProps.SetFloat(Const.NoiseCacheDensity, NoiseCacheDensity); m_materialProps.SetFloat(Const.SurfaceShift, SurfaceShift); //if (MaterialNeedsRayMarchingProperties(m_materialUsed)) if (false /* RenderModeCategory == RenderModeCategoryEnum.RayMarchedSurface */) { /* m_materialProps.SetInt(Const.MaxRayMarchSteps, MaxRayMarchSteps); m_materialProps.SetFloat(Const.RayMarchHitDistance, 1e-1f * Mathf.Pow(1e-1f, 2.0f * RayMarchAccuracy)); m_materialProps.SetFloat(Const.RayMarchMaxRayDistance, RayMarchMaxRayDistance); */ /* m_materialProps.SetFloat(Const.RayMarchStepSize, RayMarchDistance / Mathf.Max(1, MaxRayMarchSteps)); m_materialProps.SetFloat(Const.RayMarchStepSize, RayMarchStepSize); m_materialProps.SetInt(Const.NumLightMarchSteps, NumLightMarchSteps); m_materialProps.SetFloat(Const.RayMarchVolumeDensity, RayMarchVolumeDensity); if (RayMarchLight != null) { Vector3 lightPos = RayMarchLight.transform.position; m_materialProps.SetVector(Const.RayMarchLightPositionType, new Vector4(lightPos.x, lightPos.y, lightPos.z, (int) RayMarchLight.type)); m_materialProps.SetVector(Const.RayMarchLightDirection, RayMarchLight.transform.forward); } else { m_materialProps.SetVector(Const.RayMarchLightPositionType, new Vector4(0.0f, 0.0f, 0.0f, (int) LightType.Directional)); m_materialProps.SetVector(Const.RayMarchLightDirection, Vector3.down); } m_materialProps.SetVector(Const.RayMarchAbsorption, new Vector4(RayMarchVolumeAbsorption, RayMarchLightAbsorption)); m_materialProps.SetFloat(Const.RayMarchDarknesThreshold, RayMarchDarknesThreshold); m_materialProps.SetFloat(Const.RayMarchTransmittanceCurve, RayMarchTransmittanceCurve); m_materialProps.SetFloat(Const.RayMarchNoiseEdgeFade, RayMarchNoiseEdgeFade); m_materialProps.SetFloat(Const.RayMarchNoiseThreshold, UseRayMarchNoise ? RayMarchNoiseThreshold : 0.0f); m_materialProps.SetVector(Const.RayMarchNoiseScrollSpeed, RayMarchNoiseScrollSpeed); m_materialProps.SetVector(Const.RayMarchNoiseBaseOctaveSize, RayMarchNoiseBaseOctaveSize); m_materialProps.SetInt(Const.RayMarchNoiseNumOctaves, RayMarchNoiseNumOctaves); m_materialProps.SetFloat(Const.RayMarchNoiseOctaveOffsetFactor, RayMarchNoiseOctaveOffsetFactor); */ } break; } Profiler.EndSample(); } #endregion // end: Brushes //------------------------------------------------------------------------- #region Core public ComputeModeEnum ComputeMode = ComputeModeEnum.Auto; public TimeSliceModeEnum TimeSliceMode = TimeSliceModeEnum.ByPeriodAutoOffset; [Min(1)] public int TimeSliceFrames = 3; public int TimeSliceFramesOffset = 0; [Min(0.0001f)] public float TimeSlicePeriod = 0.05f; public float TimeSliceTimeOffset = 0; private float m_lastUpdateTimeSliceTime = -1.0f; private int m_rendererIndex = -1; private void SetComputeNodeDepth(int depth) { ComputeManager.SetInt(Const.CurrentNodeDepth, depth); ComputeManager.SetInt(Const.CurrentNodeBranchingFactor, (depth < VoxelNodeDepth ? VoxelTreeBranchingFactors[depth] : 0)); ComputeManager.SetFloat(Const.CurrentNodeSize, NodeSizes[depth]); } private void ComputeGpu() { Profiler.BeginSample("Compute (GPU)"); UpdateActivePreCompute(); SetUpResources(); Profiler.BeginSample("Core Dispatch"); ComputeManager.Dispatch ( s_computeVoxelGen, Const.Kernel.ClearVoxelHashTable, Mathf.Max(1, (m_nodeHashTableBuffer.count + ClearThreadGroupSize - 1) / ClearThreadGroupSize), 1, 1 ); if (ShouldDoAutoSmoothing) { ComputeManager.Dispatch ( s_computeVoxelGen, Const.Kernel.ClearAutoSmoothVertDataTable, Mathf.Max(1, (m_autoSmoothVertDataTableBuffer.count + ClearThreadGroupSize - 1) / ClearThreadGroupSize), 1, 1 ); } if (UseVoxelCache) { ComputeManager.Dispatch ( s_computeVoxelGen, Const.Kernel.ClearVoxelCache, Mathf.Max(1, (m_voxelCacheIdTableBuffer.count + ClearThreadGroupSize - 1) / ClearThreadGroupSize), 1, 1 ); } SetComputeNodeDepth(0); ComputeManager.Dispatch ( s_computeVoxelGen, Const.Kernel.RegisterTopNodes, Mathf.Max(1, (m_numSdfBrushes + ThreadGroupSize - 1) / ThreadGroupSize), 1, 1 ); for (int depth = 0; depth < VoxelNodeDepth; ++depth) { SetComputeNodeDepth(depth); ComputeManager.Dispatch(s_computeVoxelGen, Const.Kernel.UpdateBranchingIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeVoxelGen, Const.Kernel.AllocateChildNodes, m_indirectDispatchArgsBuffer); } SetComputeNodeDepth(VoxelNodeDepth); ComputeManager.Dispatch(s_computeVoxelGen, Const.Kernel.UpdateVoxelIndirectDispatchArgs, 1, 1, 1); switch (RenderModeCategory) { case RenderModeCategoryEnum.Mesh: case RenderModeCategoryEnum.Splats: case RenderModeCategoryEnum.Decal: switch (MeshingMode) { case MeshingModeEnum.MarchingCubes: switch (RenderMode) { case RenderModeEnum.FlatMesh: if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateFlatMarchingCubesMesh2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateFlatMarchingCubesMesh, m_indirectDispatchArgsBuffer); if (ShouldDoAutoSmoothing) { ComputeManager.Dispatch(s_computeMarchingCubes, Const.Kernel.UpdateMarchingCubesAutoSmoothIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.MarchingCubesUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.MarchingCubesComputeAutoSmooth, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.SmoothMesh: if (ShouldDoAutoSmoothing) { ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateFlatMarchingCubesMesh, m_indirectDispatchArgsBuffer); ComputeManager.Dispatch(s_computeMarchingCubes, Const.Kernel.UpdateMarchingCubesAutoSmoothIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.MarchingCubesUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.MarchingCubesComputeAutoSmooth, m_indirectDispatchArgsBuffer); } else { if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateSmoothMarchingCubesMesh2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateSmoothMarchingCubesMesh, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.CircleSplats: case RenderModeEnum.QuadSplats: if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateMarchingCubesSplats2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeMarchingCubes, Const.Kernel.GenerateMarchingCubesSplats, m_indirectDispatchArgsBuffer); break; } break; case MeshingModeEnum.DualQuads: if (Enable2dMode) { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads2d, m_indirectDispatchArgsBuffer); } else { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads, m_indirectDispatchArgsBuffer); switch (RenderMode) { case RenderModeEnum.FlatMesh: // do nothing break; case RenderModeEnum.SmoothMesh: ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.UpdateDualMeshingIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothMeshNormal, m_indirectDispatchArgsBuffer); break; } } break; case MeshingModeEnum.SurfaceNets: if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads, m_indirectDispatchArgsBuffer); ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.UpdateDualMeshingIndirectDispatchArgs, 1, 1, 1); if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeSurfaceNets, Const.Kernel.SurfaceNetsMovePoint2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeSurfaceNets, Const.Kernel.SurfaceNetsMovePoint, m_indirectDispatchArgsBuffer); switch (RenderMode) { case RenderModeEnum.FlatMesh: if (ShouldDoAutoSmoothing) { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingComputeAutoSmooth, m_indirectDispatchArgsBuffer); } else { if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingFlatMeshNormal2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingFlatMeshNormal, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.SmoothMesh: if (ShouldDoAutoSmoothing) { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingComputeAutoSmooth, m_indirectDispatchArgsBuffer); } else { if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothMeshNormal2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothMeshNormal, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.CircleSplats: case RenderModeEnum.QuadSplats: ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.UpdateDualMeshingSplatsIndirectArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.ConvertDualMeshingSplats, m_indirectDispatchArgsBuffer); break; } break; case MeshingModeEnum.DualContouring: if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.GenerateDualQuads, m_indirectDispatchArgsBuffer); ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.UpdateDualMeshingIndirectDispatchArgs, 1, 1, 1); if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualContouring, Const.Kernel.DualContouringMovePoint2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualContouring, Const.Kernel.DualContouringMovePoint, m_indirectDispatchArgsBuffer); switch (RenderMode) { case RenderModeEnum.FlatMesh: if (ShouldDoAutoSmoothing) { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingComputeAutoSmooth, m_indirectDispatchArgsBuffer); if (EnableSmoothCorner) { ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateSmoothCornerIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothCorner, m_indirectDispatchArgsBuffer); } } else { if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingFlatMeshNormal2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingFlatMeshNormal, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.SmoothMesh: if (ShouldDoAutoSmoothing) { ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateAutoSmooth, m_indirectDispatchArgsBuffer); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingComputeAutoSmooth, m_indirectDispatchArgsBuffer); if (EnableSmoothCorner) { ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.DualMeshingUpdateSmoothCornerIndirectDispatchArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothCorner, m_indirectDispatchArgsBuffer); } } else { if (Enable2dMode) ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothMeshNormal2d, m_indirectDispatchArgsBuffer); else ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.DualMeshingSmoothMeshNormal, m_indirectDispatchArgsBuffer); } break; case RenderModeEnum.CircleSplats: case RenderModeEnum.QuadSplats: ComputeManager.Dispatch(s_computeDualMeshing, Const.Kernel.UpdateDualMeshingSplatsIndirectArgs, 1, 1, 1); ComputeManager.DispatchIndirect(s_computeDualMeshing, Const.Kernel.ConvertDualMeshingSplats, m_indirectDispatchArgsBuffer); break; } break; } break; // done in voxel gen /* case RenderModeCategoryEnum.RayTracedVoxels: ComputeManager.Dispatch(s_computeRayTracedVoxels, Const.Kernel.UpdateRayTracedVoxelIndirectDispatchArgs, 1, 1, 1); switch(RayTracedVoxelMode) { case RayTracedVoxelModeEnum.CubesFaceted: ComputeManager.DispatchIndirect(s_computeRayTracedVoxels, Const.Kernel.ComputeRayTracedVoxelGenPoints, m_indirectDispatchArgsBuffer); break; case RayTracedVoxelModeEnum.CubesFlat: ComputeManager.DispatchIndirect(s_computeRayTracedVoxels, Const.Kernel.ComputeRayTracedVoxelGenPointsWithNormals, m_indirectDispatchArgsBuffer); break; } break; */ } Profiler.EndSample(); if (MudBun.IsFreeVersion) { if (LockMeshIntermediateState != LockMeshIntermediateStateEnum.Idle && m_indirectDrawArgsBufferUsedForCompute != null) { int[] aIndirectDrawArgs = new int[5]; m_indirectDrawArgsBufferUsedForCompute.GetData(aIndirectDrawArgs); aIndirectDrawArgs[0] = Mathf.Min(aIndirectDrawArgs[0], 3 * MaxMeshGenerationTrianglesFreeVersion); m_indirectDrawArgsBufferUsedForCompute.SetData(aIndirectDrawArgs); } } if (m_doRigging) { Profiler.BeginSample("Rigging Dispatch"); ComputeManager.SetBuffer(Const.IndirectDrawArgs, m_indirectDrawArgsBufferUsedForCompute); ComputeManager.SetBuffer(Const.GenPoints, m_genPointsBufferUsedForCompute); ComputeManager.SetBuffer(Const.BrushMaterials, m_brushMaterialBuffer); ComputeManager.SetBuffer(Const.Brushes, m_brushesBuffer); ComputeManager.SetTexture(Const.NoiseCache, NoiseCache); ComputeManager.SetInt(Const.NumBrushes, m_numSdfBrushes); int[] aIndirectDrawArgs = new int[5]; m_indirectDrawArgsBufferUsedForCompute.GetData(aIndirectDrawArgs); ComputeManager.Dispatch ( s_computeMeshLock, Const.Kernel.RigBones, (aIndirectDrawArgs[0] + ThreadGroupSize - 1) / ThreadGroupSize, 1, 1 ); Profiler.EndSample(); } // only use buffer overrides once if (m_genPointsBufferOverride != null) m_genPointsBufferOverride = null; if (m_indirectDrawArgsBufferOverride != null) m_indirectDrawArgsBufferOverride = null; UpdateActivePostCompute(); Profiler.EndSample(); } private void ComputeCpu() { Profiler.BeginSample("Compute (CPU)"); // TODO Profiler.EndSample(); } private Mesh m_renderBoxProxy; private void Render() { if (IsEditorBusy()) return; if (m_indirectDrawArgsBufferDefault == null) return; if (!s_globalResourcesValid || !m_localResourcesValid) return; Profiler.BeginSample("Render"); ValidateRenderMaterial(); BindRenderResources(); Aabb renderBounds = RenderBounds; if (renderBounds.IsEmpty) { renderBounds.Min = -Vector3.one; renderBounds.Max = Vector3.one; } switch (RenderGeometryType) { case RenderGeometryTypeEnum.Mesh: case RenderGeometryTypeEnum.Chunks: Graphics.DrawProceduralIndirect ( m_materialUsed, new Bounds(renderBounds.Center, renderBounds.Size), MeshTopology.Triangles, m_indirectDrawArgsBufferDefault, 0, null, m_materialProps, CastShadows, ReceiveShadows, gameObject.layer ); break; case RenderGeometryTypeEnum.BoxProxy: MeshUtil.UpdateRenderBoxProxy(ref m_renderBoxProxy, renderBounds); Graphics.DrawMesh ( m_renderBoxProxy, Matrix4x4.identity, m_materialUsed, gameObject.layer, null, 0, m_materialProps, CastShadows, ReceiveShadows ); break; } Profiler.EndSample(); } #endregion // end: Core } }