Ejemplo n.º 1
0
        // The following basic top-down approach is used in Bullet (btDbvt).

        /// <summary>
        /// Builds the subtree top-down.
        /// </summary>
        /// <typeparam name="T">The type of item stored in the tree.</typeparam>
        /// <param name="leaves">The leaves of the tree.</param>
        /// <param name="firstLeaf">The first leaf.</param>
        /// <param name="lastLeaf">The last leaf.</param>
        /// <param name="createNode">A function that creates a new, empty node.</param>
        /// <returns>The root node of the subtree.</returns>
        private static IAabbTreeNode <T> BuildBottomUp <T>(List <IAabbTreeNode <T> > leaves, int firstLeaf, int lastLeaf, Func <IAabbTreeNode <T> > createNode)
        {
            // Replace 'leaves' with new list we can work with.
            var nodes = DigitalRune.ResourcePools <IAabbTreeNode <T> > .Lists.Obtain();

            for (int i = firstLeaf; i <= lastLeaf; i++)
            {
                nodes.Add(leaves[i]);
            }

            // Iteratively merge nodes (subtrees) until we have a single tree.
            while (nodes.Count > 1)
            {
                float      minSize = float.PositiveInfinity;
                Pair <int> minPair = new Pair <int>(-1, -1);
                for (int i = 0; i < nodes.Count; i++)
                {
                    // Compare node with all subsequent nodes in list.
                    for (int j = i + 1; j < nodes.Count; j++)
                    {
                        Aabb mergedAabb = Aabb.Merge(nodes[i].Aabb, nodes[j].Aabb);

                        // Compute a "size" which can be used to estimate the fit of the new node.
                        // Here: volume + edges
                        Vector3F edges = mergedAabb.Extent;
                        float    size  = edges.X * edges.Y * edges.Z + edges.X + edges.Y + edges.Z;
                        if (size <= minSize) // Note: We compare with ≤ because size can be ∞.
                        {
                            minSize        = size;
                            minPair.First  = i;
                            minPair.Second = j;
                        }
                    }
                }

                if (minPair.First < 0 || minPair.Second < 0)
                {
                    throw new GeometryException("Could not build AABB tree because the AABB of an item is invalid (e.g. NaN).");
                }

                // Create a new parent node that merges the two subtrees.
                IAabbTreeNode <T> leftChild  = nodes[minPair.First];
                IAabbTreeNode <T> rightChild = nodes[minPair.Second];
                IAabbTreeNode <T> parent     = createNode();
                parent.Aabb       = Aabb.Merge(leftChild.Aabb, rightChild.Aabb);
                parent.LeftChild  = leftChild;
                parent.RightChild = rightChild;

                // Remove subtrees from list and add the new node.
                nodes.RemoveAt(minPair.Second);
                nodes.RemoveAt(minPair.First);
                nodes.Add(parent);
            }

            IAabbTreeNode <T> root = nodes[0];

            DigitalRune.ResourcePools <IAabbTreeNode <T> > .Lists.Recycle(nodes);

            return(root);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Performs a full refit of the specified subtree.
        /// </summary>
        /// <param name="node">The root node of the subtree.</param>
        private void FullRefit(Node node)
        {
            Debug.Assert(node != null, "Refit should not be called with node == null.");

            if (node.IsLeaf)
            {
                // Update leaf AABB.
                node.Aabb = GetAabbForItem(node.Item);
            }
            else
            {
                // Internal node.
                if (node.IsValid && node.IsActive && !node.LeftChild.IsActive)
                {
                    Debug.Assert(!node.RightChild.IsActive, "When the left child is inactive the right child must also be inactive.");

                    // Node was front node in the last collision detection query.
                    // Invalidate node:
                    //   - Remove left and right subtrees
                    //   - Gather all leaf nodes locally.
                    InvalidateSubtree(node);
                }

                // Update AABBs.
                if (!node.IsValid)
                {
                    // Leaf nodes are stored locally.
                    Node leaf = node.Leaves[0];
                    leaf.IsActive = false;
                    leaf.Aabb     = GetAabbForItem(leaf.Item);
                    node.Aabb     = leaf.Aabb;

                    int numberOfLeaves = node.Leaves.Count;
                    for (int index = 1; index < numberOfLeaves; index++)
                    {
                        leaf          = node.Leaves[index];
                        leaf.IsActive = false;
                        leaf.Aabb     = GetAabbForItem(leaf.Item);
                        node.Aabb.Grow(leaf.Aabb);
                    }
                }
                else
                {
                    // Valid internal node. (Node was visited in the last collision detection query, but
                    // was not a front node.)
                    FullRefit(node.LeftChild);
                    FullRefit(node.RightChild);
                    node.Aabb = Aabb.Merge(node.LeftChild.Aabb, node.RightChild.Aabb);

                    // Check whether parent/children relationship is degenerate.
                    node.IsDegenerate = IsDegenerate(node);
                }
            }

            node.IsActive = false;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Removes the specified leaf node from the tree.
        /// </summary>
        /// <param name="leaf">The leaf node .</param>
        /// <returns>
        /// The closest ancestor of <paramref name="leaf"/> that was not resized during remove.
        /// </returns>
        private Node RemoveLeaf(Node leaf)
        {
            if (_root == leaf)
            {
                // Remove only node of tree.
                _root = null;
                return(null);
            }
            else
            {
                Node parent   = leaf.Parent;
                Node previous = parent.Parent;
                Node sibling  = (parent.LeftChild == leaf) ? parent.RightChild : parent.LeftChild;
                if (previous == null)
                {
                    // The sibling becomes the new root of the tree.
                    _root          = sibling;
                    sibling.Parent = null;
                    Nodes.Recycle(parent);
                    return(_root);
                }
                else
                {
                    // Replace parent by sibling.
                    if (previous.LeftChild == parent)
                    {
                        previous.LeftChild = sibling;
                    }
                    else
                    {
                        previous.RightChild = sibling;
                    }

                    sibling.Parent = previous;
                    Nodes.Recycle(parent);

                    // Update AABBs of ancestors.
                    do
                    {
                        Aabb oldAabb = previous.Aabb;
                        previous.Aabb = Aabb.Merge(previous.LeftChild.Aabb, previous.RightChild.Aabb);
                        if (oldAabb != previous.Aabb)
                        {
                            previous = previous.Parent;
                        }
                        else
                        {
                            break;
                        }
                    } while (previous != null);

                    return(previous ?? _root);
                }
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Recomputes the AABB of the specified node. (All leaf nodes need to be up-to-date.)
        /// </summary>
        /// <param name="node">The node.</param>
        private static void RecomputeAabb(Node node)
        {
            Debug.Assert(!node.IsLeaf);

            if (node.IsValid)
            {
                node.Aabb = Aabb.Merge(node.LeftChild.Aabb, node.RightChild.Aabb);
            }
            else
            {
                node.Aabb = node.Leaves[0].Aabb;
                int numberOfLeaves = node.Leaves.Count;
                for (int i = 1; i < numberOfLeaves; i++)
                {
                    node.Aabb.Grow(node.Leaves[i].Aabb);
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Updates the AABB of the spatial partition.
        /// </summary>
        private void UpdateAabb()
        {
            bool staticPartitionValid  = (StaticPartition.Count > 0);
            bool dynamicPartitionValid = (DynamicPartition.Count > 0);

            if (staticPartitionValid && dynamicPartitionValid)
            {
                Aabb = Aabb.Merge(StaticPartition.Aabb, DynamicPartition.Aabb);
            }
            else if (staticPartitionValid)
            {
                Aabb = StaticPartition.Aabb;
            }
            else if (dynamicPartitionValid)
            {
                Aabb = DynamicPartition.Aabb;
            }
            else
            {
                Aabb = new Aabb();
            }
        }
        private void ComputeAabbs(Aabb[] buffer, int index, ref int count)
        {
            // Increment the counter for each node visited.
            count++;

            Node node = _nodes[index];

            if (node.IsLeaf)
            {
                // Store unquantized AABB of leaf node.
                buffer[index] = GetAabbForItem(node.Item);
            }
            else
            {
                // Compute AABB of child nodes.
                int leftIndex = index + 1;
                ComputeAabbs(buffer, leftIndex, ref count);

                int rightIndex = count;
                ComputeAabbs(buffer, rightIndex, ref count);
                buffer[index] = Aabb.Merge(buffer[leftIndex], buffer[rightIndex]);
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Adds a leaf node to the given tree.
        /// </summary>
        /// <param name="root">The root of the tree (or subtree).</param>
        /// <param name="leaf">The leaf node to be added.</param>
        private void AddLeaf(Node root, Node leaf)
        {
            if (_root == null)
            {
                // Insert leaf as new root.
                _root = leaf;
            }
            else
            {
                // Search for leaf node that is closest to 'leaf'. This node that will become the new
                // sibling of 'leaf'.
                Node sibling = root;
                while (!sibling.IsLeaf)
                {
                    int selection = AabbTreeHelper.SelectClosest(leaf.Aabb, sibling.LeftChild.Aabb, sibling.RightChild.Aabb);
                    sibling = (selection == 0) ? sibling.LeftChild : sibling.RightChild;
                }

                // Add a new node as the parent of (sibling, leaf).
                Node parent = sibling.Parent;
                Node node   = Nodes.Obtain();
                node.Aabb       = Aabb.Merge(sibling.Aabb, leaf.Aabb);
                node.Parent     = parent;
                node.LeftChild  = sibling;
                node.RightChild = leaf;
                sibling.Parent  = node;
                leaf.Parent     = node;

                if (parent == null)
                {
                    // node is the new root.
                    _root = node;
                }
                else
                {
                    // node is an internal node.
                    if (parent.LeftChild == sibling)
                    {
                        parent.LeftChild = node;
                    }
                    else
                    {
                        parent.RightChild = node;
                    }

                    // Update AABBs of ancestor.
                    do
                    {
                        if (!parent.Aabb.Contains(node.Aabb))
                        {
                            parent.Aabb = Aabb.Merge(parent.LeftChild.Aabb, parent.RightChild.Aabb);
                        }
                        else
                        {
                            break;
                        }

                        node   = parent;
                        parent = parent.Parent;
                    } while (parent != null);
                }
            }
        }
Ejemplo n.º 8
0
    public static Mesh Merge(IEnumerable<SceneNode> sceneNodes)
    {
      if (sceneNodes == null)
        throw new ArgumentNullException("sceneNodes");

      // Gather flat list of all mesh nodes. Each mesh node could have children.
      var meshNodes = new List<MeshNode>();
      foreach (var meshNode in sceneNodes)
        meshNodes.AddRange(meshNode.GetSubtree().OfType<MeshNode>());

      var mergedMesh = new Mesh();
      try
      {
        var jobs = new List<MergeJob>();

        // A list of all encountered vertex types.
        var vertexDeclarations = new List<VertexDeclaration>();

        // A list of total vertex counts for each vertex declaration.
        var vertexCounts = new List<int>();

        // For indices we only need one counter because we use only one shared index buffer.
        int indexCount = 0;

        var mergedAabb = new Aabb(new Vector3(float.MaxValue), new Vector3(float.MinValue));

        // Merge materials, create job list, merge AABBs, check if there is an occluder.
        bool hasOccluder = false;
        foreach (var meshNode in meshNodes)
        {
          var mesh = meshNode.Mesh;


          if (mesh.Skeleton != null)
            throw new NotSupportedException("Cannot merge skinned meshes.");


          foreach (var submesh in mesh.Submeshes)
          {
            if (submesh.PrimitiveCount <= 0)
              continue;
            if (submesh.VertexBufferEx == null)
              continue;

            // Merge materials and get material index.
            var material = submesh.GetMaterial();
            int mergedMaterialIndex = mergedMesh.Materials.IndexOf(material);
            if (mergedMaterialIndex < 0)
            {
              mergedMaterialIndex = mergedMesh.Materials.Count;
              mergedMesh.Materials.Add(material);
            }

            if (mergedMaterialIndex > byte.MaxValue)
              throw new NotSupportedException("Cannot merge meshes. Merged mesh must not have more than 256 materials.");

            // Try to find index of existing matching vertex declaration.
            var vertexDeclaration = submesh.VertexBuffer.VertexDeclaration;
            int vertexDeclarationIndex = -1;
            for (int i = 0; i < vertexDeclarations.Count; i++)
            {
              if (AreEqual(vertexDeclarations[i], vertexDeclaration))
              {
                vertexDeclarationIndex = i;
                break;
              }
            }

            if (vertexDeclarationIndex < 0)
            {
              // Add new vertex declaration.
              vertexDeclarationIndex = vertexDeclarations.Count;
              vertexDeclarations.Add(vertexDeclaration);
              vertexCounts.Add(0);
            }

            if (vertexDeclarationIndex > byte.MaxValue)
              throw new NotSupportedException("Cannot merge meshes. Merged mesh must not have more than 256 different vertex declarations.");

            // Count total number of vertices per vertex declaration.
            vertexCounts[vertexDeclarationIndex] += submesh.VertexCount;

            // Count number of indices.
            if (submesh.IndexBuffer != null)
              indexCount += GetNumberOfIndices(submesh.PrimitiveType, submesh.PrimitiveCount);

            jobs.Add(new MergeJob
            {
              Pose = meshNode.PoseWorld,
              Scale = meshNode.ScaleWorld,
              Submesh = submesh,
              MergedMaterialIndex = (byte)mergedMaterialIndex,
              VertexDeclarationIndex = (byte)vertexDeclarationIndex,

              // We set a sort key by which we can quickly sort all jobs.
              // We can merge submeshes if they have the same material, vertex declaration,
              // primitive type.
              // Submeshes do not need to have an index buffer. We could merge a submesh with
              // and without index buffer by generating indices. However, we do not merge in
              // this case.
              //             -------------------------------------------------------------------------------
              // Sort key = |  unused  |  vertex type  |  material  |  primitive type  |  has index buffer  |
              //            |  8 bit   |     8 bit     |   8 bit    |      7 bit       |       1 bit        |
              //             -------------------------------------------------------------------------------
              SortKey = (uint)(vertexDeclarationIndex << 16
                               | mergedMaterialIndex << 8
                               | (int)submesh.PrimitiveType << 1
                               | ((submesh.IndexBuffer != null) ? 1 : 0)),
            });
          }

          // Merge AABBs.
          mergedAabb = Aabb.Merge(meshNode.Aabb, mergedAabb);

          hasOccluder |= (mesh.Occluder != null);
        }

        if (jobs.Count == 0)
        {
          mergedMesh.BoundingShape = Shape.Empty;
          return mergedMesh;
        }

        // Create new bounding shape from merged AABB.
        var extent = mergedAabb.Extent;
        if (Numeric.IsFinite(extent.X + extent.Y + extent.Z))
        {
          var boxShape = new BoxShape(extent);
          if (mergedAabb.Center.IsNumericallyZero)
            mergedMesh.BoundingShape = boxShape;
          else
            mergedMesh.BoundingShape = new TransformedShape(new GeometricObject(boxShape, new Pose(mergedAabb.Center)));
        }
        else
        {
          mergedMesh.BoundingShape = Shape.Infinite;
        }

        jobs.Sort(MergeJobComparer.Instance);

        MergeSubmeshes(jobs, mergedMesh, vertexDeclarations, vertexCounts, indexCount);

        if (hasOccluder)
          mergedMesh.Occluder = MergeOccluders(meshNodes);

        return mergedMesh;
      }
      catch
      {
        mergedMesh.Dispose();
        throw;
      }
    }
Ejemplo n.º 9
0
        /// <summary>
        /// Performs a partial refit of the current subtree.
        /// </summary>
        /// <param name="node">The root node of the subtree.</param>
        /// <param name="invalidItems">The set of invalid items.</param>
        /// <returns>
        /// <see langword="true"/> if the AABB of <paramref name="node"/> was updated; otherwise,
        /// <see langword="false"/> if the AABB has not changed.
        /// </returns>
        private bool PartialRefit(Node node, HashSet <T> invalidItems)
        {
            Debug.Assert(node != null);
            Debug.Assert(invalidItems != null);

            bool updated = false;

            if (node.IsLeaf)
            {
                // Update leaf AABB if necessary.
                if (invalidItems.Contains(node.Item))
                {
                    node.Aabb = GetAabbForItem(node.Item);
                    updated   = true;
                }
            }
            else
            {
                // Refit children.
                if (!node.IsValid)
                {
                    // Leaf nodes are stored locally.
                    Node leaf = node.Leaves[0];
                    leaf.IsActive = false;
                    if (invalidItems.Contains(leaf.Item))
                    {
                        leaf.Aabb = GetAabbForItem(leaf.Item);
                        updated   = true;
                    }

                    node.Aabb = leaf.Aabb;

                    int numberOfLeaves = node.Leaves.Count;
                    for (int index = 1; index < numberOfLeaves; index++)
                    {
                        leaf          = node.Leaves[index];
                        leaf.IsActive = false;
                        if (invalidItems.Contains(leaf.Item))
                        {
                            leaf.Aabb = GetAabbForItem(leaf.Item);
                            updated   = true;
                        }

                        node.Aabb.Grow(leaf.Aabb);
                    }
                }
                else
                {
                    // Valid internal node.
                    bool leftUpdated  = PartialRefit(node.LeftChild, invalidItems);
                    bool rightUpdated = PartialRefit(node.RightChild, invalidItems);
                    updated = leftUpdated || rightUpdated;
                    if (updated)
                    {
                        // Update internal AABB.
                        node.Aabb = Aabb.Merge(node.LeftChild.Aabb, node.RightChild.Aabb);

                        // Check whether parent/children relationship is degenerate.
                        node.IsDegenerate = IsDegenerate(node);
                    }
                }
            }

            node.IsActive = false;
            return(updated);
        }
Ejemplo n.º 10
0
        public static Mesh Merge(Mesh mesh, IList <Vector3F> scales, IList <Pose> poses)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }
#if ANIMATION
            if (mesh.Skeleton != null)
            {
                throw new NotSupportedException("Cannot merge skinned meshes.");
            }
#endif
            if (poses == null)
            {
                throw new ArgumentNullException("poses");
            }

            if (scales == null)
            {
                var array = new Vector3F[poses.Count];
                for (int i = 0; i < array.Length; i++)
                {
                    array[i] = Vector3F.One;
                }

                scales = array;
            }

            if (scales.Count != poses.Count)
            {
                throw new ArgumentException("The number of elements in poses and scales must be equal.");
            }

            var mergedMesh = new Mesh();
            try
            {
                var jobs = new List <MergeJob>();

                // A list of all encountered vertex types.
                var vertexDeclarations = new List <VertexDeclaration>();

                // A list of total vertex counts for each vertex declaration.
                var vertexCounts = new List <int>();

                // For indices we only need one counter because we use only one shared index buffer.
                int indexCount = 0;

                // Merge materials, create job list.
                foreach (var submesh in mesh.Submeshes)
                {
                    if (submesh.PrimitiveCount <= 0)
                    {
                        continue;
                    }
                    if (submesh.VertexBufferEx == null)
                    {
                        continue;
                    }

                    // Merge materials and get material index.
                    var material            = submesh.GetMaterial();
                    var mergedMaterialIndex = mergedMesh.Materials.IndexOf(material);
                    if (mergedMaterialIndex < 0)
                    {
                        mergedMaterialIndex = mergedMesh.Materials.Count;
                        mergedMesh.Materials.Add(material);
                    }

                    if (mergedMaterialIndex > byte.MaxValue)
                    {
                        throw new NotSupportedException("Cannot merge meshes. Merged mesh must not have more than 256 materials.");
                    }

                    // Try to find index of existing matching vertex declaration.
                    int vertexDeclarationIndex = -1;
                    for (int i = 0; i < vertexDeclarations.Count; i++)
                    {
                        if (AreEqual(vertexDeclarations[i], submesh.VertexBuffer.VertexDeclaration))
                        {
                            vertexDeclarationIndex = i;
                            break;
                        }
                    }

                    if (vertexDeclarationIndex < 0)
                    {
                        // Add new vertex declaration.
                        vertexDeclarationIndex = vertexDeclarations.Count;
                        vertexDeclarations.Add(submesh.VertexBuffer.VertexDeclaration);
                        vertexCounts.Add(0);
                    }

                    if (vertexDeclarationIndex > byte.MaxValue)
                    {
                        throw new NotSupportedException("Cannot merge meshes. Merged mesh must not have more than 256 different vertex declarations.");
                    }

                    for (int instanceIndex = 0; instanceIndex < poses.Count; instanceIndex++)
                    {
                        // Count total number of vertices per vertex declaration.
                        vertexCounts[vertexDeclarationIndex] += submesh.VertexCount;

                        // Count number of indices.
                        if (submesh.IndexBuffer != null)
                        {
                            indexCount += GetNumberOfIndices(submesh.PrimitiveType, submesh.PrimitiveCount);
                        }

                        jobs.Add(new MergeJob
                        {
                            Pose                   = poses[instanceIndex],
                            Scale                  = scales[instanceIndex],
                            Submesh                = submesh,
                            MergedMaterialIndex    = (byte)mergedMaterialIndex,
                            VertexDeclarationIndex = (byte)vertexDeclarationIndex,

                            // We set a sort key by which we can quickly sort all jobs.
                            // We can merge submeshes if they have the same material, vertex declaration,
                            // primitive type.
                            // Submeshes do not need to have an index buffer. We could merge a submesh with
                            // and without index buffer by generating indices. However, we do not merge in
                            // this case.
                            //             -------------------------------------------------------------------------------
                            // Sort key = |  unused  |  vertex type  |  material  |  primitive type  |  has index buffer  |
                            //            |  8 bit   |     8 bit     |   8 bit    |      7 bit       |       1 bit        |
                            //             -------------------------------------------------------------------------------
                            SortKey = (uint)(vertexDeclarationIndex << 16
                                             | mergedMaterialIndex << 8
                                             | (int)submesh.PrimitiveType << 1
                                             | ((submesh.IndexBuffer != null) ? 1 : 0)),
                        });
                    }
                }

                // Merge AABBs.
                var mergedAabb = new Aabb(new Vector3F(float.MaxValue), new Vector3F(float.MinValue));
                for (int instanceIndex = 0; instanceIndex < poses.Count; instanceIndex++)
                {
                    mergedAabb = Aabb.Merge(
                        mesh.BoundingShape.GetAabb(scales[instanceIndex], poses[instanceIndex]),
                        mergedAabb);
                }

                if (jobs.Count == 0)
                {
                    mergedMesh.BoundingShape = Shape.Empty;
                    return(mergedMesh);
                }

                // Create new bounding shape from merged AABB.
                var extent = mergedAabb.Extent;
                if (Numeric.IsFinite(extent.X + extent.Y + extent.Z))
                {
                    var boxShape = new BoxShape(extent);
                    if (mergedAabb.Center.IsNumericallyZero)
                    {
                        mergedMesh.BoundingShape = boxShape;
                    }
                    else
                    {
                        mergedMesh.BoundingShape = new TransformedShape(new GeometricObject(boxShape, new Pose(mergedAabb.Center)));
                    }
                }
                else
                {
                    mergedMesh.BoundingShape = Shape.Infinite;
                }

                jobs.Sort(MergeJobComparer.Instance);

                MergeSubmeshes(jobs, mergedMesh, vertexDeclarations, vertexCounts, indexCount);

                // Merge occluders.
                if (mesh.Occluder != null)
                {
                    mergedMesh.Occluder = MergeOccluders(mesh, scales, poses);
                }

                return(mergedMesh);
            }
            catch
            {
                mergedMesh.Dispose();
                throw;
            }
        }