Exemplo n.º 1
0
            private static unsafe void ProcessMaterial(ContentManager manager, ICollection <EntityChunk> chunks, MaterialInstance material, Model prefabModel)
            {
                //we need to futher group by VertexDeclaration
                var meshes = new Dictionary <VertexDeclaration, MeshData>();

                //actually create the mesh
                foreach (var chunk in chunks)
                {
                    foreach (var modelMesh in chunk.Model.Meshes)
                    {
                        //process only right material
                        if (modelMesh.MaterialIndex == chunk.MaterialIndex)
                        {
                            MeshData mesh;
                            if (!meshes.TryGetValue(modelMesh.Draw.VertexBuffers[0].Declaration, out mesh))
                            {
                                mesh = new MeshData {
                                    VertexStride = modelMesh.Draw.VertexBuffers[0].Stride
                                };
                                meshes.Add(modelMesh.Draw.VertexBuffers[0].Declaration, mesh);
                            }

                            //vertexes
                            var    vertexBufferRef = AttachedReferenceManager.GetAttachedReference(modelMesh.Draw.VertexBuffers[0].Buffer);
                            byte[] vertexData;
                            if (vertexBufferRef.Data != null)
                            {
                                vertexData = ((BufferData)vertexBufferRef.Data).Content;
                            }
                            else if (!string.IsNullOrEmpty(vertexBufferRef.Url))
                            {
                                var dataAsset = manager.Load <Buffer>(vertexBufferRef.Url);
                                vertexData = dataAsset.GetSerializationData().Content;
                            }
                            else
                            {
                                throw new Exception($"Failed to get Vertex BufferData for entity {chunk.Entity.Name}'s model.");
                            }

                            //transform the vertexes according to the entity
                            var vertexDataCopy = vertexData.ToArray();
                            chunk.Entity.Transform.UpdateWorldMatrix(); //make sure matrix is computed
                            var  worldMatrix       = chunk.Entity.Transform.WorldMatrix;
                            var  up                = Vector3.Cross(worldMatrix.Right, worldMatrix.Forward);
                            bool isScalingNegative = Vector3.Dot(worldMatrix.Up, up) < 0.0f;

                            modelMesh.Draw.VertexBuffers[0].TransformBuffer(vertexDataCopy, ref worldMatrix);

                            //add to the big single array
                            var vertexes = vertexDataCopy
                                           .Skip(modelMesh.Draw.VertexBuffers[0].Offset)
                                           .Take(modelMesh.Draw.VertexBuffers[0].Count * modelMesh.Draw.VertexBuffers[0].Stride)
                                           .ToArray();

                            mesh.VertexData.AddRange(vertexes);

                            //indices
                            var    indexBufferRef = AttachedReferenceManager.GetAttachedReference(modelMesh.Draw.IndexBuffer.Buffer);
                            byte[] indexData;
                            if (indexBufferRef.Data != null)
                            {
                                indexData = ((BufferData)indexBufferRef.Data).Content;
                            }
                            else if (!string.IsNullOrEmpty(indexBufferRef.Url))
                            {
                                var dataAsset = manager.Load <Buffer>(indexBufferRef.Url);
                                indexData = dataAsset.GetSerializationData().Content;
                            }
                            else
                            {
                                throw new Exception("Failed to get Indices BufferData for entity {chunk.Entity.Name}'s model.");
                            }

                            var indexSize = modelMesh.Draw.IndexBuffer.Is32Bit ? sizeof(uint) : sizeof(ushort);

                            byte[] indices;
                            if (isScalingNegative)
                            {
                                // Get reversed winding order
                                modelMesh.Draw.GetReversedWindingOrder(out indices);
                                indices = indices.Skip(modelMesh.Draw.IndexBuffer.Offset)
                                          .Take(modelMesh.Draw.IndexBuffer.Count * indexSize)
                                          .ToArray();
                            }
                            else
                            {
                                // Get indices normally
                                indices = indexData
                                          .Skip(modelMesh.Draw.IndexBuffer.Offset)
                                          .Take(modelMesh.Draw.IndexBuffer.Count * indexSize)
                                          .ToArray();
                            }

                            // Convert indices to 32 bits
                            if (indexSize == sizeof(ushort))
                            {
                                var uintIndices = new byte[indices.Length * 2];
                                fixed(byte *psrc = indices)
                                fixed(byte *pdst = uintIndices)
                                {
                                    var src = (ushort *)psrc;
                                    var dst = (uint *)pdst;

                                    int numIndices = indices.Length / sizeof(ushort);

                                    for (var i = 0; i < numIndices; i++)
                                    {
                                        dst[i] = src[i];
                                    }
                                }
                                indices = uintIndices;
                            }

                            // Offset indices by mesh.IndexOffset
                            fixed(byte *pdst = indices)
                            {
                                var dst = (uint *)pdst;

                                int numIndices = indices.Length / sizeof(uint);

                                for (var i = 0; i < numIndices; i++)
                                {
                                    // Offset indices
                                    dst[i] += (uint)mesh.IndexOffset;
                                }
                            }

                            mesh.IndexOffset += modelMesh.Draw.VertexBuffers[0].Count;

                            mesh.IndexData.AddRange(indices);
                        }
                    }
                }

                //Sort out material
                var matIndex = prefabModel.Materials.Count;

                prefabModel.Materials.Add(material);

                foreach (var meshData in meshes)
                {
                    //todo need to take care of short index
                    var vertexArray = meshData.Value.VertexData.ToArray();
                    var indexArray  = meshData.Value.IndexData.ToArray();

                    var vertexCount = vertexArray.Length / meshData.Value.VertexStride;
                    var indexCount  = indexArray.Length / 4;

                    var gpuMesh = new Mesh
                    {
                        Draw = new MeshDraw {
                            PrimitiveType = PrimitiveType.TriangleList, DrawCount = indexCount, StartLocation = 0
                        },
                        MaterialIndex = matIndex
                    };

                    var vertexBuffer = new BufferData(BufferFlags.VertexBuffer, new byte[vertexArray.Length]);
                    var indexBuffer  = new BufferData(BufferFlags.IndexBuffer, new byte[indexArray.Length]);

                    var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();
                    var indexBufferSerializable  = indexBuffer.ToSerializableVersion();

                    Array.Copy(vertexArray, vertexBuffer.Content, vertexArray.Length);
                    Array.Copy(indexArray, indexBuffer.Content, indexArray.Length);

                    gpuMesh.Draw.VertexBuffers    = new VertexBufferBinding[1];
                    gpuMesh.Draw.VertexBuffers[0] = new VertexBufferBinding(vertexBufferSerializable, meshData.Key, vertexCount);
                    gpuMesh.Draw.IndexBuffer      = new IndexBufferBinding(indexBufferSerializable, true, indexCount);

                    prefabModel.Meshes.Add(gpuMesh);
                }
            }
