private void GetQuadTreeVerticesAndIndices(QuadTreeNode quadTree, int level = 0) { var vertBase = _bvhVerts.Count; var corners = quadTree.Bounds.GetCorners(); if (level == 9) { _bvhVerts.AddRange(corners.Select(c => { var color = Color.White; switch (_aabCount % 4) { case 1: color = Color.Blue; break; case 2: color = Color.Magenta; break; case 3: color = Color.Yellow; break; } return(new VertexPC(c, color)); })); _aabCount++; _bvhIndices.AddRange( new[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 4, 0, 5, 1, 7, 3, 6, 2 }.Select( i => i + vertBase)); } if (quadTree.Children != null) { foreach (var child in quadTree.Children) { GetQuadTreeVerticesAndIndices(child, level + 1); } } }
private QuadTreeNode BuildQuadTree(Vector2 topLeft, Vector2 bottomRight) { const float tolerance = 0.01f; // search the heightmap in order to get the y-extents of the terrain region var minMaxY = GetMinMaxY(topLeft, bottomRight); // convert the heightmap index bounds into world-space coordinates var minX = topLeft.X * Info.CellSpacing - Width / 2; var maxX = bottomRight.X * Info.CellSpacing - Width / 2; var minZ = -topLeft.Y * Info.CellSpacing + Depth / 2; var maxZ = -bottomRight.Y * Info.CellSpacing + Depth / 2; // adjust the bounds to get a very slight overlap of the bounding boxes minX -= tolerance; maxX += tolerance; minZ += tolerance; maxZ -= tolerance; // construct the new node and assign the world-space bounds of the terrain region var quadNode = new QuadTreeNode { Bounds = new BoundingBox(new Vector3(minX, minMaxY.X, minZ), new Vector3(maxX, minMaxY.Y, maxZ)) }; var width = (int)Math.Floor((bottomRight.X - topLeft.X) / 2); var depth = (int)Math.Floor((bottomRight.Y - topLeft.Y) / 2); // we will recurse until the terrain regions match our logical terrain tile sizes if (width >= TileSize && depth >= TileSize) { quadNode.Children = new[] { BuildQuadTree(topLeft, new Vector2(topLeft.X + width, topLeft.Y + depth)), BuildQuadTree(new Vector2(topLeft.X + width, topLeft.Y), new Vector2(bottomRight.X, topLeft.Y + depth)), BuildQuadTree(new Vector2(topLeft.X, topLeft.Y + depth), new Vector2(topLeft.X + depth, bottomRight.Y)), BuildQuadTree(new Vector2(topLeft.X + width, topLeft.Y + depth), bottomRight) }; } else { // set the maptile corresponding to this leaf node of the quad tree var center = topLeft / TileSize; var mapX = (int)Math.Floor(center.X); var mapY = (int)Math.Floor(center.Y); quadNode.MapTile = GetTile(mapX, mapY); } return quadNode; }
public bool Intersects(Ray ray, out Vector3 hit, out QuadTreeNode node) { hit = new Vector3(float.MaxValue); // This is our terminating condition if (Children == null) { float d; // check if the ray intersects this leaf node's bounding box if (!Ray.Intersects(ray, Bounds, out d)) { // No intersection node = null; return(false); } // return the centerpoint of the leaf's bounding box hit = (Bounds.Minimum + Bounds.Maximum) / 2; node = this; return(true); } // If the node has children, we need to intersect each child. // We only intersect the child's immediate bounding volume, in order to avoid fully intersecting // It is possible that the closest child intersection does not actually contain the closest // node that intersects the ray, so we maintain a priority queue of the child nodes that were hit, // indexed by the distance to intersection var pq = new SortedDictionary <float, QuadTreeNode>(); foreach (var bvhNode in Children) { float cd; if (Ray.Intersects(ray, bvhNode.Bounds, out cd)) { while (pq.ContainsKey(cd)) { // perturb things slightly so that we don't have duplicate keys cd += MathF.Rand(-0.001f, 0.001f); } pq.Add(cd, bvhNode); } } // If there were no child intersections if (pq.Count <= 0) { node = null; return(false); } // check the child intersections for the nearest intersection var intersect = false; // setup a very-far away intersection point to compare against var bestHit = ray.Position + ray.Direction * 1000; QuadTreeNode bestNode = null; foreach (var bvhNode in pq) { Vector3 thisHit; QuadTreeNode thisNode; // intersect the child node recursively var wasHit = bvhNode.Value.Intersects(ray, out thisHit, out thisNode); if (!wasHit) { // no intersection, continue and intersect the other children continue; } // Make sure that the intersection point is in front of the ray's world-space origin var dot = (Vector3.Dot(Vector3.Normalize(thisHit - ray.Position), ray.Direction)); if (!(dot > 0.9f)) { continue; } // check that the intersection is closer than the nearest intersection found thus far if (!((ray.Position - thisHit).LengthSquared() < (ray.Position - bestHit).LengthSquared())) { continue; } // if we have found a closer intersection store the new closest intersection bestHit = thisHit; bestNode = thisNode; intersect = true; } // bestHit now contains the closest intersection found, or the distant sentinel value hit = bestHit; node = bestNode; return(intersect); }
public bool Intersects(Ray ray, out Vector3 hit, out QuadTreeNode node) { return(Root.Intersects(ray, out hit, out node)); }
public bool Intersects(Ray ray, out Vector3 hit, out QuadTreeNode node) { hit = new Vector3(float.MaxValue); // This is our terminating condition if (Children == null) { float d; // check if the ray intersects this leaf node's bounding box if (!Ray.Intersects(ray, Bounds, out d)) { // No intersection node = null; return false; } // return the centerpoint of the leaf's bounding box hit = (Bounds.Minimum + Bounds.Maximum) / 2; node = this; return true; } // If the node has children, we need to intersect each child. // We only intersect the child's immediate bounding volume, in order to avoid fully intersecting // It is possible that the closest child intersection does not actually contain the closest // node that intersects the ray, so we maintain a priority queue of the child nodes that were hit, // indexed by the distance to intersection var pq = new SortedDictionary<float, QuadTreeNode>(); foreach (var bvhNode in Children) { float cd; if (Ray.Intersects(ray, bvhNode.Bounds, out cd)) { while (pq.ContainsKey(cd)) { // perturb things slightly so that we don't have duplicate keys cd += MathF.Rand(-0.001f, 0.001f); } pq.Add(cd, bvhNode); } } // If there were no child intersections if (pq.Count <= 0) { node = null; return false; } // check the child intersections for the nearest intersection var intersect = false; // setup a very-far away intersection point to compare against var bestHit = ray.Position + ray.Direction * 1000; QuadTreeNode bestNode = null; foreach (var bvhNode in pq) { Vector3 thisHit; QuadTreeNode thisNode; // intersect the child node recursively var wasHit = bvhNode.Value.Intersects(ray, out thisHit, out thisNode); if (!wasHit) { // no intersection, continue and intersect the other children continue; } // Make sure that the intersection point is in front of the ray's world-space origin var dot = (Vector3.Dot(Vector3.Normalize(thisHit - ray.Position), ray.Direction)); if (!(dot > 0.9f)) { continue; } // check that the intersection is closer than the nearest intersection found thus far if (!((ray.Position - thisHit).LengthSquared() < (ray.Position - bestHit).LengthSquared())) continue; // if we have found a closer intersection store the new closest intersection bestHit = thisHit; bestNode = thisNode; intersect = true; } // bestHit now contains the closest intersection found, or the distant sentinel value hit = bestHit; node = bestNode; return intersect; }
public bool Intersects(Ray ray, out Vector3 hit, out QuadTreeNode node) { return Root.Intersects(ray, out hit, out node); }
private void GetQuadTreeVerticesAndIndices(QuadTreeNode quadTree, int level = 0) { var vertBase = _bvhVerts.Count; var corners = quadTree.Bounds.GetCorners(); if (level == 9) { _bvhVerts.AddRange(corners.Select(c => { var color = Color.White; switch (_aabCount % 4) { case 1: color = Color.Blue; break; case 2: color = Color.Magenta; break; case 3: color = Color.Yellow; break; } return new VertexPC(c, color); })); _aabCount++; _bvhIndices.AddRange( new[] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 4, 0, 5, 1, 7, 3, 6, 2 }.Select( i => i + vertBase)); } if (quadTree.Children != null) { foreach (var child in quadTree.Children) { GetQuadTreeVerticesAndIndices(child, level + 1); } } }