/******************************************************************************/ /* Project - MudBun Publisher - Long Bunny Labs http://LongBunnyLabs.com Author - Ming-Lun "Allen" Chou http://AllenChou.net */ /******************************************************************************/ #pragma kernel generate_dual_quads #pragma kernel generate_dual_quads_2d #pragma kernel update_dual_meshing_indirect_dispatch_args #pragma kernel dual_meshing_flat_mesh_normal #pragma kernel dual_meshing_smooth_mesh_normal #pragma kernel dual_meshing_flat_mesh_normal_2d #pragma kernel dual_meshing_smooth_mesh_normal_2d #pragma kernel dual_meshing_update_auto_smooth #pragma kernel dual_meshing_compute_auto_smooth #pragma kernel dual_meshing_udpate_auto_smooth_SmoothCorner_indirect_dispatch_args #pragma kernel dual_meshing_auto_smooth_smooth_corner #pragma kernel update_dual_meshing_splats_indirect_args #pragma kernel convert_dual_meshing_splats #include "../../Shader/ComputeCommon.cginc" #include "../../Shader/AutoSmoothFuncs.cginc" #include "../../Shader/BrushFuncs.cginc" #include "../../Shader/DualMeshingFuncs.cginc" #include "../../Shader/GenPointDefs.cginc" #include "../../Shader/IndirectArgsDefs.cginc" #include "../../Shader/Math/Codec.cginc" #include "../../Shader/Math/MathConst.cginc" #include "../../Shader/MeshingModeDefs.cginc" #include "../../Shader/NormalFuncs.cginc" #include "../../Shader/RenderModeDefs.cginc" [numthreads(kThreadGroupSize, 1, 1)] void generate_dual_quads(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_ALL) || defined(MUDBUN_FAST_ITERATION) return; #endif uint iNode = uint(id.x); if (iNode >= uint(aNumNodesAllocated[currentNodeDepth + 1])) return; for (int i = 1; i <= currentNodeDepth; ++i) iNode += aNumNodesAllocated[i]; if (iNode >= nodePoolSize) return; float3 nodeCenter = nodePool[iNode].center; float halfNodeSize = 0.5f * voxelSize; float halfNodeSizeInv = 1.0f / halfNodeSize; float3 minCorner = nodeCenter - halfNodeSize; int iBrushMask = get_brush_mask_index(iNode); float3 aAxis0[3] = { kUnitX, kUnitY, kUnitZ }; float3 aAxis1[3] = { kUnitY, kUnitZ, kUnitX }; float3 aAxis2[3] = { kUnitZ, kUnitX, kUnitY }; float3 aCornerOffset[3] = { float3(voxelSize, 0.0f, 0.0f), float3(0.0f, voxelSize, 0.0f), float3(0.0f, 0.0f, voxelSize), }; SdfBrushMaterial mat = init_brush_material(); SdfBrushMaterial matTemp = init_brush_material(); float3 aSamplePoint[8] = { nodeCenter, minCorner, minCorner + aCornerOffset[0], minCorner + aCornerOffset[1], minCorner + aCornerOffset[2], minCorner + aCornerOffset[0] + 1e-2f * aAxis0[0], minCorner + aCornerOffset[1] + 1e-2f * aAxis0[1], minCorner + aCornerOffset[2] + 1e-2f * aAxis0[2], }; float aRes[8]; [loop] for (int iSample = 0; iSample < 8; ++iSample) { if (iSample < 5 || aRes[1] == 0.0f // min corner res || aRes[clamp(iSample - 3, 0, 7)] == 0.0f) // axis corner res) { // doubles generated assemblies, but gets better performance in return aRes[iSample] = sdf_masked_brushes(aSamplePoint[iSample], iBrushMask, matTemp); if (iSample == 0) mat = matTemp; } } bool emitSplats = meshingMode == kMeshingModeDualQuads && (renderMode == kRenderModeCircleSplats || renderMode == kRenderModeQuadSplats); if (emitSplats && renderMode == kRenderModeQuadSplats) mat.metallicSmoothnessSizeTightness.z *= 0.70711f; float centerRes = aRes[0]; SdfBrushMaterialCompressed packedMat = pack_material(mat); float minCornerRes = aRes[1]; int iGenPoint = 0; float aCornerRes[3] = { aRes[2], aRes[3], aRes[4] }; float aMinCornerDeltaRes[3] = { aRes[5], aRes[6], aRes[7] }; [loop] for (int iAxis = 0; iAxis < 3; ++iAxis) { float3 axis = aAxis0[iAxis]; float3 corner = minCorner + aCornerOffset[iAxis]; float cornerRes = aCornerRes[iAxis]; float s = sign(minCornerRes * cornerRes); if (s <= 0.0f) { // quad center & extent vectors float3 c = 0.5f * (minCorner + corner); float3 h1 = halfNodeSize * aAxis1[iAxis]; float3 h2 = halfNodeSize * (minCornerRes <= 0.0f ? 1 : -1) * aAxis2[iAxis]; float packedNorm = pack_normal((minCornerRes <= 0.0f ? 1 : -1) * axis * (invertNormals ? -1.0f : 1.0f)); if (s == 0.0f) { float nRes = aMinCornerDeltaRes[iAxis]; packedNorm = pack_normal((dot(nRes - minCornerRes, axis) > 0.0f ? axis : -axis) * (invertNormals ? -1.0f : 1.0f)); } if (!emitSplats) { // regular dual quads InterlockedAdd(indirectDrawArgs[0], 6, iGenPoint); float3 aQuadVertOffset[2][6] = { { - h1 - h2, + h1 - h2, + h1 + h2, - h1 - h2, + h1 + h2, - h1 + h2, }, { + h1 - h2, + h1 + h2, - h1 + h2, + h1 - h2, - h1 + h2, - h1 - h2, }, }; float3 qCenter = round((nodeCenter - 0.5f * voxelSize) / voxelSize); int iaQuadVertOffset = 0;//(uint(int(qCenter.x + qCenter.y + qCenter.z) + 0x80000000) % 2 == 0) ? 0 : 1; for (int iVert = 0; iVert < 6; ++iVert, ++iGenPoint) { float3 pos = c + aQuadVertOffset[iaQuadVertOffset][iVert]; pos = round(pos * halfNodeSizeInv) * halfNodeSize; // quantize vertices to prevent seams aGenPoint[iGenPoint].posNorm = float4(pos, packedNorm); aGenPoint[iGenPoint].iBrushMask = iBrushMask; aGenPoint[iGenPoint].material = packedMat; aGenPoint[iGenPoint].vertId = auto_smooth_vert_data_id(pos); aGenPoint[iGenPoint].sdfValue = surfaceShift; } } else { // splats int iVertBase; switch (renderMode) { case kRenderModeCircleSplats: InterlockedAdd(indirectDrawArgs[0], 3, iVertBase); iGenPoint = uint(iVertBase) / 3; break; case kRenderModeQuadSplats: InterlockedAdd(indirectDrawArgs[0], 6, iVertBase); iGenPoint = uint(iVertBase) / 6; break; } aGenPoint[iGenPoint].posNorm = float4(c, packedNorm); aGenPoint[iGenPoint].iBrushMask = iBrushMask; aGenPoint[iGenPoint].material = packedMat; aGenPoint[iGenPoint].sdfValue = surfaceShift; } } } } [numthreads(kThreadGroupSize, 1, 1)] void generate_dual_quads_2d(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_ALL) || defined(MUDBUN_FAST_ITERATION) return; #endif uint iNode = uint(id.x); if (iNode >= uint(aNumNodesAllocated[currentNodeDepth + 1])) return; for (int i = 1; i <= currentNodeDepth; ++i) iNode += aNumNodesAllocated[i]; if (iNode >= nodePoolSize) return; float3 nodeCenter = nodePool[iNode].center; float halfNodeSize = 0.5f * voxelSize; int iBrushMask = get_brush_mask_index(iNode); SdfBrushMaterial mat = init_brush_material(); float d = sdf_masked_brushes(nodeCenter, iBrushMask, mat); if (d > 0.0f) return; bool emitSplats = meshingMode == kMeshingModeDualQuads && (renderMode == kRenderModeCircleSplats || renderMode == kRenderModeQuadSplats); if (emitSplats && renderMode == kRenderModeQuadSplats) mat.metallicSmoothnessSizeTightness.z *= 0.70711f; SdfBrushMaterialCompressed packedMat = pack_material(mat); float3 n = float3(0.0f, 0.0f, -1.0f); float packedNorm = pack_normal(n); int iGenPoint = 0; { // quad center & extent vectors float3 h1 = float3(halfNodeSize, 0.0f, 0.0f); float3 h2 = float3(0.0f, halfNodeSize, 0.0f); if (!emitSplats) { // regular dual quads InterlockedAdd(indirectDrawArgs[0], 6, iGenPoint); float3 aQuadVertOffset[2][3] = { { - h1 - h2, + h1 + h2, + h1 - h2 }, { - h1 - h2, - h1 + h2, + h1 + h2 }, }; for (int iTri = 0; iTri < 2; ++iTri) { for (int iVert = 0; iVert < 3; ++iVert, ++iGenPoint) { float3 pos = nodeCenter + aQuadVertOffset[iTri][iVert]; float cornerRes = sdf_masked_brushes(pos, iBrushMask, mat); if (renderMode == kRenderModeSmoothMesh) { packedMat = pack_material(mat); } aGenPoint[iGenPoint].posNorm = float4(pos, packedNorm); aGenPoint[iGenPoint].iBrushMask = iBrushMask; aGenPoint[iGenPoint].material = packedMat; aGenPoint[iGenPoint].sdfValue = cornerRes + surfaceShift; } } } else { // splats int iVertBase; switch (renderMode) { case kRenderModeCircleSplats: InterlockedAdd(indirectDrawArgs[0], 3, iVertBase); iGenPoint = uint(iVertBase) / 3; break; case kRenderModeQuadSplats: InterlockedAdd(indirectDrawArgs[0], 6, iVertBase); iGenPoint = uint(iVertBase) / 6; break; } aGenPoint[iGenPoint].posNorm = float4(nodeCenter, packedNorm); aGenPoint[iGenPoint].iBrushMask = iBrushMask; aGenPoint[iGenPoint].material = packedMat; aGenPoint[iGenPoint].sdfValue = d + surfaceShift; } } } [numthreads(1, 1, 1)] void update_dual_meshing_indirect_dispatch_args(int3 id : SV_DispatchThreadID) { indirectDispatchArgs[0] = max(1, uint(indirectDrawArgs[0] + kThreadGroupSize - 1) / kThreadGroupSize); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_flat_mesh_normal(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_FLAT_MESH) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); uint iTriBase = iGenPoint - (iGenPoint % 3); float3 pos0 = aGenPoint[iTriBase ].posNorm.xyz; float3 pos1 = aGenPoint[iTriBase + 1].posNorm.xyz; float3 pos2 = aGenPoint[iTriBase + 2].posNorm.xyz; float3 v01 = pos1 - pos0; float3 v02 = pos2 - pos0; float3 n = normalize_safe(cross(v01, v02), 0.0f); float pn = pack_normal(n * (invertNormals ? -1.0f : 1.0f)); aGenPoint[iGenPoint].posNorm.w = pn; } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_smooth_mesh_normal(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_SMOOTH_MESH) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); float3 pos = aGenPoint[iGenPoint].posNorm.xyz; int iBrushMask = aGenPoint[iGenPoint].iBrushMask; SdfBrushMaterial mat; sdf_masked_brushes(pos, iBrushMask, mat); float3 n; SDF_NORMAL(n, pos, sdf_masked_brushes, iBrushMask, normalDifferentiationStep); aGenPoint[iGenPoint].posNorm.w = pack_normal(n * (invertNormals ? -1.0f : 1.0f)); aGenPoint[iGenPoint].material = pack_material(mat); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_flat_mesh_normal_2d(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_FLAT_MESH) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); uint iTriBase = iGenPoint - (iGenPoint % 3); float3 pos0 = aGenPoint[iTriBase].posNorm.xyz; float3 pos1 = aGenPoint[iTriBase + 1].posNorm.xyz; float3 pos2 = aGenPoint[iTriBase + 2].posNorm.xyz; float3 pos = (pos0 + pos1 + pos2) * 0.333333333f; int iBrushMask = aGenPoint[iGenPoint].iBrushMask; float3 normal2d; SDF_NORMAL_2D(normal2d, pos, sdf_masked_brushes, iBrushMask, normalDifferentiationStep); SdfBrushMaterial mat; float d = sdf_masked_brushes(pos, iBrushMask, mat); float3 n = normal_2d_blend(normal2d, d); aGenPoint[iGenPoint].posNorm.w = pack_normal(n); aGenPoint[iGenPoint].norm2d = pack_normal(normal2d); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_smooth_mesh_normal_2d(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_SMOOTH_MESH) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); float3 pos = aGenPoint[iGenPoint].posNorm.xyz; int iBrushMask = aGenPoint[iGenPoint].iBrushMask; SdfBrushMaterial mat; float d = sdf_masked_brushes(pos, iBrushMask, mat); float3 norm2d; SDF_NORMAL_2D(norm2d, pos, sdf_masked_brushes, iBrushMask, normalDifferentiationStep); float3 n = normal_2d_blend(norm2d, d); aGenPoint[iGenPoint].posNorm.w = pack_normal(n); aGenPoint[iGenPoint].material = pack_material(mat); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_update_auto_smooth(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_ALL) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); uint iTriBase = iGenPoint - (iGenPoint % 3); float3 pos0 = aGenPoint[iTriBase].posNorm.xyz; float3 pos1 = aGenPoint[iTriBase + 1].posNorm.xyz; float3 pos2 = aGenPoint[iTriBase + 2].posNorm.xyz; float3 v01 = pos1 - pos0; float3 v02 = pos2 - pos0; float3 c = cross(v01, v02); float3 n = normalize_safe(c, 0.0f); float pn = pack_normal(n * (invertNormals ? -1.0f : 1.0f)); float area = length(c); aGenPoint[iGenPoint].posNorm.w = pn; update_auto_smooth_vert_data(aGenPoint[iGenPoint].vertId, pn, area); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_compute_auto_smooth(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_ALL) || defined(MUDBUN_FAST_ITERATION) return; #endif if (id.x >= indirectDrawArgs[0]) return; uint iGenPoint = uint(id.x); float3 pos = aGenPoint[iGenPoint].posNorm.xyz; int iBrushMask = aGenPoint[iGenPoint].iBrushMask; SdfBrushMaterial mat; sdf_masked_brushes(pos, iBrushMask, mat); float3 autoSmoothNormal = compute_auto_smooth_normal(aGenPoint[iGenPoint].vertId, unpack_normal(aGenPoint[iGenPoint].posNorm.w)); bool atSmoothEdge = false; if (enableSmoothCorner) { float3 blurredNormal; SDF_NORMAL(blurredNormal, aGenPoint[iGenPoint].posNorm.xyz, sdf_masked_brushes, iBrushMask, smoothCornerNormalBlur); atSmoothEdge = abs(angle_between(blurredNormal, autoSmoothNormal)) < 0.25f * autoSmoothMaxAngle; } aGenPoint[iGenPoint].posNorm.w = pack_normal(autoSmoothNormal); aGenPoint[iGenPoint].atSmoothEdge = int(atSmoothEdge); aGenPoint[iGenPoint].material = pack_material(mat); //aGenPoint[iGenPoint].material.color = pack_rgba(float4(n, 1.0f)); } [numthreads(1, 1, 1)] void dual_meshing_udpate_auto_smooth_SmoothCorner_indirect_dispatch_args(int3 id : SV_DispatchThreadID) { indirectDispatchArgs[0] = max(1, uint(uint(indirectDrawArgs[0]) / 3 + kThreadGroupSize - 1) / kThreadGroupSize); } [numthreads(kThreadGroupSize, 1, 1)] void dual_meshing_auto_smooth_smooth_corner(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_ALL) || defined(MUDBUN_FAST_ITERATION) return; #endif if (uint(id.x) >= uint(indirectDrawArgs[0]) / 3) return; uint iTriBase = uint(id.x) * 3; bool smooth0 = (aGenPoint[iTriBase + 0].atSmoothEdge != 0); bool smooth1 = (aGenPoint[iTriBase + 1].atSmoothEdge != 0); bool smooth2 = (aGenPoint[iTriBase + 2].atSmoothEdge != 0); if (smooth0 && smooth1 && smooth2) return; int iBrushMask = aGenPoint[iTriBase].iBrushMask; uint iTri0 = iTriBase; uint iTri1 = iTriBase + 1; uint iTri2 = iTriBase + 2; GenPoint gpCopy = aGenPoint[iTri0]; float3 p0 = aGenPoint[iTri0].posNorm.xyz; float3 p1 = aGenPoint[iTri1].posNorm.xyz; float3 p2 = aGenPoint[iTri2].posNorm.xyz; float3 n0 = unpack_normal(aGenPoint[iTri0].posNorm.w); float3 n1 = unpack_normal(aGenPoint[iTri1].posNorm.w); float3 n2 = unpack_normal(aGenPoint[iTri2].posNorm.w); float4 c0 = unpack_rgba(aGenPoint[iTri0].material.color); float4 c1 = unpack_rgba(aGenPoint[iTri1].material.color); float4 c2 = unpack_rgba(aGenPoint[iTri2].material.color); float4 e0 = unpack_rgba(aGenPoint[iTri0].material.emissionTightness); float4 e1 = unpack_rgba(aGenPoint[iTri1].material.emissionTightness); float4 e2 = unpack_rgba(aGenPoint[iTri2].material.emissionTightness); float2 m0 = unpack_saturated(aGenPoint[iTri0].material.metallicSmoothness); float2 m1 = unpack_saturated(aGenPoint[iTri1].material.metallicSmoothness); float2 m2 = unpack_saturated(aGenPoint[iTri2].material.metallicSmoothness); float3 p01 = p1 - p0; float3 p02 = p2 - p0; float3 p12 = p2 - p1; float3 n01 = n1 - n0; float3 n02 = n2 - n0; float3 n12 = n2 - n1; float4 c01 = c1 - c0; float4 c02 = c2 - c0; float4 c12 = c2 - c1; float4 e01 = e1 - e0; float4 e02 = e2 - e0; float4 e12 = e2 - e1; float2 m01 = m1 - m0; float2 m02 = m2 - m0; float2 m12 = m2 - m1; int n = smoothCornerSubdivision; float dt = 1.0f / n; float3 dtp02 = dt * p02; float3 dtp12 = dt * p12; float3 dtn02 = dt * n02; float3 dtn12 = dt * n12; float4 dtc02 = dt * c02; float4 dtc12 = dt * c12; float4 dte02 = dt * e02; float4 dte12 = dt * e12; float2 dtm02 = dt * m02; float2 dtm12 = dt * m12; [loop] for (int i = 0; i < n; ++i) { int iNewTriBase = iTriBase; float3 aVert[3]; float3 aNorm[3]; float4 aC[3]; float4 aE[3]; float2 aM[3]; float idt = float(i) / n; aVert[0] = p0 + idt * p01; aVert[1] = aVert[0] + dt * p01; aVert[2] = aVert[0] + dt * p02; aNorm[0] = n0 + idt * n01; aNorm[1] = aNorm[0] + dt * n01; aNorm[2] = aNorm[0] + dt * n02; aC[0] = c0 + idt * c01; aC[1] = aC[0] + dt * c01; aC[2] = aC[0] + dt * c02; aE[0] = e0 + idt * e01; aE[1] = aE[0] + dt * e01; aE[2] = aE[0] + dt * e02; aM[0] = m0 + idt * m01; aM[1] = aM[0] + dt * m01; aM[2] = aM[0] + dt * m02; int jn = 2 * i + 1; [loop] for (int j = 0; j < jn; ++j) { bool odd = (uint(j) % 2 > 0); if (j > 0) { uint iVertChange = uint(2 + i * 3 - j) % 3; float s12 = (odd ? 2.0f : 1.0f); float s02 = (odd ? -1.0f : 1.0f); aVert[iVertChange] += s12 * dtp12 + s02 * dtp02; aNorm[iVertChange] += s12 * dtn12 + s02 * dtn02; aC[iVertChange] += s12 * dtc12 + s02 * dtc02; aE[iVertChange] += s12 * dte12 + s02 * dte02; aM[iVertChange] += s12 * dtm12 + s02 * dtm02; } if (i > 0) { InterlockedAdd(indirectDrawArgs[0], 3, iNewTriBase); } int aiProp[2][3] = { { 0, 1, 2}, {0, 2, 1 } }; [loop] for (int m = 0; m < 3; ++m) { int iProp = aiProp[odd ? 1 : 0][m]; int iNewGenPoint = iNewTriBase + m; aGenPoint[iNewGenPoint] = gpCopy; aGenPoint[iNewGenPoint].posNorm.xyz = aVert[iProp]; aGenPoint[iNewGenPoint].posNorm.w = pack_normal(normalize(aNorm[iProp])); aGenPoint[iNewGenPoint].material.color = pack_rgba(aC[iProp]); aGenPoint[iNewGenPoint].material.emissionTightness = pack_rgba(aE[iProp]); aGenPoint[iNewGenPoint].material.metallicSmoothness = pack_saturated(aM[iProp]); } [loop] for (int k = 0; k < 3; ++k) { float3 blurredNormal; SDF_NORMAL(blurredNormal, aGenPoint[iNewTriBase + k].posNorm.xyz, sdf_masked_brushes, iBrushMask, smoothCornerNormalBlur); float3 vertNormal = unpack_normal(aGenPoint[iNewTriBase + k].posNorm.w); if (autoSmoothMaxAngle > kEpsilon) { float t = saturate((abs(angle_between(vertNormal, blurredNormal))) / max(kEpsilon, kPi * smoothCornerFade)); float3 smoothCornerNormal = normalize(lerp(vertNormal, blurredNormal, t)); aGenPoint[iNewTriBase + k].posNorm.w = pack_normal(smoothCornerNormal); } } } } } [numthreads(1, 1, 1)] void update_dual_meshing_splats_indirect_args(int3 id : SV_DispatchThreadID) { int numSplats = uint(indirectDrawArgs[0]) / 6; switch (renderMode) { case kRenderModeCircleSplats: indirectDrawArgs[0] = numSplats * 3; break; } indirectDispatchArgs[0] = max(1, uint(numSplats + kThreadGroupSize - 1) / kThreadGroupSize); } [numthreads(kThreadGroupSize, 1, 1)] void convert_dual_meshing_splats(int3 id : SV_DispatchThreadID) { #if defined(MUDBUN_DISABLE_DUAL_MESHING_SPLATS) || defined(MUDBUN_FAST_ITERATION) return; #endif int maxSplats = 0; switch (renderMode) { case kRenderModeCircleSplats: maxSplats = uint(indirectDrawArgs[0]) / 3; break; case kRenderModeQuadSplats: maxSplats = uint(indirectDrawArgs[0]) / 6; break; } int iSplat = id.x; if (id.x >= maxSplats) return; int iDualQuadBase = iSplat * 6; float3 v0 = aGenPoint[iDualQuadBase ].posNorm.xyz; float3 v1 = aGenPoint[iDualQuadBase + 1].posNorm.xyz; float3 v2 = aGenPoint[iDualQuadBase + 2].posNorm.xyz; float3 v3 = aGenPoint[iDualQuadBase + 3].posNorm.xyz; float3 v4 = aGenPoint[iDualQuadBase + 4].posNorm.xyz; float3 v5 = aGenPoint[iDualQuadBase + 5].posNorm.xyz; float3 v01 = v1 - v0; float3 v02 = v2 - v0; float3 v34 = v4 - v3; float3 v35 = v5 - v3; float3 c012 = cross(v01, v02); float3 c345 = cross(v34, v35); float3 n012 = normalize_safe(c012); float3 n345 = normalize_safe(c345); float a012 = max(kEpsilon, abs(length(c012))); float a345 = max(kEpsilon, abs(length(c345))); float aTotal = a012 + a345; float3 pos = (a012 * (v0 + v1 + v2) + a345 * (v3 + v4 + v5)) / (3.0f * aTotal); float3 norm = normalize_safe(a012 * n012 + a345 * n345) / aTotal; aGenPoint[iDualQuadBase].posNorm = float4(pos, pack_normal(norm * (invertNormals ? -1.0f : 1.0f))); float scaleMult = pow(saturate(aTotal / (0.2f * voxelSize * voxelSize)), 0.1f); aGenPoint[iDualQuadBase].material.size *= scaleMult; }