public void UpdateCollider() { if (data.generateCollider != BuildrData.ColliderGenerationModes.None) { if (data.floorHeight == 0) return; if (colliderMesh == null) colliderMesh = new DynamicMeshGenericMultiMaterialMesh(); colliderMesh.Clear(); colliderMesh.subMeshCount = 1; BuildrBuildingCollider.Build(colliderMesh, data); colliderMesh.Build(false); int numberOfStairMeshes = colliderMesh.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { string meshName = "collider"; if (numberOfStairMeshes > 1) meshName += " mesh " + (i + 1); GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; meshFilt = newMeshHolder.AddComponent<MeshFilter>(); meshRend = newMeshHolder.AddComponent<MeshRenderer>(); meshFilt.mesh = colliderMesh[i].mesh; colliderHolders.Add(newMeshHolder); } } }
private static void ExportCollider(BuildrData data) { DynamicMeshGenericMultiMaterialMesh COL_MESH = new DynamicMeshGenericMultiMaterialMesh(); COL_MESH.subMeshCount = data.textures.Count; BuildrBuildingCollider.Build(COL_MESH, data); // COL_MESH.CollapseSubmeshes(); COL_MESH.Build(false); ExportMaterial[] exportTextures = new ExportMaterial[1]; ExportMaterial newTexture = new ExportMaterial(); newTexture.name = "blank"; newTexture.filepath = ""; newTexture.generated = true; exportTextures[0] = newTexture; int numberOfColliderMeshes = COL_MESH.meshCount; for (int i = 0; i < numberOfColliderMeshes; i++) { MeshUtility.Optimize(COL_MESH[i].mesh); string ColliderSuffixIndex = ((numberOfColliderMeshes > 1) ? "_" + i : ""); string ColliderFileName = data.exportFilename + COLLIDER_SUFFIX + ColliderSuffixIndex; string ColliderFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(ColliderFileName, ColliderFolder, data, COL_MESH[i].mesh, exportTextures); } }
/// <summary> /// generate an array of gameobjects that contain all the generated detail meshes - ready to display in a scene /// </summary> /// <param name="mesh"></param> /// <param name="data"></param> /// <returns></returns> public static GameObject[] Render(DynamicMeshGenericMultiMaterialMesh mesh, BuildrData data) { List<GameObject> detailGameobjects = new List<GameObject>(); int numberOfDetails = data.details.Count; if (numberOfDetails == 0) return detailGameobjects.ToArray(); BuildrDetailExportObject exportObject = Build(mesh, data); int numberOfMeshes = exportObject.detailMeshes.Length; if (numberOfMeshes == 0) return detailGameobjects.ToArray(); if (detailMat == null) detailMat = new Material(Shader.Find("Diffuse")); detailMat.mainTexture = detailtexture; for (int i = 0; i < numberOfMeshes; i++) { GameObject details = new GameObject("details " + i); details.AddComponent<MeshFilter>().mesh = exportObject.detailMeshes[i]; details.AddComponent<MeshRenderer>().sharedMaterial = detailMat; detailGameobjects.Add(details); } // Debug.Log("BuildR Detail Pack Complete: " + (Time.realtimeSinceStartup - timestart) + " sec"); return detailGameobjects.ToArray(); }
public void UpdateCollider() { if (data.generateCollider != BuildrData.ColliderGenerationModes.None) { if (data.floorHeight == 0) { return; } if (colliderMesh == null) { colliderMesh = new DynamicMeshGenericMultiMaterialMesh(); } colliderMesh.Clear(); colliderMesh.subMeshCount = 1; BuildrBuildingCollider.Build(colliderMesh, data); colliderMesh.Build(false); int numberOfStairMeshes = colliderMesh.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { string meshName = "collider"; if (numberOfStairMeshes > 1) { meshName += " mesh " + (i + 1); } GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; meshFilt = newMeshHolder.AddComponent <MeshFilter>(); meshRend = newMeshHolder.AddComponent <MeshRenderer>(); meshFilt.mesh = colliderMesh[i].mesh; colliderHolders.Add(newMeshHolder); } } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { switch(_data.generateCollider) { case BuildrData.ColliderGenerationModes.None: return; // break; case BuildrData.ColliderGenerationModes.Simple: BuildSimple(_mesh,_data); break; case BuildrData.ColliderGenerationModes.Complex: BuildrBuilding.Build(_mesh,_data); BuildrRoof.Build(_mesh, _data); int numberOfVolumes = _data.plan.numberOfVolumes; for(int v = 0; v < numberOfVolumes; v++) { BuildrInteriors.Build(_mesh,_data,v); BuildrStairs.Build(_mesh,_data,v,BuildrStairs.StairModes.Flat,false); } _mesh.CollapseSubmeshes(); break; } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { switch (_data.generateCollider) { case BuildrData.ColliderGenerationModes.None: return; // break; case BuildrData.ColliderGenerationModes.Simple: BuildSimple(_mesh, _data); break; case BuildrData.ColliderGenerationModes.Complex: BuildrBuilding.Build(_mesh, _data); BuildrRoof.Build(_mesh, _data); int numberOfVolumes = _data.plan.numberOfVolumes; for (int v = 0; v < numberOfVolumes; v++) { BuildrInteriors.Build(_mesh, _data, v); BuildrStairs.Build(_mesh, _data, v, BuildrStairs.StairModes.Flat, false); } _mesh.CollapseSubmeshes(); break; } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { int numberOfFacades = _data.plan.numberOfFacades; Rect[] dummyUVConstraints = new Rect[numberOfFacades]; for (int i = 0; i < numberOfFacades; i++) dummyUVConstraints[i] = new Rect(0,0,1,1); Build(_mesh, _data, dummyUVConstraints); }
private static int[] ExportStairwells(BuildrData data) { int numberOfVolumes = data.plan.numberOfVolumes; int[] returnNumberOfMeshes = new int[numberOfVolumes]; for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = data.plan.volumes[v]; int numberOfUnpackedTextures = data.textures.Count; List <ExportMaterial> exportTextures = new List <ExportMaterial>(); if (!volume.generateStairs) { continue; } DynamicMeshGenericMultiMaterialMesh INT_STAIRWELL = new DynamicMeshGenericMultiMaterialMesh(); INT_STAIRWELL.subMeshCount = data.textures.Count; BuildrStairs.Build(INT_STAIRWELL, data, v, BuildrStairs.StairModes.Stepped, true); INT_STAIRWELL.Build(data.includeTangents); List <int> unusedStairTextures = INT_STAIRWELL.unusedSubmeshes; numberOfUnpackedTextures = data.textures.Count; for (int t = 0; t < numberOfUnpackedTextures; t++) { if (unusedStairTextures.Contains(t)) { continue;//skip, unused } ExportMaterial newTexture = new ExportMaterial(); newTexture.name = data.textures[t].name; newTexture.material = data.textures[t].material; newTexture.generated = false; newTexture.filepath = data.textures[t].filePath; exportTextures.Add(newTexture); } int numberOfStairMeshes = INT_STAIRWELL.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { MeshUtility.Optimize(INT_STAIRWELL[i].mesh); string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numberOfStairMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(DetailFileName, DetailFolder, data, INT_STAIRWELL[i].mesh, exportTextures.ToArray()); } returnNumberOfMeshes[v] = numberOfStairMeshes; } return(returnNumberOfMeshes); }
public void UpdateDetails() { int numberOfDetails = 0; if (details != null) { numberOfDetails = details.Length; } for (int i = 0; i < numberOfDetails; i++) { DestroyImmediate(details[i]); } if (data.plan == null) { return; } if (data.floorHeight == 0) { return; } if (data.details.Count == 0) { return; } if (detailMesh == null) { detailMesh = new DynamicMeshGenericMultiMaterialMesh(); } if (renderMode != renderModes.full) { return;//once data is cleared - asses if we want to rerender the details } details = BuildrBuildingDetails.Render(detailMesh, data); numberOfDetails = details.Length; for (int i = 0; i < numberOfDetails; i++) { details[i].transform.parent = transform; details[i].transform.localPosition = Vector3.zero; details[i].transform.localRotation = Quaternion.identity; } }
private static void ExportLowLOD(BuildrData data) { DynamicMeshGenericMultiMaterialMesh dynLODMesh = new DynamicMeshGenericMultiMaterialMesh(); dynLODMesh.subMeshCount = data.textures.Count; BuildrBuildingLowDetail2.Build(dynLODMesh, data); dynLODMesh.CollapseSubmeshes(); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.80f); dynLODMesh.Build(data.includeTangents); Mesh LODMesh = dynLODMesh[0].mesh;//TODO: support many meshes MeshUtility.Optimize(LODMesh); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.90f); string textureName = data.exportFilename + ATLASED_SUFFIX + LOD_SUFFIX; string textureFileName = textureName + ".png"; string newDirectory = ROOT_FOLDER + data.exportFilename; File.WriteAllBytes(newDirectory + "/" + textureFileName, data.LODTextureAtlas.EncodeToPNG()); ExportMaterial[] exportTextures = new ExportMaterial[1]; ExportMaterial newTexture = new ExportMaterial(); newTexture.name = textureName; newTexture.filepath = textureFileName; newTexture.generated = true; exportTextures[0] = newTexture; string LODFileName = data.exportFilename + LOD_SUFFIX; string LODFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(LODFileName, LODFolder, data, LODMesh, exportTextures); if (data.placeIntoScene) { AssetDatabase.Refresh();//ensure the database is up to date... string filePath = LODFolder + LODFileName + FILE_EXTENTION; GameObject newModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(filePath)); newModel.transform.position = CURRENT_TRANSFORM.position; newModel.transform.rotation = CURRENT_TRANSFORM.rotation; } Texture2D.DestroyImmediate(data.textureAtlas); Texture2D.DestroyImmediate(data.LODTextureAtlas); }
public void UpdateInteriors() { while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } interiorMeshes.Clear(); if (data.renderInteriors) { int numberOfVolumes = data.plan.numberOfVolumes; for (int v = 0; v < numberOfVolumes; v++) { DynamicMeshGenericMultiMaterialMesh interiorMesh = new DynamicMeshGenericMultiMaterialMesh(); interiorMesh.subMeshCount = data.textures.Count; BuildrInteriors.Build(interiorMesh, data, v); interiorMesh.Build(false); int numberOfInteriorMeshes = interiorMesh.meshCount; for (int i = 0; i < numberOfInteriorMeshes; i++) { string meshName = "model interior"; if (numberOfVolumes > 0) { meshName += " volume " + (v + 1); } if (numberOfInteriorMeshes > 1) { meshName += " mesh " + (i + 1); } GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent <MeshFilter>(); meshRend = newMeshHolder.AddComponent <MeshRenderer>(); meshFilt.mesh = interiorMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); } } } }
private static void ExportCollider(TrackBuildR data) { DynamicMeshGenericMultiMaterialMesh COL_MESH = new DynamicMeshGenericMultiMaterialMesh(); // COL_MESH.subMeshCount = data.textures.Count; // BuildrBuildingCollider.Build(COL_MESH, data); // COL_MESH.CollapseSubmeshes(); COL_MESH.Build(false); ExportMaterial[] exportTextures = new ExportMaterial[1]; ExportMaterial newTexture = new ExportMaterial(); newTexture.name = "blank"; newTexture.filepath = ""; newTexture.generated = true; exportTextures[0] = newTexture; int numberOfColliderMeshes = COL_MESH.meshCount; for (int i = 0; i < numberOfColliderMeshes; i++) { MeshUtility.Optimize(COL_MESH[i].mesh); string ColliderSuffixIndex = ((numberOfColliderMeshes > 1) ? "_" + i : ""); string ColliderFileName = data.exportFilename + COLLIDER_SUFFIX + ColliderSuffixIndex; string ColliderFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(ColliderFileName, ColliderFolder, data, COL_MESH[i].mesh, exportTextures); } //string newDirectory = rootFolder+track.exportFilename; //if(!CreateFolder(newDirectory)) // return; // ExportMaterial[] exportTextures = new ExportMaterial[1]; // ExportMaterial newTexture = new ExportMaterial(); // newTexture.customName = ""; // newTexture.filepath = ""; // newTexture.generated = true; // exportTextures[0] = newTexture; // Export(track.exportFilename + COLLIDER_SUFFIX, ROOT_FOLDER + track.exportFilename + "/", track, EXPORT_MESH, exportTextures); // // COL_MESH = null; // EXPORT_MESH = null; }
/// <summary> /// generate an array of gameobjects that contain all the generated detail meshes - ready to display in a scene /// </summary> /// <param name="mesh"></param> /// <param name="data"></param> /// <returns></returns> public static GameObject[] Render(DynamicMeshGenericMultiMaterialMesh mesh, BuildrData data) { List <GameObject> detailGameobjects = new List <GameObject>(); int numberOfDetails = data.details.Count; if (numberOfDetails == 0) { return(detailGameobjects.ToArray()); } BuildrDetailExportObject exportObject = Build(mesh, data); int numberOfMeshes = exportObject.detailMeshes.Length; if (numberOfMeshes == 0) { return(detailGameobjects.ToArray()); } if (detailMat == null) { detailMat = new Material(Shader.Find("Diffuse")); } detailMat.mainTexture = detailtexture; for (int i = 0; i < numberOfMeshes; i++) { GameObject details = new GameObject("details " + i); details.AddComponent <MeshFilter>().mesh = exportObject.detailMeshes[i]; details.AddComponent <MeshRenderer>().sharedMaterial = detailMat; detailGameobjects.Add(details); } // Debug.Log("BuildR Detail Pack Complete: " + (Time.realtimeSinceStartup - timestart) + " sec"); return(detailGameobjects.ToArray()); }
//returns the number of meshes private static int ExportDetails(BuildrData data) { DynamicMeshGenericMultiMaterialMesh DET_MESH = new DynamicMeshGenericMultiMaterialMesh(); BuildrDetailExportObject exportObject = BuildrBuildingDetails.Build(DET_MESH, data); int numberOfMeshes = exportObject.detailMeshes.Length; if (numberOfMeshes == 0) { return(0); } string textureName = data.exportFilename + ATLASED_SUFFIX + DETAIL_SUFFIX; string textureFileName = textureName + ".png"; string newDirectory = ROOT_FOLDER + data.exportFilename; File.WriteAllBytes(newDirectory + "/" + textureFileName, exportObject.texture.EncodeToPNG()); ExportMaterial[] exportTextures = new ExportMaterial[1]; ExportMaterial newTexture = new ExportMaterial(); newTexture.name = textureName; newTexture.filepath = textureFileName; newTexture.generated = true; exportTextures[0] = newTexture; for (int i = 0; i < numberOfMeshes; i++) { string DetailSuffixIndex = ((numberOfMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + DETAIL_SUFFIX + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(DetailFileName, DetailFolder, data, exportObject.detailMeshes[i], exportTextures); } Texture2D.DestroyImmediate(exportObject.texture); return(numberOfMeshes); }
/// <summary> /// Generate the detail meshes and return the export object /// </summary> /// <param name="mesh"></param> /// <param name="data"></param> /// <returns></returns> public static BuildrDetailExportObject Build(DynamicMeshGenericMultiMaterialMesh mesh, BuildrData data) { BuildrDetailExportObject exportObject = new BuildrDetailExportObject(); List<Texture2D> detailTextures = new List<Texture2D>(); List<int> detailSubmeshesWithTextures = new List<int>(); int numberOfDetails = data.details.Count; mesh.Clear(); mesh.subMeshCount = numberOfDetails; for(int d = 0; d < numberOfDetails; d++) { BuildrDetail detail = data.details[d]; if(detail.mesh == null) continue; int faceIndex = detail.face; Vector3 position = Vector3.zero; BuildrPlan plan = data.plan; int numberOfVolumes = plan.numberOfVolumes; Vector2 faceUv = detail.faceUv; Quaternion faceAngle = Quaternion.identity; //Place the detail mesh if (detail.type == BuildrDetail.Types.Facade) { //find facade int facadeCount = 0; bool facadeFound = false; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int p = 0; p < numberOfVolumePoints; p++) { if (facadeCount == faceIndex) { int indexA = p; int indexB = (p + 1) % numberOfVolumePoints; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; Vector3 basePosition = Vector3.Lerp(p0, p1, faceUv.x); Vector3 detailHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight * faceUv.y); Vector3 facadeCross = Vector3.Cross(Vector3.up, p1 - p0).normalized; Vector3 detailDepth = facadeCross * detail.faceHeight; faceAngle = Quaternion.LookRotation(facadeCross); position = basePosition + detailHeight + detailDepth; facadeFound = true; break; } facadeCount++; } if (facadeFound) break; } } else//roof detail { BuildrVolume volume = plan.volumes[Mathf.Clamp(0,numberOfVolumes-1,faceIndex)]; int numberOfVolumePoints = volume.points.Count; Vector3 minimumRoofPoint = plan.points[volume.points[0]].vector3; Vector3 maximumRoofPoint = minimumRoofPoint; for (int p = 1; p < numberOfVolumePoints; p++) { Vector3 p0 = plan.points[volume.points[p]].vector3; if (p0.x < minimumRoofPoint.x) minimumRoofPoint.x = p0.x; if (p0.z < minimumRoofPoint.y) minimumRoofPoint.y = p0.z; if (p0.x > maximumRoofPoint.x) maximumRoofPoint.x = p0.x; if (p0.z > maximumRoofPoint.y) maximumRoofPoint.y = p0.z; } position.x = Mathf.Lerp(minimumRoofPoint.x, maximumRoofPoint.x, faceUv.x); position.z = Mathf.Lerp(minimumRoofPoint.y, maximumRoofPoint.y, faceUv.y); position.y = volume.numberOfFloors * data.floorHeight + detail.faceHeight; } Quaternion userRotation = Quaternion.Euler(detail.userRotation); int vertexCount = detail.mesh.vertexCount; Vector3[] verts = new Vector3[vertexCount]; Quaternion rotate = faceAngle * userRotation; for (int i = 0; i < vertexCount; i++) { Vector3 sourceVertex = Vector3.Scale(detail.mesh.vertices[i], detail.scale); Vector3 outputVertex = (rotate) * sourceVertex + position; verts[i] = outputVertex; } mesh.AddData(verts, detail.mesh.uv, detail.mesh.triangles, d); detail.worldPosition = position; detail.worldRotation = rotate; if (detail.material.mainTexture != null) { #if UNITY_EDITOR string texturePath = AssetDatabase.GetAssetPath(detail.material.mainTexture); TextureImporter textureImporter = (TextureImporter)AssetImporter.GetAtPath(texturePath); if (!textureImporter.isReadable) { Debug.LogWarning("The texture you have selected is not readable. Cannot render"); return exportObject; } detailTextures.Add((Texture2D)detail.material.mainTexture); detailSubmeshesWithTextures.Add(d); #endif } } if(detailtexture!=null) Object.DestroyImmediate(detailtexture); List<Mesh> outputMeshes = new List<Mesh>(); if (detailSubmeshesWithTextures.Count > 0) { Rect[] textureRects = BuildrTexturePacker2.Pack(out detailtexture, detailTextures.ToArray(), 512); if(detailSubmeshesWithTextures.Count > 0) mesh.Atlas(detailSubmeshesWithTextures.ToArray(), textureRects); mesh.CollapseSubmeshes(); mesh.Build(); int numberOfMeshes = mesh.meshCount; for (int i = 0; i < numberOfMeshes; i++) outputMeshes.Add(mesh[i].mesh); } exportObject.detailMeshes = outputMeshes.ToArray(); exportObject.texture = detailtexture; return exportObject; /*if (detailMat == null) detailMat = new Material(Shader.Find("Diffuse")); detailMat.mainTexture = detailtexture; List<Mesh> outputMeshes = new List<Mesh>(); for (int i = 0; i < numberOfMeshes; i++) { outputMeshes.Add(mesh[i].mesh); GameObject details = new GameObject("details " + i); details.AddComponent<MeshFilter>().mesh = mesh[i].mesh; details.AddComponent<MeshRenderer>().sharedMaterial = detailMat; detailGameobjects.Add(details); } } // Debug.Log("BuildR Detail Pack Complete: " + (Time.realtimeSinceStartup - timestart) + " sec"); return detailGameobjects.ToArray();*/ }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { // timestart = Time.realtimeSinceStartup; data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrPlan plan = data.plan; int facadeIndex = 0; numberOfFacades = 0; int numberOfVolumes = data.plan.numberOfVolumes; LogTimer("Start"); //define rectangles that represent the facades packedTexturePositions.Clear(); for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) continue; int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector2z p0 = plan.points[volume.points[indexA]]; Vector2z p1 = plan.points[volume.points[indexB]]; float facadeWidth = Vector2z.Distance(p0, p1) * PIXELS_PER_METER; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1)//no facade - adjacent facade is taller and covers this one continue; float floorHeight = data.floorHeight; float facadeHeight = (volume.numberOfFloors - floorBase) * floorHeight * PIXELS_PER_METER; if (facadeHeight < 0) { facadeWidth = 0; facadeHeight = 0; } Rect newFacadeRect = new Rect(0, 0, facadeWidth, facadeHeight); packedTexturePositions.Add(newFacadeRect); numberOfFacades++; } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.name = "Roof Mesh"; dynMeshRoof.subMeshCount = textures.Length; BuildrRoof.Build(dynMeshRoof, data, true); dynMeshRoof.CheckMaxTextureUVs(data); LogTimer("Roof A"); roofTextures.Clear(); roofTextureIndex.Clear(); foreach (BuildrRoofDesign roofDesign in data.roofs) { foreach (int textureIndex in roofDesign.textureValues) { if (!roofTextureIndex.Contains(textureIndex)) { BuildrTexture bTexture = data.textures[textureIndex]; Vector2 largestSubmeshPlaneSize = new Vector2(1,1); Vector2 minWorldUvSize = dynMeshRoof.MinWorldUvSize(textureIndex); Vector2 maxWorldUvSize = dynMeshRoof.MaxWorldUvSize(textureIndex); largestSubmeshPlaneSize.x = maxWorldUvSize.x - minWorldUvSize.x; largestSubmeshPlaneSize.y = maxWorldUvSize.y - minWorldUvSize.y; int roofTextureWidth = Mathf.RoundToInt(largestSubmeshPlaneSize.x * PIXELS_PER_METER); int roofTextureHeight = Mathf.RoundToInt(largestSubmeshPlaneSize.y * PIXELS_PER_METER); Rect newRoofTexutureRect = new Rect(0, 0, roofTextureWidth, roofTextureHeight); packedTexturePositions.Add(newRoofTexutureRect); roofTextures.Add(bTexture); roofTextureIndex.Add(textureIndex); } } } //run a custom packer to define their postions textureWidth = RectanglePack.Pack(packedTexturePositions,ATLAS_PADDING); //determine the resize scale and apply that to the rects packedScale = 1; int numberOfRects = packedTexturePositions.Count; if (textureWidth > MAXIMUM_TEXTURESIZE) { packedScale = MAXIMUM_TEXTURESIZE / (float)textureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; //Debug.Log("Rects "+roofTextures[i-+packedTexturePositions[i]); } textureWidth = Mathf.RoundToInt(packedScale * textureWidth); } else { textureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(textureWidth - 1, 2)) + 1));//find the next power of two } //Debug.Log("Texture Width "+textureWidth); //TODO: maybe restrict the resize to a power of two? LogTimer("Packed Rect Generated"); textureSize = textureWidth * textureWidth; colourArray = new Color32[textureSize]; //TestRectColours();//this test paints all the facades with rainbow colours - real pretty BuildTextures(); LogTimer("texture created"); Texture2D packedTexture = new Texture2D(textureWidth, textureWidth, TextureFormat.ARGB32, true); packedTexture.filterMode = FilterMode.Bilinear; packedTexture.SetPixels32(colourArray); packedTexture.Apply(true, false); LogTimer("apply"); if (data.LODTextureAtlas != null) Object.DestroyImmediate(data.LODTextureAtlas); data.LODTextureAtlas = packedTexture; data.LODTextureAtlas.name = "Low Detail Texture"; //build the model with new uvs if (data.drawUnderside) { for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3; newEndUVs[i] = Vector2.zero; } List<int> tris = new List<int>(data.plan.GetTrianglesBySectorBase(s)); tris.Reverse(); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } } LogTimer("Floor"); //Build facades for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) continue; int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(s, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; Rect facadeRect = packedTexturePositions[facadeIndex]; float imageSize = textureWidth; Vector2 uvMin = new Vector2(facadeRect.xMin / imageSize, facadeRect.yMin / imageSize); Vector2 uvMax = new Vector2(facadeRect.xMax / imageSize, facadeRect.yMax / imageSize); mesh.AddPlane(w0, w1, w2, w3, uvMin, uvMax, 0); facadeIndex++; } } LogTimer("Facades"); //ROOF Textures int roofRectBase = numberOfFacades; List<Rect> newAtlasRects = new List<Rect>(); for (int i = roofRectBase; i < packedTexturePositions.Count; i++) { Rect uvRect = new Rect();//generate a UV based rectangle off the packed one uvRect.x = packedTexturePositions[i].x / textureWidth; uvRect.y = packedTexturePositions[i].y / textureWidth; uvRect.width = packedTexturePositions[i].width / textureWidth; uvRect.height = packedTexturePositions[i].height / textureWidth; newAtlasRects.Add(uvRect); } dynMeshRoof.Atlas(roofTextureIndex.ToArray(), newAtlasRects.ToArray(), data.textures.ToArray()); //Add the atlased mesh data to the main model data at submesh 0 mesh.AddData(dynMeshRoof.vertices, dynMeshRoof.uv, dynMeshRoof.triangles, 0); LogTimer("Roof B"); data = null; mesh = null; textures = null; //atlasRects = null; LogTimer("Done"); System.GC.Collect(); }
public void UpdateInteriors() { while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } interiorMeshes.Clear(); if (data.renderInteriors) { int numberOfVolumes = data.plan.numberOfVolumes; for(int v = 0; v < numberOfVolumes; v++) { DynamicMeshGenericMultiMaterialMesh interiorMesh = new DynamicMeshGenericMultiMaterialMesh(); interiorMesh.subMeshCount = data.textures.Count; BuildrVolume volume = _data.plan.volumes[v]; BuildrInteriors.Build(interiorMesh, data, v); interiorMesh.Build(false); List<int> unusedInteriorTextures = interiorMesh.unusedSubmeshes; int numberOfInteriorMaterials = data.textures.Count; List<Material> interiorMaterials = new List<Material>(); for (int m = 0; m < numberOfInteriorMaterials; m++) { if (unusedInteriorTextures.Contains(m)) continue;//skip, unused BuildrTexture bTexture = data.textures[m]; interiorMaterials.Add(bTexture.usedMaterial); } int numberOfInteriorMeshes = interiorMesh.meshCount; for (int i = 0; i < numberOfInteriorMeshes; i++) { string meshName = "model interior"; if (numberOfVolumes > 0) meshName += " volume " + (v + 1); if(numberOfInteriorMeshes>1)meshName += " mesh " + (i + 1); GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent<MeshFilter>(); meshRend = newMeshHolder.AddComponent<MeshRenderer>(); meshFilt.mesh = interiorMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); int numberOfInterior = interiorMeshHolders.Count; for (int m = 0; m < numberOfInterior; m++) meshRend.sharedMaterials = interiorMaterials.ToArray(); } interiorMeshes.Add(interiorMesh); if(!volume.generateStairs) continue; DynamicMeshGenericMultiMaterialMesh stairwellMesh = new DynamicMeshGenericMultiMaterialMesh(); stairwellMesh.subMeshCount = data.textures.Count; BuildrStairs.Build(stairwellMesh, data, v, BuildrStairs.StairModes.Stepped, true); stairwellMesh.Build(false); List<int> unusedStairTextures = stairwellMesh.unusedSubmeshes; int numberOfStairMaterials = data.textures.Count; List<Material> stairMaterials = new List<Material>(); for (int m = 0; m < numberOfStairMaterials; m++) { if (unusedStairTextures.Contains(m)) continue;//skip, unused BuildrTexture bTexture = data.textures[m]; stairMaterials.Add(bTexture.usedMaterial); } int numberOfStairMeshes = stairwellMesh.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { string meshName = "model stairs"; if (numberOfVolumes > 0) meshName += " volume " + (v + 1); if (numberOfStairMeshes > 1) meshName += " mesh " + (i + 1); GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = volume.stairBaseVector[i]; meshFilt = newMeshHolder.AddComponent<MeshFilter>(); meshRend = newMeshHolder.AddComponent<MeshRenderer>(); meshFilt.mesh = stairwellMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); meshRend.sharedMaterials = stairMaterials.ToArray(); } interiorMeshes.Add(stairwellMesh); } } }
private static int[] ExportStairwells(BuildrData data) { int numberOfVolumes = data.plan.numberOfVolumes; int[] returnNumberOfMeshes = new int[numberOfVolumes]; for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = data.plan.volumes[v]; int numberOfUnpackedTextures = data.textures.Count; List<ExportMaterial> exportTextures = new List<ExportMaterial>(); if (!volume.generateStairs) continue; DynamicMeshGenericMultiMaterialMesh INT_STAIRWELL = new DynamicMeshGenericMultiMaterialMesh(); INT_STAIRWELL.subMeshCount = data.textures.Count; BuildrStairs.Build(INT_STAIRWELL, data, v, BuildrStairs.StairModes.Stepped, true); INT_STAIRWELL.Build(data.includeTangents); List<int> unusedStairTextures = INT_STAIRWELL.unusedSubmeshes; numberOfUnpackedTextures = data.textures.Count; for (int t = 0; t < numberOfUnpackedTextures; t++) { if (unusedStairTextures.Contains(t)) continue;//skip, unused ExportMaterial newTexture = new ExportMaterial(); newTexture.name = data.textures[t].name; newTexture.material = data.textures[t].material; newTexture.generated = false; newTexture.filepath = data.textures[t].filePath; exportTextures.Add(newTexture); } int numberOfStairMeshes = INT_STAIRWELL.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { MeshUtility.Optimize(INT_STAIRWELL[i].mesh); string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numberOfStairMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(DetailFileName, DetailFolder, data, INT_STAIRWELL[i].mesh, exportTextures.ToArray()); } returnNumberOfMeshes[v] = numberOfStairMeshes; } return returnNumberOfMeshes; }
private static void BuildSimple(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { BuildrData data = _data; DynamicMeshGenericMultiMaterialMesh mesh = _mesh; BuildrPlan plan = data.plan; int facadeIndex = 0; int numberOfVolumes = data.plan.numberOfVolumes; //Build Floor if (data.drawUnderside) { for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3; newEndUVs[i] = Vector2.zero; } List <int> tris = new List <int>(data.plan.GetTrianglesBySectorBase(s)); tris.Reverse(); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.subMeshCount = data.textures.Count; BuildrRoof.Build(dynMeshRoof, data, true); mesh.AddData(dynMeshRoof.vertices, dynMeshRoof.uv, dynMeshRoof.triangles, 0); Vector3 foundationVector = Vector3.down * data.foundationHeight; //Build facades for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int l = 0; l < numberOfVolumePoints; l++) { int indexA = l; int indexB = (l < numberOfVolumePoints - 1) ? l + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(s, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; if (floorBase == 0) { w0 += foundationVector; w1 += foundationVector; } mesh.AddPlane(w0, w1, w2, w3, Vector2.zero, Vector2.zero, 0); facadeIndex++; } } data = null; mesh = null; }
private static void ExportModel(BuildrData data) { try { EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.0f); //check overwrites... string newDirectory = ROOT_FOLDER + data.exportFilename; if (!CreateFolder(newDirectory)) { EditorUtility.ClearProgressBar(); return; } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.05f); if (data.fullmesh) { //export unpacked model DYN_MESH = new DynamicMeshGenericMultiMaterialMesh(); DYN_MESH.subMeshCount = data.textures.Count; BuildrBuilding.Build(DYN_MESH, data); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.30f); BuildrRoof.Build(DYN_MESH, data); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.60f); DYN_MESH.Build(data.includeTangents); int meshCount = DYN_MESH.meshCount; List <int> unusedTextures = DYN_MESH.unusedSubmeshes; int numberOfUnpackedTextures = data.textures.Count; List <ExportMaterial> exportTextureList = new List <ExportMaterial>(); for (int t = 0; t < numberOfUnpackedTextures; t++) { if (unusedTextures.Contains(t)) { continue;//skip, unused } ExportMaterial newTexture = new ExportMaterial(); newTexture.name = data.textures[t].name; newTexture.material = data.textures[t].material; newTexture.generated = false; newTexture.filepath = data.textures[t].filePath; exportTextureList.Add(newTexture); } for (int i = 0; i < meshCount; i++) { EXPORT_MESH = DYN_MESH[i].mesh; MeshUtility.Optimize(EXPORT_MESH); Export(data, EXPORT_MESH, exportTextureList.ToArray()); string filenameSuffix = (meshCount > 1)? i.ToString() : ""; string filename = data.exportFilename + filenameSuffix; Export(filename, ROOT_FOLDER + data.exportFilename + "/", data, EXPORT_MESH, exportTextureList.ToArray()); } } //Export Collider if (data.generateCollider != BuildrData.ColliderGenerationModes.None) { ExportCollider(data); } int[] numberOfInteriorMeshes = new int[data.plan.numberOfVolumes]; if (data.renderInteriors && data.fullmesh) { numberOfInteriorMeshes = ExportInteriors(data); } int[] numberOfStairwells = new int[data.plan.numberOfVolumes]; if (data.renderInteriors && data.fullmesh) { numberOfStairwells = ExportStairwells(data); } int numberOfDetailMeshes = 0; if (data.fullmesh) { numberOfDetailMeshes = ExportDetails(data); } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.70f); //Place exported version into scene if (data.fullmesh) { AssetDatabase.Refresh();//ensure the database is up to date... GameObject baseObject = new GameObject(data.exportFilename); if ((data.createPrefabOnExport || data.placeIntoScene)) { baseObject.transform.position = CURRENT_TRANSFORM.position; baseObject.transform.rotation = CURRENT_TRANSFORM.rotation; string modelFilePath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + FILE_EXTENTION; GameObject newModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(modelFilePath)); newModel.name = "model"; newModel.transform.parent = baseObject.transform; newModel.transform.localPosition = Vector3.zero; newModel.transform.localRotation = Quaternion.identity; if (data.generateCollider != BuildrData.ColliderGenerationModes.None) { GameObject colliderObject = new GameObject("collider"); string colliderFilePath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + COLLIDER_SUFFIX + FILE_EXTENTION; colliderObject.AddComponent <MeshCollider>().sharedMesh = (Mesh)AssetDatabase.LoadAssetAtPath(colliderFilePath, typeof(Mesh)); colliderObject.transform.parent = baseObject.transform; colliderObject.transform.localPosition = Vector3.zero; colliderObject.transform.localRotation = Quaternion.identity; } for (int i = 0; i < numberOfDetailMeshes; i++) { string detailSuffixIndex = ((numberOfDetailMeshes > 1) ? "_" + i : ""); string detailFileName = data.exportFilename + DETAIL_SUFFIX + detailSuffixIndex; string detailFolder = ROOT_FOLDER + data.exportFilename + "/"; string detailFilepath = detailFolder + detailFileName + FILE_EXTENTION; GameObject detailObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(detailFilepath)); detailObject.name = "details"; detailObject.transform.parent = baseObject.transform; detailObject.transform.localPosition = Vector3.zero; detailObject.transform.localRotation = Quaternion.identity; } int numberOfVolumes = data.plan.numberOfVolumes; GameObject interiorHolder = new GameObject("interiors"); interiorHolder.transform.parent = baseObject.transform; interiorHolder.transform.localPosition = Vector3.zero; interiorHolder.transform.localRotation = Quaternion.identity; for (int v = 0; v < numberOfInteriorMeshes.Length; v++) { int numMeshes = numberOfInteriorMeshes[v]; for (int i = 0; i < numMeshes; i++) { string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + INTERIOR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; string filePath = DetailFolder + DetailFileName + FILE_EXTENTION; GameObject interiorObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(filePath)); interiorObject.name = INTERIOR_SUFFIX + VolumeSuffix + DetailSuffixIndex; interiorObject.transform.parent = interiorHolder.transform; interiorObject.transform.localPosition = Vector3.zero; interiorObject.transform.localRotation = Quaternion.identity; } } for (int v = 0; v < numberOfStairwells.Length; v++) { int numMeshes = numberOfStairwells[v]; for (int i = 0; i < numMeshes; i++) { string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; string filePath = DetailFolder + DetailFileName + FILE_EXTENTION; GameObject interiorObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(filePath)); interiorObject.name = STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; interiorObject.transform.parent = interiorHolder.transform; interiorObject.transform.localPosition = data.plan.volumes[v].stairBaseVector[i]; interiorObject.transform.localRotation = Quaternion.identity; } } } if (data.createPrefabOnExport) { string prefabPath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + ".prefab"; Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); if (prefab == null) { prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); } PrefabUtility.ReplacePrefab(baseObject, prefab, ReplacePrefabOptions.ConnectToPrefab); } if (!data.placeIntoScene) { Object.DestroyImmediate(baseObject); } } if (data.exportLowLOD) { ExportLowLOD(data); } DYN_MESH = null; EXPORT_MESH = null; EditorUtility.ClearProgressBar(); EditorUtility.UnloadUnusedAssets(); AssetDatabase.Refresh(); }catch (System.Exception e) { Debug.LogError("BuildR Export Error: " + e); EditorUtility.ClearProgressBar(); } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, int volumeIndex) { data = _data; mesh = _mesh; mesh.name = "Interior Mesh Volume " + volumeIndex; textures = data.textures.ToArray(); if(!data.renderInteriors) return; float largestDepthValue = 0;//deepest value of a bay design in the building float tallestBay = 0; foreach (BuildrBay bay in data.bays) { largestDepthValue = Mathf.Max(largestDepthValue, bay.deepestValue);//get the deepest value tallestBay = Mathf.Max(tallestBay, bay.openingHeight + (data.floorHeight - bay.openingHeight) * bay.openingHeightRatio); } foreach (BuildrFacadeDesign facade in data.facades) { if(facade.type != BuildrFacadeDesign.types.simple) continue; largestDepthValue = Mathf.Max(largestDepthValue, facade.simpleBay.deepestValue);//get the deepest value if(facade.simpleBay.isOpening) tallestBay = Mathf.Max(tallestBay, facade.simpleBay.openingHeight + (data.floorHeight - facade.simpleBay.openingHeight) * facade.simpleBay.openingHeightRatio); } BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; BuildrVolume volume = plan.volumes[volumeIndex]; int numberOfFloors = volume.numberOfFloors; float floorHeight = data.floorHeight; Vector3 floorHeightVector = Vector3.up * floorHeight; float ceilingHeight = tallestBay + (floorHeight - tallestBay) * data.interiorCeilingHeight; //Calculate the internal floor plan points int numberOfVolumePoints = volume.points.Count; Vector2z[] interiorVolumePoints = new Vector2z[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { Vector3 lastPoint = plan.points[volume.points[(i > 0) ? i - 1 : numberOfVolumePoints - 1]].vector3; Vector3 thisPoint = plan.points[volume.points[i]].vector3; Vector3 nextPoint = plan.points[volume.points[(i + 1) % numberOfVolumePoints]].vector3; Vector3 normalA = Vector3.Cross(thisPoint - lastPoint, Vector3.up).normalized; Vector3 normalB = Vector3.Cross(nextPoint - thisPoint, Vector3.up).normalized; Vector2z facadeALine = new Vector2z(thisPoint - lastPoint); Vector2z facadeBLine = new Vector2z(thisPoint - nextPoint); //Calculate facade inner origins for floors Vector3 facadeOriginV3A = lastPoint + normalA * largestDepthValue; Vector3 facadeOriginV3B = nextPoint + normalB * largestDepthValue; Vector2z facadeOriginA = new Vector2z(facadeOriginV3A); Vector2z facadeOriginB = new Vector2z(facadeOriginV3B); Vector2z facadeLineIntersection = BuildrUtils.FindIntersection(facadeALine, facadeOriginA, facadeBLine, facadeOriginB); interiorVolumePoints[i] = facadeLineIntersection; } List<Vector2z> interiorVolumePointList = new List<Vector2z>(interiorVolumePoints); List<Rect> volumeCores = new List<Rect>(); List<int> linkedPoints = new List<int>(); foreach (Rect core in plan.cores) { Vector2z coreCenter = new Vector2z(core.center); if (BuildrUtils.PointInsidePoly(coreCenter, interiorVolumePoints)) { volumeCores.Add(core); } } int numberOfVolumeCores = volumeCores.Count; bool print = plan.volumes.IndexOf(volume) == 3; for (int c = 0; c < numberOfVolumeCores; c++) { int numberOfInteriorPoints = interiorVolumePointList.Count; Rect coreBounds = volumeCores[c]; Vector2z coreCenter = new Vector2z(coreBounds.center); Vector2z coreBL = new Vector2z(coreBounds.xMin, coreBounds.yMin); Vector2z coreBR = new Vector2z(coreBounds.xMax, coreBounds.yMin); Vector2z coreTL = new Vector2z(coreBounds.xMin, coreBounds.yMax); Vector2z coreTR = new Vector2z(coreBounds.xMax, coreBounds.yMax); Vector2z[] corePointArray; corePointArray = new[] { coreBL, coreBR, coreTR, coreTL }; //Find the nearest legal cut we can make to join the core and interior point poly int connectingPoint = -1; float connectingPointDistance = Mathf.Infinity; for (int p = 0; p < numberOfInteriorPoints; p++) { if(linkedPoints.Contains(p)) continue; Vector2z thisPoint = interiorVolumePointList[p]; float thisPointDistance = Vector2z.SqrMag(thisPoint, coreCenter); if (thisPointDistance < connectingPointDistance) { bool legalCut = true; for (int pc = 0; pc < numberOfInteriorPoints; pc++) { Vector2z p0 = interiorVolumePointList[pc]; Vector2z p1 = interiorVolumePointList[(pc + 1) % numberOfInteriorPoints]; if (BuildrUtils.FastLineIntersection(coreCenter, thisPoint, p0, p1))//check against all lines that this new cut doesn't intersect { if (print) Debug.Log("FLI "+pc+" "+coreCenter+" "+thisPoint+" "+p0+" "+p1); legalCut = false; break; } } if (legalCut) { connectingPoint = p; connectingPointDistance = thisPointDistance; } } } if(connectingPoint==-1) { Debug.Log("Buildr Could not place core"); continue; } Vector2z chosenPoint = interiorVolumePointList[connectingPoint]; int connectingCorePoint = 0; float connectingCorePointDistance = Mathf.Infinity;// Vector2z.SqrMag(corePointArray[0], chosenPoint); for (int cp = 0; cp < 4; cp++)//find the core point to make the cut { float thisCorePointDistance = Vector2z.SqrMag(corePointArray[cp], chosenPoint); if (thisCorePointDistance < connectingCorePointDistance) { connectingCorePoint = cp; connectingCorePointDistance = thisCorePointDistance; } } interiorVolumePointList.Insert(connectingPoint, chosenPoint);//loop back on the floorplan to close it for (int acp = 0; acp < 5; acp++)//loop back on itself to close the core { interiorVolumePointList.Insert(connectingPoint + 1, corePointArray[(connectingCorePoint + acp) % 4]); } for(int i = 0; i < linkedPoints.Count; i++) { if (linkedPoints[i] > connectingPoint) linkedPoints[i] += 7; } linkedPoints.AddRange(new[]{connectingPoint, connectingPoint + 1, connectingPoint + 2, connectingPoint + 3, connectingPoint + 4, connectingPoint + 5, connectingPoint + 6}); // linkedPoints.AddRange(new []{connectingPoint,connectingPoint+6}); } // if(linkedPoints.Count > 0) // Debug.Log(linkedPoints.Count+" "+linkedPoints[0]); Vector2z[] interiorPointListCore = interiorVolumePointList.ToArray(); for (int f = 0; f < numberOfVolumePoints; f++) { ///WALLS int indexAM = Mathf.Abs((f - 1) % numberOfVolumePoints); int indexA = f; int indexB = (f + 1) % numberOfVolumePoints; int indexBP = (f + 2) % numberOfVolumePoints; Vector3 p0m = plan.points[volume.points[indexAM]].vector3; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; Vector3 p1p = plan.points[volume.points[indexBP]].vector3; Vector3 p0interior = interiorVolumePoints[indexA].vector3; Vector3 p1interior = interiorVolumePoints[indexB].vector3; float facadeWidth = Vector3.Distance(p0, p1) - largestDepthValue * 2.0f; Vector3 facadeDirection = (p1 - p0).normalized; Vector3 facadeCross = Vector3.Cross(facadeDirection, Vector3.up); Vector3 lastFacadeDirection = (p0 - p0m).normalized; Vector3 nextFacadeDirection = (p1p - p1).normalized; //only bother with facade directions when facade may intersect inverted geometry float facadeDirDotL = Vector3.Dot(-facadeDirection, lastFacadeDirection); float facadeCrossDotL = Vector3.Dot(-facadeCross, lastFacadeDirection); if (facadeDirDotL <= 0 || facadeCrossDotL <= 0) lastFacadeDirection = -facadeCross; float facadeDirDotN = Vector3.Dot(-facadeDirection, nextFacadeDirection); float facadeCrossDotN = Vector3.Dot(-facadeCross, nextFacadeDirection); if (facadeDirDotN <= 0 || facadeCrossDotN <= 0) nextFacadeDirection = facadeCross; int floorBase = plan.GetFacadeFloorHeight(volumeIndex, volume.points[indexA], volume.points[indexB]); BuildrVolumeStylesUnit[] styleUnits = volume.styles.GetContentsByFacade(volume.points[indexA]); int floorPatternSize = 0; List<int> facadePatternReference = new List<int>();//this contains a list of all the facade style indices to refence when looking for the appropriate style per floor int patternCount = 0; foreach (BuildrVolumeStylesUnit styleUnit in styleUnits)//need to knw how big all the styles are together so we can loop through them { floorPatternSize += styleUnit.floors; for (int i = 0; i < styleUnit.floors; i++) facadePatternReference.Add(patternCount); patternCount++; } facadePatternReference.Reverse(); int rows = numberOfFloors; Vector2 facadeUV = Vector2.zero; for (int r = 0; r < rows; r++) { float currentFloorHeight = floorHeight * r; Vector3 currentFloorHeightVector = Vector3.up * (data.floorHeight * r); Vector3 facadeFloorBaseVector = p0 + Vector3.up * currentFloorHeight; Vector3 ceilingVector = Vector3.up * ceilingHeight; if (r < floorBase) { //no facade rendered //facade gap filler //interior gap points Vector3 i0 = p1 - facadeDirection.normalized * largestDepthValue; Vector3 i1 = p0 + facadeDirection.normalized * largestDepthValue; Vector3 w0 = i0 + currentFloorHeightVector; Vector3 w1 = i1 + currentFloorHeightVector; Vector3 w2 = w0 + facadeCross * largestDepthValue; Vector3 w3 = w1 + facadeCross * largestDepthValue; Vector3 w4 = w0 + ceilingVector; Vector3 w5 = w1 + ceilingVector; Vector3 w6 = w2 + ceilingVector; Vector3 w7 = w3 + ceilingVector; Vector3 w8 = p1interior + currentFloorHeightVector; Vector3 w9 = p0interior + currentFloorHeightVector; Vector3 w10 = w8 + ceilingVector; Vector3 w11 = w9 + ceilingVector; //floor AddData(new[] { w0, w1, w2, w3 }, new[] { 0, 1, 2, 1, 3, 2 }, volume.FloorTexture(r), false); //ceiling AddData(new[] { w5, w4, w7, w6 }, new[] { 0, 1, 2, 1, 3, 2 }, volume.CeilingTexture(r), false); //sides int wallSubmesh = volume.WallTexture(r); AddPlane(w0, w2, w4, w6, wallSubmesh, false, Vector3.zero, new Vector2(largestDepthValue, floorHeight)); AddPlane(w3, w1, w7, w5, wallSubmesh, false, Vector3.zero, new Vector2(largestDepthValue, floorHeight)); //other gaps float uvWidth1 = Vector3.Distance(w2, w8); AddPlane(w2, w8, w6, w10, wallSubmesh, false, Vector3.zero, new Vector2(uvWidth1, floorHeight)); float uvWidth2 = Vector3.Distance(w3, w9); AddPlane(w9, w3, w11, w7, wallSubmesh, false, Vector3.zero, new Vector2(uvWidth2, floorHeight)); continue; } //Get the facade style id //need to loop through the facade designs floor by floor until we get to the right one int modFloor = ((r - floorBase) % floorPatternSize); facadeDesign = data.facades[styleUnits[facadePatternReference[modFloor]].styleID]; bool isBlankWall = !facadeDesign.hasWindows; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { if (data.bays.Count == 0 || facadeDesign.bayPattern.Count == 0) { data.illegal = true; return; } BuildrBay firstBay = data.bays[facadeDesign.bayPattern[0]]; if (firstBay.openingWidth > facadeWidth) isBlankWall = true; if (facadeDesign.bayPattern.Count == 0) isBlankWall = true; } else { if (facadeDesign.simpleBay.openingWidth + facadeDesign.simpleBay.minimumBayWidth > facadeWidth) isBlankWall = true; } if (!isBlankWall) { float patternSize = 0;//the space the pattern fills, there will be a gap that will be distributed to all bay styles int numberOfBays = 0; BuildrBay[] bayDesignPattern; int numberOfBayDesigns; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { numberOfBayDesigns = facadeDesign.bayPattern.Count; bayDesignPattern = new BuildrBay[numberOfBayDesigns]; for (int i = 0; i < numberOfBayDesigns; i++) { bayDesignPattern[i] = data.bays[facadeDesign.bayPattern[i]]; } } else { bayDesignPattern = new[] { facadeDesign.simpleBay }; numberOfBayDesigns = 1; } //start with first window width - we'll be adding to this until we have filled the facade width int it = 100; while (true) { int patternModIndex = numberOfBays % numberOfBayDesigns; float patternAddition = bayDesignPattern[patternModIndex].openingWidth + bayDesignPattern[patternModIndex].minimumBayWidth; if (patternSize + patternAddition < facadeWidth) { patternSize += patternAddition; numberOfBays++; } else break; it--; if (it < 0) break; } Vector3 windowBase = facadeFloorBaseVector; facadeUV.x = 0; facadeUV.y += floorHeight; float perBayAdditionalSpacing = (facadeWidth - patternSize) / numberOfBays; for (int c = 0; c < numberOfBays; c++) { BuildrBay bayStyle; BuildrBay lastBay; BuildrBay nextBay; bool firstColumn = c == 0; bool lastColumn = c == numberOfBays - 1; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { int numberOfBayStyles = facadeDesign.bayPattern.Count; bayStyle = bayDesignPattern[c % numberOfBayStyles]; int lastBayIndex = (c > 0) ? (c - 1) % numberOfBayStyles : 0; lastBay = bayDesignPattern[lastBayIndex]; nextBay = bayDesignPattern[(c + 1) % numberOfBayStyles]; } else { bayStyle = facadeDesign.simpleBay; lastBay = facadeDesign.simpleBay; nextBay = facadeDesign.simpleBay; } float actualWindowSpacing = bayStyle.minimumBayWidth + perBayAdditionalSpacing; float leftWidth = actualWindowSpacing * bayStyle.openingWidthRatio; float rightWidth = actualWindowSpacing - leftWidth; float openingWidth = bayStyle.openingWidth; if (firstColumn) leftWidth += largestDepthValue; if (lastColumn) rightWidth += largestDepthValue; BuildrTexture columnTexture = textures[bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture)]; Vector2 columnuvunits = columnTexture.tileUnitUV; float openingHeight = bayStyle.openingHeight; if (columnTexture.patterned) openingHeight = Mathf.Ceil(bayStyle.openingHeight / columnuvunits.y) * columnuvunits.y; if (bayStyle.openingHeight == floorHeight) bayStyle.openingHeight = floorHeight; float rowBottomHeight = ((floorHeight - openingHeight) * bayStyle.openingHeightRatio); if (columnTexture.patterned) rowBottomHeight = Mathf.Ceil(rowBottomHeight / columnuvunits.y) * columnuvunits.y; float rowTopHeight = (floorHeight - rowBottomHeight - openingHeight); bool previousBayIdentical = bayStyle == lastBay; bool nextBayIdentical = bayStyle == nextBay; if (previousBayIdentical && !firstColumn) leftWidth = actualWindowSpacing;//if next design is identical - add the two parts together the reduce polycount Vector3 w0, w1, w2, w3; int wallSubmesh = volume.WallTexture(r); bool wallFlipped = false; if (!bayStyle.isOpening) { float bayWidthSize = openingWidth + actualWindowSpacing; if (firstColumn || lastColumn) bayWidthSize += largestDepthValue; Vector3 bayWidth = facadeDirection * bayWidthSize; Vector3 bayHeight = Vector3.up * floorHeight; Vector3 bayDepth = facadeCross * largestDepthValue; w0 = windowBase + bayDepth; w1 = windowBase + bayWidth + bayDepth; w2 = windowBase + bayHeight + bayDepth; w3 = windowBase + bayWidth + bayHeight + bayDepth; Vector2 bayOpeningUVEnd = facadeUV + new Vector2(openingWidth + actualWindowSpacing, floorHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, facadeUV, bayOpeningUVEnd); windowBase = windowBase + bayWidth;//move base vertor to next bay facadeUV.x += openingWidth + actualWindowSpacing; continue;//bay filled - move onto next bay } var verts = new Vector3[16]; verts[0] = windowBase; verts[1] = verts[0] + leftWidth * facadeDirection; verts[2] = verts[1] + openingWidth * facadeDirection; verts[3] = verts[2] + rightWidth * facadeDirection; windowBase = (nextBayIdentical) ? verts[2] : verts[3];//move to next window - if next design is identical - well add the two parts together the reduce polycount facadeUV.x += (nextBayIdentical) ? openingWidth : openingWidth + rightWidth; Vector3 rowBottomVector = Vector3.up * rowBottomHeight; verts[4] = verts[0] + rowBottomVector; verts[5] = verts[1] + rowBottomVector; verts[6] = verts[2] + rowBottomVector; verts[7] = verts[3] + rowBottomVector; Vector3 openingVector = Vector3.up * openingHeight; verts[8] = verts[4] + openingVector; verts[9] = verts[5] + openingVector; verts[10] = verts[6] + openingVector; verts[11] = verts[7] + openingVector; Vector3 rowTopVector = Vector3.up * rowTopHeight; verts[12] = verts[8] + rowTopVector; verts[13] = verts[9] + rowTopVector; verts[14] = verts[10] + rowTopVector; verts[15] = verts[11] + rowTopVector; //Realign facade end points if (firstColumn) { verts[0] = p0interior - facadeCross * largestDepthValue + currentFloorHeightVector; verts[4] = verts[0] + rowBottomVector; verts[8] = verts[4] + openingVector; verts[12] = verts[8] + rowTopVector; } if (lastColumn) { verts[3] = p1interior - facadeCross * largestDepthValue + currentFloorHeightVector; verts[7] = verts[3] + rowBottomVector; verts[11] = verts[7] + openingVector; verts[15] = verts[11] + rowTopVector; } Vector3 openingDepthVector = facadeCross * bayStyle.openingDepth; Vector3 wallDepthVecotr = facadeCross * largestDepthValue; ///WINDOWS int windowSubmesh = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningBackTexture); bool windowFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningBackTexture); w0 = verts[10] + openingDepthVector; w1 = verts[9] + openingDepthVector; w2 = verts[6] + openingDepthVector; w3 = verts[5] + openingDepthVector; Vector2 windowUVStart = new Vector2(0, 0); Vector2 windowUVEnd = new Vector2(openingWidth, openingHeight); if (bayStyle.renderBack && !data.cullBays) AddPlane(w0, w1, w2, w3, windowSubmesh, windowFlipped, windowUVStart, windowUVEnd); ///COLUMNS //Column Face if (leftWidth > 0)//Column Face Left { w0 = verts[4] + wallDepthVecotr; w1 = verts[5] + wallDepthVecotr; w2 = verts[8] + wallDepthVecotr; w3 = verts[9] + wallDepthVecotr; Vector2 leftColumnUVStart = facadeUV + new Vector2(0, rowBottomHeight); Vector2 leftColumnUVEnd = leftColumnUVStart + new Vector2(leftWidth, openingHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, leftColumnUVStart, leftColumnUVEnd); } if ((!nextBayIdentical || lastColumn) && rightWidth > 0)//Column Right { w0 = verts[6] + wallDepthVecotr; w1 = verts[7] + wallDepthVecotr; w2 = verts[10] + wallDepthVecotr; w3 = verts[11] + wallDepthVecotr; Vector2 rightColumnUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight); Vector2 rightColumnUVEnd = rightColumnUVStart + new Vector2(rightWidth, openingHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, rightColumnUVStart, rightColumnUVEnd); } ///ROWS //Row Bottom if (rowBottomHeight > 0) { w0 = verts[1] + wallDepthVecotr; w1 = verts[2] + wallDepthVecotr; w2 = verts[5] + wallDepthVecotr; w3 = verts[6] + wallDepthVecotr; Vector2 bottomRowUVStart = facadeUV + new Vector2(leftWidth, 0); Vector2 bottomRowUVEnd = bottomRowUVStart + new Vector2(openingWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, bottomRowUVStart, bottomRowUVEnd); } //Row Top if (rowTopHeight > 0) { w0 = verts[9] + wallDepthVecotr; w1 = verts[10] + wallDepthVecotr; w2 = verts[13] + wallDepthVecotr; w3 = verts[14] + wallDepthVecotr; Vector2 topRowUVStart = facadeUV + new Vector2(leftWidth, rowBottomHeight + openingHeight); Vector2 topRowUVEnd = topRowUVStart + new Vector2(openingWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, topRowUVStart, topRowUVEnd); } //Cross Left Bottom w0 = verts[0] + wallDepthVecotr; w1 = verts[1] + wallDepthVecotr; w2 = verts[4] + wallDepthVecotr; w3 = verts[5] + wallDepthVecotr; Vector2 crossLBUVStart = facadeUV + new Vector2(0, 0); Vector2 crossLBUVEnd = crossLBUVStart + new Vector2(leftWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossLBUVStart, crossLBUVEnd); //Cross Left Top w0 = verts[8] + wallDepthVecotr; w1 = verts[9] + wallDepthVecotr; w2 = verts[12] + wallDepthVecotr; w3 = verts[13] + wallDepthVecotr; Vector2 crossLTUVStart = facadeUV + new Vector2(0, rowBottomHeight + openingHeight); Vector2 crossLTUVEnd = crossLTUVStart + new Vector2(leftWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossLTUVStart, crossLTUVEnd); if ((!nextBayIdentical || lastColumn) && rightWidth > 0) { //Cross Right Bottom w0 = verts[2] + wallDepthVecotr; w1 = verts[3] + wallDepthVecotr; w2 = verts[6] + wallDepthVecotr; w3 = verts[7] + wallDepthVecotr; Vector2 crossRBUVStart = facadeUV + new Vector2(leftWidth + openingWidth, 0); Vector2 crossRBUVEnd = crossRBUVStart + new Vector2(rightWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossRBUVStart, crossRBUVEnd); //Cross Right Top w0 = verts[10] + wallDepthVecotr; w1 = verts[11] + wallDepthVecotr; w2 = verts[14] + wallDepthVecotr; w3 = verts[15] + wallDepthVecotr; Vector2 crossRTUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight + openingHeight); Vector2 crossRTUVEnd = crossRTUVStart + new Vector2(rightWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossRTUVStart, crossRTUVEnd); } } } else { // windowless wall Vector3 interiorStart = p0interior + currentFloorHeightVector; Vector3 interiorEnd = p1interior + currentFloorHeightVector; // Vector3 wallVector = (facadeDirection * facadeWidth); Vector3 wallHeightVector = Vector3.up * floorHeight; Vector3 w0 = interiorStart; Vector3 w1 = interiorEnd; Vector3 w2 = interiorStart + wallHeightVector; Vector3 w3 = interiorEnd + wallHeightVector; BuildrTexture texture = textures[facadeDesign.simpleBay.GetTexture(BuildrBay.TextureNames.WallTexture)]; var uvSize = new Vector2(facadeWidth * (1.0f / texture.textureUnitSize.x), floorHeight * (1.0f / texture.textureUnitSize.y)); Vector2 uvunits = texture.tileUnitUV; uvSize.x = Mathf.Ceil(uvSize.x / uvunits.x) * uvunits.x; uvSize.y = Mathf.Ceil(uvSize.y / uvunits.y) * uvunits.y; int wallSubmesh = 0; bool flipped = false; Vector2 wallUVStart = facadeUV; Vector2 wallUVEnd = facadeUV + new Vector2(facadeWidth, floorHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, flipped, wallUVStart, wallUVEnd); } } } ///FLOORS AND CEILING int numberOfBasements = volume.numberOfBasementFloors; int numberOfFloorPoints = interiorVolumePoints.Length; int[] baseFloorPlanTriangles = EarClipper.Triangulate(interiorVolumePoints); int baseFloorVectors = interiorVolumePoints.Length; var newEndVerts = new Vector3[baseFloorVectors]; Vector3 basementBaseDrop = -floorHeightVector * numberOfBasements; for (int i = 0; i < baseFloorVectors; i++) newEndVerts[i] = interiorVolumePoints[i].vector3 + basementBaseDrop; var tris = new List<int>(baseFloorPlanTriangles); //Bottom Floor int floorSubmesh = volume.FloorTexture(-numberOfBasements); AddData(newEndVerts, baseFloorPlanTriangles, floorSubmesh, false); //Top Ceiling if (true)//Todo: add conditional for roof opening { Vector3 ceilingHeightVector = floorHeightVector * (numberOfFloors - 1 + numberOfBasements) + Vector3.up * ceilingHeight; for (int i = 0; i < baseFloorVectors; i++) newEndVerts[i] += ceilingHeightVector; tris.Reverse(); AddData(newEndVerts, tris.ToArray(), volume.CeilingTexture(numberOfFloors-1), false); } //inner floors int[] floorPlanTriangles = EarClipper.Triangulate(interiorPointListCore); int numberOfFloorVectors = interiorPointListCore.Length; for (int floorIndex = -numberOfBasements; floorIndex < numberOfFloors; floorIndex++) { Vector3 floorVectorHeight = floorHeightVector * floorIndex; newEndVerts = new Vector3[numberOfFloorVectors]; for (int i = 0; i < numberOfFloorVectors; i++) { newEndVerts[i] = interiorPointListCore[i].vector3 + floorVectorHeight; } tris = new List<int>(floorPlanTriangles); //Floor if (floorIndex > -numberOfBasements) AddData(newEndVerts, tris.ToArray(), volume.FloorTexture(floorIndex), false); //Ceiling if (floorIndex < numberOfFloors - 1) { Vector3 ceilingHeightVector = Vector3.up * ceilingHeight; for (int i = 0; i < numberOfFloorVectors; i++) { newEndVerts[i] += ceilingHeightVector; } tris.Reverse(); AddData(newEndVerts, tris.ToArray(), volume.CeilingTexture(floorIndex), false); } //basement walls if(floorIndex < 0) { for (int f = 0; f < numberOfFloorPoints; f++) { Vector3 basementVector = floorHeightVector * floorIndex; int indexA = f; int indexB = (f + 1) % numberOfFloorPoints; Vector3 p0 = interiorVolumePoints[indexA].vector3 + basementVector; Vector3 p1 = interiorVolumePoints[indexB].vector3 + basementVector; Vector3 p2 = p0 + floorHeightVector; Vector3 p3 = p1 + floorHeightVector; Vector2 uv1 = new Vector2(Vector3.Distance(p0,p1), floorHeight); AddPlane(p0, p1, p2, p3, volume.WallTexture(floorIndex), false, Vector2.zero, uv1); } } } //Core walls for (int c = 0; c < numberOfVolumeCores; c++) { Rect coreBounds = volumeCores[c]; Vector3 coreBL = new Vector3(coreBounds.xMin, 0, coreBounds.yMin); Vector3 coreBR = new Vector3(coreBounds.xMax, 0, coreBounds.yMin); Vector3 coreTL = new Vector3(coreBounds.xMin, 0, coreBounds.yMax); Vector3 coreTR = new Vector3(coreBounds.xMax, 0, coreBounds.yMax); for (int floorIndex = -numberOfBasements; floorIndex < numberOfFloors - 1; floorIndex++) { Vector3 c0 = floorHeightVector * floorIndex + Vector3.up * ceilingHeight; Vector3 f0 = floorHeightVector * floorIndex + Vector3.up * floorHeight; float gapHeight = floorHeight - ceilingHeight; AddPlane(coreBL + c0, coreBR + c0, coreBL + f0, coreBR + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreBR + c0, coreTR + c0, coreBR + f0, coreTR + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreTR + c0, coreTL + c0, coreTR + f0, coreTL + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreTL + c0, coreBL + c0, coreTL + f0, coreBL + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); } } }
//TODO: functions to find out minimum footprint of stairwell for checking against cores? public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, int volumeIndex, StairModes stairMode, bool zeroMesh) { data = _data; mesh = _mesh; mesh.name = "Stairs Mesh Volume " + volumeIndex; textures = data.textures.ToArray(); // BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; BuildrVolume volume = plan.volumes[volumeIndex]; float floorHeight = data.floorHeight; // Vector3 floorHeightVector = Vector3.up * floorHeight; if (!volume.generateStairs) { return; } //Calculate the internal floor plan points int numberOfVolumePoints = volume.points.Count; Vector2z[] volumePoints = new Vector2z[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { volumePoints[i] = plan.points[volume.points[i]]; } List <Rect> volumeCores = new List <Rect>(); // List<int> linkedPoints = new List<int>(); foreach (Rect core in plan.cores) { Vector2z coreCenter = new Vector2z(core.center); if (BuildrUtils.PointInsidePoly(coreCenter, volumePoints)) { volumeCores.Add(core); } } int numberOfVolumeCores = volumeCores.Count; int numberOfFloors = volume.numberOfFloors + volume.numberOfBasementFloors; float basementBaseHeight = (volume.numberOfBasementFloors) * floorHeight;//plus one for the initial floor float staircaseWidth = volume.staircaseWidth; float stairwellWidth = staircaseWidth * 2.5f; float stairwellDepth = staircaseWidth * 2 + Mathf.Sqrt(floorHeight + floorHeight); float staircaseThickness = Mathf.Sqrt(volume.stepHeight * volume.stepHeight + volume.stepHeight * volume.stepHeight); Vector3 flightVector = floorHeight * Vector3.up; Vector3 staircaseWidthVector = staircaseWidth * Vector3.right; Vector3 staircaseDepthVector = stairwellDepth * 0.5f * Vector3.forward; Vector3 stairHeightVector = staircaseThickness * Vector3.up; Vector3 landingDepthVector = staircaseWidth * Vector3.forward; //Texture submeshes int floorSubmesh = volume.stairwellFloorTexture; int stepSubmesh = volume.stairwellStepTexture; int wallSubmesh = volume.stairwellWallTexture; int ceilingSubmesh = volume.stairwellCeilingTexture; volume.stairBaseVector.Clear(); for (int c = 0; c < numberOfVolumeCores; c++) { Rect coreBounds = volumeCores[c]; Vector3 stairBaseVector = new Vector3(-stairwellWidth / 2, 0, -stairwellDepth / 2); Vector3 stairPosition = new Vector3(coreBounds.xMin, -basementBaseHeight, coreBounds.yMin) - stairBaseVector; for (int f = 0; f < numberOfFloors; f++) { Vector3 flightBaseVector = stairBaseVector + (flightVector * f); if (!zeroMesh) { flightBaseVector += stairPosition; } Vector3 landingStart0 = flightBaseVector; Vector3 landingStart1 = landingStart0 + staircaseWidthVector * 2.5f; Vector3 landingStart2 = landingStart0 + landingDepthVector; Vector3 landingStart3 = landingStart1 + landingDepthVector; Vector3 landingStart4 = landingStart0 - stairHeightVector; Vector3 landingStart5 = landingStart1 - stairHeightVector; Vector3 landingStart6 = landingStart2 - stairHeightVector; Vector3 landingStart7 = landingStart3 - stairHeightVector; if (f > 0) { AddPlane(landingStart1, landingStart0, landingStart3, landingStart2, floorSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth)); //top AddPlane(landingStart4, landingStart5, landingStart6, landingStart7, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth)); //bottom AddPlane(landingStart0, landingStart1, landingStart4, landingStart5, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness)); //frontside AddPlane(landingStart3, landingStart2, landingStart7, landingStart6, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness)); //backside AddPlane(landingStart0, landingStart4, landingStart2, landingStart6, wallSubmesh, false, Vector2.zero, new Vector2(staircaseThickness, staircaseWidth)); //sideleft AddPlane(landingStart5, landingStart1, landingStart7, landingStart3, wallSubmesh, false, Vector2.zero, new Vector2(staircaseThickness, staircaseWidth)); //sideright } if (f < numberOfFloors - 1) { Vector3 bottom0 = landingStart2; Vector3 bottom1 = landingStart2 + staircaseWidthVector; Vector3 bottom2 = bottom0 - stairHeightVector; Vector3 bottom3 = bottom1 - stairHeightVector; Vector3 top0 = bottom0 + (flightVector * 0.5f) + staircaseDepthVector; Vector3 top1 = bottom1 + (flightVector * 0.5f) + staircaseDepthVector; Vector3 top2 = top0 - stairHeightVector; Vector3 top3 = top1 - stairHeightVector; Vector3 bottomB0 = top1 + Vector3.right * staircaseWidth * 0.5f; Vector3 bottomB1 = bottomB0 + staircaseWidthVector; Vector3 bottomB2 = bottomB0 - stairHeightVector; Vector3 bottomB3 = bottomB1 - stairHeightVector; Vector3 topB0 = bottomB0 + (flightVector * 0.5f) - staircaseDepthVector; Vector3 topB1 = bottomB1 + (flightVector * 0.5f) - staircaseDepthVector; Vector3 topB2 = topB0 - stairHeightVector; Vector3 topB3 = topB1 - stairHeightVector; float stairHypontenuse = Vector3.Distance(bottom0, top0); int numberOfSteps = Mathf.CeilToInt((floorHeight / 2.0f) / volume.stepHeight); switch (stairMode) { case StairModes.Flat: //flight A AddPlane(bottom1, bottom0, top1, top0, stepSubmesh, false, Vector2.zero, new Vector2(1, numberOfSteps)); //step face AddPlane(bottom3, bottom1, top3, top1, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, stairHypontenuse)); //underside AddPlane(bottom0, bottom2, top0, top2, wallSubmesh, false, new Vector2(bottom2.z, bottom2.y), new Vector2(top0.z, top0.y)); //left side AddPlane(bottom2, bottom3, top2, top3, wallSubmesh, false, new Vector2(bottom3.z, bottom3.y), new Vector2(top2.z, top2.y)); //right side //flight B AddPlane(bottomB0, bottomB1, topB0, topB1, stepSubmesh, false, Vector2.zero, new Vector2(1, numberOfSteps)); //step face AddPlane(bottomB1, bottomB3, topB1, topB3, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, stairHypontenuse)); //underside AddPlane(bottomB2, bottomB0, topB2, topB0, wallSubmesh, false, Vector2.zero, Vector2.one); //left side AddPlane(bottomB3, bottomB2, topB3, topB2, wallSubmesh, false, Vector2.zero, Vector2.one); //right side break; case StairModes.Stepped: float stepHypontenuse = stairHypontenuse / numberOfSteps; float stairAngle = Mathf.Atan2(floorHeight, stairwellDepth); float stepDepth = Mathf.Cos(stairAngle) * stepHypontenuse; float skipStep = (stepDepth / (numberOfSteps - 1)); stepDepth += skipStep; float stepRiser = Mathf.Sin(stairAngle) * stepHypontenuse; //flight one float lerpIncrement = 1.0f / numberOfSteps; float lerpIncrementB = 1.0f / (numberOfSteps - 1); for (int s = 0; s < numberOfSteps - 1; s++) { float lerpValue = lerpIncrement * s; Vector3 skipStepVector = Vector3.forward * (skipStep * s); Vector3 s0 = Vector3.Lerp(bottom1, top1, lerpValue) + skipStepVector; Vector3 s1 = Vector3.Lerp(bottom0, top0, lerpValue) + skipStepVector; Vector3 s2 = s0 + Vector3.up * stepRiser; Vector3 s3 = s1 + Vector3.up * stepRiser; Vector3 s4 = s2 + Vector3.forward * stepDepth; Vector3 s5 = s3 + Vector3.forward * stepDepth; AddPlane(s0, s1, s2, s3, wallSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); AddPlane(s2, s3, s4, s5, stepSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); //sides float lerpValueB = lerpIncrementB * s; Vector3 s6 = Vector3.Lerp(bottom3, top3, lerpValueB); Vector3 s7 = Vector3.Lerp(bottom3, top3, lerpValueB + lerpIncrementB); AddPlane(s2, s4, s6, s7, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); Vector3 s8 = Vector3.Lerp(bottom2, top2, lerpValueB); Vector3 s9 = Vector3.Lerp(bottom2, top2, lerpValueB + lerpIncrementB); AddPlane(s5, s3, s9, s8, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); } AddPlane(bottom2, bottom3, top2, top3, ceilingSubmesh, false, Vector2.zero, Vector2.one); //flight two for (int s = 0; s < numberOfSteps - 1; s++) { float lerpValue = lerpIncrement * s; Vector3 skipStepVector = -Vector3.forward * (skipStep * s); Vector3 s0 = Vector3.Lerp(bottomB0, topB0, lerpValue) + skipStepVector; Vector3 s1 = Vector3.Lerp(bottomB1, topB1, lerpValue) + skipStepVector; Vector3 s2 = s0 + Vector3.up * stepRiser; Vector3 s3 = s1 + Vector3.up * stepRiser; Vector3 s4 = s2 - Vector3.forward * stepDepth; Vector3 s5 = s3 - Vector3.forward * stepDepth; AddPlane(s0, s1, s2, s3, wallSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); AddPlane(s2, s3, s4, s5, stepSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); float lerpValueB = lerpIncrementB * s; //sides Vector3 s6 = Vector3.Lerp(bottomB2, topB2, lerpValueB); Vector3 s7 = Vector3.Lerp(bottomB2, topB2, lerpValueB + lerpIncrementB); AddPlane(s2, s4, s6, s7, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); Vector3 s8 = Vector3.Lerp(bottomB3, topB3, lerpValueB); Vector3 s9 = Vector3.Lerp(bottomB3, topB3, lerpValueB + lerpIncrementB); AddPlane(s5, s3, s9, s8, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); } AddPlane(bottomB3, bottomB2, topB3, topB2, ceilingSubmesh, false, Vector2.zero, Vector2.one); break; } Vector3 landingEnd0 = top0 + landingDepthVector; Vector3 landingEnd1 = bottomB1 + landingDepthVector; Vector3 landingEnd2 = landingEnd0 - stairHeightVector; Vector3 landingEnd3 = landingEnd1 - stairHeightVector; Vector3 landingEnd4 = top0 - stairHeightVector; Vector3 landingEnd5 = bottomB1 - stairHeightVector; AddPlane(bottomB1, top0, landingEnd1, landingEnd0, floorSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth)); //top AddPlane(landingEnd4, landingEnd5, landingEnd2, landingEnd3, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth)); //bottom AddPlane(top0, bottomB1, landingEnd4, landingEnd5, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness)); //frontside AddPlane(landingEnd1, landingEnd0, landingEnd3, landingEnd2, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness)); //backside AddPlane(landingEnd0, top0, landingEnd2, landingEnd4, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, staircaseThickness)); //sideleft AddPlane(bottomB1, landingEnd1, landingEnd5, landingEnd3, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, staircaseThickness)); //sideright } } //Center wall float coreHeight = (numberOfFloors * floorHeight); Vector3 coreHeightVector = Vector3.up * coreHeight; Vector3 corePosition = (zeroMesh) ? Vector3.zero : stairPosition; Vector3 w0 = new Vector3(-staircaseWidth / 4.0f, 0, -(stairwellDepth - (staircaseWidth * 2)) / 2.0f) + corePosition; Vector3 w1 = w0 + Vector3.right * staircaseWidth / 2; Vector3 w2 = w0 + staircaseDepthVector; Vector3 w3 = w1 + staircaseDepthVector; Vector3 w4 = w0 + coreHeightVector; Vector3 w5 = w1 + coreHeightVector; Vector3 w6 = w2 + coreHeightVector; Vector3 w7 = w3 + coreHeightVector; AddPlane(w1, w0, w5, w4, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth / 2, coreHeight)); // AddPlane(w3, w1, w7, w5, wallSubmesh, false, Vector2.zero, new Vector2(stairwellDepth / 2, coreHeight)); // AddPlane(w2, w3, w6, w7, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth / 2, coreHeight)); // AddPlane(w0, w2, w4, w6, wallSubmesh, false, Vector2.zero, new Vector2(stairwellDepth / 2, coreHeight)); // int it = 100; while (volume.stairBaseVector.Count < mesh.meshCount) { if (zeroMesh) { volume.stairBaseVector.Add(stairPosition); } else { volume.stairBaseVector.Add(Vector3.zero); } it--; if (it == 0) { break; } } if (c < numberOfVolumeCores - 1) { mesh.ForceNewMesh(); } } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { data = _data; mesh = _mesh; BuildrPlan plan = data.plan; int facadeIndex = 0; int numberOfVolumes = data.plan.numberOfVolumes; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; Vector3 volumeHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight); for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3 + volumeHeight; newEndUVs[i] = Vector2.zero; } List<int> tris = new List<int>(data.plan.GetTrianglesBySectorBase(s)); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } //Build ROOF //Build facades for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) continue; int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; float facadeWidth = Vector3.Distance(p0, p1); p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; Vector2 uvMin = new Vector2(0, 0); Vector2 uvMax = new Vector2(facadeWidth, floorHeight); mesh.AddPlane(w0, w1, w2, w3, uvMin, uvMax, 0); facadeIndex++; } } data = null; mesh = null; }
public void UpdateInteriors() { while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } interiorMeshes.Clear(); if (data.renderInteriors) { int numberOfVolumes = data.plan.numberOfVolumes; for (int v = 0; v < numberOfVolumes; v++) { DynamicMeshGenericMultiMaterialMesh interiorMesh = new DynamicMeshGenericMultiMaterialMesh(); interiorMesh.subMeshCount = data.textures.Count; BuildrVolume volume = _data.plan.volumes[v]; BuildrInteriors.Build(interiorMesh, data, v); interiorMesh.Build(false); List <int> unusedInteriorTextures = interiorMesh.unusedSubmeshes; int numberOfInteriorMaterials = data.textures.Count; List <Material> interiorMaterials = new List <Material>(); for (int m = 0; m < numberOfInteriorMaterials; m++) { if (unusedInteriorTextures.Contains(m)) { continue;//skip, unused } BuildrTexture bTexture = data.textures[m]; interiorMaterials.Add(bTexture.usedMaterial); } int numberOfInteriorMeshes = interiorMesh.meshCount; for (int i = 0; i < numberOfInteriorMeshes; i++) { string meshName = "model interior"; if (numberOfVolumes > 0) { meshName += " volume " + (v + 1); } if (numberOfInteriorMeshes > 1) { meshName += " mesh " + (i + 1); } GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent <MeshFilter>(); meshRend = newMeshHolder.AddComponent <MeshRenderer>(); meshFilt.mesh = interiorMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); int numberOfInterior = interiorMeshHolders.Count; for (int m = 0; m < numberOfInterior; m++) { meshRend.sharedMaterials = interiorMaterials.ToArray(); } } interiorMeshes.Add(interiorMesh); if (!volume.generateStairs) { continue; } DynamicMeshGenericMultiMaterialMesh stairwellMesh = new DynamicMeshGenericMultiMaterialMesh(); stairwellMesh.subMeshCount = data.textures.Count; BuildrStairs.Build(stairwellMesh, data, v, BuildrStairs.StairModes.Stepped, true); stairwellMesh.Build(false); List <int> unusedStairTextures = stairwellMesh.unusedSubmeshes; int numberOfStairMaterials = data.textures.Count; List <Material> stairMaterials = new List <Material>(); for (int m = 0; m < numberOfStairMaterials; m++) { if (unusedStairTextures.Contains(m)) { continue;//skip, unused } BuildrTexture bTexture = data.textures[m]; stairMaterials.Add(bTexture.usedMaterial); } int numberOfStairMeshes = stairwellMesh.meshCount; for (int i = 0; i < numberOfStairMeshes; i++) { string meshName = "model stairs"; if (numberOfVolumes > 0) { meshName += " volume " + (v + 1); } if (numberOfStairMeshes > 1) { meshName += " mesh " + (i + 1); } GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = volume.stairBaseVector[i]; meshFilt = newMeshHolder.AddComponent <MeshFilter>(); meshRend = newMeshHolder.AddComponent <MeshRenderer>(); meshFilt.mesh = stairwellMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); meshRend.sharedMaterials = stairMaterials.ToArray(); } interiorMeshes.Add(stairwellMesh); } } }
private static void ExportModel(TrackBuildR track) { GameObject baseObject = new GameObject(track.exportFilename); baseObject.transform.position = CURRENT_TRANSFORM.position; baseObject.transform.rotation = CURRENT_TRANSFORM.rotation; EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.0f); track.ForceFullRecalculation(); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.1f); try { TrackBuildRTrack trackData = track.track; //check overwrites... string newDirectory = ROOT_FOLDER + track.exportFilename; if (!CreateFolder(newDirectory)) { EditorUtility.ClearProgressBar(); return; } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.15f); int numberOfCurves = trackData.numberOfCurves; float exportProgress = 0.75f / (numberOfCurves * 6.0f); ExportMaterial[] exportMaterials = new ExportMaterial[1]; ExportMaterial exportTexture = new ExportMaterial(); string[] dynNames = new [] { "track", "bumper", "boundary", "bottom", "offread", "trackCollider" }; for (int c = 0; c < numberOfCurves; c++) { TrackBuildRPoint curve = trackData[c]; int numberOfDynMeshes = 6; DynamicMeshGenericMultiMaterialMesh[] dynMeshes = new DynamicMeshGenericMultiMaterialMesh[6]; dynMeshes[0] = curve.dynamicTrackMesh; dynMeshes[1] = curve.dynamicBumperMesh; dynMeshes[2] = curve.dynamicBoundaryMesh; dynMeshes[3] = curve.dynamicBottomMesh; dynMeshes[4] = curve.dynamicOffroadMesh; dynMeshes[5] = curve.dynamicColliderMesh; int[] textureIndeices = new int[] { curve.trackTextureStyleIndex, curve.bumperTextureStyleIndex, curve.boundaryTextureStyleIndex, curve.bottomTextureStyleIndex, curve.offroadTextureStyleIndex, 0 }; for (int d = 0; d < numberOfDynMeshes; d++) { if (EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "Exporting Track Curve " + c + " " + dynNames[d], 0.15f + exportProgress * (c * 6 + d))) { EditorUtility.ClearProgressBar(); return; } DynamicMeshGenericMultiMaterialMesh exportDynMesh = dynMeshes[d]; if (track.includeTangents || exportDynMesh.isEmpty) { exportDynMesh.Build(track.includeTangents);//rebuild with tangents } TrackBuildRTexture texture = trackData.Texture(textureIndeices[d]); exportTexture.name = texture.customName; exportTexture.material = texture.material; exportTexture.generated = false; exportTexture.filepath = texture.filePath; exportMaterials[0] = exportTexture; int meshCount = exportDynMesh.meshCount; for (int i = 0; i < meshCount; i++) { Mesh exportMesh = exportDynMesh[i].mesh; MeshUtility.Optimize(exportMesh); string filenameSuffix = trackModelName(dynNames[d], c, (meshCount > 1) ? i : -1);// "trackCurve" + c + ((meshCount > 1) ? "_" + i.ToString() : ""); string filename = track.exportFilename + filenameSuffix; Export(filename, ROOT_FOLDER + track.exportFilename + "/", track, exportMesh, exportMaterials); if (track.createPrefabOnExport) { AssetDatabase.Refresh();//ensure the database is up to date... string modelFilePath = ROOT_FOLDER + track.exportFilename + "/" + filename + FILE_EXTENTION; if (d < numberOfDynMeshes - 1) { GameObject newModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(modelFilePath)); newModel.name = filename; newModel.transform.parent = baseObject.transform; newModel.transform.localPosition = Vector3.zero; newModel.transform.localRotation = Quaternion.identity; } else { GameObject colliderObject = new GameObject("trackCollider"); colliderObject.AddComponent <MeshCollider>().sharedMesh = (Mesh)AssetDatabase.LoadAssetAtPath(modelFilePath, typeof(Mesh)); colliderObject.transform.parent = baseObject.transform; colliderObject.transform.localPosition = Vector3.zero; colliderObject.transform.localRotation = Quaternion.identity; } } } } } if (track.createPrefabOnExport) { string prefabPath = ROOT_FOLDER + track.exportFilename + "/" + track.exportFilename + ".prefab"; Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); if (prefab == null) { prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); } PrefabUtility.ReplacePrefab(baseObject, prefab, ReplacePrefabOptions.ConnectToPrefab); } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.70f); AssetDatabase.Refresh();//ensure the database is up to date... } catch (System.Exception e) { Debug.LogError("BuildR Export Error: " + e); EditorUtility.ClearProgressBar(); } Object.DestroyImmediate(baseObject); EditorUtility.ClearProgressBar(); EditorUtility.UnloadUnusedAssets(); AssetDatabase.Refresh(); }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { // timestart = Time.realtimeSinceStartup; data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrPlan plan = data.plan; // int facadeIndex = 0; numberOfFacades = 0; int numberOfVolumes = data.plan.numberOfVolumes; //define rectangles that represent the facades packedTexturePositions.Clear(); for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) { continue; } int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector2z p0 = plan.points[volume.points[indexA]]; Vector2z p1 = plan.points[volume.points[indexB]]; float facadeWidth = Vector2z.Distance(p0, p1) * PIXELS_PER_METER; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1)//no facade - adjacent facade is taller and covers this one { continue; } float floorHeight = data.floorHeight; float facadeHeight = (volume.numberOfFloors - floorBase) * floorHeight * PIXELS_PER_METER; if (facadeHeight < 0) { facadeWidth = 0; facadeHeight = 0; } Rect newFacadeRect = new Rect(0, 0, facadeWidth, facadeHeight); packedTexturePositions.Add(newFacadeRect); numberOfFacades++; } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.name = "Roof Mesh"; dynMeshRoof.subMeshCount = textures.Length; BuildrRoof.Build(dynMeshRoof, data, true); dynMeshRoof.CheckMaxTextureUVs(data); roofTextures.Clear(); roofTextureIndex.Clear(); foreach (BuildrRoofDesign roofDesign in data.roofs) { foreach (int textureIndex in roofDesign.textureValues) { if (!roofTextureIndex.Contains(textureIndex)) { BuildrTexture bTexture = data.textures[textureIndex]; Vector2 largestSubmeshPlaneSize = new Vector2(1, 1); Vector2 minWorldUvSize = dynMeshRoof.MinWorldUvSize(textureIndex); Vector2 maxWorldUvSize = dynMeshRoof.MaxWorldUvSize(textureIndex); largestSubmeshPlaneSize.x = maxWorldUvSize.x - minWorldUvSize.x; largestSubmeshPlaneSize.y = maxWorldUvSize.y - minWorldUvSize.y; int roofTextureWidth = Mathf.RoundToInt(largestSubmeshPlaneSize.x * PIXELS_PER_METER); int roofTextureHeight = Mathf.RoundToInt(largestSubmeshPlaneSize.y * PIXELS_PER_METER); Rect newRoofTexutureRect = new Rect(0, 0, roofTextureWidth, roofTextureHeight); packedTexturePositions.Add(newRoofTexutureRect); roofTextures.Add(bTexture); roofTextureIndex.Add(textureIndex); } } } //run a custom packer to define their postions textureWidth = RectanglePack.Pack(packedTexturePositions, ATLAS_PADDING); //determine the resize scale and apply that to the rects packedScale = 1; int numberOfRects = packedTexturePositions.Count; if (textureWidth > MAXIMUM_TEXTURESIZE) { packedScale = MAXIMUM_TEXTURESIZE / (float)textureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; //Debug.Log("Rects "+roofTextures[i-+packedTexturePositions[i]); } textureWidth = Mathf.RoundToInt(packedScale * textureWidth); } else { textureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(textureWidth - 1, 2)) + 1));//find the next power of two } textureSize = textureWidth * textureWidth; colourArray = new Color32[textureSize]; //TestRectColours();//this test paints all the facades with rainbow colours - real pretty BuildTextures(); Texture2D packedTexture = new Texture2D(textureWidth, textureWidth, TextureFormat.ARGB32, true); packedTexture.filterMode = FilterMode.Bilinear; packedTexture.SetPixels32(colourArray); packedTexture.Apply(true, false); if (data.OneDrawCallTexture != null) { Object.DestroyImmediate(data.OneDrawCallTexture); } data.OneDrawCallTexture = packedTexture; data.OneDrawCallTexture.name = "One Draw Call Texture"; int numberOfRoofTextures = roofTextures.Count - 1; List <Rect> facadeTexturePositions = new List <Rect>(packedTexturePositions); Debug.Log(numberOfRoofTextures); facadeTexturePositions.RemoveRange(packedTexturePositions.Count - numberOfRoofTextures, numberOfRoofTextures); BuildrBuilding.Build(mesh, data, facadeTexturePositions.ToArray()); data = null; mesh = null; textures = null; System.GC.Collect(); }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, bool ignoreParapets) { data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrPlan plan = data.plan; int numberOfVolumes = data.plan.numberOfVolumes; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; BuildrRoofDesign design = data.roofs[volume.roofDesignID]; BuildrRoofDesign.styles style = design.style; if (volume.points.Count != 4) if (design.style == BuildrRoofDesign.styles.leanto || design.style == BuildrRoofDesign.styles.sawtooth || design.style == BuildrRoofDesign.styles.barrel) style = BuildrRoofDesign.styles.flat;//ignore style and just do a flat roof if (volume.points.Count != 4 && design.style == BuildrRoofDesign.styles.gabled) style = BuildrRoofDesign.styles.hipped;//ignore style and just do a hipped roof switch (style) { case BuildrRoofDesign.styles.flat: FlatRoof(volume, design); break; case BuildrRoofDesign.styles.mansard: Mansard(volume, design); if (design.hasDormers) Dormers(volume, design); break; case BuildrRoofDesign.styles.gabled: Gabled(volume, design); break; case BuildrRoofDesign.styles.hipped: Hipped(volume, design); break; case BuildrRoofDesign.styles.leanto: LeanTo(volume, design); break; case BuildrRoofDesign.styles.sawtooth: Sawtooth(volume, design); break; case BuildrRoofDesign.styles.barrel: Barrel(volume, design); break; case BuildrRoofDesign.styles.steepled: Steeple(volume, design); break; } if (design.parapet && !ignoreParapets) Parapet(volume, design); } data = null; mesh = null; textures = null; }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { data = _data; mesh = _mesh; BuildrPlan plan = data.plan; int facadeIndex = 0; int numberOfVolumes = data.plan.numberOfVolumes; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; Vector3 volumeHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight); for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3 + volumeHeight; newEndUVs[i] = Vector2.zero; } List <int> tris = new List <int>(data.plan.GetTrianglesBySectorBase(s)); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } //Build ROOF //Build facades for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) { continue; } int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; float facadeWidth = Vector3.Distance(p0, p1); p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; Vector2 uvMin = new Vector2(0, 0); Vector2 uvMax = new Vector2(facadeWidth, floorHeight); mesh.AddPlane(w0, w1, w2, w3, uvMin, uvMax, 0); facadeIndex++; } } data = null; mesh = null; }
//private static BuildrData data; public static void InspectorGUI(BuildrEditMode editMode, BuildrData _data) { //ensure that the model exists in the scene if (editMode.fullMesh == null) { editMode.UpdateRender(); } Undo.RecordObject(_data, "Building Modified"); //texture library string array BuildrTexture[] textures = _data.textures.ToArray(); int numberOfTextures = textures.Length; string[] textureNames = new string[numberOfTextures]; for (int t = 0; t < numberOfTextures; t++) { textureNames[t] = textures[t].name; } //Render a full version version EditorGUILayout.LabelField("Building Generation Types"); EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUI.BeginDisabledGroup(editMode.renderMode == BuildrEditMode.renderModes.full); if (GUILayout.Button("Full Detail", GUILayout.Height(28))) { editMode.UpdateRender(BuildrEditMode.renderModes.full); } EditorGUI.EndDisabledGroup(); //Render a low detail version EditorGUI.BeginDisabledGroup(editMode.renderMode == BuildrEditMode.renderModes.lowDetail); if (GUILayout.Button("Low Detail", GUILayout.Height(28))) { editMode.UpdateRender(BuildrEditMode.renderModes.lowDetail); } EditorGUI.EndDisabledGroup(); //Render a box version EditorGUI.BeginDisabledGroup(editMode.renderMode == BuildrEditMode.renderModes.box); if (GUILayout.Button("Box Outline", GUILayout.Height(28))) { editMode.UpdateRender(BuildrEditMode.renderModes.box); } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); //Toggle showing the wireframe when we have selected the model. EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Show Wireframe"); editMode.showWireframe = EditorGUILayout.Toggle(editMode.showWireframe, GUILayout.Width(15)); EditorGUILayout.EndHorizontal(); //Tangent calculation EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUI.BeginDisabledGroup(editMode.fullMesh.hasTangents); if (GUILayout.Button("Build Tangents", GUILayout.Height(38))) { if (editMode.fullMesh != null) { editMode.fullMesh.SolveTangents(); } if (editMode.detailMesh != null) { editMode.detailMesh.SolveTangents(); } GUI.changed = false; } EditorGUI.EndDisabledGroup(); if (!editMode.fullMesh.hasTangents) { EditorGUILayout.HelpBox("The model doesn't have tangents", MessageType.Warning); } EditorGUILayout.EndHorizontal(); //Lightmap rendering EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUI.BeginDisabledGroup(editMode.fullMesh.lightmapUvsCalculated); if (GUILayout.Button("Build Lightmap UVs", GUILayout.Height(38))) { // Undo.RegisterSceneUndo("Build Lightmap UVs"); if (editMode.fullMesh != null) { for (int i = 0; i < editMode.fullMesh.meshCount; i++) { Unwrapping.GenerateSecondaryUVSet(editMode.fullMesh[i].mesh); } } editMode.fullMesh.lightmapUvsCalculated = true; if (editMode.detailMesh != null) { for (int i = 0; i < editMode.detailMesh.meshCount; i++) { Unwrapping.GenerateSecondaryUVSet(editMode.detailMesh[i].mesh); } editMode.detailMesh.lightmapUvsCalculated = true; } int numberOfInteriors = editMode.interiorMeshes.Count; for (int i = 0; i < numberOfInteriors; i++) { DynamicMeshGenericMultiMaterialMesh interiorMesh = editMode.interiorMeshes[i]; for (int j = 0; j < interiorMesh.meshCount; j++) { Unwrapping.GenerateSecondaryUVSet(interiorMesh[j].mesh); } interiorMesh.lightmapUvsCalculated = true; } GUI.changed = false; } EditorGUI.EndDisabledGroup(); if (!editMode.fullMesh.lightmapUvsCalculated) { EditorGUILayout.HelpBox("The model doesn't have lightmap UVs", MessageType.Warning); } EditorGUILayout.EndHorizontal(); //Mesh Optimisation EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUI.BeginDisabledGroup(editMode.fullMesh.optimised); if (GUILayout.Button("Optimise Mesh For Runtime", GUILayout.Height(38))) { // Undo.RegisterSceneUndo("Optimise Mesh"); if (editMode.fullMesh != null) { for (int i = 0; i < editMode.fullMesh.meshCount; i++) { MeshUtility.Optimize(editMode.fullMesh[i].mesh); } editMode.fullMesh.optimised = true; } if (editMode.detailMesh != null) { for (int i = 0; i < editMode.detailMesh.meshCount; i++) { MeshUtility.Optimize(editMode.detailMesh[i].mesh); } editMode.detailMesh.optimised = true; } GUI.changed = false; } EditorGUI.EndDisabledGroup(); if (!editMode.fullMesh.optimised) { EditorGUILayout.HelpBox("The model is currently fully optimised for runtime", MessageType.Warning); } EditorGUILayout.EndHorizontal(); //Underside render toggle EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Render Underside of Building"); bool drawUnderside = EditorGUILayout.Toggle(_data.drawUnderside, GUILayout.Width(15)); if (drawUnderside != _data.drawUnderside) { _data.drawUnderside = drawUnderside; } EditorGUILayout.EndHorizontal(); //Define the height of foundations if you need them EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Foundation Height", GUILayout.Width(120)); _data.foundationHeight = EditorGUILayout.Slider(_data.foundationHeight, 0, 25); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Foundation Texture", GUILayout.Width(120)); _data.foundationTexture = EditorGUILayout.Popup(_data.foundationTexture, textureNames, GUILayout.Width(260)); EditorGUILayout.EndHorizontal(); //Collider mesh EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Generate Collider"); _data.generateCollider = (BuildrData.ColliderGenerationModes)EditorGUILayout.EnumPopup(_data.generateCollider); EditorGUILayout.EndHorizontal(); //One draw call mesh -TODO // EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); // EditorGUILayout.LabelField("One Draw Call"); // _data.oneDrawCall = EditorGUILayout.Toggle(_data.oneDrawCall); // EditorGUILayout.EndHorizontal(); for (int i = 0; i < editMode.fullMesh.meshCount; i++) { EditorUtility.SetSelectedWireframeHidden(editMode.meshHolders[i].GetComponent <Renderer>(), !editMode.showWireframe); } }
public void UpdateInteriors() { while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } interiorMeshes.Clear(); if (data.renderInteriors) { int numberOfVolumes = data.plan.numberOfVolumes; for (int v = 0; v < numberOfVolumes; v++) { DynamicMeshGenericMultiMaterialMesh interiorMesh = new DynamicMeshGenericMultiMaterialMesh(); interiorMesh.subMeshCount = data.textures.Count; BuildrInteriors.Build(interiorMesh, data, v); interiorMesh.Build(false); int numberOfInteriorMeshes = interiorMesh.meshCount; for (int i = 0; i < numberOfInteriorMeshes; i++) { string meshName = "model interior"; if (numberOfVolumes > 0) meshName += " volume " + (v + 1); if (numberOfInteriorMeshes > 1) meshName += " mesh " + (i + 1); GameObject newMeshHolder = new GameObject(meshName); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent<MeshFilter>(); meshRend = newMeshHolder.AddComponent<MeshRenderer>(); meshFilt.mesh = interiorMesh[i].mesh; interiorMeshHolders.Add(newMeshHolder); } } } }
//returns the number of meshes private static int ExportDetails(BuildrData data) { DynamicMeshGenericMultiMaterialMesh DET_MESH = new DynamicMeshGenericMultiMaterialMesh(); BuildrDetailExportObject exportObject = BuildrBuildingDetails.Build(DET_MESH, data); int numberOfMeshes = exportObject.detailMeshes.Length; if (numberOfMeshes == 0) return 0; string textureName = data.exportFilename + ATLASED_SUFFIX + DETAIL_SUFFIX; string textureFileName = textureName + ".png"; string newDirectory = ROOT_FOLDER + data.exportFilename; File.WriteAllBytes(newDirectory + "/" + textureFileName, exportObject.texture.EncodeToPNG()); ExportMaterial[] exportTextures = new ExportMaterial[1]; ExportMaterial newTexture = new ExportMaterial(); newTexture.name = textureName; newTexture.filepath = textureFileName; newTexture.generated = true; exportTextures[0] = newTexture; for(int i = 0; i < numberOfMeshes; i++) { string DetailSuffixIndex = ((numberOfMeshes > 1) ? "_"+i : ""); string DetailFileName = data.exportFilename + DETAIL_SUFFIX + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; Export(DetailFileName, DetailFolder, data, exportObject.detailMeshes[i], exportTextures); } Texture2D.DestroyImmediate(exportObject.texture); return numberOfMeshes; }
/// <summary> /// Generate the detail meshes and return the export object /// </summary> /// <param name="mesh"></param> /// <param name="data"></param> /// <returns></returns> public static BuildrDetailExportObject Build(DynamicMeshGenericMultiMaterialMesh mesh, BuildrData data) { BuildrDetailExportObject exportObject = new BuildrDetailExportObject(); List <Texture2D> detailTextures = new List <Texture2D>(); List <int> detailSubmeshesWithTextures = new List <int>(); int numberOfDetails = data.details.Count; mesh.Clear(); mesh.subMeshCount = numberOfDetails; for (int d = 0; d < numberOfDetails; d++) { BuildrDetail detail = data.details[d]; if (detail.mesh == null) { continue; } int faceIndex = detail.face; Vector3 position = Vector3.zero; BuildrPlan plan = data.plan; int numberOfVolumes = plan.numberOfVolumes; Vector2 faceUv = detail.faceUv; Quaternion faceAngle = Quaternion.identity; //Place the detail mesh if (detail.type == BuildrDetail.Types.Facade) { //find facade int facadeCount = 0; bool facadeFound = false; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int p = 0; p < numberOfVolumePoints; p++) { if (facadeCount == faceIndex) { int indexA = p; int indexB = (p + 1) % numberOfVolumePoints; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; Vector3 basePosition = Vector3.Lerp(p0, p1, faceUv.x); Vector3 detailHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight * faceUv.y); Vector3 facadeCross = Vector3.Cross(Vector3.up, p1 - p0).normalized; Vector3 detailDepth = facadeCross * detail.faceHeight; faceAngle = Quaternion.LookRotation(facadeCross); position = basePosition + detailHeight + detailDepth; facadeFound = true; break; } facadeCount++; } if (facadeFound) { break; } } } else//roof detail { BuildrVolume volume = plan.volumes[Mathf.Clamp(0, numberOfVolumes - 1, faceIndex)]; int numberOfVolumePoints = volume.points.Count; Vector3 minimumRoofPoint = plan.points[volume.points[0]].vector3; Vector3 maximumRoofPoint = minimumRoofPoint; for (int p = 1; p < numberOfVolumePoints; p++) { Vector3 p0 = plan.points[volume.points[p]].vector3; if (p0.x < minimumRoofPoint.x) { minimumRoofPoint.x = p0.x; } if (p0.z < minimumRoofPoint.y) { minimumRoofPoint.y = p0.z; } if (p0.x > maximumRoofPoint.x) { maximumRoofPoint.x = p0.x; } if (p0.z > maximumRoofPoint.y) { maximumRoofPoint.y = p0.z; } } position.x = Mathf.Lerp(minimumRoofPoint.x, maximumRoofPoint.x, faceUv.x); position.z = Mathf.Lerp(minimumRoofPoint.y, maximumRoofPoint.y, faceUv.y); position.y = volume.numberOfFloors * data.floorHeight + detail.faceHeight; } Quaternion userRotation = Quaternion.Euler(detail.userRotation); int vertexCount = detail.mesh.vertexCount; Vector3[] verts = new Vector3[vertexCount]; Quaternion rotate = faceAngle * userRotation; for (int i = 0; i < vertexCount; i++) { Vector3 sourceVertex = Vector3.Scale(detail.mesh.vertices[i], detail.scale); Vector3 outputVertex = (rotate) * sourceVertex + position; verts[i] = outputVertex; } mesh.AddData(verts, detail.mesh.uv, detail.mesh.triangles, d); detail.worldPosition = position; detail.worldRotation = rotate; if (detail.material.mainTexture != null) { #if UNITY_EDITOR string texturePath = AssetDatabase.GetAssetPath(detail.material.mainTexture); TextureImporter textureImporter = (TextureImporter)AssetImporter.GetAtPath(texturePath); if (!textureImporter.isReadable) { Debug.LogWarning("The texture you have selected is not readable. Cannot render"); return(exportObject); } detailTextures.Add((Texture2D)detail.material.mainTexture); detailSubmeshesWithTextures.Add(d); #endif } } if (detailtexture != null) { Object.DestroyImmediate(detailtexture); } List <Mesh> outputMeshes = new List <Mesh>(); if (detailSubmeshesWithTextures.Count > 0) { Rect[] textureRects = BuildrTexturePacker2.Pack(out detailtexture, detailTextures.ToArray(), 512); if (detailSubmeshesWithTextures.Count > 0) { mesh.Atlas(detailSubmeshesWithTextures.ToArray(), textureRects); } mesh.CollapseSubmeshes(); mesh.Build(); int numberOfMeshes = mesh.meshCount; for (int i = 0; i < numberOfMeshes; i++) { outputMeshes.Add(mesh[i].mesh); } } exportObject.detailMeshes = outputMeshes.ToArray(); exportObject.texture = detailtexture; return(exportObject); /*if (detailMat == null) * detailMat = new Material(Shader.Find("Diffuse")); * detailMat.mainTexture = detailtexture; * List<Mesh> outputMeshes = new List<Mesh>(); * for (int i = 0; i < numberOfMeshes; i++) * { * outputMeshes.Add(mesh[i].mesh); * GameObject details = new GameObject("details " + i); * details.AddComponent<MeshFilter>().mesh = mesh[i].mesh; * details.AddComponent<MeshRenderer>().sharedMaterial = detailMat; * detailGameobjects.Add(details); * } * } * // Debug.Log("BuildR Detail Pack Complete: " + (Time.realtimeSinceStartup - timestart) + " sec"); * return detailGameobjects.ToArray();*/ }
private static void ExportModel(BuildrData data) { try { EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.0f); //check overwrites... string newDirectory = ROOT_FOLDER + data.exportFilename; if(!CreateFolder(newDirectory)) { EditorUtility.ClearProgressBar(); return; } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.05f); if(data.fullmesh) { //export unpacked model DYN_MESH = new DynamicMeshGenericMultiMaterialMesh(); DYN_MESH.subMeshCount = data.textures.Count; BuildrBuilding.Build(DYN_MESH, data); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.30f); BuildrRoof.Build(DYN_MESH, data); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.60f); DYN_MESH.Build(data.includeTangents); int meshCount = DYN_MESH.meshCount; List<int> unusedTextures = DYN_MESH.unusedSubmeshes; int numberOfUnpackedTextures = data.textures.Count; List<ExportMaterial> exportTextureList = new List<ExportMaterial>(); for (int t = 0; t < numberOfUnpackedTextures; t++) { if (unusedTextures.Contains(t)) continue;//skip, unused ExportMaterial newTexture = new ExportMaterial(); newTexture.name = data.textures[t].name; newTexture.material = data.textures[t].material; newTexture.generated = false; newTexture.filepath = data.textures[t].filePath; exportTextureList.Add(newTexture); } for(int i = 0; i < meshCount; i++) { EXPORT_MESH = DYN_MESH[i].mesh; MeshUtility.Optimize(EXPORT_MESH); Export(data, EXPORT_MESH, exportTextureList.ToArray()); string filenameSuffix = (meshCount>1)? i.ToString() : ""; string filename = data.exportFilename + filenameSuffix; Export(filename, ROOT_FOLDER + data.exportFilename + "/", data, EXPORT_MESH, exportTextureList.ToArray()); } } //Export Collider if(data.generateCollider != BuildrData.ColliderGenerationModes.None) ExportCollider(data); int[] numberOfInteriorMeshes = new int[data.plan.numberOfVolumes]; if(data.renderInteriors && data.fullmesh) numberOfInteriorMeshes = ExportInteriors(data); int[] numberOfStairwells = new int[data.plan.numberOfVolumes]; if (data.renderInteriors && data.fullmesh) numberOfStairwells = ExportStairwells(data); int numberOfDetailMeshes = 0; if(data.fullmesh) numberOfDetailMeshes = ExportDetails(data); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.70f); //Place exported version into scene if(data.fullmesh) { AssetDatabase.Refresh();//ensure the database is up to date... GameObject baseObject = new GameObject(data.exportFilename); if((data.createPrefabOnExport || data.placeIntoScene)) { baseObject.transform.position = CURRENT_TRANSFORM.position; baseObject.transform.rotation = CURRENT_TRANSFORM.rotation; string modelFilePath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + FILE_EXTENTION; GameObject newModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(modelFilePath)); newModel.name = "model"; newModel.transform.parent = baseObject.transform; newModel.transform.localPosition = Vector3.zero; newModel.transform.localRotation = Quaternion.identity; if(data.generateCollider != BuildrData.ColliderGenerationModes.None) { GameObject colliderObject = new GameObject("collider"); string colliderFilePath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + COLLIDER_SUFFIX + FILE_EXTENTION; colliderObject.AddComponent<MeshCollider>().sharedMesh = (Mesh)AssetDatabase.LoadAssetAtPath(colliderFilePath, typeof(Mesh)); colliderObject.transform.parent = baseObject.transform; colliderObject.transform.localPosition = Vector3.zero; colliderObject.transform.localRotation = Quaternion.identity; } for (int i = 0; i < numberOfDetailMeshes; i++) { string detailSuffixIndex = ((numberOfDetailMeshes > 1) ? "_" + i : ""); string detailFileName = data.exportFilename + DETAIL_SUFFIX + detailSuffixIndex; string detailFolder = ROOT_FOLDER + data.exportFilename + "/"; string detailFilepath = detailFolder + detailFileName + FILE_EXTENTION; GameObject detailObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(detailFilepath)); detailObject.name = "details"; detailObject.transform.parent = baseObject.transform; detailObject.transform.localPosition = Vector3.zero; detailObject.transform.localRotation = Quaternion.identity; } int numberOfVolumes = data.plan.numberOfVolumes; GameObject interiorHolder = new GameObject("interiors"); interiorHolder.transform.parent = baseObject.transform; interiorHolder.transform.localPosition = Vector3.zero; interiorHolder.transform.localRotation = Quaternion.identity; for (int v = 0; v < numberOfInteriorMeshes.Length; v++) { int numMeshes = numberOfInteriorMeshes[v]; for (int i = 0; i < numMeshes; i++) { string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + INTERIOR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; string filePath = DetailFolder + DetailFileName + FILE_EXTENTION; GameObject interiorObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(filePath)); interiorObject.name = INTERIOR_SUFFIX + VolumeSuffix + DetailSuffixIndex; interiorObject.transform.parent = interiorHolder.transform; interiorObject.transform.localPosition = Vector3.zero; interiorObject.transform.localRotation = Quaternion.identity; } } for(int v = 0; v < numberOfStairwells.Length; v++) { int numMeshes = numberOfStairwells[v]; for (int i = 0; i < numMeshes; i++) { string VolumeSuffix = ((numberOfVolumes > 1) ? "_" + v : ""); string DetailSuffixIndex = ((numMeshes > 1) ? "_" + i : ""); string DetailFileName = data.exportFilename + STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; string DetailFolder = ROOT_FOLDER + data.exportFilename + "/"; string filePath = DetailFolder + DetailFileName + FILE_EXTENTION; GameObject interiorObject = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(filePath)); interiorObject.name = STAIR_SUFFIX + VolumeSuffix + DetailSuffixIndex; interiorObject.transform.parent = interiorHolder.transform; interiorObject.transform.localPosition = data.plan.volumes[v].stairBaseVector[i]; interiorObject.transform.localRotation = Quaternion.identity; } } } if(data.createPrefabOnExport) { string prefabPath = ROOT_FOLDER + data.exportFilename + "/" + data.exportFilename + ".prefab"; Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); if(prefab == null) prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); PrefabUtility.ReplacePrefab(baseObject, prefab, ReplacePrefabOptions.ConnectToPrefab); } if(!data.placeIntoScene) Object.DestroyImmediate(baseObject); } if(data.exportLowLOD) { ExportLowLOD(data); } DYN_MESH = null; EXPORT_MESH = null; EditorUtility.ClearProgressBar(); EditorUtility.UnloadUnusedAssets(); AssetDatabase.Refresh(); }catch(System.Exception e) { Debug.LogError("BuildR Export Error: "+e); EditorUtility.ClearProgressBar(); } }
public void UpdateDetails() { int numberOfDetails = 0; if (details != null) numberOfDetails = details.Length; for (int i = 0; i < numberOfDetails; i++) DestroyImmediate(details[i]); if (data.plan == null) return; if (data.floorHeight == 0) return; if (data.details.Count == 0) return; if (detailMesh == null) detailMesh = new DynamicMeshGenericMultiMaterialMesh(); if (renderMode != renderModes.full) return;//once data is cleared - asses if we want to rerender the details details = BuildrBuildingDetails.Render(detailMesh,data); numberOfDetails = details.Length; for (int i = 0; i < numberOfDetails; i++) { details[i].transform.parent = transform; details[i].transform.localPosition = Vector3.zero; details[i].transform.localRotation = Quaternion.identity; } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, Rect[] uvConstraints) { data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; int numberOfVolumes = plan.numberOfVolumes; if(plan.numberOfFacades != uvConstraints.Length) Debug.LogError("Incompatible amount of uv constraints " + plan.numberOfFacades +" uvc: "+ uvConstraints.Length); float largestDepthValue = 0;//deepest value of a bay design in the building foreach (BuildrBay bay in data.bays) largestDepthValue = Mathf.Max(largestDepthValue, bay.deepestValue);//get the deepest value foreach (BuildrFacadeDesign facade in data.facades) largestDepthValue = Mathf.Max(largestDepthValue, facade.simpleBay.deepestValue);//get the deepest value int facadeCount = 0; for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) continue; int indexAM = Mathf.Abs((f - 1) % numberOfVolumePoints); int indexA = f; int indexB = (f + 1) % numberOfVolumePoints; int indexBP = (f + 2) % numberOfVolumePoints; Vector3 p0m = plan.points[volume.points[indexAM]].vector3; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; Vector3 p1p = plan.points[volume.points[indexBP]].vector3; facadeCount++; float realFadeWidth = Vector3.Distance(p0, p1); float facadeWidth = realFadeWidth - largestDepthValue * 2.0f; Vector3 facadeDirection = (p1 - p0).normalized; Vector3 facadeCross = Vector3.Cross(facadeDirection, Vector3.up).normalized; Vector3 lastFacadeDirection = (p0 - p0m).normalized; Vector3 nextFacadeDirection = (p1p - p1).normalized; //only bother with facade directions when facade may intersect inverted geometry float facadeDirDotL = Vector3.Dot(-facadeDirection, lastFacadeDirection); float facadeCrossDotL = Vector3.Dot(-facadeCross, lastFacadeDirection); if (facadeDirDotL <= 0 || facadeCrossDotL <= 0) lastFacadeDirection = -facadeCross; float facadeDirDotN = Vector3.Dot(-facadeDirection, nextFacadeDirection); float facadeCrossDotN = Vector3.Dot(-facadeCross, nextFacadeDirection); if (facadeDirDotN <= 0 || facadeCrossDotN <= 0) nextFacadeDirection = facadeCross; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); p0 += floorHeightStart; BuildrVolumeStylesUnit[] styleUnits = volume.styles.GetContentsByFacade(volume.points[indexA]); int floorPatternSize = 0; List<int> facadePatternReference = new List<int>();//this contains a list of all the facade style indices to refence when looking for the appropriate style per floor int patternCount = 0; foreach (BuildrVolumeStylesUnit styleUnit in styleUnits)//need to knw how big all the styles are together so we can loop through them { floorPatternSize += styleUnit.floors; for (int i = 0; i < styleUnit.floors; i++) facadePatternReference.Add(patternCount); patternCount++; } facadePatternReference.Reverse(); int rows = numberOfFloors; float foundationHeight = data.foundationHeight; if (foundationHeight > 0 && floorBase == 0) { int subMesh = _data.foundationTexture; Vector3 foundationVector = Vector3.up * -foundationHeight; Vector3 w0 = p0 + foundationVector; Vector3 w1 = p1 + foundationVector; Vector3 w2 = p0; Vector3 w3 = p1; AddPlane(w0, w1, w2, w3, subMesh, false, new Vector2(0, -foundationHeight), new Vector2(facadeWidth, 0)); } Vector2 facadeUV = Vector2.zero; for (int r = 0; r < rows; r++) { bool firstRow = r == 0; bool lastRow = r == (rows - 1); //Get the facade style id //need to loop through the facade designs floor by floor until we get to the right one float currentHeight = floorHeight * r; Vector3 facadeFloorBaseVector = p0 + Vector3.up * currentHeight; int modFloor = (r % floorPatternSize); int modFloorPlus = ((r + 1) % floorPatternSize); int modFloorMinus = (r > 0) ? ((r - 1) % floorPatternSize) : 0; BuildrFacadeDesign lastFacadeDesign = null; BuildrFacadeDesign nextFacadeDesign = null; facadeDesign = data.facades[styleUnits[facadePatternReference[modFloor]].styleID]; nextFacadeDesign = data.facades[styleUnits[facadePatternReference[modFloorPlus]].styleID]; lastFacadeDesign = data.facades[styleUnits[facadePatternReference[modFloorMinus]].styleID]; bool isBlankWall = !facadeDesign.hasWindows; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { if(data.bays.Count == 0 || facadeDesign.bayPattern.Count == 0) isBlankWall = true; else { BuildrBay firstBay = data.bays[facadeDesign.bayPattern[0]]; if (firstBay.openingWidth > facadeWidth) isBlankWall = true; if (facadeDesign.bayPattern.Count == 0) isBlankWall = true; } } else { if (facadeDesign.simpleBay.openingWidth + facadeDesign.simpleBay.minimumBayWidth > facadeWidth) isBlankWall = true; } Vector3 windowBase = facadeFloorBaseVector; facadeUV.x = 0; facadeUV.y += currentHeight; if (!isBlankWall) { float patternSize = 0;//the space the pattern fills, there will be a gap that will be distributed to all bay styles int numberOfBays = 0; BuildrBay[] bayDesignPattern; int numberOfBayDesigns; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { numberOfBayDesigns = facadeDesign.bayPattern.Count; bayDesignPattern = new BuildrBay[numberOfBayDesigns]; for (int i = 0; i < numberOfBayDesigns; i++) { bayDesignPattern[i] = data.bays[facadeDesign.bayPattern[i]]; } } else { bayDesignPattern = new[] { facadeDesign.simpleBay }; numberOfBayDesigns = 1; } //start with first window width - we'll be adding to this until we have filled the facade width int it = 100; while (true) { int patternModIndex = numberOfBays % numberOfBayDesigns; float patternAddition = bayDesignPattern[patternModIndex].openingWidth + bayDesignPattern[patternModIndex].minimumBayWidth; if (patternSize + patternAddition < facadeWidth) { patternSize += patternAddition; numberOfBays++; } else break; it--; if (it < 0) break; } float perBayAdditionalSpacing = (facadeWidth - patternSize) / numberOfBays; for (int c = 0; c < numberOfBays; c++) { BuildrBay bayStyle; BuildrBay lastBay; BuildrBay nextBay; bool firstColumn = c == 0; bool lastColumn = c == numberOfBays - 1; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { int numberOfBayStyles = facadeDesign.bayPattern.Count; bayStyle = bayDesignPattern[c % numberOfBayStyles]; int lastBayIndex = (c > 0) ? (c - 1) % numberOfBayStyles : 0; lastBay = bayDesignPattern[lastBayIndex]; nextBay = bayDesignPattern[(c + 1) % numberOfBayStyles]; } else { bayStyle = facadeDesign.simpleBay; lastBay = facadeDesign.simpleBay; nextBay = facadeDesign.simpleBay; } float actualWindowSpacing = bayStyle.minimumBayWidth + perBayAdditionalSpacing; float leftWidth = actualWindowSpacing * bayStyle.openingWidthRatio; float rightWidth = actualWindowSpacing - leftWidth; float openingWidth = bayStyle.openingWidth; if (firstColumn) leftWidth += largestDepthValue; if (lastColumn) rightWidth += largestDepthValue; // float openingHeight = bayStyle.openingHeight; BuildrTexture columnTexture = textures[bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture)]; Vector2 columnuvunits = columnTexture.tileUnitUV; float openingHeight = bayStyle.openingHeight; if (columnTexture.patterned) openingHeight = Mathf.Round(bayStyle.openingHeight / columnuvunits.y) * columnuvunits.y; //if (bayStyle.openingHeight == floorHeight) bayStyle.openingHeight = floorHeight; float rowBottomHeight = ((floorHeight - openingHeight) * bayStyle.openingHeightRatio); if (columnTexture.patterned) rowBottomHeight = Mathf.Ceil(rowBottomHeight / columnuvunits.y) * columnuvunits.y; float rowTopHeight = (floorHeight - rowBottomHeight - openingHeight); bool previousBayIdentical = bayStyle == lastBay; bool nextBayIdentical = bayStyle == nextBay; if (previousBayIdentical && !firstColumn) leftWidth = actualWindowSpacing;//if next design is identical - add the two parts together the reduce polycount Vector3 w0, w1, w2, w3; float windowSideDepth, bottomDepth; if (!bayStyle.isOpening) { int wallSubMesh = bayStyle.GetTexture(BuildrBay.TextureNames.WallTexture); bool wallFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.WallTexture); float bayWidthSize = openingWidth + actualWindowSpacing; if (firstColumn || lastColumn) bayWidthSize += largestDepthValue; Vector3 bayWidth = facadeDirection * bayWidthSize; Vector3 bayHeight = Vector3.up * floorHeight; Vector3 bayDepth = facadeCross * largestDepthValue; w0 = windowBase; w1 = windowBase + bayWidth; w2 = windowBase + bayHeight; w3 = windowBase + bayWidth + bayHeight; Vector2 bayOpeningUVEnd = facadeUV + new Vector2(openingWidth + actualWindowSpacing, floorHeight); AddPlane(w0, w1, w2, w3, wallSubMesh, wallFlipped, facadeUV, bayOpeningUVEnd); Vector2 UVEnd = new Vector2(1, floorHeight); if (!previousBayIdentical && !firstColumn)//left { Vector3 wA = w0 + bayDepth; Vector3 wB = w2 + bayDepth; AddPlane(w2, wB, w0, wA, wallSubMesh, wallFlipped, Vector2.zero, UVEnd); } if (!nextBayIdentical && !lastColumn)//right { Vector3 wA = w1 + bayDepth; Vector3 wB = w3 + bayDepth; AddPlane(w1, wA, w3, wB, wallSubMesh, wallFlipped, Vector2.zero, UVEnd); } if (lastFacadeDesign != facadeDesign && !firstRow)//bottom { Vector3 wA = w0 + ((!firstColumn) ? facadeCross*largestDepthValue : -lastFacadeDirection*largestDepthValue); Vector3 wB = w1 + ((!lastColumn) ? facadeCross*largestDepthValue : nextFacadeDirection*largestDepthValue); AddPlane(w0, wA, w1, wB, wallSubMesh, wallFlipped, Vector2.zero, UVEnd); } if (nextFacadeDesign != facadeDesign && !lastRow)//top { Vector3 wA = w2 + ((!firstColumn) ? facadeCross*largestDepthValue : -lastFacadeDirection*largestDepthValue); Vector3 wB = w3 + ((!lastColumn) ? facadeCross*largestDepthValue : nextFacadeDirection*largestDepthValue); AddPlane(w3, wB, w2, wA, wallSubMesh, wallFlipped, Vector2.zero, UVEnd); } windowBase = w1;//move base vertor to next bay facadeUV.x += bayWidthSize; continue;//bay filled - move onto next bay } var verts = new Vector3[16]; verts[0] = windowBase; verts[1] = verts[0] + leftWidth * facadeDirection; verts[2] = verts[1] + openingWidth * facadeDirection; verts[3] = verts[2] + rightWidth * facadeDirection; windowBase = (nextBayIdentical) ? verts[2] : verts[3];//move to next window - if next design is identical - well add the two parts together the reduce polycount facadeUV.x += (nextBayIdentical) ? openingWidth : openingWidth + rightWidth; Vector3 rowBottomVector = Vector3.up * rowBottomHeight; verts[4] = verts[0] + rowBottomVector; verts[5] = verts[1] + rowBottomVector; verts[6] = verts[2] + rowBottomVector; verts[7] = verts[3] + rowBottomVector; Vector3 openingVector = Vector3.up * openingHeight; verts[8] = verts[4] + openingVector; verts[9] = verts[5] + openingVector; verts[10] = verts[6] + openingVector; verts[11] = verts[7] + openingVector; Vector3 rowTopVector = Vector3.up * rowTopHeight; verts[12] = verts[8] + rowTopVector; verts[13] = verts[9] + rowTopVector; verts[14] = verts[10] + rowTopVector; verts[15] = verts[11] + rowTopVector; Vector3 openingDepthVector = facadeCross * bayStyle.openingDepth; Vector3 crossDepthVector = facadeCross * bayStyle.crossDepth; Vector3 rowDepthVector = facadeCross * bayStyle.rowDepth; Vector3 columnDepthVector = facadeCross * bayStyle.columnDepth; Vector3 largestDepthVector = facadeCross * largestDepthValue; Vector2 uvStart, uvEnd; int windowSubMesh = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningBackTexture); int submeshBottom = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningSillTexture); int submeshTop = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningCeilingTexture); int columnSubMesh = bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture); int crossSubMesh = bayStyle.GetTexture(BuildrBay.TextureNames.CrossTexture); int rowSubMesh = bayStyle.GetTexture(BuildrBay.TextureNames.RowTexture); bool windowFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningBackTexture); bool flippedBottom = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningSillTexture); bool flippedTop = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningCeilingTexture); bool columnFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.ColumnTexture); bool crossFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.CrossTexture); bool rowFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.RowTexture); int windowBoxSubmesh = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningSideTexture); bool windowBoxFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningSideTexture); ///WINDOWS w0 = verts[5] + openingDepthVector; w1 = verts[6] + openingDepthVector; w2 = verts[9] + openingDepthVector; w3 = verts[10] + openingDepthVector; Vector2 windowUVStart = facadeUV + new Vector2(leftWidth, rowBottomHeight); Vector2 windowUVEnd = windowUVStart + new Vector2(openingWidth, openingHeight); if (bayStyle.renderBack && !data.cullBays) AddPlane(w0, w1, w2, w3, windowSubMesh, windowFlipped, windowUVStart, windowUVEnd); //Window Sides w0 = verts[5] + largestDepthVector; w1 = verts[6] + largestDepthVector; w2 = verts[9] + largestDepthVector; w3 = verts[10] + largestDepthVector; windowSideDepth = Mathf.Min(bayStyle.columnDepth, bayStyle.openingDepth) - largestDepthValue;//Window Left float columnDiff = bayStyle.columnDepth - bayStyle.openingDepth; if (data.renderInteriors)//Inner Window Walls { //left uvStart = facadeUV + new Vector2(leftWidth, rowBottomHeight); uvEnd = uvStart + new Vector2(windowSideDepth, openingHeight); Vector3 leftDepthVector = facadeCross * windowSideDepth; if (firstColumn) leftDepthVector = facadeCross * -largestDepthValue; Vector3 wl0 = w0 + leftDepthVector; Vector3 wl2 = w2 + leftDepthVector; AddPlane(wl0, w0, wl2, w2, windowBoxSubmesh, windowBoxFlipped, uvStart, uvEnd); //right uvStart = facadeUV + new Vector2(leftWidth + openingWidth - windowSideDepth, rowBottomHeight); uvEnd = uvStart + new Vector2(windowSideDepth, openingHeight); Vector3 rightDepthVector = facadeCross * windowSideDepth; if (lastColumn) rightDepthVector = facadeCross * -largestDepthValue; Vector3 wr1 = w1 + rightDepthVector; Vector3 wr3 = w3 + rightDepthVector; AddPlane(wr3, w3, wr1, w1, windowBoxSubmesh, windowBoxFlipped, uvEnd, uvStart); } if (columnDiff > 0 || !data.renderInteriors)//External Window Sides { //left uvStart = facadeUV + new Vector2(leftWidth, rowBottomHeight); uvEnd = uvStart + new Vector2(columnDiff, openingHeight); Vector3 sideDepthVectorA = facadeCross * (bayStyle.columnDepth - largestDepthValue); Vector3 sideDepthVectorB = facadeCross * (bayStyle.openingDepth - largestDepthValue); if (firstColumn) sideDepthVectorA = facadeCross * -largestDepthValue; Vector3 wd0l = w0 + sideDepthVectorA; Vector3 wd1l = w2 + sideDepthVectorA; Vector3 wd2l = w0 + sideDepthVectorB; Vector3 wd3l = w2 + sideDepthVectorB; AddPlane(wd0l, wd2l, wd1l, wd3l, windowBoxSubmesh, windowBoxFlipped, uvStart, uvEnd); // right uvStart = facadeUV + new Vector2(leftWidth + openingWidth - windowSideDepth, rowBottomHeight); uvEnd = uvStart + new Vector2(windowSideDepth, openingHeight); sideDepthVectorA = facadeCross * (bayStyle.columnDepth - largestDepthValue); sideDepthVectorB = facadeCross * (bayStyle.openingDepth - largestDepthValue); if (lastColumn) sideDepthVectorA = facadeCross * -largestDepthValue; Vector3 wd0r = w1 + sideDepthVectorA; Vector3 wd1r = w3 + sideDepthVectorA; Vector3 wd2r = w1 + sideDepthVectorB; Vector3 wd3r = w3 + sideDepthVectorB; AddPlane(wd1r, wd3r, wd0r, wd2r, windowBoxSubmesh, windowBoxFlipped, uvStart, uvEnd); } //Window Row Sides/Sills bottomDepth = Mathf.Min(bayStyle.rowDepth, bayStyle.openingDepth) - largestDepthValue; float rowDiff = bayStyle.rowDepth - bayStyle.openingDepth; if (data.renderInteriors)//Window Sill Interiors { uvStart = new Vector2(leftWidth, 0); uvEnd = uvStart + new Vector2(openingWidth, bottomDepth); // if (rowBottomHeight > 0)//Bottom // { Vector3 bottomDepthVector = facadeCross * bottomDepth; Vector3 wl0 = w0 + bottomDepthVector; Vector3 wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); // } // if (rowTopHeight > 0)//Top // { Vector3 topDepthVector = facadeCross * bottomDepth; Vector3 wl2 = w2 + topDepthVector; Vector3 wl3 = w3 + topDepthVector; AddPlane(w2, w3, wl2, wl3, submeshTop, flippedTop, uvStart, uvEnd); // } } if (rowDiff > 0 || !data.renderInteriors)//Window External Sills { uvStart = facadeUV + new Vector2(leftWidth, 0); uvEnd = uvStart + new Vector2(openingWidth, rowDiff); // if (rowBottomHeight > 0)//Bottom // { Vector3 wd0l = w0 + facadeCross * (bayStyle.rowDepth - largestDepthValue); Vector3 wd1l = w1 + facadeCross * (bayStyle.rowDepth - largestDepthValue); Vector3 wd2l = w0 + facadeCross * (bayStyle.openingDepth - largestDepthValue); Vector3 wd3l = w1 + facadeCross * (bayStyle.openingDepth - largestDepthValue); AddPlane(wd0l, wd1l, wd2l, wd3l, submeshBottom, flippedBottom, uvStart, uvEnd); // } // if (rowTopHeight > 0)//Top // { Vector3 wd0r = w2 + facadeCross * (bayStyle.rowDepth - largestDepthValue); Vector3 wd1r = w3 + facadeCross * (bayStyle.rowDepth - largestDepthValue); Vector3 wd2r = w2 + facadeCross * (bayStyle.openingDepth - largestDepthValue); Vector3 wd3r = w3 + facadeCross * (bayStyle.openingDepth - largestDepthValue); AddPlane(wd1r, wd0r, wd3r, wd2r, submeshTop, flippedTop, uvStart, uvEnd); // } } ///COLUMNS //Column Face if (leftWidth > 0)//Column Face Left { Vector3 leftColumnDepthVector = (!firstColumn) ? columnDepthVector : Vector3.zero; w0 = verts[4] + leftColumnDepthVector; w1 = verts[5] + leftColumnDepthVector; w2 = verts[8] + leftColumnDepthVector; w3 = verts[9] + leftColumnDepthVector; Vector2 leftColumnUVStart = facadeUV + new Vector2(0, rowBottomHeight); Vector2 leftColumnUVEnd = leftColumnUVStart + new Vector2(leftWidth, openingHeight); AddPlane(w0, w1, w2, w3, columnSubMesh, columnFlipped, leftColumnUVStart, leftColumnUVEnd); if (!firstColumn)//Left Column Top Bottom { w0 = verts[4] + largestDepthVector; w1 = verts[5] + largestDepthVector; w2 = verts[8] + largestDepthVector; w3 = verts[9] + largestDepthVector; bottomDepth = Mathf.Min(bayStyle.crossDepth, bayStyle.columnDepth) - largestDepthValue; float colDiff = bayStyle.crossDepth - bayStyle.columnDepth; if (bottomDepth != 0) { if (data.renderInteriors) { uvStart = new Vector2(0, 0); uvEnd = uvStart + new Vector2(leftWidth, bottomDepth); Vector3 bottomDepthVector = facadeCross * bottomDepth; // if(rowBottomHeight>0) // { Vector3 wl0 = w0 + bottomDepthVector; Vector3 wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); // } // if(rowTopHeight > 0) // { Vector3 wl2 = w2 + bottomDepthVector; Vector3 wl3 = w3 + bottomDepthVector; AddPlane(w2, w3, wl2, wl3, submeshTop, flippedTop, uvStart, uvEnd); // } } if (colDiff > 0 || !data.renderInteriors) { uvStart = new Vector2(0, rowBottomHeight); uvEnd = uvStart + new Vector2(rowDiff, openingHeight); // if(rowBottomHeight > 0)//Bottom // { Vector3 wd0l = w0 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd1l = w1 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd2l = w0 + facadeCross * (bayStyle.columnDepth - largestDepthValue); Vector3 wd3l = w1 + facadeCross * (bayStyle.columnDepth - largestDepthValue); AddPlane(wd2l, wd0l, wd3l, wd1l, submeshBottom, flippedBottom, uvStart, uvEnd); // } // if(rowTopHeight > 0)//Top // { Vector3 wd0r = w2 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd1r = w3 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd2r = w2 + facadeCross * (bayStyle.columnDepth - largestDepthValue); Vector3 wd3r = w3 + facadeCross * (bayStyle.columnDepth - largestDepthValue); AddPlane(wd3r, wd1r, wd2r, wd0r, submeshTop, flippedTop, uvStart, uvEnd); // } } } } } if ((!nextBayIdentical || lastColumn) && rightWidth > 0)//Column Right { Vector3 rightColumeDepthVector = (!lastColumn) ? columnDepthVector : Vector3.zero; w0 = verts[6] + rightColumeDepthVector; w1 = verts[7] + rightColumeDepthVector; w2 = verts[10] + rightColumeDepthVector; w3 = verts[11] + rightColumeDepthVector; Vector2 rightColumnUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight); Vector2 rightColumnUVEnd = rightColumnUVStart + new Vector2(rightWidth, openingHeight); AddPlane(w0, w1, w2, w3, columnSubMesh, columnFlipped, rightColumnUVStart, rightColumnUVEnd); if (!lastColumn)//Right Column Top Bottom { w0 = verts[6] + largestDepthVector; w1 = verts[7] + largestDepthVector; w2 = verts[10] + largestDepthVector; w3 = verts[11] + largestDepthVector; bottomDepth = Mathf.Min(bayStyle.crossDepth, bayStyle.columnDepth) - largestDepthValue;//Window Left float colDiff = bayStyle.crossDepth - bayStyle.columnDepth; if (bottomDepth != 0) { if (data.renderInteriors) { uvStart = new Vector2(leftWidth+openingWidth, 0); uvEnd = uvStart + new Vector2(rightWidth, bottomDepth); Vector3 bottomDepthVector = facadeCross * bottomDepth; if (rowBottomHeight > 0) { Vector3 wl0 = w0 + bottomDepthVector; Vector3 wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); } if (rowTopHeight > 0) { Vector3 wl2 = w2 + bottomDepthVector; Vector3 wl3 = w3 + bottomDepthVector; AddPlane(wl3, wl2, w3, w2, submeshTop, flippedTop, uvStart, uvEnd); } } if (colDiff > 0 || !data.renderInteriors) { uvStart = facadeUV + new Vector2(0, rowBottomHeight); uvEnd = uvStart + new Vector2(rowDiff, openingHeight); if (rowBottomHeight > 0)//Bottom { Vector3 wd0l = w0 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd1l = w1 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd2l = w0 + facadeCross * (bayStyle.columnDepth - largestDepthValue); Vector3 wd3l = w1 + facadeCross * (bayStyle.columnDepth - largestDepthValue); AddPlane(wd2l, wd0l, wd3l, wd1l, submeshBottom, flippedBottom, uvStart, uvEnd); } if (rowTopHeight > 0)//Top { Vector3 wd0r = w2 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd1r = w3 + facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 wd2r = w2 + facadeCross * (bayStyle.columnDepth - largestDepthValue); Vector3 wd3r = w3 + facadeCross * (bayStyle.columnDepth - largestDepthValue); AddPlane(wd3r, wd1r, wd2r, wd0r, submeshTop, flippedTop, uvStart, uvEnd); } } } } } ///ROWS //Row Bottom w0 = verts[1] + rowDepthVector; w1 = verts[2] + rowDepthVector; w2 = verts[5] + rowDepthVector; w3 = verts[6] + rowDepthVector; if (rowBottomHeight > 0) { Vector2 bottomRowUVStart = facadeUV + new Vector2(leftWidth, 0); Vector2 bottomRowUVEnd = bottomRowUVStart + new Vector2(openingWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, rowSubMesh, rowFlipped, bottomRowUVStart, bottomRowUVEnd); //Row Sides w0 = verts[1] + largestDepthVector; w1 = verts[2] + largestDepthVector; w2 = verts[5] + largestDepthVector; w3 = verts[6] + largestDepthVector; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(largestDepthValue, rowBottomHeight); if (leftWidth > 0)//Left Side { Vector3 sideDepthVectorA = facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 sideDepthVectorB = facadeCross * (bayStyle.rowDepth - largestDepthValue); if(firstColumn) { sideDepthVectorA = facadeCross * -largestDepthValue; } // uvEnd.x = largestDepthValue;//Vector3.Distance(sideDepthVectorA, sideDepthVectorB); // uvEnd.y = Vector3.Distance(sideDepthVectorA,sideDepthVectorB); Vector3 wd0l = w0 + sideDepthVectorA; Vector3 wd1l = w2 + sideDepthVectorA; Vector3 wd2l = w0 + sideDepthVectorB; Vector3 wd3l = w2 + sideDepthVectorB; AddPlane(wd0l, wd2l, wd1l, wd3l, rowSubMesh, rowFlipped, uvStart, uvEnd); } // if (rightWidth > 0)//Right Side // { Vector3 sideDepthVectorC = facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 sideDepthVectorD = facadeCross * (bayStyle.rowDepth - largestDepthValue); if (lastColumn) sideDepthVectorC = facadeCross * -largestDepthValue; Vector3 wd0r = w1 + sideDepthVectorC; Vector3 wd1r = w3 + sideDepthVectorC; Vector3 wd2r = w1 + sideDepthVectorD; Vector3 wd3r = w3 + sideDepthVectorD; AddPlane(wd1r, wd3r, wd0r, wd2r, rowSubMesh, rowFlipped, uvStart, uvEnd); // } } //Row Top w0 = verts[9] + rowDepthVector; w1 = verts[10] + rowDepthVector; w2 = verts[13] + rowDepthVector; w3 = verts[14] + rowDepthVector; if (rowTopHeight > 0) { Vector2 topRowUVStart = facadeUV + new Vector2(leftWidth, rowBottomHeight + openingHeight); Vector2 topRowUVEnd = topRowUVStart + new Vector2(openingWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, rowSubMesh, rowFlipped, topRowUVStart, topRowUVEnd); //Row Sides w0 = verts[9] + largestDepthVector; w1 = verts[10] + largestDepthVector; w2 = verts[13] + largestDepthVector; w3 = verts[14] + largestDepthVector; uvStart = facadeUV + new Vector2(0, rowBottomHeight+openingHeight); uvEnd = uvStart + new Vector2(largestDepthValue, rowTopHeight); if (leftWidth > 0)//Left Side { Vector3 sideDepthVectorA = facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 sideDepthVectorB = facadeCross * (bayStyle.rowDepth - largestDepthValue); if (firstColumn) sideDepthVectorA = facadeCross * -largestDepthValue; Vector3 wd0l = w0 + sideDepthVectorA; Vector3 wd1l = w2 + sideDepthVectorA; Vector3 wd2l = w0 + sideDepthVectorB; Vector3 wd3l = w2 + sideDepthVectorB; AddPlane(wd0l, wd2l, wd1l, wd3l, rowSubMesh, rowFlipped, uvStart, uvEnd); } // if (rightWidth > 0)//Right Side // { Vector3 sideDepthVectorC = facadeCross * (bayStyle.crossDepth - largestDepthValue); Vector3 sideDepthVectorD = facadeCross * (bayStyle.rowDepth - largestDepthValue); if (lastColumn) sideDepthVectorC = facadeCross * -largestDepthValue; Vector3 wd0r = w1 + sideDepthVectorC; Vector3 wd1r = w3 + sideDepthVectorC; Vector3 wd2r = w1 + sideDepthVectorD; Vector3 wd3r = w3 + sideDepthVectorD; AddPlane(wd1r, wd3r, wd0r, wd2r, rowSubMesh, rowFlipped, uvStart, uvEnd); // } } //Cross Left Bottom Vector3 leftCrossDepthVector = (!firstColumn) ? crossDepthVector : Vector3.zero; w0 = verts[0] + leftCrossDepthVector; w1 = verts[1] + leftCrossDepthVector; w2 = verts[4] + leftCrossDepthVector; w3 = verts[5] + leftCrossDepthVector; Vector2 crossLBUVStart = facadeUV + new Vector2(0, 0); Vector2 crossLBUVEnd = crossLBUVStart + new Vector2(leftWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, crossSubMesh, crossFlipped, crossLBUVStart, crossLBUVEnd); //Cross Left Top if (rowTopHeight > 0) { w0 = verts[8] + leftCrossDepthVector; w1 = verts[9] + leftCrossDepthVector; w2 = verts[12] + leftCrossDepthVector; w3 = verts[13] + leftCrossDepthVector; Vector2 crossLTUVStart = facadeUV + new Vector2(0, rowBottomHeight + openingHeight); Vector2 crossLTUVEnd = crossLTUVStart + new Vector2(leftWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, crossSubMesh, crossFlipped, crossLTUVStart, crossLTUVEnd); } if ((!nextBayIdentical || lastColumn) && rightWidth > 0) { if(lastColumn) crossDepthVector = Vector3.zero;//zero the ends of buildings //Cross Right Bottom w0 = verts[2] + crossDepthVector; w1 = verts[3] + crossDepthVector; w2 = verts[6] + crossDepthVector; w3 = verts[7] + crossDepthVector; Vector2 crossRBUVStart = facadeUV + new Vector2(leftWidth + openingWidth, 0); Vector2 crossRBUVEnd = crossRBUVStart + new Vector2(rightWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, crossSubMesh, crossFlipped, crossRBUVStart, crossRBUVEnd); //Cross Right Top if (rowTopHeight > 0) { w0 = verts[10] + crossDepthVector; w1 = verts[11] + crossDepthVector; w2 = verts[14] + crossDepthVector; w3 = verts[15] + crossDepthVector; Vector2 crossRTUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight + openingHeight); Vector2 crossRTUVEnd = crossRTUVStart + new Vector2(rightWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, crossSubMesh, crossFlipped, crossRTUVStart, crossRTUVEnd); } } ///FACADE BOTTOMS if (lastFacadeDesign != facadeDesign && rowBottomHeight > 0) { //Row Bottom w0 = verts[1] + largestDepthVector; w1 = verts[2] + largestDepthVector; bottomDepth = bayStyle.rowDepth - largestDepthValue; uvStart = facadeUV + new Vector2(leftWidth, 0); uvEnd = uvStart + new Vector2(openingWidth, bottomDepth); Vector3 bottomDepthVector = facadeCross * bottomDepth; Vector3 wl0 = w0 + bottomDepthVector; Vector3 wl1 = w1 + bottomDepthVector; AddPlane(w0, w1, wl0, wl1, submeshBottom, flippedBottom, uvStart, uvEnd); if(!firstColumn) { //Left Cross Bottom w0 = verts[0] + largestDepthVector; w1 = verts[1] + largestDepthVector; bottomDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(leftWidth, bottomDepth); bottomDepthVector = facadeCross * bottomDepth; wl0 = w0 + bottomDepthVector; wl1 = w1 + bottomDepthVector; AddPlane(w0, w1, wl0, wl1, submeshBottom, flippedBottom, uvStart, uvEnd); } //Right Cross Bottom if ((!nextBayIdentical && !lastColumn) && rightWidth > 0) { w0 = verts[2] + largestDepthVector; w1 = verts[3] + largestDepthVector; bottomDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(leftWidth + openingWidth, 0); uvEnd = uvStart + new Vector2(rightWidth, bottomDepth); bottomDepthVector = facadeCross * bottomDepth; wl0 = w0 + bottomDepthVector; wl1 = w1 + bottomDepthVector; AddPlane(w0, w1, wl0, wl1, submeshBottom, flippedBottom, uvStart, uvEnd); } } ///FACADE TOPS if (nextFacadeDesign != facadeDesign && rowTopHeight > 0) { //Row Top w0 = verts[13] + largestDepthVector; w1 = verts[14] + largestDepthVector; bottomDepth = bayStyle.rowDepth - largestDepthValue; uvStart = facadeUV + new Vector2(leftWidth, 0); uvEnd = uvStart + new Vector2(openingWidth, bottomDepth); Vector3 bottomDepthVector = facadeCross * bottomDepth; Vector3 wl0 = w0 + bottomDepthVector; Vector3 wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); //Left Cross Top if (!firstColumn) { w0 = verts[12] + largestDepthVector; w1 = verts[13] + largestDepthVector; bottomDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(leftWidth,bottomDepth); bottomDepthVector = facadeCross * bottomDepth; wl0 = w0 + bottomDepthVector; wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); } //Right Cross Top if ((!nextBayIdentical && !lastColumn) && rightWidth > 0) { w0 = verts[14] + largestDepthVector; w1 = verts[15] + largestDepthVector; bottomDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(leftWidth+openingWidth, 0); uvEnd = uvStart + new Vector2(rightWidth, bottomDepth); bottomDepthVector = facadeCross * bottomDepth; wl0 = w0 + bottomDepthVector; wl1 = w1 + bottomDepthVector; AddPlane(wl0, wl1, w0, w1, submeshBottom, flippedBottom, uvStart, uvEnd); } } ///BAY SIDES // LEFT if(!previousBayIdentical) { //Column w1 = verts[4] + largestDepthVector; w3 = verts[8] + largestDepthVector; windowSideDepth = bayStyle.columnDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, rowBottomHeight); uvEnd = uvStart + new Vector2(windowSideDepth, openingHeight); Vector3 depthVector = facadeCross * windowSideDepth; Vector3 wr1 = w1 + depthVector; Vector3 wr3 = w3 + depthVector; AddPlane(wr3, w3, wr1, w1, columnSubMesh, columnFlipped, uvStart, uvEnd); //Cross Bottom w1 = verts[0] + largestDepthVector; w3 = verts[4] + largestDepthVector; windowSideDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(windowSideDepth, rowBottomHeight); depthVector = facadeCross * windowSideDepth; wr1 = w1 + depthVector; wr3 = w3 + depthVector; AddPlane(wr3, w3, wr1, w1, crossSubMesh, crossFlipped, uvStart, uvEnd); //Cross Top if (rowTopHeight > 0) { w1 = verts[8] + largestDepthVector; w3 = verts[12] + largestDepthVector; windowSideDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(windowSideDepth, rowTopHeight); depthVector = facadeCross * windowSideDepth; wr1 = w1 + depthVector; wr3 = w3 + depthVector; AddPlane(wr3, w3, wr1, w1, crossSubMesh, crossFlipped, uvStart, uvEnd); } } //RIGHT if (!nextBayIdentical && !lastColumn) { //Column Sides w1 = verts[7] + largestDepthVector; w3 = verts[11] + largestDepthVector; windowSideDepth = bayStyle.columnDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, rowBottomHeight); uvEnd = uvStart + new Vector2(windowSideDepth, openingHeight); Vector3 depthVector = facadeCross * windowSideDepth; Vector3 wr1 = w1 + depthVector; Vector3 wr3 = w3 + depthVector; AddPlane(w3, wr3, w1, wr1, columnSubMesh, columnFlipped, uvStart, uvEnd); //Cross Bottom w1 = verts[3] + largestDepthVector; w3 = verts[7] + largestDepthVector; windowSideDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(windowSideDepth, rowBottomHeight); depthVector = facadeCross * windowSideDepth; wr1 = w1 + depthVector; wr3 = w3 + depthVector; AddPlane(w3, wr3, w1, wr1, crossSubMesh, crossFlipped, uvStart, uvEnd); //Cross Top if (rowTopHeight > 0) { w1 = verts[11] + largestDepthVector; w3 = verts[15] + largestDepthVector; windowSideDepth = bayStyle.crossDepth - largestDepthValue; uvStart = facadeUV + new Vector2(0, 0); uvEnd = uvStart + new Vector2(windowSideDepth, rowTopHeight); depthVector = facadeCross * windowSideDepth; wr1 = w1 + depthVector; wr3 = w3 + depthVector; AddPlane(w3, wr3, w1, wr1, crossSubMesh, crossFlipped, uvStart, uvEnd); } } } } else { // windowless wall Vector3 wallVector = (facadeDirection * (facadeWidth+largestDepthValue*2.0f)); Vector3 wallHeightVector = Vector3.up * floorHeight; Vector3 w0 = facadeFloorBaseVector; Vector3 w1 = facadeFloorBaseVector + wallVector; Vector3 w2 = facadeFloorBaseVector + wallHeightVector; Vector3 w3 = facadeFloorBaseVector + wallVector + wallHeightVector; int wallSubmesh = facadeDesign.simpleBay.GetTexture(BuildrBay.TextureNames.WallTexture); bool flipped = facadeDesign.simpleBay.IsFlipped(BuildrBay.TextureNames.WallTexture); Vector2 wallUVStart = facadeUV; Vector2 wallUVEnd = facadeUV + new Vector2(realFadeWidth, floorHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, flipped, wallUVStart, wallUVEnd);//face if (nextFacadeDesign.hasWindows && !lastRow) { Vector3 wl2 = w2 - lastFacadeDirection*largestDepthValue; Vector3 wl3 = w3 + nextFacadeDirection*largestDepthValue; Vector2 uvEnd = new Vector2(facadeWidth, 1); AddPlane(w3, wl3, w2, wl2, wallSubmesh, flipped, wallUVStart, uvEnd);//top } } } } //Bottom of the mesh - it's mostly to ensure the model can render certain shadows correctly if (data.drawUnderside) { Vector3 foundationDrop = Vector3.down * data.foundationHeight; var newEndVerts = new Vector3[numberOfVolumePoints]; var newEndUVs = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3 + foundationDrop; newEndUVs[i] = Vector2.zero; } var tris = new List<int>(data.plan.GetTrianglesBySectorBase(v)); tris.Reverse(); int bottomSubMesh = facadeDesign.GetTexture(BuildrFacadeDesign.textureNames.columnTexture); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), bottomSubMesh); } } data = null; mesh = null; textures = null; }
public void UpdateRender(renderModes _mode) { if(data.plan==null) return; if (data.floorHeight == 0) return; if (fullMesh == null) fullMesh = new DynamicMeshGenericMultiMaterialMesh(); fullMesh.Clear(); fullMesh.subMeshCount = data.textures.Count; foreach(DynamicMeshGenericMultiMaterialMesh intMesh in interiorMeshes) { intMesh.Clear(); } switch(_mode) { case renderModes.full: if(data.oneDrawCall) { BuildrBuildingOneDrawCall.Build(fullMesh, data); } else { BuildrBuilding.Build(fullMesh, data); BuildrRoof.Build(fullMesh, data); } break; case renderModes.lowDetail: BuildrBuildingLowDetail2.Build(fullMesh, data); fullMesh.CollapseSubmeshes(); break; case renderModes.box: BuildrBuildingBox.Build(fullMesh, data); break; } fullMesh.Build(false); while (meshHolders.Count > 0) { GameObject destroyOld = meshHolders[0]; meshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } while (colliderHolders.Count > 0) { GameObject destroyOld = colliderHolders[0]; colliderHolders.RemoveAt(0); DestroyImmediate(destroyOld); } int numberOfMeshes = fullMesh.meshCount; for(int i = 0; i < numberOfMeshes; i++) { GameObject newMeshHolder = new GameObject("model " + (i + 1)); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent<MeshFilter>(); meshRend = newMeshHolder.AddComponent<MeshRenderer>(); meshFilt.mesh = fullMesh[i].mesh; meshHolders.Add(newMeshHolder); } while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } switch (_mode) { case renderModes.full: renderMode = renderModes.full; UpdateInteriors(); UpdateTextures(); UpdateDetails(); UpdateBayModels(); break; case renderModes.lowDetail: renderMode = renderModes.lowDetail; meshRend.sharedMaterials = new Material[0]; lowDetailMat.mainTexture = data.LODTextureAtlas; meshRend.sharedMaterial = lowDetailMat; UpdateDetails(); break; case renderModes.box: renderMode = renderModes.box; meshRend.sharedMaterials = new Material[0]; lowDetailMat.mainTexture = data.textures[0].texture; meshRend.sharedMaterial = lowDetailMat; UpdateDetails(); break; } #if UNITY_EDITOR EditorUtility.UnloadUnusedAssets(); #endif }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { Build(_mesh, _data, false); }
public void UpdateRender() { if (track.numberOfCurves == 0) { return; } track.RecalculateCurves(); float trackMeshRes = track.meshResolution; float bumperDistanceA = 0; float bumperDistanceB = 0; int numberOfCurves = track.numberOfCurves; bool renderTrack = track.render; float UVOffset = 0; int polyCount = 0; for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = track[i]; DynamicMeshGenericMultiMaterialMesh dynamicTrackMesh = curve.dynamicTrackMesh; DynamicMeshGenericMultiMaterialMesh dynamicBoundaryMesh = curve.dynamicBoundaryMesh; DynamicMeshGenericMultiMaterialMesh dynamicOffroadMesh = curve.dynamicOffroadMesh; DynamicMeshGenericMultiMaterialMesh dynamicBumperMesh = curve.dynamicBumperMesh; DynamicMeshGenericMultiMaterialMesh dynamicColliderMesh = curve.dynamicColliderMesh; DynamicMeshGenericMultiMaterialMesh dynamicBottomMesh = curve.dynamicBottomMesh; if (!curve.render || !renderTrack) { dynamicTrackMesh.Clear(); dynamicBoundaryMesh.Clear(); dynamicColliderMesh.Clear(); dynamicOffroadMesh.Clear(); dynamicBumperMesh.Clear(); dynamicBottomMesh.Clear(); } if (curve.shouldReRender && curve.render && renderTrack) { dynamicTrackMesh.Clear(); dynamicTrackMesh.subMeshCount = 1; dynamicBoundaryMesh.Clear(); dynamicBoundaryMesh.subMeshCount = 1; dynamicColliderMesh.Clear(); dynamicColliderMesh.subMeshCount = 1; dynamicOffroadMesh.Clear(); dynamicOffroadMesh.subMeshCount = 1; dynamicBumperMesh.Clear(); dynamicBumperMesh.subMeshCount = 1; dynamicBottomMesh.Clear(); dynamicBottomMesh.subMeshCount = 1; dynamicTrackMesh.name = "curve " + i + " track mesh"; dynamicBoundaryMesh.name = "curve " + i + " boundary mesh"; dynamicColliderMesh.name = "curve " + i + " trackCollider mesh"; dynamicOffroadMesh.name = "curve " + i + " offroad mesh"; dynamicBumperMesh.name = "curve " + i + " bumper mesh"; dynamicBottomMesh.name = "curve " + i + " bottom mesh"; bool trackTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.trackTextureStyleIndex).flipped : false; bool boundaryTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.boundaryTextureStyleIndex).flipped : false; bool bumperTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.bumperTextureStyleIndex).flipped : false; bool bottomTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.bottomTextureStyleIndex).flipped : false; int storedPointSize = curve.storedPointSize; float curveLength = curve.arcLength; //Store these points so we can use previous values when Bezier clips itself Vector3 leftPointA = curve.sampledLeftBoundaryPoints[0]; Vector3 rightPointA = curve.sampledRightBoundaryPoints[0]; Vector3 leftPointB = curve.sampledLeftBoundaryPoints[0]; Vector3 rightPointB = curve.sampledRightBoundaryPoints[0]; for (int p = 0; p < storedPointSize - 1; p++) { float tA = curve.normalisedT[p]; float tB = curve.normalisedT[p + 1]; int sampleIndexA = p; int sampleIndexB = sampleIndexA + 1; Vector3 pointA = curve.sampledPoints[sampleIndexA]; Vector3 pointB = curve.sampledPoints[sampleIndexB]; float trackWidthA = curve.sampledWidths[sampleIndexA] * 0.5f; float trackWidthB = curve.sampledWidths[sampleIndexB] * 0.5f; float trackCrownA = curve.sampledCrowns[sampleIndexA]; float trackCrownB = curve.sampledCrowns[sampleIndexB]; Vector3 trackUpA = curve.sampledTrackUps[sampleIndexA]; Vector3 trackUpB = curve.sampledTrackUps[sampleIndexB]; Vector3 trackCrossA = curve.sampledTrackCrosses[sampleIndexA]; Vector3 trackCrossB = curve.sampledTrackCrosses[sampleIndexB]; float trackAngle = curve.sampledAngles[sampleIndexA]; if (trackUpA == Vector3.zero || trackUpB == Vector3.zero) { return; } //TrackBuildRTexture texture = track.Texture(curve.trackTextureStyleIndex) ;// track.trackTexture; int pointANumber = Mathf.Max(Mathf.CeilToInt(trackWidthA / trackMeshRes / 2) * 2, 2); //number of verts along line A int pointBNumber = Mathf.Max(Mathf.CeilToInt(trackWidthB / trackMeshRes / 2) * 2, 2); //number of verts along line B int numberOfNewVerts = pointANumber + pointBNumber; Vector3[] uncrownedVerts = new Vector3[numberOfNewVerts]; if (curve.clipArrayLeft[sampleIndexA]) { leftPointA = (pointA + (trackCrossA * -trackWidthA)); } if (curve.clipArrayRight[sampleIndexA]) { rightPointA = (pointA + (trackCrossA * trackWidthA)); } float curveLengthA = (curveLength * tA) / trackWidthA + UVOffset; float curveLengthB = (curveLength * tB) / trackWidthB + UVOffset; float lerpASize = 1.0f / (pointANumber - 1); //track vertex/uv data for point nextNormIndex Vector3[] newAPoints = new Vector3[pointANumber]; Vector3[] newTrackPoints = new Vector3[pointANumber + pointBNumber]; Vector2[] newTrackUVs = new Vector2[pointANumber + pointBNumber]; for (int pa = 0; pa < pointANumber; pa++) { float lerpValue = lerpASize * pa; Vector3 crownVector = Quaternion.LookRotation(trackUpA) * new Vector3(0, 0, Mathf.Sin(lerpValue * Mathf.PI) * trackCrownA); Vector3 uncrownedVert = Vector3.Lerp(leftPointA, rightPointA, lerpValue); uncrownedVerts[pa] = uncrownedVert; Vector3 newVert = uncrownedVert + crownVector; newAPoints[pa] = newVert; newTrackPoints[pa] = newVert; Vector2 newUV = (!trackTextureFlip) ? new Vector2(lerpValue, curveLengthA) : new Vector2(curveLengthA, lerpValue); newTrackUVs[pa] = newUV; } //track vertex/uv data for point prevNormIndex if (curve.clipArrayLeft[sampleIndexB]) { leftPointB = (pointB + (trackCrossB * -trackWidthB)); } if (curve.clipArrayRight[sampleIndexB]) { rightPointB = (pointB + (trackCrossB * trackWidthB)); } float lerpBSize = 1.0f / (pointBNumber - 1); Vector3[] newBPoints = new Vector3[pointBNumber]; for (int pb = 0; pb < pointBNumber; pb++) { float lerpValue = lerpBSize * pb; Vector3 crownVector = Quaternion.LookRotation(trackUpB) * new Vector3(0, 0, Mathf.Sin(lerpValue * Mathf.PI) * trackCrownB); Vector3 uncrownedVert = Vector3.Lerp(leftPointB, rightPointB, lerpValue); uncrownedVerts[pb + pointANumber] = uncrownedVert; Vector3 newVert = uncrownedVert + crownVector; newBPoints[pb] = newVert; newTrackPoints[pb + pointANumber] = newVert; Vector2 newUV = (!trackTextureFlip) ? new Vector2(lerpValue, curveLengthB) : new Vector2(curveLengthB, lerpValue); newTrackUVs[pb + pointANumber] = newUV; } int baseTriPointA = 0; int baseTriPointB = pointANumber; int triPointA = baseTriPointA; int triPointB = baseTriPointB; int newTriPointCountA = 1; int newTriPointCountB = 1; int[] newTrackTris = new int[(numberOfNewVerts - 2) * 3]; for (int v = 0; v < numberOfNewVerts - 2; v++) { int newTriPointA = baseTriPointA + newTriPointCountA; int newTriPointB = baseTriPointB + newTriPointCountB; float newTriPointADist, newTriPointBDist; if (newTriPointA >= baseTriPointA + pointANumber) { newTriPointADist = float.PositiveInfinity; } else { newTriPointADist = Vector3.SqrMagnitude(uncrownedVerts[newTriPointA] - uncrownedVerts[baseTriPointA]); } if (newTriPointB >= baseTriPointB + pointBNumber) { newTriPointBDist = float.PositiveInfinity; } else { newTriPointBDist = Vector3.SqrMagnitude(uncrownedVerts[newTriPointB] - uncrownedVerts[baseTriPointB]); } if (newTriPointADist < newTriPointBDist) { newTrackTris[v * 3] = triPointA; newTrackTris[v * 3 + 1] = triPointB; newTrackTris[v * 3 + 2] = newTriPointA; triPointA = newTriPointA; newTriPointCountA++; } else { newTrackTris[v * 3] = triPointA; newTrackTris[v * 3 + 1] = triPointB; newTrackTris[v * 3 + 2] = newTriPointB; triPointB = newTriPointB; newTriPointCountB++; } } dynamicTrackMesh.AddData(newTrackPoints, newTrackUVs, newTrackTris, 0); dynamicColliderMesh.AddData(newTrackPoints, newTrackUVs, newTrackTris, 0); //Boundary float trackBoundaryWallHeight = curve.boundaryHeight;// track.boundaryHeight; Vector3 leftBoundaryPointA, leftBoundaryPointB, rightBoundaryPointA, rightBoundaryPointB; if (track.disconnectBoundary) { leftBoundaryPointA = curve.sampledLeftBoundaryPoints[sampleIndexA]; leftBoundaryPointB = curve.sampledLeftBoundaryPoints[sampleIndexB]; rightBoundaryPointA = curve.sampledRightBoundaryPoints[sampleIndexA]; rightBoundaryPointB = curve.sampledRightBoundaryPoints[sampleIndexB]; } else { leftBoundaryPointA = leftPointA; leftBoundaryPointB = leftPointB; rightBoundaryPointA = rightPointA; rightBoundaryPointB = rightPointB; } Vector3[] newWallVerts; Vector2[] newWallUVs; int[] newWallTris; //Boundary Render Mesh if (curve.renderBounds) { //LEFT newWallVerts = new[] { leftBoundaryPointA, leftBoundaryPointB, leftBoundaryPointA + trackUpA * trackBoundaryWallHeight, leftBoundaryPointB + trackUpB * trackBoundaryWallHeight }; if (!boundaryTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; // newWallTris = (boundaryTextureFlip) ? (new[] { 1, 0, 2, 1, 2, 3 }) : (new[] { 0,2,1,2,3,1 }); // newWallTris = (!track.renderBoundaryWallReverse) ? new[] { 1, 0, 2, 1, 2, 3 } : new[] { 1, 0, 2, 1, 2, 3, 0, 1, 2, 2, 1, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (track.renderBoundaryWallReverse) { newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; // newWallTris = (boundaryTextureFlip) ? (new[] { 0, 1, 2, 2, 1, 3 }) : (new[] { 0, 2, 1, 2, 3, 1 }); dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //RIGHT newWallVerts = (new[] { rightBoundaryPointA, rightBoundaryPointB, rightBoundaryPointA + trackUpA * trackBoundaryWallHeight, rightBoundaryPointB + trackUpB * trackBoundaryWallHeight }); //newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), }; if (!boundaryTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; //newWallTris = (!track.renderBoundaryWallReverse) ? new[] { 0, 1, 2, 2, 1, 3 } : new[] { 1, 0, 2, 1, 2, 3, 0, 1, 2, 2, 1, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (track.renderBoundaryWallReverse) { newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if (curve.trackCollider) { //COLLIDER walls for on track border float trackColliderWallHeight = track.trackColliderWallHeight; if (curve.colliderSides) { newWallVerts = (new[] { leftBoundaryPointA, leftBoundaryPointB, leftBoundaryPointA + trackUpA * trackColliderWallHeight, leftBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightBoundaryPointA, rightBoundaryPointB, rightBoundaryPointA + trackUpA * trackColliderWallHeight, rightBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //offroad bits if (track.disconnectBoundary) { Vector2 offroadTextureSize = Vector2.one; if (track.numberOfTextures > 0) { offroadTextureSize = track.Texture(curve.offroadTextureStyleIndex).textureUnitSize;// track.offroadTexture.textureUnitSize; } newWallVerts = (new[] { leftPointA, leftPointB, leftBoundaryPointA, leftBoundaryPointB }); newWallUVs = (new[] { new Vector2(leftPointA.x / offroadTextureSize.x, leftPointA.z / offroadTextureSize.y), new Vector2(leftPointB.x / offroadTextureSize.x, leftPointB.z / offroadTextureSize.y), new Vector2(leftBoundaryPointA.x / offroadTextureSize.x, leftBoundaryPointA.z / offroadTextureSize.y), new Vector2(leftBoundaryPointB.x / offroadTextureSize.x, leftBoundaryPointB.z / offroadTextureSize.y) }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicOffroadMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightPointA, rightPointB, rightBoundaryPointA, rightBoundaryPointB }); newWallUVs = (new[] { new Vector2(rightPointA.x / offroadTextureSize.x, rightPointA.z / offroadTextureSize.y), new Vector2(rightPointB.x / offroadTextureSize.x, rightPointB.z / offroadTextureSize.y), new Vector2(rightBoundaryPointA.x / offroadTextureSize.x, rightBoundaryPointA.z / offroadTextureSize.y), new Vector2(rightBoundaryPointB.x / offroadTextureSize.x, rightBoundaryPointB.z / offroadTextureSize.y) }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicOffroadMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { leftPointA, leftPointB, leftBoundaryPointA, leftBoundaryPointB }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightPointA, rightPointB, rightBoundaryPointA, rightBoundaryPointB }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } if (track.includeColliderRoof) { newWallVerts = (new[] { leftBoundaryPointA + trackUpA * trackColliderWallHeight, leftBoundaryPointB + trackUpB * trackColliderWallHeight, rightBoundaryPointA + trackUpA * trackColliderWallHeight, rightBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if ((track.trackBumpers && curve.generateBumpers) || curve.generateBumpers) { float bumperWidth = track.bumperWidth; float bumperHeight = track.bumperHeight; Vector3 bumperRaisedA = trackUpA * bumperHeight; Vector3 bumperRaisedB = trackUpB * bumperHeight; float trackAngleThreashold = track.bumperAngleThresold; //left bumpers if (trackAngle >= trackAngleThreashold) { Vector3 offroadEdgeDirectionA = (leftBoundaryPointA - leftPointA).normalized; Vector3 trackEdgeDirectionA = (newAPoints[1] - newAPoints[0]).normalized; Vector3 bumperDirectionA = (Vector3.Project(offroadEdgeDirectionA, trackUpA) - trackEdgeDirectionA).normalized; Vector3 offroadEdgeDirectionB = (leftBoundaryPointB - leftPointB).normalized; Vector3 trackEdgeDirectionB = (newBPoints[1] - newBPoints[0]).normalized; Vector3 bumperDirectionB = (Vector3.Project(offroadEdgeDirectionB, trackUpB) - trackEdgeDirectionB).normalized; float trackEdgeA = Vector3.Distance(pointA, leftPointA); float offroadEdgeA = Vector3.Distance(pointA, leftBoundaryPointA); bool offroadBumper = (trackEdgeA < (offroadEdgeA - bumperWidth)); Vector3 bumperLeft0 = (offroadBumper ? leftPointA + bumperDirectionA * bumperWidth : leftBoundaryPointA) + bumperRaisedA; Vector3 bumperLeft1 = (offroadBumper ? leftPointA : bumperLeft0 - (bumperDirectionA * bumperWidth) - bumperRaisedB);//bumperLeft0 + (trackEdgeDirectionA * bumperWidth)) - bumperRaisedB; Vector3 bumperLeft2 = (offroadBumper ? leftPointB + bumperDirectionB * bumperWidth : leftBoundaryPointB) + bumperRaisedB; Vector3 bumperLeft3 = (offroadBumper ? leftPointB : bumperLeft2 - (bumperDirectionB * bumperWidth) - bumperRaisedB); float bumperSegmentDistanceA = Vector3.Distance(bumperLeft0, bumperLeft2); float uvStartA, uvEndA; if (track.numberOfTextures > 0) { uvStartA = bumperDistanceA / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; uvEndA = (bumperDistanceA + bumperSegmentDistanceA) / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; } else { uvStartA = bumperDistanceA; // track.bumperTexture.textureUnitSize.y; uvEndA = (bumperDistanceA + bumperSegmentDistanceA); // track.bumperTexture.textureUnitSize.y; } newWallVerts = (new[] { bumperLeft0, bumperLeft1, bumperLeft2, bumperLeft3 }); if (!bumperTextureFlip) { newWallUVs = (new[] { new Vector2(uvStartA, 1), new Vector2(uvStartA, 0), new Vector2(uvEndA, 1), new Vector2(uvEndA, 0) }); } else { newWallUVs = (new[] { new Vector2(1, uvStartA), new Vector2(0, uvStartA), new Vector2(1, uvEndA), new Vector2(0, uvEndA) }); } newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicBumperMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); bumperDistanceA += bumperSegmentDistanceA; newWallVerts = (new[] { bumperLeft0, bumperLeft1, bumperLeft2, bumperLeft3 }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //Right bumpers if (trackAngle < -trackAngleThreashold) { Vector3 trackEdgeDirectionA = (newAPoints[pointANumber - 2] - newAPoints[pointANumber - 1]).normalized; Vector3 trackEdgeDirectionB = (newBPoints[pointBNumber - 2] - newBPoints[pointBNumber - 1]).normalized; Vector3 bumperRight0 = ((Vector3.Distance(pointA, rightPointA) < (Vector3.Distance(pointA, rightBoundaryPointA) - bumperWidth)) ? rightPointA : rightBoundaryPointA) + bumperRaisedA; Vector3 bumperRight1 = bumperRight0 + (trackEdgeDirectionA * bumperWidth); Vector3 bumperRight2 = ((Vector3.Distance(pointB, rightPointB) < (Vector3.Distance(pointB, rightBoundaryPointB) - bumperWidth)) ? rightPointB : rightBoundaryPointB) + bumperRaisedB; Vector3 bumperRight3 = bumperRight2 + (trackEdgeDirectionB * bumperWidth); float bumperSegmentDistanceB = Vector3.Distance(bumperRight0, bumperRight2); //float bumperSegmentDistanceA = Vector3.Distance(bumperLeft0, bumperLeft2); float uvStartB, uvEndB; if (track.numberOfTextures > 0) { uvStartB = bumperDistanceB / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; uvEndB = (bumperDistanceB + bumperSegmentDistanceB) / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; } else { uvStartB = bumperDistanceB; uvEndB = (bumperDistanceB + bumperSegmentDistanceB); } newWallVerts = (new[] { bumperRight0, bumperRight1, bumperRight2, bumperRight3 }); if (!bumperTextureFlip) { newWallUVs = (new[] { new Vector2(uvStartB, 1), new Vector2(uvStartB, 0), new Vector2(uvEndB, 1), new Vector2(uvEndB, 0) }); } else { newWallUVs = (new[] { new Vector2(1, uvStartB), new Vector2(0, uvStartB), new Vector2(1, uvEndB), new Vector2(0, uvEndB) }); } // newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); newWallTris = (new[] { 0, 1, 2, 1, 3, 2 }); dynamicBumperMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); bumperDistanceB += bumperSegmentDistanceB; } } //Track Bottom Mesh if (curve.extrudeTrack || curve.extrudeTrackBottom) { float extrusionLength = curve.extrudeLength; Vector3 extrusionA = -trackUpA * extrusionLength; Vector3 extrusionB = -trackUpB * extrusionLength; Vector3 pl0 = leftBoundaryPointA; Vector3 pl1 = leftBoundaryPointB; Vector3 pl2 = leftBoundaryPointA + extrusionA; Vector3 pl3 = leftBoundaryPointB + extrusionB; Vector3 pr0 = rightBoundaryPointA; Vector3 pr1 = rightBoundaryPointB; Vector3 pr2 = rightBoundaryPointA + extrusionA; Vector3 pr3 = rightBoundaryPointB + extrusionB; float bevelLerp = 0.5f - curve.extrudeBevel * 0.3333f; Vector3 bevelOutA = trackCrossA.normalized * (trackWidthA * 0.5f); Vector3 bevelOutB = trackCrossB.normalized * (trackWidthB * 0.5f); Vector3 pl2b = Vector3.Lerp(pl2 - bevelOutA, pr2 + bevelOutA, bevelLerp); Vector3 pl3b = Vector3.Lerp(pl3 - bevelOutB, pr3 + bevelOutB, bevelLerp); Vector3 pr2b = Vector3.Lerp(pr2 + bevelOutA, pl2 - bevelOutA, bevelLerp); Vector3 pr3b = Vector3.Lerp(pr3 + bevelOutB, pl3 - bevelOutB, bevelLerp); if (curve.extrudeTrack) { //LEFT newWallVerts = new[] { pl0, pl1, pl2b, pl3b }; if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //RIGHT newWallVerts = (new[] { pr0, pr1, pr2b, pr3b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } if (curve.extrudeCurveEnd) { //Ends if (p == 0) { newWallVerts = (new[] { pl0, pl2b, pr0, pr2b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if (p == storedPointSize - 2) { newWallVerts = (new[] { pl1, pl3b, pr1, pr3b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } } } if (curve.extrudeTrackBottom) { if (!curve.extrudeTrack) { newWallVerts = new[] { pl0, pl1, pr0, pr1 } } ; else { newWallVerts = new[] { pl2b, pl3b, pr2b, pr3b } }; if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } } if (p == storedPointSize - 2) { UVOffset = curveLengthB; } } if (curve.holder != null) { DestroyImmediate(curve.holder); } GameObject newCurveMeshHolder = new GameObject("curve " + (i + 1)); newCurveMeshHolder.transform.parent = transform; newCurveMeshHolder.transform.localPosition = Vector3.zero; curve.holder = newCurveMeshHolder; int numberOfMeshes; if (!dynamicTrackMesh.isEmpty) { dynamicTrackMesh.name = "Curve " + i + " Track Mesh"; dynamicTrackMesh.Build(); numberOfMeshes = dynamicTrackMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("model " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicTrackMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.trackTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (!dynamicBoundaryMesh.isEmpty) { dynamicBoundaryMesh.Build(); numberOfMeshes = dynamicBoundaryMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("boundary " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBoundaryMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.boundaryTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (track.disconnectBoundary && !dynamicOffroadMesh.isEmpty) { dynamicOffroadMesh.Build(); numberOfMeshes = dynamicOffroadMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("offroad " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicOffroadMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.offroadTextureStyleIndex).GetMaterial();// track.offroadTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (track.includeCollider && curve.trackCollider && !dynamicColliderMesh.isEmpty) { dynamicColliderMesh.Build(); int numberOfColliderMeshes = dynamicColliderMesh.meshCount; for (int m = 0; m < numberOfColliderMeshes; m++) { GameObject newMeshHolder = new GameObject("trackCollider " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshCollider>().sharedMesh = dynamicColliderMesh[m].mesh; } } if (track.trackBumpers && !dynamicBumperMesh.isEmpty) { dynamicBumperMesh.Build(); numberOfMeshes = dynamicBumperMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("bumper " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBumperMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.bumperTextureStyleIndex).GetMaterial();// track.bumperTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (!dynamicBottomMesh.isEmpty) { dynamicBottomMesh.Build(); numberOfMeshes = dynamicBottomMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("bottom " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBottomMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.bottomTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } } else { if (curve.holder != null && (!curve.render || !renderTrack)) { DestroyImmediate(curve.holder); } } polyCount += dynamicBottomMesh.triangleCount / 3; polyCount += dynamicBoundaryMesh.triangleCount / 3; polyCount += dynamicBumperMesh.triangleCount / 3; polyCount += dynamicOffroadMesh.triangleCount / 3; polyCount += dynamicTrackMesh.triangleCount / 3; } track.TrackRendered(); track.lastPolycount = polyCount; #if UNITY_EDITOR EditorUtility.UnloadUnusedAssets(); #endif }
public void UpdateRender(renderModes _mode) { if (data.plan == null) { return; } if (data.floorHeight == 0) { return; } if (fullMesh == null) { fullMesh = new DynamicMeshGenericMultiMaterialMesh(); } fullMesh.Clear(); fullMesh.subMeshCount = data.textures.Count; foreach (DynamicMeshGenericMultiMaterialMesh intMesh in interiorMeshes) { intMesh.Clear(); } switch (_mode) { case renderModes.full: BuildrBuilding.Build(fullMesh, data); BuildrRoof.Build(fullMesh, data); break; case renderModes.lowDetail: BuildrBuildingLowDetail2.Build(fullMesh, data); fullMesh.CollapseSubmeshes(); break; case renderModes.box: BuildrBuildingBox.Build(fullMesh, data); break; } fullMesh.Build(false); while (meshHolders.Count > 0) { GameObject destroyOld = meshHolders[0]; meshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } int numberOfMeshes = fullMesh.meshCount; for (int i = 0; i < numberOfMeshes; i++) { GameObject newMeshHolder = new GameObject("model " + (i + 1)); newMeshHolder.transform.parent = transform; newMeshHolder.transform.localPosition = Vector3.zero; meshFilt = newMeshHolder.AddComponent <MeshFilter>(); meshRend = newMeshHolder.AddComponent <MeshRenderer>(); meshFilt.mesh = fullMesh[i].mesh; meshHolders.Add(newMeshHolder); } while (interiorMeshHolders.Count > 0) { GameObject destroyOld = interiorMeshHolders[0]; interiorMeshHolders.RemoveAt(0); DestroyImmediate(destroyOld); } switch (_mode) { case renderModes.full: UpdateInteriors(); UpdateTextures(); break; case renderModes.lowDetail: meshRend.sharedMaterials = new Material[0]; lowDetailMat.mainTexture = data.LODTextureAtlas; meshRend.sharedMaterial = lowDetailMat; break; case renderModes.box: meshRend.sharedMaterials = new Material[0]; lowDetailMat.mainTexture = data.textures[0].texture; meshRend.sharedMaterial = lowDetailMat; break; } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { // timestart = Time.realtimeSinceStartup; data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrPlan plan = data.plan; // int facadeIndex = 0; numberOfFacades = 0; int numberOfVolumes = data.plan.numberOfVolumes; //define rectangles that represent the facades packedTexturePositions.Clear(); for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) continue; int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector2z p0 = plan.points[volume.points[indexA]]; Vector2z p1 = plan.points[volume.points[indexB]]; float facadeWidth = Vector2z.Distance(p0, p1) * PIXELS_PER_METER; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1)//no facade - adjacent facade is taller and covers this one continue; float floorHeight = data.floorHeight; float facadeHeight = (volume.numberOfFloors - floorBase) * floorHeight * PIXELS_PER_METER; if (facadeHeight < 0) { facadeWidth = 0; facadeHeight = 0; } Rect newFacadeRect = new Rect(0, 0, facadeWidth, facadeHeight); packedTexturePositions.Add(newFacadeRect); numberOfFacades++; } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.name = "Roof Mesh"; dynMeshRoof.subMeshCount = textures.Length; BuildrRoof.Build(dynMeshRoof, data, true); dynMeshRoof.CheckMaxTextureUVs(data); roofTextures.Clear(); roofTextureIndex.Clear(); foreach (BuildrRoofDesign roofDesign in data.roofs) { foreach (int textureIndex in roofDesign.textureValues) { if (!roofTextureIndex.Contains(textureIndex)) { BuildrTexture bTexture = data.textures[textureIndex]; Vector2 largestSubmeshPlaneSize = new Vector2(1, 1); Vector2 minWorldUvSize = dynMeshRoof.MinWorldUvSize(textureIndex); Vector2 maxWorldUvSize = dynMeshRoof.MaxWorldUvSize(textureIndex); largestSubmeshPlaneSize.x = maxWorldUvSize.x - minWorldUvSize.x; largestSubmeshPlaneSize.y = maxWorldUvSize.y - minWorldUvSize.y; int roofTextureWidth = Mathf.RoundToInt(largestSubmeshPlaneSize.x * PIXELS_PER_METER); int roofTextureHeight = Mathf.RoundToInt(largestSubmeshPlaneSize.y * PIXELS_PER_METER); Rect newRoofTexutureRect = new Rect(0, 0, roofTextureWidth, roofTextureHeight); packedTexturePositions.Add(newRoofTexutureRect); roofTextures.Add(bTexture); roofTextureIndex.Add(textureIndex); } } } //run a custom packer to define their postions textureWidth = RectanglePack.Pack(packedTexturePositions, ATLAS_PADDING); //determine the resize scale and apply that to the rects packedScale = 1; int numberOfRects = packedTexturePositions.Count; if (textureWidth > MAXIMUM_TEXTURESIZE) { packedScale = MAXIMUM_TEXTURESIZE / (float)textureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; //Debug.Log("Rects "+roofTextures[i-+packedTexturePositions[i]); } textureWidth = Mathf.RoundToInt(packedScale * textureWidth); } else { textureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(textureWidth - 1, 2)) + 1));//find the next power of two } textureSize = textureWidth * textureWidth; colourArray = new Color32[textureSize]; //TestRectColours();//this test paints all the facades with rainbow colours - real pretty BuildTextures(); Texture2D packedTexture = new Texture2D(textureWidth, textureWidth, TextureFormat.ARGB32, true); packedTexture.filterMode = FilterMode.Bilinear; packedTexture.SetPixels32(colourArray); packedTexture.Apply(true, false); if (data.OneDrawCallTexture != null) Object.DestroyImmediate(data.OneDrawCallTexture); data.OneDrawCallTexture = packedTexture; data.OneDrawCallTexture.name = "One Draw Call Texture"; int numberOfRoofTextures = roofTextures.Count-1; List<Rect> facadeTexturePositions = new List<Rect>(packedTexturePositions); Debug.Log(numberOfRoofTextures); facadeTexturePositions.RemoveRange(packedTexturePositions.Count - numberOfRoofTextures, numberOfRoofTextures); BuildrBuilding.Build(mesh, data, facadeTexturePositions.ToArray()); data = null; mesh = null; textures = null; System.GC.Collect(); }
private static void BuildSimple(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { BuildrData data = _data; DynamicMeshGenericMultiMaterialMesh mesh = _mesh; BuildrPlan plan = data.plan; int facadeIndex = 0; int numberOfVolumes = data.plan.numberOfVolumes; //Build Floor if (data.drawUnderside) { for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3; newEndUVs[i] = Vector2.zero; } List<int> tris = new List<int>(data.plan.GetTrianglesBySectorBase(s)); tris.Reverse(); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.subMeshCount = data.textures.Count; BuildrRoof.Build(dynMeshRoof, data, true); mesh.AddData(dynMeshRoof.vertices, dynMeshRoof.uv, dynMeshRoof.triangles, 0); Vector3 foundationVector = Vector3.down * data.foundationHeight; //Build facades for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int l = 0; l < numberOfVolumePoints; l++) { int indexA = l; int indexB = (l < numberOfVolumePoints - 1) ? l + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(s, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; if(floorBase == 0) { w0 += foundationVector; w1 += foundationVector; } mesh.AddPlane(w0, w1, w2, w3, Vector2.zero, Vector2.zero, 0); facadeIndex++; } } data = null; mesh = null; }
//TODO: functions to find out minimum footprint of stairwell for checking against cores? public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, int volumeIndex, StairModes stairMode, bool zeroMesh) { data = _data; mesh = _mesh; mesh.name = "Stairs Mesh Volume " + volumeIndex; textures = data.textures.ToArray(); // BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; BuildrVolume volume = plan.volumes[volumeIndex]; float floorHeight = data.floorHeight; // Vector3 floorHeightVector = Vector3.up * floorHeight; if(!volume.generateStairs) return; //Calculate the internal floor plan points int numberOfVolumePoints = volume.points.Count; Vector2z[] volumePoints = new Vector2z[numberOfVolumePoints]; for(int i = 0; i < numberOfVolumePoints; i++) volumePoints[i] = plan.points[volume.points[i]]; List<Rect> volumeCores = new List<Rect>(); // List<int> linkedPoints = new List<int>(); foreach (Rect core in plan.cores) { Vector2z coreCenter = new Vector2z(core.center); if (BuildrUtils.PointInsidePoly(coreCenter, volumePoints)) volumeCores.Add(core); } int numberOfVolumeCores = volumeCores.Count; int numberOfFloors = volume.numberOfFloors + volume.numberOfBasementFloors; float basementBaseHeight = (volume.numberOfBasementFloors) * floorHeight;//plus one for the initial floor float staircaseWidth = volume.staircaseWidth; float stairwellWidth = staircaseWidth * 2.5f; float stairwellDepth = staircaseWidth * 2 + Mathf.Sqrt(floorHeight+floorHeight); float staircaseThickness = Mathf.Sqrt(volume.stepHeight * volume.stepHeight + volume.stepHeight * volume.stepHeight); Vector3 flightVector = floorHeight * Vector3.up; Vector3 staircaseWidthVector = staircaseWidth * Vector3.right; Vector3 staircaseDepthVector = stairwellDepth * 0.5f * Vector3.forward; Vector3 stairHeightVector = staircaseThickness * Vector3.up; Vector3 landingDepthVector = staircaseWidth * Vector3.forward; //Texture submeshes int floorSubmesh = volume.stairwellFloorTexture; int stepSubmesh = volume.stairwellStepTexture; int wallSubmesh = volume.stairwellWallTexture; int ceilingSubmesh = volume.stairwellCeilingTexture; volume.stairBaseVector.Clear(); for(int c = 0; c < numberOfVolumeCores; c++) { Rect coreBounds = volumeCores[c]; Vector3 stairBaseVector = new Vector3(-stairwellWidth / 2, 0, -stairwellDepth/2); Vector3 stairPosition = new Vector3(coreBounds.xMin, -basementBaseHeight, coreBounds.yMin) - stairBaseVector; for(int f = 0; f < numberOfFloors; f++) { Vector3 flightBaseVector = stairBaseVector + (flightVector * f); if(!zeroMesh) flightBaseVector += stairPosition; Vector3 landingStart0 = flightBaseVector; Vector3 landingStart1 = landingStart0 + staircaseWidthVector*2.5f; Vector3 landingStart2 = landingStart0 + landingDepthVector; Vector3 landingStart3 = landingStart1 + landingDepthVector; Vector3 landingStart4 = landingStart0 - stairHeightVector; Vector3 landingStart5 = landingStart1 - stairHeightVector; Vector3 landingStart6 = landingStart2 - stairHeightVector; Vector3 landingStart7 = landingStart3 - stairHeightVector; if(f > 0) { AddPlane(landingStart1, landingStart0, landingStart3, landingStart2, floorSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth));//top AddPlane(landingStart4, landingStart5, landingStart6, landingStart7, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth));//bottom AddPlane(landingStart0, landingStart1, landingStart4, landingStart5, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness));//frontside AddPlane(landingStart3, landingStart2, landingStart7, landingStart6, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness));//backside AddPlane(landingStart0, landingStart4, landingStart2, landingStart6, wallSubmesh, false, Vector2.zero, new Vector2(staircaseThickness, staircaseWidth));//sideleft AddPlane(landingStart5, landingStart1, landingStart7, landingStart3, wallSubmesh, false, Vector2.zero, new Vector2(staircaseThickness, staircaseWidth));//sideright } if(f < numberOfFloors - 1) { Vector3 bottom0 = landingStart2; Vector3 bottom1 = landingStart2 + staircaseWidthVector; Vector3 bottom2 = bottom0 - stairHeightVector; Vector3 bottom3 = bottom1 - stairHeightVector; Vector3 top0 = bottom0 + (flightVector * 0.5f) + staircaseDepthVector; Vector3 top1 = bottom1 + (flightVector * 0.5f) + staircaseDepthVector; Vector3 top2 = top0 - stairHeightVector; Vector3 top3 = top1 - stairHeightVector; Vector3 bottomB0 = top1 + Vector3.right * staircaseWidth*0.5f; Vector3 bottomB1 = bottomB0 + staircaseWidthVector; Vector3 bottomB2 = bottomB0 - stairHeightVector; Vector3 bottomB3 = bottomB1 - stairHeightVector; Vector3 topB0 = bottomB0 + (flightVector * 0.5f) - staircaseDepthVector; Vector3 topB1 = bottomB1 + (flightVector * 0.5f) - staircaseDepthVector; Vector3 topB2 = topB0 - stairHeightVector; Vector3 topB3 = topB1 - stairHeightVector; float stairHypontenuse = Vector3.Distance(bottom0, top0); int numberOfSteps = Mathf.CeilToInt((floorHeight / 2.0f) / volume.stepHeight); switch(stairMode) { case StairModes.Flat: //flight A AddPlane(bottom1, bottom0, top1, top0, stepSubmesh, false, Vector2.zero, new Vector2(1, numberOfSteps));//step face AddPlane(bottom3, bottom1, top3, top1, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, stairHypontenuse));//underside AddPlane(bottom0, bottom2, top0, top2, wallSubmesh, false, new Vector2(bottom2.z, bottom2.y), new Vector2(top0.z, top0.y));//left side AddPlane(bottom2, bottom3, top2, top3, wallSubmesh, false, new Vector2(bottom3.z, bottom3.y), new Vector2(top2.z, top2.y));//right side //flight B AddPlane(bottomB0, bottomB1, topB0, topB1, stepSubmesh, false, Vector2.zero, new Vector2(1, numberOfSteps));//step face AddPlane(bottomB1, bottomB3, topB1, topB3, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, stairHypontenuse));//underside AddPlane(bottomB2, bottomB0, topB2, topB0, wallSubmesh, false, Vector2.zero, Vector2.one);//left side AddPlane(bottomB3, bottomB2, topB3, topB2, wallSubmesh, false, Vector2.zero, Vector2.one);//right side break; case StairModes.Stepped: float stepHypontenuse = stairHypontenuse / numberOfSteps; float stairAngle = Mathf.Atan2(floorHeight, stairwellDepth); float stepDepth = Mathf.Cos(stairAngle) * stepHypontenuse; float skipStep = (stepDepth / (numberOfSteps - 1)); stepDepth += skipStep; float stepRiser = Mathf.Sin(stairAngle) * stepHypontenuse; //flight one float lerpIncrement = 1.0f / numberOfSteps; float lerpIncrementB = 1.0f / (numberOfSteps-1); for (int s = 0; s < numberOfSteps-1; s++) { float lerpValue = lerpIncrement * s; Vector3 skipStepVector = Vector3.forward * (skipStep * s); Vector3 s0 = Vector3.Lerp(bottom1, top1, lerpValue) + skipStepVector; Vector3 s1 = Vector3.Lerp(bottom0, top0, lerpValue) + skipStepVector; Vector3 s2 = s0 + Vector3.up * stepRiser; Vector3 s3 = s1 + Vector3.up * stepRiser; Vector3 s4 = s2 + Vector3.forward * stepDepth; Vector3 s5 = s3 + Vector3.forward * stepDepth; AddPlane(s0, s1, s2, s3, wallSubmesh, false, Vector2.zero, new Vector2(1,staircaseWidth)); AddPlane(s2, s3, s4, s5, stepSubmesh, false, Vector2.zero, new Vector2(1,staircaseWidth)); //sides float lerpValueB = lerpIncrementB * s; Vector3 s6 = Vector3.Lerp(bottom3, top3, lerpValueB); Vector3 s7 = Vector3.Lerp(bottom3, top3, lerpValueB + lerpIncrementB); AddPlane(s2, s4, s6, s7, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth,staircaseThickness)); Vector3 s8 = Vector3.Lerp(bottom2, top2, lerpValueB); Vector3 s9 = Vector3.Lerp(bottom2, top2, lerpValueB + lerpIncrementB); AddPlane(s5, s3, s9, s8, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth,staircaseThickness)); } AddPlane(bottom2, bottom3, top2, top3, ceilingSubmesh, false, Vector2.zero, Vector2.one); //flight two for(int s = 0; s < numberOfSteps-1; s++) { float lerpValue = lerpIncrement * s; Vector3 skipStepVector = -Vector3.forward * (skipStep * s); Vector3 s0 = Vector3.Lerp(bottomB0, topB0, lerpValue) + skipStepVector; Vector3 s1 = Vector3.Lerp(bottomB1, topB1, lerpValue) + skipStepVector; Vector3 s2 = s0 + Vector3.up * stepRiser; Vector3 s3 = s1 + Vector3.up * stepRiser; Vector3 s4 = s2 - Vector3.forward * stepDepth; Vector3 s5 = s3 - Vector3.forward * stepDepth; AddPlane(s0, s1, s2, s3, wallSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); AddPlane(s2, s3, s4, s5, stepSubmesh, false, Vector2.zero, new Vector2(1, staircaseWidth)); float lerpValueB = lerpIncrementB * s; //sides Vector3 s6 = Vector3.Lerp(bottomB2, topB2, lerpValueB); Vector3 s7 = Vector3.Lerp(bottomB2, topB2, lerpValueB + lerpIncrementB); AddPlane(s2, s4, s6, s7, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); Vector3 s8 = Vector3.Lerp(bottomB3, topB3, lerpValueB); Vector3 s9 = Vector3.Lerp(bottomB3, topB3, lerpValueB + lerpIncrementB); AddPlane(s5, s3, s9, s8, wallSubmesh, false, Vector2.zero, new Vector2(stepDepth, staircaseThickness)); } AddPlane(bottomB3, bottomB2, topB3, topB2, ceilingSubmesh, false, Vector2.zero, Vector2.one); break; } Vector3 landingEnd0 = top0 + landingDepthVector; Vector3 landingEnd1 = bottomB1 + landingDepthVector; Vector3 landingEnd2 = landingEnd0 - stairHeightVector; Vector3 landingEnd3 = landingEnd1 - stairHeightVector; Vector3 landingEnd4 = top0 - stairHeightVector; Vector3 landingEnd5 = bottomB1 - stairHeightVector; AddPlane(bottomB1, top0, landingEnd1, landingEnd0, floorSubmesh, false, Vector2.zero, new Vector2(staircaseWidth*2.5f, staircaseWidth));//top AddPlane(landingEnd4, landingEnd5, landingEnd2, landingEnd3, ceilingSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseWidth));//bottom AddPlane(top0, bottomB1, landingEnd4, landingEnd5, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness));//frontside AddPlane(landingEnd1, landingEnd0, landingEnd3, landingEnd2, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth * 2.5f, staircaseThickness));//backside AddPlane(landingEnd0, top0, landingEnd2, landingEnd4, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, staircaseThickness));//sideleft AddPlane(bottomB1, landingEnd1, landingEnd5, landingEnd3, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth, staircaseThickness));//sideright } } //Center wall float coreHeight = (numberOfFloors * floorHeight); Vector3 coreHeightVector = Vector3.up * coreHeight; Vector3 corePosition = (zeroMesh) ? Vector3.zero : stairPosition; Vector3 w0 = new Vector3(-staircaseWidth / 4.0f, 0, -(stairwellDepth - (staircaseWidth * 2)) / 2.0f) + corePosition; Vector3 w1 = w0 + Vector3.right * staircaseWidth/2; Vector3 w2 = w0 + staircaseDepthVector; Vector3 w3 = w1 + staircaseDepthVector; Vector3 w4 = w0 + coreHeightVector; Vector3 w5 = w1 + coreHeightVector; Vector3 w6 = w2 + coreHeightVector; Vector3 w7 = w3 + coreHeightVector; AddPlane(w1, w0, w5, w4, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth / 2, coreHeight));// AddPlane(w3, w1, w7, w5, wallSubmesh, false, Vector2.zero, new Vector2(stairwellDepth / 2, coreHeight));// AddPlane(w2, w3, w6, w7, wallSubmesh, false, Vector2.zero, new Vector2(staircaseWidth / 2, coreHeight));// AddPlane(w0, w2, w4, w6, wallSubmesh, false, Vector2.zero, new Vector2(stairwellDepth / 2, coreHeight));// int it = 100; while(volume.stairBaseVector.Count < mesh.meshCount) { if (zeroMesh) volume.stairBaseVector.Add(stairPosition); else volume.stairBaseVector.Add(Vector3.zero); it--; if(it == 0) break; } if(c<numberOfVolumeCores-1) mesh.ForceNewMesh(); } }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { // timestart = Time.realtimeSinceStartup; data = _data; mesh = _mesh; textures = data.textures.ToArray(); BuildrPlan plan = data.plan; int facadeIndex = 0; numberOfFacades = 0; int numberOfVolumes = data.plan.numberOfVolumes; LogTimer("Start"); //define rectangles that represent the facades packedTexturePositions.Clear(); for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume volume = plan.volumes[v]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) { continue; } int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector2z p0 = plan.points[volume.points[indexA]]; Vector2z p1 = plan.points[volume.points[indexB]]; float facadeWidth = Vector2z.Distance(p0, p1) * PIXELS_PER_METER; int floorBase = plan.GetFacadeFloorHeight(v, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1)//no facade - adjacent facade is taller and covers this one { continue; } float floorHeight = data.floorHeight; float facadeHeight = (volume.numberOfFloors - floorBase) * floorHeight * PIXELS_PER_METER; if (facadeHeight < 0) { facadeWidth = 0; facadeHeight = 0; } Rect newFacadeRect = new Rect(0, 0, facadeWidth, facadeHeight); packedTexturePositions.Add(newFacadeRect); numberOfFacades++; } } //Build ROOF DynamicMeshGenericMultiMaterialMesh dynMeshRoof = new DynamicMeshGenericMultiMaterialMesh(); dynMeshRoof.name = "Roof Mesh"; dynMeshRoof.subMeshCount = textures.Length; BuildrRoof.Build(dynMeshRoof, data, true); dynMeshRoof.CheckMaxTextureUVs(data); LogTimer("Roof A"); roofTextures.Clear(); roofTextureIndex.Clear(); foreach (BuildrRoofDesign roofDesign in data.roofs) { foreach (int textureIndex in roofDesign.textureValues) { if (!roofTextureIndex.Contains(textureIndex)) { BuildrTexture bTexture = data.textures[textureIndex]; Vector2 largestSubmeshPlaneSize = new Vector2(1, 1); Vector2 minWorldUvSize = dynMeshRoof.MinWorldUvSize(textureIndex); Vector2 maxWorldUvSize = dynMeshRoof.MaxWorldUvSize(textureIndex); largestSubmeshPlaneSize.x = maxWorldUvSize.x - minWorldUvSize.x; largestSubmeshPlaneSize.y = maxWorldUvSize.y - minWorldUvSize.y; int roofTextureWidth = Mathf.RoundToInt(largestSubmeshPlaneSize.x * PIXELS_PER_METER); int roofTextureHeight = Mathf.RoundToInt(largestSubmeshPlaneSize.y * PIXELS_PER_METER); Rect newRoofTexutureRect = new Rect(0, 0, roofTextureWidth, roofTextureHeight); packedTexturePositions.Add(newRoofTexutureRect); roofTextures.Add(bTexture); roofTextureIndex.Add(textureIndex); } } } //run a custom packer to define their postions textureWidth = RectanglePack.Pack(packedTexturePositions, ATLAS_PADDING); //determine the resize scale and apply that to the rects packedScale = 1; int numberOfRects = packedTexturePositions.Count; if (textureWidth > MAXIMUM_TEXTURESIZE) { packedScale = MAXIMUM_TEXTURESIZE / (float)textureWidth; for (int i = 0; i < numberOfRects; i++) { Rect thisRect = packedTexturePositions[i]; thisRect.x *= packedScale; thisRect.y *= packedScale; thisRect.width *= packedScale; thisRect.height *= packedScale; packedTexturePositions[i] = thisRect; //Debug.Log("Rects "+roofTextures[i-+packedTexturePositions[i]); } textureWidth = Mathf.RoundToInt(packedScale * textureWidth); } else { textureWidth = (int)Mathf.Pow(2, (Mathf.FloorToInt(Mathf.Log(textureWidth - 1, 2)) + 1));//find the next power of two } //Debug.Log("Texture Width "+textureWidth); //TODO: maybe restrict the resize to a power of two? LogTimer("Packed Rect Generated"); textureSize = textureWidth * textureWidth; colourArray = new Color32[textureSize]; //TestRectColours();//this test paints all the facades with rainbow colours - real pretty BuildTextures(); LogTimer("texture created"); Texture2D packedTexture = new Texture2D(textureWidth, textureWidth, TextureFormat.ARGB32, true); packedTexture.filterMode = FilterMode.Bilinear; packedTexture.SetPixels32(colourArray); packedTexture.Apply(true, false); LogTimer("apply"); if (data.LODTextureAtlas != null) { Object.DestroyImmediate(data.LODTextureAtlas); } data.LODTextureAtlas = packedTexture; data.LODTextureAtlas.name = "Low Detail Texture"; //build the model with new uvs if (data.drawUnderside) { for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; Vector3[] newEndVerts = new Vector3[numberOfVolumePoints]; Vector2[] newEndUVs = new Vector2[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { newEndVerts[i] = plan.points[volume.points[i]].vector3; newEndUVs[i] = Vector2.zero; } List <int> tris = new List <int>(data.plan.GetTrianglesBySectorBase(s)); tris.Reverse(); mesh.AddData(newEndVerts, newEndUVs, tris.ToArray(), 0); } } LogTimer("Floor"); //Build facades for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = plan.volumes[s]; int numberOfVolumePoints = volume.points.Count; for (int f = 0; f < numberOfVolumePoints; f++) { if (!volume.renderFacade[f]) { continue; } int indexA = f; int indexB = (f < numberOfVolumePoints - 1) ? f + 1 : 0; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; int floorBase = plan.GetFacadeFloorHeight(s, volume.points[indexA], volume.points[indexB]); int numberOfFloors = volume.numberOfFloors - floorBase; if (numberOfFloors < 1) { //no facade - adjacent facade is taller and covers this one continue; } float floorHeight = data.floorHeight; Vector3 floorHeightStart = Vector3.up * (floorBase * floorHeight); Vector3 wallHeight = Vector3.up * (volume.numberOfFloors * floorHeight) - floorHeightStart; p0 += floorHeightStart; p1 += floorHeightStart; Vector3 w0 = p0; Vector3 w1 = p1; Vector3 w2 = w0 + wallHeight; Vector3 w3 = w1 + wallHeight; Rect facadeRect = packedTexturePositions[facadeIndex]; float imageSize = textureWidth; Vector2 uvMin = new Vector2(facadeRect.xMin / imageSize, facadeRect.yMin / imageSize); Vector2 uvMax = new Vector2(facadeRect.xMax / imageSize, facadeRect.yMax / imageSize); mesh.AddPlane(w0, w1, w2, w3, uvMin, uvMax, 0); facadeIndex++; } } LogTimer("Facades"); //ROOF Textures int roofRectBase = numberOfFacades; List <Rect> newAtlasRects = new List <Rect>(); for (int i = roofRectBase; i < packedTexturePositions.Count; i++) { Rect uvRect = new Rect();//generate a UV based rectangle off the packed one uvRect.x = packedTexturePositions[i].x / textureWidth; uvRect.y = packedTexturePositions[i].y / textureWidth; uvRect.width = packedTexturePositions[i].width / textureWidth; uvRect.height = packedTexturePositions[i].height / textureWidth; newAtlasRects.Add(uvRect); } dynMeshRoof.Atlas(roofTextureIndex.ToArray(), newAtlasRects.ToArray(), data.textures.ToArray()); //Add the atlased mesh data to the main model data at submesh 0 mesh.AddData(dynMeshRoof.vertices, dynMeshRoof.uv, dynMeshRoof.triangles, 0); LogTimer("Roof B"); data = null; mesh = null; textures = null; //atlasRects = null; LogTimer("Done"); System.GC.Collect(); }
public static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data, int volumeIndex) { data = _data; mesh = _mesh; mesh.name = "Interior Mesh Volume " + volumeIndex; textures = data.textures.ToArray(); if (!data.renderInteriors) { return; } float largestDepthValue = 0;//deepest value of a bay design in the building float tallestBay = 0; foreach (BuildrBay bay in data.bays) { largestDepthValue = Mathf.Max(largestDepthValue, bay.deepestValue);//get the deepest value tallestBay = Mathf.Max(tallestBay, bay.openingHeight + (data.floorHeight - bay.openingHeight) * bay.openingHeightRatio); } foreach (BuildrFacadeDesign facade in data.facades) { if (facade.type != BuildrFacadeDesign.types.simple) { continue; } largestDepthValue = Mathf.Max(largestDepthValue, facade.simpleBay.deepestValue);//get the deepest value if (facade.simpleBay.isOpening) { tallestBay = Mathf.Max(tallestBay, facade.simpleBay.openingHeight + (data.floorHeight - facade.simpleBay.openingHeight) * facade.simpleBay.openingHeightRatio); } } BuildrFacadeDesign facadeDesign = data.facades[0]; BuildrPlan plan = data.plan; BuildrVolume volume = plan.volumes[volumeIndex]; int numberOfFloors = volume.numberOfFloors; float floorHeight = data.floorHeight; Vector3 floorHeightVector = Vector3.up * floorHeight; float ceilingHeight = tallestBay + (floorHeight - tallestBay) * data.interiorCeilingHeight; //Calculate the internal floor plan points int numberOfVolumePoints = volume.points.Count; Vector2z[] interiorVolumePoints = new Vector2z[numberOfVolumePoints]; for (int i = 0; i < numberOfVolumePoints; i++) { Vector3 lastPoint = plan.points[volume.points[(i > 0) ? i - 1 : numberOfVolumePoints - 1]].vector3; Vector3 thisPoint = plan.points[volume.points[i]].vector3; Vector3 nextPoint = plan.points[volume.points[(i + 1) % numberOfVolumePoints]].vector3; Vector3 normalA = Vector3.Cross(thisPoint - lastPoint, Vector3.up).normalized; Vector3 normalB = Vector3.Cross(nextPoint - thisPoint, Vector3.up).normalized; Vector2z facadeALine = new Vector2z(thisPoint - lastPoint); Vector2z facadeBLine = new Vector2z(thisPoint - nextPoint); //Calculate facade inner origins for floors Vector3 facadeOriginV3A = lastPoint + normalA * largestDepthValue; Vector3 facadeOriginV3B = nextPoint + normalB * largestDepthValue; Vector2z facadeOriginA = new Vector2z(facadeOriginV3A); Vector2z facadeOriginB = new Vector2z(facadeOriginV3B); Vector2z facadeLineIntersection = BuildrUtils.FindIntersection(facadeALine, facadeOriginA, facadeBLine, facadeOriginB); interiorVolumePoints[i] = facadeLineIntersection; } List <Vector2z> interiorVolumePointList = new List <Vector2z>(interiorVolumePoints); List <Rect> volumeCores = new List <Rect>(); List <int> linkedPoints = new List <int>(); foreach (Rect core in plan.cores) { Vector2z coreCenter = new Vector2z(core.center); if (BuildrUtils.PointInsidePoly(coreCenter, interiorVolumePoints)) { volumeCores.Add(core); } } int numberOfVolumeCores = volumeCores.Count; bool print = plan.volumes.IndexOf(volume) == 3; for (int c = 0; c < numberOfVolumeCores; c++) { int numberOfInteriorPoints = interiorVolumePointList.Count; Rect coreBounds = volumeCores[c]; Vector2z coreCenter = new Vector2z(coreBounds.center); Vector2z coreBL = new Vector2z(coreBounds.xMin, coreBounds.yMin); Vector2z coreBR = new Vector2z(coreBounds.xMax, coreBounds.yMin); Vector2z coreTL = new Vector2z(coreBounds.xMin, coreBounds.yMax); Vector2z coreTR = new Vector2z(coreBounds.xMax, coreBounds.yMax); Vector2z[] corePointArray; corePointArray = new[] { coreBL, coreBR, coreTR, coreTL }; //Find the nearest legal cut we can make to join the core and interior point poly int connectingPoint = -1; float connectingPointDistance = Mathf.Infinity; for (int p = 0; p < numberOfInteriorPoints; p++) { if (linkedPoints.Contains(p)) { continue; } Vector2z thisPoint = interiorVolumePointList[p]; float thisPointDistance = Vector2z.SqrMag(thisPoint, coreCenter); if (thisPointDistance < connectingPointDistance) { bool legalCut = true; for (int pc = 0; pc < numberOfInteriorPoints; pc++) { Vector2z p0 = interiorVolumePointList[pc]; Vector2z p1 = interiorVolumePointList[(pc + 1) % numberOfInteriorPoints]; if (BuildrUtils.FastLineIntersection(coreCenter, thisPoint, p0, p1))//check against all lines that this new cut doesn't intersect { if (print) { Debug.Log("FLI " + pc + " " + coreCenter + " " + thisPoint + " " + p0 + " " + p1); } legalCut = false; break; } } if (legalCut) { connectingPoint = p; connectingPointDistance = thisPointDistance; } } } if (connectingPoint == -1) { Debug.Log("Buildr Could not place core"); continue; } Vector2z chosenPoint = interiorVolumePointList[connectingPoint]; int connectingCorePoint = 0; float connectingCorePointDistance = Mathf.Infinity; // Vector2z.SqrMag(corePointArray[0], chosenPoint); for (int cp = 0; cp < 4; cp++) //find the core point to make the cut { float thisCorePointDistance = Vector2z.SqrMag(corePointArray[cp], chosenPoint); if (thisCorePointDistance < connectingCorePointDistance) { connectingCorePoint = cp; connectingCorePointDistance = thisCorePointDistance; } } interiorVolumePointList.Insert(connectingPoint, chosenPoint); //loop back on the floorplan to close it for (int acp = 0; acp < 5; acp++) //loop back on itself to close the core { interiorVolumePointList.Insert(connectingPoint + 1, corePointArray[(connectingCorePoint + acp) % 4]); } for (int i = 0; i < linkedPoints.Count; i++) { if (linkedPoints[i] > connectingPoint) { linkedPoints[i] += 7; } } linkedPoints.AddRange(new[] { connectingPoint, connectingPoint + 1, connectingPoint + 2, connectingPoint + 3, connectingPoint + 4, connectingPoint + 5, connectingPoint + 6 }); // linkedPoints.AddRange(new []{connectingPoint,connectingPoint+6}); } // if(linkedPoints.Count > 0) // Debug.Log(linkedPoints.Count+" "+linkedPoints[0]); Vector2z[] interiorPointListCore = interiorVolumePointList.ToArray(); for (int f = 0; f < numberOfVolumePoints; f++) { ///WALLS int indexAM = Mathf.Abs((f - 1) % numberOfVolumePoints); int indexA = f; int indexB = (f + 1) % numberOfVolumePoints; int indexBP = (f + 2) % numberOfVolumePoints; Vector3 p0m = plan.points[volume.points[indexAM]].vector3; Vector3 p0 = plan.points[volume.points[indexA]].vector3; Vector3 p1 = plan.points[volume.points[indexB]].vector3; Vector3 p1p = plan.points[volume.points[indexBP]].vector3; Vector3 p0interior = interiorVolumePoints[indexA].vector3; Vector3 p1interior = interiorVolumePoints[indexB].vector3; float facadeWidth = Vector3.Distance(p0, p1) - largestDepthValue * 2.0f; Vector3 facadeDirection = (p1 - p0).normalized; Vector3 facadeCross = Vector3.Cross(facadeDirection, Vector3.up); Vector3 lastFacadeDirection = (p0 - p0m).normalized; Vector3 nextFacadeDirection = (p1p - p1).normalized; //only bother with facade directions when facade may intersect inverted geometry float facadeDirDotL = Vector3.Dot(-facadeDirection, lastFacadeDirection); float facadeCrossDotL = Vector3.Dot(-facadeCross, lastFacadeDirection); if (facadeDirDotL <= 0 || facadeCrossDotL <= 0) { lastFacadeDirection = -facadeCross; } float facadeDirDotN = Vector3.Dot(-facadeDirection, nextFacadeDirection); float facadeCrossDotN = Vector3.Dot(-facadeCross, nextFacadeDirection); if (facadeDirDotN <= 0 || facadeCrossDotN <= 0) { nextFacadeDirection = facadeCross; } int floorBase = plan.GetFacadeFloorHeight(volumeIndex, volume.points[indexA], volume.points[indexB]); BuildrVolumeStylesUnit[] styleUnits = volume.styles.GetContentsByFacade(volume.points[indexA]); int floorPatternSize = 0; List <int> facadePatternReference = new List <int>(); //this contains a list of all the facade style indices to refence when looking for the appropriate style per floor int patternCount = 0; foreach (BuildrVolumeStylesUnit styleUnit in styleUnits) //need to knw how big all the styles are together so we can loop through them { floorPatternSize += styleUnit.floors; for (int i = 0; i < styleUnit.floors; i++) { facadePatternReference.Add(patternCount); } patternCount++; } facadePatternReference.Reverse(); int rows = numberOfFloors; Vector2 facadeUV = Vector2.zero; for (int r = 0; r < rows; r++) { float currentFloorHeight = floorHeight * r; Vector3 currentFloorHeightVector = Vector3.up * (data.floorHeight * r); Vector3 facadeFloorBaseVector = p0 + Vector3.up * currentFloorHeight; Vector3 ceilingVector = Vector3.up * ceilingHeight; if (r < floorBase) { //no facade rendered //facade gap filler //interior gap points Vector3 i0 = p1 - facadeDirection.normalized * largestDepthValue; Vector3 i1 = p0 + facadeDirection.normalized * largestDepthValue; Vector3 w0 = i0 + currentFloorHeightVector; Vector3 w1 = i1 + currentFloorHeightVector; Vector3 w2 = w0 + facadeCross * largestDepthValue; Vector3 w3 = w1 + facadeCross * largestDepthValue; Vector3 w4 = w0 + ceilingVector; Vector3 w5 = w1 + ceilingVector; Vector3 w6 = w2 + ceilingVector; Vector3 w7 = w3 + ceilingVector; Vector3 w8 = p1interior + currentFloorHeightVector; Vector3 w9 = p0interior + currentFloorHeightVector; Vector3 w10 = w8 + ceilingVector; Vector3 w11 = w9 + ceilingVector; //floor AddData(new[] { w0, w1, w2, w3 }, new[] { 0, 1, 2, 1, 3, 2 }, volume.FloorTexture(r), false); //ceiling AddData(new[] { w5, w4, w7, w6 }, new[] { 0, 1, 2, 1, 3, 2 }, volume.CeilingTexture(r), false); //sides int wallSubmesh = volume.WallTexture(r); AddPlane(w0, w2, w4, w6, wallSubmesh, false, Vector3.zero, new Vector2(largestDepthValue, floorHeight)); AddPlane(w3, w1, w7, w5, wallSubmesh, false, Vector3.zero, new Vector2(largestDepthValue, floorHeight)); //other gaps float uvWidth1 = Vector3.Distance(w2, w8); AddPlane(w2, w8, w6, w10, wallSubmesh, false, Vector3.zero, new Vector2(uvWidth1, floorHeight)); float uvWidth2 = Vector3.Distance(w3, w9); AddPlane(w9, w3, w11, w7, wallSubmesh, false, Vector3.zero, new Vector2(uvWidth2, floorHeight)); continue; } //Get the facade style id //need to loop through the facade designs floor by floor until we get to the right one int modFloor = ((r - floorBase) % floorPatternSize); facadeDesign = data.facades[styleUnits[facadePatternReference[modFloor]].styleID]; bool isBlankWall = !facadeDesign.hasWindows; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { if (data.bays.Count == 0 || facadeDesign.bayPattern.Count == 0) { data.illegal = true; return; } BuildrBay firstBay = data.bays[facadeDesign.bayPattern[0]]; if (firstBay.openingWidth > facadeWidth) { isBlankWall = true; } if (facadeDesign.bayPattern.Count == 0) { isBlankWall = true; } } else { if (facadeDesign.simpleBay.openingWidth + facadeDesign.simpleBay.minimumBayWidth > facadeWidth) { isBlankWall = true; } } if (!isBlankWall) { float patternSize = 0;//the space the pattern fills, there will be a gap that will be distributed to all bay styles int numberOfBays = 0; BuildrBay[] bayDesignPattern; int numberOfBayDesigns; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { numberOfBayDesigns = facadeDesign.bayPattern.Count; bayDesignPattern = new BuildrBay[numberOfBayDesigns]; for (int i = 0; i < numberOfBayDesigns; i++) { bayDesignPattern[i] = data.bays[facadeDesign.bayPattern[i]]; } } else { bayDesignPattern = new[] { facadeDesign.simpleBay }; numberOfBayDesigns = 1; } //start with first window width - we'll be adding to this until we have filled the facade width int it = 100; while (true) { int patternModIndex = numberOfBays % numberOfBayDesigns; float patternAddition = bayDesignPattern[patternModIndex].openingWidth + bayDesignPattern[patternModIndex].minimumBayWidth; if (patternSize + patternAddition < facadeWidth) { patternSize += patternAddition; numberOfBays++; } else { break; } it--; if (it < 0) { break; } } Vector3 windowBase = facadeFloorBaseVector; facadeUV.x = 0; facadeUV.y += floorHeight; float perBayAdditionalSpacing = (facadeWidth - patternSize) / numberOfBays; for (int c = 0; c < numberOfBays; c++) { BuildrBay bayStyle; BuildrBay lastBay; BuildrBay nextBay; bool firstColumn = c == 0; bool lastColumn = c == numberOfBays - 1; if (facadeDesign.type == BuildrFacadeDesign.types.patterned) { int numberOfBayStyles = facadeDesign.bayPattern.Count; bayStyle = bayDesignPattern[c % numberOfBayStyles]; int lastBayIndex = (c > 0) ? (c - 1) % numberOfBayStyles : 0; lastBay = bayDesignPattern[lastBayIndex]; nextBay = bayDesignPattern[(c + 1) % numberOfBayStyles]; } else { bayStyle = facadeDesign.simpleBay; lastBay = facadeDesign.simpleBay; nextBay = facadeDesign.simpleBay; } float actualWindowSpacing = bayStyle.minimumBayWidth + perBayAdditionalSpacing; float leftWidth = actualWindowSpacing * bayStyle.openingWidthRatio; float rightWidth = actualWindowSpacing - leftWidth; float openingWidth = bayStyle.openingWidth; if (firstColumn) { leftWidth += largestDepthValue; } if (lastColumn) { rightWidth += largestDepthValue; } BuildrTexture columnTexture = textures[bayStyle.GetTexture(BuildrBay.TextureNames.ColumnTexture)]; Vector2 columnuvunits = columnTexture.tileUnitUV; float openingHeight = bayStyle.openingHeight; if (columnTexture.patterned) { openingHeight = Mathf.Ceil(bayStyle.openingHeight / columnuvunits.y) * columnuvunits.y; } if (bayStyle.openingHeight == floorHeight) { bayStyle.openingHeight = floorHeight; } float rowBottomHeight = ((floorHeight - openingHeight) * bayStyle.openingHeightRatio); if (columnTexture.patterned) { rowBottomHeight = Mathf.Ceil(rowBottomHeight / columnuvunits.y) * columnuvunits.y; } float rowTopHeight = (floorHeight - rowBottomHeight - openingHeight); bool previousBayIdentical = bayStyle == lastBay; bool nextBayIdentical = bayStyle == nextBay; if (previousBayIdentical && !firstColumn) { leftWidth = actualWindowSpacing;//if next design is identical - add the two parts together the reduce polycount } Vector3 w0, w1, w2, w3; int wallSubmesh = volume.WallTexture(r); bool wallFlipped = false; if (!bayStyle.isOpening) { float bayWidthSize = openingWidth + actualWindowSpacing; if (firstColumn || lastColumn) { bayWidthSize += largestDepthValue; } Vector3 bayWidth = facadeDirection * bayWidthSize; Vector3 bayHeight = Vector3.up * floorHeight; Vector3 bayDepth = facadeCross * largestDepthValue; w0 = windowBase + bayDepth; w1 = windowBase + bayWidth + bayDepth; w2 = windowBase + bayHeight + bayDepth; w3 = windowBase + bayWidth + bayHeight + bayDepth; Vector2 bayOpeningUVEnd = facadeUV + new Vector2(openingWidth + actualWindowSpacing, floorHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, facadeUV, bayOpeningUVEnd); windowBase = windowBase + bayWidth; //move base vertor to next bay facadeUV.x += openingWidth + actualWindowSpacing; continue; //bay filled - move onto next bay } var verts = new Vector3[16]; verts[0] = windowBase; verts[1] = verts[0] + leftWidth * facadeDirection; verts[2] = verts[1] + openingWidth * facadeDirection; verts[3] = verts[2] + rightWidth * facadeDirection; windowBase = (nextBayIdentical) ? verts[2] : verts[3];//move to next window - if next design is identical - well add the two parts together the reduce polycount facadeUV.x += (nextBayIdentical) ? openingWidth : openingWidth + rightWidth; Vector3 rowBottomVector = Vector3.up * rowBottomHeight; verts[4] = verts[0] + rowBottomVector; verts[5] = verts[1] + rowBottomVector; verts[6] = verts[2] + rowBottomVector; verts[7] = verts[3] + rowBottomVector; Vector3 openingVector = Vector3.up * openingHeight; verts[8] = verts[4] + openingVector; verts[9] = verts[5] + openingVector; verts[10] = verts[6] + openingVector; verts[11] = verts[7] + openingVector; Vector3 rowTopVector = Vector3.up * rowTopHeight; verts[12] = verts[8] + rowTopVector; verts[13] = verts[9] + rowTopVector; verts[14] = verts[10] + rowTopVector; verts[15] = verts[11] + rowTopVector; //Realign facade end points if (firstColumn) { verts[0] = p0interior - facadeCross * largestDepthValue + currentFloorHeightVector; verts[4] = verts[0] + rowBottomVector; verts[8] = verts[4] + openingVector; verts[12] = verts[8] + rowTopVector; } if (lastColumn) { verts[3] = p1interior - facadeCross * largestDepthValue + currentFloorHeightVector; verts[7] = verts[3] + rowBottomVector; verts[11] = verts[7] + openingVector; verts[15] = verts[11] + rowTopVector; } Vector3 openingDepthVector = facadeCross * bayStyle.openingDepth; Vector3 wallDepthVecotr = facadeCross * largestDepthValue; ///WINDOWS int windowSubmesh = bayStyle.GetTexture(BuildrBay.TextureNames.OpeningBackTexture); bool windowFlipped = bayStyle.IsFlipped(BuildrBay.TextureNames.OpeningBackTexture); w0 = verts[10] + openingDepthVector; w1 = verts[9] + openingDepthVector; w2 = verts[6] + openingDepthVector; w3 = verts[5] + openingDepthVector; Vector2 windowUVStart = new Vector2(0, 0); Vector2 windowUVEnd = new Vector2(openingWidth, openingHeight); if (bayStyle.renderBack && !data.cullBays) { AddPlane(w0, w1, w2, w3, windowSubmesh, windowFlipped, windowUVStart, windowUVEnd); } ///COLUMNS //Column Face if (leftWidth > 0)//Column Face Left { w0 = verts[4] + wallDepthVecotr; w1 = verts[5] + wallDepthVecotr; w2 = verts[8] + wallDepthVecotr; w3 = verts[9] + wallDepthVecotr; Vector2 leftColumnUVStart = facadeUV + new Vector2(0, rowBottomHeight); Vector2 leftColumnUVEnd = leftColumnUVStart + new Vector2(leftWidth, openingHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, leftColumnUVStart, leftColumnUVEnd); } if ((!nextBayIdentical || lastColumn) && rightWidth > 0)//Column Right { w0 = verts[6] + wallDepthVecotr; w1 = verts[7] + wallDepthVecotr; w2 = verts[10] + wallDepthVecotr; w3 = verts[11] + wallDepthVecotr; Vector2 rightColumnUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight); Vector2 rightColumnUVEnd = rightColumnUVStart + new Vector2(rightWidth, openingHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, rightColumnUVStart, rightColumnUVEnd); } ///ROWS //Row Bottom if (rowBottomHeight > 0) { w0 = verts[1] + wallDepthVecotr; w1 = verts[2] + wallDepthVecotr; w2 = verts[5] + wallDepthVecotr; w3 = verts[6] + wallDepthVecotr; Vector2 bottomRowUVStart = facadeUV + new Vector2(leftWidth, 0); Vector2 bottomRowUVEnd = bottomRowUVStart + new Vector2(openingWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, bottomRowUVStart, bottomRowUVEnd); } //Row Top if (rowTopHeight > 0) { w0 = verts[9] + wallDepthVecotr; w1 = verts[10] + wallDepthVecotr; w2 = verts[13] + wallDepthVecotr; w3 = verts[14] + wallDepthVecotr; Vector2 topRowUVStart = facadeUV + new Vector2(leftWidth, rowBottomHeight + openingHeight); Vector2 topRowUVEnd = topRowUVStart + new Vector2(openingWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, topRowUVStart, topRowUVEnd); } //Cross Left Bottom w0 = verts[0] + wallDepthVecotr; w1 = verts[1] + wallDepthVecotr; w2 = verts[4] + wallDepthVecotr; w3 = verts[5] + wallDepthVecotr; Vector2 crossLBUVStart = facadeUV + new Vector2(0, 0); Vector2 crossLBUVEnd = crossLBUVStart + new Vector2(leftWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossLBUVStart, crossLBUVEnd); //Cross Left Top w0 = verts[8] + wallDepthVecotr; w1 = verts[9] + wallDepthVecotr; w2 = verts[12] + wallDepthVecotr; w3 = verts[13] + wallDepthVecotr; Vector2 crossLTUVStart = facadeUV + new Vector2(0, rowBottomHeight + openingHeight); Vector2 crossLTUVEnd = crossLTUVStart + new Vector2(leftWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossLTUVStart, crossLTUVEnd); if ((!nextBayIdentical || lastColumn) && rightWidth > 0) { //Cross Right Bottom w0 = verts[2] + wallDepthVecotr; w1 = verts[3] + wallDepthVecotr; w2 = verts[6] + wallDepthVecotr; w3 = verts[7] + wallDepthVecotr; Vector2 crossRBUVStart = facadeUV + new Vector2(leftWidth + openingWidth, 0); Vector2 crossRBUVEnd = crossRBUVStart + new Vector2(rightWidth, rowBottomHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossRBUVStart, crossRBUVEnd); //Cross Right Top w0 = verts[10] + wallDepthVecotr; w1 = verts[11] + wallDepthVecotr; w2 = verts[14] + wallDepthVecotr; w3 = verts[15] + wallDepthVecotr; Vector2 crossRTUVStart = facadeUV + new Vector2(leftWidth + openingWidth, rowBottomHeight + openingHeight); Vector2 crossRTUVEnd = crossRTUVStart + new Vector2(rightWidth, rowTopHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, wallFlipped, crossRTUVStart, crossRTUVEnd); } } } else { // windowless wall Vector3 interiorStart = p0interior + currentFloorHeightVector; Vector3 interiorEnd = p1interior + currentFloorHeightVector; // Vector3 wallVector = (facadeDirection * facadeWidth); Vector3 wallHeightVector = Vector3.up * floorHeight; Vector3 w0 = interiorStart; Vector3 w1 = interiorEnd; Vector3 w2 = interiorStart + wallHeightVector; Vector3 w3 = interiorEnd + wallHeightVector; BuildrTexture texture = textures[facadeDesign.simpleBay.GetTexture(BuildrBay.TextureNames.WallTexture)]; var uvSize = new Vector2(facadeWidth * (1.0f / texture.textureUnitSize.x), floorHeight * (1.0f / texture.textureUnitSize.y)); Vector2 uvunits = texture.tileUnitUV; uvSize.x = Mathf.Ceil(uvSize.x / uvunits.x) * uvunits.x; uvSize.y = Mathf.Ceil(uvSize.y / uvunits.y) * uvunits.y; int wallSubmesh = 0; bool flipped = false; Vector2 wallUVStart = facadeUV; Vector2 wallUVEnd = facadeUV + new Vector2(facadeWidth, floorHeight); AddPlane(w0, w1, w2, w3, wallSubmesh, flipped, wallUVStart, wallUVEnd); } } } ///FLOORS AND CEILING int numberOfBasements = volume.numberOfBasementFloors; int numberOfFloorPoints = interiorVolumePoints.Length; int[] baseFloorPlanTriangles = EarClipper.Triangulate(interiorVolumePoints); int baseFloorVectors = interiorVolumePoints.Length; var newEndVerts = new Vector3[baseFloorVectors]; Vector3 basementBaseDrop = -floorHeightVector * numberOfBasements; for (int i = 0; i < baseFloorVectors; i++) { newEndVerts[i] = interiorVolumePoints[i].vector3 + basementBaseDrop; } var tris = new List <int>(baseFloorPlanTriangles); //Bottom Floor int floorSubmesh = volume.FloorTexture(-numberOfBasements); AddData(newEndVerts, baseFloorPlanTriangles, floorSubmesh, false); //Top Ceiling if (true)//Todo: add conditional for roof opening { Vector3 ceilingHeightVector = floorHeightVector * (numberOfFloors - 1 + numberOfBasements) + Vector3.up * ceilingHeight; for (int i = 0; i < baseFloorVectors; i++) { newEndVerts[i] += ceilingHeightVector; } tris.Reverse(); AddData(newEndVerts, tris.ToArray(), volume.CeilingTexture(numberOfFloors - 1), false); } //inner floors int[] floorPlanTriangles = EarClipper.Triangulate(interiorPointListCore); int numberOfFloorVectors = interiorPointListCore.Length; for (int floorIndex = -numberOfBasements; floorIndex < numberOfFloors; floorIndex++) { Vector3 floorVectorHeight = floorHeightVector * floorIndex; newEndVerts = new Vector3[numberOfFloorVectors]; for (int i = 0; i < numberOfFloorVectors; i++) { newEndVerts[i] = interiorPointListCore[i].vector3 + floorVectorHeight; } tris = new List <int>(floorPlanTriangles); //Floor if (floorIndex > -numberOfBasements) { AddData(newEndVerts, tris.ToArray(), volume.FloorTexture(floorIndex), false); } //Ceiling if (floorIndex < numberOfFloors - 1) { Vector3 ceilingHeightVector = Vector3.up * ceilingHeight; for (int i = 0; i < numberOfFloorVectors; i++) { newEndVerts[i] += ceilingHeightVector; } tris.Reverse(); AddData(newEndVerts, tris.ToArray(), volume.CeilingTexture(floorIndex), false); } //basement walls if (floorIndex < 0) { for (int f = 0; f < numberOfFloorPoints; f++) { Vector3 basementVector = floorHeightVector * floorIndex; int indexA = f; int indexB = (f + 1) % numberOfFloorPoints; Vector3 p0 = interiorVolumePoints[indexA].vector3 + basementVector; Vector3 p1 = interiorVolumePoints[indexB].vector3 + basementVector; Vector3 p2 = p0 + floorHeightVector; Vector3 p3 = p1 + floorHeightVector; Vector2 uv1 = new Vector2(Vector3.Distance(p0, p1), floorHeight); AddPlane(p0, p1, p2, p3, volume.WallTexture(floorIndex), false, Vector2.zero, uv1); } } } //Core walls for (int c = 0; c < numberOfVolumeCores; c++) { Rect coreBounds = volumeCores[c]; Vector3 coreBL = new Vector3(coreBounds.xMin, 0, coreBounds.yMin); Vector3 coreBR = new Vector3(coreBounds.xMax, 0, coreBounds.yMin); Vector3 coreTL = new Vector3(coreBounds.xMin, 0, coreBounds.yMax); Vector3 coreTR = new Vector3(coreBounds.xMax, 0, coreBounds.yMax); for (int floorIndex = -numberOfBasements; floorIndex < numberOfFloors - 1; floorIndex++) { Vector3 c0 = floorHeightVector * floorIndex + Vector3.up * ceilingHeight; Vector3 f0 = floorHeightVector * floorIndex + Vector3.up * floorHeight; float gapHeight = floorHeight - ceilingHeight; AddPlane(coreBL + c0, coreBR + c0, coreBL + f0, coreBR + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreBR + c0, coreTR + c0, coreBR + f0, coreTR + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreTR + c0, coreTL + c0, coreTR + f0, coreTL + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); AddPlane(coreTL + c0, coreBL + c0, coreTL + f0, coreBL + f0, 0, false, Vector2.zero, new Vector2(coreBounds.width, gapHeight)); } } }