/// <summary> /// Add a new child to this node at the given /// </summary> /// <param name="octant">the octant position to add the child node to</param> /// <param name="newValue">the value of the new child node</param> /// <returns></returns> OctreeNode <TType> addChild(Octants.Octant octant, TType newValue) { children[octant.Value] = new OctreeNode <TType>( getChildOctantCoordinate(octant), size / 2, newValue, this ); return(children[octant.Value]); }
/// <summary> /// Set this block's vertex 'is solid' flag for the given octant /// </summary> /// <param name="value"></param> /// <param name="octant"></param> /// <param name="toTrue"></param> /// <returns></returns> public static int SetVertexMaskForOctant(this int value, Octants.Octant octant, bool toTrue = true) { int mask = 1 << (octant.Value + 8); if (toTrue) { return(value | mask); } return(value & ~mask); }
/// <summary> /// Get if this block's given vertex is within the isosurface and is 'solid' /// </summary> /// <param name="value"></param> /// <param name="octant"></param> /// <returns>If the vertex's value is solid</returns> public static bool BlockVertexIsSolid(this int value, Octants.Octant octant) { // Get the mask of neighboring block data from the int return(( // trim the neighbor's mask off of the full block data int value ((byte)(value >> 8)) // create a mask (ex: 000100) with the 1 in the spot = to the value of the // direction we want to test for, and & compare them. & (1 << (octant.Value)) // if it is not = 000000 after the and, the bit is set. ) != 0); }
/// <summary> /// Get the position of the 0,0 of the requested child octant, given this octants size and position /// </summary> /// <param name="childOctant"></param> /// <returns></returns> Coordinate getChildOctantCoordinate(Octants.Octant childOctant) { int childOctantSize = size / 2; Coordinate childPosition = position; if (childOctant.IsEastern) { childPosition.x += childOctantSize; } if (childOctant.IsNorthern) { childPosition.z += childOctantSize; } if (childOctant.IsUpper) { childPosition.y += childOctantSize; } return(childPosition); }
/// <summary> /// Set the value of the node given the position. /// </summary> /// <param name="nodePosition">the position of the node to edit, the target</param> /// <param name="newValue">the new value to set at the positon</param> /// <param name="nodeSize">if you want to set an area of the tree at the node level, for edge cases. must be a valid node size for this octeee to work</param> public void setValueAt(Coordinate nodePosition, TType newValue, int nodeSize = 1) { // if we're at the correct size, set this and all below. if (size == nodeSize) { value = newValue; clearChildren(); return; } OctreeNode <TType> childNode; // if this node is a solid chunk of the wrong size split it, create the first node below, and recurse the set on it if (isSolid) { // if this is solid, and the position is somewhere within, we can stop if the whole this is already the right value if (value.Equals(newValue)) { return; } childNode = splitAndAddChildNode( getChildOctantFor(nodePosition), value ); // if this node is a broken chunk of the wrong size, grab/add the child node , then recurse it. } else { Octants.Octant childOctant = getChildOctantFor(nodePosition); childNode = getChild(childOctant); if (childNode == null) { childNode = addChild(childOctant, value); } } childNode.setValueAt(nodePosition, newValue, nodeSize); // cleanup after recursion, when we come up a level, check to see if this node's children are all the same, and solidify it if they are. tryToSolidify(); }
/// <summary> /// Get the uncle octant to the given direction of the current octant /// (uncle being an octant bordeing the parent sharing a parent.) /// </summary> /// <param name="direction"></param> /// <param name="uncleValue">Get the value of the uncle, can be used in case no node is returned.</param> /// <returns></returns> OctreeNode <TType> getUncleToThe(Directions.Direction direction, out TType uncleValue) { // we want to make sure we aren't too far up for uncles if (!isRoot && !parent.isRoot) { Octants.Octant currentParentOctant = parent.parent.getChildOctantFor(parent.position); Octants.Octant uncleOctant = currentParentOctant.toThe(direction); // if we have an uncle at this level, return the node and or value from the grandparent if (uncleOctant != null) { OctreeNode <TType> uncleNode = parent.parent.getChild((Octants.Octant)uncleOctant); uncleValue = uncleNode == null ? parent.parent.value : uncleNode.value; return(uncleNode); // if we don't, we want to see if this node's parent has the uncle we need. } else { return(parent.getUncleToThe(direction, out uncleValue)); } } // there's no uncles, only brothers and parents. uncleValue = default; return(null); }
/// <summary> /// Check if the neigboring node(s) in the given direction is(are) solid against this node. /// </summary> /// <param name="direction"></param> /// <returns></returns> TType[] getValuesToThe(Directions.Direction direction) { if (isRoot) { return(new TType[1]); } TType defaultValue; Octants.Octant currentOctant = parent.getChildOctantFor(position); Octants.Octant brotherOctant = currentOctant.toThe(direction); // if the octant shares a parent, move downwards into the found brother OctreeNode <TType> neighborNode; if (brotherOctant != null) { neighborNode = parent.getChild(brotherOctant); defaultValue = parent.value; // else we have to get the cousin } else { neighborNode = getCousinToThe(direction, out defaultValue); } // if we found a node at all, collect the values of that node. if (neighborNode != null) { return(collectBorderValues(neighborNode, direction.Reverse)); // if there is no neighboring node, return the value we generated for the position instead } else { return(new TType[1] { defaultValue }); } }
/// <summary> /// Split this node up and set just one octant to a new value /// </summary> /// <param name="octant"></param> /// <param name="newValue"></param> OctreeNode <TType> splitAndAddChildNode(Octants.Octant updatedOctant, TType newValue) { children = new OctreeNode <TType> [8]; return(addChild(updatedOctant, newValue)); }
/// <summary> /// Get the child by octant /// </summary> /// <param name="octant"></param> /// <returns></returns> OctreeNode <TType> getChild(Octants.Octant octant) { return(isSolid ? null : children[octant.Value]); }
/// <summary> /// Helper function to update just the vertex mask of a given block /// </summary> /// <param name="location">the x,y,z of the block to set</param> /// <param name="vertex">the vertex to toggle</param> /// <param name="setSolid">whether to set the vertex flag to solid or empty</param> protected void updateBlockVertexMask(Coordinate location, Octants.Octant vertex, bool setSolid = true) { setBlock(location, getBlock(location).SetVertexMaskForOctant(vertex, setSolid)); }