// instance functions /// <summary>Adds a new instance of a static object to the grid.</summary> /// <param name="libraryIndex">The index to a library object.</param> /// <param name="position">The absolute world position of the object.</param> /// <param name="orientation">The absolute world orientation of the object.</param> internal void Add(int libraryIndex, OpenBveApi.Math.Vector3 position, OpenBveApi.Math.Orientation3 orientation) { if (this.Root == null) { // the root node does not exist yet OpenBveApi.Math.Vector3 gridPosition = new OpenBveApi.Math.Vector3(0.0, position.Y, 0.0); GridPopulatedLeafNode leaf = new GridPopulatedLeafNode( null, new GridBounds( position.X - 0.5 * this.SideLength, position.X + 0.5 * this.SideLength, position.Z - 0.5 * this.SideLength, position.Z + 0.5 * this.SideLength ), new StaticOpaqueObject(libraryIndex, gridPosition, orientation) ); leaf.UpdateBoundingRectangle(libraryIndex, gridPosition, orientation); this.Root = leaf; } else { // the root node exists while (true) { if (position.X >= this.Root.Rectangle.Left & position.X <= this.Root.Rectangle.Right & position.Z >= this.Root.Rectangle.Near & position.Z <= this.Root.Rectangle.Far) { // the position is within the bounds of the root node GridNode node = this.Root; double left = this.Root.Rectangle.Left; double right = this.Root.Rectangle.Right; double near = this.Root.Rectangle.Near; double far = this.Root.Rectangle.Far; while (true) { if (node is GridPopulatedLeafNode) { // populated leaf node OpenBveApi.Math.Vector3 gridPosition = new OpenBveApi.Math.Vector3( position.X - 0.5 * (left + right), position.Y, position.Z - 0.5 * (near + far) ); GridPopulatedLeafNode leaf = (GridPopulatedLeafNode)node; if (leaf.StaticOpaqueObjectCount == leaf.StaticOpaqueObjects.Length) { Array.Resize<StaticOpaqueObject>(ref leaf.StaticOpaqueObjects, leaf.StaticOpaqueObjects.Length << 1); } leaf.VisibleLeafNodes = null; leaf.StaticOpaqueObjects[leaf.StaticOpaqueObjectCount] = new StaticOpaqueObject(libraryIndex, gridPosition, orientation); leaf.StaticOpaqueObjectCount++; leaf.UpdateBoundingRectangle(libraryIndex, gridPosition, orientation); break; } else if (node is GridInternalNode) { // internal node GridInternalNode intern = (GridInternalNode)node; int index; double centerX = 0.5 * (left + right); double centerZ = 0.5 * (near + far); if (position.Z <= centerZ) { if (position.X <= centerX) { index = 0; right = centerX; far = centerZ; } else { index = 1; left = centerX; far = centerZ; } } else { if (position.X <= centerX) { index = 2; right = centerX; near = centerZ; } else { index = 3; left = centerX; near = centerZ; } } if (intern.Children[index] is GridUnpopulatedLeafNode) { double sideLength = 0.5 * (right - left + far - near); const double toleranceFactor = 1.01; if (sideLength < toleranceFactor * this.SideLength) { // create populated leaf child GridPopulatedLeafNode child = new GridPopulatedLeafNode( intern, new GridBounds(left, right, near, far), null ); child.BoundingRectangle = GridBounds.Uninitialized; intern.Children[index] = child; node = child; } else { // create internal child GridInternalNode child = new GridInternalNode( intern, new GridBounds(left, right, near, far), new GridNode[] { null, null, null, null } ); child.Children[0] = new GridUnpopulatedLeafNode(child, new GridBounds(left, 0.5 * (left + right), near, 0.5 * (near + far))); child.Children[1] = new GridUnpopulatedLeafNode(child, new GridBounds(0.5 * (left + right), right, near, 0.5 * (near + far))); child.Children[2] = new GridUnpopulatedLeafNode(child, new GridBounds(left, 0.5 * (left + right), 0.5 * (near + far), far)); child.Children[3] = new GridUnpopulatedLeafNode(child, new GridBounds(0.5 * (left + right), right, 0.5 * (near + far), far)); intern.Children[index] = child; node = child; } } else { // go to child node = intern.Children[index]; } } else { throw new InvalidOperationException(); } } break; } else { // the position is outside the bounds of the root node if (position.Z <= 0.5 * (this.Root.Rectangle.Near + this.Root.Rectangle.Far)) { if (position.X <= 0.5 * (this.Root.Rectangle.Left + this.Root.Rectangle.Right)) { // expand toward near-left GridInternalNode intern = new GridInternalNode( null, new GridBounds( 2.0 * this.Root.Rectangle.Left - this.Root.Rectangle.Right, this.Root.Rectangle.Right, 2.0 * this.Root.Rectangle.Near - this.Root.Rectangle.Far, this.Root.Rectangle.Far ), new GridNode[] { null, null, null, this.Root } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } else { // expand toward near-right GridInternalNode intern = new GridInternalNode( null, new GridBounds( this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Right - this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Near - this.Root.Rectangle.Far, this.Root.Rectangle.Far ), new GridNode[] { null, null, this.Root, null } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } } else { if (position.X <= 0.5 * (this.Root.Rectangle.Left + this.Root.Rectangle.Right)) { // expand toward far-left GridInternalNode intern = new GridInternalNode( null, new GridBounds( 2.0 * this.Root.Rectangle.Left - this.Root.Rectangle.Right, this.Root.Rectangle.Right, this.Root.Rectangle.Near, 2.0 * this.Root.Rectangle.Far - this.Root.Rectangle.Near ), new GridNode[] { null, this.Root, null, null } ); this.Root.Parent = intern; intern.Children[0] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } else { // expand toward far-right GridInternalNode intern = new GridInternalNode( null, new GridBounds( this.Root.Rectangle.Left, 2.0 * this.Root.Rectangle.Right - this.Root.Rectangle.Left, this.Root.Rectangle.Near, 2.0 * this.Root.Rectangle.Far - this.Root.Rectangle.Near ), new GridNode[] { this.Root, null, null, null } ); this.Root.Parent = intern; intern.Children[1] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, intern.Rectangle.Near, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far))); intern.Children[2] = new GridUnpopulatedLeafNode(intern, new GridBounds(intern.Rectangle.Left, 0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.Children[3] = new GridUnpopulatedLeafNode(intern, new GridBounds(0.5 * (intern.Rectangle.Left + intern.Rectangle.Right), intern.Rectangle.Right, 0.5 * (intern.Rectangle.Near + intern.Rectangle.Far), intern.Rectangle.Far)); intern.UpdateBoundingRectangle(); this.Root = intern; } } } } } }
// constructors internal GridPopulatedLeafNode(GridInternalNode parent, GridBounds rectangle, StaticOpaqueObject initialStaticOpaqueObject) { this.Parent = parent; this.Rectangle = rectangle; this.BoundingRectangle = GridBounds.Uninitialized; this.VisibleLeafNodes = null; if (initialStaticOpaqueObject != null) { this.StaticOpaqueObjects = new StaticOpaqueObject[] { initialStaticOpaqueObject }; this.StaticOpaqueObjectCount = 1; } else { this.StaticOpaqueObjects = new StaticOpaqueObject[] { null }; this.StaticOpaqueObjectCount = 0; } this.DisplayList = new DisplayList(); this.TransparentFaces = new object[1]; this.TransparentFaceCount = 0; }
// constructors internal GridUnpopulatedLeafNode(GridInternalNode parent, GridBounds rectangle) { this.Parent = parent; this.Rectangle = rectangle; this.BoundingRectangle = GridBounds.Uninitialized; this.VisibleLeafNodes = null; }
// constructors internal GridInternalNode(GridInternalNode parent, GridBounds rectangle, GridNode[] children) { this.Parent = parent; this.Rectangle = rectangle; this.BoundingRectangle = GridBounds.Uninitialized; this.Children = children; }