public SEntry(ref BoundingBox query, uint res, Vector2I cell, ContainmentType result, uint level) { Box2I box = new Box2I(ref query, res); cell *= HeightmapNode.HEIGHTMAP_BRANCH_FACTOR; Box2I cellb = new Box2I(cell, cell + HeightmapNode.HEIGHTMAP_BRANCH_FACTOR - 1); box.Intersect(ref cellb); Bounds = box; Next = box.Min; Level = level; Result = result; Intersection = result; Continue = false; }
public void GetBounds(ref BoundingBox query) { float maxSize = Math.Max(query.Width, query.Height); if (maxSize < m_pixelSizeFour || PruningTree == null || PruningTree.Length == 0) { // compute an inflated box so we account for smoothing :) Box2I bb = new Box2I(ref query, (uint)Resolution); bb.Min -= 1; bb.Max += 1; bb.Intersect(ref m_bounds); ushort height; GetValue(bb.Min.X, bb.Min.Y, out height); int mmin = ushort.MaxValue, mmax = 0; for (int y = bb.Min.Y; y <= bb.Max.Y; ++y) { for (int x = bb.Min.X; x <= bb.Max.X; ++x) { GetValue(x, y, out height); if (height > mmax) { mmax = height; } if (height < mmin) { mmin = height; } } } int diff = mmax - mmin; diff = (diff * 2) / 3; mmax += diff; mmin -= diff; query.Min.Z = mmin * MyCubemapHelpers.USHORT_RECIP; query.Max.Z = mmax * MyCubemapHelpers.USHORT_RECIP; return; } // Switch to floor to grab some closer precision double log = Math.Log(Resolution / (maxSize * HeightmapNode.HEIGHTMAP_LEAF_SIZE)) / Math.Log(HeightmapNode.HEIGHTMAP_BRANCH_FACTOR); uint level = (uint)PruningTree.Length - 1 - (uint)MathHelper.Clamp(log, 0, PruningTree.Length - 1); Box2I rootBounds = new Box2I(Vector2I.Zero, new Vector2I((int)PruningTree[level].Res - 1)); Box2I queryBounds = new Box2I(ref query, PruningTree[level].Res); queryBounds.Intersect(ref rootBounds); query.Min.Z = float.PositiveInfinity; query.Max.Z = float.NegativeInfinity; int lres = (int)PruningTree[level].Res; for (int y = queryBounds.Min.Y; y <= queryBounds.Max.Y; ++y) { for (int x = queryBounds.Min.X; x <= queryBounds.Max.X; ++x) { var cell = PruningTree[level].Nodes[y * lres + x]; if (query.Min.Z > cell.Min) { query.Min.Z = cell.Min; } if (query.Max.Z < cell.Max) { query.Max.Z = cell.Max; } } } }
/** * Scan pruning tree for intersection with a bounding box. * * This method is a iterative implementation of the following algorithm: * * procedure scan(level, box, result) * for each $cell in $level * $inter = intersects($cell, $box) * * if $inter = INTERSECTS and $cell is not contained and $level != 0 * $inter = scan($level -1, box, $result) * * switch on $inter * case INTERSECTS * return INTERSECTS * * case CONTAINED * if $result == DISJOINT * return INTERSECTS * $result = CONTAINED * * case DISJOINT * if $result == CONTAINED * return INTERSECTS * $result = DISJOINT * return $result */ public ContainmentType QueryHeight(ref BoundingBox query) { // Fallback in case the user loads a bad heightmap. if (PruningTree == null) { return(ContainmentType.Intersects); } if (m_queryStack == null || m_queryStack.Length < PruningTree.Length) { m_queryStack = new SEntry[PruningTree.Length]; } if (query.Min.Z > Root.Max) { return(ContainmentType.Disjoint); } if (query.Max.Z < Root.Min) { return(ContainmentType.Contains); } if (query.Max.X < 0 || query.Max.Y < 0 || query.Min.X > 1 || query.Min.Y > 1) { return(ContainmentType.Disjoint); } // Handle minimum size heightmaps if (PruningTree.Length == 0) { return(ContainmentType.Intersects); } if (query.Max.X == 1.0) { query.Max.X = .99999999f; } if (query.Max.Y == 1.0) { query.Max.Y = .99999999f; } // state variables; ContainmentType result = ContainmentType.Intersects; float maxSize = Math.Max(query.Width, query.Height); // If the box is really small we can be even more precise by checking the heightmap directly. if (maxSize < m_pixelSizeFour) { // compute an inflated box so we account for smoothing :) Box2I bb = new Box2I(ref query, (uint)Resolution); bb.Min -= 1; bb.Max += 1; bb.Intersect(ref m_bounds); int min = (int)(query.Min.Z * ushort.MaxValue); int max = (int)(query.Max.Z * ushort.MaxValue); ushort height; GetValue(bb.Min.X, bb.Min.Y, out height); if (height > max) { result = ContainmentType.Contains; } else if (height < min) { result = ContainmentType.Disjoint; } else { return(ContainmentType.Intersects); } int mmin = ushort.MaxValue, mmax = 0; for (int y = bb.Min.Y; y <= bb.Max.Y; ++y) { for (int x = bb.Min.X; x <= bb.Max.X; ++x) { GetValue(x, y, out height); if (height > mmax) { mmax = height; } if (height < mmin) { mmin = height; } } } int diff = mmax - mmin; diff += diff >> 1; mmax += diff; mmin -= diff; if (min > mmax) { return(ContainmentType.Disjoint); } if (max < mmin) { return(ContainmentType.Contains); } return(ContainmentType.Intersects); } double log = Math.Log(maxSize * (Resolution / HeightmapNode.HEIGHTMAP_LEAF_SIZE)) / Math.Log(HeightmapNode.HEIGHTMAP_BRANCH_FACTOR); uint level = (uint)MathHelper.Clamp(log, 0, PruningTree.Length - 1); // stack index int ss = 0; var st = m_queryStack; Box2I rootBounds = new Box2I(Vector2I.Zero, new Vector2I((int)PruningTree[level].Res - 1)); st[0].Bounds = new Box2I(ref query, PruningTree[level].Res); st[0].Bounds.Intersect(ref rootBounds); st[0].Next = st[0].Bounds.Min; st[0].Level = level; st[0].Result = result; st[0].Continue = false; scan: while (true) { SEntry state; if (ss == -1) { break; } else { state = st[ss]; } for (int y = state.Next.Y; y <= state.Bounds.Max.Y; ++y) { for (int x = state.Bounds.Min.X; x <= state.Bounds.Max.X; ++x) { if (!state.Continue) { state.Intersection = PruningTree[state.Level].Intersect(x, y, ref query); if (state.Intersection == ContainmentType.Intersects && PruningTree[state.Level].IsCellNotContained(x, y, ref query) && state.Level != 0) { state.Next = new Vector2I(x, y); state.Continue = true; st[ss] = state; ss++; st[ss] = new SEntry(ref query, PruningTree[state.Level - 1].Res, new Vector2I(x, y), state.Result, state.Level - 1); goto scan; } } else { state.Continue = false; x = state.Next.X; } switch (state.Intersection) { case ContainmentType.Intersects: state.Result = ContainmentType.Intersects; goto ret; break; case ContainmentType.Disjoint: if (state.Result == ContainmentType.Contains) { state.Result = ContainmentType.Intersects; goto ret; } state.Result = ContainmentType.Disjoint; break; case ContainmentType.Contains: if (state.Result == ContainmentType.Disjoint) { state.Result = ContainmentType.Intersects; goto ret; } state.Result = ContainmentType.Contains; break; } } } ret :; result = state.Result; ss--; if (ss >= 0) { st[ss].Intersection = result; } } return(result); }
public unsafe ContainmentType QueryHeight(ref BoundingBox query) { ContainmentType intersects; int num3; SEntry[] queryStack; SEntry entry; int y; int x; if (this.PruningTree == null) { return(ContainmentType.Intersects); } if ((m_queryStack == null) || (m_queryStack.Length < this.PruningTree.Length)) { m_queryStack = new SEntry[this.PruningTree.Length]; } if (query.Min.Z > this.Root.Max) { return(ContainmentType.Disjoint); } if (query.Max.Z < this.Root.Min) { return(ContainmentType.Contains); } if (query.Max.X < 0f) { goto TR_0003; } else if (query.Max.Y < 0f) { goto TR_0003; } else if ((query.Min.X <= 1f) && (query.Min.Y <= 1f)) { if (this.PruningTree.Length == 0) { return(ContainmentType.Intersects); } if (query.Max.X == 1.0) { query.Max.X = 1f; } if (query.Max.Y == 1.0) { query.Max.Y = 1f; } intersects = ContainmentType.Intersects; float num = Math.Max(query.Width, query.Height); if (num < this.m_pixelSizeFour) { ushort num6; Box2I boxi2 = new Box2I(ref query, (uint)this.Resolution); Vector2I *vectoriPtr1 = (Vector2I *)ref boxi2.Min; vectoriPtr1[0] -= 1; Vector2I *vectoriPtr2 = (Vector2I *)ref boxi2.Max; vectoriPtr2[0] += 1; boxi2.Intersect(ref this.m_bounds); int num4 = (int)(query.Min.Z * 65535f); int num5 = (int)(query.Max.Z * 65535f); this.GetValue(boxi2.Min.X, boxi2.Min.Y, out num6); if (num6 > num5) { intersects = ContainmentType.Contains; } else { if (num6 >= num4) { return(ContainmentType.Intersects); } intersects = ContainmentType.Disjoint; } int num7 = 0xffff; int num8 = 0; int y = boxi2.Min.Y; while (y <= boxi2.Max.Y) { int x = boxi2.Min.X; while (true) { if (x > boxi2.Max.X) { y++; break; } this.GetValue(x, y, out num6); if (num6 > num8) { num8 = num6; } if (num6 < num7) { num7 = num6; } x++; } } int num9 = num8 - num7; num9 += num9 >> 1; num7 -= num9; return((num4 <= (num8 + num9)) ? ((num5 >= num7) ? ContainmentType.Intersects : ContainmentType.Contains) : ContainmentType.Disjoint); } uint index = (uint)MathHelper.Clamp(Math.Log((double)(num * (this.Resolution / HeightmapNode.HEIGHTMAP_LEAF_SIZE))) / Math.Log((double)HeightmapNode.HEIGHTMAP_BRANCH_FACTOR), 0.0, (double)(this.PruningTree.Length - 1)); num3 = 0; queryStack = m_queryStack; Box2I other = new Box2I(Vector2I.Zero, new Vector2I(((int)this.PruningTree[index].Res) - 1)); queryStack[0].Bounds = new Box2I(ref query, this.PruningTree[index].Res); queryStack[0].Bounds.Intersect(ref other); queryStack[0].Next = queryStack[0].Bounds.Min; queryStack[0].Level = index; queryStack[0].Result = intersects; queryStack[0].Continue = false; } else { goto TR_0003; } goto TR_0035; TR_0003: return(ContainmentType.Disjoint); TR_001A: intersects = entry.Result; num3--; if (num3 >= 0) { queryStack[num3].Intersection = intersects; } goto TR_0035; TR_001E: x++; goto TR_002E; TR_0026: switch (entry.Intersection) { case ContainmentType.Disjoint: if (entry.Result != ContainmentType.Contains) { entry.Result = ContainmentType.Disjoint; goto TR_001E; } else { entry.Result = ContainmentType.Intersects; } break; case ContainmentType.Contains: if (entry.Result != ContainmentType.Disjoint) { entry.Result = ContainmentType.Contains; goto TR_001E; } else { entry.Result = ContainmentType.Intersects; } break; case ContainmentType.Intersects: entry.Result = ContainmentType.Intersects; break; default: goto TR_001E; } goto TR_001A; TR_002E: while (true) { if (x <= entry.Bounds.Max.X) { if (entry.Continue) { entry.Continue = false; x = entry.Next.X; goto TR_0026; } else { SEntry *entryPtr1 = (SEntry *)ref entry; entryPtr1->Intersection = this.PruningTree[entry.Level].Intersect(x, y, ref query); if (entry.Intersection != ContainmentType.Intersects) { goto TR_0026; } else { if (this.PruningTree[entry.Level].IsCellNotContained(x, y, ref query) && (entry.Level != 0)) { entry.Next = new Vector2I(x, y); entry.Continue = true; queryStack[num3] = entry; num3++; queryStack[num3] = new SEntry(ref query, this.PruningTree[((int)entry.Level) - 1].Res, new Vector2I(x, y), entry.Result, entry.Level - 1); break; } goto TR_0026; } } } else { y++; goto TR_0031; } break; } goto TR_0035; TR_0031: while (true) { if (y <= entry.Bounds.Max.Y) { x = entry.Bounds.Min.X; } else { goto TR_001A; } break; } goto TR_002E; TR_0035: while (true) { if (num3 == -1) { return(intersects); } entry = queryStack[num3]; y = entry.Next.Y; break; } goto TR_0031; }
public unsafe void GetBounds(ref BoundingBox query) { float num = Math.Max(query.Width, query.Height); if (((num >= this.m_pixelSizeFour) && (this.PruningTree != null)) && (this.PruningTree.Length != 0)) { double num2 = Math.Log((double)(((float)this.Resolution) / (num * HeightmapNode.HEIGHTMAP_LEAF_SIZE))) / Math.Log((double)HeightmapNode.HEIGHTMAP_BRANCH_FACTOR); uint index = ((uint)(this.PruningTree.Length - 1)) - ((uint)MathHelper.Clamp(num2, 0.0, (double)(this.PruningTree.Length - 1))); Box2I other = new Box2I(Vector2I.Zero, new Vector2I(((int)this.PruningTree[index].Res) - 1)); Box2I boxi2 = new Box2I(ref query, this.PruningTree[index].Res); boxi2.Intersect(ref other); query.Min.Z = float.PositiveInfinity; query.Max.Z = float.NegativeInfinity; int res = (int)this.PruningTree[index].Res; int y = boxi2.Min.Y; while (y <= boxi2.Max.Y) { int x = boxi2.Min.X; while (true) { if (x > boxi2.Max.X) { y++; break; } HeightmapNode node = this.PruningTree[index].Nodes[(y * res) + x]; if (query.Min.Z > node.Min) { query.Min.Z = node.Min; } if (query.Max.Z < node.Max) { query.Max.Z = node.Max; } x++; } } } else { ushort num5; Box2I boxi3 = new Box2I(ref query, (uint)this.Resolution); Vector2I *vectoriPtr1 = (Vector2I *)ref boxi3.Min; vectoriPtr1[0] -= 1; Vector2I *vectoriPtr2 = (Vector2I *)ref boxi3.Max; vectoriPtr2[0] += 1; boxi3.Intersect(ref this.m_bounds); this.GetValue(boxi3.Min.X, boxi3.Min.Y, out num5); int num6 = 0xffff; int num7 = 0; int y = boxi3.Min.Y; while (y <= boxi3.Max.Y) { int x = boxi3.Min.X; while (true) { if (x > boxi3.Max.X) { y++; break; } this.GetValue(x, y, out num5); if (num5 > num7) { num7 = num5; } if (num5 < num6) { num6 = num5; } x++; } } int num8 = ((num7 - num6) * 2) / 3; num7 += num8; num6 -= num8; query.Min.Z = num6 * 1.525902E-05f; query.Max.Z = num7 * 1.525902E-05f; } }