/// <summary> Builds flat floors. </summary> protected List <MeshData> BuildFloors(Building building, int floorCount, bool lastFloorIsRoof, int extraMeshCount = 0) { var mesh = CreateMesh(building.Footprint); var floorHeight = building.Height / building.Levels; var bottomOffset = building.Elevation + building.MinHeight; var vertexPerFloor = mesh.Triangles.Count * 3 * 2; int vertexCount = vertexPerFloor * floorCount; int meshCount = 1; int floorsPerIteration = floorCount; var twoSizedMeshCount = vertexCount * 2; if (twoSizedMeshCount > Consts.MaxMeshSize) { Trace.Warn(LogCategory, Strings.MeshHasMaxVertexLimit, building.Id.ToString(), twoSizedMeshCount.ToString()); meshCount = (int)Math.Ceiling((double)twoSizedMeshCount / Consts.MaxMeshSize); floorsPerIteration = floorCount / meshCount; } var meshDataList = new List <MeshData>(meshCount + extraMeshCount); for (int i = 0; i < meshCount; i++) { var stepFloorCount = (i != meshCount - 1 || meshCount == 1) ? floorsPerIteration : floorsPerIteration + floorCount % meshCount; var stepVertexCount = vertexPerFloor * stepFloorCount; var stepBottomOffset = bottomOffset + i * (floorsPerIteration * floorHeight); var meshIndex = new MultiPlaneMeshIndex(stepFloorCount, stepVertexCount); var meshData = new MeshData(meshIndex, stepVertexCount); AttachFloors(new RoofContext() { Mesh = mesh, MeshData = meshData, MeshIndex = meshIndex, Bottom = stepBottomOffset, FloorCount = stepFloorCount, FloorHeight = floorHeight, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = i == meshCount - 1 && lastFloorIsRoof, RoofFrontGradient = CustomizationService.GetGradient(building.RoofColor), RoofBackGradient = CustomizationService.GetGradient(building.RoofColor), }); meshDataList.Add(meshData); } return(meshDataList); }
/// <inheritdoc /> public override IGameObject BuildWay(Tile tile, Rule rule, Way way) { if (way.Points.Count < 2) { Trace.Warn("model.barrier", Strings.InvalidPolyline); return(null); } if (tile.Registry.Contains(way.Id)) { return(null); } tile.Registry.RegisterGlobal(way.Id); var gameObjectWrapper = GameObjectFactory.CreateNew(GetName(way)); var maxWidth = 4f; var points = ObjectPool.NewList <Vector2d>(way.Points.Count); PointUtils.SetPolygonPoints(tile.RelativeNullPoint, way.Points, points); var vertexCount = GetVertexCount(points, maxWidth); var meshIndex = new MultiPlaneMeshIndex(points.Count - 1, vertexCount); var meshData = new MeshData(meshIndex, vertexCount) { MaterialKey = rule.GetMaterialKey(), GameObject = gameObjectWrapper, }; meshData.Index = meshIndex; var context = new SegmentBuilderContext() { MeshData = meshData, Gradient = CustomizationService.GetGradient(rule.GetFillColor()), ColorNoiseFreq = rule.GetColorNoiseFreq(), Height = rule.GetHeight(), MaxWidth = maxWidth, }; var index = 0; for (int i = 0; i < points.Count - 1; i++) { var p1 = points[i]; var p2 = points[i + 1]; var start = new Vector3((float)p1.X, ElevationProvider.GetElevation(p1), (float)p1.Y); var end = new Vector3((float)p2.X, ElevationProvider.GetElevation(p2), (float)p2.Y); meshIndex.AddPlane(new Vector3((float)p1.X, 0, (float)p1.Y), start, end, meshData.NextIndex); BuildBarrierSegment(context, start, end, ref index); } ObjectPool.StoreList(points); BuildObject(tile.GameObject, meshData, rule, way); return(gameObjectWrapper); }
/// <inheritdoc /> public override IGameObject BuildArea(Tile tile, Rule rule, Area area) { base.BuildArea(tile, rule, area); if (tile.Registry.Contains(area.Id)) { return(null); } double radius; Vector2d center; CircleUtils.GetCircle(tile.RelativeNullPoint, area.Points, out radius, out center); var elevation = ElevationProvider.GetElevation(center); var height = rule.GetHeight(); var minHeight = rule.GetMinHeight(); var actualHeight = (height - minHeight); var color = rule.GetFillColor(); var gradient = CustomizationService.GetGradient(color); tile.Registry.RegisterGlobal(area.Id); var cylinderGen = new CylinderGenerator() .SetCenter(new Vector3((float)center.X, elevation + minHeight, (float)center.Y)) .SetHeight(actualHeight) .SetMaxSegmentHeight(5f) .SetRadialSegments(7) .SetRadius((float)radius) .SetGradient(gradient); var meshData = new MeshData(MeshDestroyIndex.Default, cylinderGen.CalculateVertexCount()) { GameObject = GameObjectFactory.CreateNew(GetName(area)), MaterialKey = rule.GetMaterialKey() }; cylinderGen.Build(meshData); BuildObject(tile.GameObject, meshData, rule, area); return(meshData.GameObject); }
/// <inheritdoc /> public override IGameObject BuildArea(Tile tile, Rule rule, Area area) { base.BuildArea(tile, rule, area); if (tile.Registry.Contains(area.Id)) { return(null); } tile.Registry.RegisterGlobal(area.Id); double radius; Vector2d center; CircleUtils.GetCircle(tile.RelativeNullPoint, area.Points, out radius, out center); var elevation = ElevationProvider.GetElevation(center); var minHeight = rule.GetMinHeight(); var color = rule.GetFillColor(); var gradient = CustomizationService.GetGradient(color); int recursionLevel = rule.EvaluateDefault("recursion_level", 2); var center3d = new Vector3((float)center.X, elevation + minHeight, (float)center.Y); var sphereGen = new IcoSphereGenerator() .SetCenter(center3d) .SetRadius((float)radius) .SetRecursionLevel(recursionLevel) .SetGradient(gradient); var meshData = new MeshData(new SphereMeshIndex((float)radius, center3d), sphereGen.CalculateVertexCount()); meshData.GameObject = GameObjectFactory.CreateNew(GetName(area)); meshData.MaterialKey = rule.GetMaterialKey(); sphereGen.Build(meshData); BuildObject(tile.GameObject, meshData, rule, area); return(meshData.GameObject); }
private void CreateForest(IGameObject parent, Rule rule, Mesh mesh) { var trunkGradientKey = rule.Evaluate <string>("trunk-color"); var foliageGradientKey = rule.Evaluate <string>("foliage-color"); int treeFreq = (int)(1 / rule.EvaluateDefault <float>("tree-freq", 0.1f)); // TODO reuse tree builder? // TODO behaviour should be set somehow var node = new Node(); foreach (var triangle in mesh.Triangles) { // TODO reuse mesh and/or generator? if (triangle.Id % treeFreq != 0) { continue; } var v0 = triangle.GetVertex(0); var v1 = triangle.GetVertex(1); var v2 = triangle.GetVertex(2); var center = new Vector2d((v0.X + v1.X + v2.X) / 3, (v0.Y + v1.Y + v2.Y) / 3); var elevation = ElevationProvider.GetElevation(center); var treeGen = new TreeGenerator() .SetTrunkGradient(CustomizationService.GetGradient(trunkGradientKey)) .SetFoliageGradient(CustomizationService.GetGradient(foliageGradientKey)) .SetPosition(new Vector3((float)center.X, elevation, (float)center.Y)); var meshData = new MeshData(MeshDestroyIndex.Default, treeGen.CalculateVertexCount()); meshData.GameObject = GameObjectFactory.CreateNew("tree"); meshData.MaterialKey = rule.GetMaterialKey(); treeGen.Build(meshData); BuildObject(parent, meshData, rule, node); } }
private void BuildSurface(Rule rule, TerrainMeshData meshData, MeshRegion meshRegion, RenderMode renderMode) { if (meshRegion.Mesh == null) { return; } float colorNoiseFreq = renderMode == RenderMode.Scene ? meshRegion.ColorNoiseFreq : 0; float eleNoiseFreq = renderMode == RenderMode.Scene ? meshRegion.ElevationNoiseFreq : 0; var gradient = _customizationService.GetGradient(meshRegion.GradientKey); if (meshRegion.ModifyMeshAction != null) { meshRegion.ModifyMeshAction(meshRegion.Mesh); } foreach (var triangle in meshRegion.Mesh.Triangles) { AddTriangle(rule, meshData, triangle, gradient, eleNoiseFreq, colorNoiseFreq); } meshRegion.Dispose(); }
/// <inheritdoc /> public override IGameObject BuildNode(Tile tile, Rule rule, Node node) { var mapPoint = GeoProjection.ToMapCoordinate(tile.RelativeNullPoint, node.Point); var elevation = ElevationProvider.GetElevation(mapPoint); var trunkGradientKey = rule.Evaluate <string>("trunk-color"); var foliageGradientKey = rule.Evaluate <string>("foliage-color"); var treeGen = new TreeGenerator() .SetTrunkGradient(CustomizationService.GetGradient(trunkGradientKey)) .SetFoliageGradient(CustomizationService.GetGradient(foliageGradientKey)) .SetPosition(new Vector3((float)mapPoint.X, elevation, (float)mapPoint.Y)); var meshData = new MeshData(MeshDestroyIndex.Default, treeGen.CalculateVertexCount()); meshData.GameObject = GameObjectFactory.CreateNew("tree " + node.Id); meshData.MaterialKey = rule.GetMaterialKey(); treeGen.Build(meshData); BuildObject(tile.GameObject, meshData, rule, node); return(meshData.GameObject); }
public override List <MeshData> Build(Building building) { var roofOffset = building.Elevation + building.MinHeight + building.Height; var roofHeight = roofOffset + building.RoofHeight; // 1. detect the longest segment float length; Vector2d longestStart; Vector2d longestEnd; GetLongestSegment(building.Footprint, out length, out longestStart, out longestEnd); // 2. get direction vector var ridgeDirection = (new Vector3((float)longestEnd.X, roofOffset, (float)longestEnd.Y) - new Vector3((float)longestStart.X, roofOffset, (float)longestStart.Y)).normalized; // 3. get centroid var centroidPoint = PolygonUtils.GetCentroid(building.Footprint); var centroidVector = new Vector3((float)centroidPoint.X, roofHeight, (float)centroidPoint.Y); // 4. get something like center line Vector3 p1 = centroidVector + length * length * ridgeDirection; Vector3 p2 = centroidVector - length * length * ridgeDirection; // 5. detect segments which have intesection with center line Vector2d first, second; int firstIndex, secondIndex; DetectIntersectSegments(building.Footprint, new Vector2d(p1.x, p1.z), new Vector2d(p2.x, p2.z), out first, out firstIndex, out second, out secondIndex); if (firstIndex == -1 || secondIndex == -1) { Trace.Warn(LogCategory, Strings.RoofGenFailed, Name, building.Id.ToString()); return(base.Build(building)); } // prepare mesh and its index var mesh = CreateMesh(building.Footprint); var floorCount = building.Levels; var floorVertexCount = mesh.Triangles.Count * 3 * 2 * floorCount; var roofVertexCount = (building.Footprint.Count - 1) * 2 * 12; var vertexCount = roofVertexCount + floorVertexCount; var planeCount = building.Footprint.Count + floorCount; bool limitIsReached = false; if (vertexCount * 2 > Consts.MaxMeshSize) { vertexCount = roofVertexCount; planeCount = building.Footprint.Count; limitIsReached = true; } var meshIndex = new MultiPlaneMeshIndex(planeCount, vertexCount); var meshData = new MeshData(meshIndex, vertexCount); // 6. process all segments and create vertices FillMeshData(meshData, CustomizationService.GetGradient(building.RoofColor), roofOffset, roofHeight, building.Footprint, first, firstIndex, second, secondIndex); if (!limitIsReached) { AttachFloors(new RoofContext() { Mesh = mesh, MeshData = meshData, MeshIndex = meshIndex, Bottom = building.Elevation + building.MinHeight, FloorCount = floorCount, FloorHeight = building.Height / floorCount, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = false }); return(new List <MeshData>(1) { meshData }); } var meshDataList = BuildFloors(building, building.Levels, false); meshDataList.Add(meshData); return(meshDataList); }
/// <inheritdoc /> public override List <MeshData> Build(Building building) { var roofHeight = building.RoofHeight; var roofOffset = building.Elevation + building.MinHeight + building.Height; var skeleton = SkeletonBuilder.Build(building.Footprint); var roofVertexCount = 0; foreach (var edgeResult in skeleton.Edges) { roofVertexCount += (edgeResult.Polygon.Count - 2) * 12; } var mesh = CreateMesh(building.Footprint); var floorCount = building.Levels; var floorVertexCount = mesh.Triangles.Count * 3 * 2 * floorCount; var vertexCount = roofVertexCount + floorVertexCount; var planeCount = skeleton.Edges.Count + floorCount; bool limitIsReached = false; if (vertexCount * 2 > Consts.MaxMeshSize) { vertexCount = roofVertexCount; planeCount = building.Footprint.Count; limitIsReached = true; } var meshIndex = new MultiPlaneMeshIndex(planeCount + floorCount, vertexCount); MeshData meshData = new MeshData(meshIndex, vertexCount); try { var roofGradient = CustomizationService.GetGradient(building.RoofColor); foreach (var edge in skeleton.Edges) { if (edge.Polygon.Count < 5) { HandleSimpleCase(meshData, meshIndex, roofGradient, skeleton, edge, roofOffset, roofHeight); } else { HandleComplexCase(meshData, meshIndex, roofGradient, skeleton, edge, roofOffset, roofHeight); } } if (!limitIsReached) { // attach floors AttachFloors(new RoofContext() { Mesh = mesh, MeshData = meshData, MeshIndex = meshIndex, Bottom = building.Elevation + building.MinHeight, FloorCount = floorCount, FloorHeight = building.Height / floorCount, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = false }); return(new List <MeshData>(1) { meshData }); } var meshDataList = BuildFloors(building, building.Levels, false); meshDataList.Add(meshData); return(meshDataList); } catch { // NOTE straight skeleton may fail on some footprints. Trace.Warn("building.roof", Strings.RoofGenFailed, Name, building.Id.ToString()); return(base.Build(building)); } }
/// <inheritdoc /> public override List <MeshData> Build(Building building) { var random = new System.Random((int)building.Id); var footprint = building.Footprint; var roofOffset = building.Elevation + building.MinHeight + building.Height; var roofHeight = roofOffset + building.RoofHeight; var offset = new ClipperOffset(); offset.AddPath(footprint.Select(p => new IntPoint(p.X * Scale, p.Y * Scale)).ToList(), JoinType.jtMiter, EndType.etClosedPolygon); var result = new List <List <IntPoint> >(); offset.Execute(ref result, random.NextFloat(1, 3) * -Scale); if (result.Count != 1 || result[0].Count != footprint.Count) { Trace.Warn(LogCategory, Strings.RoofGenFailed, Name, building.Id.ToString()); return(base.Build(building)); } var topVertices = ObjectPool.NewList <Vector2d>(footprint.Count); double scale = Scale; foreach (var intPoint in result[0]) { topVertices.Add(new Vector2d(intPoint.X / scale, intPoint.Y / scale)); } // NOTE need reverse vertices topVertices.Reverse(); var floorCount = building.Levels; var topMesh = CreateMesh(topVertices); var floorMesh = CreateMesh(footprint); var roofVertexCount = topMesh.Triangles.Count * 3 * 2 + footprint.Count * 2 * 12; var floorVertexCount = floorMesh.Triangles.Count * 3 * 2 * floorCount; var vertexCount = roofVertexCount + floorVertexCount; var planeCount = footprint.Count + floorCount + 1; bool limitIsReached = false; if (vertexCount * 2 > Consts.MaxMeshSize) { vertexCount = roofVertexCount; planeCount = building.Footprint.Count + 1; limitIsReached = true; } var meshIndex = new MultiPlaneMeshIndex(planeCount, vertexCount); var meshData = new MeshData(meshIndex, vertexCount); var roofGradient = CustomizationService.GetGradient(building.RoofColor); int index = FindStartIndex(topVertices[0], footprint); for (int i = 0; i < topVertices.Count; i++) { var top = topVertices[i]; var bottom = footprint[(index + i) % footprint.Count]; var nextTop = topVertices[(i + 1) % topVertices.Count]; var nextBottom = footprint[(index + i + 1) % footprint.Count]; var v0 = new Vector3((float)bottom.X, roofOffset, (float)bottom.Y); var v1 = new Vector3((float)nextBottom.X, roofOffset, (float)nextBottom.Y); var v2 = new Vector3((float)nextTop.X, roofHeight, (float)nextTop.Y); var v3 = new Vector3((float)top.X, roofHeight, (float)top.Y); meshIndex.AddPlane(v0, v1, v2, meshData.NextIndex); AddTriangle(meshData, roofGradient, v0, v2, v3); AddTriangle(meshData, roofGradient, v2, v0, v1); } ObjectPool.StoreList(topVertices); // Attach top reusing roof context object var context = new RoofContext() { Mesh = topMesh, MeshData = meshData, MeshIndex = meshIndex, Bottom = roofHeight, FloorCount = 1, FloorHeight = building.Height / floorCount, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = true, RoofFrontGradient = roofGradient, RoofBackGradient = roofGradient }; AttachFloors(context); if (!limitIsReached) { context.Mesh = floorMesh; context.MeshData = meshData; context.Bottom = building.Elevation + building.MinHeight; context.FloorCount = floorCount; context.IsLastRoof = false; AttachFloors(context); return(new List <MeshData>(1) { meshData }); } var meshDataList = BuildFloors(building, building.Levels, false); meshDataList.Add(meshData); return(meshDataList); }
/// <inheritdoc /> public override List <MeshData> Build(Building building) { var center = PolygonUtils.GetCentroid(building.Footprint); var roofOffset = building.Elevation + building.MinHeight + building.Height; var footprint = building.Footprint; var roofHeight = building.RoofHeight; var floorCount = building.Levels; var length = footprint.Count; var mesh = CreateMesh(footprint); var roofVertexCount = 12 * length; var floorVertexCount = mesh.Triangles.Count * 3 * 2 * floorCount; var vertexCount = roofVertexCount + floorVertexCount; var planeCount = building.Footprint.Count + floorCount; bool limitIsReached = false; if (vertexCount * 2 > Consts.MaxMeshSize) { vertexCount = roofVertexCount; planeCount = building.Footprint.Count; limitIsReached = true; } var meshIndex = new MultiPlaneMeshIndex(planeCount, vertexCount); var meshData = new MeshData(meshIndex, vertexCount); var roofGradient = CustomizationService.GetGradient(building.RoofColor); for (int i = 0; i < length; i++) { var nextIndex = i == (length - 1) ? 0 : i + 1; var v0 = new Vector3((float)footprint[i].X, roofOffset, (float)footprint[i].Y); var v1 = new Vector3((float)center.X, roofOffset + roofHeight, (float)center.Y); var v2 = new Vector3((float)footprint[nextIndex].X, roofOffset, (float)footprint[nextIndex].Y); var v01 = Vector3Utils.GetIntermediatePoint(v0, v1); var v12 = Vector3Utils.GetIntermediatePoint(v1, v2); var v02 = Vector3Utils.GetIntermediatePoint(v0, v2); meshIndex.AddPlane(v0, v1, v2, meshData.NextIndex); var color = GetColor(roofGradient, v0); meshData.AddTriangle(v0, v01, v02, color, color); color = GetColor(roofGradient, v01); meshData.AddTriangle(v02, v01, v12, color, color); color = GetColor(roofGradient, v02); meshData.AddTriangle(v2, v02, v12, color, color); color = GetColor(roofGradient, v01); meshData.AddTriangle(v01, v1, v12, color, color); } if (!limitIsReached) { AttachFloors(new RoofContext() { Mesh = mesh, MeshData = meshData, MeshIndex = meshIndex, Bottom = building.Elevation + building.MinHeight, FloorCount = floorCount, FloorHeight = building.Height / floorCount, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = false, }); return(new List <MeshData>(1) { meshData }); } var meshDataList = BuildFloors(building, building.Levels, false); meshDataList.Add(meshData); return(meshDataList); }
/// <inheritdoc /> public List <MeshData> Build(Building building) { var random = new System.Random((int)building.Id); var footprint = building.Footprint; var elevation = building.MinHeight + building.Elevation; var gradient = _customizationService.GetGradient(building.FacadeColor); var hasLevels = building.Levels > 1; float height = hasLevels ? building.Height / building.Levels + 0.01f // fix for rounding issue : random.NextFloat(5f, 6f); WallBuilder wallBuilder = building.HasWindows ? new WindowWallBuilder().SetStepWidth(random.NextFloat(3, 4)) : new EmptyWallBuilder().SetStepWidth(random.NextFloat(10f, 12f)); // if windows are built, we want to have levels built too if (building.HasWindows && !hasLevels) { building.Levels = (int)Math.Ceiling(building.Height / height); } wallBuilder .SetGradient(gradient) .SetMinHeight(elevation) .SetStepHeight(height) .SetHeight(building.Height); int limitIndex; var vertCount = CalculateVertexCount(wallBuilder, building.Footprint, elevation, 0, out limitIndex); var twoSizedMeshCount = vertCount * 2; if (limitIndex != building.Footprint.Count) { Trace.Warn(LogCategory, Strings.MeshHasMaxVertexLimit, building.Id.ToString(), twoSizedMeshCount.ToString()); var meshDataList = new List <MeshData>(2); int startIndex = 0; while (startIndex != footprint.Count) { meshDataList.Add(BuildMeshData(wallBuilder, footprint, vertCount, elevation, startIndex, limitIndex)); startIndex = limitIndex; vertCount = CalculateVertexCount(wallBuilder, building.Footprint, elevation, limitIndex, out limitIndex); } return(meshDataList); } var meshData = BuildMeshData(wallBuilder, footprint, vertCount, elevation, 0, footprint.Count); return(new List <MeshData>(1) { meshData }); }
/// <inheritdoc /> public override List <MeshData> Build(Building building) { Vector2d center; double radius; CircleUtils.GetCircle(building.Footprint, out radius, out center); var center3d = new Vector3((float)center.X, building.Elevation + building.MinHeight + building.Height, (float)center.Y); var sphereGen = new IcoSphereGenerator() .SetCenter(center3d) .SetRadius((float)radius) .SetRecursionLevel(2) .IsSemiphere(true) .SetGradient(CustomizationService.GetGradient(building.RoofColor)); var mesh = CreateMesh(building.Footprint); var floorCount = building.Levels; var floorVertexCount = mesh.Triangles.Count * 3 * 2 * floorCount; IMeshIndex floorMeshIndex = new MultiPlaneMeshIndex(building.Levels, floorVertexCount); var roofVertexCount = sphereGen.CalculateVertexCount(); var vertexCount = roofVertexCount + floorVertexCount; bool limitIsReached = false; if (vertexCount * 2 > Consts.MaxMeshSize) { vertexCount = roofVertexCount; limitIsReached = true; floorMeshIndex = DummyMeshIndex.Default; } var meshIndex = new CompositeMeshIndex(2) .AddMeshIndex(new SphereMeshIndex((float)radius, center3d)) .AddMeshIndex(floorMeshIndex); var meshData = new MeshData(meshIndex, vertexCount); // attach roof sphereGen.Build(meshData); if (!limitIsReached) { // attach floors AttachFloors(new RoofContext() { Mesh = mesh, MeshData = meshData, MeshIndex = (MultiPlaneMeshIndex)floorMeshIndex, Bottom = building.Elevation + building.MinHeight, FloorCount = floorCount, FloorHeight = building.Height / floorCount, FloorFrontGradient = CustomizationService.GetGradient(building.FloorFrontColor), FloorBackGradient = CustomizationService.GetGradient(building.FloorBackColor), IsLastRoof = false }); return(new List <MeshData>(1) { meshData }); } var meshDataList = BuildFloors(building, building.Levels, false); meshDataList.Add(meshData); return(meshDataList); }