Exemplo n.º 2
0
        private object ExportModel(ICommandContext commandContext, ContentManager contentManager)
        {
            // Read from model file
            var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to

            AdjustSkeleton(modelSkeleton);
            var model = LoadModel(commandContext, contentManager);

            if (!CheckInputSlots(commandContext, model))
            {
                return(null);
            }

            // Apply materials
            foreach (var modelMaterial in Materials)
            {
                if (modelMaterial.MaterialInstance?.Material == null)
                {
                    commandContext.Logger.Verbose($"The material [{modelMaterial.Name}] is null in the list of materials.");
                }
                model.Materials.Add(modelMaterial.MaterialInstance);
            }

            model.BoundingBox = BoundingBox.Empty;

            Skeleton skeleton;

            if (SkeletonUrl != null || !MergeMeshes)
            {
                if (SkeletonUrl != null)
                {
                    // Load the skeleton
                    skeleton = contentManager.Load <Skeleton>(SkeletonUrl);
                }
                else
                {
                    skeleton    = modelSkeleton;
                    SkeletonUrl = Location + "_Skeleton_" + Guid.NewGuid();
                    contentManager.Save(SkeletonUrl, skeleton);
                }

                // Assign skeleton to model
                model.Skeleton = AttachedReferenceManager.CreateProxyObject <Skeleton>(AssetId.Empty, SkeletonUrl);
            }
            else
            {
                skeleton = null;
            }

            var skeletonMapping = new SkeletonMapping(skeleton, modelSkeleton);

            // Refresh skeleton updater with model skeleton
            var hierarchyUpdater = new SkeletonUpdater(modelSkeleton);

            hierarchyUpdater.UpdateMatrices();

            // Move meshes in the new nodes
            foreach (var mesh in model.Meshes)
            {
                // Apply scale import on meshes
                if (!MathUtil.NearEqual(ScaleImport, 1.0f))
                {
                    var transformationMatrix = Matrix.Scaling(ScaleImport);
                    for (int vbIdx = 0; vbIdx < mesh.Draw.VertexBuffers.Length; vbIdx++)
                    {
                        mesh.Draw.VertexBuffers[vbIdx].TransformBuffer(ref transformationMatrix);
                    }
                }

                var skinning = mesh.Skinning;
                if (skinning != null)
                {
                    // Update node mapping
                    // Note: we only remap skinning matrices, but we could directly remap skinning bones instead
                    for (int i = 0; i < skinning.Bones.Length; ++i)
                    {
                        var linkNodeIndex    = skinning.Bones[i].NodeIndex;
                        var newLinkNodeIndex = skeletonMapping.SourceToSource[linkNodeIndex];

                        var nodeIndex    = mesh.NodeIndex;
                        var newNodeIndex = skeletonMapping.SourceToSource[mesh.NodeIndex];

                        skinning.Bones[i].NodeIndex = skeletonMapping.SourceToTarget[linkNodeIndex];

                        // Adjust scale import
                        if (!MathUtil.NearEqual(ScaleImport, 1.0f))
                        {
                            skinning.Bones[i].LinkToMeshMatrix.TranslationVector = skinning.Bones[i].LinkToMeshMatrix.TranslationVector * ScaleImport;
                        }

                        // If it was remapped, we also need to update matrix
                        if (nodeIndex != newNodeIndex)
                        {
                            // Update mesh part
                            var transformMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, newNodeIndex, nodeIndex);
                            transformMatrix.Invert();
                            skinning.Bones[i].LinkToMeshMatrix = Matrix.Multiply(transformMatrix, skinning.Bones[i].LinkToMeshMatrix);
                        }

                        if (newLinkNodeIndex != linkNodeIndex)
                        {
                            // Update link part
                            var transformLinkMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, newLinkNodeIndex, linkNodeIndex);
                            skinning.Bones[i].LinkToMeshMatrix = Matrix.Multiply(skinning.Bones[i].LinkToMeshMatrix, transformLinkMatrix);
                        }
                    }
                }

                // Check if there was a remap using model skeleton
                if (skeletonMapping.SourceToSource[mesh.NodeIndex] != mesh.NodeIndex)
                {
                    // Transform vertices
                    var transformationMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, skeletonMapping.SourceToSource[mesh.NodeIndex], mesh.NodeIndex);
                    for (int vbIdx = 0; vbIdx < mesh.Draw.VertexBuffers.Length; vbIdx++)
                    {
                        mesh.Draw.VertexBuffers[vbIdx].TransformBuffer(ref transformationMatrix);
                    }

                    // Check if geometry is inverted, to know if we need to reverse winding order
                    // TODO: What to do if there is no index buffer? We should create one... (not happening yet)
                    if (mesh.Draw.IndexBuffer == null)
                    {
                        throw new InvalidOperationException();
                    }

                    Matrix  rotation;
                    Vector3 scale, translation;
                    if (transformationMatrix.Decompose(out scale, out rotation, out translation) &&
                        scale.X * scale.Y * scale.Z < 0)
                    {
                        mesh.Draw.ReverseWindingOrder();
                    }
                }

                // Update new node index using real asset skeleton
                mesh.NodeIndex = skeletonMapping.SourceToTarget[mesh.NodeIndex];
            }

            // Apply custom model modifiers
            if (ModelModifiers != null)
            {
                foreach (var modifier in ModelModifiers)
                {
                    modifier.Apply(commandContext, model);
                }
            }

            // Merge meshes with same parent nodes, material and skinning
            var meshesByNodes = model.Meshes.GroupBy(x => x.NodeIndex).ToList();

            foreach (var meshesByNode in meshesByNodes)
            {
                // This logic to detect similar material is kept from old code; this should be reviewed/improved at some point
                foreach (var meshesPerDrawCall in meshesByNode.GroupBy(x => x,
                                                                       new AnonymousEqualityComparer <Mesh>((x, y) =>
                                                                                                            x.MaterialIndex == y.MaterialIndex && // Same material
                                                                                                            ArrayExtensions.ArraysEqual(x.Skinning?.Bones, y.Skinning?.Bones) && // Same bones
                                                                                                            CompareParameters(model, x, y) && // Same parameters
                                                                                                            CompareShadowOptions(model, x, y), // Same shadow parameters
                                                                                                            x => 0)).ToList())
                {
                    if (meshesPerDrawCall.Count() == 1)
                    {
                        // Nothing to group, skip to next entry
                        continue;
                    }

                    // Remove old meshes
                    foreach (var mesh in meshesPerDrawCall)
                    {
                        model.Meshes.Remove(mesh);
                    }

                    // Add new combined mesh(es)
                    var baseMesh    = meshesPerDrawCall.First();
                    var newMeshList = meshesPerDrawCall.Select(x => x.Draw).ToList().GroupDrawData(Allow32BitIndex);

                    foreach (var generatedMesh in newMeshList)
                    {
                        model.Meshes.Add(new Mesh(generatedMesh, baseMesh.Parameters)
                        {
                            MaterialIndex = baseMesh.MaterialIndex,
                            Name          = baseMesh.Name,
                            Draw          = generatedMesh,
                            NodeIndex     = baseMesh.NodeIndex,
                            Skinning      = baseMesh.Skinning,
                        });
                    }
                }
            }

            // split the meshes if necessary
            model.Meshes = SplitExtensions.SplitMeshes(model.Meshes, Allow32BitIndex);

            // Refresh skeleton updater with asset skeleton
            hierarchyUpdater = new SkeletonUpdater(skeleton);
            hierarchyUpdater.UpdateMatrices();

            // bounding boxes
            var modelBoundingBox    = model.BoundingBox;
            var modelBoundingSphere = model.BoundingSphere;

            foreach (var mesh in model.Meshes)
            {
                var vertexBuffers = mesh.Draw.VertexBuffers;
                for (int vbIdx = 0; vbIdx < vertexBuffers.Length; vbIdx++)
                {
                    // Compute local mesh bounding box (no node transformation)
                    Matrix matrix = Matrix.Identity;
                    mesh.BoundingBox = vertexBuffers[vbIdx].ComputeBounds(ref matrix, out mesh.BoundingSphere);

                    // Compute model bounding box (includes node transformation)
                    hierarchyUpdater.GetWorldMatrix(mesh.NodeIndex, out matrix);
                    BoundingSphere meshBoundingSphere;
                    var            meshBoundingBox = vertexBuffers[vbIdx].ComputeBounds(ref matrix, out meshBoundingSphere);
                    BoundingBox.Merge(ref modelBoundingBox, ref meshBoundingBox, out modelBoundingBox);
                    BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere);
                }

                // TODO: temporary Always try to compact
                mesh.Draw.CompactIndexBuffer();
            }
            model.BoundingBox    = modelBoundingBox;
            model.BoundingSphere = modelBoundingSphere;

            // Count unique meshes (they can be shared)
            var uniqueDrawMeshes = model.Meshes.Select(x => x.Draw).Distinct();

            // Count unique vertex buffers and squish them together in a single buffer
            var uniqueVB = uniqueDrawMeshes.SelectMany(x => x.VertexBuffers).Distinct().ToList();

            var vbMap                    = new Dictionary <VertexBufferBinding, VertexBufferBinding>();
            var sizeVertexBuffer         = uniqueVB.Select(x => x.Buffer.GetSerializationData().Content.Length).Sum();
            var vertexBuffer             = new BufferData(BufferFlags.VertexBuffer, new byte[sizeVertexBuffer]);
            var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();

            var vertexBufferNextIndex = 0;

            foreach (var vbBinding in uniqueVB)
            {
                var oldVertexBuffer = vbBinding.Buffer.GetSerializationData().Content;
                Array.Copy(oldVertexBuffer, 0, vertexBuffer.Content, vertexBufferNextIndex, oldVertexBuffer.Length);

                vbMap.Add(vbBinding, new VertexBufferBinding(vertexBufferSerializable, vbBinding.Declaration, vbBinding.Count, vbBinding.Stride, vertexBufferNextIndex));

                vertexBufferNextIndex += oldVertexBuffer.Length;
            }

            // Count unique index buffers and squish them together in a single buffer
            var uniqueIB        = uniqueDrawMeshes.Select(x => x.IndexBuffer).NotNull().Distinct().ToList();
            var sizeIndexBuffer = 0;

            foreach (var ibBinding in uniqueIB)
            {
                // Make sure 32bit indices are properly aligned to 4 bytes in case the last alignment was 2 bytes
                if (ibBinding.Is32Bit && sizeIndexBuffer % 4 != 0)
                {
                    sizeIndexBuffer += 2;
                }

                sizeIndexBuffer += ibBinding.Buffer.GetSerializationData().Content.Length;
            }

            var ibMap = new Dictionary <IndexBufferBinding, IndexBufferBinding>();

            if (uniqueIB.Count > 0)
            {
                var indexBuffer             = new BufferData(BufferFlags.IndexBuffer, new byte[sizeIndexBuffer]);
                var indexBufferSerializable = indexBuffer.ToSerializableVersion();
                var indexBufferNextIndex    = 0;

                foreach (var ibBinding in uniqueIB)
                {
                    var oldIndexBuffer = ibBinding.Buffer.GetSerializationData().Content;

                    // Make sure 32bit indices are properly aligned to 4 bytes in case the last alignment was 2 bytes
                    if (ibBinding.Is32Bit && indexBufferNextIndex % 4 != 0)
                    {
                        indexBufferNextIndex += 2;
                    }

                    Array.Copy(oldIndexBuffer, 0, indexBuffer.Content, indexBufferNextIndex, oldIndexBuffer.Length);

                    ibMap.Add(ibBinding, new IndexBufferBinding(indexBufferSerializable, ibBinding.Is32Bit, ibBinding.Count, indexBufferNextIndex));

                    indexBufferNextIndex += oldIndexBuffer.Length;
                }
            }

            // Assign new vertex and index buffer bindings
            foreach (var drawMesh in uniqueDrawMeshes)
            {
                for (int i = 0; i < drawMesh.VertexBuffers.Length; i++)
                {
                    drawMesh.VertexBuffers[i] = vbMap[drawMesh.VertexBuffers[i]];
                }

                if (drawMesh.IndexBuffer != null)
                {
                    drawMesh.IndexBuffer = ibMap[drawMesh.IndexBuffer];
                }
            }

            vbMap.Clear();
            ibMap.Clear();

            // Convert to Entity
            return(model);
        }
