public static void GenerateFacade(FacadeData data, BuildRMesh dmesh, BuildRCollider collider = null) { // Debug.Log("******************* "+data.facadeDesign.ToString()); Vector3 facadeVector = data.baseB - data.baseA; if (facadeVector.magnitude < Mathf.Epsilon) { return; } Vector3 facadeDirection = facadeVector.normalized; Vector3 facadeNormal = Vector3.Cross(facadeDirection, Vector3.up); Vector4 facadeTangent = BuildRMesh.CalculateTangent(facadeDirection); RandomGen rGen = new RandomGen(); rGen.GenerateNewSeed(); float wallThickness = data.wallThickness; float foundation = data.foundationDepth; BuildingMeshTypes meshType = data.meshType; BuildingColliderTypes colliderType = data.colliderType; int wallSections = 0; Vector2 wallSectionSize; float facadeLength = 0; if (data.isStraight) { facadeLength = facadeVector.magnitude; wallSections = Mathf.FloorToInt(facadeLength / data.minimumWallUnitLength); if (wallSections < 1) { wallSections = 1; } wallSectionSize = new Vector2(facadeLength / wallSections, data.floorHeight); } else { wallSections = data.anchors.Count - 1; if (wallSections < 1) { wallSections = 1; } float sectionWidth = Vector2.Distance(data.anchors[0].vector2, data.anchors[1].vector2); wallSectionSize = new Vector2(sectionWidth, data.floorHeight); } Dictionary <WallSection, RawMeshData> generatedSections = new Dictionary <WallSection, RawMeshData>(); Dictionary <WallSection, RawMeshData> generatedSectionMeshColliders = new Dictionary <WallSection, RawMeshData>(); Dictionary <WallSection, BuildRCollider.BBox[]> generatedSectionPrimitiveColliders = new Dictionary <WallSection, BuildRCollider.BBox[]>(); int startFloor = data.startFloor; // Debug.Log("st fl "+startFloor); // Debug.Log("fl ct "+ data.floorCount); for (int fl = startFloor; fl < data.floorCount; fl++) { // Debug.Log(fl); if (data.facadeDesign.randomisationMode == Facade.RandomisationModes.RandomRows) { generatedSections.Clear(); //recalculate each row } // Debug.Log(wallSections); for (int s = 0; s < wallSections; s++) { // Debug.Log(s); WallSection section = data.facadeDesign.GetWallSection(s, fl + data.actualStartFloor, wallSections, data.floorCount); // Debug.Log(section); dmesh.submeshLibrary.Add(section); //add the wallsection to the main submesh library RawMeshData generatedSection = null; RawMeshData generatedSectionCollider = null; BuildRCollider.BBox[] bboxes = new BuildRCollider.BBox[0]; if (section == null) { GenerationOutput output = GenerationOutput.CreateRawOutput(); GenerationOutput outputCollider = null; if (colliderType == BuildingColliderTypes.Complex) { outputCollider = GenerationOutput.CreateRawOutput(); } if (colliderType == BuildingColliderTypes.Primitive) { BuildRCollider.BBox[] bbox = WallSectionGenerator.Generate(section, wallSectionSize, wallThickness); generatedSectionPrimitiveColliders.Add(section, bbox); } WallSectionGenerator.Generate(section, output, wallSectionSize, false, wallThickness, true, outputCollider, dmesh.submeshLibrary); generatedSection = output.raw; if (outputCollider != null) { generatedSectionCollider = outputCollider.raw; } } else { if (generatedSections.ContainsKey(section)) { generatedSection = generatedSections[section]; if (generatedSectionMeshColliders.ContainsKey(section)) { generatedSectionCollider = generatedSectionMeshColliders[section]; } } else { GenerationOutput output = GenerationOutput.CreateRawOutput(); GenerationOutput outputCollider = null; bool cullOpening = data.cullDoors && section.isDoor; if (colliderType == BuildingColliderTypes.Complex) { outputCollider = GenerationOutput.CreateRawOutput(); } if (colliderType == BuildingColliderTypes.Primitive) { BuildRCollider.BBox[] bbox = WallSectionGenerator.Generate(section, wallSectionSize, wallThickness, cullOpening); generatedSectionPrimitiveColliders.Add(section, bbox); } WallSectionGenerator.Generate(section, output, wallSectionSize, false, wallThickness, cullOpening, outputCollider, dmesh.submeshLibrary); generatedSections.Add(section, output.raw); if (generatedSectionCollider != null) { generatedSectionMeshColliders.Add(section, outputCollider.raw); } generatedSection = output.raw; if (generatedSectionCollider != null) { generatedSectionCollider = outputCollider.raw; } } if (generatedSectionPrimitiveColliders.ContainsKey(section)) { bboxes = generatedSectionPrimitiveColliders[section]; } } // Debug.Log("data strt" + data.isStraight); if (data.isStraight) { Quaternion meshRot = Quaternion.LookRotation(facadeNormal, Vector3.up); Vector3 baseMeshPos = data.baseA + facadeDirection * wallSectionSize.x + Vector3.up * wallSectionSize.y; Vector3 wallSectionVector = new Vector3(wallSectionSize.x * s, wallSectionSize.y * fl, 0); baseMeshPos += meshRot * wallSectionVector; Vector3 meshPos = baseMeshPos + meshRot * -wallSectionSize * 0.5f; Vector2 uvOffset = new Vector2(wallSectionSize.x * s, wallSectionSize.y * fl); Vector2 uvOffsetScaled = uvOffset; if (section != null && section.wallSurface != null) { uvOffsetScaled = CalculateUv(uvOffsetScaled, section.wallSurface); } //TODO account for the mesh mode of the wall section - custom meshes if (meshType == BuildingMeshTypes.Full) { dmesh.AddData(generatedSection, meshPos, meshRot, Vector3.one, uvOffsetScaled); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddData(generatedSectionCollider, meshPos, meshRot, Vector3.one); } if (collider != null && bboxes.Length > 0) { collider.AddBBox(bboxes, meshPos, meshRot); } // Debug.Log("foundation"); if (fl == 0 && foundation > Mathf.Epsilon) { Vector3 fp3 = baseMeshPos + Vector3.down * wallSectionSize.y; Vector3 fp2 = fp3 - facadeDirection * wallSectionSize.x; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Full) { Surface foundationSurface = data.foundationSurface != null ? data.foundationSurface : section.wallSurface; int foundationSubmesh = dmesh.submeshLibrary.SubmeshAdd(foundationSurface); //facadeSurfaces.IndexOf(section.wallSurface)); dmesh.AddPlane(fp0, fp1, fp2, fp3, new Vector2(uvOffset.x, -foundation), new Vector2(uvOffset.x + wallSectionSize.x, 0), -facadeNormal, facadeTangent, foundationSubmesh, foundationSurface); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } else { //todo switch - support wall section based curves for now Vector3 cp0 = data.anchors[s].vector3XZ; cp0.y = data.baseA.y; Vector3 cp1 = data.anchors[s + 1].vector3XZ; cp1.y = data.baseA.y; Vector3 curveVector = cp1 - cp0; Vector3 curveDirection = curveVector.normalized; Vector3 curveNormal = Vector3.Cross(curveDirection, Vector3.up); float actualWidth = curveVector.magnitude; Quaternion meshRot = Quaternion.LookRotation(curveNormal, Vector3.up); Vector3 meshPos = cp1 + Vector3.up * wallSectionSize.y; Vector3 wallSectionVector = new Vector3(0, wallSectionSize.y * fl, 0); meshPos += meshRot * wallSectionVector; meshPos += meshRot * -new Vector3(actualWidth, wallSectionSize.y, 0) * 0.5f; Vector3 meshScale = new Vector3(actualWidth / wallSectionSize.x, 1, 1); //Thanks Anthony Cuellar - issue #12 Vector2 uvOffset = new Vector2(wallSectionVector.x, wallSectionVector.y + (section.hasOpening ? 0 : wallSectionSize.y / 2f)); Vector2 uvOffsetScaled = CalculateUv(uvOffset, section.wallSurface); //TODO account for the mesh mode of the wall section - custom meshes if (meshType == BuildingMeshTypes.Full) { dmesh.AddData(generatedSection, meshPos, meshRot, meshScale, uvOffsetScaled); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddData(generatedSectionCollider, meshPos, meshRot, meshScale); } if (collider != null && bboxes.Length > 0) { collider.AddBBox(bboxes, meshPos, meshRot); } // Debug.Log("foundation"); if (fl == 0 && foundation > Mathf.Epsilon) { Vector3 fp3 = cp1; Vector3 fp2 = fp3 - curveDirection * actualWidth; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Full) { Surface foundationSurface = data.foundationSurface != null ? data.foundationSurface : section.wallSurface; int foundationSubmesh = dmesh.submeshLibrary.SubmeshAdd(foundationSurface); //facadeSurfaces.IndexOf(section.wallSurface); dmesh.AddPlane(fp0, fp1, fp2, fp3, new Vector2(uvOffset.x, -foundation), new Vector2(uvOffset.x + actualWidth, 0), -curveNormal, facadeTangent, foundationSubmesh, foundationSurface); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } } //string course is completely ignored for a collision // Debug.Log("string"); if (fl > 0 && data.facadeDesign.stringCourse && meshType == BuildingMeshTypes.Full) //no string course on ground floor { float baseStringCoursePosition = wallSectionSize.y * fl + wallSectionSize.y * data.facadeDesign.stringCoursePosition; Vector3 scBaseUp = baseStringCoursePosition * Vector3.up; Vector3 scTopUp = (data.facadeDesign.stringCourseHeight + baseStringCoursePosition) * Vector3.up; if (data.isStraight) { Vector3 scNm = data.facadeDesign.stringCourseDepth * facadeNormal; Vector3 p0 = data.baseA; Vector3 p1 = data.baseB; Vector3 p0o = data.baseA - scNm; Vector3 p1o = data.baseB - scNm; int submesh = dmesh.submeshLibrary.SubmeshAdd(data.facadeDesign.stringCourseSurface); //data.facadeDesign.stringCourseSurface != null ? facadeSurfaces.IndexOf(data.facadeDesign.stringCourseSurface) : 0; Vector2 uvMax = new Vector2(facadeLength, data.facadeDesign.stringCourseHeight); dmesh.AddPlane(p0o + scBaseUp, p1o + scBaseUp, p0o + scTopUp, p1o + scTopUp, Vector3.zero, uvMax, -facadeNormal, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //front dmesh.AddPlane(p0 + scBaseUp, p0o + scBaseUp, p0 + scTopUp, p0o + scTopUp, facadeNormal, facadeTangent, submesh); //left dmesh.AddPlane(p1o + scBaseUp, p1 + scBaseUp, p1o + scTopUp, p1 + scTopUp, facadeNormal, facadeTangent, submesh); //right float facadeAngle = BuildrUtils.CalculateFacadeAngle(facadeDirection); dmesh.AddPlaneComplexUp(p0 + scBaseUp, p1 + scBaseUp, p0o + scBaseUp, p1o + scBaseUp, facadeAngle, Vector3.down, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //bottom dmesh.AddPlaneComplexUp(p1 + scTopUp, p0 + scTopUp, p1o + scTopUp, p0o + scTopUp, facadeAngle, Vector3.up, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //top } else { int baseCurvePointCount = data.anchors.Count; //baseCurvepoints.Count; Vector3[] interSectionNmls = new Vector3[baseCurvePointCount]; for (int i = 0; i < baseCurvePointCount - 1; i++) { Vector3 p0 = data.anchors[i].vector3XZ; //baseCurvepoints[i]; Vector3 p1 = data.anchors[i + 1].vector3XZ; //baseCurvepoints[i + 1]; Vector3 p2 = data.anchors[Mathf.Max(i - 1, 0)].vector3XZ; //baseCurvepoints[Mathf.Max(i - 1, 0)]; interSectionNmls[i] = Vector3.Cross((p1 - p0 + p0 - p2).normalized, Vector3.up); } for (int i = 0; i < baseCurvePointCount - 1; i++) { Vector3 p0 = data.anchors[i].vector3XZ; //baseCurvepoints[i]; Vector3 p1 = data.anchors[i + 1].vector3XZ; //baseCurvepoints[i + 1]; Vector3 sectionVector = p1 - p0; Vector3 sectionDir = sectionVector.normalized; Vector3 sectionNml = Vector3.Cross(sectionDir, Vector3.up); Vector4 sectionTgnt = BuildRMesh.CalculateTangent(sectionDir); Vector3 scNmA = data.facadeDesign.stringCourseDepth * interSectionNmls[i + 0]; Vector3 scNmB = data.facadeDesign.stringCourseDepth * interSectionNmls[i + 1]; Vector3 p0o = p0 - scNmA; Vector3 p1o = p1 - scNmB; int submesh = dmesh.submeshLibrary.SubmeshAdd(data.facadeDesign.stringCourseSurface); //data.facadeDesign.stringCourseSurface != null ? facadeSurfaces.IndexOf(data.facadeDesign.stringCourseSurface) : 0; dmesh.AddPlane(p0o + scBaseUp, p1o + scBaseUp, p0o + scTopUp, p1o + scTopUp, sectionNml, sectionTgnt, submesh); dmesh.AddPlane(p0 + scBaseUp, p0o + scBaseUp, p0 + scTopUp, p0o + scTopUp, sectionNml, sectionTgnt, submesh); dmesh.AddPlane(p1o + scBaseUp, p1 + scBaseUp, p1o + scTopUp, p1 + scTopUp, sectionNml, sectionTgnt, submesh); float facadeAngle = BuildrUtils.CalculateFacadeAngle(sectionDir); dmesh.AddPlaneComplexUp(p0 + scBaseUp, p1 + scBaseUp, p0o + scBaseUp, p1o + scBaseUp, facadeAngle, Vector3.down, sectionTgnt, submesh, data.facadeDesign.stringCourseSurface); //bottom dmesh.AddPlaneComplexUp(p1 + scTopUp, p0 + scTopUp, p1o + scTopUp, p0o + scTopUp, facadeAngle, Vector3.up, sectionTgnt, submesh, data.facadeDesign.stringCourseSurface); //top } } } } }
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; } }