void Awake() { data = gameObject.AddComponent<BuildrData>(); data.Init(); constraints = ScriptableObject.CreateInstance<BuildrRuntimeConstraints>(); }
public static void Generate(BuildrData data) { BuildrGenerateConstraints constraints = data.generatorConstraints; uint seed = (uint)(constraints.useSeed ? constraints.seed : Random.Range(0, int.MaxValue)); constraints.seed = (int)seed;//reassign value incase it's changed constraints.rgen = new RandomGen(seed); RandomGen rgen = constraints.rgen; data.ResetData(constraints.constrainPlanByPlan); if (!constraints.constrainPlanByPlan) GenerateFloorPlan(data); else data.plan = constraints.plan; data.floorHeight = rgen.OutputRange(constraints.minimumFloorHeight, constraints.maximumFloorHeight); float minBuildingSize = (constraints.constrainHeight) ? constraints.minimumHeight : BuildrGenerateConstraints.MINIMUM_BUILDING_HEIGHT; float maxBuildingSize = (constraints.constrainHeight) ? constraints.maximumHeight : BuildrGenerateConstraints.MAXIMUM_BUILDING_HEIGHT; foreach(BuildrVolume volume in data.plan.volumes) { volume.height = rgen.OutputRange(minBuildingSize, maxBuildingSize); volume.numberOfFloors = Mathf.FloorToInt(volume.height / data.floorHeight); } //texture generation GetTextures(data); //facade generation GenerateFacades(data); //roof generation GenerateRoof(data); }
private static void GenerateFloorPlan(BuildrData data) { BuildrGenerateConstraints constraints = data.generatorConstraints; RandomGen rgen = constraints.rgen; BuildrPlan plan = ScriptableObject.CreateInstance <BuildrPlan>(); List <Vector2z> bounds = new List <Vector2z>(); Rect floorplanBounds = new Rect(-15, -15, 30, 30); if (constraints.constrainPlanByArea) { floorplanBounds = constraints.area; } bounds.Add(new Vector2z(rgen.OutputRange(-5, floorplanBounds.xMin), rgen.OutputRange(-5, floorplanBounds.yMin))); bounds.Add(new Vector2z(rgen.OutputRange(5, floorplanBounds.xMax), rgen.OutputRange(-5, floorplanBounds.yMin))); bounds.Add(new Vector2z(rgen.OutputRange(5, floorplanBounds.xMax), rgen.OutputRange(5, floorplanBounds.yMax))); bounds.Add(new Vector2z(rgen.OutputRange(-5, floorplanBounds.xMin), rgen.OutputRange(5, floorplanBounds.yMax))); if (rgen.output < 0.25f)//should we split the volume? { float ratio = rgen.OutputRange(0.25f, 0.75f); bounds.Insert(1, Vector2z.Lerp(bounds[0], bounds[1], ratio)); bounds.Insert(4, Vector2z.Lerp(bounds[3], bounds[4], ratio)); plan.AddVolume(new [] { bounds[0], bounds[1], bounds[4], bounds[5] }); plan.AddVolume(1, 2, new [] { bounds[2], bounds[3] }); } else { plan.AddVolume(bounds.ToArray()); } data.plan = plan; }
/// <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 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; } }
private static void ImportFacades(XmlDocument xml, BuildrData data) { foreach(XmlNode node in xml.SelectNodes("buildr/facades/facade")) { BuildrFacadeDesign facadeDesign = new BuildrFacadeDesign(); data.facades.Add(facadeDesign); facadeDesign.name = node["name"].FirstChild.Value; facadeDesign.hasWindows = node["hasWindows"].FirstChild.Value == valueTrue; facadeDesign.type = (BuildrFacadeDesign.types)System.Enum.Parse(typeof(BuildrFacadeDesign.types), (string)node["type"].FirstChild.Value); facadeDesign.simpleBay.name = node["simpleBayName"].FirstChild.Value; facadeDesign.simpleBay.openingWidth = float.Parse(node["simpleBayOpeningWidth"].FirstChild.Value); facadeDesign.simpleBay.openingHeight = float.Parse(node["simpleBayOpeningHeight"].FirstChild.Value); facadeDesign.simpleBay.minimumBayWidth = float.Parse(node["simpleBayMinimumBayWidth"].FirstChild.Value); facadeDesign.simpleBay.openingWidthRatio = float.Parse(node["simpleBayOpeningWidthRatio"].FirstChild.Value); facadeDesign.simpleBay.openingHeightRatio = float.Parse(node["simpleBayOpeningHeightRatio"].FirstChild.Value); facadeDesign.simpleBay.openingDepth = float.Parse(node["simpleBayOpeningDepth"].FirstChild.Value); facadeDesign.simpleBay.columnDepth = float.Parse(node["simpleBayColumnDepth"].FirstChild.Value); facadeDesign.simpleBay.rowDepth = float.Parse(node["simpleBayRowDepth"].FirstChild.Value); facadeDesign.simpleBay.crossDepth = float.Parse(node["simpleBayCrossDepth"].FirstChild.Value); for(int i = 0; i < 8; i++) { facadeDesign.simpleBay.textureValues[i] = int.Parse(node.SelectNodes("simpleBayTextures/texture")[i].FirstChild.Value); facadeDesign.simpleBay.flipValues[i] = node.SelectNodes("simpleBayflipvalues/flipvalue")[i].FirstChild.Value == valueTrue; } foreach(XmlNode baynode in node.SelectNodes("bays/bayid")) { facadeDesign.bayPattern.Add(int.Parse(baynode.FirstChild.Value)); } } }
private static void ImportBays(XmlDocument xml, BuildrData data) { foreach (XmlNode node in xml.SelectNodes("buildr/bays/bay")) { BuildrBay bay = new BuildrBay(""); data.bays.Add(bay); bay.name = node["bayName"].FirstChild.Value; bay.isOpening = bool.Parse(node["isOpening"].FirstChild.Value); bay.openingWidth = float.Parse(node["bayOpeningWidth"].FirstChild.Value); bay.openingHeight = float.Parse(node["bayOpeningHeight"].FirstChild.Value); bay.minimumBayWidth = float.Parse(node["bayMinimumBayWidth"].FirstChild.Value); bay.openingWidthRatio = float.Parse(node["bayOpeningWidthRatio"].FirstChild.Value); bay.openingHeightRatio = float.Parse(node["bayOpeningHeightRatio"].FirstChild.Value); bay.openingDepth = float.Parse(node["bayOpeningDepth"].FirstChild.Value); bay.columnDepth = float.Parse(node["bayColumnDepth"].FirstChild.Value); bay.rowDepth = float.Parse(node["bayRowDepth"].FirstChild.Value); bay.crossDepth = float.Parse(node["bayCrossDepth"].FirstChild.Value); for (int i = 0; i < 8; i++) { bay.textureValues[i] = int.Parse(node.SelectNodes("bayTextures/texture")[i].FirstChild.Value); bay.flipValues[i] = node.SelectNodes("bayflipvalues/flipvalue")[i].FirstChild.Value == valueTrue; } } }
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); } }
private static void ImportDetails(XmlDocument xml, BuildrData data) { foreach (XmlNode node in xml.SelectNodes("buildr/details/detail")) { BuildrDetail detail = new BuildrDetail(""); data.details.Add(detail); detail.name = node["name"].FirstChild.Value; detail.mesh = (Mesh)AssetDatabase.LoadAssetAtPath(node["mesh"].FirstChild.Value, typeof(Mesh)); detail.material.mainTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(node["texture"].FirstChild.Value, typeof(Texture2D)); Vector2 faceUV = new Vector2(); faceUV.x = float.Parse(node["faceuvx"].FirstChild.Value); faceUV.y = float.Parse(node["faceuvy"].FirstChild.Value); detail.faceUv = faceUV; detail.faceHeight = float.Parse(node["faceheight"].FirstChild.Value); Vector3 scale = new Vector3(); scale.x = float.Parse(node["scalex"].FirstChild.Value); scale.y = float.Parse(node["scaley"].FirstChild.Value); scale.z = float.Parse(node["scalez"].FirstChild.Value); detail.scale = scale; detail.orientation = (BuildrDetail.Orientations)System.Enum.Parse(typeof(BuildrDetail.Orientations), node["orientation"].FirstChild.Value); Vector3 userRotation = new Vector3(); userRotation.x = float.Parse(node["userRotationx"].FirstChild.Value); userRotation.y = float.Parse(node["userRotationy"].FirstChild.Value); userRotation.z = float.Parse(node["userRotationz"].FirstChild.Value); detail.userRotation = userRotation; detail.face = int.Parse(node["face"].FirstChild.Value); detail.type = (BuildrDetail.Types)System.Enum.Parse(typeof(BuildrDetail.Types), node["type"].FirstChild.Value); } }
/// <summary> /// Checks the Max UV values used in this model for each texture. /// </summary> /// <param name='data'> /// BuildR Data. /// </param> public void CheckMaxTextureUVs(BuildrData data) { foreach (DynamicMeshGenericMultiMaterial mesh in _meshes) { mesh.CheckMaxTextureUVs(data); } }
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; } }
private static void GenerateFloorPlan(BuildrData data) { RandomGen rgen = constraints.rgen; BuildrPlan plan = ScriptableObject.CreateInstance <BuildrPlan>(); List <Vector2z> bounds = new List <Vector2z>(); bounds.Add(new Vector2z(rgen.OutputRange(-5, -15), rgen.OutputRange(-5, -15))); bounds.Add(new Vector2z(rgen.OutputRange(5, 15), rgen.OutputRange(-5, -15))); bounds.Add(new Vector2z(rgen.OutputRange(5, 15), rgen.OutputRange(5, 15))); bounds.Add(new Vector2z(rgen.OutputRange(-5, -15), rgen.OutputRange(5, 15))); if (rgen.output < 0.25f)//should we split the volume? { float ratio = rgen.OutputRange(0.25f, 0.75f); bounds.Insert(1, Vector2z.Lerp(bounds[0], bounds[1], ratio)); bounds.Insert(4, Vector2z.Lerp(bounds[3], bounds[4], ratio)); plan.AddVolume(new [] { bounds[0], bounds[1], bounds[4], bounds[5] }); plan.AddVolume(1, 2, new [] { bounds[2], bounds[3] }); } else { plan.AddVolume(bounds.ToArray()); } data.plan = plan; }
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); }
public static void RefreshTextures(BuildrData data) { data.textures.Clear(); BuildrGenerateConstraints constraints = data.generatorConstraints; uint seed = (uint)(constraints.useSeed ? constraints.seed : Random.Range(0, int.MaxValue)); constraints.rgen = new RandomGen(seed); GetTextures(data); }
public static void SceneGUI(BuildrEditMode editMode, BuildrData data, bool shouldSnap, float handleSize) { if (GUI.changed) { EditorUtility.SetDirty(editMode); EditorUtility.SetDirty(data); editMode.UpdateRender(); } }
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 static void ImportFacades(string XMLPath, BuildrData data) { Debug.Log("Import Facades " + XMLPath); XmlDocument xml = new XmlDocument(); using (StreamReader sr = new StreamReader(XMLPath)) { xml.LoadXml(sr.ReadToEnd()); } data.facades.Clear(); ImportFacades(xml, data); }
public static void InspectorGUI(BuildrEditMode _editMode, BuildrData _data) { editMode = _editMode; data = _data; constraints = data.generatorConstraints; EditModes newmode = (EditModes)EditorGUILayout.EnumPopup(mode); if (newmode != mode) { mode = newmode; switch (mode) { case EditModes.general: editMode.stage = BuildrEditMode.stages.building; break; case EditModes.floorplan: editMode.stage = BuildrEditMode.stages.floorplan; editMode.SetMode(BuildrEditMode.modes.floorplan); break; } } switch (mode) { case EditModes.general: GeneralOptionsInspector(); break; case EditModes.floorplan: EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Constrain Building Generation to Floorplan", GUILayout.Width(280)); constraints.constrainPlanByPlan = EditorGUILayout.Toggle(constraints.constrainPlanByPlan); EditorGUILayout.EndHorizontal(); EditorGUI.BeginDisabledGroup(!constraints.constrainPlanByPlan); BuildrEditModeFloorplan.InspectorGUI(editMode, _data.plan); EditorGUI.EndDisabledGroup(); if (data.plan != null) { constraints.plan = data.plan.Duplicate(); } break; } if (GUI.changed) { EditorUtility.SetDirty(_editMode); EditorUtility.SetDirty(_data); _editMode.UpdateRender(); } }
private static void Export(string filename, string folder, BuildrData data, Mesh exportMesh, ExportMaterial[] exportTextures) { switch (data.filetype) { case BuildrData.filetypes.obj: OBJExporter.Export(folder, filename, exportMesh, exportTextures, data.copyTexturesIntoExportFolder); break; case BuildrData.filetypes.fbx: FBXExporter.Export(folder, filename, exportMesh, exportTextures, data.copyTexturesIntoExportFolder); break; } }
private static void ImportVolumes(XmlDocument xml, BuildrData data) { foreach(XmlNode node in xml.SelectNodes("buildr/plan/volumes/volume")) { BuildrVolume volume = ScriptableObject.CreateInstance<BuildrVolume>(); BuildrVolumeStyles styles = ScriptableObject.CreateInstance<BuildrVolumeStyles>(); data.plan.volumes.Add(volume); foreach(XmlNode pointnode in node.SelectNodes("points/point")) { volume.points.Add(int.Parse(pointnode.FirstChild.Value)); } foreach(XmlNode pointnode in node.SelectNodes("facades/render")) { volume.renderFacade.Add(pointnode.FirstChild.Value == valueTrue); } volume.height = float.Parse(node["height"].FirstChild.Value); volume.numberOfFloors = int.Parse(node["numberoffloors"].FirstChild.Value); volume.roofDesignID = int.Parse(node["roofdesignid"].FirstChild.Value); foreach(XmlNode stylenode in node.SelectNodes("styles/style")) { int styleID = int.Parse(stylenode["styleid"].FirstChild.Value); int facadeID = int.Parse(stylenode["facadeid"].FirstChild.Value); int floors = int.Parse(stylenode["floors"].FirstChild.Value); styles.AddStyle(styleID, facadeID, floors); } volume.styles = styles; volume.generateStairs = bool.Parse(node["generateStairs"].FirstChild.Value); volume.staircaseWidth = float.Parse(node["staircaseWidth"].FirstChild.Value); volume.stepHeight = float.Parse(node["stepHeight"].FirstChild.Value); volume.stairwellCeilingTexture = int.Parse(node["stairwellCeilingTexture"].FirstChild.Value); volume.stairwellFloorTexture = int.Parse(node["stairwellFloorTexture"].FirstChild.Value); volume.stairwellStepTexture = int.Parse(node["stairwellStepTexture"].FirstChild.Value); volume.stairwellWallTexture = int.Parse(node["stairwellWallTexture"].FirstChild.Value); volume.numberOfBasementFloors = int.Parse(node["numberOfBasementFloors"].FirstChild.Value); int itemCount = 0; foreach (XmlNode basementNode in node.SelectNodes("basementtextures/basementfloortextures")) { volume.FloorTexture(itemCount, int.Parse(basementNode["FloorTexture"].FirstChild.Value)); volume.WallTexture(itemCount, int.Parse(basementNode["WallTexture"].FirstChild.Value)); volume.CeilingTexture(itemCount, int.Parse(basementNode["CeilingTexture"].FirstChild.Value)); itemCount++; } } }
private static void ImportTextures(XmlDocument xml, BuildrData data) { foreach (XmlNode node in xml.SelectNodes("buildr/textures/texture")) { BuildrTexture texture = new BuildrTexture(""); data.textures.Add(texture); texture.name = node["name"].FirstChild.Value; texture.tiled = node["tiled"].FirstChild.Value == valueTrue; texture.patterned = node["patterned"].FirstChild.Value == valueTrue; texture.texture = (Texture2D)AssetDatabase.LoadAssetAtPath(node["texture"].FirstChild.Value, typeof(Texture2D)); texture.tileUnitUV = new Vector2(float.Parse(node["tileUnitUV"]["x"].FirstChild.Value), float.Parse(node["tileUnitUV"]["y"].FirstChild.Value)); texture.textureUnitSize = new Vector2(float.Parse(node["textureUnitSize"]["x"].FirstChild.Value), float.Parse(node["textureUnitSize"]["y"].FirstChild.Value)); } }
void OnEnable() { if (target != null) { _editMode = (BuildrEditMode)target; _data = _editMode.data; } bool editing = (_data != null) ? _data.editing : true; if (!editing) { BuildrGenerateModeEditor.OnEnable(); } }
public static void Generate(BuildrData data, BuildrRuntimeConstraints _constraints) { constraints = _constraints; uint seed = (uint)(constraints.useSeed ? constraints.seed : Random.Range(0, int.MaxValue)); constraints.seed = (int)seed;//reassign value incase it's changed constraints.rgen = new RandomGen(seed); RandomGen rgen = constraints.rgen; //Debug.Log("Generate Seed "+seed); data.ResetData(constraints.constrainPlanByPlan); if (!constraints.constrainPlanByPlan) { GenerateFloorPlan(data); } else { data.plan = constraints.plan; } data.floorHeight = rgen.OutputRange(constraints.minimumFloorHeight, constraints.maximumFloorHeight); float minBuildingSize = (constraints.constrainHeight) ? constraints.minimumHeight : BuildrGenerateConstraints.MINIMUM_BUILDING_HEIGHT; float maxBuildingSize = (constraints.constrainHeight) ? constraints.maximumHeight : BuildrGenerateConstraints.MAXIMUM_BUILDING_HEIGHT; foreach (BuildrVolume volume in data.plan.volumes) { volume.height = rgen.OutputRange(minBuildingSize, maxBuildingSize); volume.numberOfFloors = Mathf.FloorToInt(volume.height / data.floorHeight); } //texture generation GetTextures(data); //facade generation GenerateFacades(data); //roof generation GenerateRoof(data); //building generation //build/optimise }
public static void ExportFacades(string filepath, BuildrData _data) { data = _data; StringBuilder sb = new StringBuilder(); sb.AppendLine("<?xml version='1.0' encoding='ISO-8859-15'?>"); sb.AppendLine("<!-- Unity3D Asset Buildr XML Exporter http://buildr.jasperstocker.com -->"); sb.AppendLine("<buildr version='" + data.versionNumber + "'>"); sb.Append(ExportFacades()); sb.AppendLine("</buildr>"); using (StreamWriter sw = new StreamWriter(filepath)) { sw.Write(sb.ToString());//write out contents of data to XML } data = null; }
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); }
//data should be already created, attached to the gameobject as a component and init run. public static void Import(string XMLPath, BuildrData data) { Debug.Log("Import " + XMLPath); XmlDocument xml = new XmlDocument(); using(StreamReader sr = new StreamReader(XMLPath)) { xml.LoadXml(sr.ReadToEnd()); } float xmlVersionNumber = float.Parse(xml["buildr"].Attributes["version"].Value); if(xmlVersionNumber != BuildrVersion.NUMBER) Debug.Log("Version upgrade " + xmlVersionNumber + " > " + BuildrVersion.NUMBER); data.name = xml["buildr"]["buildingname"].FirstChild.Value; data.floorHeight = float.Parse(xml["buildr"]["floorheight"].FirstChild.Value); data.foundationHeight = float.Parse(xml["buildr"]["foundationHeight"].FirstChild.Value); data.drawUnderside = bool.Parse(xml["buildr"]["drawUnderside"].FirstChild.Value); data.generateCollider = (BuildrData.ColliderGenerationModes)System.Enum.Parse(typeof(BuildrData.ColliderGenerationModes), xml["buildr"]["generateCollider"].FirstChild.Value); data.renderInteriors = bool.Parse(xml["buildr"]["renderInteriors"].FirstChild.Value); data.interiorCeilingHeight = float.Parse(xml["buildr"]["interiorCeilingHeight"].FirstChild.Value); data.cullBays = bool.Parse(xml["buildr"]["cullBays"].FirstChild.Value); //volume points foreach(XmlNode node in xml.SelectNodes("buildr/plan/points/point")) { data.plan.points.Add(new Vector2z(node["x"].FirstChild.Value, node["z"].FirstChild.Value)); } //volume cores foreach (XmlNode node in xml.SelectNodes("buildr/plan/cores/core")) { data.plan.cores.Add(new Rect(float.Parse(node["xMin"].FirstChild.Value), float.Parse(node["yMin"].FirstChild.Value), float.Parse(node["width"].FirstChild.Value), float.Parse(node["height"].FirstChild.Value))); } ImportVolumes(xml,data); ImportFacades(xml,data); ImportRoofs(xml,data); ImportTextures(xml,data); ImportBays(xml,data); ImportDetails(xml,data); }
private static void GetTextures(BuildrData data) { //Load in textures from the RESOURCES folder wallTexture = new BuildrTexture("wall");//wall wallTexture.texture = (Texture2D)Resources.Load("Textures/BrickSmallBrown0078_2_S"); data.textures.Add(wallTexture); windowTexture = new BuildrTexture("window");//window windowTexture.texture = (Texture2D)Resources.Load("Textures/WindowsHouseOld0260_S"); data.textures.Add(windowTexture); roofTexture = new BuildrTexture("roof");//roof roofTexture.texture = (Texture2D)Resources.Load("Textures/RooftilesMetal0012_2_S"); data.textures.Add(roofTexture); doorTexture = new BuildrTexture("door");//door doorTexture.texture = (Texture2D)Resources.Load("Textures/DoorsWoodPanelled0124_S"); data.textures.Add(doorTexture); }
public static void ExportRoofs(string filepath, BuildrData _data) { data = _data; StringBuilder sb = new StringBuilder(); sb.AppendLine("<?xml version='1.0' encoding='ISO-8859-15'?>"); sb.AppendLine("<!-- Unity3D Asset Buildr XML Exporter http://buildr.jasperstocker.com -->"); sb.AppendLine("<buildr version='" + data.versionNumber + "'>"); sb.Append(ExportRoofs()); sb.AppendLine("</buildr>"); using (StreamWriter sw = new StreamWriter(filepath)) { sw.Write(sb.ToString());//write out contents of data to XML } data = null; }
/// <summary> /// Checks the Max UV values used in this model for each texture. /// </summary> /// <param name='data'> /// BuildR Data. /// </param> public void CheckMaxTextureUVs(BuildrData data) { Vector2[] subMeshUVOffsets = new Vector2[subMeshCount]; int[] subMeshIDs = new List <int>(subTriangles.Keys).ToArray(); int numberOfSubmeshIDs = subMeshIDs.Length; for (int sm = 0; sm < numberOfSubmeshIDs; sm++) { int subMeshID = subMeshIDs[sm]; if (subTriangles.ContainsKey(subMeshID)) { int[] submeshIndices = subTriangles[subMeshID].ToArray(); subMeshUVOffsets[sm] = Vector2.zero; foreach (int index in submeshIndices) { if (uv[index].x < subMeshUVOffsets[sm].x) { subMeshUVOffsets[sm].x = uv[index].x; } if (uv[index].y < subMeshUVOffsets[sm].y) { subMeshUVOffsets[sm].y = uv[index].y; } } List <int> UVsOffset = new List <int>(); foreach (int uvindex in subTriangles[subMeshID]) { if (!UVsOffset.Contains(uvindex)) { uv[uvindex] += -subMeshUVOffsets[sm];//offset the UV to ensure it isn't negative UVsOffset.Add(uvindex); } data.textures[subMeshID].CheckMaxUV(uv[uvindex]); } } else { Debug.Log("Mesh does not contain key for texture " + data.textures[subMeshID].name); } } }
public static bool Export(string folder, string filename, BuildrData _data) { targetFolder = folder; targetName = filename; data = _data; if (folder.Contains(" ")) { EditorUtility.DisplayDialog("Filename Error", "The filename can't contain spaces", "I'm sorry"); return(false); } if (filename.Contains(" ")) { EditorUtility.DisplayDialog("Filename Error", "The filename can't contain spaces", "I'm sorry"); return(false); } //Export code StringBuilder sb = new StringBuilder(); sb.AppendLine("<?xml version='1.0' encoding='ISO-8859-15'?>"); sb.AppendLine("<!-- Unity3D Asset Buildr XML Exporter http://buildr.jasperstocker.com -->"); sb.AppendLine("<buildr version='" + data.versionNumber + "'>"); sb.Append(ExportData()); //end sb.AppendLine("</buildr>"); CreateTargetFolder(); using (StreamWriter sw = new StreamWriter(targetFolder + "/" + targetName + ".xml")) { sw.Write(sb.ToString());//write out contents of data to XML } data = null; return(true); }
public static bool Export(string folder, string filename, BuildrData _data) { targetFolder = folder; targetName = filename; data = _data; if (folder.Contains(" ")) { EditorUtility.DisplayDialog("Filename Error", "The filename can't contain spaces", "I'm sorry"); return false; } if (filename.Contains(" ")) { EditorUtility.DisplayDialog("Filename Error", "The filename can't contain spaces", "I'm sorry"); return false; } //Export code StringBuilder sb = new StringBuilder(); sb.AppendLine("<?xml version='1.0' encoding='ISO-8859-15'?>"); sb.AppendLine("<!-- Unity3D Asset Buildr XML Exporter http://buildr.jasperstocker.com -->"); sb.AppendLine("<buildr version='" + data.versionNumber + "'>"); sb.Append(ExportData()); //end sb.AppendLine("</buildr>"); CreateTargetFolder(); using (StreamWriter sw = new StreamWriter(targetFolder + "/" + targetName + ".xml")) { sw.Write(sb.ToString());//write out contents of data to XML } data = null; return true; }
private static void ImportRoofs(XmlDocument xml, BuildrData data) { foreach(XmlNode node in xml.SelectNodes("buildr/roofs/roof")) { BuildrRoofDesign roofDesign = new BuildrRoofDesign(""); data.roofs.Add(roofDesign); roofDesign.name = node["name"].FirstChild.Value; roofDesign.style = (BuildrRoofDesign.styles)System.Enum.Parse(typeof(BuildrRoofDesign.styles), node["style"].FirstChild.Value); roofDesign.height = float.Parse(node["height"].FirstChild.Value); roofDesign.depth = float.Parse(node["depth"].FirstChild.Value); roofDesign.floorDepth = float.Parse(node["floorDepth"].FirstChild.Value); roofDesign.direction = int.Parse(node["direction"].FirstChild.Value); roofDesign.sawtoothTeeth = int.Parse(node["sawtoothTeeth"].FirstChild.Value); roofDesign.barrelSegments = int.Parse(node["barrelSegments"].FirstChild.Value); roofDesign.parapet = node["parapet"].FirstChild.Value == valueTrue; roofDesign.parapetStyle = (BuildrRoofDesign.parapetStyles)System.Enum.Parse(typeof(BuildrRoofDesign.parapetStyles), node["parapetStyle"].FirstChild.Value); roofDesign.parapetDesignWidth = float.Parse(node["parapetDesignWidth"].FirstChild.Value); roofDesign.parapetHeight = float.Parse(node["parapetHeight"].FirstChild.Value); roofDesign.parapetFrontDepth = float.Parse(node["parapetFrontDepth"].FirstChild.Value); roofDesign.parapetBackDepth = float.Parse(node["parapetBackDepth"].FirstChild.Value); roofDesign.hasDormers = node["hasDormers"].FirstChild.Value == valueTrue; roofDesign.dormerWidth = float.Parse(node["dormerWidth"].FirstChild.Value); roofDesign.dormerHeight = float.Parse(node["dormerHeight"].FirstChild.Value); roofDesign.dormerRoofHeight = float.Parse(node["dormerRoofHeight"].FirstChild.Value); roofDesign.minimumDormerSpacing = float.Parse(node["minimumDormerSpacing"].FirstChild.Value); roofDesign.dormerHeightRatio = float.Parse(node["dormerHeightRatio"].FirstChild.Value); for(int i = 0; i < 8; i++) { roofDesign.textureValues[i] = int.Parse(node.SelectNodes("textures/texture")[i].FirstChild.Value); roofDesign.flipValues[i] = node.SelectNodes("flipvalues/flipvalue")[i].FirstChild.Value == valueTrue; } } }
//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 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()); }
/// <summary> /// Checks the Max UV values used in this model for each texture. /// </summary> /// <param name='data'> /// BuildR Data. /// </param> public void CheckMaxTextureUVs(BuildrData data) { Vector2[] subMeshUVOffsets = new Vector2[subMeshCount]; int[] subMeshIDs = new List<int>(subTriangles.Keys).ToArray(); int numberOfSubmeshIDs = subMeshIDs.Length; for (int sm = 0; sm < numberOfSubmeshIDs; sm++) { int subMeshID = subMeshIDs[sm]; if (subTriangles.ContainsKey(subMeshID)) { int[] submeshIndices = subTriangles[subMeshID].ToArray(); subMeshUVOffsets[sm] = Vector2.zero; foreach (int index in submeshIndices) { if (uv[index].x < subMeshUVOffsets[sm].x) subMeshUVOffsets[sm].x = uv[index].x; if (uv[index].y < subMeshUVOffsets[sm].y) subMeshUVOffsets[sm].y = uv[index].y; } List<int> UVsOffset = new List<int>(); foreach (int uvindex in subTriangles[subMeshID]) { if (!UVsOffset.Contains(uvindex)) { uv[uvindex] += -subMeshUVOffsets[sm];//offset the UV to ensure it isn't negative UVsOffset.Add(uvindex); } data.textures[subMeshID].CheckMaxUV(uv[uvindex]); } } else { Debug.Log("Mesh does not contain key for texture " + data.textures[subMeshID].name); } } }
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 InspectorGUI(BuildrEditMode editMode, BuildrData _data) { int helpWidth = 20; data = _data; Undo.RecordObject(data, "Facade Modified"); BuildrFacadeDesign[] facades = data.facades.ToArray(); int numberOfFacades = facades.Length; selectedFacade = Mathf.Clamp(selectedFacade, 0, numberOfFacades - 1); if (numberOfFacades == 0) { EditorGUILayout.HelpBox("There are no facade designs to show", MessageType.Info); return; } bool hasUnusedFacades = false; int unusedIndex = 0; //Check all facades have een used and warn if there are unused ones for(int i = 0; i < numberOfFacades; i++) { bool facadeUnused = true; foreach(BuildrVolume volume in data.plan.volumes) { if(volume.ContainsFacade(i)) { facadeUnused = false; break; } } if(facadeUnused) { hasUnusedFacades = true; unusedIndex = i; break; } } if (hasUnusedFacades) EditorGUILayout.HelpBox("There are facade designs that are not applied to your building and are unused.\nGo to the Building section to apply them to a facade.\nCheck facade design \""+facades[unusedIndex].name+"\"", MessageType.Warning); //Facade Selector EditorGUILayout.BeginVertical("box"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Facade Design:", GUILayout.Width(145)); string[] facadeNames = new string[numberOfFacades]; for (int f = 0; f < numberOfFacades; f++) facadeNames[f] = facades[f].name; selectedFacade = EditorGUILayout.Popup(selectedFacade, facadeNames); BuildrFacadeDesign bFacade = facades[selectedFacade]; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Add", GUILayout.Width(60))) { data.facades.Add(new BuildrFacadeDesign("new facade " + numberOfFacades)); facades = data.facades.ToArray(); numberOfFacades++; selectedFacade = numberOfFacades - 1; } if (GUILayout.Button("Duplicate", GUILayout.Width(90))) { data.facades.Add(bFacade.Duplicate()); facades = data.facades.ToArray(); numberOfFacades++; selectedFacade = numberOfFacades - 1; } if (GUILayout.Button("Delete", GUILayout.Width(70))) { if (EditorUtility.DisplayDialog("Deleting Facade Design Entry", "Are you sure you want to delete this facade?", "Delete", "Cancel")) { data.RemoveFacadeDesign(bFacade); selectedFacade = 0; GUI.changed = true; return; } } if (GUILayout.Button("Import", GUILayout.Width(71))) { string xmlPath = EditorUtility.OpenFilePanel("Select the XML file...", "Assets/BuildR/Exported/", "xml"); if (xmlPath == "") return; BuildrXMLImporter.ImportFacades(xmlPath, _data); facades = _data.facades.ToArray(); selectedFacade = 0; GUI.changed = true; } if (GUILayout.Button("Export", GUILayout.Width(71))) { string xmlPath = EditorUtility.SaveFilePanel("Export as...", "Assets/BuildR/Exported/", _data.name + "_facadeLibrary", "xml"); if (xmlPath == "") return; BuildrXMLExporter.ExportFacades(xmlPath, _data); GUI.changed = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.EndVertical(); bFacade = facades[selectedFacade];//reassign bFacade.name = EditorGUILayout.TextField("Facade Name: ", bFacade.name); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Facade Design Type:", GUILayout.Width(145)); bFacade.type = (BuildrFacadeDesign.types)EditorGUILayout.EnumPopup(bFacade.type); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Design Type"; string helpBody = "This allows you to select the type of design you're using.\n" + "Simple - the facade openings will be uniform\n" + "Patterned - the facade openings will follow a pattern of defined dimensions and textures\n"; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); int numberOfTextures = data.textures.Count; string[] textureNames = new string[numberOfTextures]; for (int t = 0; t < numberOfTextures; t++) textureNames[t] = data.textures[t].name; bFacade.hasWindows = EditorGUILayout.Toggle("Facade Has Bays", bFacade.hasWindows); if (bFacade.hasWindows) { if (bFacade.type == BuildrFacadeDesign.types.simple) { BuildrBay bbay = bFacade.simpleBay; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Render Bay", GUILayout.Width(146)); bool renderBayBack = EditorGUILayout.Toggle(bbay.renderBack); if (renderBayBack != bbay.renderBack) { bbay.renderBack = renderBayBack; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Bay Model", GUILayout.Width(146)); bbay.bayModel = (GameObject)EditorGUILayout.ObjectField(bbay.bayModel, typeof(GameObject), false); if (GUILayout.Button("Clear", GUILayout.Width(70))) bbay.bayModel = null; EditorGUILayout.EndHorizontal(); float bbayopeningWidth = Mathf.Max(EditorGUILayout.FloatField("Opening Width", bbay.openingWidth), 0); if (bbayopeningWidth != bbay.openingWidth) { bbay.openingWidth = bbayopeningWidth; } float bbayopeningHeight = Mathf.Max(EditorGUILayout.FloatField("Opening Height", bbay.openingHeight), 0); if (bbayopeningHeight > data.floorHeight) bbayopeningHeight = data.floorHeight; if (bbayopeningHeight != bbay.openingHeight) { bbay.openingHeight = bbayopeningHeight; } float bbayminimumBayWidth = Mathf.Max(EditorGUILayout.FloatField("Min. Spacing", bbay.minimumBayWidth), 0); if (bbayminimumBayWidth != bbay.minimumBayWidth) { bbay.minimumBayWidth = bbayminimumBayWidth; } float bbayopeningWidthRatio = EditorGUILayout.Slider("Horizontal Space Ratio", bbay.openingWidthRatio, 0, 1); if(bbayopeningWidthRatio != bbay.openingWidthRatio) { bbay.openingWidthRatio = bbayopeningWidthRatio; } float bbayopeningHeightRatio = EditorGUILayout.Slider("Vertical Space Ratio", bbay.openingHeightRatio, 0, 1); if (bbayopeningHeightRatio != bbay.openingHeightRatio) { bbay.openingHeightRatio = bbayopeningHeightRatio; } float bbayopeningDepth = EditorGUILayout.Slider("Opening Depth", bbay.openingDepth, -depth, depth); if (bbayopeningDepth != bbay.openingDepth) { bbay.openingDepth = bbayopeningDepth; } float bbaycolumnDepth = EditorGUILayout.Slider("Column Depth", bbay.columnDepth, -depth, depth); if (bbaycolumnDepth != bbay.columnDepth) { bbay.columnDepth = bbaycolumnDepth; } float bbayrowDepth = EditorGUILayout.Slider("Row Depth", bbay.rowDepth, -depth, depth); if (bbayrowDepth != bbay.rowDepth) { bbay.rowDepth = bbayrowDepth; } float bbaycrossDepth = EditorGUILayout.Slider("Cross Depth", bbay.crossDepth, -depth, depth); if (bbaycrossDepth != bbay.crossDepth) { bbay.crossDepth = bbaycrossDepth; } int numberOfTextureSlots = bbay.numberOfTextures; string[] titles = new string[numberOfTextureSlots]; for (int bft = 0; bft < numberOfTextureSlots; bft++) { titles[bft] = ((BuildrBay.TextureNames)(bft)).ToString(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Wall Surface:", GUILayout.Width(75)); editTextureOnFacade = EditorGUILayout.Popup(editTextureOnFacade, titles); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Wall Texture:", GUILayout.Width(75)); int newFacadeTextureID = EditorGUILayout.Popup(bbay.textureValues[editTextureOnFacade], textureNames); if (newFacadeTextureID != bbay.textureValues[editTextureOnFacade]) { bbay.textureValues[editTextureOnFacade] = newFacadeTextureID; } EditorGUILayout.EndHorizontal(); BuildrTexture bTexture = data.textures[bbay.textureValues[editTextureOnFacade]]; Texture2D texture = bTexture.texture; EditorGUILayout.BeginHorizontal(); if (texture != null) GUILayout.Label(texture, GUILayout.Width(100), GUILayout.Height(100)); else EditorGUILayout.HelpBox("No texture assigned for '" + textureNames[bbay.textureValues[editTextureOnFacade]] + "', assign one in the Textures menu above", MessageType.Warning); bbay.flipValues[editTextureOnFacade] = EditorGUILayout.Toggle("Flip 90\u00B0", bbay.flipValues[editTextureOnFacade]); EditorGUILayout.EndHorizontal(); } else { //Patterned design GUI int numberOfBays = bFacade.bayPattern.Count; int numberOfBayDesigns = data.bays.Count; EditorGUILayout.BeginHorizontal(); GUILayout.BeginHorizontal("box"); if (GUILayout.Button("Add New Bay Design")) { BuildrBay newBay = new BuildrBay("new bay design " + (numberOfBayDesigns + 1)); data.bays.Add(newBay); bFacade.bayPattern.Add(numberOfBayDesigns); numberOfBays++; selectedBayPatternIndex = numberOfBays - 1; numberOfBayDesigns++; GUI.changed = true; } EditorGUILayout.EndHorizontal(); if (numberOfBays == 0 || data.bays.Count == 0) { EditorGUILayout.HelpBox("There are no bay designs to show", MessageType.Info); // EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); return; } BuildrBay[] bays = new BuildrBay[numberOfBays]; for (int i = 0; i < numberOfBays; i++) { bays[i] = data.bays[bFacade.bayPattern[i]]; } selectedBayPatternIndex = Mathf.Clamp(selectedBayPatternIndex, 0, numberOfBays - 1); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.BeginHorizontal("box"); string[] bayDesignNames = new string[data.bays.Count]; for (int i = 0; i < numberOfBayDesigns; i++) { bayDesignNames[i] = data.bays[i].name; } selectedBayDesign = EditorGUILayout.Popup(selectedBayDesign, bayDesignNames); if (GUILayout.Button("Add Selected")) { bFacade.bayPattern.Add(selectedBayDesign); GUI.changed = true; } if (GUILayout.Button("Duplicate Selected")) { BuildrBay newBay = data.bays[selectedBayDesign].Duplicate(); data.bays.Add(newBay); bFacade.bayPattern.Add(numberOfBayDesigns); numberOfBays++; selectedBayPatternIndex = numberOfBays - 1; numberOfBayDesigns++; GUI.changed = true; } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal(); GUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Bay Design Order:"); var scrollbarHStyle = new GUIStyle(GUI.skin.horizontalScrollbar); var scrollbarBackStyle = new GUIStyle(); var scrollbarVStyle = new GUIStyle(GUI.skin.verticalScrollbar); scrollbarVStyle.fixedHeight = scrollbarVStyle.fixedWidth = 0; bayDesignPatternScrollView = EditorGUILayout.BeginScrollView(bayDesignPatternScrollView, false, false, scrollbarHStyle, scrollbarVStyle, scrollbarBackStyle, GUILayout.Height(40)); List<string> bayNames = new List<string>(); foreach (int bayIndex in bFacade.bayPattern) { bayNames.Add(data.bays[bayIndex].name); } selectedBayPatternIndex = GUILayout.Toolbar(selectedBayPatternIndex, bayNames.ToArray()); EditorGUILayout.EndScrollView(); BuildrBay bBay = data.bays[bFacade.bayPattern[selectedBayPatternIndex]]; EditorGUILayout.BeginHorizontal(); EditorGUI.BeginDisabledGroup(selectedBayPatternIndex == 0); if (GUILayout.Button("<<", GUILayout.Width(40))) { int bayDesignIndex = bFacade.bayPattern[selectedBayPatternIndex]; bFacade.bayPattern.RemoveAt(selectedBayPatternIndex); bFacade.bayPattern.Insert(selectedBayPatternIndex - 1, bayDesignIndex); selectedBayPatternIndex--; GUI.changed = true; } EditorGUI.EndDisabledGroup(); if (GUILayout.Button("Remove")) { bFacade.bayPattern.RemoveAt(selectedBayPatternIndex); GUI.changed = true; } if (GUILayout.Button("Delete")) { if (EditorUtility.DisplayDialog("Deleting Bay Design Entry", "Are you sure you want to delete this bay?", "Delete", "Cancel")) { int deletedBayDesignIndex = bFacade.bayPattern[selectedBayPatternIndex]; Debug.Log("Delete Bay Design " + deletedBayDesignIndex); Debug.Log("Delete Bay Design " + data.bays[deletedBayDesignIndex].name); data.bays.RemoveAt(deletedBayDesignIndex); int numberOfFacadeDesigns = data.facades.Count; for (int i = 0; i < numberOfFacadeDesigns; i++) { BuildrFacadeDesign checkFacade = data.facades[i]; int bayPatternSize = checkFacade.bayPattern.Count; for (int j = 0; j < bayPatternSize; j++) { if (checkFacade.bayPattern[j] == deletedBayDesignIndex) { checkFacade.bayPattern.RemoveAt(j); j--; bayPatternSize--; } else if (checkFacade.bayPattern[j] > deletedBayDesignIndex) checkFacade.bayPattern[j]--; } } GUI.changed = true; } } EditorGUI.BeginDisabledGroup(selectedBayPatternIndex == numberOfBays - 1); if (GUILayout.Button(">>", GUILayout.Width(40))) { int bayDesignIndex = bFacade.bayPattern[selectedBayPatternIndex]; bFacade.bayPattern.Insert(selectedBayPatternIndex + 2, bayDesignIndex); bFacade.bayPattern.RemoveAt(selectedBayPatternIndex); selectedBayPatternIndex++; GUI.changed = true; } EditorGUI.EndDisabledGroup(); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); GUILayout.Space(10); EditorGUILayout.BeginVertical("box"); bBay.name = EditorGUILayout.TextField("Name: ", bBay.name); bool bBayisOpening = EditorGUILayout.Toggle("Has Opening", bBay.isOpening); if(bBayisOpening != bBay.isOpening) { bBay.isOpening = bBayisOpening; } bool bBayRenderBack = EditorGUILayout.Toggle("Render Back", bBay.renderBack); if (bBayRenderBack != bBay.renderBack) { bBay.renderBack = bBayRenderBack; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Bay Model", GUILayout.Width(146)); bBay.bayModel = (GameObject)EditorGUILayout.ObjectField(bBay.bayModel, typeof(GameObject), false); if (GUILayout.Button("Clear", GUILayout.Width(70))) bBay.bayModel = null; EditorGUILayout.EndHorizontal(); float bBayopeningWidth = Mathf.Max(EditorGUILayout.FloatField("Opening Width", bBay.openingWidth), 0); if (bBayopeningWidth != bBay.openingWidth) { bBay.openingWidth = bBayopeningWidth; } float bBayopeningHeight = Mathf.Clamp(EditorGUILayout.FloatField("Opening Height", bBay.openingHeight), 0, data.floorHeight); if (bBayopeningHeight != bBay.openingHeight) { bBay.openingHeight = bBayopeningHeight; } float bBayminimumBayWidth = Mathf.Max(EditorGUILayout.FloatField("Bay Spacing Width", bBay.minimumBayWidth), 0); if (bBayminimumBayWidth != bBay.minimumBayWidth) { bBay.minimumBayWidth = bBayminimumBayWidth; } float bBayopeningWidthRatio = EditorGUILayout.Slider("Horizontal Space Ratio", bBay.openingWidthRatio, 0, 1); if (bBayopeningWidthRatio != bBay.openingWidthRatio) { bBay.openingWidthRatio = bBayopeningWidthRatio; } float bBayopeningHeightRatio = EditorGUILayout.Slider("Vertical Space Ratio", bBay.openingHeightRatio, 0, 1); if (bBayopeningHeightRatio != bBay.openingHeightRatio) { bBay.openingHeightRatio = bBayopeningHeightRatio; } float bBayopeningDepth = EditorGUILayout.Slider("Opening Depth", bBay.openingDepth, -depth, depth); if (bBayopeningDepth != bBay.openingDepth) { bBay.openingDepth = bBayopeningDepth; } float bBaycolumnDepth = EditorGUILayout.Slider("Column depth", bBay.columnDepth, -depth, depth); if (bBaycolumnDepth != bBay.columnDepth) { bBay.columnDepth = bBaycolumnDepth; } float bBayrowDepth = EditorGUILayout.Slider("Row depth", bBay.rowDepth, -depth, depth); if (bBayrowDepth != bBay.rowDepth) { bBay.rowDepth = bBayrowDepth; } float bBaycrossDepth = EditorGUILayout.Slider("Cross depth", bBay.crossDepth, -depth, depth); if (bBaycrossDepth != bBay.crossDepth) { bBay.crossDepth = bBaycrossDepth; } //BAY TEXTURES int numberOfTextureSlots = bBay.numberOfTextures; string[] titles = new string[numberOfTextureSlots]; for (int bft = 0; bft < numberOfTextureSlots; bft++) { titles[bft] = ((BuildrBay.TextureNames)(bft)).ToString(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Surface:", GUILayout.Width(75)); editTextureOnFacade = EditorGUILayout.Popup(editTextureOnFacade, titles); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Texture:", GUILayout.Width(75)); bBay.textureValues[editTextureOnFacade] = EditorGUILayout.Popup(bBay.textureValues[editTextureOnFacade], textureNames); EditorGUILayout.EndHorizontal(); BuildrTexture bTexture = data.textures[bBay.textureValues[editTextureOnFacade]]; Texture2D texture = bTexture.texture; EditorGUILayout.BeginHorizontal(); if (texture != null) GUILayout.Label(texture, GUILayout.Width(100), GUILayout.Height(100)); else EditorGUILayout.HelpBox("No texture assigned for '" + textureNames[bBay.textureValues[editTextureOnFacade]] + "', assign one in the Textures menu above", MessageType.Warning); bFacade.flipValues[editTextureOnFacade] = EditorGUILayout.Toggle("Flip 90\u00B0", bFacade.flipValues[editTextureOnFacade]); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } } else { editTextureOnFacade = 7; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Surface:", GUILayout.Width(75)); EditorGUILayout.LabelField("Wall"); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Texture:", GUILayout.Width(75)); int newFacadeTexture = EditorGUILayout.Popup(bFacade.simpleBay.textureValues[editTextureOnFacade], textureNames); if (newFacadeTexture != bFacade.simpleBay.textureValues[editTextureOnFacade]) { bFacade.simpleBay.textureValues[editTextureOnFacade] = newFacadeTexture; } EditorGUILayout.EndHorizontal(); BuildrTexture bTexture = data.textures[bFacade.simpleBay.textureValues[editTextureOnFacade]]; Texture2D texture = bTexture.texture; EditorGUILayout.BeginHorizontal(); if (texture != null) GUILayout.Label(texture, GUILayout.Width(100), GUILayout.Height(100)); else EditorGUILayout.HelpBox("No texture assigned for '" + textureNames[bFacade.simpleBay.textureValues[editTextureOnFacade]] + "', assign one in the Textures menu above", MessageType.Warning); bFacade.simpleBay.flipValues[editTextureOnFacade] = EditorGUILayout.Toggle("Flip 90\u00B0", bFacade.simpleBay.flipValues[editTextureOnFacade]); EditorGUILayout.EndHorizontal(); } }
public static void SceneGUI(BuildrEditMode editMode, BuildrData data, bool shouldSnap, float handleSize) { if(editMode.fullMesh==null) return; Rect HUDRect = new Rect(0,0,300,300); Handles.BeginGUI(); GUILayout.BeginArea(HUDRect); EditorGUILayout.LabelField("Buildr"); EditorGUILayout.LabelField("Vertices: "+editMode.fullMesh.vertexCount); EditorGUILayout.LabelField("Triangles "+editMode.fullMesh.triangleCount/3); GUILayout.EndArea(); Handles.EndGUI(); bool isLegal = !(data.plan.illegalPoints.Length > 0); if (isLegal) isLegal = editMode.transform.localScale == Vector3.one; if(isLegal) return; int numberOfFacades = data.facades.Count; int numberOfRoofs = data.roofs.Count; int numberOfTextures = data.textures.Count; if (numberOfFacades == 0 || numberOfRoofs == 0 || numberOfTextures == 0) return; Vector3 position = editMode.transform.position; BuildrPlan area = data.plan; int numberOfVolumes = area.numberOfVolumes; int facadeCounter = 0; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = data.plan.volumes[s]; int volumeSize = volume.Count; Vector3 floorCentre = Vector3.zero; Handles.color = Color.red; for (int p = 0; p < volumeSize; p++) { int point = volume.points[p]; Vector3 pointPos = area.points[point].vector3; floorCentre += pointPos; List<Vector3> verts = new List<Vector3>(); int indexB = (p < volumeSize - 1) ? p + 1 : 0; Vector3 volumeHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight); verts.Add(pointPos + position); verts.Add(area.points[volume.points[indexB]].vector3 + position); verts.Add(verts[1] + volumeHeight); verts.Add(verts[0] + volumeHeight); Handles.DrawSolidRectangleWithOutline(verts.ToArray(), new Color(1,0,0,0.2f), Color.red); Handles.DrawLine(verts[2], verts[3]); facadeCounter++; } } }
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 InspectorGUI(BuildrEditMode editMode, BuildrData data) { if (data.plan==null || data.plan.numberOfVolumes == 0) { EditorGUILayout.HelpBox("There are no defined volumes, go to Floorplan and define one", MessageType.Error); return; } const int guiWidth = 400; const int textWidth = 348; const int toggleWidth = 25; const int helpWidth = 20; CURRENT_TRANSFORM = editMode.transform; EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Filename", GUILayout.Width(225)); data.exportFilename = EditorGUILayout.TextField(data.exportFilename, GUILayout.Width(175)); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Filetype", GUILayout.Width(350)); data.filetype = (BuildrData.filetypes)EditorGUILayout.EnumPopup(data.filetype, GUILayout.Width(50)); switch (data.filetype) { case BuildrData.filetypes.obj: FILE_EXTENTION = ".obj"; break; case BuildrData.filetypes.fbx: FILE_EXTENTION = ".fbx"; break; } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Export Full Mesh", GUILayout.Width(textWidth)); data.fullmesh = EditorGUILayout.Toggle(data.fullmesh, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Export Full Mesh"; string helpBody = "Select this checkbox if you want your export the full detail model."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Place Exported Model Into Scene", GUILayout.Width(textWidth)); data.placeIntoScene = EditorGUILayout.Toggle(data.placeIntoScene, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Place Exported Model Into Scene"; string helpBody = "Select this checkbox if you want your exported models to be copied into your scene." + "\nThese will be positioned correctly and will include colliders and LOD models if you opt to export them also."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Copy Textures into Export Folder", GUILayout.Width(textWidth)); data.copyTexturesIntoExportFolder = EditorGUILayout.Toggle(data.copyTexturesIntoExportFolder, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Copy Textures into Export Folder"; string helpBody = "Check this box if you want to copy the textures you are using into the export folder." + "\nThis is useful if you plan to use the exported model elsewhere. Having the model and the textures in one folder will allow you to move this model with ease."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Export Collider"); data.generateCollider = (BuildrData.ColliderGenerationModes)EditorGUILayout.EnumPopup(data.generateCollider, GUILayout.Width(80)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Export Collider Mesh"; string helpBody = "Check this box if you wish to generate a collider mesh for your model." + "\nThis will generate a mesh to be used with colliders."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Export as Prefab", GUILayout.Width(textWidth)); data.createPrefabOnExport = EditorGUILayout.Toggle(data.createPrefabOnExport, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Export as Prefab"; string helpBody = "Select this if you wish to create a prefab of your model." + "\nThis is recommended if you're exporting a collider so they will get packaged together."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Export a Low Detail Version", GUILayout.Width(textWidth)); data.exportLowLOD = EditorGUILayout.Toggle(data.exportLowLOD, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - Export a Low Detail Version"; string helpBody = "Check this box to export a simplified model of your building." + "\nIdeal to use as a low level of detail version of your model." + "\nGeometry will be significantly reduced." + "\nFacades will flat and exported as a single atlased texture" + "\nThe model will use 1 draw call and will have around 10% of the triangles and verticies"; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Export with tangents", GUILayout.Width(textWidth)); data.includeTangents = EditorGUILayout.Toggle(data.includeTangents, GUILayout.Width(toggleWidth)); if (GUILayout.Button("?", GUILayout.Width(helpWidth))) { string helpTitle = "Help - with tangents"; string helpBody = "Export the models with calculated tangents." + "\nSome shaders require tangents to be calculated on the model." + "\nUnity will do this automatically on all imported meshes so it's not neccessary here." + "/nBut you might want them if you're taking them to another program."; EditorUtility.DisplayDialog(helpTitle, helpBody, "close"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); bool unreadableTextures = false; bool nullTextures = false; string unreadableTextureList = ""; foreach (BuildrTexture bTexture in data.textures)//check texture readablility { if(bTexture.type == BuildrTexture.Types.Substance) continue;//substances are preset in BuildrTexture class if (bTexture.texture != null) { string texturePath = AssetDatabase.GetAssetPath(bTexture.texture); TextureImporter textureImporter = (TextureImporter)AssetImporter.GetAtPath(texturePath); if (!textureImporter.isReadable) { unreadableTextures = true; if (unreadableTextureList.Length > 0) unreadableTextureList += ", "; unreadableTextureList += "'" + bTexture.name + "'"; } } else { nullTextures = true; } } if (unreadableTextures) { EditorGUILayout.HelpBox("Unreadable Texture Error." + "\nThe following textures you are useing are not readable." + "\n" + unreadableTextureList + "." + "\nPlease select the readable checkbox under advanced texture settings." + "\nOr move this texture to the BuildR texture folder and reimport.", MessageType.Error); } if (nullTextures) { EditorGUILayout.HelpBox("Null Texture Error" + "\nSome of the textures have not been set" + "\nEnsure you are not using null textures to proceed.", MessageType.Error); } bool usingSubstances = false; foreach(BuildrTexture bTexture in data.textures) { if(bTexture.type == BuildrTexture.Types.Substance) { usingSubstances = true; break; } } if (usingSubstances) { EditorGUILayout.HelpBox("Model uses Substance textures." + "\nExporting model to " + data.filetype + " will lose references to this texture and it will be rendered white.", MessageType.Warning); } EditorGUI.BeginDisabledGroup(unreadableTextures || nullTextures); if (GUILayout.Button("Export", GUILayout.Width(guiWidth), GUILayout.Height(40))) { ExportModel(data); } if (GUILayout.Button("Export Data to XML", GUILayout.Width(guiWidth))) { BuildrXMLExporter.Export(ROOT_FOLDER + data.exportFilename + "/", data.exportFilename, data); AssetDatabase.Refresh(); } EditorGUI.EndDisabledGroup(); CURRENT_TRANSFORM = null; }
/// <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();*/ }
//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; }
private static void Export(BuildrData data, Mesh exportMesh, ExportMaterial[] exportTextures) { Export(data.exportFilename, ROOT_FOLDER + data.exportFilename + "/", data, exportMesh, exportTextures); }
public static void SceneGUI(BuildrEditMode editMode, BuildrData data, bool shouldSnap, float handleSize) { if (editMode.fullMesh == null) { return; } Rect HUDRect = new Rect(0, 0, 300, 300); Handles.BeginGUI(); GUILayout.BeginArea(HUDRect); EditorGUILayout.LabelField("Buildr"); EditorGUILayout.LabelField("Vertices: " + editMode.fullMesh.vertexCount); EditorGUILayout.LabelField("Triangles " + editMode.fullMesh.triangleCount / 3); GUILayout.EndArea(); Handles.EndGUI(); bool isLegal = !(data.plan.illegalPoints.Length > 0); if (isLegal) { isLegal = editMode.transform.localScale == Vector3.one; } if (isLegal) { return; } int numberOfFacades = data.facades.Count; int numberOfRoofs = data.roofs.Count; int numberOfTextures = data.textures.Count; if (numberOfFacades == 0 || numberOfRoofs == 0 || numberOfTextures == 0) { return; } Vector3 position = editMode.transform.position; BuildrPlan area = data.plan; int numberOfVolumes = area.numberOfVolumes; int facadeCounter = 0; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = data.plan.volumes[s]; int volumeSize = volume.Count; Vector3 floorCentre = Vector3.zero; Handles.color = Color.red; for (int p = 0; p < volumeSize; p++) { int point = volume.points[p]; Vector3 pointPos = area.points[point].vector3; floorCentre += pointPos; List <Vector3> verts = new List <Vector3>(); int indexB = (p < volumeSize - 1) ? p + 1 : 0; Vector3 volumeHeight = Vector3.up * (volume.numberOfFloors * data.floorHeight); verts.Add(pointPos + position); verts.Add(area.points[volume.points[indexB]].vector3 + position); verts.Add(verts[1] + volumeHeight); verts.Add(verts[0] + volumeHeight); Handles.DrawSolidRectangleWithOutline(verts.ToArray(), new Color(1, 0, 0, 0.2f), Color.red); Handles.DrawLine(verts[2], verts[3]); facadeCounter++; } } }
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 InspectorGUI(BuildrEditMode editMode, BuildrData _data) { data = _data; Undo.RecordObject(data, "Interior Modified"); 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; EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Render Interior of Building"); bool renderInterior = EditorGUILayout.Toggle(_data.renderInteriors, GUILayout.Width(15)); if (renderInterior != _data.renderInteriors) { _data.renderInteriors = renderInterior; } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(GUILayout.Width(400)); EditorGUILayout.LabelField("Cull All Building Bays"); bool cullBays = EditorGUILayout.Toggle(_data.cullBays, GUILayout.Width(15)); if (cullBays != _data.cullBays) { _data.cullBays = cullBays; } EditorGUILayout.EndHorizontal(); //Floor Height EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Floor height", GUILayout.Width(200)); float newFloorHeight = EditorGUILayout.FloatField(data.floorHeight, GUILayout.Width(50)); if (newFloorHeight != data.floorHeight) { data.floorHeight = newFloorHeight; } EditorGUILayout.LabelField("metres", GUILayout.Width(50)); EditorGUILayout.EndHorizontal(); EditorGUI.BeginDisabledGroup(!renderInterior); //Ceiling Height EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Interior Ceiling Height", GUILayout.Width(200)); float newCeilingHeight = EditorGUILayout.Slider(data.interiorCeilingHeight, 0, 1); if (newCeilingHeight != data.interiorCeilingHeight) { data.interiorCeilingHeight = newCeilingHeight; } EditorGUILayout.EndHorizontal(); BuildrPlan plan = data.plan; int numberOfVolumes = plan.numberOfVolumes; int[] volumeSeletionsList = new int[numberOfVolumes]; string[] volumeSeletionsStringList = new string[numberOfVolumes]; for (int s = 0; s < numberOfVolumes; s++) { volumeSeletionsStringList[s] = ("volume " + s); volumeSeletionsList[s] = (s); } selectedVolumeIndex = EditorGUILayout.IntPopup("Selected Volume", selectedVolumeIndex, volumeSeletionsStringList, volumeSeletionsList, GUILayout.Width(400)); BuildrVolume volume = plan.volumes[selectedVolumeIndex]; //Stairs EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Generate Stairs", GUILayout.Width(110)); volume.generateStairs = EditorGUILayout.Toggle(volume.generateStairs); EditorGUILayout.EndHorizontal(); if(plan.cores.Count == 0) EditorGUILayout.HelpBox("There are no building cores defined. Go to floorplan to define one so you can generate a stairwell", MessageType.Error); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Stair Width", GUILayout.Width(110)); volume.staircaseWidth = EditorGUILayout.Slider(volume.staircaseWidth, 0.5f, 5.0f); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Minimum Step Riser Value", GUILayout.Width(150)); volume.stepHeight = EditorGUILayout.Slider(volume.stepHeight, 0.05f, 0.5f); EditorGUILayout.EndHorizontal(); EditorGUI.BeginDisabledGroup(!volume.generateStairs); EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Stairwell Textures"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Wall Texture", GUILayout.Width(120)); volume.stairwellWallTexture = EditorGUILayout.Popup(volume.stairwellWallTexture, textureNames); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Floor Texture", GUILayout.Width(120)); volume.stairwellFloorTexture = EditorGUILayout.Popup(volume.stairwellFloorTexture, textureNames); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Ceiling Texture", GUILayout.Width(120)); volume.stairwellCeilingTexture = EditorGUILayout.Popup(volume.stairwellCeilingTexture, textureNames); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Step Texture", GUILayout.Width(120)); volume.stairwellStepTexture = EditorGUILayout.Popup(volume.stairwellStepTexture, textureNames); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUI.EndDisabledGroup(); //Basement floors EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Basement Floors", GUILayout.Width(110)); EditorGUILayout.LabelField(volume.numberOfBasementFloors.ToString("F0"), GUILayout.Width(40)); EditorGUI.BeginDisabledGroup(volume.numberOfBasementFloors < 1); if (GUILayout.Button("-")) volume.numberOfBasementFloors--; EditorGUI.EndDisabledGroup(); if (GUILayout.Button("+")) volume.numberOfBasementFloors++; EditorGUILayout.EndHorizontal(); int numberOfFloors = volume.numberOfFloors; int numberOfBasementFloors = volume.numberOfBasementFloors; int totalNumberOfFloors = numberOfBasementFloors + numberOfFloors; int[] floorSeletionsList = new int[totalNumberOfFloors]; string[] floorSeletionsStringList = new string[totalNumberOfFloors]; for (int f = -numberOfBasementFloors; f < numberOfFloors; f++) { int index = f + numberOfBasementFloors; if(f>0) floorSeletionsStringList[index] = "floor " + f; else if(f<0) floorSeletionsStringList[index] = "basement " + -f; else floorSeletionsStringList[index] = "ground floor "; floorSeletionsList[index] = (f); } selectedFloor = EditorGUILayout.IntPopup("Selected Floor", selectedFloor, floorSeletionsStringList, floorSeletionsList, GUILayout.Width(400)); EditorGUILayout.LabelField("Interior Textures"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Floor Texture", GUILayout.Width(120)); volume.FloorTexture(selectedFloor, EditorGUILayout.Popup(volume.FloorTexture(selectedFloor), textureNames)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Wall Texture", GUILayout.Width(120)); volume.WallTexture(selectedFloor, EditorGUILayout.Popup(volume.WallTexture(selectedFloor), textureNames)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Ceiling Texture", GUILayout.Width(120)); volume.CeilingTexture(selectedFloor, EditorGUILayout.Popup(volume.CeilingTexture(selectedFloor), textureNames)); EditorGUILayout.EndHorizontal(); if(GUILayout.Button("Use Values for All Floors")) { int useFloorTextureIndex = volume.FloorTexture(selectedFloor); int useWallTextureIndex = volume.WallTexture(selectedFloor); int useCeilingTextureIndex = volume.CeilingTexture(selectedFloor); for(int f = 0; f < numberOfFloors; f++) { volume.FloorTexture(f, useFloorTextureIndex); volume.WallTexture(f, useWallTextureIndex); volume.CeilingTexture(f, useCeilingTextureIndex); } } if (GUILayout.Button("Use Values for Entire Building")) { int useFloorTextureIndex = volume.FloorTexture(selectedFloor); int useWallTextureIndex = volume.WallTexture(selectedFloor); int useCeilingTextureIndex = volume.CeilingTexture(selectedFloor); for (int v = 0; v < numberOfVolumes; v++) { BuildrVolume thisvolume = plan.volumes[v]; int numberOfFloorsToModifiy = thisvolume.numberOfFloors; for (int f = 0; f < numberOfFloorsToModifiy; f++) { thisvolume.FloorTexture(f, useFloorTextureIndex); thisvolume.WallTexture(f, useWallTextureIndex); thisvolume.CeilingTexture(f, useCeilingTextureIndex); } } } EditorGUI.EndDisabledGroup(); }
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 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 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 static void Build(DynamicMeshGenericMultiMaterialMesh _mesh, BuildrData _data) { Build(_mesh, _data, false); }
public static void SceneGUI(BuildrEditMode _editMode, BuildrData _data, bool shouldSnap, float handleSize) { Vector3 camDirection = Camera.current.transform.forward; Vector3 camPosition = Camera.current.transform.position; Vector3 position = _editMode.transform.position; data = _data; BuildrPlan plan = data.plan; BuildrVolume selectedVolume = plan.volumes[selectedVolumeIndex]; int selectedVolumeSize = selectedVolume.Count; Handles.color = Color.white; int facadeCounter = 0; GUIStyle whiteLabelStyle = new GUIStyle(); whiteLabelStyle.normal.textColor = Color.white; whiteLabelStyle.alignment = TextAnchor.MiddleCenter; whiteLabelStyle.fixedWidth = 75.0f; int numberOfVolumes = plan.numberOfVolumes; for (int s = 0; s < numberOfVolumes; s++) { BuildrVolume volume = data.plan.volumes[s]; int volumeSize = volume.Count; Vector3 floorCentre = Vector3.zero; for (int p = 0; p < volumeSize; p++) { int point = volume.points[p]; Vector3 pointPos = plan.points[point].vector3; floorCentre += pointPos; } floorCentre /= volumeSize; if(s==selectedVolumeIndex) { whiteLabelStyle.normal.textColor = BuildrColours.RED; whiteLabelStyle.fontStyle = FontStyle.Bold; } else { whiteLabelStyle.normal.textColor = Color.white; whiteLabelStyle.fontStyle = FontStyle.Normal; } //Volume height/floor number slider Vector3 volumeHeightDir = Vector3.up * (volume.numberOfFloors * data.floorHeight); Vector3 volumePosition = floorCentre + position + volumeHeightDir; Handles.color = Color.white; if (Vector3.Dot(camDirection, volumePosition - camPosition) > 0)//only display label when facade is facing camera { Handles.Label(volumePosition + (Vector3.up * (handleSize * 0.1f)), "volume " + s, whiteLabelStyle); } } whiteLabelStyle.normal.textColor = Color.white; whiteLabelStyle.fontStyle = FontStyle.Normal; for (int p = 0; p < selectedVolumeSize; p++) { int point = selectedVolume.points[p]; Vector3 pointPos = plan.points[point].vector3; List<Vector3> verts = new List<Vector3>(); int indexB = (p < selectedVolumeSize - 1) ? p + 1 : 0; Vector3 floorHeightVector = Vector3.up * (selectedFloor * data.floorHeight); Vector3 ceilingHeightVector = Vector3.up * ((selectedFloor+1) * data.floorHeight); Vector3 p0 = pointPos + position; Vector3 p1 = plan.points[selectedVolume.points[indexB]].vector3 + position; verts.Add(pointPos + position); verts.Add(plan.points[selectedVolume.points[indexB]].vector3 + position); verts.Add(verts[1] + floorHeightVector); verts.Add(verts[0] + floorHeightVector); Handles.color = BuildrColours.RED; Handles.DrawLine(p0 + floorHeightVector, p1 + floorHeightVector); Handles.DrawLine(p0 + ceilingHeightVector, p1 + ceilingHeightVector); Handles.DrawLine(p0 + floorHeightVector, p0 + ceilingHeightVector); if (_editMode.showFacadeMarkers) { Handles.color = Color.white; Vector3 facadeDirection = Vector3.Cross((verts[0] - verts[1]), Vector3.up); Vector3 centerPos = (verts[0] + verts[1]) * 0.5f; bool camVisible = Vector3.Dot(camDirection, centerPos - camPosition) > 0; bool facadeVisible = Vector3.Dot(camDirection, facadeDirection) < 0; if (camVisible && facadeVisible)//only display label when facade is facing camera and is in camera view { Vector3 labelPos = centerPos + facadeDirection.normalized; Handles.Label(labelPos, "facade " + facadeCounter, whiteLabelStyle); Handles.DrawLine(centerPos, labelPos); } } facadeCounter++; } }
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 SceneGUI(BuildrEditMode editMode, BuildrData data, bool shouldSnap, float handleSize) { }