Exemplo n.º 3
0
        private object ExportModel(ICommandContext commandContext, ContentManager contentManager)
        {
            // Read from model file
            var modelSkeleton = LoadSkeleton(commandContext, contentManager); // we get model skeleton to compare it to real skeleton we need to map to

            AdjustSkeleton(modelSkeleton);
            var model = LoadModel(commandContext, contentManager);

            // Apply materials
            foreach (var modelMaterial in Materials)
            {
                if (modelMaterial.MaterialInstance?.Material == null)
                {
                    commandContext.Logger.Warning($"The material [{modelMaterial.Name}] is null in the list of materials.");
                }
                model.Materials.Add(modelMaterial.MaterialInstance);
            }

            model.BoundingBox = BoundingBox.Empty;

            foreach (var mesh in model.Meshes)
            {
                if (TessellationAEN)
                {
                    // TODO: Generate AEN model view
                    commandContext.Logger.Error("TessellationAEN is not supported in {0}", ContextAsString);
                }
            }

            SkeletonMapping skeletonMapping;

            Skeleton skeleton;

            if (SkeletonUrl != null)
            {
                // Load skeleton and process it
                skeleton = contentManager.Load <Skeleton>(SkeletonUrl);

                // Assign skeleton to model
                model.Skeleton = AttachedReferenceManager.CreateProxyObject <Skeleton>(Guid.Empty, SkeletonUrl);
            }
            else
            {
                skeleton = null;
            }

            skeletonMapping = new SkeletonMapping(skeleton, modelSkeleton);

            // Refresh skeleton updater with model skeleton
            var hierarchyUpdater = new SkeletonUpdater(modelSkeleton);

            hierarchyUpdater.UpdateMatrices();

            // Move meshes in the new nodes
            foreach (var mesh in model.Meshes)
            {
                // Apply scale import on meshes
                if (!MathUtil.NearEqual(ScaleImport, 1.0f))
                {
                    var transformationMatrix = Matrix.Scaling(ScaleImport);
                    mesh.Draw.VertexBuffers[0].TransformBuffer(ref transformationMatrix);
                }

                var skinning = mesh.Skinning;
                if (skinning != null)
                {
                    // Update node mapping
                    // Note: we only remap skinning matrices, but we could directly remap skinning bones instead
                    for (int i = 0; i < skinning.Bones.Length; ++i)
                    {
                        var linkNodeIndex    = skinning.Bones[i].NodeIndex;
                        var newLinkNodeIndex = skeletonMapping.SourceToSource[linkNodeIndex];

                        var nodeIndex    = mesh.NodeIndex;
                        var newNodeIndex = skeletonMapping.SourceToSource[mesh.NodeIndex];

                        skinning.Bones[i].NodeIndex = skeletonMapping.SourceToTarget[linkNodeIndex];

                        // Adjust scale import
                        if (!MathUtil.NearEqual(ScaleImport, 1.0f))
                        {
                            skinning.Bones[i].LinkToMeshMatrix.TranslationVector = skinning.Bones[i].LinkToMeshMatrix.TranslationVector * ScaleImport;
                        }

                        // If it was remapped, we also need to update matrix
                        if (nodeIndex != newNodeIndex)
                        {
                            // Update mesh part
                            var transformMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, newNodeIndex, nodeIndex);
                            transformMatrix.Invert();
                            skinning.Bones[i].LinkToMeshMatrix = Matrix.Multiply(transformMatrix, skinning.Bones[i].LinkToMeshMatrix);
                        }

                        if (newLinkNodeIndex != linkNodeIndex)
                        {
                            // Update link part
                            var transformLinkMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, newLinkNodeIndex, linkNodeIndex);
                            skinning.Bones[i].LinkToMeshMatrix = Matrix.Multiply(skinning.Bones[i].LinkToMeshMatrix, transformLinkMatrix);
                        }
                    }
                }

                // Check if there was a remap using model skeleton
                if (skeletonMapping.SourceToSource[mesh.NodeIndex] != mesh.NodeIndex)
                {
                    // Transform vertices
                    var transformationMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, skeletonMapping.SourceToSource[mesh.NodeIndex], mesh.NodeIndex);
                    mesh.Draw.VertexBuffers[0].TransformBuffer(ref transformationMatrix);

                    // Check if geometry is inverted, to know if we need to reverse winding order
                    // TODO: What to do if there is no index buffer? We should create one... (not happening yet)
                    if (mesh.Draw.IndexBuffer == null)
                    {
                        throw new InvalidOperationException();
                    }

                    Matrix  rotation;
                    Vector3 scale, translation;
                    if (transformationMatrix.Decompose(out scale, out rotation, out translation) &&
                        scale.X * scale.Y * scale.Z < 0)
                    {
                        mesh.Draw.ReverseWindingOrder();
                    }
                }

                // Update new node index using real asset skeleton
                mesh.NodeIndex = skeletonMapping.SourceToTarget[mesh.NodeIndex];
            }

            // Merge meshes with same parent nodes, material and skinning
            var meshesByNodes = model.Meshes.GroupBy(x => x.NodeIndex).ToList();

            foreach (var meshesByNode in meshesByNodes)
            {
                // This logic to detect similar material is kept from old code; this should be reviewed/improved at some point
                foreach (var meshesPerDrawCall in meshesByNode.GroupBy(x => x,
                                                                       new AnonymousEqualityComparer <Mesh>((x, y) =>
                                                                                                            x.MaterialIndex == y.MaterialIndex && // Same material
                                                                                                            ArrayExtensions.ArraysEqual(x.Skinning?.Bones, y.Skinning?.Bones) && // Same bones
                                                                                                            CompareParameters(model, x, y) && // Same parameters
                                                                                                            CompareShadowOptions(model, x, y), // Same shadow parameters
                                                                                                            x => 0)).ToList())
                {
                    if (meshesPerDrawCall.Count() == 1)
                    {
                        // Nothing to group, skip to next entry
                        continue;
                    }

                    // Remove old meshes
                    foreach (var mesh in meshesPerDrawCall)
                    {
                        model.Meshes.Remove(mesh);
                    }

                    // Add new combined mesh(es)
                    var baseMesh    = meshesPerDrawCall.First();
                    var newMeshList = meshesPerDrawCall.Select(x => x.Draw).ToList().GroupDrawData(Allow32BitIndex);

                    foreach (var generatedMesh in newMeshList)
                    {
                        model.Meshes.Add(new Mesh(generatedMesh, baseMesh.Parameters)
                        {
                            MaterialIndex = baseMesh.MaterialIndex,
                            Name          = baseMesh.Name,
                            Draw          = generatedMesh,
                            NodeIndex     = baseMesh.NodeIndex,
                            Skinning      = baseMesh.Skinning,
                        });
                    }
                }
            }

            // split the meshes if necessary
            model.Meshes = SplitExtensions.SplitMeshes(model.Meshes, Allow32BitIndex);

            // Refresh skeleton updater with asset skeleton
            hierarchyUpdater = new SkeletonUpdater(skeleton);
            hierarchyUpdater.UpdateMatrices();

            // bounding boxes
            var modelBoundingBox    = model.BoundingBox;
            var modelBoundingSphere = model.BoundingSphere;

            foreach (var mesh in model.Meshes)
            {
                var vertexBuffers = mesh.Draw.VertexBuffers;
                if (vertexBuffers.Length > 0)
                {
                    // Compute local mesh bounding box (no node transformation)
                    Matrix matrix = Matrix.Identity;
                    mesh.BoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out mesh.BoundingSphere);

                    // Compute model bounding box (includes node transformation)
                    hierarchyUpdater.GetWorldMatrix(mesh.NodeIndex, out matrix);
                    BoundingSphere meshBoundingSphere;
                    var            meshBoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out meshBoundingSphere);
                    BoundingBox.Merge(ref modelBoundingBox, ref meshBoundingBox, out modelBoundingBox);
                    BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere);
                }

                // TODO: temporary Always try to compact
                mesh.Draw.CompactIndexBuffer();
            }
            model.BoundingBox    = modelBoundingBox;
            model.BoundingSphere = modelBoundingSphere;

            // merges all the Draw VB and IB together to produce one final VB and IB by entity.
            var sizeVertexBuffer = model.Meshes.SelectMany(x => x.Draw.VertexBuffers).Select(x => x.Buffer.GetSerializationData().Content.Length).Sum();
            var sizeIndexBuffer  = 0;

            foreach (var x in model.Meshes)
            {
                // Let's be aligned (if there was 16bit indices before, we might be off)
                if (x.Draw.IndexBuffer.Is32Bit && sizeIndexBuffer % 4 != 0)
                {
                    sizeIndexBuffer += 2;
                }

                sizeIndexBuffer += x.Draw.IndexBuffer.Buffer.GetSerializationData().Content.Length;
            }
            var vertexBuffer = new BufferData(BufferFlags.VertexBuffer, new byte[sizeVertexBuffer]);
            var indexBuffer  = new BufferData(BufferFlags.IndexBuffer, new byte[sizeIndexBuffer]);

            // Note: reusing same instance, to avoid having many VB with same hash but different URL
            var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();
            var indexBufferSerializable  = indexBuffer.ToSerializableVersion();

            var vertexBufferNextIndex = 0;
            var indexBufferNextIndex  = 0;

            foreach (var drawMesh in model.Meshes.Select(x => x.Draw))
            {
                // the index buffer
                var oldIndexBuffer = drawMesh.IndexBuffer.Buffer.GetSerializationData().Content;

                // Let's be aligned (if there was 16bit indices before, we might be off)
                if (drawMesh.IndexBuffer.Is32Bit && indexBufferNextIndex % 4 != 0)
                {
                    indexBufferNextIndex += 2;
                }

                Array.Copy(oldIndexBuffer, 0, indexBuffer.Content, indexBufferNextIndex, oldIndexBuffer.Length);

                drawMesh.IndexBuffer = new IndexBufferBinding(indexBufferSerializable, drawMesh.IndexBuffer.Is32Bit, drawMesh.IndexBuffer.Count, indexBufferNextIndex);

                indexBufferNextIndex += oldIndexBuffer.Length;

                // the vertex buffers
                for (int index = 0; index < drawMesh.VertexBuffers.Length; index++)
                {
                    var vertexBufferBinding = drawMesh.VertexBuffers[index];
                    var oldVertexBuffer     = vertexBufferBinding.Buffer.GetSerializationData().Content;

                    Array.Copy(oldVertexBuffer, 0, vertexBuffer.Content, vertexBufferNextIndex, oldVertexBuffer.Length);

                    drawMesh.VertexBuffers[index] = new VertexBufferBinding(vertexBufferSerializable, vertexBufferBinding.Declaration, vertexBufferBinding.Count, vertexBufferBinding.Stride,
                                                                            vertexBufferNextIndex);

                    vertexBufferNextIndex += oldVertexBuffer.Length;
                }
            }

            // Convert to Entity
            return(model);
        }
