Example #1
0
 public void PostLoad()
 {
     if (PrimaryVertexData.Vertices.Count > 0)
     {
         VertexFormat = PrimaryVertexData.Vertices[0].Format;
     }
 }
Example #2
0
        private Mesh ImportMesh(geometry geom, mesh mesh, VertexDescriptor vertexFormat)
        {
            var  collada   = new ColladaMesh();
            bool isSkinned = SkinnedMeshes.Contains(geom.id);

            collada.ImportFromCollada(mesh, vertexFormat, isSkinned, Options);

            var m = new Mesh();

            m.VertexFormat = collada.InternalVertexType;
            m.Name         = "Unnamed";

            m.PrimaryVertexData          = new VertexData();
            m.PrimaryVertexData.Vertices = collada.ConsolidatedVertices;

            if (!Options.StripMetadata)
            {
                var components = m.VertexFormat.ComponentNames().Select(s => new GrannyString(s)).ToList();
                m.PrimaryVertexData.VertexComponentNames = components;
            }
            else
            {
                m.PrimaryVertexData.VertexComponentNames = null;
            }

            m.PrimaryTopology         = new TriTopology();
            m.PrimaryTopology.Indices = collada.ConsolidatedIndices;
            m.PrimaryTopology.Groups  = new List <TriTopologyGroup>();
            var triGroup = new TriTopologyGroup();

            triGroup.MaterialIndex = 0;
            triGroup.TriFirst      = 0;
            triGroup.TriCount      = collada.TriangleCount;
            m.PrimaryTopology.Groups.Add(triGroup);

            m.MaterialBindings = new List <MaterialBinding>();
            m.MaterialBindings.Add(new MaterialBinding());

            // m.BoneBindings; - TODO

            m.OriginalToConsolidatedVertexIndexMap = collada.OriginalToConsolidatedVertexIndexMap;

            var divModelType = FindDivModelType(mesh);

            if (divModelType != 0)
            {
                m.ModelType            = divModelType;
                m.HasDefiniteModelType = true;
            }

            Utils.Info(String.Format("Imported {0} mesh ({1} tri groups, {2} tris)",
                                     (m.VertexFormat.HasBoneWeights ? "skinned" : "rigid"),
                                     m.PrimaryTopology.Groups.Count,
                                     collada.TriangleCount));

            return(m);
        }
Example #3
0
        private Mesh ImportMesh(Root root, string name, geometry geom, mesh mesh, VertexDescriptor vertexFormat)
        {
            var m = ImportMesh(geom, mesh, vertexFormat);

            m.Name = name;
            root.VertexDatas.Add(m.PrimaryVertexData);
            root.TriTopologies.Add(m.PrimaryTopology);
            root.Meshes.Add(m);
            return(m);
        }
Example #4
0
        public void PostLoad()
        {
            if (PrimaryVertexData.Vertices.Count > 0)
            {
                VertexFormat = PrimaryVertexData.Vertices[0].Format;
            }

            if (HasDefiniteModelType == false)
            {
                ModelType = DivinityHelpers.DetermineModelFlags(this, out HasDefiniteModelType);
            }
        }
        private VertexDescriptor FindVertexFormat(bool isSkinned)
        {
            var desc = new VertexDescriptor();

            desc.HasPosition = true;
            if (isSkinned)
            {
                desc.HasBoneWeights = true;
            }

            foreach (var input in Mesh.vertices.input)
            {
                switch (input.semantic)
                {
                case "NORMAL": desc.NormalType = NormalType.Float3; break;

                case "TANGENT": desc.TangentType = NormalType.Float3; break;

                case "BINORMAL": desc.BinormalType = NormalType.Float3; break;
                }
            }

            foreach (var input in Inputs)
            {
                switch (input.semantic)
                {
                case "NORMAL": desc.NormalType = NormalType.Float3; break;

                case "TANGENT": desc.TangentType = NormalType.Float3; break;

                case "BINORMAL": desc.BinormalType = NormalType.Float3; break;

                case "TEXCOORD":
                    desc.TextureCoordinateType = TextureCoordinateType.Float2;
                    desc.TextureCoordinates++;
                    break;

                case "COLOR":
                    desc.ColorMapType = ColorMapType.Float4;
                    desc.ColorMaps++;
                    break;
                }
            }

            return(desc);
        }
