public static Block45OctNode GetNodeRW(IntVector4 poslod, ref Block45OctNode root)          // Write new if null
    {
        // Note: if req's w > root.w, root would be returned
        root = root.RerootToContainPos(poslod);
        int            ind         = 0;
        int            nodeCenterX = 0;
        int            nodeCenterY = 0;
        int            nodeCenterZ = 0;
        Block45OctNode cur         = root;

        while (cur._pos.w > poslod.w)
        {
            if (cur.IsLeaf)
            {
                cur.Split();
            }

            int centerOfs = Block45Constants.CenterOfs(cur._pos.w);
            nodeCenterX = cur._pos.x + centerOfs;
            nodeCenterY = cur._pos.y + centerOfs;
            nodeCenterZ = cur._pos.z + centerOfs;
            ind         = ((poslod.x >= nodeCenterX) ? 1 : 0) |
                          ((poslod.y >= nodeCenterY) ? 2 : 0) |
                          ((poslod.z >= nodeCenterZ) ? 4 : 0);
            cur = cur._children[ind];
        }
        return(cur);
    }
    public static void SplitAt(Block45OctNode root, IntVector3 atpos, int lod)
    {
        // calculate the index of the child in which the split will happen.
        int            ind         = 0;
        Block45OctNode cur         = root;
        int            nodeCenterX = 0;
        int            nodeCenterY = 0;
        int            nodeCenterZ = 0;

        for (int i = 0; i < lod; i++)
        {
            int centerOfs = Block45Constants.CenterOfs(cur._pos.w);
            nodeCenterX = cur._pos.x + centerOfs;
            nodeCenterY = cur._pos.y + centerOfs;
            nodeCenterZ = cur._pos.z + centerOfs;
            ind         = ((atpos.x > nodeCenterX) ? 1 : 0) |
                          ((atpos.y > nodeCenterY) ? 2 : 0) |
                          ((atpos.z > nodeCenterZ) ? 4 : 0);
            if (cur.IsLeaf)
            {
                cur.Split();
            }
            cur = cur._children[ind];
        }
    }
    public bool IsOverlapped(IntVector3 boundPos, IntVector3 boundSize)     //Is Node overlapped with bound?
    {
        int size = Block45Constants.Size(_pos.w);

        if (boundPos.x > _pos.x + size || boundPos.x + boundSize.x < _pos.x ||
            boundPos.y > _pos.y + size || boundPos.y + boundSize.y < _pos.y ||
            boundPos.z > _pos.z + size || boundPos.z + boundSize.z < _pos.z
            )
        {
            return(false);
        }
        return(true);
    }
    public bool IsWholeInside(IntVector3 boundPos, IntVector3 boundSize)     //Is Node whole inside of bound?
    {
        int size = Block45Constants.Size(_pos.w);

        if (boundPos.x <= _pos.x && boundPos.x + boundSize.x >= _pos.x + size &&
            boundPos.y <= _pos.y && boundPos.y + boundSize.y >= _pos.y + size &&
            boundPos.z <= _pos.z && boundPos.z + boundSize.z >= _pos.z + size
            )
        {
            return(true);
        }
        return(false);
    }
    public bool IsCenterInside(IntVector3 boundPos, IntVector3 boundSize)     //Is Node center inside of bound?
    {
        int centerOfs = Block45Constants.CenterOfs(_pos.w);

        if (boundPos.x <= _pos.x + centerOfs && boundPos.x + boundSize.x >= _pos.x + centerOfs &&
            boundPos.y <= _pos.y + centerOfs && boundPos.y + boundSize.y >= _pos.y + centerOfs &&
            boundPos.z <= _pos.z + centerOfs && boundPos.z + boundSize.z >= _pos.z + centerOfs
            )
        {
            return(true);
        }
        return(false);
    }
    // use this to determine if a pos is inside the range of the node
    public bool Covers(IntVector4 pos)
    {
        int size = Block45Constants.Size(_pos.w);

        if (_pos.x <= pos.x && _pos.x + size > pos.x &&
            _pos.y <= pos.y && _pos.y + size > pos.y &&
            _pos.z <= pos.z && _pos.z + size > pos.z
            )
        {
            return(true);
        }
        return(false);
    }
    public B45Block Read(int x, int y, int z, int lod = 0)
    {
        IntVector4     poslod  = Block45Constants.ToWorldUnitPos(x, y, z, lod);
        Block45OctNode curNode = Block45OctNode.GetNodeRO(poslod, _octRoot);

        if (curNode == null)
        {
            return(new B45Block(0, 0));
        }

        int vx = (x >> lod) & Block45Constants._mask;
        int vy = (y >> lod) & Block45Constants._mask;
        int vz = (z >> lod) & Block45Constants._mask;

        return(curNode.Read(vx, vy, vz));
    }
    // append all children(inclusive) with lod recursively for savedata
    private static int AppendToWrite(Block45OctNode node, BinaryWriter bw, int lod)
    {
        int count = 0;

        if (node._pos.w == lod)
        {
            List <BlockVec> vecData = node.VecData;
            if (vecData != null)
            {
                IntVector4 blockUnitPos = Block45Constants.ToBlockUnitPos(node._pos.x, node._pos.y, node._pos.z, node._pos.w);
                int        n            = vecData.Count;
                for (int i = 0; i < n; i++)
                {
                    BlockVec bv = vecData[i];
                    int      x  = bv.x - Block45Constants._numVoxelsPrefix;
                    int      y  = bv.y - Block45Constants._numVoxelsPrefix;
                    int      z  = bv.z - Block45Constants._numVoxelsPrefix;
                    if (x < 0 || x >= Block45Constants._numVoxelsPerAxis ||
                        y < 0 || y >= Block45Constants._numVoxelsPerAxis ||
                        z < 0 || z >= Block45Constants._numVoxelsPerAxis)
                    {
                        continue;
                    }
                    bw.Write(blockUnitPos.x + x);
                    bw.Write(blockUnitPos.y + y);
                    bw.Write(blockUnitPos.z + z);
                    bw.Write(bv._byte0);
                    bw.Write(bv._byte1);
                    count++;
                }
            }
        }
        else
        if (node._pos.w > lod && node._children != null)
        {
            for (int i = 0; i < 8; i++)
            {
                count += AppendToWrite(node._children[i], bw, lod);
            }
        }
        return(count);
    }
    public static Block45OctNode GetNodeRO(IntVector4 poslod /*w unused*/, Block45OctNode root)         // Read only
    {
        if (root == null || !root.Covers(poslod))
        {
            return(null);
        }
        if (root._pos.w < poslod.w)
        {
            return(null);
        }
        if (root._pos.w == poslod.w)
        {
            return(root);
        }

        int            ind         = 0;
        int            nodeCenterX = 0;
        int            nodeCenterY = 0;
        int            nodeCenterZ = 0;
        Block45OctNode cur         = root;

        while (cur._pos.w > poslod.w)
        {
            if (cur.IsLeaf)
            {
                return(null);
            }

            int centerOfs = Block45Constants.CenterOfs(cur._pos.w);
            nodeCenterX = cur._pos.x + centerOfs;
            nodeCenterY = cur._pos.y + centerOfs;
            nodeCenterZ = cur._pos.z + centerOfs;
            ind         = ((poslod.x >= nodeCenterX) ? 1 : 0) |
                          ((poslod.y >= nodeCenterY) ? 2 : 0) |
                          ((poslod.z >= nodeCenterZ) ? 4 : 0);
            cur = cur._children[ind];
        }
        return(cur);
    }
    public int Write(B45Block voxel, int x, int y, int z, int lod = 0)                                  // logic pos
    {
        int vx = (x >> lod) & Block45Constants._mask;
        int vy = (y >> lod) & Block45Constants._mask;
        int vz = (z >> lod) & Block45Constants._mask;
        //Debug.LogWarning("[BlockWrite]:"+x+","+y+","+z+","+voxel.blockType+","+voxel.materialType);

        IntVector4     poslod;
        Block45OctNode curNode;

        if (_octRoot == null)
        {
            poslod = Block45Constants.ToWorldUnitPos(x & ~Block45Constants._mask,
                                                     y & ~Block45Constants._mask,
                                                     z & ~Block45Constants._mask,
                                                     lod);
            IntVector4 rootPosLod = new IntVector4(poslod);
            curNode = _octRoot = Block45OctNode.CreateNode(rootPosLod, _onCreateNode);
            //Extend root node if root.lod < VoxelTerrainConstans.MaxLOD
            _octRoot = _octRoot.RerootToLOD(LODOctreeMan._maxLod);
        }
        else
        {
            poslod  = Block45Constants.ToWorldUnitPos(x, y, z, lod);
            curNode = Block45OctNode.GetNodeRW(poslod, ref _octRoot);
        }
        curNode.Write(vx, vy, vz, voxel.blockType, voxel.materialType);

        // Write neighbour
        int fx = 0, fy = 0, fz = 0;
        int dirtyMask = 0x80;           // 0,1,2 bit for xyz dirty mask;4,5,6 bit for sign(neg->1);7 bit for current pos(now not used)

        // If write one edge's voxel may cause the other edge being modified
        if (vx < Block45OctNode.S_MinNoDirtyIdx)
        {
            fx = -1; dirtyMask |= 0x11;
        }
        else
        if (vx >= Block45OctNode.S_MaxNoDirtyIdx)
        {
            fx = 1; dirtyMask |= 0x01;
        }
        if (vy < Block45OctNode.S_MinNoDirtyIdx)
        {
            fy = -1; dirtyMask |= 0x22;
        }
        else
        if (vy >= Block45OctNode.S_MaxNoDirtyIdx)
        {
            fy = 1; dirtyMask |= 0x02;
        }
        if (vz < Block45OctNode.S_MinNoDirtyIdx)
        {
            fz = -1; dirtyMask |= 0x44;
        }
        else
        if (vz >= Block45OctNode.S_MaxNoDirtyIdx)
        {
            fz = 1; dirtyMask |= 0x04;
        }

        if (dirtyMask != 0x80)
        {
            int _shift = Block45Constants._shift;
            int cxlod  = (x >> (lod + _shift));
            int cylod  = (y >> (lod + _shift));
            int czlod  = (z >> (lod + _shift));
            int cxround;
            int cyround;
            int czround;
            for (int i = 1; i < 8; i++)
            {
                if ((dirtyMask & i) == i)
                {
                    int dx = fx * Block45OctNode.S_NearNodeOfs[i, 0], dy = fy * Block45OctNode.S_NearNodeOfs[i, 1], dz = fz * Block45OctNode.S_NearNodeOfs[i, 2];
                    cxround = (cxlod + dx);
                    cyround = (cylod + dy);
                    czround = (czlod + dz);

                    poslod.x = cxround << (Block45Constants._scaledShift + lod);
                    poslod.y = cyround << (Block45Constants._scaledShift + lod);
                    poslod.z = czround << (Block45Constants._scaledShift + lod);
                    Block45OctNode nearNode = Block45OctNode.GetNodeRW(poslod, ref _octRoot);
                    nearNode.Write(vx - dx * Block45Constants._numVoxelsPerAxis,
                                   vy - dy * Block45Constants._numVoxelsPerAxis,
                                   vz - dz * Block45Constants._numVoxelsPerAxis,
                                   voxel.blockType, voxel.materialType);
                }
            }
        }
        return(dirtyMask);
    }