private static void Pitched(BuildRMesh mesh, Vector3[] points, Roof design, int submesh) { int numberOfVolumePoints = points.Length; Vector2[] volumePoints = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { volumePoints[i] = new Vector2(points[i].x, points[i].z); } OffsetSkeleton offsetPoly = new OffsetSkeleton(volumePoints); offsetPoly.Execute(); }
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); }
public static void OnInspectorGUI(Building _building) { EditorGUILayout.BeginVertical(GUILayout.Width(BuildingEditor.MAIN_GUI_WIDTH)); Volume volume = BuildingVolumeEditor.VolumeSelectorInspectorGUI(_building); if (volume != null) { BuildingEditor.volume = volume; } else { volume = BuildingEditor.volume; } if (volume != null) { Undo.RecordObject(BuildingEditor.volume, "Roof Modification"); Roof roof = volume.roof; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Enable"); roof.exists = EditorGUILayout.Toggle(roof.exists); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Open Interior"); roof.interiorOpen = EditorGUILayout.Toggle(roof.interiorOpen); EditorGUILayout.EndHorizontal(); // EditorGUI.BeginDisabledGroup(BuildingEditor.volume.abovePlans.Count > 0); // if (BuildingEditor.volume.abovePlans.Count > 0) // roof.type = Roof.Types.Flat; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Type"); roof.type = (Roof.Types)EditorGUILayout.EnumPopup(roof.type); EditorGUILayout.EndHorizontal(); // EditorGUI.EndDisabledGroup(); if (roof.type != Roof.Types.Flat) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Height"); roof.height = EditorGUILayout.Slider(roof.height, 0, 20);//TODO setting for max building roof height EditorGUILayout.EndHorizontal(); } // if (roof.type == Roof.Types.Gambrel) // { // EditorGUILayout.BeginHorizontal(); // EditorGUILayout.LabelField("Sub Height"); // roof.heightB = EditorGUILayout.Slider(roof.heightB, 0, 20);//TODO setting for max building roof height // EditorGUILayout.EndHorizontal(); // } if (roof.type == Roof.Types.Mansard)//|| roof.type == Roof.Types.Gambrel) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Depth"); roof.depth = EditorGUILayout.Slider(roof.depth, 0, 5); EditorGUILayout.EndHorizontal(); } if (roof.type == Roof.Types.Mansard) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Floor Depth"); roof.floorDepth = EditorGUILayout.Slider(roof.floorDepth, 0, 5); EditorGUILayout.EndHorizontal(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Overhang"); roof.overhang = EditorGUILayout.Slider(roof.overhang, 0, 5); EditorGUILayout.EndHorizontal(); // EditorGUILayout.BeginHorizontal(); //two directions of the ridge // string[] options = { "Short", "Long" }; // roof.direction = EditorGUILayout.Popup(roof.direction, options); // EditorGUILayout.EndHorizontal(); // if (roof.type == Roof.Types.Sawtooth) // { // EditorGUILayout.BeginHorizontal(); // EditorGUILayout.LabelField("Overhang"); // roof.sawtoothTeeth = EditorGUILayout.IntSlider(roof.sawtoothTeeth, 0, 10); // EditorGUILayout.EndHorizontal(); // } // if (roof.type == Roof.Types.Barrel) // { // EditorGUILayout.BeginHorizontal(); // EditorGUILayout.LabelField("Segments"); // roof.barrelSegments = EditorGUILayout.IntSlider(roof.barrelSegments, 0, 10); // EditorGUILayout.EndHorizontal(); // } EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Textures"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Main"); roof.mainSurface = EditorGUILayout.ObjectField(roof.mainSurface, typeof(Surface), false) as Surface; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Wall"); roof.wallSurface = EditorGUILayout.ObjectField(roof.wallSurface, typeof(Surface), false) as Surface; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Floor"); roof.floorSurface = EditorGUILayout.ObjectField(roof.floorSurface, typeof(Surface), false) as Surface; EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Parapet"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Enabled"); roof.parapet = EditorGUILayout.Toggle(roof.parapet); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Type"); roof.parapetStyle = (Roof.ParapetStyles)EditorGUILayout.EnumPopup(roof.parapetStyle); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Height"); roof.parapetHeight = EditorGUILayout.Slider(roof.parapetHeight, 0.01f, 3); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Front Extrusion"); roof.parapetFrontDepth = EditorGUILayout.Slider(roof.parapetFrontDepth, 0.01f, 1); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Back Extrusion"); roof.parapetBackDepth = EditorGUILayout.Slider(roof.parapetBackDepth, 0.01f, 1); EditorGUILayout.EndHorizontal(); if (roof.parapetStyle == Roof.ParapetStyles.Battlement) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Battlement Height Ratio"); roof.battlementHeightRatio = EditorGUILayout.Slider(roof.battlementHeightRatio, 0.01f, 1); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Battlement Spacing"); roof.battlementSpacing = EditorGUILayout.Slider(roof.battlementSpacing, 0.5f, 5); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); //DORMERS EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Dormer"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Enabled"); roof.hasDormers = EditorGUILayout.Toggle(roof.hasDormers); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Width"); roof.dormerWidth = EditorGUILayout.Slider(roof.dormerWidth, 0, 4); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Height"); roof.dormerHeight = EditorGUILayout.Slider(roof.dormerHeight, 0, 3); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Roof Height"); roof.dormerRoofHeight = EditorGUILayout.Slider(roof.dormerRoofHeight, 0, 3); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Spacing"); roof.minimumDormerSpacing = EditorGUILayout.Slider(roof.minimumDormerSpacing, 0, 5); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Rows"); roof.dormerRows = EditorGUILayout.IntSlider(roof.dormerRows, 1, 4); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Style"); roof.wallSection = (WallSection)EditorGUILayout.ObjectField(roof.wallSection, typeof(WallSection), false); EditorGUILayout.EndHorizontal(); if (roof.wallSection != null) { GUILayout.Label(roof.wallSection.previewTexture); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Gables"); for (int g = 0; g < volume.numberOfPoints; g++) { if (g > 0) { EditorGUILayout.Space(); } RoofFacadeInspectorGUI(volume, g); } EditorGUILayout.EndVertical(); BuildingEditor.volume.roof = roof; if (BuildingEditor.volume != null && BuildingEditor.volume.isModified) { _building.MarkModified(); } if (_building.isModified || GUI.changed) { Repaint(); } } EditorGUILayout.EndVertical(); }
//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 Flat(IBuilding building, IVolume volume, BuildRMesh mesh, BuildRCollider collider, Vector2[] points, float roofBaseHeight, Roof design, int submesh, Surface surface, Rect clampUV) { BuildRVolumeUtil.VolumeShape[] roofPoints = BuildRVolumeUtil.GetTopShape(building, volume, points); int roofShapeCount = roofPoints.Length; for (int r = 0; r < roofShapeCount; r++) { Poly2TriWrapper.BMesh(mesh, roofBaseHeight, surface, submesh, roofPoints[r].outer, clampUV, true, roofPoints[r].holes, collider); } }
public static void Generate(IBuilding building, IVolume volume, BuildRMesh mesh, BuildRCollider collider, Rect clampUV) { int numberOfPoints = volume.numberOfPoints; float totalPlanHeight = volume.planTotalHeight; Roof roof = volume.roof; bool generateColliders = building.colliderType != BuildingColliderTypes.None; if (!roof.exists) { return; } List <Vector2> roofPoints = new List <Vector2>(); List <int> facadeIndicies = new List <int>(); mesh.submeshLibrary.SubmeshAdd(roof.mainSurface); int wallSubmesh = mesh.submeshLibrary.SubmeshAdd(roof.wallSurface != null ? roof.wallSurface : roof.mainSurface); int floorSubmesh = mesh.submeshLibrary.SubmeshAdd(roof.floorSurface != null?roof.floorSurface: roof.mainSurface); bool[] facadeParapets = new bool[numberOfPoints]; for (int p = 0; p < numberOfPoints; p++) { Vector3 p0 = volume.BuildingPoint(p); roofPoints.Add(new Vector2(p0.x, p0.z)); facadeIndicies.Add(p); if (!volume.IsWallStraight(p)) { int anchorCount = volume.facadeWallAnchors[p].Count; for (int a = 1; a < anchorCount - 1; a++) { roofPoints.Add(volume.facadeWallAnchors[p][a].vector2); facadeIndicies.Add(p); } } facadeParapets[p] = BuildRFacadeUtil.HasParapet(building, volume, p); } int numberOfRoofPoints = roofPoints.Count; Vector3[] facadeNormals = new Vector3[numberOfRoofPoints]; Vector3[] facadeDirections = new Vector3[numberOfRoofPoints]; float[] facadeLengths = new float[numberOfRoofPoints]; for (int p = 0; p < numberOfRoofPoints; p++) { Vector3 p0 = roofPoints[p]; Vector3 p1 = roofPoints[(p + 1) % numberOfRoofPoints]; Vector3 facadeVector = (p1 - p0); facadeDirections[p] = facadeVector.normalized; facadeNormals[p] = Vector3.Cross(Vector3.up, facadeDirections[p]); facadeLengths[p] = facadeVector.magnitude; } Vector2[] roofPointsA = roofPoints.ToArray(); bool[] roofGables = new bool[numberOfPoints]; for (int g = 0; g < numberOfPoints; g++) { roofGables[g] = volume[g].isGabled; } Vector2[] baseRoofPoints = new Vector2[0]; if (roof.overhang > 0) { OffsetPoly polyOffset = new OffsetPoly(roofPointsA, -roof.overhang); polyOffset.Execute(); baseRoofPoints = polyOffset.Shape(); ShapeToRoofMesh.OverhangUnderside(ref mesh, roofPointsA, baseRoofPoints, totalPlanHeight, roof); } else { baseRoofPoints = roofPointsA; } if (baseRoofPoints.Length == 0) { return; } Vector2[] parapetExternalPoints = new Vector2[0]; Vector2[] parapetInternalPoints = new Vector2[0]; float parapetFrontDepth = roof.parapetFrontDepth; float parapetBackDepth = roof.parapetBackDepth; if (generateColliders) { collider.thickness = parapetFrontDepth * 0.5f + parapetBackDepth; } bool parapet = roof.parapet && building.meshType == BuildingMeshTypes.Full; if (parapet) { OffsetPoly polyOffset = new OffsetPoly(baseRoofPoints, -parapetFrontDepth); polyOffset.Execute(); parapetExternalPoints = polyOffset.Shape(); polyOffset = new OffsetPoly(baseRoofPoints, parapetBackDepth); polyOffset.Execute(); parapetInternalPoints = polyOffset.Shape(); } int roofPointCount = baseRoofPoints.Length; if (parapet && parapetExternalPoints.Length > 0 && parapetInternalPoints.Length > 0) { List <BuildRVolumeUtil.ParapetWallData> parapetShapes = BuildRVolumeUtil.GetParapetShapes(building, volume, baseRoofPoints); for (int p = 0; p < roofPointCount; p++) { BuildRVolumeUtil.ParapetWallData parapetWallData = parapetShapes[p]; int facadeIndex = facadeIndicies[p]; if (!facadeParapets[facadeIndex] || parapetWallData.type == BuildRVolumeUtil.ParapetWallData.Types.None) { continue; } int pb = (p + 1) % roofPointCount; int pbi = (p + 1) % parapetInternalPoints.Length; int pbe = (p + 1) % parapetExternalPoints.Length; int facadeIndexB = (facadeIndex + 1) % numberOfPoints; int facadeIndexC = (facadeIndex - 1 + numberOfPoints) % numberOfPoints; bool facadeParapetB = facadeParapets[facadeIndexB] && parapetShapes[facadeIndexB].type != BuildRVolumeUtil.ParapetWallData.Types.None; bool facadeParapetC = facadeParapets[facadeIndexC] && parapetShapes[facadeIndexC].type != BuildRVolumeUtil.ParapetWallData.Types.None; Vector3 p0 = new Vector3(baseRoofPoints[p].x, totalPlanHeight, baseRoofPoints[p].y); Vector3 p1 = new Vector3(baseRoofPoints[pb].x, totalPlanHeight, baseRoofPoints[pb].y); Vector3 facadeVector = (p1 - p0); Vector3 facadeDirection = facadeVector.normalized; Vector3 facadeNormal = Vector3.Cross(Vector3.up, facadeDirection); int pCount = Mathf.Min(parapetExternalPoints.Length, parapetInternalPoints.Length); if (p < pCount) { float facadeLength = facadeVector.magnitude; if (!facadeParapetC)//need to straighten the ends if no parapet exists { Vector3 parapetEndExternalC = p0 + facadeNormal * parapetFrontDepth; Vector3 parapetEndInternalC = p0 - facadeNormal * parapetBackDepth; parapetExternalPoints[p] = new Vector2(parapetEndExternalC.x, parapetEndExternalC.z); parapetInternalPoints[p] = new Vector2(parapetEndInternalC.x, parapetEndInternalC.z); } if (!facadeParapetB)//need to straighten the ends if no parapet exists { Vector3 parapetEndExternalB = p1 + facadeNormal * parapetFrontDepth; Vector3 parapetEndInternalB = p1 - facadeNormal * parapetBackDepth; parapetExternalPoints[pbe] = new Vector2(parapetEndExternalB.x, parapetEndExternalB.z); parapetInternalPoints[pbi] = new Vector2(parapetEndInternalB.x, parapetEndInternalB.z); } //external points Vector3 p0e = new Vector3(parapetExternalPoints[p].x, totalPlanHeight, parapetExternalPoints[p].y); Vector3 p1e = new Vector3(parapetExternalPoints[pbe].x, totalPlanHeight, parapetExternalPoints[pbe].y); //internal points Vector3 p0i = new Vector3(parapetInternalPoints[p].x, totalPlanHeight, parapetInternalPoints[p].y); Vector3 p1i = new Vector3(parapetInternalPoints[pbi].x, totalPlanHeight, parapetInternalPoints[pbi].y); float uvAngle = JMath.SignAngle(new Vector2(facadeDirection.x, facadeDirection.z).normalized) + 90; Vector4 facadeTangent = BuildRMesh.CalculateTangent(facadeDirection); Vector4 facadeTangentInverse = BuildRMesh.CalculateTangent(-facadeDirection); if (parapetWallData.type == BuildRVolumeUtil.ParapetWallData.Types.AtoIntersection) { Vector2 intV2 = parapetWallData.Int; Vector3 intV3 = new Vector3(intV2.x, totalPlanHeight, intV2.y); p1e = intV3 + facadeNormal * parapetFrontDepth; p1i = intV3 - facadeNormal * parapetBackDepth; } if (parapetWallData.type == BuildRVolumeUtil.ParapetWallData.Types.IntersectiontoB) { Vector2 intV2 = parapetWallData.Int; Vector3 intV3 = new Vector3(intV2.x, totalPlanHeight, intV2.y); p0e = intV3 + facadeNormal * parapetFrontDepth; p0i = intV3 - facadeNormal * parapetBackDepth; } if (roof.parapetStyle == Roof.ParapetStyles.Flat) { Vector3 parapetUp = Vector3.up * roof.parapetHeight; Vector3 w0 = p0e; //front left Vector3 w1 = p1e; //front right Vector3 w2 = p0i; //back left Vector3 w3 = p1i; //back right Vector3 w6 = w2 + parapetUp; //front left top Vector3 w7 = w3 + parapetUp; //front right top Vector3 w4 = w0 + parapetUp; //back left top Vector3 w5 = w1 + parapetUp; //back right top mesh.AddPlane(w0, w1, w4, w5, Vector2.zero, new Vector2(facadeLength, roof.parapetHeight), facadeNormal, facadeTangent, wallSubmesh, roof.wallSurface); //front mesh.AddPlane(w3, w2, w7, w6, Vector2.zero, new Vector2(facadeLength, roof.parapetHeight), -facadeNormal, facadeTangentInverse, wallSubmesh, roof.wallSurface); //back mesh.AddPlaneComplexUp(w7, w6, w5, w4, uvAngle, Vector3.up, facadeTangent, wallSubmesh, roof.wallSurface); //top if (generateColliders) { collider.AddPlane(w0, w1, w4, w5); if (!collider.usingPrimitives) { collider.mesh.AddPlane(w3, w2, w7, w6, 0); collider.mesh.AddPlane(w7, w6, w5, w4, 0); } } if (parapetFrontDepth > 0) { mesh.AddPlaneComplexUp(p0, p1, w0, w1, uvAngle, Vector3.down, facadeTangent, wallSubmesh, roof.wallSurface);//bottom } bool leftParapet = facadeParapetB; if (!leftParapet) { //todo proper calculations Vector3 leftCapNormal = Vector3.forward; mesh.AddPlane(w0, w2, w4, w6, Vector2.zero, new Vector2(parapetBackDepth + parapetFrontDepth, roof.parapetHeight), leftCapNormal, facadeTangent, wallSubmesh, roof.wallSurface);//left cap } bool rightParapet = facadeParapetC; if (!rightParapet) { //todo proper calculations Vector3 rightCapNormal = Vector3.forward; mesh.AddPlane(w3, w1, w7, w5, Vector2.zero, new Vector2(parapetBackDepth + parapetFrontDepth, roof.parapetHeight), rightCapNormal, facadeTangent, wallSubmesh, roof.wallSurface);//right cap } } else//battlements! { int battlementCount = Mathf.CeilToInt(facadeLength / roof.battlementSpacing) * 2 + 1; for (int b = 0; b < battlementCount + 1; b++) { float percentLeft = b / (float)(battlementCount); float percentRight = (b + 1f) / (battlementCount); float parapetUVStart = percentLeft * facadeLength; float parapetUVWidth = (percentRight - percentLeft) * facadeLength; Vector3 b0 = Vector3.Lerp(p0e, p1e, percentLeft); Vector3 b1 = Vector3.Lerp(p0e, p1e, percentRight); Vector3 b2 = Vector3.Lerp(p0i, p1i, percentLeft); Vector3 b3 = Vector3.Lerp(p0i, p1i, percentRight); bool upperBattlement = b % 2 == 0; float battlementUp = upperBattlement ? roof.parapetHeight : roof.parapetHeight * roof.battlementHeightRatio; Vector3 battlementUpV = Vector3.up * battlementUp; Vector3 b6 = b2 + battlementUpV; //front left top Vector3 b7 = b3 + battlementUpV; //front right top Vector3 b4 = b0 + battlementUpV; //back left top Vector3 b5 = b1 + battlementUpV; //back right top //front mesh.AddPlane(b0, b1, b4, b5, new Vector2(parapetUVStart, 0), new Vector2(parapetUVStart + parapetUVWidth, battlementUp), facadeNormal, facadeTangent, wallSubmesh, roof.wallSurface); //back mesh.AddPlane(b3, b2, b7, b6, new Vector2(parapetUVStart, 0), new Vector2(parapetUVStart + parapetUVWidth, battlementUp), -facadeNormal, facadeTangentInverse, wallSubmesh, roof.wallSurface); //top mesh.AddPlaneComplexUp(b7, b6, b5, b4, uvAngle, Vector3.up, facadeTangent, wallSubmesh, roof.wallSurface); if (parapetFrontDepth > 0) { mesh.AddPlaneComplexUp(p0, p1, b0, b1, uvAngle, Vector3.down, facadeTangent, wallSubmesh, roof.wallSurface);//bottom } if (generateColliders) { collider.AddPlane(b0, b1, b4, b5); if (!collider.usingPrimitives) { collider.mesh.AddPlane(b3, b2, b7, b6, 0); collider.mesh.AddPlane(b7, b6, b5, b4, 0); } } if (upperBattlement) { //todo proper calculations float uvBattlementCapUp = roof.parapetHeight * roof.battlementHeightRatio; Vector3 leftCapNormal = -facadeDirection; Vector4 leftCapTangent = BuildRMesh.CalculateTangent(-facadeNormal); mesh.AddPlane(b2, b0, b6, b4, new Vector2(parapetUVStart, 0), new Vector2(parapetUVStart + roof.parapetBackDepth + parapetFrontDepth, uvBattlementCapUp), leftCapNormal, leftCapTangent, wallSubmesh, roof.wallSurface);//left cap Vector3 rightCapNormal = facadeDirection; Vector4 rightCapTangent = BuildRMesh.CalculateTangent(facadeNormal); mesh.AddPlane(b1, b3, b5, b7, new Vector2(parapetUVStart, 0), new Vector2(parapetUVStart + roof.parapetBackDepth + parapetFrontDepth, uvBattlementCapUp), rightCapNormal, rightCapTangent, wallSubmesh, roof.wallSurface);//right cap if (generateColliders) { if (!collider.usingPrimitives) { collider.mesh.AddPlane(b2, b0, b6, b4, 0); collider.mesh.AddPlane(b1, b3, b5, b7, 0); } } } } } } } } Vector2[] roofFloorBasePoints = (roof.parapet && roof.parapetBackDepth > 0 && parapetInternalPoints.Length > 0) ? parapetInternalPoints : baseRoofPoints; Roof.Types roofType = roof.type; if (volume.abovePlanCount > 0) { roofType = Roof.Types.Flat; } switch (roofType) { default: Flat(building, volume, mesh, collider, roofFloorBasePoints, totalPlanHeight, roof, floorSubmesh, roof.floorSurface, clampUV); break; case Roof.Types.Pitched: if (!PitchedRoofGenerator.Generate(mesh, collider, roofFloorBasePoints, facadeIndicies.ToArray(), totalPlanHeight, volume, clampUV)) { Flat(building, volume, mesh, collider, roofFloorBasePoints, totalPlanHeight, roof, floorSubmesh, roof.floorSurface, clampUV); } break; case Roof.Types.Mansard: if (!MansardRoofGenerator.Generate(mesh, collider, roofFloorBasePoints, facadeIndicies.ToArray(), totalPlanHeight, volume)) { Flat(building, volume, mesh, collider, roofFloorBasePoints, totalPlanHeight, roof, floorSubmesh, roof.floorSurface, clampUV); } // ShapeToRoofMesh.MansardRoof(ref mesh, roofFloorBasePoints, roofGables, totalPlanHeight, roof, surfaceMapping); break; // case Roof.Types.Gambrel: // ShapeToRoofMesh.Gambrel(ref mesh, roofFloorBasePoints, roofGables, totalPlanHeight, roof, surfaceMapping); // break; } }
public static bool Generate(BuildRMesh mesh, BuildRCollider collider, Vector2[] points, int[] facadeIndices, float roofBaseHeight, IVolume volume) { if (points.Length == 0) { return(false); } if (BuildrUtils.SelfIntersectingPoly(points)) { return(false); } Roof design = volume.roof; float floorWidth = design.floorDepth; float roofDepth = design.depth; float roofHeight = design.height; Surface mainSurface = design.mainSurface; Surface floorSurface = design.floorSurface; int mainSubmesh = mesh.submeshLibrary.SubmeshAdd(mainSurface); int floorSubmesh = mesh.submeshLibrary.SubmeshAdd(floorSurface); //mansard floor if (floorWidth > 0) { OffsetSkeleton offsetFloorPoly = new OffsetSkeleton(points, null, floorWidth); offsetFloorPoly.direction = 1; offsetFloorPoly.Execute(); Shape floorShape = offsetFloorPoly.shape; if (floorShape != null) { ToMesh(ref mesh, ref floorShape, roofBaseHeight, 0, facadeIndices, volume, floorSubmesh, design.floorSurface); points = new Vector2[floorShape.terminatedNodeCount]; for (int i = 0; i < floorShape.terminatedNodeCount; i++) { points[i] = floorShape.TerminatedNode(i).position; } } else { return(false); //todo } } if (points.Length == 0) { return(false); } //mansard pitch OffsetSkeleton offsetRoofPoly = new OffsetSkeleton(points, null, roofDepth); offsetRoofPoly.direction = 1; offsetRoofPoly.Execute(); Shape roofShape = offsetRoofPoly.shape; if (roofShape == null) { return(false); } if (facadeIndices.Length < roofShape.baseEdges.Count) { return(false); } ToMesh(ref mesh, ref roofShape, roofBaseHeight, roofHeight, facadeIndices, volume, mainSubmesh, mainSurface, design.hasDormers); points = new Vector2[roofShape.terminatedNodeCount]; for (int i = 0; i < roofShape.terminatedNodeCount; i++) { points[i] = roofShape.TerminatedNode(i).position; } //mansard top ToMesh(ref mesh, points, roofBaseHeight + roofHeight, facadeIndices, volume, floorSubmesh, floorSurface); return(true); }
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); } }