Example #6
0
        public Root Import(string inputPath)
        {
            var collada = COLLADA.Load(inputPath);
            var root    = new Root();

            root.ArtToolInfo = ImportArtToolInfo(collada);
            if (!Options.StripMetadata)
            {
                root.ExporterInfo = ImportExporterInfo(collada);
            }

            root.FromFileName = inputPath;

            root.Skeletons     = new List <Skeleton>();
            root.VertexDatas   = new List <VertexData>();
            root.TriTopologies = new List <TriTopology>();
            root.Meshes        = new List <Mesh>();
            root.Models        = new List <Model>();
            root.TrackGroups   = new List <TrackGroup>();
            root.Animations    = new List <Animation>();

            ColladaGeometries = new Dictionary <string, Mesh>();
            SkinnedMeshes     = new HashSet <string>();

            var collGeometries = new List <geometry>();
            var collSkins      = new List <skin>();
            var collAnimations = new List <animation>();
            var rootBones      = new List <RootBoneInfo>();

            // Import skinning controllers after skeleton and geometry loading has finished, as
            // we reference both of them during skin import
            foreach (var item in collada.Items)
            {
                if (item is library_controllers)
                {
                    var controllers = item as library_controllers;
                    if (controllers.controller != null)
                    {
                        foreach (var controller in controllers.controller)
                        {
                            if (controller.Item is skin)
                            {
                                collSkins.Add(controller.Item as skin);
                                SkinnedMeshes.Add((controller.Item as skin).source1.Substring(1));
                            }
                            else
                            {
                                Utils.Warn(String.Format("Controller {0} is unsupported and will be ignored", controller.Item.GetType().Name));
                            }
                        }
                    }
                }
                else if (item is library_visual_scenes)
                {
                    var scenes = item as library_visual_scenes;
                    if (scenes.visual_scene != null)
                    {
                        foreach (var scene in scenes.visual_scene)
                        {
                            if (scene.node != null)
                            {
                                foreach (var node in scene.node)
                                {
                                    FindRootBones(new List <node>(), node, rootBones);
                                }
                            }
                        }
                    }
                }
                else if (item is library_geometries)
                {
                    var geometries = item as library_geometries;
                    if (geometries.geometry != null)
                    {
                        foreach (var geometry in geometries.geometry)
                        {
                            if (geometry.Item is mesh)
                            {
                                collGeometries.Add(geometry);
                            }
                            else
                            {
                                Utils.Warn(String.Format("Geometry type {0} is unsupported and will be ignored", geometry.Item.GetType().Name));
                            }
                        }
                    }
                }
                else if (item is library_animations)
                {
                    var animations = item as library_animations;
                    if (animations.animation != null)
                    {
                        collAnimations.AddRange(animations.animation);
                    }
                }
                else
                {
                    Utils.Warn(String.Format("Library {0} is unsupported and will be ignored", item.GetType().Name));
                }
            }

            foreach (var bone in rootBones)
            {
                var skeleton      = Skeleton.FromCollada(bone.Bone);
                var rootTransform = NodeHelpers.GetTransformHierarchy(bone.Parents);
                skeleton.TransformRoots(rootTransform.Inverted());
                root.Skeletons.Add(skeleton);
            }

            foreach (var geometry in collGeometries)
            {
                VertexDescriptor vertexFormat = null;
                // Use the override vertex format, if one was specified
                Options.VertexFormats.TryGetValue(geometry.name, out vertexFormat);
                var mesh = ImportMesh(root, geometry.name, geometry, geometry.Item as mesh, vertexFormat);
                ColladaGeometries.Add(geometry.id, mesh);
            }

            // Import skinning controllers after skeleton and geometry loading has finished, as
            // we reference both of them during skin import
            if (rootBones.Count > 0)
            {
                foreach (var skin in collSkins)
                {
                    ImportSkin(root, skin);
                }
            }

            if (collAnimations.Count > 0)
            {
                ImportAnimations(collAnimations, root, root.Skeletons.FirstOrDefault());
            }

            var rootModel = new Model();

            rootModel.Name = "Unnamed"; // TODO
            if (root.Skeletons.Count > 0)
            {
                rootModel.Skeleton = root.Skeletons[0];
                rootModel.Name     = rootModel.Skeleton.Bones[0].Name;
            }
            rootModel.InitialPlacement = new Transform();
            rootModel.MeshBindings     = new List <MeshBinding>();
            foreach (var mesh in root.Meshes)
            {
                var binding = new MeshBinding();
                binding.Mesh = mesh;
                rootModel.MeshBindings.Add(binding);
            }

            root.Models.Add(rootModel);
            // TODO: make this an option!
            if (root.Skeletons.Count > 0)
            {
                root.Skeletons[0].UpdateWorldTransforms();
            }
            root.ZUp = ZUp;
            root.PostLoad(GR2.Header.DefaultTag);

            this.UpdateUserDefinedProperties(root);

            return(root);
        }