Exemplo n.º 4
0
        /// <summary>
        /// The method to override containing the actual command code. It is called by the <see cref="DoCommand" /> function
        /// </summary>
        /// <param name="commandContext">The command context.</param>
        /// <returns>Task{ResultStatus}.</returns>
        protected override async Task <ResultStatus> DoCommandOverride(ICommandContext commandContext)
        {
            var assetManager = new AssetManager();

            while (Interlocked.Increment(ref spawnedFbxCommands) >= 2)
            {
                Interlocked.Decrement(ref spawnedFbxCommands);
                await Task.Delay(1, CancellationToken);
            }

            try
            {
                object exportedObject;

                if (ExportType == "animation")
                {
                    // Read from model file
                    var animationClip = LoadAnimation(commandContext, assetManager);
                    exportedObject = animationClip;
                    if (animationClip == null)
                    {
                        commandContext.Logger.Info("File {0} has an empty animation.", SourcePath);
                    }
                    else if (animationClip.Duration.Ticks == 0)
                    {
                        commandContext.Logger.Warning("File {0} has a 0 tick long animation.", SourcePath);
                    }
                    else
                    {
                        animationClip.RepeatMode = AnimationRepeatMode;
                        animationClip.Optimize();
                    }
                }
                else if (ExportType == "model")
                {
                    // Read from model file
                    var model = LoadModel(commandContext, assetManager);

                    // Apply materials
                    foreach (var modelMaterial in Materials)
                    {
                        if (modelMaterial.MaterialInstance == null || modelMaterial.MaterialInstance.Material == null)
                        {
                            commandContext.Logger.Warning(string.Format("The material [{0}] is null in the list of materials.", modelMaterial.Name));
                            continue;
                        }
                        model.Materials.Add(modelMaterial.MaterialInstance);
                    }

                    model.BoundingBox = BoundingBox.Empty;
                    var hierarchyUpdater = new ModelViewHierarchyUpdater(model.Hierarchy.Nodes);
                    hierarchyUpdater.UpdateMatrices();

                    bool hasErrors = false;
                    foreach (var mesh in model.Meshes)
                    {
                        if (TessellationAEN)
                        {
                            // TODO: Generate AEN model view
                            commandContext.Logger.Error("TessellationAEN is not supported in {0}", ContextAsString);
                            hasErrors = true;
                        }
                    }

                    // split the meshes if necessary
                    model.Meshes = SplitExtensions.SplitMeshes(model.Meshes, Allow32BitIndex);

                    // merge the meshes
                    if (Compact)
                    {
                        var indicesBlackList = new HashSet <int>();
                        if (PreservedNodes != null)
                        {
                            for (var index = 0; index < model.Hierarchy.Nodes.Length; ++index)
                            {
                                var node = model.Hierarchy.Nodes[index];
                                if (PreservedNodes.Contains(node.Name))
                                {
                                    indicesBlackList.Add(index);
                                }
                            }
                        }

                        // group meshes with same material and same root
                        var sameMaterialMeshes = new List <GroupList <int, Mesh> >();
                        GroupFromIndex(model, 0, indicesBlackList, model.Meshes, sameMaterialMeshes);

                        // remove meshes that cannot be merged
                        var excludedMeshes  = new List <Mesh>();
                        var finalMeshGroups = new List <GroupList <int, Mesh> >();
                        foreach (var meshList in sameMaterialMeshes)
                        {
                            var mergeList = new GroupList <int, Mesh> {
                                Key = meshList.Key
                            };

                            foreach (var mesh in meshList)
                            {
                                if (mesh.Skinning != null || indicesBlackList.Contains(mesh.NodeIndex))
                                {
                                    excludedMeshes.Add(mesh);
                                }
                                else
                                {
                                    mergeList.Add(mesh);
                                }
                            }

                            if (mergeList.Count <= 1)
                            {
                                excludedMeshes.AddRange(mergeList);
                            }
                            else
                            {
                                finalMeshGroups.Add(mergeList);
                            }
                        }

                        var finalMeshes = new List <Mesh>();

                        finalMeshes.AddRange(excludedMeshes);

                        foreach (var meshList in finalMeshGroups)
                        {
                            // transform the buffers
                            foreach (var mesh in meshList)
                            {
                                var transformationMatrix = GetMatrixFromIndex(model.Hierarchy.Nodes, hierarchyUpdater, meshList.Key, mesh.NodeIndex);
                                mesh.Draw.VertexBuffers[0].TransformBuffer(ref transformationMatrix);
                            }

                            // refine the groups base on several tests
                            var newMeshGroups = new List <GroupList <int, Mesh> > {
                                meshList
                            };
                            // only regroup meshes if they share the same parameters
                            newMeshGroups = RefineGroups(model, newMeshGroups, CompareParameters);

                            // only regroup meshes if they share the shadow options
                            newMeshGroups = RefineGroups(model, newMeshGroups, CompareShadowOptions);

                            // add to the final meshes groups
                            foreach (var sameParamsMeshes in newMeshGroups)
                            {
                                var baseMesh    = sameParamsMeshes[0];
                                var newMeshList = sameParamsMeshes.Select(x => x.Draw).ToList().GroupDrawData(Allow32BitIndex);
                                foreach (var generatedMesh in newMeshList)
                                {
                                    finalMeshes.Add(new Mesh(generatedMesh, baseMesh.Parameters)
                                    {
                                        MaterialIndex = baseMesh.MaterialIndex,
                                        Name          = baseMesh.Name,
                                        Draw          = generatedMesh,
                                        NodeIndex     = meshList.Key,
                                        Skinning      = null,
                                    });
                                }
                            }
                        }

                        // delete empty nodes (neither mesh nor bone attached)
                        var keptNodes = new bool[model.Hierarchy.Nodes.Length];
                        for (var i = 0; i < keptNodes.Length; ++i)
                        {
                            keptNodes[i] = false;
                        }
                        foreach (var keepIndex in indicesBlackList)
                        {
                            var nodeIndex = keepIndex;
                            while (nodeIndex != -1 && !keptNodes[nodeIndex])
                            {
                                keptNodes[nodeIndex] = true;
                                nodeIndex            = model.Hierarchy.Nodes[nodeIndex].ParentIndex;
                            }
                        }
                        foreach (var mesh in finalMeshes)
                        {
                            var nodeIndex = mesh.NodeIndex;
                            while (nodeIndex != -1 && !keptNodes[nodeIndex])
                            {
                                keptNodes[nodeIndex] = true;
                                nodeIndex            = model.Hierarchy.Nodes[nodeIndex].ParentIndex;
                            }

                            if (mesh.Skinning != null)
                            {
                                foreach (var bone in mesh.Skinning.Bones)
                                {
                                    nodeIndex = bone.NodeIndex;
                                    while (nodeIndex != -1 && !keptNodes[nodeIndex])
                                    {
                                        keptNodes[nodeIndex] = true;
                                        nodeIndex            = model.Hierarchy.Nodes[nodeIndex].ParentIndex;
                                    }
                                }
                            }
                        }

                        var newNodes   = new List <ModelNodeDefinition>();
                        var newMapping = new int[model.Hierarchy.Nodes.Length];
                        for (var i = 0; i < keptNodes.Length; ++i)
                        {
                            if (keptNodes[i])
                            {
                                var parentIndex = model.Hierarchy.Nodes[i].ParentIndex;
                                if (parentIndex != -1)
                                {
                                    model.Hierarchy.Nodes[i].ParentIndex = newMapping[parentIndex]; // assume that the nodes are well ordered
                                }
                                newMapping[i] = newNodes.Count;
                                newNodes.Add(model.Hierarchy.Nodes[i]);
                            }
                        }

                        foreach (var mesh in finalMeshes)
                        {
                            mesh.NodeIndex = newMapping[mesh.NodeIndex];

                            if (mesh.Skinning != null)
                            {
                                for (var i = 0; i < mesh.Skinning.Bones.Length; ++i)
                                {
                                    mesh.Skinning.Bones[i].NodeIndex = newMapping[mesh.Skinning.Bones[i].NodeIndex];
                                }
                            }
                        }

                        model.Meshes          = finalMeshes;
                        model.Hierarchy.Nodes = newNodes.ToArray();

                        hierarchyUpdater = new ModelViewHierarchyUpdater(model.Hierarchy.Nodes);
                        hierarchyUpdater.UpdateMatrices();
                    }

                    // bounding boxes
                    var modelBoundingBox    = model.BoundingBox;
                    var modelBoundingSphere = model.BoundingSphere;
                    foreach (var mesh in model.Meshes)
                    {
                        var vertexBuffers = mesh.Draw.VertexBuffers;
                        if (vertexBuffers.Length > 0)
                        {
                            // Compute local mesh bounding box (no node transformation)
                            Matrix matrix = Matrix.Identity;
                            mesh.BoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out mesh.BoundingSphere);

                            // Compute model bounding box (includes node transformation)
                            hierarchyUpdater.GetWorldMatrix(mesh.NodeIndex, out matrix);
                            BoundingSphere meshBoundingSphere;
                            var            meshBoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out meshBoundingSphere);
                            BoundingBox.Merge(ref modelBoundingBox, ref meshBoundingBox, out modelBoundingBox);
                            BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere);
                        }

                        // TODO: temporary Always try to compact
                        mesh.Draw.CompactIndexBuffer();
                    }
                    model.BoundingBox    = modelBoundingBox;
                    model.BoundingSphere = modelBoundingSphere;

                    // merges all the Draw VB and IB together to produce one final VB and IB by entity.
                    var sizeVertexBuffer = model.Meshes.SelectMany(x => x.Draw.VertexBuffers).Select(x => x.Buffer.GetSerializationData().Content.Length).Sum();
                    var sizeIndexBuffer  = 0;
                    foreach (var x in model.Meshes)
                    {
                        // Let's be aligned (if there was 16bit indices before, we might be off)
                        if (x.Draw.IndexBuffer.Is32Bit && sizeIndexBuffer % 4 != 0)
                        {
                            sizeIndexBuffer += 2;
                        }

                        sizeIndexBuffer += x.Draw.IndexBuffer.Buffer.GetSerializationData().Content.Length;
                    }
                    var vertexBuffer = new BufferData(BufferFlags.VertexBuffer, new byte[sizeVertexBuffer]);
                    var indexBuffer  = new BufferData(BufferFlags.IndexBuffer, new byte[sizeIndexBuffer]);

                    // Note: reusing same instance, to avoid having many VB with same hash but different URL
                    var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();
                    var indexBufferSerializable  = indexBuffer.ToSerializableVersion();

                    var vertexBufferNextIndex = 0;
                    var indexBufferNextIndex  = 0;
                    foreach (var drawMesh in model.Meshes.Select(x => x.Draw))
                    {
                        // the index buffer
                        var oldIndexBuffer = drawMesh.IndexBuffer.Buffer.GetSerializationData().Content;

                        // Let's be aligned (if there was 16bit indices before, we might be off)
                        if (drawMesh.IndexBuffer.Is32Bit && indexBufferNextIndex % 4 != 0)
                        {
                            indexBufferNextIndex += 2;
                        }

                        Array.Copy(oldIndexBuffer, 0, indexBuffer.Content, indexBufferNextIndex, oldIndexBuffer.Length);

                        drawMesh.IndexBuffer = new IndexBufferBinding(indexBufferSerializable, drawMesh.IndexBuffer.Is32Bit, drawMesh.IndexBuffer.Count, indexBufferNextIndex);

                        indexBufferNextIndex += oldIndexBuffer.Length;

                        // the vertex buffers
                        for (int index = 0; index < drawMesh.VertexBuffers.Length; index++)
                        {
                            var vertexBufferBinding = drawMesh.VertexBuffers[index];
                            var oldVertexBuffer     = vertexBufferBinding.Buffer.GetSerializationData().Content;

                            Array.Copy(oldVertexBuffer, 0, vertexBuffer.Content, vertexBufferNextIndex, oldVertexBuffer.Length);

                            drawMesh.VertexBuffers[index] = new VertexBufferBinding(vertexBufferSerializable, vertexBufferBinding.Declaration, vertexBufferBinding.Count, vertexBufferBinding.Stride, vertexBufferNextIndex);

                            vertexBufferNextIndex += oldVertexBuffer.Length;
                        }
                    }


                    // If there were any errors while importing models
                    if (hasErrors)
                    {
                        return(ResultStatus.Failed);
                    }

                    // Convert to Entity
                    exportedObject = model;
                }
                else
                {
                    commandContext.Logger.Error("Unknown export type [{0}] {1}", ExportType, ContextAsString);
                    return(ResultStatus.Failed);
                }

                if (exportedObject != null)
                {
                    assetManager.Save(Location, exportedObject);
                }

                commandContext.Logger.Info("The {0} has been successfully imported.", ContextAsString);

                return(ResultStatus.Successful);
            }
            catch (Exception ex)
            {
                commandContext.Logger.Error("Unexpected error while importing {0}", ex, ContextAsString);
                return(ResultStatus.Failed);
            }
            finally
            {
                Interlocked.Decrement(ref spawnedFbxCommands);
            }
        }
