/// <summary> /// Converts a DynamicOctree into this BFSOctree. /// </summary> /// <param name="node">The DynamicOctree to convert into this BFSOctree.</param> private void convert(DynamicOctreeNode node) { Queue<DynamicOctreeNode> queue = new Queue<DynamicOctreeNode>(); queue.Enqueue(node); node = null; uint offset = 1; int index = 0; while (queue.Count > 0) { DynamicOctreeNode currentNode = queue.Dequeue(); uint mask = 0; if (currentNode.hasChildren()) { DynamicOctreeNode child = currentNode.getFirstChild(); for (byte i = 0; i < currentNode.getChildCount(); ++i) { queue.Enqueue(child); mask |= (byte)(1u << child.position); child = child.getNextNode(); } } if (index < _innerNodes.Length) _innerNodes[index++] = new BFSInnerNode(mask, offset, currentNode.vd); else _leaves[index++ - _innerNodes.Length] = new BFSLeaf(currentNode.vd); offset += currentNode.getChildCount(); } queue.Clear(); }
/// <summary> /// Creates a new empty node with no child nodes. /// </summary> /// <param name="position">The position of this node relative to /// its parent. <seealso cref="DynamicOctree"/></param> public DynamicOctreeNode(byte position) { childCount = 0; this.position = position; firstChild = null; nextNode = null; dist = float.MaxValue; }
/// <summary> /// Helper function used to assign bone weights to voxels. /// Since a voxel is approximated by a triangle, in the worst case, /// 12 bone weights could affect it. This function determines the 4 most /// influential bones for a voxel and assigns them to it. /// </summary> /// <param name="node">The node to assign bone weights to.</param> /// <param name="vertices">Vertex buffer of the triangle mesh.</param> /// <param name="indices">Index buffer of the triangle mesh.</param> /// <param name="index">Index of the first vertex of the triangle.</param> /// <param name="u">Barycentric coordinate of the triangle's first vertex.</param> /// <param name="v">Barycentric coordinate of the triangle's second vertex.</param> /// <param name="w">Barycentric coordinate of the triangle's third vertex.</param> private static void weight(DynamicOctreeNode node, FBXVertexFormat[] vertices, int[] indices, int index, float u, float v, float w) { for (int i = 0; i < 256; ++i) { boneWeights[i] = 0.0f; boneIndices[i] = (byte) i; } boneWeights[vertices[indices[index]].boneIndex0] += vertices[indices[index]].boneWeights.X * u; boneWeights[vertices[indices[index]].boneIndex1] += vertices[indices[index]].boneWeights.Y * u; boneWeights[vertices[indices[index]].boneIndex2] += vertices[indices[index]].boneWeights.Z * u; boneWeights[vertices[indices[index]].boneIndex3] += vertices[indices[index]].boneWeights.W * u; boneWeights[vertices[indices[index + 1]].boneIndex0] += vertices[indices[index + 1]].boneWeights.X * v; boneWeights[vertices[indices[index + 1]].boneIndex1] += vertices[indices[index + 1]].boneWeights.Y * v; boneWeights[vertices[indices[index + 1]].boneIndex2] += vertices[indices[index + 1]].boneWeights.Z * v; boneWeights[vertices[indices[index + 1]].boneIndex3] += vertices[indices[index + 1]].boneWeights.W * v; boneWeights[vertices[indices[index + 2]].boneIndex0] += vertices[indices[index + 2]].boneWeights.X * w; boneWeights[vertices[indices[index + 2]].boneIndex1] += vertices[indices[index + 2]].boneWeights.Y * w; boneWeights[vertices[indices[index + 2]].boneIndex2] += vertices[indices[index + 2]].boneWeights.Z * w; boneWeights[vertices[indices[index + 2]].boneIndex3] += vertices[indices[index + 2]].boneWeights.W * w; Array.Sort(boneWeights, boneIndices); float sum = 1.0f / (boneWeights[255] + boneWeights[254] + boneWeights[253] + boneWeights[252]); node.vd.boneIndex0 = boneIndices[255]; node.vd.boneIndex1 = boneIndices[254]; node.vd.boneIndex2 = boneIndices[253]; node.vd.boneIndex3 = boneIndices[252]; node.vd.boneWeights.X = boneWeights[255] * sum; node.vd.boneWeights.Y = boneWeights[254] * sum; node.vd.boneWeights.Z = boneWeights[253] * sum; node.vd.boneWeights.W = boneWeights[252] * sum; }
/// <summary> /// Helper method used by <see cref="TriangleMesh.toDynamicOctree"/>. /// It constructs the DynamicOctree recursively. It is supplied a /// triangle and adds all nodes to the octree that intersect the triangle. /// </summary> /// <param name="pos">Position of the current node relative to its parent. /// See <see cref="DynamicOctree"/></param> /// <param name="x">x-coordinate within the virtual uniform grid spanned by the octree.</param> /// <param name="y">y-coordinate within the virtual uniform grid spanned by the octree.</param> /// <param name="z">z-coordinate within the virtual uniform grid spanned by the octree.</param> /// <param name="octreeMin">Minimum vector of the octree.</param> /// <param name="gridDimension">Dimension of the octree (a cube).</param> /// <param name="index">Index of the first triangle vertex.</param> /// <param name="triBBmin">Minimum vector of the triangle bounding box.</param> /// <param name="triBBmax">Maximum vector of the triangle bounding box.</param> /// <param name="level">Current tree level/depth.</param> /// <param name="maxLevel">The maximum node level/depth of any node in /// the created DynamicOctree.</param> /// <param name="parent">Parent node of the current node.</param> private void traverse(byte pos, ushort x, ushort y, ushort z, Vector3 octreeMin, double gridDimension, int index, Vector3 triBBmin, Vector3 triBBmax, byte level, byte maxLevel, DynamicOctreeNode parent) { if (level == 0) { for (byte i = 0; i < 8; ++i) { traverse(i, (ushort)(x * 2 + (i & 1)), (ushort)(y * 2 + ((i & 2) >> 1)), (ushort)(z * 2 + ((i & 4) >> 2)), octreeMin, gridDimension, index, triBBmin, triBBmax, (byte)(level + 1), maxLevel, parent); } } else { if (Math3DHelper.intersects(x, y, z, level, gridDimension, octreeMin, _vertices[_indices[index]].position, _vertices[_indices[index + 1]].position, _vertices[_indices[index + 2]].position, triBBmin, triBBmax)) { DynamicOctreeNode newParent = parent.addChild(pos); float gridCellDim = (float)(gridDimension / (1u << level)); float gridCellHalfDim = gridCellDim * .5f; Vector3 center = new Vector3(x * gridCellDim + gridCellHalfDim, y * gridCellDim + gridCellHalfDim, z * gridCellDim + gridCellHalfDim) + octreeMin; float triArea = Vector3.Cross(_vertices[_indices[index + 1]].position - _vertices[_indices[index]].position, _vertices[_indices[index + 2]].position - _vertices[_indices[index]].position).Length() * 0.5f; float u = Vector3.Cross(_vertices[_indices[index + 1]].position - center, _vertices[_indices[index + 2]].position - center).Length() * 0.5f; float v = Vector3.Cross(_vertices[_indices[index]].position - center, _vertices[_indices[index + 2]].position - center).Length() * 0.5f; float w = Vector3.Cross(_vertices[_indices[index]].position - center, _vertices[_indices[index + 1]].position - center).Length() * 0.5f; float sum; if ((sum = u + v + w) < newParent.dist) { newParent.dist = sum; triArea = 1.0f / triArea; u *= triArea; v *= triArea; w *= triArea; sum = 1.0f / (u + v + w); u *= sum; v *= sum; w *= sum; newParent.vd.normal = u * _vertices[_indices[index]].normal + v * _vertices[_indices[index + 1]].normal + w * _vertices[_indices[index + 2]].normal; newParent.vd.normal.Normalize(); newParent.vd.texCoords = u * _vertices[_indices[index]].texCoord + v * _vertices[_indices[index + 1]].texCoord + w * _vertices[_indices[index + 2]].texCoord; weight(newParent, _vertices, _indices, index, u, v, w); Vector3 e1 = _vertices[_indices[index + 1]].position - _vertices[_indices[index]].position; Vector3 e2 = _vertices[_indices[index + 2]].position - _vertices[_indices[index]].position; Vector2 euv1 = _vertices[_indices[index + 1]].texCoord - _vertices[_indices[index]].texCoord; Vector2 euv2 = _vertices[_indices[index + 2]].texCoord - _vertices[_indices[index]].texCoord; float cp = euv1.Y * euv2.X - euv1.X * euv2.Y; if (cp != 0.0f) { newParent.vd.tangent = (e1 * (-euv2.Y) + e2 * euv1.Y) / cp; newParent.vd.tangent.Normalize(); } } if (level < maxLevel) { for (byte i = 0; i < 8; ++i) { traverse(i, (ushort)(x * 2 + (i & 1)), (ushort)(y * 2 + ((i & 2) >> 1)), (ushort)(z * 2 + ((i & 4) >> 2)), octreeMin, gridDimension, index, triBBmin, triBBmax, (byte)(level + 1), maxLevel, newParent); } } } } }
/// <summary> /// Adds a child with the specified node to this /// node, iff there is no child node with position <paramref name="withPos"/> yet. /// In there is already such a node, it is returned. /// </summary> /// <param name="withPos">The position of the child node relative to this node.</param> /// <returns>The child of this node with relative position <paramref name="withPos"/>.</returns> public DynamicOctreeNode addChild(byte withPos) { if (childCount == 0) { firstChild = new DynamicOctreeNode(withPos); ++childCount; return firstChild; } else { if (withPos < firstChild.position) { DynamicOctreeNode insert = new DynamicOctreeNode(withPos); insert.nextNode = firstChild; firstChild = insert; ++childCount; return insert; } else if (withPos == firstChild.position) return firstChild; DynamicOctreeNode currentNode = firstChild.nextNode; DynamicOctreeNode previousNode = firstChild; for (int i = 1; i < childCount; ++i) { if (withPos < currentNode.position) { DynamicOctreeNode insert = new DynamicOctreeNode(withPos); previousNode.nextNode = insert; insert.nextNode = currentNode; ++childCount; return insert; } else if (withPos == currentNode.position) { return currentNode; } else { currentNode = currentNode.nextNode; previousNode = previousNode.nextNode; } } previousNode.nextNode = new DynamicOctreeNode(withPos); ++childCount; return previousNode.nextNode; } }