private void SetNextSurfacePoint() { CapsuleD surfaceFinder; surfaceFinder.Radius = 1f; float maxRingSize = m_grid.LocalVolume.Radius; maxRingSize *= maxRingSize; bool overMax = false; surfaceFinder.P0 = m_grid.GetCentre(); Vector3D targetWorld = m_target.WorldPosition(); for (int i = 0; i < 1000; i++) { ExpandingRings.Ring ring = ExpandingRings.GetRing(m_ringIndex); if (m_squareIndex >= ring.Squares.Length) { ring = ExpandingRings.GetRing(++m_ringIndex); m_squareIndex = 0; } Vector2I square = ring.Squares[m_squareIndex++]; Vector3 direct1; Vector3.Multiply(ref m_perp1, square.X, out direct1); Vector3 direct2; Vector3.Multiply(ref m_perp2, square.Y, out direct2); surfaceFinder.P1 = targetWorld + direct1 + direct2; if (CapsuleDExtensions.Intersects(ref surfaceFinder, (MyVoxelBase)m_target.Entity, out m_surfacePoint)) { Log.DebugLog("test from " + surfaceFinder.P0 + " to " + surfaceFinder.P1 + ", hit voxel at " + m_surfacePoint); m_finalMine = Vector3D.DistanceSquared(m_surfacePoint, m_target.WorldPosition()) < 1d; m_stage = Stage.Mine; return; } Log.DebugLog("test from " + surfaceFinder.P0 + " to " + surfaceFinder.P1 + ", did not hit voxel. P1 constructed from " + targetWorld + ", " + direct1 + ", " + direct2); if (ring.DistanceSquared > maxRingSize) { if (overMax) { Log.AlwaysLog("Infinite loop", Logger.severity.FATAL); throw new Exception("Infinte loop"); } overMax = true; Log.DebugLog("Over max ring size, starting next level", Logger.severity.INFO); m_squareIndex = 0; m_ringIndex = 0; } } Log.AlwaysLog("Infinite loop", Logger.severity.FATAL); throw new Exception("Infinte loop"); }
/// <summary> /// Tests if the ship is obstructed by any voxel. /// </summary> /// <param name="input"><see cref="TestInput"/></param> /// <param name="result"><see cref="VoxelTestResult"/></param> /// <returns>True iff a voxel is obstructing the ship.</returns> public bool RayCastIntersectsVoxel(ref TestInput input, out VoxelTestResult result) { Profiler.StartProfileBlock(); Logger.DebugLog("direction vector is invalid: " + input.Direction, Logger.severity.FATAL, condition: !input.Direction.IsValid() || Math.Abs(1f - input.Direction.LengthSquared()) > 0.01f); Logger.TraceLog(input.ToString()); if (input.Length < 1f) { // need to skip as Proximity doesn't work with short capsules // should be safe, as the ship got here somehow Logger.TraceLog("Input length is small, no voxel test necessary"); result = VoxelTestResult.Default; result.Distance = input.Length; result.Proximity = 1f; return(false); } Vector3D currentPosition = AutopilotGrid.GetCentre(); Vector3 startOffset; Vector3.Multiply(ref input.Direction, StartRayCast, out startOffset); Vector3D startOffsetD = startOffset; Vector3D totalOffset; Vector3D.Add(ref input.Offset, ref startOffsetD, out totalOffset); CapsuleD capsule; Vector3D.Add(ref currentPosition, ref totalOffset, out capsule.P0); Vector3D capsuleDisp; { capsuleDisp.X = input.Direction.X * input.Length; capsuleDisp.Y = input.Direction.Y * input.Length; capsuleDisp.Z = input.Direction.Z * input.Length; } Vector3D.Add(ref capsule.P0, ref capsuleDisp, out capsule.P1); capsule.Radius = AutopilotGrid.PositionComp.LocalVolume.Radius; //Logger.DebugLog("current position: " + currentPosition + ", offset: " + offset + ", line: " + rayDirectionLength + ", start: " + capsule.P0 + ", end: " + capsule.P1); result = VoxelTestResult.Default; Vector3D hitPosition; float proximity = (float)CapsuleDExtensions.ProximityToVoxel(ref capsule, out result.ObstructingVoxel, out hitPosition, true, input.Length); result.Proximity = 10f * proximity; // lie because we have not done a proper test but could have a very nice result if (proximity > 1f) { Logger.TraceLog("Large capsule DOES NOT intersect voxel: " + capsule.String() + ", proximity: " + proximity + "/" + result.Proximity); result.Distance = input.Length; Profiler.EndProfileBlock(); return(false); } Logger.TraceLog("Large capsule DOES intersect voxel: " + capsule.String() + ", proximity: " + proximity + "/" + result.Proximity); IEnumerable <CubeGridCache> myCaches = AttachedGrid.AttachedGrids(AutopilotGrid, AttachedGrid.AttachmentKind.Physics, true).Select(CubeGridCache.GetFor); Vector3 v; input.Direction.CalculatePerpendicularVector(out v); Vector3 w; Vector3.Cross(ref input.Direction, ref v, out w); Matrix to3D = new Matrix(v.X, v.Y, v.Z, 0f, w.X, w.Y, w.Z, 0f, input.Direction.X, input.Direction.Y, input.Direction.Z, 0f, 0f, 0f, 0f, 1f); Matrix to2D; Matrix.Invert(ref to3D, out to2D); Vector2IMatrix <Vector3D> apShipRejections; ResourcePool.Get(out apShipRejections); MatrixD worldMatrix = AutopilotGrid.WorldMatrix; float gridSize = AutopilotGrid.GridSize; foreach (CubeGridCache cache in myCaches) { if (cache == null) { Logger.DebugLog("Missing a cache", Logger.severity.DEBUG); Profiler.EndProfileBlock(); return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { Vector3 local = cell * gridSize; Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D offsetWorld; Vector3D.Add(ref world, ref totalOffset, out offsetWorld); Vector3D relative; Vector3D.Subtract(ref offsetWorld, ref currentPosition, out relative); Vector3 relativeF = relative; Vector3 rejection; Vector3.Reject(ref relativeF, ref input.Direction, out rejection); Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.FATAL, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); apShipRejections.Add(ToCell(pc2, gridSize), offsetWorld); } } Vector2IMatrix <bool> testedRejections; ResourcePool.Get(out testedRejections); const int allowedEmpty = 2; foreach (KeyValuePair <Vector2I, Vector3D> cell in apShipRejections.MiddleOut()) { //Logger.DebugLog("Cell was not set: " + cell, Logger.severity.FATAL, condition: cell.Value == Vector3D.Zero); if (!testedRejections.Add(cell.Key, true)) { continue; } int ringIndex = 0; m_insideRing.Clear(); int biggestRingSq = 0; while (true) { int consecutiveEmpty = 0; ExpandingRings.Ring ring = ExpandingRings.GetRing(ringIndex++); foreach (Vector2I ringOffset in ring.Squares) { if (apShipRejections.Contains(ringOffset + cell.Key)) { consecutiveEmpty = 0; } else { consecutiveEmpty++; if (consecutiveEmpty > allowedEmpty) { goto GotRing; } } } m_insideRing.AddArray(ring.Squares); biggestRingSq = ring.DistanceSquared; } GotRing: foreach (Vector2I ringOffset in m_insideRing) { testedRejections.Add(ringOffset + cell.Key, true); } capsule.P0 = cell.Value; Vector3D.Add(ref capsule.P0, ref capsuleDisp, out capsule.P1); capsule.Radius = (1f + (float)Math.Sqrt(biggestRingSq)) * gridSize; result.Proximity = (float)CapsuleDExtensions.ProximityToVoxel(ref capsule, out result.ObstructingVoxel, out hitPosition, true, input.Length); if (result.Proximity <= 1f) { Logger.TraceLog("Block capsule does hit voxel: " + capsule.String() + ", proxmity: " + result.Proximity); double distance; Vector3D.Distance(ref capsule.P0, ref hitPosition, out distance); result.Distance = (float)distance; apShipRejections.Clear(); testedRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(testedRejections); Profiler.EndProfileBlock(); return(true); } } Logger.TraceLog("Ship's path is clear from voxels, proximity: " + result.Proximity); apShipRejections.Clear(); testedRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(testedRejections); Profiler.EndProfileBlock(); return(false); }
/// <summary> /// Tests a grid for obstructing the ship via vector rejection. /// </summary> /// <param name="oGrid">The grid that may obstruct this one.</param> /// <param name="ignoreBlock">Block to ignore, or null</param> /// <param name="input"><see cref="TestInput"/></param> /// <param name="result"><see cref="GridTestResult"/></param> /// <returns>True if oGrid is blocking the ship.</returns> private bool RejectionIntersects(MyCubeGrid oGrid, MyCubeBlock ignoreBlock, ref TestInput input, ref GridTestResult result) { //Logger.DebugLog("Rejection vector is not normalized, length squared: " + rejectionVector.LengthSquared(), Logger.severity.FATAL, condition: Math.Abs(rejectionVector.LengthSquared() - 1f) > 0.001f); //Logger.DebugLog("Testing for rejection intersection: " + oGrid.nameWithId() + ", starting from: " + (AutopilotGrid.GetCentre() + offset) + ", rejection vector: " + rejectionVector + ", distance: " + rejectionDistance + // ", final: " + (AutopilotGrid.GetCentre() + offset + rejectionVector * rejectionDistance)); //Logger.DebugLog("rejction distance < 0: " + rejectionDistance, Logger.severity.ERROR, condition: rejectionDistance < 0f); IEnumerable <CubeGridCache> myCaches = AttachedGrid.AttachedGrids(AutopilotGrid, AttachedGrid.AttachmentKind.Physics, true).Select(CubeGridCache.GetFor); Vector3D currentPosition = AutopilotGrid.GetCentre(); CubeGridCache oCache = CubeGridCache.GetFor(oGrid); if (oCache == null) { Logger.DebugLog("Failed to get cache for other grid", Logger.severity.DEBUG); return(false); } bool checkBlock = ignoreBlock != null && oGrid == ignoreBlock.CubeGrid; Vector3 v; input.Direction.CalculatePerpendicularVector(out v); Vector3 w; Vector3.Cross(ref input.Direction, ref v, out w); Matrix to3D = new Matrix(v.X, v.Y, v.Z, 0f, w.X, w.Y, w.Z, 0f, input.Direction.X, input.Direction.Y, input.Direction.Z, 0f, 0f, 0f, 0f, 1f); Matrix to2D; Matrix.Invert(ref to3D, out to2D); float roundTo; int minDistanceSquared; if (AutopilotGrid.GridSizeEnum == oGrid.GridSizeEnum) { roundTo = AutopilotGrid.GridSize; minDistanceSquared = 1; } else { roundTo = Math.Min(AutopilotGrid.GridSize, oGrid.GridSize); minDistanceSquared = (int)Math.Ceiling(Math.Max(AutopilotGrid.GridSize, oGrid.GridSize) / roundTo); minDistanceSquared *= minDistanceSquared; } int maxDistanceSquared = minDistanceSquared * 100; Profiler.StartProfileBlock("RejectionIntersects:Build ship"); Vector2IMatrix <bool> apShipRejections; ResourcePool.Get(out apShipRejections); MatrixD worldMatrix = AutopilotGrid.WorldMatrix; float gridSize = AutopilotGrid.GridSize; float minProjection = float.MaxValue, maxProjection = float.MinValue; // the permitted range when rejecting the other grids cells foreach (CubeGridCache cache in myCaches) { if (cache == null) { Logger.DebugLog("Missing a cache", Logger.severity.DEBUG); Profiler.EndProfileBlock(); return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { Vector3 local = cell * gridSize; MyCubeBlock block = (MyCubeBlock)cache.CubeGrid.GetCubeBlock(cell)?.FatBlock; if (block != null && block.Subparts != null && block.Subparts.Count != 0 && !CellOccupiedByBlock(cell, block)) { continue; } Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D relative; Vector3D.Subtract(ref world, ref currentPosition, out relative); Vector3 relativeF = relative; float projectionDistance; Vector3 rejection; VectorExtensions.RejectNormalized(ref relativeF, ref input.Direction, out projectionDistance, out rejection); if (projectionDistance < minProjection) { minProjection = projectionDistance; } else if (projectionDistance > maxProjection) { maxProjection = projectionDistance; } Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); //Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.WARNING, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); apShipRejections[ToCell(pc2, roundTo)] = true; //Logger.DebugLog("My rejection: " + rejection + ", planar: " + ToCell(pc2, roundTo)); } } Profiler.EndProfileBlock(); minProjection += StartRayCast; // allow autopilot to move away from a touching object //Logger.DebugLog("projection min: " + minProjection + ", max: " + maxProjection + ", max for other: " + (maxProjection + rejectionDistance)); maxProjection += input.Length; //Logger.DebugLog("checking other grid cells"); Profiler.StartProfileBlock("RejectionIntersects:other grid"); Vector2IMatrix <bool> otherGridRejections; ResourcePool.Get(out otherGridRejections); worldMatrix = oGrid.WorldMatrix; gridSize = oGrid.GridSize; foreach (Vector3I cell in oCache.OccupiedCells()) { //Logger.DebugLog("cell: " + cell); Vector3 local = cell * gridSize; Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D offsetWorld; Vector3D.Subtract(ref world, ref input.Offset, out offsetWorld); Vector3D relative; Vector3D.Subtract(ref offsetWorld, ref currentPosition, out relative); Vector3 relativeF = relative; Vector3 rejection; VectorExtensions.RejectNormalized(ref relativeF, ref input.Direction, out result.Distance, out rejection); if (result.Distance < minProjection || result.Distance > maxProjection) { continue; } Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); //Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.WARNING, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); Vector2I cell2D = ToCell(pc2, roundTo); if (!otherGridRejections.Add(cell2D, true)) { //Logger.DebugLog("Already tested: " + cell2D); continue; } //Logger.DebugLog("Rejection: " + rejection + ", planar: " + cell2D); //Logger.DebugLog("testing range. x: " + (cell2D.X - steps) + " - " + (cell2D.X + steps)); ExpandingRings.Ring ring = default(ExpandingRings.Ring); for (int ringIndex = 0; ring.DistanceSquared <= maxDistanceSquared; ringIndex++) { ring = ExpandingRings.GetRing(ringIndex); for (int squareIndex = 0; squareIndex < ring.Squares.Length; squareIndex++) { if (apShipRejections.Contains(cell2D + ring.Squares[squareIndex])) { if (ring.DistanceSquared <= minDistanceSquared) { IMySlimBlock slim = oGrid.GetCubeBlock(cell); if (slim != null) { if (checkBlock && slim.FatBlock == ignoreBlock) { continue; } MyCubeBlock fat = (MyCubeBlock)slim.FatBlock; if (fat != null && fat.Subparts != null && fat.Subparts.Count != 0 && !CellOccupiedByBlock(cell, fat)) { continue; } result.ObstructingBlock = fat; } else { result.ObstructingBlock = null; } result.Proximity = 0f; Logger.DebugLog("Hit, projectionDistance: " + result.Distance + ", min: " + minProjection + ", max: " + maxProjection + ", ring: " + ringIndex + ", ring dist sq: " + ring.DistanceSquared + ", min dist sq: " + minDistanceSquared + ", max dist sq: " + maxDistanceSquared); Profiler.EndProfileBlock(); apShipRejections.Clear(); otherGridRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(otherGridRejections); return(true); } else { maxDistanceSquared = ring.DistanceSquared; goto NextCell; } } } } NextCell :; } Profiler.EndProfileBlock(); apShipRejections.Clear(); otherGridRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(otherGridRejections); result.Proximity = (float)Math.Sqrt(maxDistanceSquared); return(false); }