Example #7
0
        public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructDefinition defn, object parent)
        {
            var desc = new VertexDescriptor();

            foreach (var member in defn.Members)
            {
                switch (member.Name)
                {
                case "Position":
                    if (member.Type != MemberType.Real32 ||
                        member.ArraySize != 3)
                    {
                        throw new Exception("Vertex position must be a Vector3");
                    }
                    desc.HasPosition = true;
                    break;

                case "BoneWeights":
                    if (member.Type != MemberType.NormalUInt8)
                    {
                        throw new Exception("Bone weight must be a NormalUInt8");
                    }

                    if (member.ArraySize != 2 && member.ArraySize != 4)
                    {
                        throw new Exception($"Unsupported bone influence count: {member.ArraySize}");
                    }

                    desc.HasBoneWeights    = true;
                    desc.NumBoneInfluences = (int)member.ArraySize;
                    break;

                case "BoneIndices":
                    if (member.Type != MemberType.UInt8)
                    {
                        throw new Exception("Bone index must be an UInt8");
                    }
                    break;

                case "Normal":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.NormalType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else
                    {
                        throw new Exception($"Unsupported normal format: {member.Type}, {member.ArraySize}");
                    }
                    break;

                case "QTangent":
                    if (member.Type == MemberType.BinormalInt16 && member.ArraySize == 4)
                    {
                        desc.NormalType   = NormalType.QTangent;
                        desc.TangentType  = NormalType.QTangent;
                        desc.BinormalType = NormalType.QTangent;
                    }
                    else
                    {
                        throw new Exception($"Unsupported QTangent format: {member.Type}, {member.ArraySize}");
                    }
                    break;

                case "Tangent":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.TangentType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else
                    {
                        throw new Exception($"Unsupported tangent format: {member.Type}, {member.ArraySize}");
                    }
                    break;

                case "Binormal":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.BinormalType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else
                    {
                        throw new Exception($"Unsupported binormal format: {member.Type}, {member.ArraySize}");
                    }
                    break;

                case "DiffuseColor0":
                case "DiffuseColor1":
                    desc.ColorMaps++;
                    if (member.Type == MemberType.Real32 && member.ArraySize == 4)
                    {
                        desc.ColorMapType = ColorMapType.Float4;
                    }
                    else if (member.Type == MemberType.NormalUInt8 && member.ArraySize == 4)
                    {
                        desc.ColorMapType = ColorMapType.Byte4;
                    }
                    //Some Granny2 model formats report their color maps as UInt8 type instead of NormalUInt8, causing it to fail checks.
                    else if (member.Type == MemberType.UInt8 && member.ArraySize == 4)
                    {
                        desc.ColorMapType = ColorMapType.Byte4;
                    }
                    else
                    {
                        throw new Exception($"Unsupported color map type: {member.Type}, {member.ArraySize}");
                    }
                    break;

                case "TextureCoordinates0":
                case "TextureCoordinates1":
                case "TextureCoordinates2":
                case "TextureCoordinates3":
                    desc.TextureCoordinates++;
                    if (member.Type == MemberType.Real32 && member.ArraySize == 2)
                    {
                        desc.TextureCoordinateType = TextureCoordinateType.Float2;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 2)
                    {
                        desc.TextureCoordinateType = TextureCoordinateType.Half2;
                    }
                    else
                    {
                        throw new Exception($"Unsupported texture coordinate format: {member.Type}, {member.ArraySize}");
                    }
                    break;

                default:
                    throw new Exception($"Unknown vertex property: {member.Name}");
                }
            }

            return(desc);
        }
