//x ~ (0, setting.maxX * power(2, subdivision)), z ~ (0, setting.maxZ * power(2, subdivision)) public MPPathNode GetStandablePathNode(int u, int v, ushort refH) { float centerx = 0; float centerz = 0; int subdivision = setting.subdivision; QuadTreeLeaf leaf = GetLeaf(u, v, ref centerx, ref centerz, ref subdivision); if (leaf == null || leaf.Slices == null) { return(null); } for (uint i = (uint)leaf.Slices.Length - 1; i > 0; --i) { ulong currentSlice = leaf.Slices[i]; byte currentf = SliceAccessor.flag(currentSlice); ushort currentheight = SliceAccessor.heightGrade(currentSlice); ulong lowerSlice = leaf.Slices[i - 1]; ushort lowerf = SliceAccessor.flag(lowerSlice); ushort lowerheight = SliceAccessor.heightGrade(lowerSlice); if ((currentf & SliceAccessor.SliceCeiling) > 0) { continue; } if (refH >= currentheight || ((lowerf & SliceAccessor.SliceCeiling) > 0 && refH >= lowerheight)) {//pillar roof return(MPPathNodePool.Pop(leaf.Header, u, v, i, subdivision, setting.subdivision, centerx, centerz, currentSlice)); } } ulong floorSlice = leaf.Slices[0]; return(MPPathNodePool.Pop(leaf.Header, u, v, 0, subdivision, setting.subdivision, centerx, centerz, floorSlice)); }
private short GetSlope(ulong start, ulong end, float delta, float sliceThickness) { uint startH = SliceAccessor.heightGrade(start); uint endH = SliceAccessor.heightGrade(end); return((short)(SliceAccessor.slopeMagnify * sliceThickness * (endH - startH) / delta)); }
public void GetPathNeighbours(MPPathNode current, int maxX, int maxZ, MPNeigbours neighbours) { ulong[] curSlices = HeightSlicePool.GetSlices(current.SliceHeader); if (curSlices == null) { return; } //get current ceiling, where I can't go across ushort maxH = ushort.MaxValue; if (current.HIdx < curSlices.Length - 1) { ulong higherSlice = curSlices[current.HIdx + 1]; ushort higherf = SliceAccessor.flag(higherSlice); ushort higherheight = SliceAccessor.heightGrade(higherSlice); if ((higherf & SliceAccessor.SliceCeiling) > 0) { maxH = higherheight; } } for (int u = current.BoundaryXMin; u <= current.BoundaryXMax; ++u) { if (u < 0 || u >= maxX) { continue; } if (current.BoundaryZMin >= 0 && current.BoundaryZMin < maxZ) { GetPathNeighbour(current.HeightGrade, maxH, u, current.BoundaryZMin, neighbours); } if (current.BoundaryZMax >= 0 && current.BoundaryZMax < maxZ) { GetPathNeighbour(current.HeightGrade, maxH, u, current.BoundaryZMax, neighbours); } } for (int v = current.BoundaryZMin; v <= current.BoundaryZMax; ++v) { if (v < 0 || v >= maxZ) { continue; } if (current.BoundaryXMin >= 0 && current.BoundaryXMin < maxX) { GetPathNeighbour(current.HeightGrade, maxH, current.BoundaryXMin, v, neighbours); } if (current.BoundaryXMax >= 0 && current.BoundaryXMax < maxX) { GetPathNeighbour(current.HeightGrade, maxH, current.BoundaryXMax, v, neighbours); } } }
//detailed x, z //dx ~ (0, setting.maxX * power(2, subdivision)), dx ~ (0, setting.maxZ * power(2, subdivision)) public void Reset(uint header, int dx, int dz, uint ih, int sub, int maxSub, float centerx, float centerz, ulong val) { SliceHeader = header; HIdx = ih; Subdivision = sub; X = centerx; Z = centerz; HeightGrade = SliceAccessor.heightGrade(val); Flag = SliceAccessor.flag(val); mSlopeU = SliceAccessor.slopeUGrade(val) / SliceAccessor.slopeMagnify; mSlopeV = SliceAccessor.slopeVGrade(val) / SliceAccessor.slopeMagnify; //boundary BoundaryXMin = 0; BoundaryZMin = 0; int detail = 1 << sub; if (sub == 0) { BoundaryXMin = dx; BoundaryZMin = dz; } else { for (int s = maxSub; s >= sub; --s) { detail = 1 << s; int u = dx >> s; // x / power(2, subdivision); int v = dz >> s; BoundaryXMin += u * detail; BoundaryZMin += v * detail; dx -= u * detail; dz -= v * detail; } } BoundaryXMax = BoundaryXMin + detail; BoundaryZMax = BoundaryZMin + detail; BoundaryXMin -= 1; BoundaryZMin -= 1; }
//dynamically add pillars in //x ~ (0, setting.maxX * power(2, subdivision)), x ~ (0, setting.maxZ * power(2, subdivision)) public void AddPillar(int subdivision, int x, int z, OrderedSlices rawSlices) { //first grade int u = x >> subdivision; // x / power(2, subdivision); int v = z >> subdivision; int subx = x - u * (1 << subdivision); int subz = z - v * (1 << subdivision); --subdivision; int idx = (subx >> subdivision) * 2 + (subz >> subdivision); if (subdivision > 0) { if (Children[idx] is QuadTreeLeaf) { SubdividLeaf(idx); } QuadTreeNode node = (QuadTreeNode)Children[idx]; node.AddPillar(subdivision, subx, subz, rawSlices); } else { if (Children[idx] is QuadTreeNode) { MPLog.LogError("AddPillar leaf still a tree : " + subdivision); return; } QuadTreeLeaf leaf = (QuadTreeLeaf)Children[idx]; if (leaf.Slices != null) { HeightSlicePool.Push(leaf.Header, leaf.Slices); } leaf.Reset(rawSlices.Count, rawSlices.HashValue); for (int i = 0; i < rawSlices.Count; ++i) { leaf.Slices[i] = SliceAccessor.packVal(rawSlices[i].heightGrade, 0, 0, rawSlices[i].flag); } } }
public void Unify(float startHeight, float heightPerGrade) { SortSlices(); if (Count == 0) { MPLog.LogError("pillar is empty."); } //merge the slices, slices should be floor|ceiling|floor|ceiling....|floor bool bNeedMerge = true; while (bNeedMerge && Count > 0) { bNeedMerge = false; for (int i = 0; i < Count - 1; ++i) { if (this[i].flag == this[i + 1].flag) { if ((this[i].flag & SliceAccessor.SliceCeiling) > 0) {//ceiling use lower one RemoveAt(i + 1); } else {//floor use higher one RemoveAt(i); } bNeedMerge = true; break; } } } HashValue = 0; for (int i = 0; i < Count; ++i) { RawSlice slice = this[i]; slice.heightGrade = (ushort)Math.Ceiling((slice.height - startHeight) / heightPerGrade); HashValue += SliceAccessor.packVal(slice.heightGrade, 0, 0, slice.flag); } }
private void GetLeafDisplaySlice(ulong[] slices, float startx, float startz, float sizex, float sizez, int x, int z, List <DisplaySlice> lSlices) { float minx = startx + sizex * x; float minz = startz + sizez * z; float maxx = startx + sizex * (x + 1); float maxz = startz + sizez * (z + 1); for (int i = 0; i < slices.Length; ++i) { ulong rawSlice = slices[i]; DisplaySlice slice = new DisplaySlice(); slice.height = setting.heightValRange[0] + SliceAccessor.heightGrade(rawSlice) * setting.heightPerGrade; slice.flag = SliceAccessor.flag(rawSlice); slice.min = new float[2] { minx, minz }; slice.max = new float[2] { maxx, maxz }; lSlices.Add(slice); } }
private void GetPathNeighbour(ushort curH, ushort maxH, int u, int v, MPNeigbours neighbours) { float centerx = 0; float centerz = 0; int subdivision = setting.subdivision; QuadTreeLeaf leaf = GetLeaf(u, v, ref centerx, ref centerz, ref subdivision); if (leaf == null || leaf.Slices == null) { return; } //bigger subdivision may has the same slice structure, add only one if (neighbours.Contains(leaf.Header)) { return; } //each height slice could be a neighbour if (leaf.Slices.Length == 1) { ulong floorSlice = leaf.Slices[0]; ushort height = SliceAccessor.heightGrade(floorSlice); byte flag = SliceAccessor.flag(floorSlice); if (CanMove2Neighbour(setting, curH, maxH, height, ushort.MaxValue)) { neighbours.Add(leaf.Header, u, v, 0, subdivision, setting.subdivision, centerx, centerz, floorSlice); } } else { for (uint i = 0; i < leaf.Slices.Length - 1; ++i) { ulong currentSlice = leaf.Slices[i]; byte currentf = SliceAccessor.flag(currentSlice); ushort currentheight = SliceAccessor.heightGrade(currentSlice); ulong higherSlice = leaf.Slices[i + 1]; byte higherf = SliceAccessor.flag(higherSlice); ushort higherheight = SliceAccessor.heightGrade(higherSlice); if (i == leaf.Slices.Length - 2 && (higherf & SliceAccessor.SliceCeiling) == 0) {//pillar roof if (CanMove2Neighbour(setting, curH, maxH, higherheight, ushort.MaxValue)) { neighbours.Add(leaf.Header, u, v, i + 1, subdivision, setting.subdivision, centerx, centerz, higherSlice); } break; } if ((currentf & SliceAccessor.SliceCeiling) > 0) { continue; } ushort currentMaxH = ushort.MaxValue; if ((higherf & SliceAccessor.SliceCeiling) > 0) {//check standable float holeHeight = (higherheight - currentheight) * setting.heightPerGrade; if (holeHeight < setting.boundHeight) { continue; } currentMaxH = higherheight; } if (CanMove2Neighbour(setting, curH, maxH, currentheight, currentMaxH)) { neighbours.Add(leaf.Header, u, v, i, subdivision, setting.subdivision, centerx, centerz, currentSlice); } } } }
private QuadTreeLeaf Combine(float dx, float dz, float sliceThickness, float slopeErr) { if (Children[0] == null || !(Children[0] is QuadTreeLeaf)) { return(null); } QuadTreeLeaf leaf = (QuadTreeLeaf)Children[0]; for (int i = 1; i < Children.Length; ++i) { if (Children[i] == null || !(Children[i] is QuadTreeLeaf)) { return(null); } if (!leaf.IsCombinable((QuadTreeLeaf)Children[i])) { return(null); } } for (int s = 0; s < leaf.Slices.Length; ++s) { ulong leafS = leaf.Slices[s]; byte flag = SliceAccessor.flag(leafS); //x axis short slopeU = GetSlope(leafS, ((QuadTreeLeaf)Children[1]).Slices[s], dx, sliceThickness); short slopeU1 = GetSlope(((QuadTreeLeaf)Children[2]).Slices[s], ((QuadTreeLeaf)Children[3]).Slices[s], dx, sliceThickness); if ((slopeU != 0 && slopeU1 != 0 && Math.Abs(slopeU1 - slopeU) > slopeErr * SliceAccessor.slopeMagnify)) { return(null); } slopeU += slopeU1; slopeU /= 2; //z axis short slopeV = GetSlope(leafS, ((QuadTreeLeaf)Children[2]).Slices[s], dz, sliceThickness); short slopeV1 = GetSlope(((QuadTreeLeaf)Children[1]).Slices[s], ((QuadTreeLeaf)Children[3]).Slices[s], dz, sliceThickness); if ((slopeV != 0 && slopeV1 != 0 && Math.Abs(slopeV1 - slopeV) > slopeErr * SliceAccessor.slopeMagnify)) { return(null); } slopeV += slopeV1; slopeV /= 2; ushort updateH = 0; for (int i = 0; i < Children.Length; ++i) { QuadTreeLeaf l = (QuadTreeLeaf)Children[i]; ulong lS = l.Slices[s]; if (leaf.HashVal != l.HashVal) { byte f = SliceAccessor.flag(lS); short sU = SliceAccessor.slopeUGrade(lS); short sV = SliceAccessor.slopeVGrade(lS); //floor cant match ceiling if (flag != f) { return(null); } //slope error if ((sU != 0 && Math.Abs(sU - slopeU) > slopeErr * SliceAccessor.slopeMagnify) || (sV != 0 && Math.Abs(sV - slopeV) > slopeErr * SliceAccessor.slopeMagnify)) { return(null); } } updateH += SliceAccessor.heightGrade(lS); } updateH >>= 2;//average height leaf.Slices[s] = SliceAccessor.packVal(updateH, slopeU, slopeV, flag); } leaf.RefreshHash(); return(leaf); }