private static void MeasureIntersectionPerformance(HeightField heightfield, Vector3 start, Vector3 end, int iterations) { Vector3 direction = end - start; MyIntersector intersector = new MyIntersector(heightfield); FootprintDebugInfo debugInfo = new FootprintDebugInfo(); Intersection isec = intersector.Intersect(start, end, ref debugInfo); int visitedPixels = debugInfo.VisitedPixels.Count; Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < iterations; i++) { Intersection intersection = intersector.Intersect(start, end); } sw.Stop(); Console.WriteLine("Intersection?: {0}", isec != null); Console.WriteLine("Ray length: {0:0.0}", direction.Length); Console.WriteLine("Visited pixels: {0}", visitedPixels); Console.WriteLine("Iterations: {0}", iterations); Console.WriteLine("Total time: {0} ms", sw.ElapsedMilliseconds); Console.WriteLine("Average time: {0:0.000} ms", sw.ElapsedMilliseconds / (double)iterations); double throughput = iterations / ((double)sw.ElapsedMilliseconds * 0.001); Console.WriteLine("Throughput: {0:0.000} traversals/s", throughput); Console.WriteLine("Throughput of visited pixels: {0:0.000} Mpx/s", throughput * visitedPixels * 1e-6); }
internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { int stepCount = 5; //float near = 2; //float far = 10; //Vector2 start = (near / (float)ray.Direction.Z) * new Vector2((float)ray.Origin.X, (float)ray.Origin.Y); //Vector3d rayEnd = ray.Origin + ray.Direction; //Vector2 end = (far / (float)ray.Direction.Z) * new Vector2((float)rayEnd.X, (float)rayEnd.Y); Vector2 startXY = start.Xy; Vector2 endXY = end.Xy; float depthSize = 1; float currentDepth = 0; float isecDepth = 1; Vector2 position = startXY; for (int i = 0; i < stepCount; i++) { depthSize *= 0.5f; position = startXY + currentDepth * endXY; float hfDepth = HeightField.GetDepthBilinear(position, 0); if (currentDepth >= hfDepth) { isecDepth = currentDepth; currentDepth -= 2 * depthSize; } currentDepth += depthSize; } if (isecDepth < (1 - 0.0001)) { return(new Intersection(new Vector3d(position.X, position.Y, isecDepth))); } else { return(null); } }
internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { int stepCount = 5; //float near = 2; //float far = 10; //Vector2 start = (near / (float)ray.Direction.Z) * new Vector2((float)ray.Origin.X, (float)ray.Origin.Y); //Vector3d rayEnd = ray.Origin + ray.Direction; //Vector2 end = (far / (float)ray.Direction.Z) * new Vector2((float)rayEnd.X, (float)rayEnd.Y); Vector2 startXY = start.Xy; Vector2 endXY = end.Xy; float depthSize = 1; float currentDepth = 0; float isecDepth = 1; Vector2 position = startXY; for (int i = 0; i < stepCount; i++) { depthSize *= 0.5f; position = startXY + currentDepth * endXY; float hfDepth = HeightField.GetDepthBilinear(position, 0); if (currentDepth >= hfDepth) { isecDepth = currentDepth; currentDepth -= 2 * depthSize; } currentDepth += depthSize; } if (isecDepth < (1 - 0.0001)) { return new Intersection(new Vector3d(position.X, position.Y, isecDepth)); } else { return null; } }
internal override Intersection Intersect(Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { int steps = (int)Math.Floor(Math.Max((end - start).Xy.Length, 1)); Vector3 rayStep = (end - start) / (float)steps; //Vector3 rayStep = (end - start) / (float)Steps; Vector3 currentPos = start; int prevIndicator = GetRayLayerIndicator(start.Z, HeightField.GetDepthBilinear(start.Xy, 0)); int isecLayer = 0; for (int i = 0; i < steps; i++) { currentPos += rayStep; float layerDepth = HeightField.GetDepthBilinear(currentPos.Xy, 0); int indicator = GetRayLayerIndicator(currentPos.Z, layerDepth); if (layerDepth > 0) { // some data present // Find the first layer with indicator value 1. In case // of a single layer it is the single value. int indicatorDiff = prevIndicator - indicator; if (indicatorDiff == 1) { isecLayer = 1; } if (isecLayer != 0) { if (debugInfo != null) { debugInfo.LayerOfIntersection = isecLayer - 1; } return new Intersection((Vector3d)currentPos); // TODO: grab color from the particular color layer } } prevIndicator = indicator; } return null; }
internal override Intersection Intersect(Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { int steps = (int)Math.Floor(Math.Max((end - start).Xy.Length, 1)); Vector3 rayStep = (end - start) / (float)steps; //Vector3 rayStep = (end - start) / (float)Steps; Vector3 currentPos = start; int prevIndicator = GetRayLayerIndicator(start.Z, HeightField.GetDepthBilinear(start.Xy, 0)); int isecLayer = 0; for (int i = 0; i < steps; i++) { currentPos += rayStep; float layerDepth = HeightField.GetDepthBilinear(currentPos.Xy, 0); int indicator = GetRayLayerIndicator(currentPos.Z, layerDepth); if (layerDepth > 0) { // some data present // Find the first layer with indicator value 1. In case // of a single layer it is the single value. int indicatorDiff = prevIndicator - indicator; if (indicatorDiff == 1) { isecLayer = 1; } if (isecLayer != 0) { if (debugInfo != null) { debugInfo.LayerOfIntersection = isecLayer - 1; } return(new Intersection((Vector3d)currentPos)); // TODO: grab color from the particular color layer } } prevIndicator = indicator; } return(null); }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { bool collectDebugInfo = debugInfo != null; #region initialization Vector2 rayStart = start.Xy; Vector2 rayEnd = end.Xy; Vector3 dir = end - start; Vector2 rayDir = dir.Xy; // make sure the denominator in tMax and tDelta is not zero if (Math.Abs(rayDir.X) < epsilonForRayDir) { rayDir.X = epsilonForRayDir; } if (Math.Abs(rayDir.Y) < epsilonForRayDir) { rayDir.Y = epsilonForRayDir; } Vector2 step = new Vector2(Math.Sign(rayDir.X), Math.Sign(rayDir.Y)); Vector2 currentPixel = GetPixelCorner(start.Xy, step); Vector2 endPixel = GetPixelCorner(start.Xy + rayDir, -step); Vector2 boundary = new Vector2( currentPixel.X + ((step.X > 0) ? 1 : 0), currentPixel.Y + ((step.Y > 0) ? 1 : 0)); Vector2 boundaryToStart = boundary - rayStart; Vector2 rayDirInv = new Vector2(1 / rayDir.X, 1 / rayDir.Y); Vector2 tMax = new Vector2(boundaryToStart.X * rayDirInv.X, boundaryToStart.Y * rayDirInv.Y); Vector2 tDelta = new Vector2(step.X * rayDirInv.X, step.Y * rayDirInv.Y); if (collectDebugInfo) { debugInfo.StartPixel = currentPixel; debugInfo.EndPixel = endPixel; } #endregion #region traversal List <Vector2> footprintPixels = new List <Vector2>(); int maxIterations = (int)(2 * rayDir.Length); int iterations = 0; debugInfo.EntryPoints.Add(rayStart); Vector3 entry = start; while (currentPixel != endPixel) { if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); //debugInfo.EntryPoints.Add(rayStart + Math.Min(tMax.X, tMax.Y) * rayDir); debugInfo.EntryPoints.Add(entry.Xy); } Vector3 exit; if (tMax.X < tMax.Y) { exit = start + tMax.X * dir; tMax.X += tDelta.X; currentPixel.X += step.X; } else { exit = start + tMax.Y * dir; tMax.Y += tDelta.Y; currentPixel.Y += step.Y; } // height field intersection for (int layer = 0; layer < this.HeightField.LayerCount; layer++) { float layerZ = this.HeightField.GetDepth((int)currentPixel.X, (int)currentPixel.Y, layer); // we could compare: // (1) sign(entry.Z - hf[pixel.xy]) != sign(exit.Z - hf[pixel.xy]) // (2) sign(entry.Z - hf[entry.xy]) != sign(exit.Z - hf[exit.xy]) // (3) sign(entry.Z - hf[middle.xy]) != sign(exit.Z - hf[middle.xy]) //Vector2 middle = 0.5f * (exit.Xy - entry.Xy); //float layerZ = this.HeightField.GetDepth((int)middle.X, (int)middle.Y, 0); if ((Math.Sign(layerZ - entry.Z) != Math.Sign(layerZ - exit.Z))) { if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return(new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, layerZ))); } } entry = exit; if (iterations == maxIterations) { break; } iterations++; } debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(rayEnd); #endregion return(null); }
/// <summary> /// Intersects a height field with a ray. /// </summary> /// <remarks> /// <para> /// A height field is a layered 2D table with depth values. At each /// position there can be multiple pixels in layers ordered by /// increasing depth. /// </para> /// <para> /// This procedures find the position of intersection in the /// height field along with the depth of the intersected pixel, or /// a zero vector in case of no intersection, and indicates if there /// was an intersection. /// </para> /// <para> /// Note that there is are only divisions at beginning and the loop /// contains only additions, multiplications, Math.Abs() and /// height field table lookups. /// </para> /// <para> /// Based on the line footprint traversal algorithm. See /// BokehLab.Math.LineFootprint. /// </para> /// </remarks> /// <param name="ray">incoming ray</param> /// <param name="heightfield">height field to be intersected</param> /// <returns>Intersection instance - position of the intersection in /// the height field (only the pixel corner, not the exact position) /// - if the was an intersection; null otherwise</returns> internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { bool collectDebugInfo = debugInfo != null; Vector3 rayDirection = end - start; if (Math.Abs(rayDirection.Z) < epsilonForCorners) { return null; } bool rayGoesFromDepth = rayDirection.Z < 0; Vector2 dir = rayDirection.Xy; bool rayGoesRatherHorizontal = Math.Abs(dir.X) > Math.Abs(dir.Y); Vector2 dirInv = new Vector2((float)(1 / dir.X), (float)(1 / dir.Y)); Vector2 rayDzOverDxy = rayDirection.Z * dirInv; // direction to the nearest corner: [1,1], [1,-1], [-1,1] or [-1,-1] Vector2 relDir = new Vector2(Math.Sign(dir.X), Math.Sign(dir.Y)); bool isDirectionAxisAligned = relDir.X * relDir.Y == 0; // relDir converted to a single pixel: [1,1], [1,0], [0,1] or [0,0] Vector2 relCorner = isDirectionAxisAligned ? relDir : (0.5f * (relDir + new Vector2(1, 1))); // point where the 2D ray projection enters the current pixel Vector3 entry = start; Vector2 entryXY = entry.Xy; Vector2 currentPixel = GetPixelCorner(entryXY, relDir); Vector2 endPixel = GetPixelCorner(end.Xy, -relDir); if (collectDebugInfo) { debugInfo.StartPixel = currentPixel; debugInfo.EndPixel = endPixel; } // absolute position of the nearest corner Vector2 corner = currentPixel + relCorner; // depth of the last square in the previous pixel which was // completely in front of the ray float? previousLastZ = null; while (currentPixel != endPixel) { if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(entryXY); } if ((currentPixel.X < 0) || (currentPixel.X >= this.HeightField.Width) || (currentPixel.Y < 0) || (currentPixel.Y >= this.HeightField.Height)) { return null; } // We can only decide the orientation to nearest corner only // if the ray direction in the XY plane is axis aligned. // Otherwise we have to use the original relative direction. // Also this direction is used if traversing directly across // the corner. Vector2 nextDir = relDir; // across the corner by default if (!isDirectionAxisAligned) { // Get a direction of a vector perpendicular to directions // from the current position both to the nearest corner and // the end of the ray (it is aligned with the Z axis). float crossLength = (corner.X - entryXY.X) * (end.Y - entryXY.Y) - (corner.Y - entryXY.Y) * (end.X - entryXY.X); // it is equavalent to: // float crossLength = Cross2d(corner - entryXY, end.Xy - entryXY); // or to: // float crossLength = Vector3.Cross(new Vector3(corner - entryXY), new Vector3(rayEnd - entryXY)).Z; // The direction of the cross vector determines the relative // orientation of the two examined vectors: // 0 (+- epsilon) -> go across the corner // > 0 -> go to the clockwise edge // < 0 -> go to the counter-clockwise edge if (Math.Abs(crossLength) > epsilonForCorners) { // add a vector perpendicular in one or another direction // to get a vector 45 degrees apart from the corner Vector2 cw = new Vector2(-relDir.Y, relDir.X); if (crossLength > 0) { // (relDir + ccw) / 2 -> clockwise edge nextDir += cw; } else { // (relDir - ccw) / 2 -> counter-clockwise edge nextDir -= cw; } nextDir *= 0.5f; } } // point where the 2D ray projection exits the current pixel Vector3 exit = new Vector3(IntersectPixelEdge2d(entryXY, dir, dirInv, corner, nextDir)); if (rayGoesRatherHorizontal) { exit.Z = start.Z + (exit.X - start.X) * rayDzOverDxy.X; } else { exit.Z = start.Z + (exit.Y - start.Y) * rayDzOverDxy.Y; } // compute intersection with the height field pixel (in several layers) Intersection isec = IntersectLayerAtPixel(currentPixel, entry.Z, exit.Z, rayGoesFromDepth, collectDebugInfo, debugInfo, ref previousLastZ); if (isec != null) { return isec; } entry = exit; entryXY = entry.Xy; currentPixel += nextDir; corner += nextDir; } // tail of the ray (the end pixel) if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(entryXY); } return IntersectLayerAtPixel(currentPixel, entry.Z, end.Z, rayGoesFromDepth, collectDebugInfo, debugInfo, ref previousLastZ); }
private Intersection IntersectLayerAtPixel( Vector2 currentPixel, float entryZ, float exitZ, bool rayGoesFromDepth, bool collectDebugInfo, FootprintDebugInfo debugInfo, ref float? previousLastZ) { // depth of the last square in the current pixel which was // completely in front of the ray float? currentLastZ = null; int layer; for (layer = 0; layer < this.HeightField.LayerCount; layer++) { // Tests whether a ray going over a height field pixel intersects it or not. // // There is an intersection if the heightfield pixel depth is between // depths of ray entry and exit points. In case of equality (up to // epsilon) the ray touches the pixel. Otherwise it misses the pixel. float layerZ = this.HeightField.GetDepth((int)currentPixel.X, (int)currentPixel.Y, layer); if (layerZ == 1) { // early termination - no data in the height field break; } if (layer == 0) { currentLastZ = layerZ; } if (Math.Sign(entryZ - layerZ) != Math.Sign(exitZ - layerZ)) { // ray crosses the square, proper intersection if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, layerZ)); } else if ((layerZ > exitZ) ^ rayGoesFromDepth) { // termination since the rest of layers is also behind break; } // else: ray misses the square in front of it currentLastZ = layerZ; } // ray misses the square behind it if (previousLastZ.HasValue && currentLastZ.HasValue) { //float diff = (rayGoesFromDepth ? -1 : 1) * // (previousLastZ.Value - currentLastZ.Value); //if ((diff > 0) && (diff < epsilonForClosePixelDepth)) //{ float diff = previousLastZ.Value - currentLastZ.Value; if ((Math.Sign(currentLastZ.Value - entryZ) != Math.Sign(previousLastZ.Value - entryZ)) && (Math.Abs(diff) < EpsilonForClosePixelDepth)) { // in case the squares of subsequent pixels are too // close and the ray goes between them we can consider // it an intersection if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, 0.5f * diff)); } } previousLastZ = currentLastZ; return null; }
/// <summary> /// Intersects a height field with a ray. /// </summary> /// <remarks> /// <para> /// A height field is a layered 2D table with depth values. At each /// position there can be multiple pixels in layers ordered by /// increasing depth. /// </para> /// <para> /// This procedures find the position of intersection in the /// height field along with the depth of the intersected pixel, or /// a zero vector in case of no intersection, and indicates if there /// was an intersection. /// </para> /// <para> /// Note that there is are only divisions at beginning and the loop /// contains only additions, multiplications, Math.Abs() and /// height field table lookups. /// </para> /// <para> /// Based on the line footprint traversal algorithm. See /// BokehLab.Math.LineFootprint. /// </para> /// </remarks> /// <param name="ray">incoming ray</param> /// <param name="heightfield">height field to be intersected</param> /// <returns>Intersection instance - position of the intersection in /// the height field (only the pixel corner, not the exact position) /// - if the was an intersection; null otherwise</returns> internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { bool collectDebugInfo = debugInfo != null; Vector3 rayDirection = end - start; if (Math.Abs(rayDirection.Z) < epsilonForCorners) { return(null); } bool rayGoesFromDepth = rayDirection.Z < 0; Vector2 dir = rayDirection.Xy; bool rayGoesRatherHorizontal = Math.Abs(dir.X) > Math.Abs(dir.Y); Vector2 dirInv = new Vector2((float)(1 / dir.X), (float)(1 / dir.Y)); Vector2 rayDzOverDxy = rayDirection.Z * dirInv; // direction to the nearest corner: [1,1], [1,-1], [-1,1] or [-1,-1] Vector2 relDir = new Vector2(Math.Sign(dir.X), Math.Sign(dir.Y)); bool isDirectionAxisAligned = relDir.X * relDir.Y == 0; // relDir converted to a single pixel: [1,1], [1,0], [0,1] or [0,0] Vector2 relCorner = isDirectionAxisAligned ? relDir : (0.5f * (relDir + new Vector2(1, 1))); // point where the 2D ray projection enters the current pixel Vector3 entry = start; Vector2 entryXY = entry.Xy; Vector2 currentPixel = GetPixelCorner(entryXY, relDir); Vector2 endPixel = GetPixelCorner(end.Xy, -relDir); if (collectDebugInfo) { debugInfo.StartPixel = currentPixel; debugInfo.EndPixel = endPixel; } // absolute position of the nearest corner Vector2 corner = currentPixel + relCorner; // depth of the last square in the previous pixel which was // completely in front of the ray float?previousLastZ = null; while (currentPixel != endPixel) { if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(entryXY); } if ((currentPixel.X < 0) || (currentPixel.X >= this.HeightField.Width) || (currentPixel.Y < 0) || (currentPixel.Y >= this.HeightField.Height)) { return(null); } // We can only decide the orientation to nearest corner only // if the ray direction in the XY plane is axis aligned. // Otherwise we have to use the original relative direction. // Also this direction is used if traversing directly across // the corner. Vector2 nextDir = relDir; // across the corner by default if (!isDirectionAxisAligned) { // Get a direction of a vector perpendicular to directions // from the current position both to the nearest corner and // the end of the ray (it is aligned with the Z axis). float crossLength = (corner.X - entryXY.X) * (end.Y - entryXY.Y) - (corner.Y - entryXY.Y) * (end.X - entryXY.X); // it is equavalent to: // float crossLength = Cross2d(corner - entryXY, end.Xy - entryXY); // or to: // float crossLength = Vector3.Cross(new Vector3(corner - entryXY), new Vector3(rayEnd - entryXY)).Z; // The direction of the cross vector determines the relative // orientation of the two examined vectors: // 0 (+- epsilon) -> go across the corner // > 0 -> go to the clockwise edge // < 0 -> go to the counter-clockwise edge if (Math.Abs(crossLength) > epsilonForCorners) { // add a vector perpendicular in one or another direction // to get a vector 45 degrees apart from the corner Vector2 cw = new Vector2(-relDir.Y, relDir.X); if (crossLength > 0) { // (relDir + ccw) / 2 -> clockwise edge nextDir += cw; } else { // (relDir - ccw) / 2 -> counter-clockwise edge nextDir -= cw; } nextDir *= 0.5f; } } // point where the 2D ray projection exits the current pixel Vector3 exit = new Vector3(IntersectPixelEdge2d(entryXY, dir, dirInv, corner, nextDir)); if (rayGoesRatherHorizontal) { exit.Z = start.Z + (exit.X - start.X) * rayDzOverDxy.X; } else { exit.Z = start.Z + (exit.Y - start.Y) * rayDzOverDxy.Y; } // compute intersection with the height field pixel (in several layers) Intersection isec = IntersectLayerAtPixel(currentPixel, entry.Z, exit.Z, rayGoesFromDepth, collectDebugInfo, debugInfo, ref previousLastZ); if (isec != null) { return(isec); } entry = exit; entryXY = entry.Xy; currentPixel += nextDir; corner += nextDir; } // tail of the ray (the end pixel) if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(entryXY); } return(IntersectLayerAtPixel(currentPixel, entry.Z, end.Z, rayGoesFromDepth, collectDebugInfo, debugInfo, ref previousLastZ)); }
private Intersection IntersectLayerAtPixel( Vector2 currentPixel, float entryZ, float exitZ, bool rayGoesFromDepth, bool collectDebugInfo, FootprintDebugInfo debugInfo, ref float?previousLastZ) { // depth of the last square in the current pixel which was // completely in front of the ray float?currentLastZ = null; int layer; for (layer = 0; layer < this.HeightField.LayerCount; layer++) { // Tests whether a ray going over a height field pixel intersects it or not. // // There is an intersection if the heightfield pixel depth is between // depths of ray entry and exit points. In case of equality (up to // epsilon) the ray touches the pixel. Otherwise it misses the pixel. float layerZ = this.HeightField.GetDepth((int)currentPixel.X, (int)currentPixel.Y, layer); if (layerZ == 1) { // early termination - no data in the height field break; } if (layer == 0) { currentLastZ = layerZ; } if (Math.Sign(entryZ - layerZ) != Math.Sign(exitZ - layerZ)) { // ray crosses the square, proper intersection if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return(new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, layerZ))); } else if ((layerZ > exitZ) ^ rayGoesFromDepth) { // termination since the rest of layers is also behind break; } // else: ray misses the square in front of it currentLastZ = layerZ; } // ray misses the square behind it if (previousLastZ.HasValue && currentLastZ.HasValue) { //float diff = (rayGoesFromDepth ? -1 : 1) * // (previousLastZ.Value - currentLastZ.Value); //if ((diff > 0) && (diff < epsilonForClosePixelDepth)) //{ float diff = previousLastZ.Value - currentLastZ.Value; if ((Math.Sign(currentLastZ.Value - entryZ) != Math.Sign(previousLastZ.Value - entryZ)) && (Math.Abs(diff) < EpsilonForClosePixelDepth)) { // in case the squares of subsequent pixels are too // close and the ray goes between them we can consider // it an intersection if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return(new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, 0.5f * diff))); } } previousLastZ = currentLastZ; return(null); }
internal abstract Intersection Intersect(Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo);
public Intersection Intersect(Ray ray) { FootprintDebugInfo debugInfo = null; return(Intersect((Vector3)ray.Origin, (Vector3)(ray.Origin + ray.Direction), ref debugInfo)); }
public Intersection Intersect(Vector3 start, Vector3 end) { FootprintDebugInfo debugInfo = null; return(Intersect(start, end, ref debugInfo)); }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> internal override Intersection Intersect( Vector3 start, Vector3 end, ref FootprintDebugInfo debugInfo) { bool collectDebugInfo = debugInfo != null; #region initialization Vector2 rayStart = start.Xy; Vector2 rayEnd = end.Xy; Vector3 dir = end - start; Vector2 rayDir = dir.Xy; // make sure the denominator in tMax and tDelta is not zero if (Math.Abs(rayDir.X) < epsilonForRayDir) { rayDir.X = epsilonForRayDir; } if (Math.Abs(rayDir.Y) < epsilonForRayDir) { rayDir.Y = epsilonForRayDir; } Vector2 step = new Vector2(Math.Sign(rayDir.X), Math.Sign(rayDir.Y)); Vector2 currentPixel = GetPixelCorner(start.Xy, step); Vector2 endPixel = GetPixelCorner(start.Xy + rayDir, -step); Vector2 boundary = new Vector2( currentPixel.X + ((step.X > 0) ? 1 : 0), currentPixel.Y + ((step.Y > 0) ? 1 : 0)); Vector2 boundaryToStart = boundary - rayStart; Vector2 rayDirInv = new Vector2(1 / rayDir.X, 1 / rayDir.Y); Vector2 tMax = new Vector2(boundaryToStart.X * rayDirInv.X, boundaryToStart.Y * rayDirInv.Y); Vector2 tDelta = new Vector2(step.X * rayDirInv.X, step.Y * rayDirInv.Y); if (collectDebugInfo) { debugInfo.StartPixel = currentPixel; debugInfo.EndPixel = endPixel; } #endregion #region traversal List<Vector2> footprintPixels = new List<Vector2>(); int maxIterations = (int)(2 * rayDir.Length); int iterations = 0; debugInfo.EntryPoints.Add(rayStart); Vector3 entry = start; while (currentPixel != endPixel) { if (collectDebugInfo) { debugInfo.VisitedPixels.Add(currentPixel); //debugInfo.EntryPoints.Add(rayStart + Math.Min(tMax.X, tMax.Y) * rayDir); debugInfo.EntryPoints.Add(entry.Xy); } Vector3 exit; if (tMax.X < tMax.Y) { exit = start + tMax.X * dir; tMax.X += tDelta.X; currentPixel.X += step.X; } else { exit = start + tMax.Y * dir; tMax.Y += tDelta.Y; currentPixel.Y += step.Y; } // height field intersection for (int layer = 0; layer < this.HeightField.LayerCount; layer++) { float layerZ = this.HeightField.GetDepth((int)currentPixel.X, (int)currentPixel.Y, layer); // we could compare: // (1) sign(entry.Z - hf[pixel.xy]) != sign(exit.Z - hf[pixel.xy]) // (2) sign(entry.Z - hf[entry.xy]) != sign(exit.Z - hf[exit.xy]) // (3) sign(entry.Z - hf[middle.xy]) != sign(exit.Z - hf[middle.xy]) //Vector2 middle = 0.5f * (exit.Xy - entry.Xy); //float layerZ = this.HeightField.GetDepth((int)middle.X, (int)middle.Y, 0); if ((Math.Sign(layerZ - entry.Z) != Math.Sign(layerZ - exit.Z))) { if (collectDebugInfo) { debugInfo.LayerOfIntersection = layer; } return new Intersection(new Vector3d(currentPixel.X, currentPixel.Y, layerZ)); } } entry = exit; if (iterations == maxIterations) break; iterations++; } debugInfo.VisitedPixels.Add(currentPixel); debugInfo.EntryPoints.Add(rayEnd); #endregion return null; }
private void ComputeIntersection(bool withDebugInfo) { rayStartPoint = new Point((int)rayStart.X, (int)rayStart.Y); rayEndPoint = new Point((int)rayEnd.X, (int)rayEnd.Y); if (withDebugInfo) { footprintDebugInfo = new FootprintDebugInfo(); } else { footprintDebugInfo = null; } intersection = selectedIntersector.Intersect(rayStart, rayEnd, ref footprintDebugInfo); intersectionLabel.Text = (intersection != null) ? intersection.Position.ToString() : "none"; isecLayerLabel.Text = (footprintDebugInfo != null) && (intersection != null) ? footprintDebugInfo.LayerOfIntersection.ToString() : ""; heightFieldPanel.Invalidate(); footprintTraversalPanel.Invalidate(); }