public static void Generate(IBuilding building) { int numberOfVolumes = building.numberOfVolumes; // Debug.Log("n vol "+numberOfVolumes); for (int v = 0; v < numberOfVolumes; v++) { IVolume volume = building[v]; volume.CheckVolume(); if (!volume.isLegal) { GenerateMesh.ClearVisuals(volume); continue; } int numberOfPoints = volume.numberOfPoints; float totalPlanHeight = volume.planHeight; Vector3 planUp = totalPlanHeight * Vector3.up; VerticalOpening[] volumeOpenings = BuildrUtils.GetOpeningsQuick(building, volume); float foundation = building.IsBaseVolume(volume) ? building.foundationDepth : 0;//set suspended volumes foundation to 0 IVisualPart visual = volume.visualPart; BuildRMesh dMesh = visual.dynamicMesh; BuildRCollider cMesh = visual.colliderMesh; BuildingMeshTypes meshType = building.meshType; BuildingColliderTypes colliderType = building.colliderType; dMesh.Clear(); cMesh.Clear(); cMesh.TogglePrimitives(colliderType == BuildingColliderTypes.Primitive); cMesh.thickness = volume.wallThickness; if (colliderType == BuildingColliderTypes.None) { cMesh = null; } Transform[] prefabs = volume.prefabs.GetComponentsInChildren <Transform>(); int prefabCount = prefabs.Length; for (int p = 0; p < prefabCount; p++) { if (prefabs[p] == volume.prefabs) { continue; } if (prefabs[p] == null) { continue; //gone already man } #if UNITY_EDITOR Object.DestroyImmediate(prefabs[p].gameObject); #else Object.Destroy(prefabs[p].gameObject); #endif } Dictionary <int, List <Vector2Int> > anchorPoints = volume.facadeWallAnchors; Texture2D facadeTexture = null; #region Exteriors // Debug.Log("ext"); if (building.generateExteriors) { for (int p = 0; p < numberOfPoints; p++) { if (!volume[p].render) { continue; } Vector3 p0 = volume.BuildingPoint(p); Vector3 p1 = volume.BuildingPoint((p + 1) % numberOfPoints); Vector3 p0u = p0 + planUp; Vector3 p1u = p1 + planUp; Vector3 cw0 = volume.BuildingControlPointA(p); Vector3 cw1 = volume.BuildingControlPointB(p); Facade facade = volume.GetFacade(p); bool isStraight = volume.IsWallStraight(p); Vector3 facadeVector = p1 - p0; Vector3 facadeDirection = facadeVector.normalized; float facadeLength = facadeVector.magnitude; if (facadeLength < Mathf.Epsilon) { continue; } // Debug.Log("flength "+facadeLength); if (facade == null || colliderType == BuildingColliderTypes.Simple) { // Debug.Log("simple"); if (isStraight) { Vector3 normal = Vector3.Cross(Vector3.up, facadeDirection); Vector4 tangent = BuildRMesh.CalculateTangent(facadeDirection); if (facade == null) { dMesh.AddPlane(p0, p1, p0u, p1u, normal, tangent, 0); } if (colliderType != BuildingColliderTypes.None) { cMesh.AddPlane(p0, p1, p0u, p1u); } if (foundation > Mathf.Epsilon) { Vector3 fp2 = p0; Vector3 fp3 = p1; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (facade == null) { Surface foundationSurface = building.foundationSurface != null ? building.foundationSurface : null; int foundationSubmesh = dMesh.submeshLibrary.SubmeshAdd(foundationSurface); Vector2 uxmax = new Vector2(Vector3.Distance(p0, p1), foundation); dMesh.AddPlane(fp0, fp1, fp2, fp3, Vector2.zero, uxmax, normal, tangent, foundationSubmesh, foundationSurface); } if (colliderType != BuildingColliderTypes.None) { cMesh.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } else { List <Vector2Int> facadeAnchorPoints = anchorPoints[p]; int anchorCount = facadeAnchorPoints.Count; for (int i = 0; i < anchorCount - 1; i++) { Vector3 c0 = facadeAnchorPoints[i].vector3XZ; c0.y = p0.y; Vector3 c1 = facadeAnchorPoints[i + 1].vector3XZ; c1.y = p0.y; Vector3 c2 = c0 + planUp; Vector3 c3 = c1 + planUp; Vector3 sectionDirection = (c1 - c0).normalized; Vector3 normal = Vector3.Cross(Vector3.up, sectionDirection); Vector4 tangent = BuildRMesh.CalculateTangent(sectionDirection); if (facade == null) { dMesh.AddPlane(c0, c1, c2, c3, normal, tangent, 0); } if (colliderType != BuildingColliderTypes.None) { cMesh.AddPlane(c0, c1, c2, c3); } if (foundation > Mathf.Epsilon) { Vector3 fp2 = c0; Vector3 fp3 = c1; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (facade == null) { Surface foundationSurface = building.foundationSurface != null ? building.foundationSurface : null; int foundationSubmesh = dMesh.submeshLibrary.SubmeshAdd(foundationSurface); Vector2 uxmax = new Vector2(Vector3.Distance(c0, c1), foundation); dMesh.AddPlane(fp0, fp1, fp2, fp3, Vector2.zero, uxmax, normal, tangent, foundationSubmesh, foundationSurface); } if (colliderType != BuildingColliderTypes.None) { cMesh.AddPlane(fp0, fp1, fp2, fp3); } } } } // Debug.Log("Generate facade " + p + " " + dMesh.vertexCount ); } // Debug.Log("fac "+p); if (facade != null && (meshType == BuildingMeshTypes.Full || colliderType == BuildingColliderTypes.Primitive || colliderType == BuildingColliderTypes.Complex)) { //generate the facade // Debug.Log("full"); FacadeGenerator.FacadeData fData = new FacadeGenerator.FacadeData(); // fData.building = building; // fData.volume = volume; fData.baseA = p0; fData.baseB = p1; fData.controlA = cw0; fData.controlB = cw1; fData.anchors = anchorPoints[p]; fData.isStraight = isStraight; fData.curveStyle = volume[p].curveStyle; fData.floorCount = volume.floors; fData.facadeDesign = facade; // fData.submeshList = usedFloorplanSurfaces; fData.startFloor = BuildRFacadeUtil.MinimumFloor(building, volume, p); fData.actualStartFloor = building.VolumeBaseFloor(volume); fData.foundationDepth = foundation; fData.foundationSurface = building.foundationSurface; fData.wallThickness = volume.wallThickness; fData.minimumWallUnitLength = volume.minimumWallUnitLength; fData.floorHeight = volume.floorHeight; fData.floors = volume.floors; fData.meshType = building.meshType; fData.colliderType = building.colliderType; fData.cullDoors = building.cullDoors; fData.prefabs = volume.prefabs; // Debug.Log("mesh"); FacadeGenerator.GenerateFacade(fData, dMesh, cMesh); // Debug.Log("pref"); FacadeGenerator.GeneratePrefabs(fData); // Debug.Log("Generate facade "+p+" "+dMesh.vertexCount); } } } #endregion #region Interiors // Debug.Log("int"); bool generateInteriors = building.generateInteriors && meshType == BuildingMeshTypes.Full; if (generateInteriors) { int floors = volume.floors; IFloorplan[] floorplans = volume.InteriorFloorplans(); for (int fl = 0; fl < floors; fl++) { IFloorplan floorplan = floorplans[fl]; IVisualPart floorVisual = floorplan.visualPart; BuildRMesh flMesh = floorVisual.dynamicMesh; BuildRCollider flCollider = floorVisual.colliderMesh; flMesh.Clear(); flCollider.Clear(); flCollider.TogglePrimitives(colliderType == BuildingColliderTypes.Primitive); FloorplanGenerator.Generate(building, volume, floorplans[fl], fl, volumeOpenings, flMesh, flCollider); floorVisual.GenerateFromDynamicMesh(); floorplan.transform.localPosition = Vector3.up * (fl * volume.floorHeight); floorVisual.transform.localPosition = Vector3.zero;// floorVisual.transform.localRotation = Quaternion.identity; } } else { IFloorplan[] floorplans = volume.InteriorFloorplans(); int floors = floorplans.Length; for (int fl = 0; fl < floors; fl++) { floorplans[fl].visualPart.Clear(); } } #endregion #region Volume Underside Generation // Debug.Log("und"); BuildRVolumeUtil.VolumeShape[] underShapes = BuildRVolumeUtil.GetBottomShape(building, volume); int underShapeCount = underShapes.Length; float volumeBaseHeight = volume.baseHeight - building.foundationDepth; for (int u = 0; u < underShapeCount; u++) { if (underShapes[u].outer == null) { continue; //no underside shape } int undersideSubmesh = dMesh.submeshLibrary.SubmeshAdd(volume.undersideSurafce); Poly2TriWrapper.BMesh(dMesh, volumeBaseHeight, null, undersideSubmesh, underShapes[u].outer, new Rect(0, 0, 0, 0), false, underShapes[u].holes); } #endregion // Debug.Log("roof"); if (building.generateExteriors) { RoofGenerator.Generate(building, volume, dMesh, cMesh); visual.GenerateFromDynamicMesh(); } else { visual.Clear(); } // Debug.Log("mat"); switch (meshType) { case BuildingMeshTypes.None: visual.materials = null; break; case BuildingMeshTypes.Box: visual.materials = new[] { new Material(Shader.Find("Standard")) }; break; case BuildingMeshTypes.Simple: facadeTexture.filterMode = FilterMode.Bilinear; facadeTexture.Apply(true, false); Material simpleMaterial = new Material(Shader.Find("Standard")); simpleMaterial.mainTexture = facadeTexture; visual.materials = new[] { simpleMaterial }; break; case BuildingMeshTypes.Full: visual.materials = dMesh.materials.ToArray(); break; } } }
public static void Generate(IBuilding building) { int numberOfVolumes = building.numberOfPlans; for (int v = 0; v < numberOfVolumes; v++) { IVolume volume = building[v]; volume.CheckVolume(); if (!volume.isLegal) { GenerateMesh.ClearVisuals(volume); continue; } int numberOfPoints = volume.numberOfPoints; float totalPlanHeight = volume.planHeight; Vector3 planUp = totalPlanHeight * Vector3.up; // List<Surface> usedFloorplanSurfaces = volume.CalculateSurfaceArray(); // VerticalOpening[] volumeOpenings = BuildrUtils.GetOpeningsQuick(building, volume); IVisualPart visual = volume.visualPart; BuildRMesh dMesh = visual.dynamicMesh; BuildRCollider cMesh = visual.colliderMesh; BuildingMeshTypes meshType = building.meshType; BuildingColliderTypes colliderType = building.colliderType; dMesh.Clear(); dMesh.ignoreSubmeshAssignment = true; cMesh.Clear(); cMesh.TogglePrimitives(colliderType == BuildingColliderTypes.Primitive); cMesh.thickness = volume.wallThickness; if (meshType == BuildingMeshTypes.None && colliderType == BuildingColliderTypes.None) { visual.Clear(); return; } Dictionary <int, List <Vector2Int> > anchorPoints = volume.facadeWallAnchors; Texture2D facadeTexture = null; Rect[] faciaRectangles = null; Rect[] faciaUVs = null; Rect roofRect = new Rect(); Rect roofPixelRect = new Rect(); #region Exteriors if (building.generateExteriors) { // List<Rect> faciaRectangles = null; faciaRectangles = new Rect[numberOfPoints + 1]; //one additional for the roof float foundation = building.IsBaseVolume(volume) ? building.foundationDepth : 0; //set suspended volumes foundation to 0 // faciaRectangles = new List<Rect>(); for (int p = 0; p < numberOfPoints; p++) { if (!volume[p].render) { continue; } int indexA = p; int indexB = (p < numberOfPoints - 1) ? p + 1 : 0; Vector2Int p0 = volume[indexA].position; Vector2Int p1 = volume[indexB].position; float facadeWidth = Vector2Int.DistanceWorld(p0, p1) * PIXELS_PER_METER; int floorBase = BuildRFacadeUtil.MinimumFloor(building, volume, indexA); int numberOfFloors = volume.floors - floorBase; if (numberOfFloors < 1)//no facade - adjacent facade is taller and covers this one { continue; } float floorHeight = volume.floorHeight; float facadeHeight = ((volume.floors - floorBase) * floorHeight) * PIXELS_PER_METER; if (facadeHeight < 0)//?? { facadeWidth = 0; facadeHeight = 0; } Rect newFacadeRect = new Rect(0, 0, facadeWidth, facadeHeight); faciaRectangles[p] = newFacadeRect; // Debug.Log(newFacadeRect); // faciaRectangles.Add(newFacadeRect); } roofRect = new Rect(0, 0, volume.bounds.size.x, volume.bounds.size.z); roofPixelRect = new Rect(0, 0, volume.bounds.size.x * PIXELS_PER_METER, volume.bounds.size.z * PIXELS_PER_METER); faciaRectangles[numberOfPoints] = roofPixelRect; // Debug.Log(roofRect); int currentWidth = RectanglePack.Pack(faciaRectangles, ATLAS_PADDING); currentWidth = RectanglePack.CheckMaxScale(faciaRectangles, currentWidth, MAXIMUM_TEXTURESIZE); faciaUVs = RectanglePack.ConvertToUVSpace(faciaRectangles, currentWidth); facadeTexture = new Texture2D(currentWidth, currentWidth); // float uvOffsetX = 0; int rectIndex = 0; for (int p = 0; p < numberOfPoints; p++) { if (!volume[p].render) { continue; } Vector3 p0 = volume.BuildingPoint(p); Vector3 p1 = volume.BuildingPoint((p + 1) % numberOfPoints); Vector3 p0u = p0 + planUp; Vector3 p1u = p1 + planUp; Vector3 cw0 = volume.BuildingControlPointA(p); Vector3 cw1 = volume.BuildingControlPointB(p); Facade facade = volume.GetFacade(p); bool isStraight = volume.IsWallStraight(p); Vector3 facadeVector = p1 - p0; Vector3 facadeDirection = facadeVector.normalized; FacadeGenerator.FacadeData fData = new FacadeGenerator.FacadeData(); fData.baseA = p0; fData.baseB = p1; fData.controlA = cw0; fData.controlB = cw1; fData.anchors = anchorPoints[p]; fData.isStraight = isStraight; fData.curveStyle = volume[p].curveStyle; fData.floorCount = volume.floors; fData.facadeDesign = facade; fData.wallThickness = volume.wallThickness; fData.minimumWallUnitLength = volume.minimumWallUnitLength; fData.floorHeight = volume.floorHeight; fData.floors = volume.floors; fData.meshType = building.meshType; fData.colliderType = building.colliderType; fData.cullDoors = building.cullDoors; fData.prefabs = volume.prefabs; // fData.submeshList = usedFloorplanSurfaces; fData.startFloor = BuildRFacadeUtil.MinimumFloor(building, volume, p); if (isStraight) { Vector3 normal = Vector3.Cross(Vector3.up, facadeDirection); Vector4 tangent = BuildRMesh.CalculateTangent(facadeDirection); Vector3 fp2 = p0; Vector3 fp3 = p1; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Simple) { // if(facade != null) // { if (facade != null) { SimpleTextureGenerator.GenerateFacade(fData, facadeTexture, faciaRectangles[rectIndex]); } Vector3[] verts = { p0, p1, p0u, p1u }; Vector2[] uvs = new Vector2[4]; Rect uvRect = faciaUVs[rectIndex]; uvs[0] = new Vector2(uvRect.xMin, uvRect.yMin); uvs[1] = new Vector2(uvRect.xMax, uvRect.yMin); uvs[2] = new Vector2(uvRect.xMin, uvRect.yMax); uvs[3] = new Vector2(uvRect.xMax, uvRect.yMax); int[] tris = { 0, 2, 1, 1, 2, 3 }; Vector3[] norms = { normal, normal, normal, normal }; Vector4[] tangents = { tangent, tangent, tangent, tangent }; dMesh.AddData(verts, uvs, tris, norms, tangents, 0); if (foundation > Mathf.Epsilon) { dMesh.AddPlane(fp0, fp1, fp2, fp3, uvs[0], uvs[0], normal, tangent, 0, null); } } else { dMesh.AddPlane(p0, p1, p0u, p1u, normal, tangent, 0); if (foundation > Mathf.Epsilon) { dMesh.AddPlane(fp0, fp1, fp2, fp3, normal, tangent, 0); } } if (colliderType != BuildingColliderTypes.None) { cMesh.AddPlane(p0, p1, p0u, p1u); if (foundation > Mathf.Epsilon) { cMesh.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } else { List <Vector2Int> facadeAnchorPoints = anchorPoints[p]; int anchorCount = facadeAnchorPoints.Count; for (int i = 0; i < anchorCount - 1; i++) { Vector3 c0 = facadeAnchorPoints[i].vector3XZ; c0.y = p0.y; Vector3 c1 = facadeAnchorPoints[i + 1].vector3XZ; c1.y = p0.y; Vector3 c2 = c0 + planUp; Vector3 c3 = c1 + planUp; Vector3 sectionDirection = (c1 - c0).normalized; Vector3 normal = Vector3.Cross(Vector3.up, sectionDirection); Vector4 tangent = BuildRMesh.CalculateTangent(sectionDirection); Vector3 fp2 = c0; Vector3 fp3 = c1; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Simple) { if (facade != null) { SimpleTextureGenerator.GenerateFacade(fData, facadeTexture, faciaRectangles[rectIndex]); } Rect uvRect = faciaUVs[rectIndex]; float facadePercentA = i / (float)(anchorCount - 1); float facadePercentB = (i + 1) / (float)(anchorCount - 1); float uvxa = uvRect.xMin + uvRect.width * facadePercentA; float uvxb = uvRect.xMin + uvRect.width * facadePercentB; Vector3[] verts = { c0, c1, c2, c3 }; Vector2[] uvs = new Vector2[4]; uvs[0] = new Vector2(uvxa, uvRect.yMin); uvs[1] = new Vector2(uvxb, uvRect.yMin); uvs[2] = new Vector2(uvxa, uvRect.yMax); uvs[3] = new Vector2(uvxb, uvRect.yMax); int[] tris = { 0, 2, 1, 1, 2, 3 }; Vector3[] norms = { normal, normal, normal, normal }; Vector4[] tangents = { tangent, tangent, tangent, tangent }; // Vector2 uvMin = new Vector2(uvOffsetX, 0); // Vector2 uvMax = new Vector2(uvOffsetX + facadeLength, totalPlanHeight); dMesh.AddData(verts, uvs, tris, norms, tangents, 0); // dMesh.AddPlane(p0, p1, p0u, p1u, uvMin, uvMax, normal, tangent, 0); //todo simple mesh with textured facade // rectIndex++; if (foundation > Mathf.Epsilon) { dMesh.AddPlane(fp0, fp1, fp2, fp3, uvs[0], uvs[0], normal, tangent, 0, null); } } else { dMesh.AddPlane(p0, p1, p0u, p1u, normal, tangent, 0); if (foundation > Mathf.Epsilon) { dMesh.AddPlane(fp0, fp1, fp2, fp3, normal, tangent, 0); } } if (colliderType != BuildingColliderTypes.None) { cMesh.AddPlane(c0, c1, c2, c3); if (foundation > Mathf.Epsilon) { cMesh.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } } rectIndex++; } } #endregion #region Interiors IFloorplan[] floorplans = volume.InteriorFloorplans(); int floors = volume.floors; for (int fl = 0; fl < floors; fl++) { floorplans[fl].visualPart.Clear(); } #endregion #region Volume Underside Generation BuildRVolumeUtil.VolumeShape[] underShapes = BuildRVolumeUtil.GetBottomShape(building, volume); int underShapeCount = underShapes.Length; // Debug.Log(underShapeCount); float volumeBaseHeight = volume.baseHeight; for (int u = 0; u < underShapeCount; u++) { // Debug.Log(underShapes[u].outer); if (underShapes[u].outer == null) { continue; //no underside shape } // Debug.Log(underShapes[u].outer.Length); Poly2TriWrapper.BMesh(dMesh, volumeBaseHeight, null, 0, underShapes[u].outer, new Rect(0, 0, 0, 0), false, underShapes[u].holes); } #endregion if (building.generateExteriors) { Surface roofSurface = volume.roof.mainSurface; if (roofSurface != null) { SimpleTextureGenerator.GenerateTexture(facadeTexture, roofSurface, faciaRectangles[faciaRectangles.Length - 1], roofRect); } RoofGenerator.Generate(building, volume, dMesh, cMesh, faciaUVs[faciaUVs.Length - 1]); visual.GenerateFromDynamicMesh(); } else { visual.Clear(); } switch (meshType) { case BuildingMeshTypes.Box: visual.materials = new[] { new Material(Shader.Find("Standard")) }; break; case BuildingMeshTypes.Simple: facadeTexture.filterMode = FilterMode.Bilinear; facadeTexture.Apply(true, false); Material simpleMaterial = new Material(Shader.Find("Standard")); simpleMaterial.mainTexture = facadeTexture; visual.materials = new[] { simpleMaterial }; break; } } }
private static void DrawFloorplans(IBuilding _building, IVolume mouseOverPlan) { int numberOfFloorplans = _building.numberOfPlans; Vector3 position = _building.transform.position; Quaternion rotation = _building.transform.rotation; Camera sceneCamera = Camera.current; Vector3[] centerPoints = new Vector3[numberOfFloorplans]; for (int f = 0; f < numberOfFloorplans; f++) { centerPoints[f] = BuildrUtils.CalculateFloorplanCenter(_building[f]); } // Ray ray = sceneCamera.ScreenPointToRay(new Vector3(Event.current.mousePosition.x, Screen.height - Event.current.mousePosition.y - 30, 0)); // Floorplan mouseOverPlan = BuildrUtils.OnFloorplanSelectionClick(_building, ray); //Draw Floorplan outlines for (int f = 0; f < numberOfFloorplans; f++) { IVolume volume = _building[f]; int numberOfPoints = volume.numberOfPoints; Vector2 leftPoint = Vector2.right; Vector3 labelPoint = volume.BuildingPoint(0) + position; int numberOfTopPoints = 0; for (int p = 0; p < numberOfPoints; p++) { if (volume.IsWallStraight(p)) { numberOfTopPoints++; } else { numberOfTopPoints += 9; } } Vector3[] planVs = new Vector3[numberOfTopPoints]; int planVIndex = 0; Vector3 planUp = Vector3.up * volume.planHeight; for (int p = 0; p < numberOfPoints; p++) { Vector3 p0 = volume.WorldPoint(p) + planUp;//rotation * floorplan.BuildingPoint(p) + position); Vector2 p0ss = sceneCamera.WorldToScreenPoint(p0); if (p0ss.x < leftPoint.x) { leftPoint = p0ss; labelPoint = p0; } //Render handles if (volume.IsWallStraight(p)) { planVs[planVIndex] = p0; planVIndex++; } else { Vector3 p1 = volume.WorldPoint((p + 1) % numberOfPoints) + planUp; //rotation * floorplan.BuildingPoint((p + 1) % numberOfPoints) + position); Vector3 cw0 = volume.WorldControlPointA(p) + planUp; //rotation * floorplan.BuildingControlPoint(p) + position); Vector3 cw1 = volume.WorldControlPointB(p) + planUp; //rotation * floorplan.BuildingControlPoint(p) + position); Vector3[] curveWall = new Vector3[10]; for (int t = 0; t < 10; t++) { Vector3 cp = FacadeSpline.Calculate(p0, cw0, cw1, p1, t / 9f); curveWall[t] = cp; if (t < 9) { planVs[planVIndex] = cp; planVIndex++; } } } } if ((Volume)volume == BuildingEditor.volume) { Handles.color = SELECTED_BLUEPRINT_COLOUR; } else if (mouseOverPlan == volume) { Handles.color = HIGHLIGHTED_BLUEPRINT_COLOUR; } else { Handles.color = UNSELECTED_BLUEPRINT_COLOUR; } // Handles.DrawAAConvexPolygon(planVs);//draw plan outline DrawConcavePolygonHandle.Shape(planVs, Handles.color); // Vector2 textDimensions = GUI.skin.label.CalcSize(new GUIContent(volume.name)); Handles.Label(labelPoint, volume.name); int linkCount = volume.linkPlanCount; Vector3 thisCenter = centerPoints[f]; Handles.color = Color.green; for (int l = 0; l < linkCount; l++) { if (f == l) { continue; } // Volume link = volume.linkedPlans[l]; Vector3 linkCenter = centerPoints[l]; Handles.DrawDottedLine(thisCenter, linkCenter, 5); } int numberOfFloors = volume.floors; float planHeight = volume.floorHeight; float totalPlanHeight = volume.planHeight; Vector3 planHeightV = totalPlanHeight * Vector3.up; Handles.color = new Color(0, 0.2f, 1, 0.5f); for (int p = 0; p < numberOfPoints; p++) { Vector3 p0 = rotation * volume.BuildingPoint(p) + position; Vector3 p1 = rotation * volume.BuildingPoint((p + 1) % numberOfPoints) + position; //Render handles if (volume.IsWallStraight(p)) { int wallSections = Mathf.FloorToInt(Vector3.Distance(p0, p1) / volume.minimumWallUnitLength); if (wallSections < 1) { wallSections = 1; } for (int ws = 0; ws < wallSections + 1; ws++) { float lerp = ws / ((float)wallSections); Vector3 basePos = Vector3.Lerp(p0, p1, lerp); Handles.DrawLine(basePos, basePos + planHeightV); } for (int fl = 0; fl < numberOfFloors + 1; fl++) { Handles.color = fl == 0 || fl == numberOfFloors ? MAIN_LINE_COLOUR : SUB_LINE_COLOUR; float lineHeight = fl * planHeight; Vector3 lineHeightV = lineHeight * Vector3.up; Handles.DrawLine(p0 + lineHeightV, p1 + lineHeightV); } } else { Vector3 cw0 = volume.WorldControlPointA(p); Vector3 cw1 = volume.WorldControlPointB(p); Vector3[] curveWall = new Vector3[10]; float arcLength = 0; for (int t = 0; t < 10; t++) { Vector3 cp = FacadeSpline.Calculate(p0, cw0, cw1, p1, t / 9f); curveWall[t] = cp; if (t > 0) { arcLength += Vector3.Distance(curveWall[t - 1], curveWall[t]); } } for (int fl = 0; fl < numberOfFloors + 1; fl++) { Handles.color = fl == 0 || fl == numberOfFloors ? MAIN_LINE_COLOUR : SUB_LINE_COLOUR; float lineHeight = fl * planHeight; Vector3 lineHeightV = lineHeight * Vector3.up; for (int t = 0; t < 9; t++) { Vector3 cwp0 = curveWall[t]; Vector3 cwp1 = curveWall[t + 1]; Handles.DrawLine(cwp0 + lineHeightV, cwp1 + lineHeightV); } // Handles.DrawLine(p0 + lineHeightV, p1 + lineHeightV); } } Handles.color = MAIN_LINE_COLOUR; Handles.DrawLine(p0, p0 + Vector3.up * totalPlanHeight); if ((Volume)volume == BuildingEditor.volume) { Vector3 facadeCenter = Vector3.Lerp(p0, p1, 0.5f) + planUp; // float gtbSize = HandleUtility.GetHandleSize(facadeCenter) * 0.1f; // Handles.Label(facadeCenter, "Is gabled"); // Handles.Button(facadeCenter, Quaternion.identity, gtbSize, gtbSize, Handles.DotCap); Handles.BeginGUI(); Vector2 portalScreenPos = Camera.current.WorldToScreenPoint(facadeCenter); portalScreenPos.y = Camera.current.pixelHeight - portalScreenPos.y; Rect screenRect = new Rect(portalScreenPos, new Vector2(350, 500)); GUILayout.BeginArea(screenRect); EditorGUILayout.LabelField(string.Format("Facade: {0}", p + 1)); // RoofFacadeInspectorGUI(volume, p); GUILayout.EndArea(); Handles.EndGUI(); } } } }
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; } }