Exemplo n.º 5
0
        private object ExportModel(ICommandContext commandContext, AssetManager assetManager)
        {
            // Read from model file
            var modelSkeleton = LoadSkeleton(commandContext, assetManager); // we get model skeleton to compare it to real skeleton we need to map to
            var model = LoadModel(commandContext, assetManager);

            // Apply materials
            foreach (var modelMaterial in Materials)
            {
                if (modelMaterial.MaterialInstance?.Material == null)
                {
                    commandContext.Logger.Warning($"The material [{modelMaterial.Name}] is null in the list of materials.");
                    continue;
                }
                model.Materials.Add(modelMaterial.MaterialInstance);
            }

            model.BoundingBox = BoundingBox.Empty;

            foreach (var mesh in model.Meshes)
            {
                if (TessellationAEN)
                {
                    // TODO: Generate AEN model view
                    commandContext.Logger.Error("TessellationAEN is not supported in {0}", ContextAsString);
                }
            }

            SkeletonMapping skeletonMapping;

            Skeleton skeleton;
            if (SkeletonUrl != null)
            {
                // Load skeleton and process it
                skeleton = assetManager.Load<Skeleton>(SkeletonUrl);

                // Assign skeleton to model
                model.Skeleton = AttachedReferenceManager.CreateSerializableVersion<Skeleton>(Guid.Empty, SkeletonUrl);
            }
            else
            {
                skeleton = null;

            }

            skeletonMapping = new SkeletonMapping(skeleton, modelSkeleton);

            // Refresh skeleton updater with model skeleton
            var hierarchyUpdater = new SkeletonUpdater(modelSkeleton);
            hierarchyUpdater.UpdateMatrices();

            // Move meshes in the new nodes
            foreach (var mesh in model.Meshes)
            {
                // Check if there was a remap using model skeleton
                if (skeletonMapping.SourceToSource[mesh.NodeIndex] != mesh.NodeIndex)
                {
                    // Transform vertices
                    var transformationMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, skeletonMapping.SourceToSource[mesh.NodeIndex], mesh.NodeIndex);
                    mesh.Draw.VertexBuffers[0].TransformBuffer(ref transformationMatrix);

                    // Check if geometry is inverted, to know if we need to reverse winding order
                    // TODO: What to do if there is no index buffer? We should create one... (not happening yet)
                    if (mesh.Draw.IndexBuffer == null)
                        throw new InvalidOperationException();

                    Matrix rotation;
                    Vector3 scale, translation;
                    if (transformationMatrix.Decompose(out scale, out rotation, out translation)
                        && scale.X * scale.Y * scale.Z < 0)
                    {
                        mesh.Draw.ReverseWindingOrder();
                    }
                }

                // Update new node index using real asset skeleton
                mesh.NodeIndex = skeletonMapping.SourceToTarget[mesh.NodeIndex];
            }

            // Merge meshes with same parent nodes, material and skinning
            var meshesByNodes = model.Meshes.GroupBy(x => x.NodeIndex).ToList();

            foreach (var meshesByNode in meshesByNodes)
            {
                // This logic to detect similar material is kept from old code; this should be reviewed/improved at some point
                foreach (var meshesPerDrawCall in meshesByNode.GroupBy(x => x,
                    new AnonymousEqualityComparer<Mesh>((x, y) =>
                    x.MaterialIndex == y.MaterialIndex // Same material
                    && ArrayExtensions.ArraysEqual(x.Skinning?.Bones, y.Skinning?.Bones) // Same bones
                    && CompareParameters(model, x, y) // Same parameters
                    && CompareShadowOptions(model, x, y), // Same shadow parameters
                    x => 0)).ToList())
                {
                    if (meshesPerDrawCall.Count() == 1)
                    {
                        // Nothing to group, skip to next entry
                        continue;
                    }

                    // Remove old meshes
                    foreach (var mesh in meshesPerDrawCall)
                    {
                        model.Meshes.Remove(mesh);
                    }

                    // Add new combined mesh(es)
                    var baseMesh = meshesPerDrawCall.First();
                    var newMeshList = meshesPerDrawCall.Select(x => x.Draw).ToList().GroupDrawData(Allow32BitIndex);

                    foreach (var generatedMesh in newMeshList)
                    {
                        model.Meshes.Add(new Mesh(generatedMesh, baseMesh.Parameters)
                        {
                            MaterialIndex = baseMesh.MaterialIndex,
                            Name = baseMesh.Name,
                            Draw = generatedMesh,
                            NodeIndex = baseMesh.NodeIndex,
                            Skinning = baseMesh.Skinning,
                        });
                    }
                }
            }

            // Remap skinning
            foreach (var skinning in model.Meshes.Select(x => x.Skinning).Where(x => x != null).Distinct())
            {
                // Update node mapping
                // Note: we only remap skinning matrices, but we could directly remap skinning bones instead
                for (int i = 0; i < skinning.Bones.Length; ++i)
                {
                    var nodeIndex = skinning.Bones[i].NodeIndex;
                    var newNodeIndex = skeletonMapping.SourceToSource[nodeIndex];

                    skinning.Bones[i].NodeIndex = skeletonMapping.SourceToTarget[nodeIndex];

                    // If it was remapped, we also need to update matrix
                    if (newNodeIndex != nodeIndex)
                    {
                        var transformationMatrix = CombineMatricesFromNodeIndices(hierarchyUpdater.NodeTransformations, newNodeIndex, nodeIndex);
                        skinning.Bones[i].LinkToMeshMatrix = Matrix.Multiply(skinning.Bones[i].LinkToMeshMatrix, transformationMatrix);
                    }
                }
            }

            // split the meshes if necessary
            model.Meshes = SplitExtensions.SplitMeshes(model.Meshes, Allow32BitIndex);

            // Refresh skeleton updater with asset skeleton
            hierarchyUpdater = new SkeletonUpdater(skeleton);
            hierarchyUpdater.UpdateMatrices();

            // bounding boxes
            var modelBoundingBox = model.BoundingBox;
            var modelBoundingSphere = model.BoundingSphere;
            foreach (var mesh in model.Meshes)
            {
                var vertexBuffers = mesh.Draw.VertexBuffers;
                if (vertexBuffers.Length > 0)
                {
                    // Compute local mesh bounding box (no node transformation)
                    Matrix matrix = Matrix.Identity;
                    mesh.BoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out mesh.BoundingSphere);

                    // Compute model bounding box (includes node transformation)
                    hierarchyUpdater.GetWorldMatrix(mesh.NodeIndex, out matrix);
                    BoundingSphere meshBoundingSphere;
                    var meshBoundingBox = vertexBuffers[0].ComputeBounds(ref matrix, out meshBoundingSphere);
                    BoundingBox.Merge(ref modelBoundingBox, ref meshBoundingBox, out modelBoundingBox);
                    BoundingSphere.Merge(ref modelBoundingSphere, ref meshBoundingSphere, out modelBoundingSphere);
                }

                // TODO: temporary Always try to compact
                mesh.Draw.CompactIndexBuffer();
            }
            model.BoundingBox = modelBoundingBox;
            model.BoundingSphere = modelBoundingSphere;

            // merges all the Draw VB and IB together to produce one final VB and IB by entity.
            var sizeVertexBuffer = model.Meshes.SelectMany(x => x.Draw.VertexBuffers).Select(x => x.Buffer.GetSerializationData().Content.Length).Sum();
            var sizeIndexBuffer = 0;
            foreach (var x in model.Meshes)
            {
                // Let's be aligned (if there was 16bit indices before, we might be off)
                if (x.Draw.IndexBuffer.Is32Bit && sizeIndexBuffer % 4 != 0)
                    sizeIndexBuffer += 2;

                sizeIndexBuffer += x.Draw.IndexBuffer.Buffer.GetSerializationData().Content.Length;
            }
            var vertexBuffer = new BufferData(BufferFlags.VertexBuffer, new byte[sizeVertexBuffer]);
            var indexBuffer = new BufferData(BufferFlags.IndexBuffer, new byte[sizeIndexBuffer]);

            // Note: reusing same instance, to avoid having many VB with same hash but different URL
            var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();
            var indexBufferSerializable = indexBuffer.ToSerializableVersion();

            var vertexBufferNextIndex = 0;
            var indexBufferNextIndex = 0;
            foreach (var drawMesh in model.Meshes.Select(x => x.Draw))
            {
                // the index buffer
                var oldIndexBuffer = drawMesh.IndexBuffer.Buffer.GetSerializationData().Content;

                // Let's be aligned (if there was 16bit indices before, we might be off)
                if (drawMesh.IndexBuffer.Is32Bit && indexBufferNextIndex % 4 != 0)
                    indexBufferNextIndex += 2;

                Array.Copy(oldIndexBuffer, 0, indexBuffer.Content, indexBufferNextIndex, oldIndexBuffer.Length);

                drawMesh.IndexBuffer = new IndexBufferBinding(indexBufferSerializable, drawMesh.IndexBuffer.Is32Bit, drawMesh.IndexBuffer.Count, indexBufferNextIndex);

                indexBufferNextIndex += oldIndexBuffer.Length;

                // the vertex buffers
                for (int index = 0; index < drawMesh.VertexBuffers.Length; index++)
                {
                    var vertexBufferBinding = drawMesh.VertexBuffers[index];
                    var oldVertexBuffer = vertexBufferBinding.Buffer.GetSerializationData().Content;

                    Array.Copy(oldVertexBuffer, 0, vertexBuffer.Content, vertexBufferNextIndex, oldVertexBuffer.Length);

                    drawMesh.VertexBuffers[index] = new VertexBufferBinding(vertexBufferSerializable, vertexBufferBinding.Declaration, vertexBufferBinding.Count, vertexBufferBinding.Stride,
                        vertexBufferNextIndex);

                    vertexBufferNextIndex += oldVertexBuffer.Length;
                }
            }

            // Convert to Entity
            return model;
        }
