private MultiPlaneMeshIndex GetMeshIndex(out Vector3[] vertices) { var plane = new List <Vector3>() { new Vector3(0, 0, 0), new Vector3(10, 0, 0), new Vector3(10, 0, 10), new Vector3(0, 0, 10) }; var center = new Vector3(5, 5, 5); vertices = new Vector3[plane.Count * 2 * 3]; var halfVertCount = vertices.Length / 2; var meshIndex = new MultiPlaneMeshIndex(plane.Count, halfVertCount); for (int i = 0; i < plane.Count; i++) { var start = plane[i]; var end = plane[i == plane.Count - 1 ? 0 : i + 1]; meshIndex.AddPlane(start, end, center, i * 3); vertices[i * 3] = start; vertices[i * 3 + 1] = end; vertices[i * 3 + 2] = center; vertices[halfVertCount + i * 3] = start; vertices[halfVertCount + i * 3 + 1] = end; vertices[halfVertCount + i * 3 + 2] = center; } return(meshIndex); }
private void HandleSimpleCase(MeshData meshData, MultiPlaneMeshIndex meshIndex, GradientWrapper gradient, Skeleton skeleton, EdgeResult edge, float roofOffset, float roofHeight) { var polygon = edge.Polygon; var distances = skeleton.Distances; for (int i = 0; i <= polygon.Count - 2; i += 2) { var p0 = polygon[i]; var p1 = polygon[i + 1]; var p2 = polygon[i + 2 == polygon.Count ? 0 : i + 2]; var v0 = new Vector3((float)p0.X, distances[p0] > 0 ? roofHeight + roofOffset : roofOffset, (float)p0.Y); var v1 = new Vector3((float)p1.X, distances[p1] > 0 ? roofHeight + roofOffset : roofOffset, (float)p1.Y); var v2 = new Vector3((float)p2.X, distances[p2] > 0 ? roofHeight + roofOffset : roofOffset, (float)p2.Y); if (i == 0) { meshIndex.AddPlane(v0, v1, v2, meshData.NextIndex); } AddTriangle(meshData, gradient, v0, v1, v2); } }
private MeshData BuildMeshData(WallBuilder emptyWallBuilder, List <Vector2d> footprint, int vertCount, float elevation, int startIndex, int endIndex) { var meshIndex = new MultiPlaneMeshIndex(footprint.Count, vertCount); var meshData = new MeshData(meshIndex, vertCount); emptyWallBuilder.SetMeshData(meshData); for (int i = startIndex; i < endIndex; i++) { var nextIndex = i == (footprint.Count - 1) ? 0 : i + 1; var start = footprint[i]; var end = footprint[nextIndex]; var startVector = new Vector3((float)start.X, elevation, (float)start.Y); var endVector = new Vector3((float)end.X, elevation, (float)end.Y); var somePointOnPlane = new Vector3((float)end.X, elevation + 10, (float)end.Y); meshIndex.AddPlane(startVector, endVector, somePointOnPlane, meshData.NextIndex); emptyWallBuilder .SetStartIndex(meshData.NextIndex) .Build(startVector, endVector); } return(meshData); }
/// <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); }
/// <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); }
private void HandleComplexCase(MeshData meshData, MultiPlaneMeshIndex meshIndex, GradientWrapper gradient, Skeleton skeleton, EdgeResult edge, float roofOffset, float roofHeight) { var polygon = edge.Polygon; var distances = skeleton.Distances; using (var trianglePolygon = new Polygon(polygon.Count, ObjectPool)) { trianglePolygon.AddContour(polygon.Select(p => new Point(p.X, p.Y)).ToList()); var mesh = trianglePolygon.Triangulate(); bool planeIsAdded = false; foreach (var triangle in mesh.Triangles) { var p0 = new Vector2d(triangle.vertices[0].X, triangle.vertices[0].Y); var p1 = new Vector2d(triangle.vertices[1].X, triangle.vertices[1].Y); var p2 = new Vector2d(triangle.vertices[2].X, triangle.vertices[2].Y); double y; if (distances.TryGetValue(p0, out y) && y > 0) { y = roofHeight + roofOffset; } else { y = roofOffset; } var v0 = new Vector3((float)p0.X, (float)y, (float)p0.Y); if (distances.TryGetValue(p1, out y) && y > 0) { y = roofHeight + roofOffset; } else { y = roofOffset; } var v1 = new Vector3((float)p1.X, (float)y, (float)p1.Y); if (distances.TryGetValue(p2, out y) && y > 0) { y = roofHeight + roofOffset; } else { y = roofOffset; } var v2 = new Vector3((float)p2.X, (float)y, (float)p2.Y); if (!planeIsAdded) { meshIndex.AddPlane(v0, v1, v2, meshData.NextIndex); planeIsAdded = true; } AddTriangle(meshData, gradient, v0, v1, v2); } } }
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 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); }