Example #1
0
        /// <summary>
        /// Create groups of mergeable meshes.
        /// </summary>
        /// <param name="model">The current model.</param>
        /// <param name="index">The index of the currently visited node.</param>
        /// <param name="nodeBlackList">List of the nodes that should be kept.</param>
        /// <param name="meshes">The meshes and their node index.</param>
        /// <param name="finalLists">List of mergeable meshes and their root node.</param>
        /// <returns>A list of mergeable meshes in progress.</returns>
        private Dictionary <Guid, List <MeshData> > GroupFromIndex(ModelData model, int index, HashSet <int> nodeBlackList, List <MeshData> meshes, List <GroupList <int, MeshData> > finalLists)
        {
            var children = GetChildren(model.Hierarchy.Nodes, index);

            var materialGroups = new Dictionary <Guid, List <MeshData> >();

            // Get the group from each child
            foreach (var child in children)
            {
                var newMaterialGroups = GroupFromIndex(model, child, nodeBlackList, meshes, finalLists);

                foreach (var group in newMaterialGroups)
                {
                    if (!materialGroups.ContainsKey(group.Key))
                    {
                        materialGroups.Add(group.Key, new List <MeshData>());
                    }
                    materialGroups[group.Key].AddRange(group.Value);
                }
            }

            // Add the current node if it has a mesh
            foreach (var nodeMesh in meshes.Where(x => x.NodeIndex == index))
            {
                var matId = nodeMesh.Material.Id;
                if (!materialGroups.ContainsKey(matId))
                {
                    materialGroups.Add(matId, new List <MeshData>());
                }
                materialGroups[matId].Add(nodeMesh);
            }

            // Store the generated list as final if the node should be kept
            if (nodeBlackList.Contains(index) || index == 0)
            {
                foreach (var materialGroup in materialGroups)
                {
                    var groupList = new GroupList <int, MeshData>();
                    groupList.Key = index;
                    groupList.AddRange(materialGroup.Value);
                    finalLists.Add(groupList);
                }
                materialGroups.Clear();
            }

            return(materialGroups);
        }
Example #2
0
        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);

                    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;
                            continue;
                        }

                        if (!Materials.ContainsKey(mesh.Name))
                        {
                            commandContext.Logger.Error("Mesh material [{0}] was not found in {1}", mesh.Name, ContextAsString);
                            hasErrors = true;
                            continue;
                        }

                        // set the material
                        var materialReference = Materials[mesh.Name];
                        mesh.Material = new ContentReference <MaterialData>(materialReference.Item1, materialReference.Item2);

                        // set the parameters
                        if (Parameters.ContainsKey(mesh.Name) && Parameters[mesh.Name] != null)
                        {
                            if (mesh.Parameters == null)
                            {
                                mesh.Parameters = new ParameterCollectionData();
                            }
                            foreach (var keyValue in Parameters[mesh.Name])
                            {
                                mesh.Parameters.Set(keyValue.Key, keyValue.Value);
                            }
                        }

                        // TODO: remove this when Lighting configuration will be behind a key in mesh parameters. This case will be handled by the code just above
                        // set the lighting configuration description
                        Tuple <Guid, string> lightingReference;
                        if (Lightings.TryGetValue(mesh.Name, out lightingReference))
                        {
                            mesh.Parameters.Set(LightingKeys.LightingConfigurations, new ContentReference <LightingConfigurationsSetData>(lightingReference.Item1, lightingReference.Item2));
                        }
                    }

                    // 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, MeshData> >();
                        GroupFromIndex(model, 0, indicesBlackList, model.Meshes, sameMaterialMeshes);

                        // remove meshes that cannot be merged
                        var excludedMeshes  = new List <MeshData>();
                        var finalMeshGroups = new List <GroupList <int, MeshData> >();
                        foreach (var meshList in sameMaterialMeshes)
                        {
                            var mergeList = new GroupList <int, MeshData> {
                                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 <MeshData>();

                        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, MeshData> > {
                                meshList
                            };
                            // only regroup meshes if they share the same parameters
                            newMeshGroups = RefineGroups(newMeshGroups, CompareParameters);
                            // only regroup meshes if they share the shadow options
                            newMeshGroups = RefineGroups(newMeshGroups, CompareShadowOptions);
                            //only regroup meshes if they share the same lighting configurations
                            newMeshGroups = RefineGroups(newMeshGroups, CompareLightingConfigurations);


                            // 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 MeshData {
                                        Material   = baseMesh.Material,
                                        Parameters = baseMesh.Parameters,
                                        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
                    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].ComputeBoundingBox(ref matrix);

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

                        // TODO: temporary Always try to compact
                        mesh.Draw.CompactIndexBuffer();
                    }

                    // 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.Value.Content.Length).Sum();
                    var sizeIndexBuffer       = model.Meshes.Select(x => x.Draw.IndexBuffer).Select(x => x.Buffer.Value.Content.Length).Sum();
                    var vertexBuffer          = new BufferData(BufferFlags.VertexBuffer, new byte[sizeVertexBuffer]);
                    var indexBuffer           = new BufferData(BufferFlags.IndexBuffer, new byte[sizeIndexBuffer]);
                    var vertexBufferNextIndex = 0;
                    var indexBufferNextIndex  = 0;
                    foreach (var drawMesh in model.Meshes.Select(x => x.Draw))
                    {
                        // the index buffer
                        var oldIndexBuffer = drawMesh.IndexBuffer.Buffer.Value.Content;

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

                        drawMesh.IndexBuffer.Offset = indexBufferNextIndex;
                        drawMesh.IndexBuffer.Buffer = indexBuffer;

                        indexBufferNextIndex += oldIndexBuffer.Length;

                        // the vertex buffers
                        foreach (var vertexBufferBinding in drawMesh.VertexBuffers)
                        {
                            var oldVertexBuffer = vertexBufferBinding.Buffer.Value.Content;

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

                            vertexBufferBinding.Offset = vertexBufferNextIndex;
                            vertexBufferBinding.Buffer = vertexBuffer;

                            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);
            }
        }