internal void InvalidateRange(Vector3I lodMin, Vector3I lodMax) { // MyLog.Default.WriteLine("InvalidateRange Lod: " + m_lodIndex + " Min: " + lodMin + " Max: " + lodMax); var cell = new MyCellCoord(m_lodIndex, lodMin); for (var it = new Vector3I_RangeIterator(ref lodMin, ref lodMax); it.IsValid(); it.GetNext(out cell.CoordInLod)) { MyClipmap_CellData data; var id = cell.PackId64(); // MyLog.Default.WriteLine("Setting to: m_lodIndex " + cell.Lod + " Coord: " + cell.CoordInLod); if (m_storedCellData.TryGetValue(id, out data)) { data.State = CellState.Invalid; //MyLog.Default.WriteLine("Really set to: m_lodIndex " + cell.Lod + " Coord: " + cell.CoordInLod); } if (MyClipmap.UseCache) { var clipmapCellId = MyCellCoord.GetClipmapCellHash(m_clipmap.Id, id); var cachedCell = MyClipmap.CellsCache.Read(clipmapCellId); if (cachedCell != null) { cachedCell.State = CellState.Invalid; } } } }
/// <summary> /// Checks only immediate children (any deeper would take too long). /// </summary> private static bool ChildrenWereLoaded(LodLevel childLod, ref MyCellCoord thisLodCell) { if (childLod == null) { return(false); } Debug.Assert(thisLodCell.Lod == childLod.m_lodIndex + 1); var childLodCell = new MyCellCoord(); childLodCell.Lod = childLod.m_lodIndex; var shiftToChild = MyVoxelCoordSystems.RenderCellSizeShiftToMoreDetailed(thisLodCell.Lod); var start = thisLodCell.CoordInLod << shiftToChild; var end = start + ((1 << shiftToChild) >> 1); Vector3I.Max(ref childLod.m_lodSizeMinusOne, ref Vector3I.Zero, out childLod.m_lodSizeMinusOne); Vector3I.Min(ref end, ref childLod.m_lodSizeMinusOne, out end); childLodCell.CoordInLod = start; for (var it = new Vector3I_RangeIterator(ref start, ref end); it.IsValid(); it.GetNext(out childLodCell.CoordInLod)) { var key = childLodCell.PackId64(); MyClipmap_CellData data; if (!childLod.m_storedCellData.TryGetValue(key, out data) || !data.WasLoaded) { return(false); } } return(true); }
// Actually, this function marks even cubes around the block to make sure that any changes caused in their triangles // will be reflected in the navigation mesh. public void MarkBlockChanged(MySlimBlock block) { Vector3I min = block.Min - Vector3I.One; Vector3I max = block.Max + Vector3I.One; Vector3I pos = min; for (var it = new Vector3I.RangeIterator(ref block.Min, ref block.Max); it.IsValid(); it.GetNext(out pos)) { m_changedCubes.Add(pos); } Vector3I minCell = CubeToCell(ref min); Vector3I maxCell = CubeToCell(ref max); pos = minCell; for (var it = new Vector3I.RangeIterator(ref minCell, ref maxCell); it.IsValid(); it.GetNext(out pos)) { m_changedCells.Add(pos); MyCellCoord cellCoord = new MyCellCoord(0, pos); ulong packedCell = cellCoord.PackId64(); TryClearCell(packedCell); } }
/// <summary> /// Checks only immediate children (any deeper would take too long). /// </summary> private static bool ChildrenWereLoaded(LodLevel childLod, ref MyCellCoord thisLodCell) { if (childLod == null || !childLod.Visible) return false; Debug.Assert(thisLodCell.Lod == childLod.m_lodIndex + 1); var childLodCell = new MyCellCoord(); childLodCell.Lod = childLod.m_lodIndex; var start = thisLodCell.CoordInLod << 1; var end = start + 1; Vector3I.Min(ref end, ref childLod.m_lodSizeMinusOne, out end); for (childLodCell.CoordInLod.Z = start.Z; childLodCell.CoordInLod.Z <= end.Z; ++childLodCell.CoordInLod.Z) for (childLodCell.CoordInLod.Y = start.Y; childLodCell.CoordInLod.Y <= end.Y; ++childLodCell.CoordInLod.Y) for (childLodCell.CoordInLod.X = start.X; childLodCell.CoordInLod.X <= end.X; ++childLodCell.CoordInLod.X) { var key = childLodCell.PackId64(); CellData data; if (!childLod.m_storedCellData.TryGetValue(key, out data)) { return false; } if (!data.WasLoaded) { return false; } } return true; }
/// <summary> /// Checks ancestor nodes recursively. /// </summary> private static bool WasAncestorCellLoaded(LodLevel parentLod, ref MyCellCoord thisLodCell) { if (parentLod == null || !parentLod.m_fitsInFrustum) { return(true); } Debug.Assert(thisLodCell.Lod == parentLod.m_lodIndex - 1); var shiftToParent = MyVoxelCoordSystems.RenderCellSizeShiftToLessDetailed(thisLodCell.Lod); var parentCell = new MyCellCoord(thisLodCell.Lod + 1, thisLodCell.CoordInLod >> shiftToParent); MyClipmap_CellData data; if (parentLod.m_storedCellData.TryGetValue(parentCell.PackId64(), out data)) { return(data.WasLoaded); } LodLevel ancestor; if (parentLod.m_clipmap.m_lodLevels.TryGetValue(parentLod.m_lodIndex + 1, out ancestor)) { return(WasAncestorCellLoaded(ancestor, ref parentCell)); } else { return(false); } }
/// <summary> /// Checks ancestor nodes recursively. Typically, this checks at most 9 nodes or so (depending on settings). /// </summary> private static bool WasAncestorCellLoaded(LodLevel parentLod, ref MyCellCoord thisLodCell) { if (parentLod == null || !parentLod.m_fitsInFrustum || !parentLod.Visible) { return(true); } Debug.Assert(thisLodCell.Lod == parentLod.m_lodIndex - 1); var parentCell = new MyCellCoord(thisLodCell.Lod + 1, thisLodCell.CoordInLod >> 1); CellData data; if (parentLod.m_storedCellData.TryGetValue(parentCell.PackId64(), out data)) { return(data.WasLoaded); } LodLevel ancestor; if (parentLod.m_parent.m_lodLevels.TryGetValue(parentLod.m_lodIndex + 1, out ancestor)) { return(WasAncestorCellLoaded(ancestor, ref parentCell)); } else { return(false); } }
internal void InvalidateRange(Vector3I lodMin, Vector3I lodMax) { var cell = new MyCellCoord(m_lodIndex, lodMin); for (var it = new Vector3I.RangeIterator(ref lodMin, ref lodMax); it.IsValid(); it.GetNext(out cell.CoordInLod)) { CellData data; var id = cell.PackId64(); if (m_storedCellData.TryGetValue(id, out data)) { data.State = CellState.Invalid; } } }
internal void InvalidateRange(Vector3I lodMin, Vector3I lodMax) { var cell = new MyCellCoord(m_lodIndex, lodMin); for (var it = new Vector3I.RangeIterator(ref lodMin, ref lodMax); it.IsValid(); it.GetNext(out cell.CoordInLod)) { CellData data; var id = cell.PackId64(); using (m_storedCellDataLock.AcquireSharedUsing()) { if (m_storedCellData.TryGetValue(id, out data)) { data.State = CellState.Invalid; } } } }
private void UnclipCell(RequestCollector collector, MyCellCoord cell, bool isVisible) { var cellId = cell.PackId64(); var clipmapCellId = MyCellCoord.GetClipmapCellHash(m_clipmap.Id, cellId); MyClipmap_CellData data; if (isVisible) { bool highPriority = true; if (m_clippedCells.TryGetValue(cellId, out data)) { m_clippedCells.Remove(cellId); } else { highPriority = false; CellBlendData blendData; if (!m_blendedCells.TryGetValue(cellId, out blendData)) { data = CellsCache.Read(clipmapCellId); if (data == null) //cache miss { data = new MyClipmap_CellData(); ClippingCacheMisses++; } else { //cache hit ClippingCacheHits++; //System.Diagnostics.Debug.Assert((!data.InScene && data.Cell != null) || data.Cell == null, "Not allowed cell state"); data.InScene = false; if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } else { data = blendData.CellData; if (blendData.State == BlendState.Removing) blendData.UndoAfterFinish = true; if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } if (data.State == CellState.Invalid) { if (MyClipmap.UseQueries) { BoundingBoxD bbd; MyVoxelCoordSystems.RenderCellCoordToLocalAABB(ref cell, out bbd); BoundingBox bb = new BoundingBox(bbd); if (m_clipmap.m_prunningFunc == null || m_clipmap.m_prunningFunc(ref bb, false) == ContainmentType.Intersects) { collector.AddRequest(cellId, data, highPriority); } else { data.State = CellState.Loaded; data.WasLoaded = true; } } else collector.AddRequest(cellId, data, highPriority); } m_storedCellData.Add(cellId, data); data.ReadyInClipmap = true; data.ClippedOut = false; } else { if (!m_storedCellData.ContainsKey(cellId) && (!PendingCacheCellData.ContainsKey(clipmapCellId) || PendingCacheCellData[clipmapCellId].State == CellState.Invalid) && CellsCache.Read(clipmapCellId) == null) { if (!PendingCacheCellData.TryGetValue(clipmapCellId, out data)) { data = new MyClipmap_CellData(); PendingCacheCellData.Add(clipmapCellId, data); } if (MyClipmap.UseQueries) { BoundingBoxD bbd; MyVoxelCoordSystems.RenderCellCoordToLocalAABB(ref cell, out bbd); BoundingBox bb = new BoundingBox(bbd); if (m_clipmap.m_prunningFunc == null || m_clipmap.m_prunningFunc(ref bb, false) == ContainmentType.Intersects) { data.State = CellState.Invalid; collector.AddRequest(cellId, data, false); } else { data.State = CellState.Loaded; data.WasLoaded = true; } } else { data.State = CellState.Invalid; collector.AddRequest(cellId, data, false); } } } }
public void Submit() { ProfilerShort.Begin("RequestCollector.Submit"); MyCellCoord cell = default(MyCellCoord); foreach (var cellId in m_cancelRequests) { cell.SetUnpack(cellId); MyRenderProxy.CancelClipmapCell(m_clipmapId, cell); bool removed = m_sentRequests.Remove(cellId); Debug.Assert(removed); } foreach (var highPriorityRequest in m_unsentRequestsHigh) { cell.SetUnpack(highPriorityRequest); MyRenderProxy.RequireClipmapCell(m_clipmapId, cell, highPriority: true); } m_unsentRequestsHigh.Clear(); int addedCount = 0; for (int i = m_unsentRequestsLow.Length - 1; i >= 0; i--) { var unsent = m_unsentRequestsLow[i]; while (0 < unsent.Count && m_sentRequests.Count < m_maxRequests) { var cellId = unsent.FirstElement(); cell.SetUnpack(cellId); // Do Z-order style iteration of siblings that also need to // be requested. This ensures faster processing of cells and // shorter time when both lods are rendered. var baseCoord = (cell.CoordInLod >> 1) << 1; var offset = Vector3I.Zero; for (var it = new Vector3I.RangeIterator(ref Vector3I.Zero, ref Vector3I.One); it.IsValid(); it.GetNext(out offset)) { cell.CoordInLod = baseCoord + offset; cellId = cell.PackId64(); if (!unsent.Remove(cellId)) { continue; } Debug.Assert(!m_cancelRequests.Contains(cellId)); MyRenderProxy.RequireClipmapCell(m_clipmapId, cell, highPriority: false); bool added = m_sentRequests.Add(cellId); Debug.Assert(added); addedCount++; } } // When set reaches reasonably small size, stop freeing memory if (unsent.Count > 100) { unsent.TrimExcess(); } } m_cancelRequests.Clear(); ProfilerShort.End(); }
private void UnclipCell(RequestCollector collector, MyCellCoord cell, bool isVisible) { var cellId = cell.PackId64(); var clipmapCellId = MyCellCoord.GetClipmapCellHash(m_clipmap.Id, cellId); CellData data; if (isVisible) { bool highPriority = true; if (m_clippedCells.TryGetValue(cellId, out data)) { m_clippedCells.Remove(cellId); } else { highPriority = false; CellBlendData blendData; if (!m_blendedCells.TryGetValue(cellId, out blendData)) { data = CellsCache.Read(clipmapCellId); if (data == null) //cache miss { data = new CellData(); ClippingCacheMisses++; } else { //cache hit ClippingCacheHits++; //System.Diagnostics.Debug.Assert((!data.InScene && data.Cell != null) || data.Cell == null, "Not allowed cell state"); data.InScene = false; if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } else { data = blendData.CellData; if (blendData.State == BlendState.Removing) { blendData.UndoAfterFinish = true; } if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } if (data.State == CellState.Invalid) { if (MyClipmap.UseQueries) { BoundingBoxD bbd; MyVoxelCoordSystems.RenderCellCoordToLocalAABB(ref cell, out bbd); BoundingBox bb = new BoundingBox(bbd); if (m_clipmap.m_prunningFunc == null || m_clipmap.m_prunningFunc(ref bb, false) == ContainmentType.Intersects) { collector.AddRequest(cellId, data, highPriority); } else { data.State = CellState.Loaded; data.WasLoaded = true; } } else { collector.AddRequest(cellId, data, highPriority); } } m_storedCellData.Add(cellId, data); data.ReadyInClipmap = true; data.ClippedOut = false; } else { if (!m_storedCellData.ContainsKey(cellId) && (!PendingCacheCellData.ContainsKey(clipmapCellId) || PendingCacheCellData[clipmapCellId].State == CellState.Invalid) && CellsCache.Read(clipmapCellId) == null) { if (!PendingCacheCellData.TryGetValue(clipmapCellId, out data)) { data = new CellData(); PendingCacheCellData.Add(clipmapCellId, data); } if (MyClipmap.UseQueries) { BoundingBoxD bbd; MyVoxelCoordSystems.RenderCellCoordToLocalAABB(ref cell, out bbd); BoundingBox bb = new BoundingBox(bbd); if (m_clipmap.m_prunningFunc == null || m_clipmap.m_prunningFunc(ref bb, false) == ContainmentType.Intersects) { data.State = CellState.Invalid; collector.AddRequest(cellId, data, false); } else { data.State = CellState.Loaded; data.WasLoaded = true; } } else { data.State = CellState.Invalid; collector.AddRequest(cellId, data, false); } } } }
private MyNavigationTriangle GetClosestNavigationTriangle(ref Vector3 point, ref float closestDistanceSq) { // TODO: When point is completely away (according to BB), return null MyNavigationTriangle closestTriangle = null; // Convert from world matrix local coords to LeftBottomCorner-based coords Vector3 lbcPoint = point + (m_voxelMap.PositionComp.GetPosition() - m_voxelMap.PositionLeftBottomCorner); Vector3I closestCellCorner = Vector3I.Round(lbcPoint / m_cellSize); for (int i = 0; i < 8; ++i) { Vector3I cell = closestCellCorner + m_cornerOffsets[i]; if (!m_processedCells.Contains(cell)) continue; MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, cell); ulong packedCoord = coord.PackId64(); MyIntervalList triList = m_higherLevelHelper.TryGetTriangleList(packedCoord); if (triList == null) continue; foreach (var triIndex in triList) { MyNavigationTriangle tri = GetTriangle(triIndex); // TODO: Use triangle centers so far float distSq = Vector3.DistanceSquared(tri.Center, point); if (distSq < closestDistanceSq) { closestDistanceSq = distSq; closestTriangle = tri; } } } return closestTriangle; }
private bool AddCell(Vector3I cellPos) { MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, cellPos); var generatedMesh = MyPrecalcComponent.IsoMesher.Precalc(new MyIsoMesherArgs() { Storage = m_voxelMap.Storage, GeometryCell = coord, }); if (generatedMesh == null) { m_processedCells.Add(ref cellPos); m_higherLevelHelper.AddExplored(ref cellPos); return false; } ulong packedCoord = coord.PackId64(); List<DebugDrawEdge> debugEdgesList = new List<DebugDrawEdge>(); m_debugCellEdges[packedCoord] = debugEdgesList; MyVoxelPathfinding.CellId cellId = new MyVoxelPathfinding.CellId() { VoxelMap = m_voxelMap, Pos = cellPos }; MyTrace.Send(TraceWindow.Ai, "Adding cell " + cellPos); m_connectionHelper.ClearCell(); m_vertexMapping.Init(generatedMesh.VerticesCount); // Prepare list of possibly intersecting cube grids for voxel-grid navmesh intersection testing Vector3D bbMin = m_voxelMap.PositionLeftBottomCorner + (m_cellSize * (new Vector3D(-0.125) + cellPos)); Vector3D bbMax = m_voxelMap.PositionLeftBottomCorner + (m_cellSize * (Vector3D.One + cellPos)); BoundingBoxD cellBB = new BoundingBoxD(bbMin, bbMax); m_tmpGridList.Clear(); m_navmeshCoordinator.PrepareVoxelTriangleTests(cellBB, m_tmpGridList); Vector3D voxelMapCenter = m_voxelMap.PositionComp.GetPosition(); Vector3 centerDisplacement = voxelMapCenter - m_voxelMap.PositionLeftBottomCorner; // This is needed for correct edge classification - to tell, whether the edges are inner or outer edges of the cell ProfilerShort.Begin("Triangle preprocessing"); for (int i = 0; i < generatedMesh.TrianglesCount; i++) { ushort a = generatedMesh.Triangles[i].VertexIndex0; ushort b = generatedMesh.Triangles[i].VertexIndex1; ushort c = generatedMesh.Triangles[i].VertexIndex2; Vector3 aPos, bPos, cPos; Vector3 vert; generatedMesh.GetUnpackedPosition(a, out vert); aPos = vert - centerDisplacement; generatedMesh.GetUnpackedPosition(b, out vert); bPos = vert - centerDisplacement; generatedMesh.GetUnpackedPosition(c, out vert); cPos = vert - centerDisplacement; bool invalidTriangle = false; if ((bPos - aPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ) { m_vertexMapping.Union(a, b); invalidTriangle = true; } if ((cPos - aPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ) { m_vertexMapping.Union(a, c); invalidTriangle = true; } if ((cPos - bPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ) { m_vertexMapping.Union(b, c); invalidTriangle = true; } if (invalidTriangle) continue; m_connectionHelper.PreprocessInnerEdge(a, b); m_connectionHelper.PreprocessInnerEdge(b, c); m_connectionHelper.PreprocessInnerEdge(c, a); } ProfilerShort.End(); ProfilerShort.Begin("Free face sorting"); // Ensure that the faces have increasing index numbers Mesh.SortFreeFaces(); ProfilerShort.End(); m_higherLevelHelper.OpenNewCell(coord); ProfilerShort.Begin("Adding triangles"); for (int i = 0; i < generatedMesh.TrianglesCount; i++) { ushort a = generatedMesh.Triangles[i].VertexIndex0; ushort b = generatedMesh.Triangles[i].VertexIndex1; ushort c = generatedMesh.Triangles[i].VertexIndex2; ushort setA = (ushort)m_vertexMapping.Find(a); ushort setB = (ushort)m_vertexMapping.Find(b); ushort setC = (ushort)m_vertexMapping.Find(c); if (setA == setB || setB == setC || setA == setC) continue; Vector3 aPos, bPos, cPos; Vector3 vert; generatedMesh.GetUnpackedPosition(a, out vert); aPos = vert - centerDisplacement; generatedMesh.GetUnpackedPosition(b, out vert); bPos = vert - centerDisplacement; generatedMesh.GetUnpackedPosition(c, out vert); cPos = vert - centerDisplacement; if (MyPerGameSettings.NavmeshPresumesDownwardGravity) { Vector3 normal = (cPos - aPos).Cross(bPos - aPos); normal.Normalize(); if (normal.Dot(ref Vector3.Up) <= Math.Cos(MathHelper.ToRadians(54.0f))) continue; } Vector3D aTformed = aPos + voxelMapCenter; Vector3D bTformed = bPos + voxelMapCenter; Vector3D cTformed = cPos + voxelMapCenter; bool intersecting = false; m_tmpLinkCandidates.Clear(); m_navmeshCoordinator.TestVoxelNavmeshTriangle(ref aTformed, ref bTformed, ref cTformed, m_tmpGridList, m_tmpLinkCandidates, out intersecting); if (intersecting) { m_tmpLinkCandidates.Clear(); continue; } if (!m_connectionHelper.IsInnerEdge(a, b)) debugEdgesList.Add(new DebugDrawEdge(aTformed, bTformed)); if (!m_connectionHelper.IsInnerEdge(b, c)) debugEdgesList.Add(new DebugDrawEdge(bTformed, cTformed)); if (!m_connectionHelper.IsInnerEdge(c, a)) debugEdgesList.Add(new DebugDrawEdge(cTformed, aTformed)); int edgeAB = m_connectionHelper.TryGetAndRemoveEdgeIndex(b, a, ref bPos, ref aPos); int edgeBC = m_connectionHelper.TryGetAndRemoveEdgeIndex(c, b, ref cPos, ref bPos); int edgeCA = m_connectionHelper.TryGetAndRemoveEdgeIndex(a, c, ref aPos, ref cPos); int formerAB = edgeAB; int formerBC = edgeBC; int formerCA = edgeCA; ProfilerShort.Begin("AddTriangle"); var tri = AddTriangle(ref aPos, ref bPos, ref cPos, ref edgeAB, ref edgeBC, ref edgeCA); ProfilerShort.End(); CheckMeshConsistency(); m_higherLevelHelper.AddTriangle(tri.Index); if (formerAB == -1) m_connectionHelper.AddEdgeIndex(a, b, ref aPos, ref bPos, edgeAB); if (formerBC == -1) m_connectionHelper.AddEdgeIndex(b, c, ref bPos, ref cPos, edgeBC); if (formerCA == -1) m_connectionHelper.AddEdgeIndex(c, a, ref cPos, ref aPos, edgeCA); // TODO: Instead of this, just add the tri into a list of tris that want to connect with the link candidates //m_navmeshCoordinator.TryAddVoxelNavmeshLinks(tri, cellId, m_tmpLinkCandidates); foreach (var candidate in m_tmpLinkCandidates) { List<MyNavigationPrimitive> primitives = null; if (!m_tmpCubeLinkCandidates.TryGetValue(candidate, out primitives)) { primitives = m_primitiveListPool.Allocate(); m_tmpCubeLinkCandidates.Add(candidate, primitives); } primitives.Add(tri); } m_tmpLinkCandidates.Clear(); } ProfilerShort.End(); m_tmpGridList.Clear(); m_connectionHelper.ClearCell(); m_vertexMapping.Clear(); Debug.Assert(!m_processedCells.Contains(ref cellPos)); m_processedCells.Add(ref cellPos); m_higherLevelHelper.AddExplored(ref cellPos); // Find connected components in the current cell's subgraph of the navigation mesh m_higherLevelHelper.ProcessCellComponents(); m_higherLevelHelper.CloseCell(); // Create navmesh links using the navmesh coordinator, taking into consideration the high level components m_navmeshCoordinator.TryAddVoxelNavmeshLinks2(cellId, m_tmpCubeLinkCandidates); m_navmeshCoordinator.UpdateVoxelNavmeshCellHighLevelLinks(cellId); foreach (var candidate in m_tmpCubeLinkCandidates) { candidate.Value.Clear(); m_primitiveListPool.Deallocate(candidate.Value); } m_tmpCubeLinkCandidates.Clear(); return true; }
private void ComputeIsEmptyLookup(MyCellCoord cell, out ulong outCacheKey, out int outBit) { var offset = cell.CoordInLod % 4; cell.CoordInLod >>= 2; Debug.Assert(offset.IsInsideInclusive(Vector3I.Zero, new Vector3I(3))); outCacheKey = cell.PackId64(); outBit = offset.X + 4 * (offset.Y + 4 * offset.Z); Debug.Assert((uint)outBit < 64u); }
/// <param name="minVoxelChanged">Inclusive min.</param> /// <param name="maxVoxelChanged">Inclusive max.</param> private void storage_RangeChanged(Vector3I minChanged, Vector3I maxChanged, MyStorageDataTypeFlags changedData) { MyPrecalcComponent.AssertUpdateThread(); ProfilerShort.Begin("MyVoxelGeometry.storage_RangeChanged"); minChanged -= MyPrecalcComponent.InvalidatedRangeInflate; maxChanged += MyPrecalcComponent.InvalidatedRangeInflate; m_storage.ClampVoxelCoord(ref minChanged); m_storage.ClampVoxelCoord(ref maxChanged); var minCellChanged = minChanged >> MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS_BITS; var maxCellChanged = maxChanged >> MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS_BITS; using (m_lock.AcquireExclusiveUsing()) { if (minCellChanged == Vector3I.Zero && maxCellChanged == m_cellsCount - 1) { m_cellsByCoordinate.Clear(); m_coordinateToMesh.Clear(); m_isEmptyCache.Reset(); } else { MyCellCoord cell = new MyCellCoord(); cell.CoordInLod = minCellChanged; for (var it = new Vector3I_RangeIterator(ref minCellChanged, ref maxCellChanged); it.IsValid(); it.GetNext(out cell.CoordInLod)) { var key = cell.PackId64(); m_cellsByCoordinate.Remove(key); m_coordinateToMesh.Remove(key); SetEmpty(ref cell, false); } } } ProfilerShort.End(); }
public bool TryGetMesh(MyCellCoord cell, out bool isEmpty, out MyIsoMesh nonEmptyMesh) { using (m_lock.AcquireSharedUsing()) { if (IsEmpty(ref cell)) { isEmpty = true; nonEmptyMesh = null; return true; } UInt64 key = cell.PackId64(); if (m_coordinateToMesh.TryGetValue(key, out nonEmptyMesh)) { isEmpty = false; return true; } isEmpty = default(bool); nonEmptyMesh = default(MyIsoMesh); return false; } }
public void InvalidateRange(Vector3I minVoxelChanged, Vector3I maxVoxelChanged) { minVoxelChanged -= MyPrecalcComponent.InvalidatedRangeInflate + 1; maxVoxelChanged += MyPrecalcComponent.InvalidatedRangeInflate + 1; m_voxelMap.Storage.ClampVoxelCoord(ref minVoxelChanged); m_voxelMap.Storage.ClampVoxelCoord(ref maxVoxelChanged); Vector3I minCellLod0, maxCellLod0; minVoxelChanged -= m_voxelMap.StorageMin; maxVoxelChanged -= m_voxelMap.StorageMin; MyVoxelCoordSystems.VoxelCoordToRenderCellCoord(0, ref minVoxelChanged, out minCellLod0); MyVoxelCoordSystems.VoxelCoordToRenderCellCoord(0, ref maxVoxelChanged, out maxCellLod0); MyRenderProxy.InvalidateClipmapRange(m_renderObjectIDs[0], minCellLod0, maxCellLod0); if (minCellLod0 == Vector3I.Zero && maxCellLod0 == ((m_voxelMap.Storage.Size - 1) >> MyVoxelCoordSystems.RenderCellSizeInLodVoxelsShift(0))) { m_renderWorkTracker.InvalidateAll(); m_mergeWorkTracker.InvalidateAll(); } else { for (int i = 0; i < MyCellCoord.MAX_LOD_COUNT; ++i) { var minCell = minCellLod0 >> i; var maxCell = maxCellLod0 >> i; var cellCoord = new MyCellCoord(i, ref minCell); for (var it = new Vector3I_RangeIterator(ref minCell, ref maxCell); it.IsValid(); it.GetNext(out cellCoord.CoordInLod)) { m_renderWorkTracker.Invalidate(cellCoord.PackId64()); m_mergeWorkTracker.Invalidate(cellCoord.PackId64()); } } } }
internal void OnCellRequest(MyCellCoord cell, Func<int> priorityFunction, Action<Color> debugDraw) { ProfilerShort.Begin("OnCellRequest"); try { var workId = cell.PackId64(); MyPrecalcJobRender.Start(new MyPrecalcJobRender.Args { Storage = m_voxelMap.Storage, ClipmapId = ClipmapId, Cell = cell, WorkId = workId, RenderWorkTracker = m_renderWorkTracker, Priority = priorityFunction, DebugDraw = debugDraw, }); } finally { ProfilerShort.End(); } }
private static void WriteRange( ref WriteRangeArgs args, byte defaultData, int lodIdx, Vector3I lodCoord, ref Vector3I min, ref Vector3I max) { MyOctreeNode node = new MyOctreeNode(); { MyCellCoord leaf = new MyCellCoord(lodIdx - LeafLodCount, ref lodCoord); var leafKey = leaf.PackId64(); if (args.Leaves.ContainsKey(leafKey)) { args.Leaves.Remove(leafKey); var childBase = lodCoord << 1; Vector3I childOffset; MyCellCoord child = new MyCellCoord(); child.Lod = leaf.Lod - 1; var leafSize = LeafSizeInVoxels << child.Lod; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i) { ComputeChildCoord(i, out childOffset); child.CoordInLod = childBase + childOffset; var childCopy = child; childCopy.Lod += LeafLodCount; IMyOctreeLeafNode octreeLeaf = new MyProviderLeaf(args.Provider, args.DataType, ref childCopy); args.Leaves.Add(child.PackId64(), octreeLeaf); node.SetChild(i, true); node.SetData(i, octreeLeaf.GetFilteredValue()); } } else { leaf.Lod -= 1; // changes to node coord instead of leaf coord var nodeKey = leaf.PackId64(); if (!args.Nodes.TryGetValue(nodeKey, out node)) { for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i) node.SetData(i, defaultData); } } } if (lodIdx == (LeafLodCount + 1)) { MyCellCoord child = new MyCellCoord(); Vector3I childBase = lodCoord << 1; Vector3I minInLod = min >> LeafLodCount; Vector3I maxInLod = max >> LeafLodCount; Vector3I leafSizeMinusOne = new Vector3I(LeafSizeInVoxels - 1); Vector3I childOffset; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i) { ComputeChildCoord(i, out childOffset); child.CoordInLod = childBase + childOffset; if (!child.CoordInLod.IsInsideInclusive(ref minInLod, ref maxInLod)) continue; var childMin = child.CoordInLod << LeafLodCount; var childMax = childMin + LeafSizeInVoxels - 1; Vector3I.Max(ref childMin, ref min, out childMin); Vector3I.Min(ref childMax, ref max, out childMax); var readOffset = childMin - min; IMyOctreeLeafNode leaf; var leafKey = child.PackId64(); var startInChild = childMin - (child.CoordInLod << LeafLodCount); var endInChild = childMax - (child.CoordInLod << LeafLodCount); args.Leaves.TryGetValue(leafKey, out leaf); byte uniformValue; bool uniformLeaf; { // ensure leaf exists and is writable // the only writable leaf type is MicroOctree at this point byte childDefaultData = node.GetData(i); if (leaf == null) { var octree = new MyMicroOctreeLeaf(args.DataType, LeafLodCount, child.CoordInLod << (child.Lod + LeafLodCount)); octree.BuildFrom(childDefaultData); leaf = octree; } if (leaf.ReadOnly) { var rangeEnd = new Vector3I(LeafSizeInVoxels - 1); m_temporaryCache.Resize(Vector3I.Zero, rangeEnd); leaf.ReadRange(m_temporaryCache, ref Vector3I.Zero, 0, ref Vector3I.Zero, ref rangeEnd); var inCell = startInChild; for (var it2 = new Vector3I.RangeIterator(ref startInChild, ref endInChild); it2.IsValid(); it2.GetNext(out inCell)) { var read = readOffset + (inCell - startInChild); m_temporaryCache.Set(args.DataType, ref inCell, args.Source.Get(args.DataType, ref read)); } var octree = new MyMicroOctreeLeaf(args.DataType, LeafLodCount, child.CoordInLod << (child.Lod + LeafLodCount)); octree.BuildFrom(m_temporaryCache); leaf = octree; } else { leaf.WriteRange(args.Source, ref readOffset, ref startInChild, ref endInChild); } uniformLeaf = ((MyMicroOctreeLeaf)leaf).TryGetUniformValue(out uniformValue); } if (!uniformLeaf) { args.Leaves[leafKey] = leaf; node.SetChild(i, true); } else { args.Leaves.Remove(leafKey); node.SetChild(i, false); } node.SetData(i, leaf.GetFilteredValue()); } args.Nodes[new MyCellCoord(lodIdx - 1 - LeafLodCount, ref lodCoord).PackId64()] = node; } else { MyCellCoord child = new MyCellCoord(); child.Lod = lodIdx - 2 - LeafLodCount; var childBase = lodCoord << 1; Vector3I childOffset; var minInChild = (min >> (lodIdx-1)) - childBase; var maxInChild = (max >> (lodIdx-1)) - childBase; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i) { ComputeChildCoord(i, out childOffset); if (!childOffset.IsInsideInclusive(ref minInChild, ref maxInChild)) continue; child.CoordInLod = childBase + childOffset; WriteRange(ref args, node.GetData(i), lodIdx - 1, child.CoordInLod, ref min, ref max); var childKey = child.PackId64(); var childNode = args.Nodes[childKey]; if (!childNode.HasChildren && childNode.AllDataSame()) { node.SetChild(i, false); node.SetData(i, childNode.GetData(0)); args.Nodes.Remove(childKey); } else { node.SetChild(i, true); node.SetData(i, childNode.ComputeFilteredValue(args.DataFilter)); } } args.Nodes[new MyCellCoord(lodIdx - 1 - LeafLodCount, ref lodCoord).PackId64()] = node; } }
/// <summary> /// Recursive clipping function requests cells in provided range and /// cells needed from parent to wrap the lod safely /// </summary> /// <param name="collector"></param> /// <param name="it0">requested range</param> /// <param name="ignore">inner range filled by children</param> private void DoClipping(RequestCollector collector, Vector3I min, Vector3I max, ref BoundingBox ignore) { LodLevel parentLod, clevel; GetNearbyLodLevels(out parentLod, out clevel); MyCellCoord cell = new MyCellCoord(m_lodIndex, Vector3I.Zero); //if (collector.SentRequestsEmpty) { MyUtils.Swap(ref m_storedCellData, ref m_clippedCells); m_storedCellData.Clear(); } var it0 = new Vector3I.RangeIterator(ref min, ref max); cell.CoordInLod = it0.Current; var shiftToParent = MyVoxelCoordSystems.RenderCellSizeShiftToLessDetailed(cell.Lod); var parentCell = parentLod != null ? new MyCellCoord(parentLod.m_lodIndex, cell.CoordInLod >> shiftToParent) : cell; var parentIgnore = new BoundingBox(parentCell.CoordInLod, parentCell.CoordInLod); BoundingBox bb = new BoundingBox(cell.CoordInLod, cell.CoordInLod); for (; it0.IsValid(); it0.GetNext(out cell.CoordInLod)) //cells to be loaded { if (ignore.Contains((Vector3)cell.CoordInLod) == ContainmentType.Contains) { continue; //lower lod requested } if (parentLod != null) //get also their lodcell mates { parentCell = new MyCellCoord(parentLod.m_lodIndex, cell.CoordInLod >> shiftToParent); var it = GetChildrenCoords(this, ref parentCell); bb.Include(it); parentIgnore.Max = parentCell.CoordInLod; } } if (parentLod != null) { Vector3I parentMinI = Vector3I.Round(parentIgnore.Min - Vector3.One); Vector3I parentMaxI = Vector3I.Round(parentIgnore.Max + Vector3.One); //Vector3I.Clamp(ref parentMinI, ref Vector3I.Zero, ref m_lodSizeMinusOne, out parentMinI); //Vector3I.Clamp(ref parentMaxI, ref Vector3I.Zero, ref m_lodSizeMinusOne, out parentMaxI); var parentIterator = new Vector3I.RangeIterator(ref parentMinI, ref parentMaxI); parentLod.DoClipping(collector, parentMinI, parentMaxI, ref parentIgnore); } Vector3I start, end; start = Vector3I.Round(bb.Min); end = Vector3I.Round(bb.Max); Vector3I.Clamp(ref start, ref Vector3I.Zero, ref m_lodSizeMinusOne, out start); Vector3I.Clamp(ref end, ref Vector3I.Zero, ref m_lodSizeMinusOne, out end); it0 = new Vector3I.RangeIterator(ref start, ref end); cell.CoordInLod = it0.Current; for (; it0.IsValid(); it0.GetNext(out cell.CoordInLod)) //cells to be loaded { if (ignore.Contains((Vector3)cell.CoordInLod) == ContainmentType.Contains) { continue; //lower lod requested } var cellId = cell.PackId64(); CellData data; if (m_clippedCells.TryGetValue(cellId, out data)) { m_clippedCells.Remove(cellId); } else { var clipmapCellId = MyCellCoord.GetClipmapCellHash(m_clipmap.Id, cellId); data = CellsCache.Read(clipmapCellId); if (data == null) //cache miss { data = new CellData(); ClippingCacheMisses++; } else { //cache hit ClippingCacheHits++; data.InScene = false; if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } if (data.State == CellState.Invalid) { if (!TryAddCellRequest(collector, parentLod, cell, cellId, data)) { continue; } } if (!m_storedCellData.ContainsKey(cellId)) { m_storedCellData.Add(cellId, data); } } }
internal void DoClipping_Old(Vector3D localPosition, float farPlaneDistance, RequestCollector collector) { m_localPosition = localPosition; MyClipmap.ComputeLodViewBounds(m_clipmap.m_scaleGroup, m_lodIndex, out m_nearDistance, out m_farDistance); m_fitsInFrustum = (farPlaneDistance * 1.25f) > m_nearDistance; if (!m_fitsInFrustum) { return; } //var localFrustum = new BoundingFrustumD(CameraFrustumGetter().Matrix * m_parent.m_invWorldMatrix); var frustum = CameraFrustumGetter(); Vector3I min, max; Vector3I ignoreMin, ignoreMax; var minD = m_localPosition - m_farDistance; var maxD = m_localPosition + m_farDistance; MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref minD, out min); MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref maxD, out max); BoundingBoxI lodBox = new BoundingBoxI(Vector3I.Zero, m_lodSizeMinusOne); bool intersects = false; bool intersectsNear = false; m_localFarCameraBox = new BoundingBoxI(min, max); m_localNearCameraBox = new BoundingBoxI(min, max); if (lodBox.Intersects(m_localFarCameraBox)) { intersects = true; var intersection = lodBox.Intersect(m_localFarCameraBox); min = intersection.Min; max = intersection.Max; //Optimize only LOD2 and higher by two lods, because neighbour cells shares border cells if (m_lodIndex > 1) { float lowerFar, lowerNear; MyClipmap.ComputeLodViewBounds(m_clipmap.m_scaleGroup, m_lodIndex - 2, out lowerFar, out lowerNear); var minNear = m_localPosition - (lowerNear - MyVoxelCoordSystems.RenderCellSizeInMeters(m_lodIndex) / 2); var maxNear = m_localPosition + (lowerNear - MyVoxelCoordSystems.RenderCellSizeInMeters(m_lodIndex) / 2); MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref minNear, out ignoreMin); MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref maxNear, out ignoreMax); m_localNearCameraBox = new BoundingBoxI(ignoreMin, ignoreMax); if (lodBox.Intersects(m_localNearCameraBox)) { intersectsNear = false; } } } if (m_lastMin == min && m_lastMax == max && !m_clipmap.m_updateClipping) { return; } m_lastMin = min; m_lastMax = max; LodLevel parentLod, childLod; GetNearbyLodLevels(out parentLod, out childLod); // Moves cells which are still needed from one collection to another. // All that is left behind is unloaded as no longer needed. // Move everything in range to collection of next stored cells. MyUtils.Swap(ref m_storedCellData, ref m_clippedCells); m_storedCellData.Clear(); if (intersects) { float sizeInMetres = MyVoxelCoordSystems.RenderCellSizeInMeters(m_lodIndex); MyCellCoord cell = new MyCellCoord(m_lodIndex, ref min); for (var it = new Vector3I.RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out cell.CoordInLod)) { if (intersectsNear && m_localNearCameraBox.Contains(cell.CoordInLod) == ContainmentType.Contains) { continue; } //if (!WasAncestorCellLoaded(parentLod, ref cell)) // continue; Vector3D minAABB = Vector3D.Transform((Vector3D)(sizeInMetres * (cell.CoordInLod - 2)), m_clipmap.m_worldMatrix); Vector3D maxAABB = Vector3D.Transform((Vector3D)(sizeInMetres * (cell.CoordInLod + 2) + new Vector3(sizeInMetres)), m_clipmap.m_worldMatrix); if (frustum.Contains(new BoundingBoxD(minAABB, maxAABB)) == ContainmentType.Disjoint) { continue; } var cellId = cell.PackId64(); CellData data; if (m_clippedCells.TryGetValue(cellId, out data)) { m_clippedCells.Remove(cellId); } else { var clipmapCellId = MyCellCoord.GetClipmapCellHash(m_clipmap.Id, cellId); data = CellsCache.Read(clipmapCellId); if (data == null) //cache miss { data = new CellData(); ClippingCacheMisses++; } else { //cache hit ClippingCacheHits++; //System.Diagnostics.Debug.Assert((!data.InScene && data.Cell != null) || data.Cell == null, "Not allowed cell state"); data.InScene = false; if (data.Cell != null) { m_nonEmptyCells[cellId] = data; } } } if (data.State == CellState.Invalid) { if (!TryAddCellRequest(collector, parentLod, cell, cellId, data)) { continue; } } m_storedCellData.Add(cellId, data); } } }
public void ProcessChangedCellComponents() { ProfilerShort.Begin("ProcessChangedCellComponents"); m_currentHelper = this; Vector3I min, max, pos; List<int> triangles = null; foreach (var cell in m_changedCells) { min = CellToLowestCube(cell); max = min + m_cellSize - Vector3I.One; // Save a hashset of all the triangles in the current cell pos = min; for (var it = new Vector3I_RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out pos)) { if (!m_triangleRegistry.TryGetValue(pos, out triangles)) continue; foreach (var triIndex in triangles) { m_tmpCellTriangles.Add(triIndex); } } if (m_tmpCellTriangles.Count == 0) continue; MyCellCoord cellCoord = new MyCellCoord(0, cell); ulong packedCell = cellCoord.PackId64(); m_components.OpenCell(packedCell); long timeBegin = m_mesh.GetCurrentTimestamp() + 1; long timeEnd = timeBegin; m_currentComponentRel = 0; m_tmpComponentTriangles.Clear(); foreach (var triIndex in m_tmpCellTriangles) { // Skip already visited triangles var triangle = m_mesh.GetTriangle(triIndex); if (m_currentComponentRel != 0 && m_mesh.VisitedBetween(triangle, timeBegin, timeEnd)) continue; m_components.OpenComponent(); // Make sure we have place in m_currentCellConnections if (m_currentComponentRel >= m_currentCellConnections.Count) { m_currentCellConnections.Add(new List<int>()); } // Find connected component from an unvisited triangle and mark its connections m_components.AddComponentTriangle(triangle, triangle.Center); triangle.ComponentIndex = m_currentComponentRel; m_tmpComponentTriangles.Add(triangle); m_mesh.PrepareTraversal(triangle, null, m_processTrianglePredicate); m_mesh.PerformTraversal(); m_tmpComponentTriangles.Add(null); m_components.CloseComponent(); timeEnd = m_mesh.GetCurrentTimestamp(); if (m_currentComponentRel == 0) { timeBegin = timeEnd; } m_currentComponentRel++; } m_tmpCellTriangles.Clear(); MyNavmeshComponents.ClosedCellInfo cellInfo = new MyNavmeshComponents.ClosedCellInfo(); m_components.CloseAndCacheCell(ref cellInfo); // Renumber triangles from the old indices to the newly assigned index from m_components int componentIndex = cellInfo.StartingIndex; foreach (var triangle in m_tmpComponentTriangles) { if (triangle == null) { componentIndex++; continue; } triangle.ComponentIndex = componentIndex; } m_tmpComponentTriangles.Clear(); // Remove old component primitives if (!cellInfo.NewCell && cellInfo.ComponentNum != cellInfo.OldComponentNum) { for (int i = 0; i < cellInfo.OldComponentNum; ++i) { m_mesh.HighLevelGroup.RemovePrimitive(cellInfo.OldStartingIndex + i); } } // Add new component primitives if (cellInfo.NewCell || cellInfo.ComponentNum != cellInfo.OldComponentNum) { for (int i = 0; i < cellInfo.ComponentNum; ++i) { m_mesh.HighLevelGroup.AddPrimitive(cellInfo.StartingIndex + i, m_components.GetComponentCenter(i)); } } // Update existing component primitives if (!cellInfo.NewCell && cellInfo.ComponentNum == cellInfo.OldComponentNum) { for (int i = 0; i < cellInfo.ComponentNum; ++i) { var primitive = m_mesh.HighLevelGroup.GetPrimitive(cellInfo.StartingIndex + i); primitive.UpdatePosition(m_components.GetComponentCenter(i)); } } // Connect new components with the others in the neighboring cells for (int i = 0; i < cellInfo.ComponentNum; ++i) { int compIndex = cellInfo.StartingIndex + i; var primitive = m_mesh.HighLevelGroup.GetPrimitive(compIndex); primitive.GetNeighbours(m_tmpNeighbors); // Connect to disconnected components foreach (var connection in m_currentCellConnections[i]) { if (!m_tmpNeighbors.Remove(connection)) { m_mesh.HighLevelGroup.ConnectPrimitives(compIndex, connection); } } // Disconnect neighbors that should be no longer connected foreach (var neighbor in m_tmpNeighbors) { // Only disconnect from the other cell if it is expanded and there was no connection found var neighborPrimitive = m_mesh.HighLevelGroup.TryGetPrimitive(neighbor); if (neighborPrimitive != null && neighborPrimitive.IsExpanded) { m_mesh.HighLevelGroup.DisconnectPrimitives(compIndex, neighbor); } } m_tmpNeighbors.Clear(); m_currentCellConnections[i].Clear(); } // Set all the components as expanded for (int i = 0; i < cellInfo.ComponentNum; ++i) { componentIndex = cellInfo.StartingIndex + i; var component = m_mesh.HighLevelGroup.GetPrimitive(componentIndex); if (component != null) { component.IsExpanded = true; } } } m_changedCells.Clear(); m_currentHelper = null; ProfilerShort.End(); }
public void ProcessCellComponents() { ProfilerShort.Begin("ProcessCellComponents"); m_triangleLists.Add(m_packedCoord, m_triangleList.GetCopy()); long timeBegin = m_mesh.GetCurrentTimestamp() + 1; long timeEnd = timeBegin; m_currentComponentRel = 0; m_currentComponent = m_navmeshComponents.OpenCell(m_packedCoord); foreach (var triIndex in m_triangleList) { // Skip already visited triangles var triangle = m_mesh.GetTriangle(triIndex); if (m_mesh.VisitedBetween(triangle, timeBegin, timeEnd)) { continue; } m_navmeshComponents.OpenComponent(); // Make sure we have place in m_currentCellConnections if (m_currentComponentRel >= m_currentCellConnections.Count) { m_currentCellConnections.Add(new List<ConnectionInfo>()); } // Find connected component from an unvisited triangle ProfilerShort.Begin("Graph traversal"); m_currentHelper = this; m_navmeshComponents.AddComponentTriangle(triangle, triangle.Center); triangle.ComponentIndex = m_navmeshComponents.OpenComponentIndex; m_mesh.PrepareTraversal(triangle, null, m_processTrianglePredicate); var primitiveEnum = m_mesh.GetEnumerator(); while (primitiveEnum.MoveNext()); primitiveEnum.Dispose(); ProfilerShort.End(); m_navmeshComponents.CloseComponent(); timeEnd = m_mesh.GetCurrentTimestamp(); m_currentComponentRel++; } MyNavmeshComponents.ClosedCellInfo cellInfo = new MyNavmeshComponents.ClosedCellInfo(); m_navmeshComponents.CloseAndCacheCell(ref cellInfo); // Add new component primitives if (cellInfo.NewCell) { for (int i = 0; i < cellInfo.ComponentNum; ++i) { m_mesh.HighLevelGroup.AddPrimitive(cellInfo.StartingIndex + i, m_navmeshComponents.GetComponentCenter(i)); } } // Connect new components with the others in the neighboring cells for (int i = 0; i < cellInfo.ComponentNum; ++i) { foreach (var connectionInfo in m_currentCellConnections[i]) { if (!cellInfo.ExploredDirections.HasFlag(Base6Directions.GetDirectionFlag(connectionInfo.Direction))) { m_mesh.HighLevelGroup.ConnectPrimitives(cellInfo.StartingIndex + i, connectionInfo.ComponentIndex); } } m_currentCellConnections[i].Clear(); } // Mark explored directions in the navmesh component helper foreach (var direction in Base6Directions.EnumDirections) { var dirFlag = Base6Directions.GetDirectionFlag(direction); if (cellInfo.ExploredDirections.HasFlag(dirFlag)) { continue; } Vector3I dirVec = Base6Directions.GetIntVector(direction); MyCellCoord otherCoord = new MyCellCoord(); otherCoord.Lod = MyVoxelNavigationMesh.NAVMESH_LOD; otherCoord.CoordInLod = m_currentCell + dirVec; if (otherCoord.CoordInLod.X == -1 || otherCoord.CoordInLod.Y == -1 || otherCoord.CoordInLod.Z == -1) { continue; } ulong otherPackedCoord = otherCoord.PackId64(); if (m_triangleLists.ContainsKey(otherPackedCoord)) { m_navmeshComponents.MarkExplored(otherPackedCoord, Base6Directions.GetFlippedDirection(direction)); cellInfo.ExploredDirections |= Base6Directions.GetDirectionFlag(direction); } } m_navmeshComponents.SetExplored(m_packedCoord, cellInfo.ExploredDirections); // Set all the components as expanded for (int i = 0; i < cellInfo.ComponentNum; ++i) { int componentIndex = cellInfo.StartingIndex + i; var component = m_mesh.HighLevelGroup.GetPrimitive(componentIndex); if (component != null) { component.IsExpanded = true; } } ProfilerShort.End(); }
internal void OnCellRequestCancelled(MyCellCoord cell) { var workId = cell.PackId64(); m_renderWorkTracker.Cancel(workId); }
public void TryClearCell(ulong packedCoord) { if (m_triangleLists.ContainsKey(packedCoord)) { ClearCachedCell(packedCoord); } RemoveExplored(packedCoord); MyNavmeshComponents.CellInfo cellInfo; if (!m_navmeshComponents.TryGetCell(packedCoord, out cellInfo)) { return; } for (int i = 0; i < cellInfo.ComponentNum; ++i) { int componentIndex = cellInfo.StartingIndex + i; m_mesh.HighLevelGroup.RemovePrimitive(componentIndex); } foreach (var direction in Base6Directions.EnumDirections) { Base6Directions.DirectionFlags dirFlag = Base6Directions.GetDirectionFlag(direction); if (cellInfo.ExploredDirections.HasFlag(dirFlag)) { Vector3I dirVec = Base6Directions.GetIntVector(direction); MyCellCoord otherCoord = new MyCellCoord(); otherCoord.SetUnpack(packedCoord); Debug.Assert(otherCoord.Lod == MyVoxelNavigationMesh.NAVMESH_LOD); otherCoord.CoordInLod = otherCoord.CoordInLod + dirVec; MyNavmeshComponents.CellInfo otherCellInfo; if (m_navmeshComponents.TryGetCell(otherCoord.PackId64(), out otherCellInfo)) { Base6Directions.DirectionFlags flippedFlag = Base6Directions.GetDirectionFlag(Base6Directions.GetFlippedDirection(direction)); m_navmeshComponents.SetExplored(otherCoord.PackId64(), otherCellInfo.ExploredDirections & ~flippedFlag); } else { Debug.Assert(false, "Could not get the oposite explored cell!"); } } } m_navmeshComponents.ClearCell(packedCoord, ref cellInfo); }
private bool TryGetCell(MyCellCoord cell, out bool isEmpty, out CellData nonEmptyCell) { MyPrecalcComponent.AssertUpdateThread(); using (m_lock.AcquireSharedUsing()) { if (IsEmpty(ref cell)) { isEmpty = true; nonEmptyCell = null; return true; } UInt64 key = cell.PackId64(); if (m_cellsByCoordinate.TryGetValue(key, out nonEmptyCell)) { isEmpty = false; return true; } isEmpty = default(bool); nonEmptyCell = default(CellData); return false; } }
/// <summary> /// Begins processing a voxel geometry cell /// </summary> public void OpenNewCell(MyCellCoord coord) { Debug.Assert(m_cellOpen == false, "Cannot open a new cell in MyVoxelHighLevelHelper while another one is open!"); m_cellOpen = true; m_currentCell = coord.CoordInLod; m_packedCoord = coord.PackId64(); m_triangleList.Clear(); }
public void SetMesh(MyCellCoord cell, MyIsoMesh mesh) { // Don't store anything but the most detailed lod (used in physics and raycasts). // This cache is mostly supposed to help physics and raycasts, not render. if (cell.Lod != 0) return; MyPrecalcComponent.AssertUpdateThread(); using (m_lock.AcquireExclusiveUsing()) { if (mesh != null) { var key = cell.PackId64(); m_coordinateToMesh[key] = mesh; } else { SetEmpty(ref cell, true); } } }
internal void DoClipping(Vector3D localPosition, float farPlaneDistance, RequestCollector collector) { MyClipmap.ComputeLodViewBounds(m_parent.m_scaleGroup, m_lodIndex, out m_nearDistance, out m_farDistance); m_fitsInFrustum = (farPlaneDistance * 1.25f) > m_nearDistance; if (!m_fitsInFrustum) return; Vector3I min, max; { var minD = localPosition - m_farDistance; var maxD = localPosition + m_farDistance; MyVoxelCoordSystems.LocalPositionToRenderCellCoord(ref minD, out min); MyVoxelCoordSystems.LocalPositionToRenderCellCoord(ref maxD, out max); Vector3I.Max(ref min, ref Vector3I.Zero, out min); Vector3I.Max(ref max, ref Vector3I.Zero, out max); min >>= m_lodIndex; max >>= m_lodIndex; Vector3I.Min(ref min, ref m_lodSizeMinusOne, out min); Vector3I.Min(ref max, ref m_lodSizeMinusOne, out max); } if (m_lastMin == min && m_lastMax == max && !m_parent.m_updateClipping) return; m_lastMin = min; m_lastMax = max; LodLevel parentLod, childLod; GetNearbyLodLevels(out parentLod, out childLod); // Moves cells which are still needed from one collection to another. // All that is left behind is unloaded as no longer needed. // Move everything in range to collection of next stored cells. MyUtils.Swap(ref m_storedCellData, ref m_clippedCells); m_storedCellData.Clear(); MyCellCoord cell = new MyCellCoord(m_lodIndex, ref min); for (var it = new Vector3I.RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out cell.CoordInLod)) { if (!WasAncestorCellLoaded(parentLod, ref cell)) continue; var cellId = cell.PackId64(); CellData data; if (m_clippedCells.TryGetValue(cellId, out data)) m_clippedCells.Remove(cellId); else data = new CellData(); if (data.State == CellState.Invalid) { collector.AddRequest(cellId, data.WasLoaded); data.State = CellState.Pending; } m_storedCellData.Add(cellId, data); } }
internal CellData GetCell(ref MyCellCoord cell) { MyPrecalcComponent.AssertUpdateThread(); bool isEmpty; CellData data; if (TryGetCell(cell, out isEmpty, out data)) { return data; } MyIsoMesh mesh; if (!TryGetMesh(cell, out isEmpty, out mesh)) { ProfilerShort.Begin("Cell precalc"); if (true) { var min = cell.CoordInLod << MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS_BITS; var max = min + MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS; // overlap to neighbor; introduces extra data but it makes logic for raycasts simpler (no need to check neighbor cells) min -= 1; max += 2; mesh = MyPrecalcComponent.IsoMesher.Precalc(m_storage, 0, min, max, false, MyFakes.ENABLE_VOXEL_COMPUTED_OCCLUSION, true); } else { mesh = MyPrecalcComponent.IsoMesher.Precalc(new MyIsoMesherArgs() { Storage = m_storage, GeometryCell = cell, }); } ProfilerShort.End(); } if (mesh != null) { data = new CellData(); data.Init( mesh.PositionOffset, mesh.PositionScale, mesh.Positions.GetInternalArray(), mesh.VerticesCount, mesh.Triangles.GetInternalArray(), mesh.TrianglesCount); } if (cell.Lod == 0) { using (m_lock.AcquireExclusiveUsing()) { if (data != null) { var key = cell.PackId64(); m_cellsByCoordinate[key] = data; } else { SetEmpty(ref cell, true); } } } return data; }
/// <summary> /// Checks ancestor nodes recursively. Typically, this checks at most 9 nodes or so (depending on settings). /// </summary> private static bool WasAncestorCellLoaded(LodLevel parentLod, ref MyCellCoord thisLodCell) { if (parentLod == null || !parentLod.m_fitsInFrustum || !parentLod.Visible) { return true; } Debug.Assert(thisLodCell.Lod == parentLod.m_lodIndex - 1); var parentCell = new MyCellCoord(thisLodCell.Lod + 1, thisLodCell.CoordInLod >> 1); CellData data; if (parentLod.m_storedCellData.TryGetValue(parentCell.PackId64(), out data)) { return data.WasLoaded; } LodLevel ancestor; if (parentLod.m_parent.m_lodLevels.TryGetValue(parentLod.m_lodIndex+1, out ancestor)) return WasAncestorCellLoaded(ancestor, ref parentCell); else return false; }
public void InvalidateRange(Vector3I minVoxelChanged, Vector3I maxVoxelChanged) { minVoxelChanged -= MyPrecalcComponent.InvalidatedRangeInflate; maxVoxelChanged += MyPrecalcComponent.InvalidatedRangeInflate; m_voxelMap.Storage.ClampVoxelCoord(ref minVoxelChanged); m_voxelMap.Storage.ClampVoxelCoord(ref maxVoxelChanged); Vector3I minCell, maxCell; MyVoxelCoordSystems.VoxelCoordToGeometryCellCoord(ref minVoxelChanged, out minCell); MyVoxelCoordSystems.VoxelCoordToGeometryCellCoord(ref maxVoxelChanged, out maxCell); Vector3I currentCell = minCell; for (var it = new Vector3I.RangeIterator(ref minCell, ref maxCell); it.IsValid(); it.GetNext(out currentCell)) { if (m_processedCells.Contains(ref currentCell)) { RemoveCell(currentCell); } MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, currentCell); m_higherLevelHelper.TryClearCell(coord.PackId64()); } }
/// <summary> /// Checks only immediate children (any deeper would take too long). /// </summary> private static bool ChildrenWereLoaded(LodLevel childLod, ref MyCellCoord thisLodCell) { if (childLod == null || !childLod.Visible) return false; Debug.Assert(thisLodCell.Lod == childLod.m_lodIndex + 1); var childLodCell = new MyCellCoord(); childLodCell.Lod = childLod.m_lodIndex; var start = thisLodCell.CoordInLod << 1; var end = start + 1; Vector3I.Min(ref end, ref childLod.m_lodSizeMinusOne, out end); for (childLodCell.CoordInLod.Z = start.Z; childLodCell.CoordInLod.Z <= end.Z; ++childLodCell.CoordInLod.Z) for (childLodCell.CoordInLod.Y = start.Y; childLodCell.CoordInLod.Y <= end.Y; ++childLodCell.CoordInLod.Y) for (childLodCell.CoordInLod.X = start.X; childLodCell.CoordInLod.X <= end.X; ++childLodCell.CoordInLod.X) { var key = childLodCell.PackId64(); CellData data; if (!childLod.m_storedCellData.TryGetValue(key, out data)) { return false; } if (!data.WasLoaded) { return false; } } return true; }
private bool RemoveCell(Vector3I cell) { if (!MyFakes.REMOVE_VOXEL_NAVMESH_CELLS) return true; Debug.Assert(m_processedCells.Contains(cell), "Removing a non-existent cell from the navmesh!"); if (!m_processedCells.Contains(cell)) return false; MyTrace.Send(TraceWindow.Ai, "Removing cell " + cell); ProfilerShort.Begin("Removing navmesh links"); MyVoxelPathfinding.CellId cellId = new MyVoxelPathfinding.CellId() { VoxelMap = m_voxelMap, Pos = cell }; m_navmeshCoordinator.RemoveVoxelNavmeshLinks(cellId); ProfilerShort.End(); ProfilerShort.Begin("Removing triangles"); MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, cell); ulong packedCoord = coord.PackId64(); MyIntervalList triangleList = m_higherLevelHelper.TryGetTriangleList(packedCoord); if (triangleList != null) { foreach (var triangleIndex in triangleList) { RemoveTerrainTriangle(GetTriangle(triangleIndex)); } m_higherLevelHelper.ClearCachedCell(packedCoord); } ProfilerShort.End(); Debug.Assert(m_processedCells.Contains(ref cell)); m_processedCells.Remove(ref cell); return triangleList != null; }
protected override void ResetInternal(MyStorageDataTypeFlags dataToReset) { bool resetContent = (dataToReset & MyStorageDataTypeFlags.Content) != 0; bool resetMaterials = (dataToReset & MyStorageDataTypeFlags.Material) != 0; if (resetContent) { m_contentLeaves.Clear(); m_contentNodes.Clear(); } if (resetMaterials) { m_materialLeaves.Clear(); m_materialNodes.Clear(); } if (m_dataProvider != null) { var cellCoord = new MyCellCoord(m_treeHeight, ref Vector3I.Zero); var leafId = cellCoord.PackId64(); cellCoord.Lod += LeafLodCount; var end = Size - 1; if (resetContent) { m_contentLeaves.Add(leafId, new MyProviderLeaf(m_dataProvider, MyStorageDataTypeEnum.Content, ref cellCoord)); } if (resetMaterials) { m_materialLeaves.Add(leafId, new MyProviderLeaf(m_dataProvider, MyStorageDataTypeEnum.Material, ref cellCoord)); } } else { var nodeId = new MyCellCoord(m_treeHeight - 1, ref Vector3I.Zero).PackId64(); if (resetContent) { m_contentNodes.Add(nodeId, new MyOctreeNode()); } if (resetMaterials) { m_materialNodes.Add(nodeId, new MyOctreeNode()); } } }
private MyHighLevelPrimitive GetClosestHighLevelPrimitive(ref Vector3 point, ref float closestDistanceSq) { MyHighLevelPrimitive retval = null; // Convert from world matrix local coords to LeftBottomCorner-based coords Vector3 lbcPoint = point + (m_voxelMap.PositionComp.GetPosition() - m_voxelMap.PositionLeftBottomCorner); m_tmpIntList.Clear(); // Collect components from the eight closest cells Vector3I closestCellCorner = Vector3I.Round(lbcPoint / m_cellSize); for (int i = 0; i < 8; ++i) { Vector3I cell = closestCellCorner + m_cornerOffsets[i]; MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, cell); ulong packedCoord = coord.PackId64(); m_higherLevelHelper.CollectComponents(packedCoord, m_tmpIntList); } foreach (int componentIndex in m_tmpIntList) { var hlPrimitive = m_higherLevel.GetPrimitive(componentIndex); Debug.Assert(hlPrimitive != null, "Couldnt' find a high-level primitive for the index given by higher level helper!"); if (hlPrimitive == null) continue; float distSq = Vector3.DistanceSquared(hlPrimitive.Position, point); if (distSq < closestDistanceSq) { closestDistanceSq = distSq; retval = hlPrimitive; } } m_tmpIntList.Clear(); return retval; }
private static ulong CountChangedVoxelsAmount( MyOctreeStorage baseStorage, int lodIdx, Dictionary<UInt64, MyOctreeNode> nodes, Dictionary<UInt64, IMyOctreeLeafNode> leaves, Vector3I lodCoord) { var currentCell = new MyCellCoord(lodIdx, lodCoord); var leafKey = currentCell.PackId64(); IMyOctreeLeafNode leaf; if (leaves.TryGetValue(leafKey, out leaf)) { if (!leaf.ReadOnly && currentCell.Lod == 0) { // Read data from leaf var rangeEnd = new Vector3I(LeafSizeInVoxels - 1); m_temporaryCache.Resize(Vector3I.Zero, rangeEnd); leaf.ReadRange(m_temporaryCache, ref Vector3I.Zero, 0, ref Vector3I.Zero, ref rangeEnd); // Read data from base storage var minLeafVoxel = currentCell.CoordInLod * LeafSizeInVoxels; var maxLeafVoxel = minLeafVoxel + (LeafSizeInVoxels - 1); m_temporaryCache2.Resize(minLeafVoxel, maxLeafVoxel); baseStorage.ReadRange(m_temporaryCache2, MyStorageDataTypeFlags.Content, currentCell.Lod, ref minLeafVoxel, ref maxLeafVoxel); byte[] origData = m_temporaryCache2.Data; byte[] currData = m_temporaryCache.Data; Debug.Assert(currData.Length == origData.Length); if (currData.Length != origData.Length) return 0; ulong countChangedVoxels = 0; for (int i = (int)MyStorageDataTypeEnum.Content; i < m_temporaryCache.SizeLinear; i += m_temporaryCache.StepLinear) { countChangedVoxels += (ulong)Math.Abs(currData[i] - origData[i]); } return countChangedVoxels; } } else { currentCell.Lod -= 1; var nodeKey = currentCell.PackId64(); var node = nodes[nodeKey]; var childBase = lodCoord << 1; Vector3I childOffset; if (node.HasChildren) { ulong count = 0; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; i++) { if (node.HasChild(i)) { ComputeChildCoord(i, out childOffset); currentCell.CoordInLod = childBase + childOffset; count += CountChangedVoxelsAmount(baseStorage, currentCell.Lod, nodes, leaves, currentCell.CoordInLod); } } return count; } else { return (ulong)((MyOctreeNode.CHILD_COUNT << (currentCell.Lod * 3)) * LeafSizeInVoxels * LeafSizeInVoxels * LeafSizeInVoxels * MyVoxelConstants.VOXEL_CONTENT_FULL); } } return 0; }
internal void DoClipping(Vector3D localPosition, float farPlaneDistance, RequestCollector collector) { MyClipmap.ComputeLodViewBounds(m_parent.m_scaleGroup, m_lodIndex, out m_nearDistance, out m_farDistance); m_fitsInFrustum = (farPlaneDistance * 1.25f) > m_nearDistance; if (!m_fitsInFrustum) { return; } Vector3I min, max; { var minD = localPosition - m_farDistance; var maxD = localPosition + m_farDistance; MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref minD, out min); MyVoxelCoordSystems.LocalPositionToRenderCellCoord(m_lodIndex, ref maxD, out max); Vector3I.Clamp(ref min, ref Vector3I.Zero, ref m_lodSizeMinusOne, out min); Vector3I.Clamp(ref max, ref Vector3I.Zero, ref m_lodSizeMinusOne, out max); } if (m_lastMin == min && m_lastMax == max && !m_parent.m_updateClipping) { return; } m_lastMin = min; m_lastMax = max; LodLevel parentLod, childLod; GetNearbyLodLevels(out parentLod, out childLod); // Moves cells which are still needed from one collection to another. // All that is left behind is unloaded as no longer needed. // Move everything in range to collection of next stored cells. MyUtils.Swap(ref m_storedCellData, ref m_clippedCells); m_storedCellData.Clear(); MyCellCoord cell = new MyCellCoord(m_lodIndex, ref min); for (var it = new Vector3I.RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out cell.CoordInLod)) { if (!WasAncestorCellLoaded(parentLod, ref cell)) { continue; } var cellId = cell.PackId64(); CellData data; if (m_clippedCells.TryGetValue(cellId, out data)) { m_clippedCells.Remove(cellId); } else { data = new CellData(); } if (data.State == CellState.Invalid) { collector.AddRequest(cellId, data.WasLoaded); data.State = CellState.Pending; } m_storedCellData.Add(cellId, data); } }
private static bool ResetOutsideBorders( IMyStorageDataProvider provider, MyStorageDataTypeEnum dataType, int lodIdx, Dictionary<UInt64, MyOctreeNode> nodes, Dictionary<UInt64, IMyOctreeLeafNode> leaves, Vector3I lodCoord, Vector3I minVoxel, Vector3I maxVoxel, out bool canCollapse, Dictionary<UInt64, IMyOctreeLeafNode> outResetLeaves = null) { canCollapse = false; bool changed = false; var currentCell = new MyCellCoord(lodIdx, lodCoord); var key = currentCell.PackId64(); var leafCell = currentCell; var leafKey = leafCell.PackId64(); IMyOctreeLeafNode leaf; if (leaves.TryGetValue(leafKey, out leaf)) { canCollapse = leaf.ReadOnly; if (leafCell.Lod != 0) { Debug.Assert(leaf.ReadOnly); return false; } else if (!leaf.ReadOnly) { var minCell = minVoxel >> (LeafLodCount + leafCell.Lod); var maxCell = maxVoxel >> (LeafLodCount + leafCell.Lod); if (!leafCell.CoordInLod.IsInsideInclusive(ref minCell, ref maxCell)) { canCollapse = true; leaves.Remove(leafKey); var leafCellCopy = leafCell; leafCellCopy.Lod += LeafLodCount; var leafNew = new MyProviderLeaf(provider, dataType, ref leafCellCopy); leaves.Add(leafKey, leafNew); changed = true; if (outResetLeaves != null) outResetLeaves.Add(leafKey, leafNew); } } } else { currentCell.Lod -= 1; key = currentCell.PackId64(); var nodeCell = currentCell; var nodeKey = currentCell.PackId64(); var node = nodes[nodeKey]; var childBase = lodCoord << 1; Vector3I childOffset; var minInChild = (minVoxel >> (LeafLodCount + currentCell.Lod)) - childBase; var maxInChild = (maxVoxel >> (LeafLodCount + currentCell.Lod)) - childBase; var leafSize = LeafSizeInVoxels << currentCell.Lod; unsafe { canCollapse = true; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; i++) { ComputeChildCoord(i, out childOffset); if (childOffset.IsInsideExclusive(ref minInChild, ref maxInChild)) { canCollapse = false; continue; } currentCell.CoordInLod = childBase + childOffset; if (node.HasChild(i)) { bool localCanCollapse; bool resetChanged = ResetOutsideBorders(provider, dataType, currentCell.Lod, nodes, leaves, currentCell.CoordInLod, minVoxel, maxVoxel, out localCanCollapse, outResetLeaves: outResetLeaves); changed = changed || resetChanged; canCollapse = localCanCollapse && canCollapse; } else { var currentCellCopy = currentCell; currentCellCopy.Lod += LeafLodCount; IMyOctreeLeafNode octreeLeaf = new MyProviderLeaf(provider, dataType, ref currentCellCopy); leaves.Add(currentCell.PackId64(), octreeLeaf); node.SetChild(i, true); node.SetData(i, octreeLeaf.GetFilteredValue()); changed = true; } } nodes[nodeKey] = node; if (canCollapse) { // Remove leaves for (int i = 0; i < MyOctreeNode.CHILD_COUNT; i++) { if (node.HasChild(i)) { ComputeChildCoord(i, out childOffset); currentCell.CoordInLod = childBase + childOffset; var childKey = currentCell.PackId64(); leaves.Remove(childKey); node.SetChild(i, false); } } // Remove node nodes.Remove(nodeKey); // Add leaf var leafCellCopy = leafCell; leafCellCopy.Lod += LeafLodCount; var leafNew = new MyProviderLeaf(provider, dataType, ref leafCellCopy); leaves.Add(leafKey, leafNew); } } } return changed; }
internal MyIsoMesh CreateMesh(IMyStorage storage, MyCellCoord coord) { // mk:NOTE This method must be thread safe. Called from worker threads. coord.CoordInLod += m_cellsOffset >> coord.Lod; if (m_voxelMap is MyVoxelPhysics) { var clipmapId = ((MyVoxelPhysics)m_voxelMap).Parent.Render.RenderObjectIDs[0]; var clipmapCellId = MyCellCoord.GetClipmapCellHash(clipmapId, coord.PackId64()); var isoMesh = MyPrecalcJobRender.IsoMeshCache.Read(clipmapCellId); if (isoMesh != null) { return isoMesh; } } var min = coord.CoordInLod << MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS_BITS; var max = min + MyVoxelConstants.GEOMETRY_CELL_SIZE_IN_VOXELS; // overlap to neighbor; introduces extra data but it makes logic for raycasts and collisions simpler (no need to check neighbor cells) min -= 1; max += 2; return MyPrecalcComponent.IsoMesher.Precalc(storage, coord.Lod, min, max, false, false); }
private static unsafe void ReadRange( MyStorageDataCache target, ref Vector3I targetWriteOffset, MyStorageDataTypeEnum type, int treeHeight, Dictionary<UInt64, MyOctreeNode> nodes, Dictionary<UInt64, IMyOctreeLeafNode> leaves, int lodIndex, ref Vector3I minInLod, ref Vector3I maxInLod) { int stackIdx = 0; int stackSize = MySparseOctree.EstimateStackSize(treeHeight); MyCellCoord* stack = stackalloc MyCellCoord[stackSize]; MyCellCoord data = new MyCellCoord(treeHeight + LeafLodCount, ref Vector3I.Zero); stack[stackIdx++] = data; MyCellCoord cell = new MyCellCoord(); while (stackIdx > 0) { Debug.Assert(stackIdx <= stackSize); data = stack[--stackIdx]; cell.Lod = data.Lod - LeafLodCount; cell.CoordInLod = data.CoordInLod; int lodDiff; IMyOctreeLeafNode leaf; if (leaves.TryGetValue(cell.PackId64(), out leaf)) { lodDiff = data.Lod - lodIndex; var rangeMinInDataLod = minInLod >> lodDiff; var rangeMaxInDataLod = maxInLod >> lodDiff; if (data.CoordInLod.IsInsideInclusive(ref rangeMinInDataLod, ref rangeMaxInDataLod)) { var nodePosInLod = data.CoordInLod << lodDiff; var writeOffset = nodePosInLod - minInLod; Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset); writeOffset += targetWriteOffset; var lodSizeMinusOne = new Vector3I((1 << lodDiff) - 1); var minInLeaf = Vector3I.Clamp(minInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne); var maxInLeaf = Vector3I.Clamp(maxInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne); leaf.ReadRange(target, ref writeOffset, lodIndex, ref minInLeaf, ref maxInLeaf); } continue; } cell.Lod -= 1; lodDiff = data.Lod - 1 - lodIndex; var node = nodes[cell.PackId64()]; var min = minInLod >> lodDiff; var max = maxInLod >> lodDiff; var nodePositionInChild = data.CoordInLod << 1; min -= nodePositionInChild; max -= nodePositionInChild; for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i) { Vector3I childPosRelative; ComputeChildCoord(i, out childPosRelative); if (!childPosRelative.IsInsideInclusive(ref min, ref max)) continue; if (lodIndex < data.Lod && node.HasChild(i)) { Debug.Assert(stackIdx < stackSize); stack[stackIdx++] = new MyCellCoord(data.Lod - 1, nodePositionInChild + childPosRelative); } else { var childMin = nodePositionInChild + childPosRelative; childMin <<= lodDiff; var writeOffset = childMin - minInLod; Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset); writeOffset += targetWriteOffset; var nodeData = node.GetData(i); if (lodDiff == 0) { target.Set(type, ref writeOffset, nodeData); } else { var childMax = childMin + ((1 << lodDiff) - 1); Vector3I.Max(ref childMin, ref minInLod, out childMin); Vector3I.Min(ref childMax, ref maxInLod, out childMax); for (int z = childMin.Z; z <= childMax.Z; ++z) for (int y = childMin.Y; y <= childMax.Y; ++y) for (int x = childMin.X; x <= childMax.X; ++x) { Vector3I write = writeOffset; write.X += x - childMin.X; write.Y += y - childMin.Y; write.Z += z - childMin.Z; target.Set(type, ref write, nodeData); } } } } } }