public static GenerationOutput CreateMeshOutput() { GenerationOutput output = new GenerationOutput(); output.mesh = new Mesh(); return(output); }
public static GenerationOutput CreateRawOutput() { GenerationOutput output = new GenerationOutput(); output.raw = new RawMeshData(); return(output); }
public ReportProcessor() { _dataAccessor = new DataAccessor(); _lsFinalGenOutput = new List <GeneratorOutput>(); _lsFinalMaxGenOutput = new List <DayOutput>(); _existingGenOutput = new GenerationOutput(); }
/// <summary> /// Processing the incoming datas /// </summary> /// <param name="generationReport">The generation report.</param> /// <returns></returns> private async Task <GenerationOutput> FileProcessing(Input.GenerationReport generationReport) { if (generationReport == null) { return(null); } _iLog.Info("File processing started."); var generationOutput = new GenerationOutput(); var actualHeatRates = new List <ActualHeatRates>(); var generators = new List <Generator>(); var days = new List <Day>(); try { foreach (var item in generationReport.Wind?.WindGenerators) { //Daily wind generation calculation adding it to generator collection generators.Add(await CalculateGenerationTotal(item.Generations, item.Name, item.Location)); } ; foreach (var item in generationReport.Coal?.CoalGenerators) { //Daily coal generation calculation and adding it to generator collection generators.Add(await CalculateGenerationTotal(item.Generations, item.Name, null)); //Coal max emission calculation and adding it to days collection. days.AddRange(await CalculateCoalMaxEmission(item)); //Coal actual heat rate calculation. actualHeatRates.Add(await CalculateActualHeatRate(item)); } ; foreach (var item in generationReport.Gas.GasGenerators) { //Daily gas generation calculation and adding it to generator collection generators.Add(await CalculateGenerationTotal(item.Generations, item.Name, null)); //Gas max emission calculation and adding it to days collection. days.AddRange(await CalculateGasMaxEmission(item)); } ; //Adding items to the GenerationOut object generationOutput.Totals = new Totals { Generators = new List <Generator>() }; generationOutput.Totals.Generators.AddRange(generators); generationOutput.MaxEmissionGenerators = new MaxEmissionGenerators { Days = new List <Day>() }; generationOutput.MaxEmissionGenerators.Days.AddRange(days); generationOutput.ActualHeatRates = new List <ActualHeatRates>(); generationOutput.ActualHeatRates.AddRange(actualHeatRates); } catch (Exception ex) { _iLog.Error($"Error in calculation. Error: {ex.Message}"); } return(generationOutput); }
private void SaveOutputInFile(GenerationOutput output, string outputPath) { var date = DateTime.Now; var outputPathName = $"{outputPath}{FileName}{date.ToShortDateString()}{date:HH.mm.ss}{Extension}"; _serializer.SerializeToFile <GenerationOutput>(output, outputPathName); }
public static GenerationOutput CreateCombinedOutput() { GenerationOutput output = new GenerationOutput(); output.mesh = new Mesh(); output.raw = new RawMeshData(); return(output); }
public void GenerateOutputFile(GenerationOutput finalGenerationOutput) { var outputFilePath = ConfigurationManager.AppSettings["OutputFilePath"]; var writer = new XmlSerializer(typeof(GenerationOutput)); using (var file = System.IO.File.Create(outputFilePath)) { writer.Serialize(file, finalGenerationOutput); } }
public GenerationReport(InputGenerationReport input) { _config = new ConfigurationBuilder().AddJsonFile("specflow.json").Build(); _xmlDataSourceProcessor = new XmlDataSourceProcessor(); _input = input; _generationOutput = new GenerationOutput(); _referenceData = new ReferenceData(); _calculatedTotalValue = new Totals(); _energyCalculator = new EnergyCalculator(); _maxEmissionGeneratorsDays = new List <DTOs.OutputGeneration.Day>(); _heatRates = new List <ActualHeatRates>(); _generationOutput = _xmlDataSourceProcessor.GetGenerationOutput(); }
public void GenerateOutputFile(GenerationReport incomingGeneratedReport) { FetchExistingGenerationOutput(); //Get Generation Report var finalOutput = new GenerationOutput() { totals = TotalGeneratorsOutputPerDay(incomingGeneratedReport), maxEmissionGenerators = MaxEmissionGeneratorPerDay(incomingGeneratedReport), actualHeatRates = GetActualHeatRates(incomingGeneratedReport.coal) }; _dataAccessor.GenerateOutputFile(finalOutput); }
private void UpdatePreview() { GenerationOutput output = GenerationOutput.CreateMeshOutput(); ChimneyGenerator.Generate(_chimney, output); _mesh = output.mesh; if (_materialList == null) { _materialList = new List <Material>(); } _materialList.Clear(); _materialList.AddRange(ChimneyGenerator.DYNAMIC_MESH.materials); }
/// <summary> /// Serializes the object to XML. /// </summary> /// <param name="result">The result.</param> /// <returns></returns> public static bool SerializeObjectToXml(GenerationOutput result) { bool isFileGotSerialize = false; try { XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenerationOutput)); StreamWriter streamWriter = new StreamWriter(_outputFilePath); xmlSerializer.Serialize(streamWriter, result); streamWriter.Dispose(); streamWriter.Close(); isFileGotSerialize = true; } catch (Exception) { throw; } return(isFileGotSerialize); }
private void UpdatePreview() { // if (_submeshLibrary == null) _submeshLibrary = new SubmeshLibrary(); // _submeshLibrary.Clear(); // _submeshLibrary.Add(_wallSection); // _wallSection.UpdatePreviewMesh(_submeshLibrary); // if (_materialList == null) _materialList = new List<Material>(); // _materialList.Clear(); // _materialList.AddRange(WallSectionGenerator.DYNAMIC_MESH.materials); // BuildRMesh dMesh = new BuildRMesh("wallsection preview"); GenerationOutput output = GenerationOutput.CreateMeshOutput(); WallSectionGenerator.Generate(_wallSection, output, previewMeshSize, false, 0.2f); _mesh = output.mesh; if (_materialList == null) { _materialList = new List <Material>(); } _materialList.Clear(); _materialList.AddRange(WallSectionGenerator.DYNAMIC_MESH.materials); }
public void Read() { System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-US"); System.Threading.Thread.CurrentThread.CurrentCulture = ci; string file_xml_generation = ConfigurationManager.AppSettings["GenerationReport"]; string file_xml_reference = ConfigurationManager.AppSettings["ReferenceData"]; string file_xml_write = ConfigurationManager.AppSettings["outputXml"]; ParseXmlReferenceData xmlRef = new ParseXmlReferenceData(file_xml_reference); ParseXmlGenerationReport xmlGeneration = new ParseXmlGenerationReport(); var xReport = xmlGeneration.Read(file_xml_generation); XmlOffshore offShore = new XmlOffshore(xReport); List <WindOffShore> offshoreList = offShore.Read(); XmlOnshore onshore = new XmlOnshore(xReport); List <WindOnshore> onshoreList = onshore.Read(); XmlGas gas = new XmlGas(xReport); Dictionary <int, List <Gas> > gasList = gas.Read(); //gas.GasTotalGeneration(); XmlCoal coal = new XmlCoal(xReport); Dictionary <int, List <Coal> > coalList = coal.Read(); //coal.CoalTotalGeneration(); Emission emission = new Emission(gasList, coalList); //emission.HighestEmissionByDate(); HeatRate ht = new HeatRate(coalList); //ht.ActualHeatRates(); GenerationOutput outputXml = new GenerationOutput(offShore, onshore, gas, coal, emission, ht); outputXml.XmlWrite(file_xml_write); }
public static void Generate(Chimney chimney, GenerationOutput output, SubmeshLibrary submeshLibrary = null) { RGEN.seed = chimney.seed; DYNAMIC_MESH.Clear(); if (submeshLibrary != null) { DYNAMIC_MESH.submeshLibrary.AddRange(submeshLibrary.SURFACES.ToArray()); //DYNAMIC_MESH.submeshLibrary.Inject(ref submeshLibrary); } else { DYNAMIC_MESH.submeshLibrary.Add(chimney); } submeshLibrary = DYNAMIC_MESH.submeshLibrary; //CASE Vector3 caseNoiseVector = new Vector3(chimney.noise.x * RGEN.OneRange(), chimney.noise.y * RGEN.OneRange(), chimney.noise.z * RGEN.OneRange()); Vector3 cs0 = new Vector3(-chimney.caseSize.x * 0.5f, 0, -chimney.caseSize.z * 0.5f); Vector3 cs1 = new Vector3(chimney.caseSize.x * 0.5f, 0, -chimney.caseSize.z * 0.5f); Vector3 cs2 = new Vector3(-chimney.caseSize.x * 0.5f, 0, chimney.caseSize.z * 0.5f); Vector3 cs3 = new Vector3(chimney.caseSize.x * 0.5f, 0, chimney.caseSize.z * 0.5f); Vector3 cs4 = new Vector3(-chimney.caseSize.x * 0.5f, chimney.caseSize.y, -chimney.caseSize.z * 0.5f) + caseNoiseVector; Vector3 cs5 = new Vector3(chimney.caseSize.x * 0.5f, chimney.caseSize.y, -chimney.caseSize.z * 0.5f) + caseNoiseVector; Vector3 cs6 = new Vector3(-chimney.caseSize.x * 0.5f, chimney.caseSize.y, chimney.caseSize.z * 0.5f) + caseNoiseVector; Vector3 cs7 = new Vector3(chimney.caseSize.x * 0.5f, chimney.caseSize.y, chimney.caseSize.z * 0.5f) + caseNoiseVector; Vector2 csuv0 = new Vector2(0, 0); Vector2 csuv1 = new Vector2(chimney.caseSize.x, chimney.caseSize.y); Vector2 csuv2 = new Vector2(csuv1.x, 0); Vector2 csuv3 = new Vector2(csuv1.x + chimney.caseSize.z, chimney.caseSize.y); Vector2 csuv4 = new Vector2(csuv3.x, 0); Vector2 csuv5 = new Vector2(csuv3.x + chimney.caseSize.x, chimney.caseSize.y); Vector2 csuv6 = new Vector2(csuv5.x, 0); Vector2 csuv7 = new Vector2(csuv5.x + chimney.caseSize.z, chimney.caseSize.y); Vector2 csuv8 = new Vector2(0, 0); Vector2 csuv9 = new Vector2(chimney.caseSize.x, chimney.caseSize.z); Vector4 cst0 = new Vector4(0, 0, 1, 0); Vector4 cst1 = new Vector4(1, 0, 1, 0); Vector4 cst2 = new Vector4(0, 0, -1, 0); Vector4 cst3 = new Vector4(-1, 0, 0, 0); Vector4 cst4 = new Vector4(0, 0, 1, 0); int caseSubmesh = submeshLibrary.SubmeshAdd(chimney.caseSurface); //sides DYNAMIC_MESH.AddPlane(cs0, cs1, cs4, cs5, csuv0, csuv1, Vector3.back, cst0, caseSubmesh, chimney.caseSurface); DYNAMIC_MESH.AddPlane(cs1, cs3, cs5, cs7, csuv2, csuv3, Vector3.right, cst1, caseSubmesh, chimney.caseSurface); DYNAMIC_MESH.AddPlane(cs3, cs2, cs7, cs6, csuv4, csuv5, Vector3.forward, cst2, caseSubmesh, chimney.caseSurface); DYNAMIC_MESH.AddPlane(cs2, cs0, cs6, cs4, csuv6, csuv7, Vector3.left, cst3, caseSubmesh, chimney.caseSurface); //top DYNAMIC_MESH.AddPlane(cs4, cs5, cs6, cs7, csuv8, csuv9, Vector3.up, cst4, caseSubmesh, chimney.caseSurface);//todo calculate the values for this - don't be lazy //CROWN Vector3 crownBase = caseNoiseVector + Vector3.up * chimney.caseSize.y; Vector3 crownNoiseVector = new Vector3(chimney.noise.x * RGEN.OneRange(), chimney.noise.y * RGEN.OneRange(), chimney.noise.z * RGEN.OneRange()); Vector3 cr0 = crownBase + new Vector3(-chimney.crownSize.x * 0.5f, 0, -chimney.crownSize.z * 0.5f); Vector3 cr1 = crownBase + new Vector3(chimney.crownSize.x * 0.5f, 0, -chimney.crownSize.z * 0.5f); Vector3 cr2 = crownBase + new Vector3(-chimney.crownSize.x * 0.5f, 0, chimney.crownSize.z * 0.5f); Vector3 cr3 = crownBase + new Vector3(chimney.crownSize.x * 0.5f, 0, chimney.crownSize.z * 0.5f); Vector3 cr4 = crownBase + new Vector3(-chimney.crownSize.x * 0.5f, chimney.crownSize.y, -chimney.crownSize.z * 0.5f) + crownNoiseVector; Vector3 cr5 = crownBase + new Vector3(chimney.crownSize.x * 0.5f, chimney.crownSize.y, -chimney.crownSize.z * 0.5f) + crownNoiseVector; Vector3 cr6 = crownBase + new Vector3(-chimney.crownSize.x * 0.5f, chimney.crownSize.y, chimney.crownSize.z * 0.5f) + crownNoiseVector; Vector3 cr7 = crownBase + new Vector3(chimney.crownSize.x * 0.5f, chimney.crownSize.y, chimney.crownSize.z * 0.5f) + crownNoiseVector; Vector2 cruv0 = new Vector2(0, 0); Vector2 cruv1 = new Vector2(chimney.crownSize.x, chimney.crownSize.y); Vector2 cruv2 = new Vector2(csuv1.x, 0); Vector2 cruv3 = new Vector2(csuv1.x + chimney.caseSize.z, chimney.crownSize.y); Vector2 cruv4 = new Vector2(csuv3.x, 0); Vector2 cruv5 = new Vector2(csuv3.x + chimney.crownSize.x, chimney.crownSize.y); Vector2 cruv6 = new Vector2(csuv5.x, chimney.crownSize.y); Vector2 cruv7 = new Vector2(csuv5.x + chimney.crownSize.z, chimney.crownSize.y); Vector2 cruv8 = new Vector2(0, 0); Vector2 cruv9 = new Vector2(chimney.crownSize.x, chimney.crownSize.z); Vector4 crt0 = new Vector4(0, 0, 1, 0); Vector4 crt1 = new Vector4(1, 0, 1, 0); Vector4 crt2 = new Vector4(0, 0, -1, 0); Vector4 crt3 = new Vector4(-1, 0, 0, 0); Vector4 crt4 = new Vector4(0, 0, 1, 0); int crownSubmesh = submeshLibrary.SubmeshAdd(chimney.crownSurface); DYNAMIC_MESH.AddPlane(cr0, cr1, cr4, cr5, cruv0, cruv1, Vector3.back, crt0, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(cr1, cr3, cr5, cr7, cruv2, cruv3, Vector3.right, crt1, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(cr3, cr2, cr7, cr6, cruv4, cruv5, Vector3.forward, crt2, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(cr2, cr0, cr6, cr4, cruv6, cruv7, Vector3.left, crt3, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(cr1, cr0, cr3, cr2, cruv8, cruv9, Vector3.down, crt4, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(cr4, cr5, cr6, cr7, cruv8, cruv9, Vector3.up, crt4, crownSubmesh, chimney.crownSurface); //todo calculate the values for this - don't be lazy int xCount = 1; int zCount = 1; if (chimney.allowMultiple) { xCount = Mathf.FloorToInt((chimney.crownSize.x - chimney.flueSpacing) / (chimney.flueSize.x + chimney.flueSpacing)); if (xCount < 1) { xCount = 1; } if (chimney.allowMultipleRows) { zCount = Mathf.FloorToInt((chimney.crownSize.z - chimney.flueSpacing) / (chimney.flueSize.z + chimney.flueSpacing)); if (zCount < 1) { zCount = 1; } } } float xSpacing = (chimney.crownSize.x - chimney.flueSize.x * xCount) / (xCount + 1); float zSpacing = (chimney.crownSize.z - chimney.flueSize.z * zCount) / (zCount + 1); //FLUES for (int x = 0; x < xCount; x++) { for (int z = 0; z < zCount; z++) { Vector3 flueBase = cr4 + new Vector3(xSpacing + x * (chimney.flueSize.x + xSpacing) + chimney.flueSize.x * 0.5f, 0, zSpacing + z * (chimney.flueSize.z + zSpacing) + chimney.flueSize.z * 0.5f); float thickness = (chimney.flueSize.x + chimney.flueSize.z) * 0.05f;//10% float drop = chimney.flueSize.y * 0.9f; Vector4 topTangent = new Vector4(1, 0, 0, 0); Surface useFlueSurface = GenerationUtil.GetSurface(chimney.flueSurfaces, RGEN); int flueSubmesh = submeshLibrary.SubmeshAdd(useFlueSurface); int innerSubmesh = submeshLibrary.SubmeshAdd(chimney.innerSurface); Vector3 flueNoiseVector = new Vector3(chimney.noise.x * RGEN.OneRange(), chimney.noise.y * RGEN.OneRange(), chimney.noise.z * RGEN.OneRange()); if (chimney.square) { Vector3 f0 = flueBase + new Vector3(-chimney.flueSize.x * 0.5f, 0, -chimney.flueSize.z * 0.5f); Vector3 f1 = flueBase + new Vector3(chimney.flueSize.x * 0.5f, 0, -chimney.flueSize.z * 0.5f); Vector3 f2 = flueBase + new Vector3(-chimney.flueSize.x * 0.5f, 0, chimney.flueSize.z * 0.5f); Vector3 f3 = flueBase + new Vector3(chimney.flueSize.x * 0.5f, 0, chimney.flueSize.z * 0.5f); Vector3 f4 = flueBase + new Vector3(-chimney.flueSize.x * 0.5f, chimney.flueSize.y, -chimney.flueSize.z * 0.5f) + flueNoiseVector; Vector3 f5 = flueBase + new Vector3(chimney.flueSize.x * 0.5f, chimney.flueSize.y, -chimney.flueSize.z * 0.5f) + flueNoiseVector; Vector3 f6 = flueBase + new Vector3(-chimney.flueSize.x * 0.5f, chimney.flueSize.y, chimney.flueSize.z * 0.5f) + flueNoiseVector; Vector3 f7 = flueBase + new Vector3(chimney.flueSize.x * 0.5f, chimney.flueSize.y, chimney.flueSize.z * 0.5f) + flueNoiseVector; Vector3 f4i = f4 + new Vector3(thickness, 0, thickness) + flueNoiseVector; Vector3 f5i = f5 + new Vector3(-thickness, 0, thickness) + flueNoiseVector; Vector3 f6i = f6 + new Vector3(thickness, 0, -thickness) + flueNoiseVector; Vector3 f7i = f7 + new Vector3(-thickness, 0, -thickness) + flueNoiseVector; Vector3 f4id = f4i + new Vector3(0, -drop, 0); Vector3 f5id = f5i + new Vector3(0, -drop, 0); Vector3 f6id = f6i + new Vector3(0, -drop, 0); Vector3 f7id = f7i + new Vector3(0, -drop, 0); // Vector2 fuv0 = new Vector2(0, 0); // Vector2 fuv1 = new Vector2(chimney.flueSize.x, 0); // Vector2 fuv2 = new Vector2(fuv1.x + chimney.flueSize.z, 0); // Vector2 fuv3 = new Vector2(fuv2.x + chimney.flueSize.x, 0); // // Vector2 fuv4 = new Vector2(0, chimney.flueSize.y); // Vector2 fuv5 = new Vector2(chimney.flueSize.x, chimney.flueSize.y); // Vector2 fuv6 = new Vector2(fuv1.x + chimney.flueSize.z, chimney.flueSize.y); // Vector2 fuv7 = new Vector2(fuv2.x + chimney.flueSize.x, chimney.flueSize.y); //Flue Sides DYNAMIC_MESH.AddPlane(f0, f1, f4, f5, flueSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f1, f3, f5, f7, flueSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f3, f2, f7, f6, flueSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f2, f0, f6, f4, flueSubmesh); //todo calculate the values for this - don't be lazy //Flue Top DYNAMIC_MESH.AddPlaneComplex(f4, f5, f4i, f5i, Vector3.up, topTangent, flueSubmesh, useFlueSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlaneComplex(f5, f7, f5i, f7i, Vector3.up, topTangent, flueSubmesh, useFlueSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlaneComplex(f7, f6, f7i, f6i, Vector3.up, topTangent, flueSubmesh, useFlueSurface); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlaneComplex(f6, f4, f6i, f4i, Vector3.up, topTangent, flueSubmesh, useFlueSurface); //todo calculate the values for this - don't be lazy //Flue Drop DYNAMIC_MESH.AddPlane(f5id, f4id, f5i, f4i, innerSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f7id, f5id, f7i, f5i, innerSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f6id, f7id, f6i, f7i, innerSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f4id, f6id, f4i, f6i, innerSubmesh); //todo calculate the values for this - don't be lazy DYNAMIC_MESH.AddPlane(f4id, f5id, f6id, f7id, innerSubmesh); //todo calculate the values for this - don't be lazy } else { int vertCount = (chimney.segments + 1) * 2;//add an additonal so we can wrap the UVs well RawMeshData flueOuter = new RawMeshData(vertCount, chimney.segments * 6); RawMeshData flueTop = new RawMeshData(vertCount, chimney.segments * 6); //add additional point for the middle, bottom of the inside of the flue RawMeshData flueInner = new RawMeshData(vertCount + 1, chimney.segments * 9); //the additonal point at the bottom of the flue - added to the end of the mesh data flueInner.vertices[vertCount] = flueBase; flueInner.normals[vertCount] = Vector3.up; flueInner.tangents[vertCount] = new Vector4(1, 0, 0, 0); int indexIm = flueInner.vertCount - 1; float circumference = Mathf.PI * (chimney.flueSize.x + chimney.flueSize.z); for (int s = 0; s < chimney.segments + 1; s++) { float percent = s / (float)(chimney.segments); percent = (percent + (chimney.angleOffset / 360)) % 1f; int indexV0 = s * 2; int indexV1 = s * 2 + 1; int indexV2 = s * 2 + 2; int indexV3 = s * 2 + 3; if (s == chimney.segments - 1) { indexV2 = 0; indexV3 = 1; } float xa = Mathf.Sin(percent * Mathf.PI * 2) * chimney.flueSize.x * 0.5f; float za = Mathf.Cos(percent * Mathf.PI * 2) * chimney.flueSize.z * 0.5f; // float innerHalf = thickness / (chimney.flueSize.x + chimney.flueSize.z) / 2; float xai = Mathf.Sin(percent * Mathf.PI * 2) * chimney.flueSize.x * 0.4f; float zai = Mathf.Cos(percent * Mathf.PI * 2) * chimney.flueSize.z * 0.4f; Vector3 v0 = flueBase + new Vector3(xa, 0, za); Vector3 v1 = flueBase + new Vector3(xa, chimney.flueSize.y, za) + flueNoiseVector; Vector3 v2 = flueBase + new Vector3(xai, chimney.flueSize.y, zai) + flueNoiseVector; Vector3 v3 = flueBase + new Vector3(xai, chimney.flueSize.y * 0.1f, zai); Vector2 uv0 = new Vector2(-circumference * percent, 0); Vector2 uv1 = new Vector2(-circumference * percent, chimney.flueSize.y); Vector2 uv2 = new Vector2(-circumference * percent, chimney.flueSize.y + 0.1f); Vector2 uv3 = new Vector2(-circumference * percent, 0); int rdnFlueSurfaceIndex = RGEN.Index(chimney.flueSurfaces.Count); Surface flueSurface = rdnFlueSurfaceIndex != -1 ? chimney.flueSurfaces[rdnFlueSurfaceIndex] : null; if (flueSurface != null) { uv0 = flueSurface.CalculateUV(uv0); uv1 = flueSurface.CalculateUV(uv1); uv2 = flueSurface.CalculateUV(uv2); uv3 = flueSurface.CalculateUV(uv3); } flueOuter.vertices[indexV0] = v0; flueOuter.vertices[indexV1] = v1; flueOuter.uvs[indexV0] = uv0; flueOuter.uvs[indexV1] = uv1; flueTop.vertices[indexV0] = v1; flueTop.vertices[indexV1] = v2; flueTop.uvs[indexV0] = uv1; flueTop.uvs[indexV1] = uv2; flueInner.vertices[indexV0] = v2; flueInner.vertices[indexV1] = v3; flueInner.uvs[indexV0] = uv2; flueInner.uvs[indexV1] = uv3; Vector3 outerNormal = new Vector3(Mathf.Sin(percent * Mathf.PI * 2), 0, Mathf.Cos(percent * Mathf.PI * 2)); flueOuter.normals[indexV0] = outerNormal; flueOuter.normals[indexV1] = outerNormal; flueTop.normals[indexV0] = Vector3.up; flueTop.normals[indexV1] = Vector3.up; flueInner.normals[indexV0] = -outerNormal; flueInner.normals[indexV1] = -outerNormal; if (s < chimney.segments) { int tidx0 = s * 6; flueOuter.triangles[tidx0 + 0] = indexV0; flueOuter.triangles[tidx0 + 2] = indexV1; flueOuter.triangles[tidx0 + 1] = indexV2; flueOuter.triangles[tidx0 + 3] = indexV1; flueOuter.triangles[tidx0 + 4] = indexV2; flueOuter.triangles[tidx0 + 5] = indexV3; flueTop.triangles[tidx0 + 0] = indexV0; flueTop.triangles[tidx0 + 2] = indexV1; flueTop.triangles[tidx0 + 1] = indexV2; flueTop.triangles[tidx0 + 3] = indexV1; flueTop.triangles[tidx0 + 4] = indexV2; flueTop.triangles[tidx0 + 5] = indexV3; int tidx0i = s * 9; flueInner.triangles[tidx0i + 0] = indexV0; flueInner.triangles[tidx0i + 2] = indexV1; flueInner.triangles[tidx0i + 1] = indexV2; flueInner.triangles[tidx0i + 3] = indexV1; flueInner.triangles[tidx0i + 4] = indexV2; flueInner.triangles[tidx0i + 5] = indexV3; flueInner.triangles[tidx0i + 6] = indexV1; flueInner.triangles[tidx0i + 7] = indexV3; flueInner.triangles[tidx0i + 8] = indexIm; } } DYNAMIC_MESH.AddData(flueOuter, flueSubmesh); DYNAMIC_MESH.AddData(flueTop, flueSubmesh); DYNAMIC_MESH.AddData(flueInner, innerSubmesh); } } } if (output.raw != null) { output.raw.Copy(DYNAMIC_MESH); } if (output.mesh != null) { output.mesh.Clear(false); DYNAMIC_MESH.Build(output.mesh); } }
public static void Generate(IBuilding building, IVolume volume, IFloorplan floorplan, int volumeFloor, VerticalOpening[] openings, BuildRMesh mesh, BuildRCollider collider) { SubmeshLibrary submeshLibrary = mesh.submeshLibrary; bool generateColliders = building.colliderType != BuildingColliderTypes.None; bool generateMeshColliders = building.colliderType != BuildingColliderTypes.Primitive && generateColliders; BuildRCollider sendCollider = (generateColliders) ? collider : null; collider.thickness = volume.wallThickness; if (!generateMeshColliders) { collider = null; } float wallThickness = volume.wallThickness; float wallUp = volume.floorHeight - wallThickness; Vector3 wallUpV = Vector3.up * wallUp; Vector3 floorBaseV = Vector3.up * volume.baseHeight; int roomCount = floorplan.RoomCount; int actualFloor = building.VolumeBaseFloor(volume) + volumeFloor; int openingCount = openings.Length; bool[] openingBelow = new bool[openingCount]; bool[] openingAbove = new bool[openingCount]; FlatBounds[] openingBounds = new FlatBounds[openingCount]; Vector2[][] openingShapes = new Vector2[openingCount][]; bool[] openingUsedInThisFloor = new bool[openingCount]; for (int o = 0; o < openingCount; o++) { VerticalOpening opening = openings[o]; if (!openings[o].FloorIsIncluded(actualFloor)) { continue; } openingBelow[o] = opening.FloorIsIncluded(actualFloor - 1); openingAbove[o] = opening.FloorIsIncluded(actualFloor + 1); openingShapes[o] = opening.PointsRotated(); openingBounds[o] = new FlatBounds(openingShapes[o]); submeshLibrary.Add(opening.surfaceA); submeshLibrary.Add(opening.surfaceB); submeshLibrary.Add(opening.surfaceC); submeshLibrary.Add(opening.surfaceD); } Dictionary <int, List <Vector2Int> > externalWallAnchors = volume.facadeWallAnchors; Room[] rooms = floorplan.AllRooms(); for (int r = 0; r < roomCount; r++) { Room room = rooms[r]; int pointCount = room.numberOfPoints; Surface floorSurface = null; Surface wallSurface = null; Surface ceilingSurface = null; if (room.style != null) { RoomStyle style = room.style; floorSurface = style.floorSurface; wallSurface = style.wallSurface; ceilingSurface = style.ceilingSurface; } int floorSubmesh = submeshLibrary.SubmeshAdd(floorSurface); int wallSubmesh = submeshLibrary.SubmeshAdd(wallSurface); int ceilingSubmesh = submeshLibrary.SubmeshAdd(ceilingSurface); FloorplanUtil.RoomWall[] walls = FloorplanUtil.CalculatePoints(room, volume); Vector2[] roomArchorPoints = FloorplanUtil.RoomArchorPoints(walls); Vector4 tangent = BuildRMesh.CalculateTangent(Vector3.right); Vector2[] offsetRoomAnchorPoints = QuickPolyOffset.Execute(roomArchorPoints, wallThickness); FlatBounds roomBounds = new FlatBounds(offsetRoomAnchorPoints); List <Vector2[]> floorCuts = new List <Vector2[]>(); List <Vector2[]> ceilingCuts = new List <Vector2[]>(); List <VerticalOpening> roomOpenings = new List <VerticalOpening>(); for (int o = 0; o < openingCount; o++) { if (openings[o].FloorIsIncluded(actualFloor)) { if (roomBounds.Overlaps(openingBounds[o])) { if (CheckShapeWithinRoom(offsetRoomAnchorPoints, openingShapes[o])) { if (openingBelow[o]) { floorCuts.Add(openingShapes[o]); } if (openingAbove[o]) { ceilingCuts.Add(openingShapes[o]); } if (openingAbove[o] || openingBelow[o]) { roomOpenings.Add(openings[o]); openingUsedInThisFloor[o] = true; } } } } } int offsetPointBase = 0; for (int p = 0; p < pointCount; p++)//generate room walls { FloorplanUtil.RoomWall wall = walls[p]; int wallPointCount = wall.offsetPoints.Length; List <RoomPortal> wallPortals = floorplan.GetWallPortals(room, p); int wallPortalCount = wallPortals.Count; if (!wall.isExternal) { int indexA = offsetPointBase; int indexB = (offsetPointBase + 1) % roomArchorPoints.Length; Vector2 origBaseA = roomArchorPoints[indexA]; Vector2 origBaseB = roomArchorPoints[indexB]; Vector2 baseA = offsetRoomAnchorPoints[indexA]; Vector2 baseB = offsetRoomAnchorPoints[indexB]; Vector3 v0 = new Vector3(origBaseA.x, 0, origBaseA.y) + floorBaseV; Vector3 v1 = new Vector3(origBaseB.x, 0, origBaseB.y) + floorBaseV; Vector3 vOffset0 = new Vector3(baseA.x, 0, baseA.y) + floorBaseV; Vector3 vOffset1 = new Vector3(baseB.x, 0, baseB.y) + floorBaseV; if (wallPortalCount == 0) //just draw the wall - no portals to cut { Vector3 v2 = vOffset1 + wallUpV; Vector3 v3 = vOffset0 + wallUpV; Vector2 minUV = Vector2.zero; Vector2 maxUV = new Vector2(Vector2.Distance(baseA, baseB), wallUp); if (wallSurface != null) { maxUV = wallSurface.CalculateUV(maxUV); } Vector3 wallDir = (vOffset0 - vOffset1).normalized; Vector3 wallNormal = Vector3.Cross(Vector3.up, wallDir); Vector4 wallTangent = BuildRMesh.CalculateTangent(wallDir); mesh.AddPlane(vOffset1, vOffset0, v2, v3, minUV, maxUV, wallNormal, wallTangent, wallSubmesh, wallSurface); if (generateColliders) { collider.AddPlane(vOffset1, vOffset0, v2, v3); } } else { List <float> useLaterals = new List <float>(); List <bool> hasPortals = new List <bool>(); for (int wp = 0; wp < wallPortalCount; wp++) { RoomPortal portal = wallPortals[wp]; bool hasPortal = room.HasPortal(portal); hasPortals.Add(hasPortal); if (hasPortal) { useLaterals.Add(portal.lateralPosition); } else { useLaterals.Add(1 - portal.lateralPosition);//portal from other wall - wall orientation is flipped } } Vector3 wallVector = vOffset1 - vOffset0; Vector3 wallDirection = wallVector.normalized; Vector3 wallStart = vOffset0; Vector4 wallTangent = BuildRMesh.CalculateTangent(wallDirection); Vector3 wallNormal = Vector3.Cross(Vector3.up, wallDirection); Vector4 wallNormalTangent = BuildRMesh.CalculateTangent(wallNormal); Vector4 wallNormalTangentReverse = BuildRMesh.CalculateTangent(-wallNormal); while (wallPortalCount > 0) { int portalIndex = 0; RoomPortal usePortal = wallPortals[0]; float lowestLat = useLaterals[0]; for (int wp = 1; wp < wallPortalCount; wp++) { if (useLaterals[wp] < lowestLat) { portalIndex = wp; usePortal = wallPortals[wp]; lowestLat = useLaterals[wp]; } } wallPortals.RemoveAt(portalIndex); useLaterals.RemoveAt(portalIndex); wallPortalCount--; Vector3 vl0 = v0 + (-wallNormal + wallDirection) * wallThickness; Vector3 vl1 = v1 + (-wallNormal - wallDirection) * wallThickness; Vector3 portalCenter = Vector3.Lerp(vl0, vl1, lowestLat); Vector3 portalHalfvector = wallDirection * (usePortal.width * 0.5f); Vector3 portalBase = Vector3.up * (volume.floorHeight - usePortal.height) * usePortal.verticalPosition; Vector3 portalUp = portalBase + Vector3.up * usePortal.height; Vector3 portalStart = portalCenter - portalHalfvector; Vector3 portalEnd = portalCenter + portalHalfvector; Vector2 initalWallUVMin = new Vector2(Vector3.Dot(portalStart, wallDirection), 0); Vector2 initalWallUVMax = new Vector2(Vector3.Dot(wallStart, wallDirection), wallUp); mesh.AddPlane(portalStart, wallStart, portalStart + wallUpV, wallStart + wallUpV, initalWallUVMin, initalWallUVMax, wallNormal, wallTangent, wallSubmesh, wallSurface);//initial wall if (generateColliders) { collider.AddPlane(portalStart, wallStart, portalStart + wallUpV, wallStart + wallUpV); } if (usePortal.verticalPosition > 0) { Vector2 portalBaseUVMin = new Vector2(Vector3.Dot(portalEnd, wallDirection), 0); Vector2 portalBaseUVMax = new Vector2(Vector3.Dot(portalStart, wallDirection), portalBase.y); mesh.AddPlane(portalEnd, portalStart, portalEnd + portalBase, portalStart + portalBase, portalBaseUVMin, portalBaseUVMax, wallNormal, wallTangent, wallSubmesh, wallSurface);//bottom if (generateColliders) { collider.AddPlane(portalEnd, portalStart, portalEnd + portalBase, portalStart + portalBase); } } if (usePortal.verticalPosition < 1) { Vector2 portalBaseUVMin = new Vector2(Vector3.Dot(portalEnd, wallDirection), portalUp.y); Vector2 portalBaseUVMax = new Vector2(Vector3.Dot(portalStart, wallDirection), wallUp); mesh.AddPlane(portalEnd + portalUp, portalStart + portalUp, portalEnd + wallUpV, portalStart + wallUpV, portalBaseUVMin, portalBaseUVMax, wallNormal, wallTangent, wallSubmesh, wallSurface);//top if (generateColliders) { collider.AddPlane(portalEnd + portalUp, portalStart + portalUp, portalEnd + wallUpV, portalStart + wallUpV); } } if (hasPortals[portalIndex])//only do this once - from the room it's attached to { //portal interior frame Vector3 portalDepth = wallNormal * wallThickness * 2; //sides mesh.AddPlane(portalStart + portalDepth + portalBase, portalStart + portalBase, portalStart + portalDepth + portalUp, portalStart + portalUp, wallDirection, wallNormalTangentReverse, wallSubmesh); mesh.AddPlane(portalEnd + portalBase, portalEnd + portalDepth + portalBase, portalEnd + portalUp, portalEnd + portalDepth + portalUp, -wallDirection, wallNormalTangent, wallSubmesh); if (generateMeshColliders) { collider.AddPlane(portalStart + portalDepth + portalBase, portalStart + portalBase, portalStart + portalDepth + portalUp, portalStart + portalUp); collider.AddPlane(portalEnd + portalBase, portalEnd + portalDepth + portalBase, portalEnd + portalUp, portalEnd + portalDepth + portalUp); } //floor Vector2 minFloorUv = new Vector2((portalEnd + portalBase).z, (portalEnd + portalBase).x); Vector2 maxFloorUv = minFloorUv + new Vector2(wallThickness, usePortal.width); mesh.AddPlane(portalStart + portalBase, portalStart + portalDepth + portalBase, portalEnd + portalBase, portalEnd + portalDepth + portalBase, minFloorUv, maxFloorUv, Vector3.up, wallTangent, floorSubmesh, floorSurface); if (generateMeshColliders) { collider.AddPlane(portalStart + portalBase, portalStart + portalDepth + portalBase, portalEnd + portalBase, portalEnd + portalDepth + portalBase); } //ceiling mesh.AddPlane(portalEnd + portalUp, portalEnd + portalDepth + portalUp, portalStart + portalUp, portalStart + portalDepth + portalUp, Vector3.down, wallTangent, wallSubmesh); if (generateMeshColliders) { collider.AddPlane(portalEnd + portalUp, portalEnd + portalDepth + portalUp, portalStart + portalUp, portalStart + portalDepth + portalUp); } } wallStart = portalEnd;//move the start for the next calculation } Vector2 finalWallUVMin = new Vector2(Vector3.Dot(vOffset1, wallDirection), 0); Vector2 finalWallUVMax = new Vector2(Vector3.Dot(wallStart, wallDirection), wallUp); mesh.AddPlane(vOffset1, wallStart, vOffset1 + wallUpV, wallStart + wallUpV, finalWallUVMin, finalWallUVMax, wallNormal, wallTangent, wallSubmesh, wallSurface);//final wall section if (generateColliders) { collider.AddPlane(vOffset1, wallStart, vOffset1 + wallUpV, wallStart + wallUpV); } } offsetPointBase += 1; } else//external anchored wall { int facadeIndex = wall.facadeIndex; Facade facadeDesign = volume.GetFacade(facadeIndex); int currentFacadeWallSectionLength = externalWallAnchors[facadeIndex].Count - 1; int currentWallSectionIndex = wall.offsetPointWallSection[0]; int wallOffsetPoints = wall.offsetPoints.Length; for (int w = 0; w < wallOffsetPoints - 1; w++) { int roomPointIndex = offsetPointBase + w; Vector2 baseA = offsetRoomAnchorPoints[roomPointIndex]; int offsetIndexB = (roomPointIndex + 1) % offsetRoomAnchorPoints.Length; Vector2 baseB = offsetRoomAnchorPoints[offsetIndexB]; Vector3 v0 = new Vector3(baseA.x, 0, baseA.y) + floorBaseV; Vector3 v1 = new Vector3(baseB.x, 0, baseB.y) + floorBaseV; int wallSectionIndex = wall.offsetPointWallSection[w]; bool canGenerateWallSection = facadeDesign != null; Vector3 wallVector = v0 - v1; Vector3 wallDir = wallVector.normalized; float wallLength = wallVector.magnitude; if (!canGenerateWallSection) { if (wallSurface != null) { submeshLibrary.Add(wallSurface); } Vector3 v2 = v1 + wallUpV; Vector3 v3 = v0 + wallUpV; Vector2 minUV = Vector2.zero; Vector2 maxUV = new Vector2(Vector2.Distance(baseA, baseB), wallUp); Vector3 wallNormal = Vector3.Cross(Vector3.up, wallDir); Vector4 wallTangent = BuildRMesh.CalculateTangent(wallDir); mesh.AddPlane(v1, v0, v2, v3, minUV, maxUV, wallNormal, wallTangent, wallSubmesh, wallSurface); if (generateMeshColliders) { collider.AddPlane(v1, v0, v2, v3); } } else { WallSection section = facadeDesign.GetWallSection(wallSectionIndex, volumeFloor, currentFacadeWallSectionLength, volume.floors); if (section.model != null) { continue;//cannot account for custom meshes assume custom mesh does include interior mesh or if does - will be generated with the exterior } GenerationOutput generatedSection = GenerationOutput.CreateRawOutput(); Vector2 wallSectionSize = new Vector2(wallLength, wallUp + wallThickness); bool cullOpening = building.cullDoors && section.isDoor; SubmeshLibrary sectionLib = new SubmeshLibrary(); if (wallSurface != null) { sectionLib.Add(wallSurface);//add interior wall surface submeshLibrary.Add(wallSurface); } sectionLib.Add(section.openingSurface);//add windows - the only surface we'll use in the interior room submeshLibrary.Add(section.openingSurface); float offset = 0; if (w == 0) { offset = wallThickness; } if (w == wallOffsetPoints - 2) { offset = -wallThickness; } WallSectionGenerator.Generate(section, generatedSection, wallSectionSize, true, wallThickness, cullOpening, null, sectionLib, offset); int[] mapping = submeshLibrary.MapSubmeshes(generatedSection.raw.materials); Vector3 curveNormal = Vector3.Cross(wallDir, Vector3.up); Quaternion meshRot = Quaternion.LookRotation(curveNormal, Vector3.up); Vector3 meshPos = new Vector3(v1.x, volume.baseHeight, v1.z) + wallDir * wallSectionSize.x + Vector3.up * wallSectionSize.y; meshPos += meshRot * -new Vector3(wallSectionSize.x, wallSectionSize.y, 0) * 0.5f; mesh.AddData(generatedSection.raw, mapping, meshPos, meshRot, Vector3.one); } currentWallSectionIndex++; if (currentWallSectionIndex >= currentFacadeWallSectionLength) { //reached the end of the facade - move to the next one and continue currentFacadeWallSectionLength = externalWallAnchors[facadeIndex].Count; currentWallSectionIndex = 0; } } offsetPointBase += wallPointCount - 1; } } //FLOOR Vector2[] mainShape = offsetRoomAnchorPoints; Vector2[][] floorCutPoints = floorCuts.ToArray(); int floorVertCount = mainShape.Length; for (int flc = 0; flc < floorCutPoints.Length; flc++) { floorVertCount += floorCutPoints[flc].Length; } Vector2[] allFloorPoints = new Vector2[floorVertCount]; int mainShapeLength = mainShape.Length; for (int ms = 0; ms < mainShapeLength; ms++) { allFloorPoints[ms] = mainShape[ms]; } int cutPointIterator = mainShapeLength; for (int flc = 0; flc < floorCutPoints.Length; flc++) { for (int flcp = 0; flcp < floorCutPoints[flc].Length; flcp++) { allFloorPoints[cutPointIterator] = floorCutPoints[flc][flcp]; cutPointIterator++; } } Vector3[] floorPoints = new Vector3[floorVertCount]; Vector2[] floorUvs = new Vector2[floorVertCount]; Vector3[] floorNorms = new Vector3[floorVertCount]; Vector4[] floorTangents = new Vector4[floorVertCount]; for (int rp = 0; rp < floorVertCount; rp++) { floorPoints[rp] = new Vector3(allFloorPoints[rp].x, 0, allFloorPoints[rp].y) + floorBaseV; Vector2 uv = allFloorPoints[rp]; if (floorSurface != null) { uv = floorSurface.CalculateUV(uv); } floorUvs[rp] = uv; floorNorms[rp] = Vector3.up; floorTangents[rp] = tangent; } int[] tris = Poly2TriWrapper.Triangulate(mainShape, true, floorCutPoints); mesh.AddData(floorPoints, floorUvs, tris, floorNorms, floorTangents, floorSubmesh); if (generateColliders) { collider.mesh.AddData(floorPoints, floorUvs, tris, floorNorms, floorTangents, 0); } //CEILING! Vector2[][] ceilingCutPoints = ceilingCuts.ToArray(); int ceilingVertCount = mainShape.Length; for (int flc = 0; flc < ceilingCutPoints.Length; flc++) { ceilingVertCount += ceilingCutPoints[flc].Length; } Vector2[] allCeilingPoints = new Vector2[ceilingVertCount]; for (int ms = 0; ms < mainShapeLength; ms++) { allCeilingPoints[ms] = mainShape[ms]; } cutPointIterator = mainShapeLength; for (int flc = 0; flc < ceilingCutPoints.Length; flc++) { for (int flcp = 0; flcp < ceilingCutPoints[flc].Length; flcp++) { allCeilingPoints[cutPointIterator] = ceilingCutPoints[flc][flcp]; cutPointIterator++; } } Vector3[] ceilingPoints = new Vector3[ceilingVertCount]; Vector2[] ceilingUvs = new Vector2[ceilingVertCount]; Vector3[] ceilingNorms = new Vector3[ceilingVertCount]; Vector4[] ceilingTangents = new Vector4[ceilingVertCount]; for (int rp = 0; rp < ceilingVertCount; rp++) { ceilingPoints[rp] = new Vector3(allCeilingPoints[rp].x, wallUp, allCeilingPoints[rp].y) + floorBaseV; Vector2 uv = allCeilingPoints[rp]; if (floorSurface != null) { uv = ceilingSurface.CalculateUV(uv); } ceilingUvs[rp] = uv; ceilingNorms[rp] = Vector3.down; ceilingTangents[rp] = tangent; } tris = Poly2TriWrapper.Triangulate(mainShape, false, ceilingCutPoints); mesh.AddData(ceilingPoints, ceilingUvs, tris, ceilingNorms, ceilingTangents, ceilingSubmesh); if (generateColliders) { collider.mesh.AddData(ceilingPoints, ceilingUvs, tris, ceilingNorms, ceilingTangents, 0); } for (int ob = 0; ob < openingCount; ob++) { VerticalOpening opening = openings[ob]; int openingIndex = Array.IndexOf(openings, opening); Vector3 basePosition = openingBounds[openingIndex].center; basePosition.z = basePosition.y; basePosition.y = volume.baseHeight; if (roomOpenings.Contains(opening))//opening used in this floorplan { int externalWallSubmesh = wallSubmesh != -1 ? wallSubmesh : -1; switch (opening.usage) { case VerticalOpening.Usages.Space: if (ceilingCutPoints.Length <= ob) { continue; } Vector3 ceilingCutUpV = Vector3.up * wallThickness; Vector2[] ceilingCut = ceilingCutPoints[ob]; int custSize = ceilingCut.Length; for (int cp = 0; cp < custSize; cp++) { int indexA = (cp + 1) % custSize; int indexB = cp; Vector3 cp0 = new Vector3(ceilingCut[indexA].x, wallUp, ceilingCut[indexA].y) + floorBaseV; Vector3 cp1 = new Vector3(ceilingCut[indexB].x, wallUp, ceilingCut[indexB].y) + floorBaseV; Vector3 cp2 = cp0 + ceilingCutUpV; Vector3 cp3 = cp1 + ceilingCutUpV; mesh.AddPlane(cp0, cp1, cp2, cp3, ceilingSubmesh); if (generateColliders) { collider.AddPlane(cp0, cp1, cp2, cp3); } } break; case VerticalOpening.Usages.Stairwell: StaircaseGenerator.Generate(mesh, opening, basePosition, volume.floorHeight, actualFloor, externalWallSubmesh, sendCollider); if (volumeFloor == volume.floors - 1 && opening.baseFloor + opening.floors > building.VolumeBaseFloor(volume) + volume.floors - 1 && volume.abovePlanCount == 0) { StaircaseGenerator.GenerateRoofAccess(mesh, opening, basePosition, volume.floorHeight, actualFloor, externalWallSubmesh, sendCollider); } break; case VerticalOpening.Usages.Elevator: ElevatorShaftGenerator.Generate(ref mesh, opening, actualFloor, basePosition, volume.floorHeight, externalWallSubmesh, sendCollider); break; } } } } for (int ob = 0; ob < openingCount; ob++) { Vector2[] openingShape = openingShapes[ob]; if (openingShape == null) { continue; //opening not used by this floorplan } if (openingUsedInThisFloor[ob]) { continue; //opening already generated } //seal this opening from the void VerticalOpening opening = openings[ob]; int openingIndex = Array.IndexOf(openings, opening); Vector3 basePosition = openingBounds[openingIndex].center; basePosition.z = basePosition.y; basePosition.y = 0; int cutSize = openingShape.Length; Vector3 sealingWallUpV = Vector3.up * volume.floorHeight; int sealWallSubmesh = submeshLibrary.SubmeshAdd(opening.surfaceB); Vector2[] offsetOpeningShape = QuickPolyOffset.Execute(openingShape, wallThickness); for (int cp = 0; cp < cutSize; cp++) { int indexA = (cp + 1) % cutSize; int indexB = cp; Vector2 p0 = opening.usage == VerticalOpening.Usages.Space ? openingShape[indexA] : offsetOpeningShape[indexA]; Vector2 p1 = opening.usage == VerticalOpening.Usages.Space ? openingShape[indexB] : offsetOpeningShape[indexB]; Vector3 cp0 = new Vector3(p0.x, 0, p0.y) + floorBaseV; Vector3 cp1 = new Vector3(p1.x, 0, p1.y) + floorBaseV; Vector3 cp2 = cp0 + sealingWallUpV; Vector3 cp3 = cp1 + sealingWallUpV; mesh.AddPlane(cp0, cp1, cp2, cp3, sealWallSubmesh); if (generateColliders) { collider.AddPlane(cp0, cp1, cp2, cp3); } } switch (opening.usage) { case VerticalOpening.Usages.Space: //nothing to implement break; case VerticalOpening.Usages.Stairwell: //need stairs to connect used floors StaircaseGenerator.GenerateStairs(mesh, opening, basePosition, volume.floorHeight, actualFloor, -1, sendCollider); if (volumeFloor == volume.floors - 1) { StaircaseGenerator.GenerateRoofAccess(mesh, opening, basePosition, volume.floorHeight, actualFloor, -1, sendCollider); } break; case VerticalOpening.Usages.Elevator: //nothing to implement break; } } }
//TODO support custom models coming in from roof design public static void Generate(ref BuildRMesh mesh, IVolume volume, List <Vector3[]> roofFaces) { Roof design = volume.roof; float roofDepth = design.depth; float roofHeight = design.height; float dormerWidth = design.dormerWidth; float dormerHeight = design.dormerHeight; int dormerRows = design.dormerRows; if (dormerHeight * dormerRows > roofHeight) { dormerHeight = roofHeight / dormerRows; } float dormerRoofHeight = design.dormerRoofHeight; float roofPitchRad = Mathf.Atan2(roofHeight, roofDepth); float roofHyp = Mathf.Sqrt(roofDepth * roofDepth + roofHeight * roofHeight);//todo make a proper calculation - this is incorrect float dormerDepth = Mathf.Cos(roofPitchRad) * dormerHeight; float dormerHyp = Mathf.Sqrt(dormerHeight * dormerHeight + dormerDepth * dormerDepth); float dormerRowSpace = roofHyp / dormerRows; dormerHyp = Mathf.Min(dormerHyp, dormerRowSpace); float dormerSpace = dormerRowSpace - dormerHyp; float dormerSpaceLerp = dormerSpace / roofHyp; if (INTERNAL_B_MESH == null) { INTERNAL_B_MESH = new BuildRMesh("internal dormer"); } INTERNAL_B_MESH.Clear(); INTERNAL_B_MESH.submeshLibrary.AddRange(mesh.submeshLibrary.MATERIALS.ToArray()); Vector3 bpl = Vector3.left * dormerWidth * 0.5f; Vector3 bpr = Vector3.right * dormerWidth * 0.5f; Vector3 tpc = Vector3.up * dormerHeight; float dormerFaceHeight = dormerHeight - dormerHeight * dormerRoofHeight; Vector3 tpl = bpl + Vector3.up * dormerFaceHeight; Vector3 tpr = bpr + Vector3.up * dormerFaceHeight; Vector3 rpc = tpc + Vector3.back * dormerDepth; Vector3 rpl = tpl + Vector3.back * dormerDepth; Vector3 rpr = tpr + Vector3.back * dormerDepth; Surface mainSurface = design.mainSurface; Surface wallSurface = design.wallSurface; int mainSubmesh = mesh.submeshLibrary.SubmeshAdd(mainSurface); int wallSubmesh = mesh.submeshLibrary.SubmeshAdd(wallSurface); Vector2 sectionSize = new Vector2(dormerWidth, dormerFaceHeight); if (design.wallSection && design.wallSection.CanRender(sectionSize)) { // mesh.submeshLibrary.Add(design.wallSection); mesh.submeshLibrary.Add(design.wallSection); GenerationOutput output = GenerationOutput.CreateRawOutput(); WallSectionGenerator.Generate(design.wallSection, output, sectionSize, false, 0.02f, false, null, mesh.submeshLibrary); Vector3 sectionPos = new Vector3(0, dormerFaceHeight * 0.5f, 0); int[] mapping = new int[output.raw.materials.Count]; for (int s = 0; s < output.raw.materials.Count; s++) { mapping[s] = 0; } INTERNAL_B_MESH.AddDataKeepSubmeshStructure(output.raw, sectionPos, Quaternion.Euler(0, 180, 0), Vector3.one); } else { INTERNAL_B_MESH.AddPlane(bpr, bpl, tpr, tpl, wallSubmesh);//dormer front square } //front triangle INTERNAL_B_MESH.AddTri(tpl, tpr, tpc, Vector3.right, wallSubmesh); //roof Vector3 normalRoofRight = Vector3.Cross((tpr - tpc).normalized, (rpc - tpc).normalized); Vector4 tangentRoofRight = BuildRMesh.CalculateTangent(Vector3.back); Vector3 normalRoofLeft = Vector3.Cross((rpc - tpc).normalized, (tpl - tpc).normalized); Vector4 tangentRoofLeft = BuildRMesh.CalculateTangent(Vector3.forward); Vector2 roofUvMax = new Vector2(dormerDepth, Vector3.Distance(tpc, tpl)); INTERNAL_B_MESH.AddPlane(rpr, tpr, rpc, tpc, Vector2.zero, roofUvMax, normalRoofRight, tangentRoofRight, mainSubmesh, mainSurface); INTERNAL_B_MESH.AddPlane(rpc, tpc, rpl, tpl, Vector2.zero, roofUvMax, normalRoofLeft, tangentRoofLeft, mainSubmesh, mainSurface); //side triangles INTERNAL_B_MESH.AddTri(bpr, rpr, tpr, Vector3.back, wallSubmesh); INTERNAL_B_MESH.AddTri(bpl, tpl, rpl, Vector3.back, wallSubmesh); RawMeshData data = RawMeshData.CopyBuildRMesh(INTERNAL_B_MESH); int roofFaceCount = roofFaces.Count; for (int r = 0; r < roofFaceCount; r++) { Vector3[] roofFace = roofFaces[r]; Vector3 p0 = roofFace[0]; Vector3 p1 = roofFace[1]; Vector3 p2 = roofFace[2]; Vector3 p3 = roofFace[3]; //center line Vector3 pDB = Vector3.Lerp(p0, p1, 0.5f); Vector3 facadeVector = p1 - p0; Vector3 facadeDirection = facadeVector.normalized; Vector3 facadeNormal = Vector3.Cross(Vector3.up, facadeDirection); Vector3 projTL = p0 + Vector3.Project(p2 - p0, facadeDirection); Vector3 projTR = p1 + Vector3.Project(p3 - p1, facadeDirection); float sqrMagP0 = Vector3.SqrMagnitude(p0 - pDB); float sqrMagP1 = Vector3.SqrMagnitude(p1 - pDB); float sqrMagP2 = Vector3.SqrMagnitude(projTL - pDB); float sqrMagP3 = Vector3.SqrMagnitude(projTR - pDB); Vector3 dormerBaseLeft = sqrMagP0 < sqrMagP2 ? p0 : projTL; Vector3 dormerBaseRight = sqrMagP1 < sqrMagP3 ? p1 : projTR; Vector3 roofNormal = BuildRMesh.CalculateNormal(p0, p2, p1); Vector3 roofUp = Vector3.Cross(roofNormal, -facadeDirection); float actualHyp = sqrMagP0 < sqrMagP2?Vector3.Distance(p0, p2 + Vector3.Project(p0 - p2, facadeDirection)) : Vector3.Distance(projTL, p2); Vector3 dormerTopLeft = dormerBaseLeft + roofUp * actualHyp; Vector3 dormerTopRight = dormerBaseRight + roofUp * actualHyp; float topLength = Vector3.Distance(dormerBaseLeft, dormerBaseRight); int numberOfDormers = Mathf.FloorToInt((topLength - design.minimumDormerSpacing * 2) / (design.minimumDormerSpacing + dormerWidth)); if (numberOfDormers == 0) { if (topLength > sectionSize.x) { numberOfDormers = 1; } } for (int dr = 0; dr < dormerRows; dr++) { float rowPercent = dr / (dormerRows + 0f) + dormerSpaceLerp * 0.5f; //row vector Vector3 rl = Vector3.Lerp(dormerBaseLeft, dormerTopLeft, rowPercent); Vector3 rr = Vector3.Lerp(dormerBaseRight, dormerTopRight, rowPercent); for (int dc = 0; dc < numberOfDormers; dc++) { float columnPercent = (dc + 1f) / (numberOfDormers + 1f); Vector3 dormerBegin = Vector3.Lerp(rl, rr, columnPercent); Quaternion meshRot = Quaternion.LookRotation(facadeNormal, Vector3.up); Vector3 meshPos = dormerBegin; //TODO account for the mesh mode of the wall section - custom meshes mesh.AddDataKeepSubmeshStructure(data, meshPos, meshRot, Vector3.one); } } } }
public static void GenerateFacade(FacadeData data, BuildRMesh dmesh, BuildRCollider collider = null) { // Debug.Log("******************* "+data.facadeDesign.ToString()); Vector3 facadeVector = data.baseB - data.baseA; if (facadeVector.magnitude < Mathf.Epsilon) { return; } Vector3 facadeDirection = facadeVector.normalized; Vector3 facadeNormal = Vector3.Cross(facadeDirection, Vector3.up); Vector4 facadeTangent = BuildRMesh.CalculateTangent(facadeDirection); RandomGen rGen = new RandomGen(); rGen.GenerateNewSeed(); float wallThickness = data.wallThickness; float foundation = data.foundationDepth; BuildingMeshTypes meshType = data.meshType; BuildingColliderTypes colliderType = data.colliderType; int wallSections = 0; Vector2 wallSectionSize; float facadeLength = 0; if (data.isStraight) { facadeLength = facadeVector.magnitude; wallSections = Mathf.FloorToInt(facadeLength / data.minimumWallUnitLength); if (wallSections < 1) { wallSections = 1; } wallSectionSize = new Vector2(facadeLength / wallSections, data.floorHeight); } else { wallSections = data.anchors.Count - 1; if (wallSections < 1) { wallSections = 1; } float sectionWidth = Vector2.Distance(data.anchors[0].vector2, data.anchors[1].vector2); wallSectionSize = new Vector2(sectionWidth, data.floorHeight); } Dictionary <WallSection, RawMeshData> generatedSections = new Dictionary <WallSection, RawMeshData>(); Dictionary <WallSection, RawMeshData> generatedSectionMeshColliders = new Dictionary <WallSection, RawMeshData>(); Dictionary <WallSection, BuildRCollider.BBox[]> generatedSectionPrimitiveColliders = new Dictionary <WallSection, BuildRCollider.BBox[]>(); int startFloor = data.startFloor; // Debug.Log("st fl "+startFloor); // Debug.Log("fl ct "+ data.floorCount); for (int fl = startFloor; fl < data.floorCount; fl++) { // Debug.Log(fl); if (data.facadeDesign.randomisationMode == Facade.RandomisationModes.RandomRows) { generatedSections.Clear(); //recalculate each row } // Debug.Log(wallSections); for (int s = 0; s < wallSections; s++) { // Debug.Log(s); WallSection section = data.facadeDesign.GetWallSection(s, fl + data.actualStartFloor, wallSections, data.floorCount); // Debug.Log(section); dmesh.submeshLibrary.Add(section); //add the wallsection to the main submesh library RawMeshData generatedSection = null; RawMeshData generatedSectionCollider = null; BuildRCollider.BBox[] bboxes = new BuildRCollider.BBox[0]; if (section == null) { GenerationOutput output = GenerationOutput.CreateRawOutput(); GenerationOutput outputCollider = null; if (colliderType == BuildingColliderTypes.Complex) { outputCollider = GenerationOutput.CreateRawOutput(); } if (colliderType == BuildingColliderTypes.Primitive) { BuildRCollider.BBox[] bbox = WallSectionGenerator.Generate(section, wallSectionSize, wallThickness); generatedSectionPrimitiveColliders.Add(section, bbox); } WallSectionGenerator.Generate(section, output, wallSectionSize, false, wallThickness, true, outputCollider, dmesh.submeshLibrary); generatedSection = output.raw; if (outputCollider != null) { generatedSectionCollider = outputCollider.raw; } } else { if (generatedSections.ContainsKey(section)) { generatedSection = generatedSections[section]; if (generatedSectionMeshColliders.ContainsKey(section)) { generatedSectionCollider = generatedSectionMeshColliders[section]; } } else { GenerationOutput output = GenerationOutput.CreateRawOutput(); GenerationOutput outputCollider = null; bool cullOpening = data.cullDoors && section.isDoor; if (colliderType == BuildingColliderTypes.Complex) { outputCollider = GenerationOutput.CreateRawOutput(); } if (colliderType == BuildingColliderTypes.Primitive) { BuildRCollider.BBox[] bbox = WallSectionGenerator.Generate(section, wallSectionSize, wallThickness, cullOpening); generatedSectionPrimitiveColliders.Add(section, bbox); } WallSectionGenerator.Generate(section, output, wallSectionSize, false, wallThickness, cullOpening, outputCollider, dmesh.submeshLibrary); generatedSections.Add(section, output.raw); if (generatedSectionCollider != null) { generatedSectionMeshColliders.Add(section, outputCollider.raw); } generatedSection = output.raw; if (generatedSectionCollider != null) { generatedSectionCollider = outputCollider.raw; } } if (generatedSectionPrimitiveColliders.ContainsKey(section)) { bboxes = generatedSectionPrimitiveColliders[section]; } } // Debug.Log("data strt" + data.isStraight); if (data.isStraight) { Quaternion meshRot = Quaternion.LookRotation(facadeNormal, Vector3.up); Vector3 baseMeshPos = data.baseA + facadeDirection * wallSectionSize.x + Vector3.up * wallSectionSize.y; Vector3 wallSectionVector = new Vector3(wallSectionSize.x * s, wallSectionSize.y * fl, 0); baseMeshPos += meshRot * wallSectionVector; Vector3 meshPos = baseMeshPos + meshRot * -wallSectionSize * 0.5f; Vector2 uvOffset = new Vector2(wallSectionSize.x * s, wallSectionSize.y * fl); Vector2 uvOffsetScaled = uvOffset; if (section != null && section.wallSurface != null) { uvOffsetScaled = CalculateUv(uvOffsetScaled, section.wallSurface); } //TODO account for the mesh mode of the wall section - custom meshes if (meshType == BuildingMeshTypes.Full) { dmesh.AddData(generatedSection, meshPos, meshRot, Vector3.one, uvOffsetScaled); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddData(generatedSectionCollider, meshPos, meshRot, Vector3.one); } if (collider != null && bboxes.Length > 0) { collider.AddBBox(bboxes, meshPos, meshRot); } // Debug.Log("foundation"); if (fl == 0 && foundation > Mathf.Epsilon) { Vector3 fp3 = baseMeshPos + Vector3.down * wallSectionSize.y; Vector3 fp2 = fp3 - facadeDirection * wallSectionSize.x; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Full) { Surface foundationSurface = data.foundationSurface != null ? data.foundationSurface : section.wallSurface; int foundationSubmesh = dmesh.submeshLibrary.SubmeshAdd(foundationSurface); //facadeSurfaces.IndexOf(section.wallSurface)); dmesh.AddPlane(fp0, fp1, fp2, fp3, new Vector2(uvOffset.x, -foundation), new Vector2(uvOffset.x + wallSectionSize.x, 0), -facadeNormal, facadeTangent, foundationSubmesh, foundationSurface); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } else { //todo switch - support wall section based curves for now Vector3 cp0 = data.anchors[s].vector3XZ; cp0.y = data.baseA.y; Vector3 cp1 = data.anchors[s + 1].vector3XZ; cp1.y = data.baseA.y; Vector3 curveVector = cp1 - cp0; Vector3 curveDirection = curveVector.normalized; Vector3 curveNormal = Vector3.Cross(curveDirection, Vector3.up); float actualWidth = curveVector.magnitude; Quaternion meshRot = Quaternion.LookRotation(curveNormal, Vector3.up); Vector3 meshPos = cp1 + Vector3.up * wallSectionSize.y; Vector3 wallSectionVector = new Vector3(0, wallSectionSize.y * fl, 0); meshPos += meshRot * wallSectionVector; meshPos += meshRot * -new Vector3(actualWidth, wallSectionSize.y, 0) * 0.5f; Vector3 meshScale = new Vector3(actualWidth / wallSectionSize.x, 1, 1); //Thanks Anthony Cuellar - issue #12 Vector2 uvOffset = new Vector2(wallSectionVector.x, wallSectionVector.y + (section.hasOpening ? 0 : wallSectionSize.y / 2f)); Vector2 uvOffsetScaled = CalculateUv(uvOffset, section.wallSurface); //TODO account for the mesh mode of the wall section - custom meshes if (meshType == BuildingMeshTypes.Full) { dmesh.AddData(generatedSection, meshPos, meshRot, meshScale, uvOffsetScaled); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddData(generatedSectionCollider, meshPos, meshRot, meshScale); } if (collider != null && bboxes.Length > 0) { collider.AddBBox(bboxes, meshPos, meshRot); } // Debug.Log("foundation"); if (fl == 0 && foundation > Mathf.Epsilon) { Vector3 fp3 = cp1; Vector3 fp2 = fp3 - curveDirection * actualWidth; Vector3 fp0 = fp2 + Vector3.down * foundation; Vector3 fp1 = fp3 + Vector3.down * foundation; if (meshType == BuildingMeshTypes.Full) { Surface foundationSurface = data.foundationSurface != null ? data.foundationSurface : section.wallSurface; int foundationSubmesh = dmesh.submeshLibrary.SubmeshAdd(foundationSurface); //facadeSurfaces.IndexOf(section.wallSurface); dmesh.AddPlane(fp0, fp1, fp2, fp3, new Vector2(uvOffset.x, -foundation), new Vector2(uvOffset.x + actualWidth, 0), -curveNormal, facadeTangent, foundationSubmesh, foundationSurface); } if (collider != null && generatedSectionCollider != null) { collider.mesh.AddPlane(fp0, fp1, fp2, fp3, 0); } } } } //string course is completely ignored for a collision // Debug.Log("string"); if (fl > 0 && data.facadeDesign.stringCourse && meshType == BuildingMeshTypes.Full) //no string course on ground floor { float baseStringCoursePosition = wallSectionSize.y * fl + wallSectionSize.y * data.facadeDesign.stringCoursePosition; Vector3 scBaseUp = baseStringCoursePosition * Vector3.up; Vector3 scTopUp = (data.facadeDesign.stringCourseHeight + baseStringCoursePosition) * Vector3.up; if (data.isStraight) { Vector3 scNm = data.facadeDesign.stringCourseDepth * facadeNormal; Vector3 p0 = data.baseA; Vector3 p1 = data.baseB; Vector3 p0o = data.baseA - scNm; Vector3 p1o = data.baseB - scNm; int submesh = dmesh.submeshLibrary.SubmeshAdd(data.facadeDesign.stringCourseSurface); //data.facadeDesign.stringCourseSurface != null ? facadeSurfaces.IndexOf(data.facadeDesign.stringCourseSurface) : 0; Vector2 uvMax = new Vector2(facadeLength, data.facadeDesign.stringCourseHeight); dmesh.AddPlane(p0o + scBaseUp, p1o + scBaseUp, p0o + scTopUp, p1o + scTopUp, Vector3.zero, uvMax, -facadeNormal, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //front dmesh.AddPlane(p0 + scBaseUp, p0o + scBaseUp, p0 + scTopUp, p0o + scTopUp, facadeNormal, facadeTangent, submesh); //left dmesh.AddPlane(p1o + scBaseUp, p1 + scBaseUp, p1o + scTopUp, p1 + scTopUp, facadeNormal, facadeTangent, submesh); //right float facadeAngle = BuildrUtils.CalculateFacadeAngle(facadeDirection); dmesh.AddPlaneComplexUp(p0 + scBaseUp, p1 + scBaseUp, p0o + scBaseUp, p1o + scBaseUp, facadeAngle, Vector3.down, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //bottom dmesh.AddPlaneComplexUp(p1 + scTopUp, p0 + scTopUp, p1o + scTopUp, p0o + scTopUp, facadeAngle, Vector3.up, facadeTangent, submesh, data.facadeDesign.stringCourseSurface); //top } else { int baseCurvePointCount = data.anchors.Count; //baseCurvepoints.Count; Vector3[] interSectionNmls = new Vector3[baseCurvePointCount]; for (int i = 0; i < baseCurvePointCount - 1; i++) { Vector3 p0 = data.anchors[i].vector3XZ; //baseCurvepoints[i]; Vector3 p1 = data.anchors[i + 1].vector3XZ; //baseCurvepoints[i + 1]; Vector3 p2 = data.anchors[Mathf.Max(i - 1, 0)].vector3XZ; //baseCurvepoints[Mathf.Max(i - 1, 0)]; interSectionNmls[i] = Vector3.Cross((p1 - p0 + p0 - p2).normalized, Vector3.up); } for (int i = 0; i < baseCurvePointCount - 1; i++) { Vector3 p0 = data.anchors[i].vector3XZ; //baseCurvepoints[i]; Vector3 p1 = data.anchors[i + 1].vector3XZ; //baseCurvepoints[i + 1]; Vector3 sectionVector = p1 - p0; Vector3 sectionDir = sectionVector.normalized; Vector3 sectionNml = Vector3.Cross(sectionDir, Vector3.up); Vector4 sectionTgnt = BuildRMesh.CalculateTangent(sectionDir); Vector3 scNmA = data.facadeDesign.stringCourseDepth * interSectionNmls[i + 0]; Vector3 scNmB = data.facadeDesign.stringCourseDepth * interSectionNmls[i + 1]; Vector3 p0o = p0 - scNmA; Vector3 p1o = p1 - scNmB; int submesh = dmesh.submeshLibrary.SubmeshAdd(data.facadeDesign.stringCourseSurface); //data.facadeDesign.stringCourseSurface != null ? facadeSurfaces.IndexOf(data.facadeDesign.stringCourseSurface) : 0; dmesh.AddPlane(p0o + scBaseUp, p1o + scBaseUp, p0o + scTopUp, p1o + scTopUp, sectionNml, sectionTgnt, submesh); dmesh.AddPlane(p0 + scBaseUp, p0o + scBaseUp, p0 + scTopUp, p0o + scTopUp, sectionNml, sectionTgnt, submesh); dmesh.AddPlane(p1o + scBaseUp, p1 + scBaseUp, p1o + scTopUp, p1 + scTopUp, sectionNml, sectionTgnt, submesh); float facadeAngle = BuildrUtils.CalculateFacadeAngle(sectionDir); dmesh.AddPlaneComplexUp(p0 + scBaseUp, p1 + scBaseUp, p0o + scBaseUp, p1o + scBaseUp, facadeAngle, Vector3.down, sectionTgnt, submesh, data.facadeDesign.stringCourseSurface); //bottom dmesh.AddPlaneComplexUp(p1 + scTopUp, p0 + scTopUp, p1o + scTopUp, p0o + scTopUp, facadeAngle, Vector3.up, sectionTgnt, submesh, data.facadeDesign.stringCourseSurface); //top } } } } }