Esempio n. 1
0
        public static void Update(ModelViewHierarchyUpdater hierarchy, RenderModel renderModel, int slot)
        {
            var boneMatrices = staticBoneMatrices;

            var meshes = renderModel.RenderMeshesList[slot];
            {
                if (meshes == null)
                {
                    return;
                }

                foreach (var renderMesh in meshes)
                {
                    var mesh     = renderMesh.Mesh;
                    var skinning = mesh.Skinning;

                    if (skinning == null)
                    {
                        // For unskinned meshes, use the original bounding box
                        var boundingBoxExt = (BoundingBoxExt)mesh.BoundingBox;
                        boundingBoxExt.Transform(renderMesh.WorldMatrix);
                        renderMesh.BoundingBox = boundingBoxExt;

                        continue;
                    }

                    var bones = skinning.Bones;

                    // Make sure there is enough spaces in boneMatrices
                    if (boneMatrices == null || bones.Length > boneMatrices.Length)
                    {
                        staticBoneMatrices = boneMatrices = new Matrix[bones.Length];
                    }

                    var bindPoseBoundingBox = new BoundingBoxExt(renderMesh.Mesh.BoundingBox);
                    renderMesh.BoundingBox = BoundingBoxExt.Empty;

                    for (int index = 0; index < bones.Length; index++)
                    {
                        var nodeIndex = bones[index].NodeIndex;

                        // Compute bone matrix
                        Matrix.Multiply(ref bones[index].LinkToMeshMatrix, ref hierarchy.NodeTransformations[nodeIndex].WorldMatrix, out boneMatrices[index]);

                        // Calculate and extend bounding box for each bone
                        // TODO: Move runtime bounding box into ModelViewHierarchyUpdater?

                        // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                        // Compute transformed AABB (by world)
                        var boundingBoxExt = bindPoseBoundingBox;
                        boundingBoxExt.Transform(boneMatrices[index]);
                        BoundingBoxExt.Merge(ref renderMesh.BoundingBox, ref boundingBoxExt, out renderMesh.BoundingBox);
                    }

                    // Upload bones
                    renderMesh.Parameters.Set(TransformationSkinningKeys.BlendMatrixArray, boneMatrices, 0, bones.Length);
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Calculate the bounding sphere of the entity's models.
        /// </summary>
        /// <param name="entity">The entity to measure</param>
        /// <param name="isRecursive">Indicate the child entities bounding spheres should be merged</param>
        /// <param name="meshSelector">Selects which meshes are considered for bounding box calculation.</param>
        /// <returns>The bounding sphere (world matrix included)</returns>
        public static BoundingSphere CalculateBoundSphere(this Entity entity, bool isRecursive = true, Func <Model, IEnumerable <Mesh> > meshSelector = null)
        {
            entity.Transform.UpdateWorldMatrix();
            var worldMatrix = entity.Transform.WorldMatrix;

            var boundingSphere = BoundingSphere.Empty;

            // calculate the bounding sphere of the model if any
            var modelComponent = entity.Get <ModelComponent>();
            var hasModel       = modelComponent?.Model != null;

            if (hasModel)
            {
                var hierarchy      = modelComponent.Skeleton;
                var nodeTransforms = new Matrix[hierarchy.Nodes.Length];

                // Calculate node transforms here, since there might not be a ModelProcessor running
                for (int i = 0; i < nodeTransforms.Length; i++)
                {
                    if (hierarchy.Nodes[i].ParentIndex == -1)
                    {
                        nodeTransforms[i] = worldMatrix;
                    }
                    else
                    {
                        Matrix localMatrix;

                        Matrix.Transformation(
                            ref hierarchy.Nodes[i].Transform.Scale,
                            ref hierarchy.Nodes[i].Transform.Rotation,
                            ref hierarchy.Nodes[i].Transform.Position, out localMatrix);

                        Matrix.Multiply(ref localMatrix, ref nodeTransforms[hierarchy.Nodes[i].ParentIndex], out nodeTransforms[i]);
                    }
                }

                // calculate the bounding sphere
                var boundingBox = BoundingBoxExt.Empty;

                var meshes         = modelComponent.Model.Meshes;
                var filteredMeshes = meshSelector == null ? meshes : meshSelector(modelComponent.Model);

                // Calculate skinned bounding boxes.
                // TODO: Cloned from ModelSkinningUpdater. Consolidate.
                foreach (var mesh in filteredMeshes)
                {
                    var skinning = mesh.Skinning;

                    if (skinning == null)
                    {
                        // For unskinned meshes, use the original bounding box
                        var boundingBoxExt = (BoundingBoxExt)mesh.BoundingBox;
                        boundingBoxExt.Transform(nodeTransforms[mesh.NodeIndex]);
                        BoundingBoxExt.Merge(ref boundingBox, ref boundingBoxExt, out boundingBox);
                    }
                    else
                    {
                        var bones = skinning.Bones;
                        var bindPoseBoundingBox = new BoundingBoxExt(mesh.BoundingBox);

                        for (var index = 0; index < bones.Length; index++)
                        {
                            var    nodeIndex = bones[index].NodeIndex;
                            Matrix boneMatrix;

                            // Compute bone matrix
                            Matrix.Multiply(ref bones[index].LinkToMeshMatrix, ref nodeTransforms[nodeIndex], out boneMatrix);

                            // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                            // Compute transformed AABB (by world)
                            var boundingBoxExt = bindPoseBoundingBox;
                            boundingBoxExt.Transform(boneMatrix);
                            BoundingBoxExt.Merge(ref boundingBox, ref boundingBoxExt, out boundingBox);
                        }
                    }
                }
                var halfSize    = boundingBox.Extent;
                var maxHalfSize = Math.Max(halfSize.X, Math.Max(halfSize.Y, halfSize.Z));
                boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(boundingBox.Center, maxHalfSize));
            }

            // Calculate the bounding sphere for the sprite component if any and merge the result
            var spriteComponent = entity.Get <SpriteComponent>();
            var hasSprite       = spriteComponent?.CurrentSprite != null;

            if (hasSprite && !(hasModel && meshSelector != null))
            {
                var spriteSize         = spriteComponent.CurrentSprite.Size;
                var spriteDiagonalSize = (float)Math.Sqrt(spriteSize.X * spriteSize.X + spriteSize.Y * spriteSize.Y);

                // Note: this is probably wrong, need to unify with SpriteComponentRenderer
                var center   = worldMatrix.TranslationVector;
                var scales   = new Vector3(worldMatrix.Row1.Length(), worldMatrix.Row2.Length(), worldMatrix.Row3.Length());
                var maxScale = Math.Max(scales.X, Math.Max(scales.Y, scales.Z));

                boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(center, maxScale * spriteDiagonalSize / 2f));
            }

            var spriteStudioComponent = entity.Get <SpriteStudioComponent>();

            if (spriteStudioComponent != null)
            {
                // Make sure nodes are prepared
                if (!SpriteStudioProcessor.PrepareNodes(spriteStudioComponent))
                {
                    return(new BoundingSphere());
                }

                // Update root nodes
                foreach (var node in spriteStudioComponent.Nodes)
                {
                    node.UpdateTransformation();
                }

                // Compute bounding sphere for each node
                foreach (var node in spriteStudioComponent.Nodes.SelectDeep(x => x.ChildrenNodes))
                {
                    if (node.Sprite == null || node.Hide != 0)
                    {
                        continue;
                    }

                    var nodeMatrix = node.ModelTransform * worldMatrix;

                    var spriteSize         = node.Sprite.Size;
                    var spriteDiagonalSize = (float)Math.Sqrt(spriteSize.X * spriteSize.X + spriteSize.Y * spriteSize.Y);

                    Vector3 pos, scale;
                    nodeMatrix.Decompose(out scale, out pos);

                    var center   = pos;
                    var maxScale = Math.Max(scale.X, scale.Y); //2d ignore Z

                    boundingSphere = BoundingSphere.Merge(boundingSphere, new BoundingSphere(center, maxScale * (spriteDiagonalSize / 2f)));
                }
            }

            var particleComponent = entity.Get <ParticleSystemComponent>();

            if (particleComponent != null)
            {
                var center = worldMatrix.TranslationVector;
                var sphere = particleComponent.ParticleSystem?.BoundingShape != null?BoundingSphere.FromBox(particleComponent.ParticleSystem.BoundingShape.GetAABB(center, Quaternion.Identity, 1.0f)) : new BoundingSphere(center, 2.0f);

                boundingSphere = BoundingSphere.Merge(boundingSphere, sphere);
            }

            var boundingBoxComponent = entity.Get <NavigationBoundingBoxComponent>();

            if (boundingBoxComponent != null)
            {
                var center = worldMatrix.TranslationVector;
                var scales = new Vector3(worldMatrix.Row1.Length(), worldMatrix.Row2.Length(), worldMatrix.Row3.Length()) * boundingBoxComponent.Size;
                boundingSphere = BoundingSphere.FromBox(new BoundingBox(-scales + center, scales + center));
            }

            // Extend the bounding sphere to include the children
            if (isRecursive)
            {
                foreach (var child in entity.GetChildren())
                {
                    boundingSphere = BoundingSphere.Merge(boundingSphere, child.CalculateBoundSphere(true, meshSelector));
                }
            }

            // If the entity does not contain any components having an impact on the bounding sphere, create an empty bounding sphere centered on the entity position.
            if (boundingSphere == BoundingSphere.Empty)
            {
                boundingSphere = new BoundingSphere(worldMatrix.TranslationVector, 0);
            }

            return(boundingSphere);
        }