public static IReadOnlyList <Mesh> CreateMeshes <TMaterial>(this ModelRoot root, Converter <TMaterial, Material> materialConverter, Scenes.SceneBuilderSchema2Settings settings, params IMeshBuilder <TMaterial>[] meshBuilders) { Guard.NotNull(root, nameof(root)); Guard.NotNull(materialConverter, nameof(materialConverter)); Guard.NotNull(meshBuilders, nameof(meshBuilders)); Guard.IsTrue(meshBuilders.Length == meshBuilders.Distinct().Count(), nameof(meshBuilders), "The collection has repeated meshes."); foreach (var m in meshBuilders) { m.Validate(); } // create a new material for every unique (byRef) material in the mesh builders. var mapMaterials = meshBuilders .SelectMany(item => item.Primitives) .Where(item => !item.IsEmpty()) .Select(item => item.Material) .Distinct() .ToDictionary(m => m, m => materialConverter(m)); // create Schema2.Mesh collections for every gathered group. var srcMeshes = PackedMeshBuilder <TMaterial> .CreatePackedMeshes(meshBuilders, settings) .ToList(); PackedMeshBuilder <TMaterial> .MergeBuffers(srcMeshes); // create schema2 meshes var dstMeshes = new List <Mesh>(); foreach (var srcMesh in srcMeshes) { var dstMesh = srcMesh.CreateSchema2Mesh(root, m => mapMaterials[m]); dstMeshes.Add(dstMesh); } return(dstMeshes); }
/// <summary> /// Converts a collection of <see cref="IMeshBuilder{TMaterial}"/> meshes into a collection of <see cref="PackedMeshBuilder{TMaterial}"/> meshes, /// ensuring that the resources are shared across all meshes. /// </summary> /// <param name="meshBuilders">A collection of <see cref="IMeshBuilder{TMaterial}"/> meshes.</param> /// <param name="settings">Mesh packaging settings.</param> /// <returns>A collectio of <see cref="PackedMeshBuilder{TMaterial}"/> meshes.</returns> internal static IEnumerable <PackedMeshBuilder <TMaterial> > CreatePackedMeshes(IEnumerable <IMeshBuilder <TMaterial> > meshBuilders, Scenes.SceneBuilderSchema2Settings settings) { try { foreach (var m in meshBuilders) { m.Validate(); } } catch (Exception ex) { throw new ArgumentException(ex.Message, nameof(meshBuilders), ex); } var vertexEncodings = new PackedEncoding(); vertexEncodings.JointsEncoding = meshBuilders.GetOptimalJointEncoding(); vertexEncodings.WeightsEncoding = settings.CompactVertexWeights ? EncodingType.UNSIGNED_SHORT : EncodingType.FLOAT; var indexEncoding = meshBuilders.GetOptimalIndexEncoding(); foreach (var srcMesh in meshBuilders) { var dstMesh = new PackedMeshBuilder <TMaterial>(srcMesh.Name); foreach (var srcPrim in srcMesh.Primitives) { if (srcPrim.Vertices.Count == 0) { continue; } var dstPrim = dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive); bool useStrided = settings.UseStridedBuffers; if (srcPrim.MorphTargets.Count > 0) { useStrided = false; // if the primitive has morphing, it is better not to use strided vertex buffers. } if (useStrided) { dstPrim.SetStridedVertices(srcPrim, vertexEncodings); } else { dstPrim.SetStreamedVertices(srcPrim, vertexEncodings); } dstPrim.SetIndices(srcPrim, indexEncoding); dstPrim.SetMorphTargets(srcPrim, vertexEncodings); } yield return(dstMesh); } }
/// <summary> /// Converts a collection of <see cref="IMeshBuilder{TMaterial}"/> meshes into a collection of <see cref="PackedMeshBuilder{TMaterial}"/> meshes, /// ensuring that the resources are shared across all meshes. /// </summary> /// <param name="meshBuilders">A collection of <see cref="IMeshBuilder{TMaterial}"/> meshes.</param> /// <param name="settings">Mesh packaging settings.</param> /// <returns>A collectio of <see cref="PackedMeshBuilder{TMaterial}"/> meshes.</returns> internal static IEnumerable <PackedMeshBuilder <TMaterial> > CreatePackedMeshes(IEnumerable <IMeshBuilder <TMaterial> > meshBuilders, Scenes.SceneBuilderSchema2Settings settings) { try { foreach (var m in meshBuilders) { m.Validate(); } } catch (Exception ex) { throw new ArgumentException(ex.Message, nameof(meshBuilders), ex); } var vertexEncodings = new PackedEncoding(); vertexEncodings.JointsEncoding = meshBuilders.GetOptimalJointEncoding(); vertexEncodings.WeightsEncoding = settings.CompactVertexWeights ? EncodingType.UNSIGNED_SHORT : EncodingType.FLOAT; var indexEncoding = meshBuilders.GetOptimalIndexEncoding(); foreach (var srcMesh in meshBuilders) { // Gather all the primitives of the mesh var srcPrims = srcMesh .Primitives .Where(item => item.Vertices.Count > 0) .ToList(); // identify morph target attributes in use var morphTargetsAttributes = new HashSet <string>(); foreach (var srcPrim in srcPrims) { srcPrim._GatherMorphTargetAttributes(morphTargetsAttributes); } // adjust vertex encoding if (morphTargetsAttributes.Count > 0) { // if any primitive has morph targets, it is better not to use strided vertex buffers. settings.UseStridedBuffers = false; } bool hasColorMorph = morphTargetsAttributes.Contains("COLOR_0DELTA") || morphTargetsAttributes.Contains("COLOR_1DELTA") || morphTargetsAttributes.Contains("COLOR_2DELTA") || morphTargetsAttributes.Contains("COLOR_3DELTA"); // if any primitive has color morphing, we need to ensure the vertex // color attribute encoding is FLOAT to allow negative delta values. vertexEncodings.ColorEncoding = hasColorMorph ? EncodingType.FLOAT : (EncodingType?)null; // Create a packed mesh var dstMesh = new PackedMeshBuilder <TMaterial>(srcMesh.Name, srcMesh.Extras); foreach (var srcPrim in srcPrims) { var dstPrim = dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive); if (settings.UseStridedBuffers) { dstPrim.SetStridedVertices(srcPrim, vertexEncodings); } else { dstPrim.SetStreamedVertices(srcPrim, vertexEncodings); } dstPrim.SetIndices(srcPrim, indexEncoding); if (morphTargetsAttributes.Count > 0) { dstPrim.SetMorphTargets(srcPrim, vertexEncodings, morphTargetsAttributes); } } yield return(dstMesh); } }