Exemplo n.º 6
0
            private static void ProcessMaterial(ContentManager manager, ICollection <EntityChunk> chunks, MaterialInstance material, Rendering.Model prefabModel)
            {
                //we need to futher group by VertexDeclaration
                var meshes = new Dictionary <VertexDeclaration, MeshData>();

                //actually create the mesh
                foreach (var chunk in chunks)
                {
                    foreach (var modelMesh in chunk.Model.Meshes)
                    {
                        //process only right material
                        if (modelMesh.MaterialIndex == chunk.MaterialIndex)
                        {
                            MeshData mesh;
                            if (!meshes.TryGetValue(modelMesh.Draw.VertexBuffers[0].Declaration, out mesh))
                            {
                                mesh = new MeshData {
                                    VertexStride = modelMesh.Draw.VertexBuffers[0].Stride
                                };
                                meshes.Add(modelMesh.Draw.VertexBuffers[0].Declaration, mesh);
                            }

                            //vertexes
                            var    vertexBufferRef = AttachedReferenceManager.GetAttachedReference(modelMesh.Draw.VertexBuffers[0].Buffer);
                            byte[] vertexData;
                            if (vertexBufferRef.Data != null)
                            {
                                vertexData = ((BufferData)vertexBufferRef.Data).Content;
                            }
                            else if (!vertexBufferRef.Url.IsNullOrEmpty())
                            {
                                var dataAsset = manager.Load <Graphics.Buffer>(vertexBufferRef.Url);
                                vertexData = dataAsset.GetSerializationData().Content;
                            }
                            else
                            {
                                throw new Exception($"Failed to get Vertex BufferData for entity {chunk.Entity.Name}'s model.");
                            }

                            //transform the vertexes according to the entity
                            var vertexDataCopy = vertexData.ToArray();
                            chunk.Entity.Transform.UpdateWorldMatrix(); //make sure matrix is computed
                            modelMesh.Draw.VertexBuffers[0].TransformBuffer(vertexDataCopy, ref chunk.Entity.Transform.WorldMatrix);

                            //add to the big single array
                            var vertexes = vertexDataCopy
                                           .Skip(modelMesh.Draw.VertexBuffers[0].Offset)
                                           .Take(modelMesh.Draw.VertexBuffers[0].Count * modelMesh.Draw.VertexBuffers[0].Stride)
                                           .ToArray();

                            mesh.VertexData.AddRange(vertexes);

                            //indices
                            var    indexBufferRef = AttachedReferenceManager.GetAttachedReference(modelMesh.Draw.IndexBuffer.Buffer);
                            byte[] indexData;
                            if (indexBufferRef.Data != null)
                            {
                                indexData = ((BufferData)indexBufferRef.Data).Content;
                            }
                            else if (!indexBufferRef.Url.IsNullOrEmpty())
                            {
                                var dataAsset = manager.Load <Graphics.Buffer>(indexBufferRef.Url);
                                indexData = dataAsset.GetSerializationData().Content;
                            }
                            else
                            {
                                throw new Exception("Failed to get Indices BufferData for entity {chunk.Entity.Name}'s model.");
                            }

                            var indexSize = modelMesh.Draw.IndexBuffer.Is32Bit ? sizeof(uint) : sizeof(ushort);

                            var indices = indexData
                                          .Skip(modelMesh.Draw.IndexBuffer.Offset)
                                          .Take(modelMesh.Draw.IndexBuffer.Count * indexSize)
                                          .ToArray();

                            //todo this code is not optimal, use unsafe

                            //must convert to 32bits
                            if (indexSize == sizeof(ushort))
                            {
                                var uintIndex = new List <byte>();
                                for (var i = 0; i < indices.Length; i += sizeof(ushort))
                                {
                                    var index = BitConverter.ToUInt16(indices, i);
                                    var bi    = BitConverter.GetBytes((uint)index);
                                    uintIndex.Add(bi[0]);
                                    uintIndex.Add(bi[1]);
                                    uintIndex.Add(bi[2]);
                                    uintIndex.Add(bi[3]);
                                }
                                indices = uintIndex.ToArray();
                            }

                            //need to offset the indices
                            for (var i = 0; i < indices.Length; i += sizeof(uint))
                            {
                                var index = BitConverter.ToUInt32(indices, i) + mesh.IndexOffset;
                                var bi    = BitConverter.GetBytes(index);
                                indices[i + 0] = bi[0];
                                indices[i + 1] = bi[1];
                                indices[i + 2] = bi[2];
                                indices[i + 3] = bi[3];
                            }

                            mesh.IndexOffset += modelMesh.Draw.VertexBuffers[0].Count;

                            mesh.IndexData.AddRange(indices);
                        }
                    }
                }

                //Sort out material
                var matIndex = prefabModel.Materials.Count;

                prefabModel.Materials.Add(material);

                foreach (var meshData in meshes)
                {
                    //todo need to take care of short index
                    var vertexArray = meshData.Value.VertexData.ToArray();
                    var indexArray  = meshData.Value.IndexData.ToArray();

                    var vertexCount = vertexArray.Length / meshData.Value.VertexStride;
                    var indexCount  = indexArray.Length / 4;

                    var gpuMesh = new Mesh
                    {
                        Draw = new MeshDraw {
                            PrimitiveType = PrimitiveType.TriangleList, DrawCount = indexCount, StartLocation = 0
                        },
                        MaterialIndex = matIndex
                    };

                    var vertexBuffer = new BufferData(BufferFlags.VertexBuffer, new byte[vertexArray.Length]);
                    var indexBuffer  = new BufferData(BufferFlags.IndexBuffer, new byte[indexArray.Length]);

                    var vertexBufferSerializable = vertexBuffer.ToSerializableVersion();
                    var indexBufferSerializable  = indexBuffer.ToSerializableVersion();

                    Array.Copy(vertexArray, vertexBuffer.Content, vertexArray.Length);
                    Array.Copy(indexArray, indexBuffer.Content, indexArray.Length);

                    gpuMesh.Draw.VertexBuffers    = new VertexBufferBinding[1];
                    gpuMesh.Draw.VertexBuffers[0] = new VertexBufferBinding(vertexBufferSerializable, meshData.Key, vertexCount);
                    gpuMesh.Draw.IndexBuffer      = new IndexBufferBinding(indexBufferSerializable, true, indexCount);

                    prefabModel.Meshes.Add(gpuMesh);
                }
            }