private string GetShapeCode(SDFShape shape, string parentPosition) { Vector3 offset = shape.transform.localPosition; Vector3 up = shape.transform.localRotation * Vector3.up; // Vector3 right = shape.transform.localRotation * Vector3.right; // Vector3 forward = shape.transform.localRotation * Vector3.forward; Vector3 parameters = shape.GetParameters(); switch (shape.shapeType) { case SDFShape.ShapeType.Plane: return("dot(" + parentPosition + " - " + ConstructVariable(offset) + ", " + ConstructVariable(up) + ")"); case SDFShape.ShapeType.FracturedPlane: //return "wsPos.y + (clamp(wsPos.x, 0.0, 2.0) * 0.05 + clamp(wsPos.z + .5, 0.0, 1.0) * .1)"; return("frPlane(wsPos)"); case SDFShape.ShapeType.Sphere: return("length(wsPos) - .5"); case SDFShape.ShapeType.Cube: return("fBox(wsPos," + ConstructVariable(shape.GetParameters()) + ")"); case SDFShape.ShapeType.Cylinder: return("fCylinder(wsPos, " + ConstructVariable(parameters.x) + "," + ConstructVariable(parameters.y) + ")"); } return("0.0"); // Worst case }
private Vector3 GetShapeLocalScale(SDFShape shape) { if (shape.shapeType == SDFShape.ShapeType.Cube || shape.shapeType == SDFShape.ShapeType.Cylinder) { return(Vector3.one); } return(shape.transform.localScale); }
private bool UseTransform(SDFShape shape) { switch (shape.shapeType) { case SDFShape.ShapeType.Plane: return(false); default: return(true); } }
private void BuildNodeTree(GameObject go, List <Node> nodeList, int depth) { if (!go.activeInHierarchy) { return; } SDFOperation op = go.GetComponent <SDFOperation>(); SDFShape shape = go.GetComponent <SDFShape>(); Vector3 scale = go.transform.localScale; if (shape) { scale = GetShapeLocalScale(shape); } Node node = new Node(); node.transform = Matrix4x4.TRS(go.transform.localPosition, go.transform.localRotation, scale).inverse; node.type = 0; node.depth = depth; node.domainDistortionType = 0; node.domainDistortion = Vector3.one; node.bias = 1f; if (op) { node.type = 0; node.parameters = (int)op.operationType; node.domainDistortionType = (int)op.distortionType; node.domainDistortion = op.domainRepeat; // For now... node.bias = 1f; } else if (shape) { node.type = 1; node.parameters = (int)shape.shapeType; node.domainDistortion = shape.GetParameters(); node.bias = shape.sdfBias; } nodeList.Add(node); foreach (Transform child in go.transform) { BuildNodeTree(child.gameObject, nodeList, depth + 1); } }
private string GenerateCodeForNode(Dictionary <int, bool> visitMap, Dictionary <int, bool> dVisitMap, Dictionary <SDFOperation, int> nodeMap, GameObject go, int depth, List <Matrix4x4> matrices) { string output = ""; SDFOperation op = go.GetComponent <SDFOperation>(); if (op) { if (verboseOutput) { output += GetTabs(depth + 1) + "{\n"; } int stackIndex = nodeMap[op]; bool firstTime = !visitMap.ContainsKey(stackIndex); string nodeStackPosition = GetStackPositionVariableName(stackIndex, firstTime); visitMap[stackIndex] = true; float defaultDistance = op.operationType == SDFOperation.OperationType.Intersection ? 1000f : 0f; // stack[stackTop].sdf = node.parameters == 2 ? 0.0 : 1000.0; // Make sure we initialize knowing the operation // output += GetTabs(depth + 2) + GetStackDistanceVariableName(stackIndex, true) + " = " + defaultDistance.ToString("0.00") + ";\n"; Matrix4x4 localInverse = Matrix4x4.TRS(op.transform.localPosition, op.transform.localRotation, op.transform.localScale).inverse; if (localInverse != Matrix4x4.identity) { string sourcePosition = nodeStackPosition; if (depth > 0) { int parentStackIndex = nodeMap[go.transform.parent.gameObject.GetComponent <SDFOperation>()]; sourcePosition = GetStackPositionVariableName(parentStackIndex); } if (op.transform.localRotation == Quaternion.identity) { Vector4 offset = op.transform.localPosition; offset.w = 0f; if (verboseOutput) { output += GetTabs(depth + 2) + "// Optimized rotation\n"; } output += GetTabs(depth + 2) + nodeStackPosition + " = "; if (op.transform.localScale != Vector3.one) { Vector3 scale = op.transform.localScale; Vector4 invScale = new Vector4(1f / scale.x, 1f / scale.y, 1f / scale.z, 1f); output += "(" + sourcePosition + " * " + ConstructVariable(invScale) + ")"; } else { output += sourcePosition; } if (offset.magnitude > 0f) { output += " - " + ConstructVariable(offset) + ";\n"; } else { output += ";\n"; } } else { // Just use the matrix if there's rotation string matrixVariable = "tr[" + matrices.Count + "]"; matrices.Add(localInverse); if (transformArray) { output += GetTabs(depth + 2) + nodeStackPosition + " = " + MultiplyMatrixVector4(matrixVariable, sourcePosition) + ";\n"; } else { output += GetTabs(depth + 2) + nodeStackPosition + " = " + MultiplyMatrixVector4(ConstructVariable(localInverse), sourcePosition) + ";\n"; } } } else { if (verboseOutput) { output += GetTabs(depth + 2) + "// Transform optimized\n"; } if (depth > 0) { int parentStackIndex = nodeMap[go.transform.parent.gameObject.GetComponent <SDFOperation>()]; output += GetTabs(depth + 2) + nodeStackPosition + " = " + GetStackPositionVariableName(parentStackIndex) + ";\n"; } } nodeStackPosition = GetStackPositionVariableName(stackIndex); if (op.distortionType != SDFOperation.DomainDistortion.None) { switch (op.distortionType) { case SDFOperation.DomainDistortion.Repeat3D: output += GetTabs(depth + 2) + nodeStackPosition + ".xyz = domainRepeat(" + nodeStackPosition + ".xyz , " + ConstructVariable(op.domainRepeat) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatX: output += GetTabs(depth + 2) + nodeStackPosition + ".x = domainRepeat1D(" + nodeStackPosition + ".x , " + ConstructVariable(op.domainRepeat.x) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatY: output += GetTabs(depth + 2) + nodeStackPosition + ".y = domainRepeat1D(" + nodeStackPosition + ".y , " + ConstructVariable(op.domainRepeat.y) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatZ: output += GetTabs(depth + 2) + nodeStackPosition + ".z = domainRepeat1D(" + nodeStackPosition + ".z , " + ConstructVariable(op.domainRepeat.z) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatPolarX: output += GetTabs(depth + 2) + nodeStackPosition + ".yz = pModPolar(" + nodeStackPosition + ".yz , " + ConstructVariable(op.domainRepeat.x) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatPolarY: output += GetTabs(depth + 2) + nodeStackPosition + ".xz = pModPolar(" + nodeStackPosition + ".xz , " + ConstructVariable(op.domainRepeat.x) + ");\n"; break; case SDFOperation.DomainDistortion.RepeatPolarZ: output += GetTabs(depth + 2) + nodeStackPosition + ".xy = pModPolar(" + nodeStackPosition + ".xy , " + ConstructVariable(op.domainRepeat.x) + ");\n"; break; case SDFOperation.DomainDistortion.MirrorXYZ: output += GetTabs(depth + 2) + nodeStackPosition + " = abs(" + nodeStackPosition + ");\n"; break; case SDFOperation.DomainDistortion.MirrorXZ: output += GetTabs(depth + 2) + nodeStackPosition + ".xz = abs(" + nodeStackPosition + ".xz) * " + ConstructVariable(new Vector2(-1f, 1f)) + ";\n"; break; case SDFOperation.DomainDistortion.MirrorX: output += GetTabs(depth + 2) + nodeStackPosition + ".x = abs(" + nodeStackPosition + ".x);\n"; break; case SDFOperation.DomainDistortion.MirrorY: output += GetTabs(depth + 2) + nodeStackPosition + ".y = abs(" + nodeStackPosition + ".y);\n"; break; case SDFOperation.DomainDistortion.MirrorZ: output += GetTabs(depth + 2) + nodeStackPosition + ".z = abs(" + nodeStackPosition + ".z);\n"; break; case SDFOperation.DomainDistortion.RotateDiscreteX: output += GetTabs(depth + 2) + nodeStackPosition + ".xyz = rdX(" + nodeStackPosition + ".xyz);\n"; break; case SDFOperation.DomainDistortion.RotateDiscreteY: output += GetTabs(depth + 2) + nodeStackPosition + ".xyz = rdY(" + nodeStackPosition + ".xyz);\n"; break; case SDFOperation.DomainDistortion.RotateDiscreteZ: output += GetTabs(depth + 2) + nodeStackPosition + ".xyz = rdZ(" + nodeStackPosition + ".xyz);\n"; break; case SDFOperation.DomainDistortion.FlipX: output += GetTabs(depth + 2) + nodeStackPosition + ".x = -" + nodeStackPosition + ".x;\n"; break; case SDFOperation.DomainDistortion.FlipY: output += GetTabs(depth + 2) + nodeStackPosition + ".y = -" + nodeStackPosition + ".y;\n"; break; case SDFOperation.DomainDistortion.FlipZ: output += GetTabs(depth + 2) + nodeStackPosition + ".z = -" + nodeStackPosition + ".z;\n"; break; } } bool first = true; bool carryOverFirstOp = false; int carryOverOpIndex = -1; bool distanceEstablished = false; foreach (Transform child in go.transform) { GameObject childGO = child.gameObject; if (!childGO.activeInHierarchy) { continue; } int currentIndex = stackIndex; if (carryOverFirstOp) { currentIndex = carryOverOpIndex; } if (childGO.GetComponent <SDFOperation>()) { output += GenerateCodeForNode(visitMap, dVisitMap, nodeMap, childGO, depth + 1, matrices); int childStackIndex = nodeMap[childGO.GetComponent <SDFOperation>()]; if (first) { carryOverFirstOp = true; carryOverOpIndex = childStackIndex; if (verboseOutput) { output += GetTabs(depth + 2) + "// Optimized first operation carry over\n"; } } else { output += GetTabs(depth + 2) + GetStackDistanceVariableName(stackIndex, !distanceEstablished) + " = "; output += GetOperationCode((int)op.operationType, GetStackDistanceVariableName(currentIndex), GetStackDistanceVariableName(childStackIndex)) + ";\n"; carryOverFirstOp = false; distanceEstablished = true; } } else if (childGO.GetComponent <SDFShape>()) { SDFShape shape = childGO.GetComponent <SDFShape>(); string shapeCode = GetShapeCode(shape, nodeStackPosition + ".xyz"); if (shape.sdfBias != 1f) { shapeCode = "(" + shapeCode + "*" + ConstructVariable(shape.sdfBias) + ")"; } if (UseTransform(shape)) { Matrix4x4 localShapeTransform = GetLocalTransform(shape); if (localShapeTransform == Matrix4x4.identity) { if (verboseOutput) { output += GetTabs(depth + 2) + "// Shape transform optimized\n"; } output += GetTabs(depth + 2) + "wsPos = " + nodeStackPosition + ".xyz;\n"; } else { if (shape.transform.localRotation == Quaternion.identity) { Vector3 offset = shape.transform.localPosition; if (verboseOutput) { output += GetTabs(depth + 2) + "// Shape optimized rotation\n"; } output += GetTabs(depth + 2) + "wsPos = "; Vector3 scale = GetShapeLocalScale(shape); if (scale != Vector3.one) { Vector3 invScale = new Vector3(1f / scale.x, 1f / scale.y, 1f / scale.z); output += "( " + nodeStackPosition + ".xyz * " + ConstructVariable(invScale) + ")"; } else { output += nodeStackPosition + ".xyz"; } if (offset.magnitude > 0f) { output += " - " + ConstructVariable(offset) + ";\n"; } else { output += ";\n"; } } else { Matrix4x4 localShapeInverse = localShapeTransform.inverse; string matrixVariable = "tr[" + matrices.Count + "]"; matrices.Add(localShapeInverse); if (transformArray) { output += GetTabs(depth + 2) + "wsPos = " + MultiplyMatrixVector4(matrixVariable, nodeStackPosition) + ".xyz;\n"; } else { output += GetTabs(depth + 2) + "wsPos = " + MultiplyMatrixVector4(ConstructVariable(localShapeInverse), nodeStackPosition) + ".xyz;\n"; } } } } output += GetTabs(depth + 2) + GetStackDistanceVariableName(stackIndex, !distanceEstablished) + " = "; distanceEstablished = true; if (first) { output += shapeCode + ";\n"; } else { output += GetOperationCode((int)op.operationType, GetStackDistanceVariableName(currentIndex), shapeCode) + ";\n"; } carryOverFirstOp = false; } else { Debug.LogError("Something that is not an op or a shape is in the hierarchy! " + childGO.name, childGO); } first = false; } // If we're still carrying over, but finished this node... if (carryOverFirstOp) { if (verboseOutput) { output += GetTabs(depth + 2) + "// Carrying over node with single child \n"; } output += GetTabs(depth + 2) + GetStackDistanceVariableName(stackIndex, true) + " = " + GetStackDistanceVariableName(carryOverOpIndex) + ";\n"; } if (verboseOutput) { output += GetTabs(depth + 1) + "}\n"; } } return(output); }
private Matrix4x4 GetLocalTransform(SDFShape shape) { return(Matrix4x4.TRS(shape.transform.localPosition, shape.transform.localRotation, GetShapeLocalScale(shape))); }