/// <summary> /// Project the specified Base UVs to find the appropriate 2D shape from 3D space - mainly used for angled roofs /// </summary> public static Vector2[] Project(Vector3 p0, Vector3 p1, Vector3 p2, Vector2 baseUV) { Vector2[] uvs = new Vector2[3]; Vector3 normal = BuildRMesh.CalculateNormal(p0, p1, p2); Quaternion normalToFaceUp = Quaternion.FromToRotation(normal, Vector3.up); Vector3 pC = (p0 + p1 + p2) / 3f; p0 = normalToFaceUp * (p0 - pC); p1 = normalToFaceUp * (p1 - pC); p2 = normalToFaceUp * (p2 - pC); uvs[0] = new Vector2(p0.x, p0.z); uvs[1] = new Vector2(p1.x, p1.z); uvs[2] = new Vector2(p2.x, p2.z); float minX = Mathf.Min(uvs[0].x, uvs[1].x, uvs[2].x); float minY = Mathf.Min(uvs[0].y, uvs[1].y, uvs[2].y); if (minX < 0) { uvs[0].x += -minX; uvs[1].x += -minX; uvs[2].x += -minX; } if (minY < 0) { uvs[0].y += -minY; uvs[1].y += -minY; uvs[2].y += -minY; } return(uvs); }
public static bool Generate(BuildRMesh mesh, BuildRCollider collider, Vector2[] points, int[] facadeIndices, float roofBaseHeight, IVolume volume, Rect clampUV) { Roof design = volume.roof; OffsetSkeleton offsetPoly = new OffsetSkeleton(points); offsetPoly.direction = 1; offsetPoly.Execute(); Shape shape = offsetPoly.shape; int submesh = mesh.submeshLibrary.SubmeshAdd(design.mainSurface); // surfaceMapping.IndexOf(design.mainSurface); int wallSubmesh = mesh.submeshLibrary.SubmeshAdd(design.wallSurface); //surfaceMapping.IndexOf(design.wallSurface); if (shape == null) { return(false); } List <Edge> edges = new List <Edge>(shape.edges); List <Edge> baseEdges = new List <Edge>(shape.baseEdges); float shapeHeight = shape.HeighestPoint(); float designHeight = design.height; float heightScale = designHeight / shapeHeight; Vector2 clampUVScale = Vector2.one; if (clampUV.width > 0) { FlatBounds bounds = new FlatBounds(); for (int fvc = 0; fvc < points.Length; fvc++) { bounds.Encapsulate(points[fvc]); } clampUVScale.x = bounds.width / clampUV.width; clampUVScale.y = bounds.height / clampUV.height; } Dictionary <Node, int> shapeConnectionCount = new Dictionary <Node, int>(); Dictionary <Node, List <Node> > shapeConnections = new Dictionary <Node, List <Node> >(); int edgeCount = edges.Count; for (int e = 0; e < edgeCount; e++) { Edge edge = edges[e]; if (edge.length < Mathf.Epsilon) { continue; } if (!shapeConnectionCount.ContainsKey(edge.nodeA)) { shapeConnectionCount.Add(edge.nodeA, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeA, new List <Node> { edge.nodeB }); } else { shapeConnectionCount[edge.nodeA]++; if (!shapeConnections[edge.nodeA].Contains(edge.nodeB)) { shapeConnections[edge.nodeA].Add(edge.nodeB); } } if (!shapeConnectionCount.ContainsKey(edge.nodeB)) { shapeConnectionCount.Add(edge.nodeB, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeB, new List <Node> { edge.nodeA }); } else { shapeConnectionCount[edge.nodeB]++; if (!shapeConnections[edge.nodeB].Contains(edge.nodeA)) { shapeConnections[edge.nodeB].Add(edge.nodeA); } } } int baseEdgeCount = baseEdges.Count; for (int b = 0; b < baseEdgeCount; b++) { Edge baseEdge = baseEdges[b]; Node nodeA = baseEdge.nodeA; Node nodeB = baseEdge.nodeB; Node currentNode = nodeA; Node lastNode = nodeB; int itMax = 50; List <Node> edgeShape = new List <Node>() { nodeA }; while (currentNode != nodeB) { List <Node> nodeConnections = shapeConnections[currentNode]; int nodeConnectionCount = nodeConnections.Count; float minAngle = Mathf.Infinity; Node nextNode = null; Vector2 currentDirection = (currentNode.position - lastNode.position).normalized; for (int n = 0; n < nodeConnectionCount; n++) { Node connectingNode = nodeConnections[n]; if (connectingNode == lastNode) { continue; } Vector2 nextDirection = (connectingNode.position - currentNode.position).normalized; float nodeAngle = JMath.SignAngleDirection(currentDirection, nextDirection); if (nodeAngle < minAngle) { minAngle = nodeAngle; nextNode = connectingNode; } } if (nextNode != null) { edgeShape.Add(nextNode); lastNode = currentNode; currentNode = nextNode; } itMax--; if (itMax < 0) { break; } } int edgeShapeCount = edgeShape.Count; if (edgeShapeCount < 3) { continue; } // Debug.Log("Generate edgeShapeCount "+ edgeShapeCount); Vector3[] verts = new Vector3[edgeShapeCount]; Vector2[] uvs = new Vector2[edgeShapeCount]; Vector3 baseShapeDirection = ShapeOffset.Utils.ToV3(nodeB.position - nodeA.position).normalized; float uvAngle = JMath.SignAngle(new Vector2(baseShapeDirection.x, baseShapeDirection.z).normalized) - 90; Vector2[] faceShape = new Vector2[edgeShapeCount]; Vector3[] normals = new Vector3[edgeShapeCount]; Vector4[] tangents = new Vector4[edgeShapeCount]; // Vector3 normal = Vector3.up;//BuildRMesh.CalculateNormal(); TODO Vector4 tangent = BuildRMesh.CalculateTangent(baseShapeDirection); for (int i = 0; i < edgeShapeCount; i++)//what on earth did I write here? { Vector3 newVert = new Vector3(edgeShape[i].position.x, edgeShape[i].height * heightScale + roofBaseHeight, edgeShape[i].position.y); verts[i] = newVert; Vector2 baseUV = new Vector2(newVert.x - verts[0].x, newVert.z - verts[0].z); Vector2 newUV = Vector2.zero; if (i != 0) { newUV = JMath.Rotate(baseUV, uvAngle); } if (clampUV.width > Mathf.Epsilon) { newUV.x = Mathf.Clamp(clampUV.x + newUV.x / clampUVScale.x, clampUV.xMin, clampUV.xMax); newUV.y = Mathf.Clamp(clampUV.y + newUV.y / clampUVScale.y, clampUV.yMin, clampUV.yMax); } else { if (i != 0) { float faceHeight = edgeShape[i].height * heightScale; newUV.y = Mathf.Sqrt((newUV.y * newUV.y) + (faceHeight * faceHeight));//hypotenuse of roof to give length of roof face if (design.mainSurface != null) { newUV = design.mainSurface.CalculateUV(newUV); } } } uvs[i] = newUV; faceShape[i] = edgeShape[i].position;//used for triangulation // normals[i] = normal; tangents[i] = tangent; } // int[] tris = EarClipper.Triangulate(faceShape, 0, -1); int[] tris = Poly2TriWrapper.Triangulate(faceShape, true); int triCount = tris.Length; Vector3 normal = (verts.Length > 2 && triCount > 2) ? BuildRMesh.CalculateNormal(verts[tris[0]], verts[tris[1]], verts[tris[2]]) : Vector3.up; for (int i = 0; i < edgeShapeCount; i++) { normals[i] = normal; } mesh.AddData(verts, uvs, tris, normals, tangents, submesh); //gable bool isGabled = volume[facadeIndices[b]].isGabled; if (isGabled) { for (int t = 0; t < triCount; t += 3) { if (tris[t] == 0 || tris[t + 1] == 0 || tris[t + 2] == 0) { int beB = edgeShapeCount - 1; if (tris[t] == beB || tris[t + 1] == beB || tris[t + 2] == beB) { Vector3 b0 = verts[0]; Vector3 b1 = verts[beB]; Vector3 g0 = b0; Vector3 g1 = b1; int topIndex = 0; for (int tx = 0; tx < 3; tx++) { if (tris[t + tx] != 0 && tris[t + tx] != beB) { topIndex = tris[t + tx]; } } Vector3 b2 = verts[topIndex]; Vector3 baseV = b1 - b0; Vector3 dir = baseV.normalized; Vector3 face = Vector3.Cross(Vector3.up, dir).normalized; Vector3 up = Vector3.Project(b2 - b0, Vector3.up); //clear triangle tris[t] = 0; tris[t + 1] = 0; tris[t + 2] = 0; bool simpleGable = volume[facadeIndices[b]].simpleGable; Gable gableStyle = volume[facadeIndices[b]].gableStyle; float thickness = volume[facadeIndices[b]].gableThickness; float additionalHeight = volume[facadeIndices[b]].gableHeight; float height = up.magnitude + additionalHeight; if (simpleGable || gableStyle != null) { Vector3 pitchVectorA = (b2 - b0).normalized; Vector3 pitchVectorB = (b2 - b1).normalized; float angle = Vector3.Angle(-face, pitchVectorA); float scale = Mathf.Cos(angle / 57.2957795f); b0 += pitchVectorA * (thickness * (1 / scale)); b1 += pitchVectorB * (thickness * (1 / scale)); } Vector3 center = Vector3.Lerp(b0, b1, 0.5f); up = Vector3.Project(b2 - b0, Vector3.up); //recalculate after b change(?) Vector3 b3 = center + up; if (simpleGable) //generate a simple gable { //generate simple gable based on roof Vector3 gCenter = Vector3.Lerp(g0, g1, 0.5f); Vector3 gBaseUp = Vector3.up * additionalHeight; Vector3 gUp = up.normalized * height; Vector3 gBack = -face * thickness; //todo further calculations //face mesh.AddPlane(g0, g1, g0 + gBaseUp, g1 + gBaseUp, wallSubmesh); mesh.AddTri(g1 + gBaseUp, g0 + gBaseUp, gCenter + gUp, dir, wallSubmesh); //backface mesh.AddPlane(g1 + gBack, g0 + gBack, g1 + gBaseUp + gBack, g0 + gBaseUp + gBack, wallSubmesh); mesh.AddTri(g0 + gBack + gBaseUp, g1 + gBack + gBaseUp, b3 + gBaseUp, -dir, wallSubmesh); //left mesh.AddPlane(g0 + gBack, g0, g0 + gBaseUp + gBack, g0 + gBaseUp, wallSubmesh); mesh.AddPlane(g0 + gBaseUp + gBack, g0 + gBaseUp, b3 + gBaseUp, gCenter + gUp, wallSubmesh); //right mesh.AddPlane(g1, g1 + gBack, g1 + gBaseUp, g1 + gBaseUp + gBack, wallSubmesh); mesh.AddPlane(g1 + gBaseUp, g1 + gBaseUp + gBack, gCenter + gUp, b3 + gBaseUp, wallSubmesh); } else if (volume[facadeIndices[b]].gableStyle != null) { Vector2 baseUV = new Vector2(0, volume.planHeight); GableGenerator.Generate(ref mesh, gableStyle, g0, g1, height, thickness, baseUV); } else { mesh.AddTri(b0, b3, b1, dir, submesh);//face - no separate gable } mesh.AddTri(b0, b2, b3, face, submesh); //left mesh.AddTri(b1, b3, b2, -face, submesh); //right } } } } } return(true); }
//TODO support custom models coming in from roof design public static void Generate(ref BuildRMesh mesh, IVolume volume, List <Vector3[]> roofFaces) { Roof design = volume.roof; float roofDepth = design.depth; float roofHeight = design.height; float dormerWidth = design.dormerWidth; float dormerHeight = design.dormerHeight; int dormerRows = design.dormerRows; if (dormerHeight * dormerRows > roofHeight) { dormerHeight = roofHeight / dormerRows; } float dormerRoofHeight = design.dormerRoofHeight; float roofPitchRad = Mathf.Atan2(roofHeight, roofDepth); float roofHyp = Mathf.Sqrt(roofDepth * roofDepth + roofHeight * roofHeight);//todo make a proper calculation - this is incorrect float dormerDepth = Mathf.Cos(roofPitchRad) * dormerHeight; float dormerHyp = Mathf.Sqrt(dormerHeight * dormerHeight + dormerDepth * dormerDepth); float dormerRowSpace = roofHyp / dormerRows; dormerHyp = Mathf.Min(dormerHyp, dormerRowSpace); float dormerSpace = dormerRowSpace - dormerHyp; float dormerSpaceLerp = dormerSpace / roofHyp; if (INTERNAL_B_MESH == null) { INTERNAL_B_MESH = new BuildRMesh("internal dormer"); } INTERNAL_B_MESH.Clear(); INTERNAL_B_MESH.submeshLibrary.AddRange(mesh.submeshLibrary.MATERIALS.ToArray()); Vector3 bpl = Vector3.left * dormerWidth * 0.5f; Vector3 bpr = Vector3.right * dormerWidth * 0.5f; Vector3 tpc = Vector3.up * dormerHeight; float dormerFaceHeight = dormerHeight - dormerHeight * dormerRoofHeight; Vector3 tpl = bpl + Vector3.up * dormerFaceHeight; Vector3 tpr = bpr + Vector3.up * dormerFaceHeight; Vector3 rpc = tpc + Vector3.back * dormerDepth; Vector3 rpl = tpl + Vector3.back * dormerDepth; Vector3 rpr = tpr + Vector3.back * dormerDepth; Surface mainSurface = design.mainSurface; Surface wallSurface = design.wallSurface; int mainSubmesh = mesh.submeshLibrary.SubmeshAdd(mainSurface); int wallSubmesh = mesh.submeshLibrary.SubmeshAdd(wallSurface); Vector2 sectionSize = new Vector2(dormerWidth, dormerFaceHeight); if (design.wallSection && design.wallSection.CanRender(sectionSize)) { // mesh.submeshLibrary.Add(design.wallSection); mesh.submeshLibrary.Add(design.wallSection); GenerationOutput output = GenerationOutput.CreateRawOutput(); WallSectionGenerator.Generate(design.wallSection, output, sectionSize, false, 0.02f, false, null, mesh.submeshLibrary); Vector3 sectionPos = new Vector3(0, dormerFaceHeight * 0.5f, 0); int[] mapping = new int[output.raw.materials.Count]; for (int s = 0; s < output.raw.materials.Count; s++) { mapping[s] = 0; } INTERNAL_B_MESH.AddDataKeepSubmeshStructure(output.raw, sectionPos, Quaternion.Euler(0, 180, 0), Vector3.one); } else { INTERNAL_B_MESH.AddPlane(bpr, bpl, tpr, tpl, wallSubmesh);//dormer front square } //front triangle INTERNAL_B_MESH.AddTri(tpl, tpr, tpc, Vector3.right, wallSubmesh); //roof Vector3 normalRoofRight = Vector3.Cross((tpr - tpc).normalized, (rpc - tpc).normalized); Vector4 tangentRoofRight = BuildRMesh.CalculateTangent(Vector3.back); Vector3 normalRoofLeft = Vector3.Cross((rpc - tpc).normalized, (tpl - tpc).normalized); Vector4 tangentRoofLeft = BuildRMesh.CalculateTangent(Vector3.forward); Vector2 roofUvMax = new Vector2(dormerDepth, Vector3.Distance(tpc, tpl)); INTERNAL_B_MESH.AddPlane(rpr, tpr, rpc, tpc, Vector2.zero, roofUvMax, normalRoofRight, tangentRoofRight, mainSubmesh, mainSurface); INTERNAL_B_MESH.AddPlane(rpc, tpc, rpl, tpl, Vector2.zero, roofUvMax, normalRoofLeft, tangentRoofLeft, mainSubmesh, mainSurface); //side triangles INTERNAL_B_MESH.AddTri(bpr, rpr, tpr, Vector3.back, wallSubmesh); INTERNAL_B_MESH.AddTri(bpl, tpl, rpl, Vector3.back, wallSubmesh); RawMeshData data = RawMeshData.CopyBuildRMesh(INTERNAL_B_MESH); int roofFaceCount = roofFaces.Count; for (int r = 0; r < roofFaceCount; r++) { Vector3[] roofFace = roofFaces[r]; Vector3 p0 = roofFace[0]; Vector3 p1 = roofFace[1]; Vector3 p2 = roofFace[2]; Vector3 p3 = roofFace[3]; //center line Vector3 pDB = Vector3.Lerp(p0, p1, 0.5f); Vector3 facadeVector = p1 - p0; Vector3 facadeDirection = facadeVector.normalized; Vector3 facadeNormal = Vector3.Cross(Vector3.up, facadeDirection); Vector3 projTL = p0 + Vector3.Project(p2 - p0, facadeDirection); Vector3 projTR = p1 + Vector3.Project(p3 - p1, facadeDirection); float sqrMagP0 = Vector3.SqrMagnitude(p0 - pDB); float sqrMagP1 = Vector3.SqrMagnitude(p1 - pDB); float sqrMagP2 = Vector3.SqrMagnitude(projTL - pDB); float sqrMagP3 = Vector3.SqrMagnitude(projTR - pDB); Vector3 dormerBaseLeft = sqrMagP0 < sqrMagP2 ? p0 : projTL; Vector3 dormerBaseRight = sqrMagP1 < sqrMagP3 ? p1 : projTR; Vector3 roofNormal = BuildRMesh.CalculateNormal(p0, p2, p1); Vector3 roofUp = Vector3.Cross(roofNormal, -facadeDirection); float actualHyp = sqrMagP0 < sqrMagP2?Vector3.Distance(p0, p2 + Vector3.Project(p0 - p2, facadeDirection)) : Vector3.Distance(projTL, p2); Vector3 dormerTopLeft = dormerBaseLeft + roofUp * actualHyp; Vector3 dormerTopRight = dormerBaseRight + roofUp * actualHyp; float topLength = Vector3.Distance(dormerBaseLeft, dormerBaseRight); int numberOfDormers = Mathf.FloorToInt((topLength - design.minimumDormerSpacing * 2) / (design.minimumDormerSpacing + dormerWidth)); if (numberOfDormers == 0) { if (topLength > sectionSize.x) { numberOfDormers = 1; } } for (int dr = 0; dr < dormerRows; dr++) { float rowPercent = dr / (dormerRows + 0f) + dormerSpaceLerp * 0.5f; //row vector Vector3 rl = Vector3.Lerp(dormerBaseLeft, dormerTopLeft, rowPercent); Vector3 rr = Vector3.Lerp(dormerBaseRight, dormerTopRight, rowPercent); for (int dc = 0; dc < numberOfDormers; dc++) { float columnPercent = (dc + 1f) / (numberOfDormers + 1f); Vector3 dormerBegin = Vector3.Lerp(rl, rr, columnPercent); Quaternion meshRot = Quaternion.LookRotation(facadeNormal, Vector3.up); Vector3 meshPos = dormerBegin; //TODO account for the mesh mode of the wall section - custom meshes mesh.AddDataKeepSubmeshStructure(data, meshPos, meshRot, Vector3.one); } } } }
private static void ToMesh(ref BuildRMesh mesh, Shape shape, bool[] gabled, float roofBaseHeight, float meshHeight, int submesh, Surface surface) { List <Edge> edges = new List <Edge>(shape.edges); List <Edge> baseEdges = new List <Edge>(shape.baseEdges); float shapeHeight = shape.HeighestPoint(); float designHeight = meshHeight; float heightScale = designHeight / shapeHeight; Dictionary <Node, int> shapeConnectionCount = new Dictionary <Node, int>(); Dictionary <Node, List <Node> > shapeConnections = new Dictionary <Node, List <Node> >(); int edgeCount = edges.Count; for (int e = 0; e < edgeCount; e++) { Edge edge = edges[e]; // Node nodeA = edge.nodeA; // Node nodeB = edge.nodeB; // Vector3 na = new Vector3(nodeA.position.x, roofBaseHeight * 1.5f, nodeA.position.y); // Vector3 nb = new Vector3(nodeB.position.x, roofBaseHeight * 1.5f, nodeB.position.y); // Debug.DrawLine(na, nb, Color.blue); if (edge.length < Mathf.Epsilon) { continue; } if (!shapeConnectionCount.ContainsKey(edge.nodeA)) { shapeConnectionCount.Add(edge.nodeA, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeA, new List <Node> { edge.nodeB }); } else { shapeConnectionCount[edge.nodeA]++; if (!shapeConnections[edge.nodeA].Contains(edge.nodeB)) { shapeConnections[edge.nodeA].Add(edge.nodeB); } } if (!shapeConnectionCount.ContainsKey(edge.nodeB)) { shapeConnectionCount.Add(edge.nodeB, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeB, new List <Node> { edge.nodeA }); } else { shapeConnectionCount[edge.nodeB]++; if (!shapeConnections[edge.nodeB].Contains(edge.nodeA)) { shapeConnections[edge.nodeB].Add(edge.nodeA); } } // Vector3 na = new Vector3(edge.nodeA.position.x + 75, roofBaseHeight * 2 + edge.nodeA.height, edge.nodeA.position.y); // Vector3 nb = new Vector3(edge.nodeB.position.x + 75, roofBaseHeight * 2 + edge.nodeB.height, edge.nodeB.position.y); // Debug.DrawLine(na, nb, new Color(1,0,1,0.24f)); // // GizmoLabel.Label(edge.nodeA.ToString(), na); // GizmoLabel.Label(edge.nodeB.ToString(), nb); } int baseEdgeCount = baseEdges.Count; for (int b = 0; b < baseEdgeCount; b++) { Edge baseEdge = baseEdges[b]; Node nodeA = baseEdge.nodeA; Node nodeB = baseEdge.nodeB; // Color col = new Color(Random.value, Random.value, Random.value, 0.5f); // Vector3 na = new Vector3(nodeA.position.x + 75, roofBaseHeight * 2, nodeA.position.y); // Vector3 nb = new Vector3(nodeB.position.x + 75, roofBaseHeight * 2, nodeB.position.y); // Debug.DrawLine(na, nb, col);//base edge Node currentNode = nodeA; Node lastNode = nodeB; int itMax = 50; List <Node> edgeShape = new List <Node>() { nodeA }; while (currentNode != nodeB) { List <Node> nodeConnections = shapeConnections[currentNode]; int nodeConnectionCount = nodeConnections.Count; float minAngle = Mathf.Infinity; Node nextNode = null; Vector2 currentDirection = (currentNode.position - lastNode.position).normalized; for (int n = 0; n < nodeConnectionCount; n++) { Node connectingNode = nodeConnections[n]; if (connectingNode == lastNode) { continue; } Vector2 nextDirection = (connectingNode.position - currentNode.position).normalized; float nodeAngle = JMath.SignAngleDirection(currentDirection, nextDirection); if (nodeAngle < minAngle) { minAngle = nodeAngle; nextNode = connectingNode; } } if (nextNode != null) { edgeShape.Add(nextNode); lastNode = currentNode; currentNode = nextNode; } itMax--; if (itMax < 0) { break; } // if(edgeShape.Count == 3) break; } int edgeShapeCount = edgeShape.Count; Vector3[] verts = new Vector3[edgeShapeCount]; Vector2[] uvs = new Vector2[edgeShapeCount]; Vector3 baseShapeDirection = Utils.ToV3(nodeB.position - nodeA.position).normalized; float uvAngle = JMath.SignAngle(new Vector2(baseShapeDirection.x, baseShapeDirection.z).normalized) - 90; Vector2[] faceShape = new Vector2[edgeShapeCount]; Vector3[] normals = new Vector3[edgeShapeCount]; Vector4[] tangents = new Vector4[edgeShapeCount]; // Vector3 normal = Vector3.up;//BuildRMesh.CalculateNormal(); TODO Vector4 tangent = BuildRMesh.CalculateTangent(baseShapeDirection); for (int i = 0; i < edgeShapeCount; i++) { float testHAdd = 0;//5 + b; Vector3 newVert = new Vector3(edgeShape[i].position.x, edgeShape[i].height * heightScale + roofBaseHeight + testHAdd, edgeShape[i].position.y); verts[i] = newVert; Vector2 baseUV = (i == 0) ? Vector2.zero : new Vector2(newVert.x - verts[0].x, newVert.z - verts[0].z); Vector2 newUV = JMath.Rotate(baseUV, uvAngle); float faceHeight = edgeShape[i].height * heightScale; newUV.y = Mathf.Sqrt((newUV.y * newUV.y) + (faceHeight * faceHeight)); if (surface != null) { newUV = surface.CalculateUV(newUV); } uvs[i] = newUV; faceShape[i] = edgeShape[i].position;//used for triangulation // normals[i] = normal; tangents[i] = tangent; } // int[] tris = EarClipper.Triangulate(faceShape, 0, -1); int[] tris = Poly2TriWrapper.Triangulate(faceShape); int triCount = tris.Length; Vector3 normal = BuildRMesh.CalculateNormal(verts[tris[0]], verts[tris[1]], verts[tris[2]]); // Vector3[] normCal = new Vector3[edgeShapeCount]; // for (int t = 0; t < triCount; t += 3) // { // int[] triIndicies = {tris[t], tris[t + 1], tris[t + 2]}; // Vector3 newNormal = BuildRMesh.CalculateNormal(verts[triIndicies[0]], verts[triIndicies[1]], verts[triIndicies[2]]); // for(int i = 0; i < 3; i++) // normCal[triIndicies[i]] = newNormal; // } for (int i = 0; i < edgeShapeCount; i++) { normals[i] = normal;//normCal[i].normalized; } mesh.AddData(verts, uvs, tris, normals, tangents, submesh); if (gabled[b]) { for (int t = 0; t < triCount; t += 3) { if (tris[t] == 0 || tris[t + 1] == 0 || tris[t + 2] == 0) { int beB = edgeShapeCount - 1; if (tris[t] == beB || tris[t + 1] == beB || tris[t + 2] == beB) { Vector3 b0 = verts[0]; Vector3 b1 = verts[beB]; int topIndex = 0; for (int tx = 0; tx < 3; tx++) { if (tris[t + tx] != 0 && tris[t + tx] != beB) { topIndex = tris[t + tx]; } } Vector3 b2 = verts[topIndex]; Vector3 baseV = b1 - b0; Vector3 dir = baseV.normalized; Vector3 face = Vector3.Cross(Vector3.up, dir); // float length = baseV.magnitude; Vector3 center = Vector3.Lerp(b0, b1, 0.5f); Vector3 up = Vector3.Project(b2 - b0, Vector3.up); Vector3 b3 = center + up; mesh.AddTri(b0, b2, b3, face, submesh); //left mesh.AddTri(b1, b3, b2, -face, submesh); //right mesh.AddTri(b0, b3, b1, dir, submesh); //face //clear triangle tris[t] = 0; tris[t + 1] = 0; tris[t + 2] = 0; } } } } // for (int i = 0; i < edgeShapeCount; i++) // { // Node nodeAS = edgeShape[i]; // Node nodeBS = edgeShape[(i + 1) % edgeShapeCount]; // Vector3 nas = new Vector3(nodeAS.position.x + 75, roofBaseHeight * 5 + b, nodeAS.position.y); // Vector3 nbs = new Vector3(nodeBS.position.x + 75, roofBaseHeight * 5 + b, nodeBS.position.y); // Debug.DrawLine(nas, nbs + Vector3.up, col);//Color.yellow); // } } //Assumption - each based edge is a single shape //There are no shapes without a base edge //Enumerate through the base edges //Build the shape //use angle to find shape clockwise or something //triangulate the shape and add to mesh //node data will provide height information //??? //profit // int itMax = 5000; // while(unmappedNodes.Count > 0) // { // Node currentNode = unmappedNodes[0]; // unmappedNodes.RemoveAt(0); // // // itMax--; // if(itMax < 0) // return; // } }
private static void ToMesh(ref BuildRMesh mesh, ref Shape shape, float roofBaseHeight, float meshHeight, int[] facadeIndices, IVolume volume, int submesh, Surface surface, bool generateDormers = false) { //TODO fix this error properly if (shape == null) { Debug.Log("ToMesh: Error to fix"); return; } List <Edge> edges = new List <Edge>(shape.edges); List <Edge> baseEdges = new List <Edge>(shape.baseEdges); float shapeHeight = shape.HeighestPoint(); float heightScale = meshHeight / shapeHeight; bool isFloor = meshHeight < 0.00001f; Dictionary <Node, int> shapeConnectionCount = new Dictionary <Node, int>(); Dictionary <Node, List <Node> > shapeConnections = new Dictionary <Node, List <Node> >(); int edgeCount = edges.Count; for (int e = 0; e < edgeCount; e++) { Edge edge = edges[e]; if (edge.length < Mathf.Epsilon) { continue; } if (!shapeConnectionCount.ContainsKey(edge.nodeA)) { shapeConnectionCount.Add(edge.nodeA, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeA, new List <Node> { edge.nodeB }); } else { shapeConnectionCount[edge.nodeA]++; if (!shapeConnections[edge.nodeA].Contains(edge.nodeB)) { shapeConnections[edge.nodeA].Add(edge.nodeB); } } if (!shapeConnectionCount.ContainsKey(edge.nodeB)) { shapeConnectionCount.Add(edge.nodeB, 0);//start at zero - we need two edges to make a shape... shapeConnections.Add(edge.nodeB, new List <Node> { edge.nodeA }); } else { shapeConnectionCount[edge.nodeB]++; if (!shapeConnections[edge.nodeB].Contains(edge.nodeA)) { shapeConnections[edge.nodeB].Add(edge.nodeA); } } } int baseEdgeCount = baseEdges.Count; List <Vector3[]> roofFaces = new List <Vector3[]>(); for (int b = 0; b < baseEdgeCount; b++) { int facadeIndex = facadeIndices[b]; bool isGabled = volume[facadeIndex].isGabled; if (!isGabled) { int facadeIndexLeft = (facadeIndex - 1 + volume.numberOfFacades) % volume.numberOfFacades; int facadeIndexRight = (facadeIndex + 1) % volume.numberOfFacades; bool isGabledLeft = volume[facadeIndexLeft].isGabled; bool isGabledRight = volume[facadeIndexRight].isGabled; Edge baseEdge = baseEdges[b]; Node nodeA = baseEdge.nodeA; Node nodeB = baseEdge.nodeB; Node currentNode = nodeA; Node lastNode = nodeB; int itMax = 50; List <Node> edgeShape = new List <Node>() { nodeA }; while (currentNode != nodeB) { List <Node> nodeConnections = shapeConnections[currentNode]; int nodeConnectionCount = nodeConnections.Count; float minAngle = Mathf.Infinity; Node nextNode = null; Vector2 currentDirection = (currentNode.position - lastNode.position).normalized; for (int n = 0; n < nodeConnectionCount; n++) { Node connectingNode = nodeConnections[n]; if (connectingNode == lastNode) { continue; //end this circus! } Vector2 nextDirection = (connectingNode.position - currentNode.position).normalized; float nodeAngle = SignAngleDirection(currentDirection, nextDirection); if (nodeAngle < minAngle) { minAngle = nodeAngle; nextNode = connectingNode; } } if (nextNode != null) { edgeShape.Add(nextNode); lastNode = currentNode; currentNode = nextNode; } itMax--; if (itMax < 0) { break; } } int edgeShapeCount = edgeShape.Count; if (edgeShapeCount == 4 && generateDormers) { Vector3[] edgeShapeV3 = new Vector3[4]; edgeShapeV3[0] = new Vector3(edgeShape[0].position.x, roofBaseHeight, edgeShape[0].position.y); edgeShapeV3[1] = new Vector3(edgeShape[3].position.x, roofBaseHeight, edgeShape[3].position.y); edgeShapeV3[2] = new Vector3(edgeShape[1].position.x, roofBaseHeight + meshHeight, edgeShape[1].position.y); edgeShapeV3[3] = new Vector3(edgeShape[2].position.x, roofBaseHeight + meshHeight, edgeShape[2].position.y); roofFaces.Add(edgeShapeV3); } if ((isGabledLeft || isGabledRight) && edgeShapeCount == 4)//modify shape if gables are detected { Vector3 p0 = edgeShape[0].position; Vector3 p1 = edgeShape[3].position; Vector3 p2 = edgeShape[1].position; Vector3 vector = p1 - p0; Vector3 dir = vector.normalized; Vector3 cross = Vector3.Cross(Vector3.back, dir); if (isGabledLeft) { float gableThickness = volume[facadeIndexLeft].gableThickness; bool simpleGable = volume[facadeIndexLeft].simpleGable; Gable gableStyle = volume[facadeIndexLeft].gableStyle; if (!simpleGable && gableStyle == null || !isFloor) { gableThickness = 0; } Vector3 newPointA = Vector3.Project(p2 - p1, cross) + dir * gableThickness; edgeShape[1].position = edgeShape[0].position + new Vector2(newPointA.x, newPointA.y); } if (isGabledRight) { float gableThickness = volume[facadeIndexRight].gableThickness; bool simpleGable = volume[facadeIndexRight].simpleGable; Gable gableStyle = volume[facadeIndexRight].gableStyle; if (!simpleGable && gableStyle == null || !isFloor) { gableThickness = 0; } Vector3 newPointB = Vector3.Project(p2 - p1, cross) - dir * gableThickness; edgeShape[2].position = edgeShape[3].position + new Vector2(newPointB.x, newPointB.y); } } Vector3[] verts = new Vector3[edgeShapeCount]; Vector2[] uvs = new Vector2[edgeShapeCount]; Vector3 baseShapeDirection = ToV3(nodeB.position - nodeA.position).normalized; float uvAngle = SignAngle(new Vector2(baseShapeDirection.x, baseShapeDirection.z).normalized) - 90; Vector2[] faceShape = new Vector2[edgeShapeCount]; Vector3[] normals = new Vector3[edgeShapeCount]; Vector4[] tangents = new Vector4[edgeShapeCount]; Vector4 tangent = BuildRMesh.CalculateTangent(baseShapeDirection); for (int i = 0; i < edgeShapeCount; i++) { Vector3 newVert = new Vector3(edgeShape[i].position.x, edgeShape[i].height * heightScale + roofBaseHeight, edgeShape[i].position.y); verts[i] = newVert; Vector2 baseUV = (i == 0) ? Vector2.zero : new Vector2(newVert.x - verts[0].x, newVert.z - verts[0].z); Vector2 newUV = Rotate(baseUV, uvAngle); float faceHeight = edgeShape[i].height * heightScale; newUV.y = Mathf.Sqrt((newUV.y * newUV.y) + (faceHeight * faceHeight)); if (surface != null) { newUV = surface.CalculateUV(newUV); } uvs[i] = newUV; faceShape[i] = edgeShape[i].position;//used for triangulation // normals[i] = normal; tangents[i] = tangent; } // int[] tris = EarClipper.Triangulate(faceShape, 0, -1); int[] tris = Poly2TriWrapper.Triangulate(faceShape, true); int triCount = tris.Length; if (triCount < 3) { continue; } Vector3 normal = BuildRMesh.CalculateNormal(verts[tris[0]], verts[tris[1]], verts[tris[2]]); for (int i = 0; i < edgeShapeCount; i++) { normals[i] = normal;//normCal[i].normalized; } mesh.AddData(verts, uvs, tris, normals, tangents, submesh); if (isGabled) { for (int t = 0; t < triCount; t += 3) { if (tris[t] == 0 || tris[t + 1] == 0 || tris[t + 2] == 0) { int beB = edgeShapeCount - 1; if (tris[t] == beB || tris[t + 1] == beB || tris[t + 2] == beB) { Vector3 b0 = verts[0]; Vector3 b1 = verts[beB]; int topIndex = 0; for (int tx = 0; tx < 3; tx++) { if (tris[t + tx] != 0 && tris[t + tx] != beB) { topIndex = tris[t + tx]; } } Vector3 b2 = verts[topIndex]; Vector3 baseV = b1 - b0; Vector3 dir = baseV.normalized; Vector3 face = Vector3.Cross(Vector3.up, dir); // float length = baseV.magnitude; Vector3 center = Vector3.Lerp(b0, b1, 0.5f); Vector3 up = Vector3.Project(b2 - b0, Vector3.up); Vector3 b3 = center + up; mesh.AddTri(b0, b2, b3, face, submesh); //left mesh.AddTri(b1, b3, b2, -face, submesh); //right mesh.AddTri(b0, b3, b1, dir, submesh); //face //clear triangle tris[t] = 0; tris[t + 1] = 0; tris[t + 2] = 0; } } } } } else if (isFloor) { Roof roof = volume.roof; Edge baseEdge = baseEdges[b]; Node nodeA = baseEdge.nodeA; Node nodeB = baseEdge.nodeB; Vector3 p0 = new Vector3(nodeA.position.x, heightScale + roofBaseHeight, nodeA.position.y); Vector3 p1 = new Vector3(nodeB.position.x, heightScale + roofBaseHeight, nodeB.position.y); Vector3 baseV = p1 - p0; Vector3 dir = baseV.normalized; Vector3 face = Vector3.Cross(Vector3.up, dir).normalized; Vector3 parapetEdgeModifier = dir * (roof.overhang - (roof.parapetFrontDepth + roof.parapetBackDepth)) * 1.05f; p0 += parapetEdgeModifier; p1 += -parapetEdgeModifier; // p0 += face * (roof.parapetFrontDepth + roof.parapetBackDepth + roof.overhang); VolumePoint volumePoint = volume[facadeIndices[b]]; bool simpleGable = volumePoint.simpleGable; Gable gableStyle = volume[facadeIndices[b]].gableStyle; if (!simpleGable && gableStyle == null) { simpleGable = true; } float thickness = volume[facadeIndices[b]].gableThickness; float additionalHeight = volume[facadeIndices[b]].gableHeight; float height = roof.height + additionalHeight; if (simpleGable) //generate a simple gable { int wallSubmesh = mesh.submeshLibrary.SubmeshAdd(roof.wallSurface); //surfaceMapping.IndexOf(roof.wallSurface); if (wallSubmesh == -1) { wallSubmesh = submesh; } Vector3 g0 = p0; Vector3 g1 = p0 + Vector3.up * additionalHeight; Vector3 g2 = g1 + dir * roof.floorDepth * 0.5f; Vector3 g3 = g2 + dir * roof.depth * 0.5f + Vector3.up * roof.height; Vector3 g7 = p1; Vector3 g6 = p1 + Vector3.up * additionalHeight; Vector3 g5 = g6 - dir * roof.floorDepth * 0.5f; Vector3 g4 = g5 - dir * roof.depth * 0.5f + Vector3.up * roof.height; Vector3 gF = -face * thickness; mesh.AddPlane(g0, g7, g1, g6, wallSubmesh); //bottom front mesh.AddPlane(g7 + gF, g0 + gF, g6 + gF, g1 + gF, wallSubmesh); //bottom back mesh.AddPlane(g1, g6, g1 + gF, g6 + gF, wallSubmesh); //bottom top mesh.AddPlane(g0, g1, g0 + gF, g1 + gF, wallSubmesh); //bottom sides mesh.AddPlane(g6, g7, g6 + gF, g7 + gF, wallSubmesh); mesh.AddPlane(g2, g5, g3, g4, wallSubmesh); //top front mesh.AddPlane(g5 + gF, g2 + gF, g4 + gF, g3 + gF, wallSubmesh); //top back mesh.AddPlane(g2 + gF, g2, g3 + gF, g3, wallSubmesh); //top sides mesh.AddPlane(g5, g5 + gF, g4, g4 + gF, wallSubmesh); //top sides mesh.AddPlane(g3 + gF, g3, g4 + gF, g4, wallSubmesh); //top top } else { Vector2 baseUV = new Vector2(0, volume.planHeight); GableGenerator.Generate(ref mesh, gableStyle, p0, p1, height, thickness, baseUV); } } } if (generateDormers) { DormerGenerator.Generate(ref mesh, volume, roofFaces); } }