/******************************************************************************/ /* Project - MudBun Publisher - Long Bunny Labs http://LongBunnyLabs.com Author - Ming-Lun "Allen" Chou http://AllenChou.net */ /******************************************************************************/ #ifndef MUDBUN_DECAL #define MUDBUN_DECAL #include "Render/ShaderCommon.cginc" #if MUDBUN_VALID #include "BrushFuncs.cginc" struct DecalResults { bool hit; float3 pos; SdfBrushMaterial mat; float sdfValue; }; #define SDF_DECAL_MASKED_BRUSHES(res, p, brushMask, mat) \ { \ int iStack = -1; \ float3 pStack[kMaxBrushGroupDepth]; \ float resStack[kMaxBrushGroupDepth]; \ SdfBrushMaterial matStack[kMaxBrushGroupDepth]; \ \ res = kInfinity; \ mat = init_brush_material(); \ float3 groupP = p; \ float groupRes = kInfinity; \ SdfBrushMaterial groupMat = init_brush_material(); \ FOR_EACH_BRUSH_EXTERN_MASK(brushMask, \ switch (aBrush[iBrush].type) \ { \ case kSdfBeginGroup: \ iStack = min(kMaxBrushGroupDepth - 1, iStack + 1); \ pStack[iStack] = p; \ resStack[iStack] = res; \ matStack[iStack] = mat; \ res = kInfinity; \ mat = init_brush_material(); \ break; \ case kSdfEndGroup: \ groupP = p; \ groupRes = res; \ groupMat = mat; \ p = pStack[iStack]; \ res = resStack[iStack]; \ mat = matStack[iStack]; \ break; \ } \ res = sdf_brush_apply(res, groupRes, groupMat, groupP, aBrush[iBrush], mat); \ switch (aBrush[iBrush].type) \ { \ case kSdfEndGroup: \ iStack = max(-1, iStack - 1); \ break; \ } \ ); \ } bool sdf_decal_aabb_contains(Aabb aabb, float3 p) { // TODO: use max blend aabb.boundsMin -= 5.0f; aabb.boundsMax += 5.0f; return aabb_contains(aabb, p); } // stmt = statements processing "iData" of hit leaf AABB nodes // will gracefully handle maxed-out stacks #define SDF_DECAL_AABB_TREE_CONTAINS(tree, root, p, stmt) \ { \ int stackTop = 0; \ int stack[kAabbTreeNodeStackSize]; \ stack[stackTop] = root; \ \ int numIters = 0; \ while (stackTop >= 0 && numIters < 128 /* safeguard */) \ { \ int index = stack[stackTop--]; \ if (index < 0) \ continue; \ \ if (!sdf_decal_aabb_contains(tree[index].aabb, p)) \ continue; \ \ if (tree[index].iChildA < 0) \ { \ int iData = tree[index].iData; \ \ stmt \ } \ else \ { \ stackTop = min(stackTop + 1, kAabbTreeNodeStackSize - 1); \ stack[stackTop] = tree[index].iChildA; \ stackTop = min(stackTop + 1, kAabbTreeNodeStackSize - 1); \ stack[stackTop] = tree[index].iChildB; \ } \ } \ } DecalResults sdf_decal ( float3 p ) { p = mul(worldToLocal, float4(p, 1.0f)).xyz; DecalResults res; res.hit = false; res.pos = p; res.mat = init_brush_material(); res.sdfValue = kInfinity; // don't crash Unity previews #ifdef MUDBUN_PROCEDURAL BRUSH_MASK(brushMask); BRUSH_MASK_CLEAR_ALL(brushMask); SDF_DECAL_AABB_TREE_CONTAINS(aabbTree, aabbRoot, p, BRUSH_MASK_SET(brushMask, iData); ); float d = kInfinity; SDF_DECAL_MASKED_BRUSHES(d, p, brushMask, res.mat); if (d > 0.0f) discard; res.sdfValue = d; #endif return res; } #else struct SdfBrushMaterialDummy { float4 color; float4 emissionHash; float4 metallicSmoothnessSizeTightness; float4 textureWeight; }; struct DecalResults { bool hit; float3 pos; SdfBrushMaterialDummy mat; float sdfValue; }; DecalResults sdf_decal ( float3 p ) { DecalResults res; res.hit = false; res.pos = p; res.mat.color = 0.0f; res.mat.emissionHash = 0.0f; res.mat.metallicSmoothnessSizeTightness = 0.0f; res.mat.textureWeight = 0.0f; res.sdfValue = 0.0f; return res; } #endif #endif