public static CollisionDetails GetFirstCollision(Octree tree, Shapes.Ray r) { var c = new CollisionDetails { Distance = double.PositiveInfinity }; double distanceTravelled = 0; //distance traveled since the ray's origin //Algorithm starts at the ray's origin and in the root node. Node n = tree.GetRoot(); c.CheckCollisionSet(n.Content, r); var pos = r.Origin; if (!n.Encloses(pos)) { throw new Exception("Ray's origin is not located in the octree!"); } do { //March to the leaf containing "pos" and check collisions n = n.MarchToCheckingCollisions(pos, c, r); //No leaf is containing "pos" -> left tree -> stop searching if (n == null) { break; } //march out of current node double d = RayCube.GetDistanceToBorderPlane(pos, r.Direction, n.Center, n.Width / 2) + Constants.EPS * 1e4; //distance traveled since the ray's origin pos = pos + r.Direction * d; distanceTravelled += d; //in case eps was not enough while (n.Encloses(pos)) { d = Constants.EPS * 1e4; pos = pos + r.Direction * d; distanceTravelled += d; } /** * When passing a box-border, all intersection tests between the ray's origin and the * box-border-intersection-point must have taken place (and some beyond the box-border too). * Therefore, if an intersection located between the ray's origin and the box-border has been found, this * must be the nearest intersection and the search may stop. */ } while (c.Distance > distanceTravelled); return(c); }
public static Octree BuildTree(Vect3 center, IList <I3DObject> objects) { double maxQuadDist = 0; Log.Debug("Building octree... "); foreach (I3DObject o in objects) { Sphere s = o.GetBoundingSphere(); if (s != null) { Vect3 dist = center - s.Position; double qdist = s.Radius; if (double.IsInfinity(qdist) || Double.IsNaN(qdist)) { throw new Exception("Invalid BoundingSphere: " + s + " from " + o); } qdist = qdist * qdist + dist.QuadLength(); if (maxQuadDist < qdist) { maxQuadDist = qdist; } } } /** * Workaround: *2.1 instead of *2, because rays must always start inside an octree for RayPath. * To fix this problem, a RayPath should allow ray-origins outside the octree, but this would need * an additional ray-cube intersection test which is currently not implemented. Another downside of * this is that the camera must always be located inside the octree - it must not be moved outside. */ double sizeHint = System.Math.Sqrt(maxQuadDist) * 2.1; Sw.Restart(); Octree t; while (true) { t = new Octree(center, sizeHint); foreach (I3DObject o in objects) { if (!t.Root.Insert(o, o.GetBoundingSphere())) { sizeHint *= 2; Log.Warn("Warning - resize needed!"); goto cont; } } break; cont: { } } t.GetRoot().Compress(); Sw.Stop(); Log.Debug(string.Format("{0} ms\n", Sw.ElapsedMilliseconds)); Log.Debug(string.Format(" - contains {0} of {1} elements ({2:0.##}%)", t.GetRoot().GetSize(), objects.Count, t.GetRoot().GetSize() / (float)objects.Count * 100)); Log.Debug(" - avg depth: " + t.GetAverageObjectDepth()); Log.Debug(" - node count: " + t.GetRoot().GetNodeCount()); Log.Debug(" - objects per node: " + t.GetRoot().GetSize() / (float)t.GetRoot().GetNodeCount()); return(t); }