Example #8
0
        public VertexDescriptor ConstructDescriptor(MemberDefinition memberDefn, StructDefinition defn, object parent)
        {
            var desc = new VertexDescriptor();

            foreach (var member in defn.Members)
            {
                switch (member.Name)
                {
                case "Position":
                    if (member.Type != MemberType.Real32 ||
                        member.ArraySize != 3)
                    {
                        throw new Exception("Vertex position must be a Vector3");
                    }
                    desc.HasPosition = true;
                    break;

                case "BoneWeights":
                    if (member.Type != MemberType.NormalUInt8)
                    {
                        throw new Exception("Bone weight must be a NormalUInt8");
                    }

                    if (member.ArraySize != 2 && member.ArraySize != 4)
                    {
                        throw new Exception("Unsupported bone influence count");
                    }

                    desc.HasBoneWeights    = true;
                    desc.NumBoneInfluences = (int)member.ArraySize;
                    break;

                case "BoneIndices":
                    if (member.Type != MemberType.UInt8)
                    {
                        throw new Exception("Bone index must be an UInt8");
                    }
                    break;

                case "Normal":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.NormalType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else if (member.Type == MemberType.BinormalInt16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Short4;
                    }
                    else
                    {
                        throw new Exception("Unsupported vertex normal format");
                    }
                    break;

                case "QTangent":     // TODO - different format?
                case "Tangent":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.TangentType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else if (member.Type == MemberType.BinormalInt16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Short4;
                    }
                    else
                    {
                        throw new Exception("Unsupported vertex tangent format");
                    }
                    break;

                case "Binormal":
                    if (member.Type == MemberType.Real32 && member.ArraySize == 3)
                    {
                        desc.BinormalType = NormalType.Float3;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Half4;
                    }
                    else if (member.Type == MemberType.BinormalInt16 && member.ArraySize == 4)
                    {
                        desc.NormalType = NormalType.Short4;
                    }
                    else
                    {
                        throw new Exception("Unsupported vertex binormal format");
                    }
                    break;

                case "DiffuseColor0":
                    desc.DiffuseColors = 1;
                    if (member.Type == MemberType.Real32 && member.ArraySize == 4)
                    {
                        desc.DiffuseType = DiffuseColorType.Float4;
                    }
                    else if (member.Type == MemberType.NormalUInt8 && member.ArraySize == 4)
                    {
                        desc.DiffuseType = DiffuseColorType.Byte4;
                    }
                    else
                    {
                        throw new Exception("Unsupported vertex diffuse color type");
                    }
                    break;

                case "TextureCoordinates0":
                    desc.TextureCoordinates = 1;
                    if (member.Type == MemberType.Real32 && member.ArraySize == 2)
                    {
                        desc.TextureCoordinateType = TextureCoordinateType.Float2;
                    }
                    else if (member.Type == MemberType.Real16 && member.ArraySize == 2)
                    {
                        desc.TextureCoordinateType = TextureCoordinateType.Half2;
                    }
                    else
                    {
                        throw new Exception("Unsupported vertex binormal format");
                    }
                    break;

                case "TextureCoordinates1":
                    desc.TextureCoordinates = 2;
                    break;

                default:
                    throw new Exception($"Unknown vertex property: {member.Name}");
                }
            }

            return(desc);
        }
        public void ImportFromCollada(mesh mesh, VertexDescriptor vertexFormat, bool isSkinned, ExporterOptions options)
        {
            Options = options;
            Mesh    = mesh;
            ImportSources();
            ImportFaces();

            if (vertexFormat == null)
            {
                vertexFormat = FindVertexFormat(isSkinned);
            }

            InputVertexType  = vertexFormat;
            OutputVertexType = new VertexDescriptor
            {
                HasPosition           = InputVertexType.HasPosition,
                HasBoneWeights        = InputVertexType.HasBoneWeights,
                NumBoneInfluences     = InputVertexType.NumBoneInfluences,
                NormalType            = InputVertexType.NormalType,
                TangentType           = InputVertexType.TangentType,
                BinormalType          = InputVertexType.BinormalType,
                ColorMapType          = InputVertexType.ColorMapType,
                ColorMaps             = InputVertexType.ColorMaps,
                TextureCoordinateType = InputVertexType.TextureCoordinateType,
                TextureCoordinates    = InputVertexType.TextureCoordinates
            };

            ImportVertices();

            // TODO: This should be done before deduplication!
            // TODO: Move this to somewhere else ... ?
            if (!HasNormals || Options.RecalculateNormals)
            {
                if (!HasNormals)
                {
                    Utils.Info(String.Format("Channel 'NORMAL' not found, will rebuild vertex normals after import."));
                }

                HasNormals = true;
                OutputVertexType.NormalType = NormalType.Float3;
                computeNormals();
            }

            ImportColors();
            ImportUVs();

            if (UVInputIndices.Count() > 0 || ColorInputIndices.Count() > 0 ||
                NormalsInputIndex != -1 || TangentsInputIndex != -1 || BinormalsInputIndex != -1)
            {
                var outVertexIndices = new Dictionary <int[], int>(new VertexIndexComparer());
                ConsolidatedIndices  = new List <int>(TriangleCount * 3);
                ConsolidatedVertices = new List <Vertex>(Vertices.Count);
                OriginalToConsolidatedVertexIndexMap = new Dictionary <int, List <int> >();
                for (var vert = 0; vert < TriangleCount * 3; vert++)
                {
                    var index = new int[InputOffsetCount];
                    for (var i = 0; i < InputOffsetCount; i++)
                    {
                        index[i] = Indices[vert * InputOffsetCount + i];
                    }

                    int consolidatedIndex;
                    if (!outVertexIndices.TryGetValue(index, out consolidatedIndex))
                    {
                        var vertexIndex = index[VertexInputIndex];
                        consolidatedIndex = ConsolidatedVertices.Count;
                        Vertex vertex = Vertices[vertexIndex].Clone();
                        if (NormalsInputIndex != -1)
                        {
                            vertex.Normal = Normals[index[NormalsInputIndex]];
                        }
                        if (TangentsInputIndex != -1)
                        {
                            vertex.Tangent = Tangents[index[TangentsInputIndex]];
                        }
                        if (BinormalsInputIndex != -1)
                        {
                            vertex.Binormal = Binormals[index[BinormalsInputIndex]];
                        }
                        for (int uv = 0; uv < UVInputIndices.Count(); uv++)
                        {
                            vertex.SetUV(uv, UVs[uv][index[UVInputIndices[uv]]]);
                        }
                        for (int color = 0; color < ColorInputIndices.Count(); color++)
                        {
                            vertex.SetColor(color, Colors[color][index[ColorInputIndices[color]]]);
                        }
                        outVertexIndices.Add(index, consolidatedIndex);
                        ConsolidatedVertices.Add(vertex);

                        List <int> mappedIndices = null;
                        if (!OriginalToConsolidatedVertexIndexMap.TryGetValue(vertexIndex, out mappedIndices))
                        {
                            mappedIndices = new List <int>();
                            OriginalToConsolidatedVertexIndexMap.Add(vertexIndex, mappedIndices);
                        }

                        mappedIndices.Add(consolidatedIndex);
                    }

                    ConsolidatedIndices.Add(consolidatedIndex);
                }

                Utils.Info(String.Format("Merged {0} vertices into {1} output vertices", Vertices.Count, ConsolidatedVertices.Count));
            }
            else
            {
                Utils.Info(String.Format("Mesh has no separate normals, colors or UV map, vertex consolidation step skipped."));

                ConsolidatedVertices = Vertices;

                ConsolidatedIndices = new List <int>(TriangleCount * 3);
                for (var vert = 0; vert < TriangleCount * 3; vert++)
                {
                    ConsolidatedIndices.Add(VertexIndex(vert));
                }

                OriginalToConsolidatedVertexIndexMap = new Dictionary <int, List <int> >();
                for (var i = 0; i < Vertices.Count; i++)
                {
                    OriginalToConsolidatedVertexIndexMap.Add(i, new List <int> {
                        i
                    });
                }
            }

            if ((InputVertexType.TangentType == NormalType.None ||
                 InputVertexType.BinormalType == NormalType.None) &&
                ((!HasTangents && UVs.Count > 0) || Options.RecalculateTangents))
            {
                if (!HasTangents)
                {
                    Utils.Info(String.Format("Channel 'TANGENT'/'BINROMAL' not found, will rebuild vertex tangents after import."));
                }

                OutputVertexType.TangentType  = NormalType.Float3;
                OutputVertexType.BinormalType = NormalType.Float3;
                HasTangents = true;
                computeTangents();
            }

            // Use optimized tangent, texture map and color map format when exporting for D:OS 2
            if ((Options.ModelInfoFormat == DivinityModelInfoFormat.LSMv0 ||
                 Options.ModelInfoFormat == DivinityModelInfoFormat.LSMv1) &&
                HasNormals &&
                HasTangents)
            {
                OutputVertexType.NormalType   = NormalType.QTangent;
                OutputVertexType.TangentType  = NormalType.QTangent;
                OutputVertexType.BinormalType = NormalType.QTangent;

                if (OutputVertexType.TextureCoordinateType == TextureCoordinateType.Float2)
                {
                    OutputVertexType.TextureCoordinateType = TextureCoordinateType.Half2;
                }

                if (OutputVertexType.ColorMapType == ColorMapType.Float4)
                {
                    OutputVertexType.ColorMapType = ColorMapType.Byte4;
                }
            }
        }
        public static List <DivinityFormatDesc> FromVertexFormat(VertexDescriptor format)
        {
            var formats = new List <DivinityFormatDesc>();

            if (format.HasPosition)
            {
                formats.Add(Make(DivinityVertexUsage.Position, DivinityVertexAttributeFormat.Real32, 3));
            }

            if (format.HasBoneWeights)
            {
                formats.Add(Make(DivinityVertexUsage.BoneWeights, DivinityVertexAttributeFormat.NormalUInt8, (byte)format.NumBoneInfluences));
                formats.Add(Make(DivinityVertexUsage.BoneIndices, DivinityVertexAttributeFormat.UInt8, (byte)format.NumBoneInfluences));
            }

            if (format.NormalType != NormalType.None)
            {
                if (format.NormalType == NormalType.QTangent)
                {
                    formats.Add(Make(DivinityVertexUsage.QTangent, DivinityVertexAttributeFormat.BinormalInt16, 4));
                }
                else if (format.NormalType == NormalType.Float3)
                {
                    formats.Add(Make(DivinityVertexUsage.Normal, DivinityVertexAttributeFormat.Real32, 3));
                    if (format.TangentType == NormalType.Float3)
                    {
                        formats.Add(Make(DivinityVertexUsage.Tangent, DivinityVertexAttributeFormat.Real32, 3));
                    }
                    if (format.BinormalType == NormalType.Float3)
                    {
                        formats.Add(Make(DivinityVertexUsage.Binormal, DivinityVertexAttributeFormat.Real32, 3));
                    }
                }
                else
                {
                    throw new InvalidOperationException($"Normal format not supported in LSM: {format.NormalType}");
                }
            }

            if (format.ColorMapType != ColorMapType.None)
            {
                if (format.ColorMapType == ColorMapType.Byte4)
                {
                    for (int i = 0; i < format.ColorMaps; i++)
                    {
                        formats.Add(Make(DivinityVertexUsage.Color, DivinityVertexAttributeFormat.NormalUInt8, 4, (byte)i));
                    }
                }
                else if (format.ColorMapType == ColorMapType.Float4)
                {
                    for (int i = 0; i < format.ColorMaps; i++)
                    {
                        formats.Add(Make(DivinityVertexUsage.Color, DivinityVertexAttributeFormat.Real32, 4, (byte)i));
                    }
                }
                else
                {
                    throw new InvalidOperationException($"Color format not supported in LSM: {format.ColorMapType}");
                }
            }

            if (format.TextureCoordinateType != TextureCoordinateType.None)
            {
                if (format.TextureCoordinateType == TextureCoordinateType.Half2)
                {
                    for (int i = 0; i < format.TextureCoordinates; i++)
                    {
                        formats.Add(Make(DivinityVertexUsage.TexCoord, DivinityVertexAttributeFormat.Real16, 2, (byte)i));
                    }
                }
                else if (format.TextureCoordinateType == TextureCoordinateType.Float2)
                {
                    for (int i = 0; i < format.TextureCoordinates; i++)
                    {
                        formats.Add(Make(DivinityVertexUsage.TexCoord, DivinityVertexAttributeFormat.Real32, 2, (byte)i));
                    }
                }
                else
                {
                    throw new InvalidOperationException($"UV format not supported in LSM: {format.TextureCoordinateType}");
                }
            }

            return(formats);
        }