private void CreateCornerFacades(ISubdivisionGeometry geometry, Footprint footprint, int topIndex, IReadOnlyList <IReadOnlyList <Vector2> > corners, string material) { Contract.Requires(geometry != null); Contract.Requires(corners != null); Contract.Requires(_floors != null); //Calculate altitude of bottom and top of this facade var bot = _floors[footprint.BottomIndex].FloorAltitude; var top = _floors[topIndex].FloorAltitude + _floors[topIndex].Bounds.Height; var mid = (bot + top) * 0.5f; //Fill in corner sections (solid) foreach (var corner in corners) { try { geometry.Union(geometry .CreatePrism(material, corner, top - bot) .Translate(new Vector3(0, mid, 0)) ); } catch (ArgumentException) { //Suppress the argument exception, why? //If we try to create a degenerate prism (negative height, only 2 points) we get an arg exception //Corner sections can be very slim, sometimes *so slim* that the four points merge together //In this case we don't care, the section is so small we'll just skip it! } } }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Create land parcels var nodes = CreateParcelNodes(GenerateParcels(bounds.Footprint).ToArray(), bounds.Height).ToArray(); //Set ground height (for nodes which care) foreach (var grounded in nodes.Select(node => node.Value).OfType <IGrounded>()) { grounded.GroundHeight = GroundHeight; } //Build neighbour set var neighbours = new NeighbourSet <ISubdivisionContext>(); foreach (var keyValuePair in nodes) { foreach (var edge in keyValuePair.Key.Edges) { neighbours.Add(new LineSegment2(edge.Start, edge.End), keyValuePair.Value); } } //Associate node with their neighbours (for nodes which care) foreach (var neighbour in nodes.Where(node => node.Value is INeighbour)) { ((INeighbour)neighbour.Value).Neighbours = CalculateNeighbours(neighbour, neighbours).ToArray(); } }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Select external building parameters var externals = SelectExternals().ToArray(); //Create things _floors = CreateFloors(SelectFloors().ToArray(), Footprints(externals)); _verticals = SelectVerticals().ToArray(); _facades = CreateFacades(geometry, externals, hierarchicalParameters); //Setup relationship between floors (floor PrerequisiteOf floor-further-from-ground) foreach (var keyValuePair in _floors) { //Ground floor has no prerequisites if (keyValuePair.Key == 0) { continue; } //Work out the index of the prerequisite var prereqIndex = keyValuePair.Key < 0 ? keyValuePair.Key + 1 : keyValuePair.Key - 1; //Try to find it (this *shouldn't* ever fail, if we have a continuous run of floors) IFloor prereq; if (_floors.TryGetValue(prereqIndex, out prereq)) { keyValuePair.Value.AddPrerequisite(prereq); } } }
private void CreateFootpathFromPair(ISubdivisionGeometry geometry, string material, float height, IHalfEdgeBuilder a, IHalfEdgeBuilder b) { Contract.Requires(geometry != null); Contract.Requires(a != null); Contract.Requires(b != null); //Determine the inner point we're curving around bool r1LeftInner = a.LeftEnd.TolerantEquals(b.RightEnd, 0.01f); bool r1RightInner = a.RightEnd.TolerantEquals(b.LeftEnd, 0.01f); //Straight on, no junction and no paths needed if (r1LeftInner && r1RightInner) { return; } if (r1LeftInner) { //One side is an inner side, create a footpath around that point CreateFootpath2Inner(geometry, material, height, true, a, b); } else { //No point is an inner point, follow around the outer edge connecting these two roads CreateFootpath2Outer(geometry, material, height, a, b); } }
/// <summary> /// Create the base block for this facade (which embossing stamps will be applied to) /// </summary> /// <param name="bounds"></param> /// <param name="geometry"></param> /// <param name="hierarchicalParameters"></param> /// <returns></returns> protected virtual ICsgShape CreateFacade(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { Contract.Requires(geometry != null); Contract.Requires(hierarchicalParameters != null); return(geometry.CreatePrism(hierarchicalParameters.DefaultMaterial(Random), bounds.Footprint, bounds.Height)); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Generate a topological map of city roads var m = GenerateBlockMesh(); //Materialize the topological map into a topographical one MaterializeMesh(m); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { if (!_deleted) { base.Subdivide(bounds, geometry, _parameters ?? hierarchicalParameters); } IsSubdivided = true; }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { var height = (float)Math.Min(bounds.Height, Math.Sqrt(Math.Abs(bounds.Footprint.Area())) * (Random() + 0.5)); var material = hierarchicalParameters.DefaultMaterial(Random); var prism = geometry.CreatePrism(material, bounds.Footprint, height).Transform(Matrix4x4.CreateTranslation(0, height / 2f - bounds.Height / 2f + GroundHeight, 0)); geometry.Union(prism); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { if (_designer == null) { _designer = hierarchicalParameters.GetValue(FloorDesignerName, false); } base.Subdivide(bounds, geometry, hierarchicalParameters); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Get internals generated up tree in container var internals = hierarchicalParameters.GetValue(BaseSpecBuildingContainer.BuildingInternalsName); //Generate externals, now we can get the neighbour information that we need from the surrounding containers _design = internals.Externals(Random, hierarchicalParameters, (a, b) => ScriptReference.Find(a, b).Random((Func <double>)Random), GetNeighbourInfo(bounds)); base.Subdivide(bounds, geometry, hierarchicalParameters); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { var height = hierarchicalParameters.GetMaybeValue(new TypedName <float>("height")) ?? 1; this.CreateFlatPlane(geometry, hierarchicalParameters.GetValue(new TypedName <string>("material")) ?? "grass", bounds.Footprint, height, -height ); }
private void CreateCeilings(Prism bounds, ISubdivisionGeometry geometry, IEnumerable <KeyValuePair <VerticalSelection, IRoomPlan> > verticalSubsections, string material) { Contract.Requires(geometry != null); Contract.Requires(verticalSubsections != null); var ceiling = geometry.CreatePrism(material, bounds.Footprint, _ceilingThickness).Translate(new Vector3(0, bounds.Height / 2 - _ceilingThickness / 2, 0)); ceiling = CutVerticalHoles(ceiling, geometry, material, verticalSubsections.Select(a => a.Value)); geometry.Union(ceiling); }
public static void CreateFlatPlane(this IGrounded grounded, ISubdivisionGeometry geometry, string material, IReadOnlyList <Vector2> footprint, float height, float yOffset = 0) { Contract.Requires(grounded != null); Contract.Requires(geometry != null); Contract.Requires(footprint != null && footprint.Count >= 3); var offset = GroundOffset(grounded, height) + yOffset; var prism = geometry.CreatePrism(material, footprint, height).Transform(Matrix4x4.CreateTranslation(0, offset, 0)); geometry.Union(prism); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Sanity checks if (!_floorIndex.HasValue) { throw new InvalidOperationException("Attempted to subdivide BaseFloor, but FloorIndex is not set"); } if (!_floorAltitude.HasValue) { throw new InvalidOperationException("Attempted to subdivide BaseFloor, but FloorAltitude is not set"); } //Calculate some handy values _roomHeight = bounds.Height - _floorThickness - _ceilingThickness; var roomOffsetY = -bounds.Height / 2 + _roomHeight / 2 + _floorThickness; //Find vertical elements which start on this floor var constrainedVerticalElements = ConstrainVerticalElements(this.SearchUp <IBuilding, IBuilding>(a => a, typeof(IBuildingContainer))); //Create a plan for this floor var plan = new Plan.Geometric.GeometricFloorplan(Bounds.Footprint); var overlappingVerticalRooms = InsertOverlappingVerticals( plan, this.SearchUp <IVerticalFeatureContainer, IVerticalFeatureContainer>(a => a, typeof(IBuildingContainer)).Overlapping(FloorIndex, false) ); var verticals = CreateFloorPlan(plan, overlappingVerticalRooms, constrainedVerticalElements).ToArray(); _plan = plan.Freeze(); PlanFrozen(_plan); //Create nodes for all the vertical elements which started on this floor CreateVerticalNodes(verticals); //Create Floor and ceiling (with holes for vertical sections) CreateFloors(bounds, geometry, verticals, HierarchicalParameters.DefaultCeilingMaterial(Random)); CreateCeilings(bounds, geometry, verticals, HierarchicalParameters.DefaultCeilingMaterial(Random)); //Create room scripts CreateRoomNodes(roomOffsetY, _roomHeight, _plan); //Create external facades (subsections of building over this floor facade) var externalFacades = CreateExternalFacades(bounds, _plan); //Create facades for rooms var dist = hierarchicalParameters.ExternalWallThickness(Random); CreateRoomFacades(externalFacades, roomOffsetY, dist, _plan); }
private void CreateFootpathN(ISubdivisionGeometry geometry, string material, float height) { Contract.Requires(geometry != null); Contract.Requires(Vertex != null); //Order the edges by their angle around the vertex var orderedEdges = Vertex.OrderedEdges().ToArray(); //Create path for pairs of roads for (int i = 0; i < orderedEdges.Length; i++) { var prev = orderedEdges[(i + orderedEdges.Length - 1) % orderedEdges.Length]; var edge = orderedEdges[i]; CreateFootpathFromPair(geometry, material, height, prev, edge); } }
private void CreateFootpaths(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters, string material, float height) { switch (Vertex.Edges.Count()) { //A junction not connected to any roads... suspicious but not technically disallowed case 0: //Dead end, don't do anything - the road places everything it needs case 1: { break; } //N-Way junction default: { CreateFootpathN(geometry, material, height); break; } } }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { _internals = _designer.Internals(Random, HierarchicalParameters, ScriptReference.Find(Random)); HierarchicalParameters.Set(BuildingInternalsName, _internals); base.Subdivide(bounds, geometry, hierarchicalParameters); //Create the node which will create the building form the spec var building = (SpecBuilding)CreateChild(bounds, Quaternion.Identity, Vector3.Zero, new ScriptReference(typeof(SpecBuilding))); //Make sure sibling container subdivide before building foreach (var sibling in Parent.Children.OfType <IBuildingContainer>()) { building.AddPrerequisite(sibling, false); } //Copy neighbour data into building (from container) building.Neighbours = Neighbours; }
private static ICsgShape CutVerticalHoles(ICsgShape shape, ISubdivisionGeometry geometry, string material, IEnumerable <IRoomPlan> verticalSubsections) { Contract.Requires(shape != null); Contract.Requires(geometry != null); Contract.Requires(verticalSubsections != null); Contract.Ensures(Contract.Result <ICsgShape>() != null); var shapeHeight = (shape.Bounds.Max.Y - shape.Bounds.Min.Y) * 2f; var shapeMid = (shape.Bounds.Min.Y + shape.Bounds.Max.Y) * 0.5f; foreach (var verticalSubsection in verticalSubsections) { shape = shape.Subtract( geometry.CreatePrism(material, verticalSubsection.OuterFootprint, shapeHeight).Translate(new Vector3(0, shapeMid, 0)) ); Contract.Assume(shape != null); } return(shape); }
private IReadOnlyCollection <IBuildingFacade> CreateFacades(ISubdivisionGeometry geometry, IEnumerable <Footprint> footprints, INamedDataCollection hierarchicalParameters) { Contract.Requires(geometry != null); Contract.Requires(footprints != null); Contract.Requires(hierarchicalParameters != null); //Accumulate results var results = new List <IBuildingFacade>(); //Calculate external wall thickness var thickness = hierarchicalParameters.ExternalWallThickness(Random); var material = hierarchicalParameters.ExternalWallMaterial(Random); var footprintArr = footprints.OrderBy(a => a.BottomIndex).ToArray(); for (var i = 0; i < footprintArr.Length; i++) { var footprint = footprintArr[i]; var topIndex = (i == footprintArr.Length - 1) ? (_floors[_floors.Keys.Max()].FloorIndex) : (footprintArr[i + 1].BottomIndex - 1); //Sanity check that we have the correct number of facades if (footprint.Facades.Count != footprint.Shape.Count) { throw new InvalidOperationException(string.Format("Tried to created {0} facades for {1} walls", footprint.Facades.Count, footprint.Shape.Count)); } //Generate wall sections to fill in Vector2[] inner; IReadOnlyList <IReadOnlyList <Vector2> > corners; var sections = footprint.Shape.Sections(thickness, out inner, out corners); //Create the tiny bits of facade in the corners CreateCornerFacades(geometry, footprint, topIndex, corners, material); //Now iterate through sides and create facades CreatePrimaryFacades(footprint, sections, results); } return(results); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { //Create a brush which will represent our facade var facade = CreateFacade(bounds, geometry, hierarchicalParameters); //Convert stamps and apply them to the facade brush var stamps = EmbossingStamps(hierarchicalParameters, Section.Width, bounds.Height); foreach (var stamp in stamps) { var brush = ConvertStampToBrush(Section, stamp, stamp.Material, geometry, hierarchicalParameters); if (brush == null) { continue; } //Add the shape if this is additive. However if this is a glass stamp ignore the additive flag and subtract always. facade = (stamp.Additive && !stamp.GlassFill.HasValue) ? facade.Union(brush) : facade.Subtract(brush); //Create a block of glass in the appropriate place if (stamp.GlassFill.HasValue) { //Create a brusg for the glass. Either reuse the existing brush (if the material is the same) or create a new one with the glass material var glassBrush = stamp.GlassFill.Value.Material == stamp.Material ? brush : ConvertStampToBrush(Section, stamp, stamp.GlassFill.Value.Material, geometry, hierarchicalParameters); if (glassBrush == null) { continue; } //Transform this with world-transform to place window in correct place in world Window.Create(this, glassBrush.Transform(WorldTransformation), stamp.GlassFill.Value.Opacity, stamp.GlassFill.Value.Scattering, stamp.GlassFill.Value.Attenuation); } } //Place the geometry in the world //Clip:false here allows the facade to overhang the bounds it is contained within (the wall section). Facades almost always do this in practice... //...e.g. fancy window decorations stick out from the wall a little bit geometry.Union(facade, clip: false); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { _height = CalculateHeight(); }
private void CreateFootpath2Outer(ISubdivisionGeometry geometry, string material, float height, IHalfEdgeBuilder r1, IHalfEdgeBuilder r2) { Contract.Requires(geometry != null); Contract.Requires(r1 != null); Contract.Requires(r2 != null); //r1.LeftEnd and r2.RightEnd are the outsides of the roads //Trace a path along the boundary between these points, use the path which does *not* include the other points Vector2[] cwp, ccwp; bool cw, ccw; Bounds.Footprint.TraceConnectingPath( Vector3.Transform(r1.LeftEnd.X_Y(0), InverseWorldTransformation).XZ(), Vector3.Transform(r2.RightEnd.X_Y(0), InverseWorldTransformation).XZ(), 0.01f, out cwp, out cw, out ccwp, out ccw, Vector3.Transform(r1.RightEnd.X_Y(0), InverseWorldTransformation).XZ(), Vector3.Transform(r2.LeftEnd.X_Y(0), InverseWorldTransformation).XZ()); //Sanity check (only 1 direction should be acceptable) if (!(cw ^ ccw)) { return; } //Follow along edge, then back along edge (pushed inwards by footpath width) //todo: Offset width by angle at end of road var outerPoints = cw ? cwp : ccwp; var innerPoints = new Vector2[cw ? cwp.Length : ccwp.Length]; Contract.Assert(innerPoints.Length == outerPoints.Length); var widthStart = cw ? r1.SidewalkWidth : r2.SidewalkWidth; var widthEnd = cw ? r2.SidewalkWidth : r1.SidewalkWidth; for (var i = 0; i < outerPoints.Length && outerPoints.Length > 1; i++) { Vector2 dir; float w; if (i == 0) { dir = Vector2.Normalize(cw ? r1.RightEnd - r1.LeftEnd : r2.LeftEnd - r2.RightEnd); w = CalculateFootpathAngleScale(cw ? r1.Direction : r2.Direction, dir, widthStart); } else if (i == outerPoints.Length - 1) { dir = Vector2.Normalize(cw ? r2.LeftEnd - r2.RightEnd : r1.RightEnd - r1.LeftEnd); w = CalculateFootpathAngleScale(cw ? r2.Direction : r1.Direction, -dir, widthEnd); } else { //perpendicular to path direction dir = Vector2.Normalize(outerPoints[i] - outerPoints[i - 1]).Perpendicular() * (cw ? 1 : -1); //Interpolate widths along path var t = i / (float)(outerPoints.Length - 1); w = MathHelper.Lerp(widthStart, widthEnd, t); } innerPoints[i] = outerPoints[i] + dir * w; } //Create footprint from 2 halves var footprint = cw ? outerPoints.Concat(innerPoints.Reverse()) : outerPoints.Reverse().Concat(innerPoints); geometry.Union(geometry .CreatePrism(material, footprint.ToArray(), height) .Translate(new Vector3(0, this.GroundOffset(height), 0)) ); }
private void CreateFootpath2Inner(ISubdivisionGeometry geometry, string material, float height, bool r1LeftInner, IHalfEdgeBuilder r1, IHalfEdgeBuilder r2) { Contract.Requires(r1 != null); Contract.Requires(r2 != null); Contract.Requires(geometry != null); var innerPoint = r1LeftInner ? r1.LeftEnd : r1.RightEnd; var points = new List <Vector2> { innerPoint }; //Points where the footpath terminates at the end of the road var r1Across = r1LeftInner ? r1.Direction.Perpendicular() : -r1.Direction.Perpendicular(); var r1Point = innerPoint + r1Across * r1.SidewalkWidth; var r2Across = r1LeftInner ? -r2.Direction.Perpendicular() : r2.Direction.Perpendicular(); var r2Point = innerPoint + r2Across * r2.SidewalkWidth; //Point to turn the connect around var centerPoint = new Ray2(r1Point, r1.Direction.Perpendicular()).Intersects(new Ray2(r2Point, r2.Direction.Perpendicular())); //Sanity check! if (!centerPoint.HasValue) { return; } //Evaluate segment var curve = new CircleSegment { CenterPoint = centerPoint.Value.Position, StartPoint = r1Point, EndPoint = r2Point, }; points.AddRange(curve.Evaluate(0.05f)); //Helpers function for creating prisms Action <IEnumerable <Vector2>, bool> unionPrism = (footprint, check) => geometry.Union(geometry .CreatePrism(material, footprint.ConvexHull().ToArray(), height) .Translate(new Vector3(0, this.GroundOffset(height), 0)) .Transform(InverseWorldTransformation), check ); //Materialize result unionPrism(points, false); //Extend straight sections to edge of node //We only need to do this if we were turning precisely about the contact points of these two roads if (centerPoint.Value.Position.TolerantEquals(innerPoint, 0.1f)) { unionPrism(new[] { r1Point, innerPoint, innerPoint - r1.Direction * r1.SidewalkWidth, r1Point - r1.Direction * r1.SidewalkWidth }, true); unionPrism(new[] { r2Point, innerPoint, innerPoint - r2.Direction * r2.SidewalkWidth, r2Point - r2.Direction * r2.SidewalkWidth }, true); } }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { this.CreateFlatPlane(geometry, "tarmac", bounds.Footprint, 1, -1); CreateFootpaths(bounds, geometry, hierarchicalParameters, hierarchicalParameters.RoadSidewalkMaterial(Random), HierarchicalParameters.RoadSidewalkHeight(Random)); }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { }
public override void Subdivide(Prism bounds, ISubdivisionGeometry geometry, INamedDataCollection hierarchicalParameters) { CreateChild(_prism, Quaternion.Identity, Vector3.Zero, _script); }