/// <summary> /// Refine the mesh groups based on the result of the delegate test method. /// </summary> /// <param name="meshList">The list of mesh groups.</param> /// <param name="sameGroupDelegate">The test delegate.</param> /// <returns>The new list of mesh groups.</returns> private List <GroupList <int, MeshData> > RefineGroups(List <GroupList <int, MeshData> > meshList, SameGroup sameGroupDelegate) { var finalGroups = new List <GroupList <int, MeshData> >(); foreach (var meshGroup in meshList) { var updatedGroups = new List <GroupList <int, MeshData> >(); foreach (var mesh in meshGroup) { var createNewGroup = true; foreach (var sameParamsMeshes in updatedGroups) { if (sameGroupDelegate(sameParamsMeshes[0], mesh)) { sameParamsMeshes.Add(mesh); createNewGroup = false; break; } } if (createNewGroup) { var newGroup = new GroupList <int, MeshData> { Key = meshGroup.Key }; newGroup.Add(mesh); updatedGroups.Add(newGroup); } } finalGroups.AddRange(updatedGroups); } return(finalGroups); }
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); } }