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) { // 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) { // 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 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; } }
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(); } }
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; }