Description() public static method

public static Description ( Type type ) : VertexDescriptionAttribute
type System.Type
return VertexDescriptionAttribute
コード例 #1
0
        public bool IsSkinned()
        {
            // Check if we have both the BoneWeights and BoneIndices vertex components.
            bool hasWeights = false, hasIndices = false;

            // If we have vertices, check the vertex prototype, as VertexComponentNames is unreliable.
            if (PrimaryVertexData.Vertices.Count > 0)
            {
                var desc = Vertex.Description(PrimaryVertexData.Vertices[0].GetType());
                hasWeights = desc.BoneWeights;
                hasIndices = desc.BoneIndices;
            }
            else
            {
                // Otherwise try to figure out the components from VertexComponentNames
                foreach (var component in PrimaryVertexData.VertexComponentNames)
                {
                    if (component.String == "BoneWeights")
                    {
                        hasWeights = true;
                    }
                    else if (component.String == "BoneIndices")
                    {
                        hasIndices = true;
                    }
                }
            }

            return(hasWeights && hasIndices);
        }
コード例 #2
0
        private void DetermineInputsFromVertex(Vertex vertex)
        {
            var desc = Vertex.Description(vertex.GetType());

            if (!desc.Position)
            {
                throw new NotImplementedException("Cannot import vertices without position");
            }

            // Vertex positions
            var positions = ExportedMesh.PrimaryVertexData.MakeColladaPositions(ExportedMesh.Name);

            AddInput(positions, "POSITION", "VERTEX");

            // Normals
            if (desc.Normal && Options.ExportNormals)
            {
                var normals = ExportedMesh.PrimaryVertexData.MakeColladaNormals(ExportedMesh.Name);
                AddInput(normals, "NORMAL");
            }

            // Tangents
            if (desc.Tangent && Options.ExportTangents)
            {
                var normals = ExportedMesh.PrimaryVertexData.MakeColladaTangents(ExportedMesh.Name);
                AddInput(normals, "TANGENT");
            }

            // Binormals
            if (desc.Binormal && Options.ExportTangents)
            {
                var normals = ExportedMesh.PrimaryVertexData.MakeColladaBinormals(ExportedMesh.Name);
                AddInput(normals, "BINORMAL");
            }

            // Texture coordinates
            if (Options.ExportUVs)
            {
                for (var uvIndex = 0; uvIndex < desc.TextureCoordinates; uvIndex++)
                {
                    var uvs = ExportedMesh.PrimaryVertexData.MakeColladaUVs(ExportedMesh.Name, uvIndex, Options.FlipUVs);
                    AddInput(uvs, null, "TEXCOORD", (ulong)uvIndex);
                }
            }

            // Vertex colors
            if (Options.ExportColors)
            {
                for (var colorIndex = 0; colorIndex < desc.DiffuseColors; colorIndex++)
                {
                    var colors = ExportedMesh.PrimaryVertexData.MakeColladaColors(ExportedMesh.Name, colorIndex);
                    AddInput(colors, null, "COLOR", (ulong)colorIndex);
                }
            }

            // BoneWeights and BoneIndices are handled in ExportSkin()
            // TODO: DiffuseColor0 is not exported at the moment.
        }
コード例 #3
0
ファイル: Mesh.cs プロジェクト: xenogenesi/lslib
        public static Mesh ImportFromCollada(mesh mesh, string vertexFormat, bool rebuildNormals = false, bool rebuildTangents = false)
        {
            var collada = new ColladaMesh();

            collada.ImportFromCollada(mesh, vertexFormat, rebuildNormals, rebuildTangents);

            var m = new Mesh();

            m.VertexFormat = VertexFormatRegistry.Resolve(vertexFormat);
            m.Name         = "Unnamed";

            m.PrimaryVertexData = new VertexData();
            var components = new List <GrannyString>();

            components.Add(new GrannyString("Position"));

            var vertexDesc = Vertex.Description(m.VertexFormat);

            if (vertexDesc.BoneWeights)
            {
                components.Add(new GrannyString("BoneWeights"));
                components.Add(new GrannyString("BoneIndices"));
            }

            components.Add(new GrannyString("Normal"));
            components.Add(new GrannyString("Tangent"));
            components.Add(new GrannyString("Binormal"));
            components.Add(new GrannyString("MaxChannel_1"));
            m.PrimaryVertexData.VertexComponentNames = components;
            m.PrimaryVertexData.Vertices             = collada.ConsolidatedVertices;

            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;
            Utils.Info(String.Format("Imported {0} mesh ({1} tri groups, {2} tris)", (vertexDesc.BoneWeights ? "skinned" : "rigid"), m.PrimaryTopology.Groups.Count, collada.TriangleCount));

            return(m);
        }
コード例 #4
0
        public static DivinityModelType DetermineModelType(Root root)
        {
            // Check if one of the meshes already has a UserDefinedProperties attribute.
            if (root.Meshes != null)
            {
                foreach (var mesh in root.Meshes)
                {
                    if (mesh.ExtendedData != null &&
                        mesh.ExtendedData.UserDefinedProperties != null &&
                        mesh.ExtendedData.UserDefinedProperties.Length > 0)
                    {
                        return(UserDefinedPropertiesToModelType(mesh.ExtendedData.UserDefinedProperties));
                    }
                }
            }

            // Check if one of the bones already has a UserDefinedProperties attribute.
            if (root.Skeletons != null)
            {
                foreach (var skeleton in root.Skeletons)
                {
                    if (skeleton.Bones != null)
                    {
                        foreach (var bone in skeleton.Bones)
                        {
                            if (bone.ExtendedData != null &&
                                bone.ExtendedData.UserDefinedProperties != null &&
                                bone.ExtendedData.UserDefinedProperties.Length > 0)
                            {
                                return(UserDefinedPropertiesToModelType(bone.ExtendedData.UserDefinedProperties));
                            }
                        }
                    }
                }
            }

            // Check if any of the meshes has a rigid vertex format
            if (root.Meshes != null)
            {
                foreach (var mesh in root.Meshes)
                {
                    var isSkinned = Vertex.Description(mesh.VertexFormat).BoneWeights;
                    if (!isSkinned)
                    {
                        return(DivinityModelType.Rigid);
                    }
                }
            }

            return(DivinityModelType.Normal);
        }
コード例 #5
0
        public SectionType SelectSection(MemberDefinition member, Type type, object obj)
        {
            var vertices = obj as System.Collections.IList;

            if (vertices == null || vertices.Count == 0)
            {
                return(SectionType.RigidVertex);
            }

            if (Vertex.Description(vertices[0].GetType()).BoneWeights)
            {
                return(SectionType.DeformableVertex);
            }
            else
            {
                return(SectionType.RigidVertex);
            }
        }
コード例 #6
0
ファイル: Mesh.cs プロジェクト: xenogenesi/lslib
        public void Deduplicate(List <Vertex> vertices)
        {
            var positions = new Dictionary <Vector3, int>(new VertexPositionComparer());

            for (var i = 0; i < vertices.Count; i++)
            {
                int mappedIndex;
                if (!positions.TryGetValue(vertices[i].Position, out mappedIndex))
                {
                    mappedIndex = positions.Count;
                    positions.Add(vertices[i].Position, mappedIndex);
                    DeduplicatedPositions.Add(vertices[i]);
                }

                VertexDeduplicationMap.Add(i, mappedIndex);
            }

            var numUvs = Vertex.Description(vertices[0].GetType()).TextureCoordinates;

            for (var uv = 0; uv < numUvs; uv++)
            {
                var uvMap           = new Dictionary <int, int>();
                var deduplicatedUvs = new List <Vector2>();
                UVDeduplicationMaps.Add(uvMap);
                DeduplicatedUVs.Add(deduplicatedUvs);

                var uvs = new Dictionary <Vector2, int>(new VertexUVComparer());
                for (var i = 0; i < vertices.Count; i++)
                {
                    int mappedIndex;
                    if (!uvs.TryGetValue(vertices[i].GetTextureCoordinates(uv), out mappedIndex))
                    {
                        mappedIndex = uvs.Count;
                        uvs.Add(vertices[i].GetTextureCoordinates(uv), mappedIndex);
                        deduplicatedUvs.Add(vertices[i].GetTextureCoordinates(uv));
                    }

                    uvMap.Add(i, mappedIndex);
                }
            }
        }
コード例 #7
0
        public void MakeIdentityMapping(List <Vertex> vertices)
        {
            for (var i = 0; i < vertices.Count; i++)
            {
                DeduplicatedPositions.Add(vertices[i]);
                VertexDeduplicationMap.Add(i, i);
            }

            var numUvs = Vertex.Description(vertices[0].GetType()).TextureCoordinates;

            for (var uv = 0; uv < numUvs; uv++)
            {
                var uvMap           = new Dictionary <int, int>();
                var deduplicatedUvs = new List <Vector2>();
                UVDeduplicationMaps.Add(uvMap);
                DeduplicatedUVs.Add(deduplicatedUvs);

                for (var i = 0; i < vertices.Count; i++)
                {
                    deduplicatedUvs.Add(vertices[i].GetUV(uv));
                    uvMap.Add(i, i);
                }
            }

            var numColors = Vertex.Description(vertices[0].GetType()).DiffuseColors;

            for (var color = 0; color < numColors; color++)
            {
                var colorMap           = new Dictionary <int, int>();
                var deduplicatedColors = new List <Vector4>();
                ColorDeduplicationMaps.Add(colorMap);
                DeduplicatedColors.Add(deduplicatedColors);

                for (var i = 0; i < vertices.Count; i++)
                {
                    deduplicatedColors.Add(vertices[i].GetColor(color));
                    colorMap.Add(i, i);
                }
            }
        }
コード例 #8
0
ファイル: Root.cs プロジェクト: xenogenesi/lslib
        private void ImportSkin(skin skin)
        {
            if (skin.source1[0] != '#')
            {
                throw new ParsingException("Only ID references are supported for skin geometries");
            }

            Mesh mesh = null;

            if (!ColladaGeometries.TryGetValue(skin.source1.Substring(1), out mesh))
            {
                throw new ParsingException("Skin references nonexistent mesh: " + skin.source1);
            }

            if (!Vertex.Description(mesh.VertexFormat).BoneWeights)
            {
                var msg = String.Format("Tried to apply skin to mesh ({0}) with non-skinned vertices ({1})", mesh.Name, mesh.VertexFormat.Name);
                throw new ParsingException(msg);
            }

            var sources = new Dictionary <String, Source>();

            foreach (var source in skin.source)
            {
                var src = Source.FromCollada(source);
                sources.Add(src.id, src);
            }

            List <Bone>    joints          = null;
            List <Matrix4> invBindMatrices = null;

            foreach (var input in skin.joints.input)
            {
                if (input.source[0] != '#')
                {
                    throw new ParsingException("Only ID references are supported for joint input sources");
                }

                Source inputSource = null;
                if (!sources.TryGetValue(input.source.Substring(1), out inputSource))
                {
                    throw new ParsingException("Joint input source does not exist: " + input.source);
                }

                if (input.semantic == "JOINT")
                {
                    List <string> jointNames = inputSource.NameParams.Values.SingleOrDefault();
                    if (jointNames == null)
                    {
                        throw new ParsingException("Joint input source 'JOINT' must contain array of names.");
                    }

                    var skeleton = Skeletons.Single();
                    joints = new List <Bone>();
                    foreach (var name in jointNames)
                    {
                        Bone bone       = null;
                        var  lookupName = name.Replace("_x0020_", " ");
                        if (!skeleton.BonesBySID.TryGetValue(lookupName, out bone))
                        {
                            throw new ParsingException("Joint name list references nonexistent bone: " + lookupName);
                        }

                        joints.Add(bone);
                    }
                }
                else if (input.semantic == "INV_BIND_MATRIX")
                {
                    invBindMatrices = inputSource.MatrixParams.Values.SingleOrDefault();
                    if (invBindMatrices == null)
                    {
                        throw new ParsingException("Joint input source 'INV_BIND_MATRIX' must contain a single array of matrices.");
                    }
                }
                else
                {
                    throw new ParsingException("Unsupported joint semantic: " + input.semantic);
                }
            }

            if (joints == null)
            {
                throw new ParsingException("Required joint input semantic missing: JOINT");
            }

            if (invBindMatrices == null)
            {
                throw new ParsingException("Required joint input semantic missing: INV_BIND_MATRIX");
            }

            var influenceCounts = ColladaHelpers.StringsToIntegers(skin.vertex_weights.vcount);
            var influences      = ColladaHelpers.StringsToIntegers(skin.vertex_weights.v);

            foreach (var count in influenceCounts)
            {
                if (count > 4)
                {
                    throw new ParsingException("GR2 only supports at most 4 vertex influences");
                }
            }

            // TODO
            if (influenceCounts.Count != mesh.OriginalToConsolidatedVertexIndexMap.Count)
            {
                Utils.Warn(String.Format("Vertex influence count ({0}) differs from vertex count ({1})", influenceCounts.Count, mesh.OriginalToConsolidatedVertexIndexMap.Count));
            }

            List <Single> weights = null;

            int jointInputIndex = -1, weightInputIndex = -1;

            foreach (var input in skin.vertex_weights.input)
            {
                if (input.semantic == "JOINT")
                {
                    jointInputIndex = (int)input.offset;
                }
                else if (input.semantic == "WEIGHT")
                {
                    weightInputIndex = (int)input.offset;

                    if (input.source[0] != '#')
                    {
                        throw new ParsingException("Only ID references are supported for weight input sources");
                    }

                    Source inputSource = null;
                    if (!sources.TryGetValue(input.source.Substring(1), out inputSource))
                    {
                        throw new ParsingException("Weight input source does not exist: " + input.source);
                    }

                    if (!inputSource.FloatParams.TryGetValue("WEIGHT", out weights))
                    {
                        weights = inputSource.FloatParams.Values.SingleOrDefault();
                    }

                    if (weights == null)
                    {
                        throw new ParsingException("Weight input source " + input.source + " must have WEIGHT float attribute");
                    }
                }
                else
                {
                    throw new ParsingException("Unsupported skin input semantic: " + input.semantic);
                }
            }

            if (jointInputIndex == -1)
            {
                throw new ParsingException("Required vertex weight input semantic missing: JOINT");
            }

            if (weightInputIndex == -1)
            {
                throw new ParsingException("Required vertex weight input semantic missing: WEIGHT");
            }

            // Remove bones that are not actually influenced from the binding list
            var boundBones = new HashSet <Bone>();
            int offset     = 0;
            int stride     = skin.vertex_weights.input.Length;

            while (offset < influences.Count)
            {
                var jointIndex  = influences[offset + jointInputIndex];
                var weightIndex = influences[offset + weightInputIndex];
                var joint       = joints[jointIndex];
                var weight      = weights[weightIndex];
                if (!boundBones.Contains(joint))
                {
                    boundBones.Add(joint);
                }

                offset += stride;
            }

            if (boundBones.Count > 255)
            {
                throw new ParsingException("GR2 supports at most 255 bound bones per mesh.");
            }

            mesh.BoneBindings = new List <BoneBinding>();
            var boneToIndexMaps = new Dictionary <Bone, int>();

            for (var i = 0; i < joints.Count; i++)
            {
                if (boundBones.Contains(joints[i]))
                {
                    // Collada allows one inverse bind matrix for each skin, however Granny
                    // only has one matrix for one bone, even if said bone is used from multiple meshes.
                    // Hopefully the Collada ones are all equal ...
                    var iwt = invBindMatrices[i];
                    // iwt.Transpose();
                    joints[i].InverseWorldTransform = new float[] {
                        iwt[0, 0], iwt[1, 0], iwt[2, 0], iwt[3, 0],
                        iwt[0, 1], iwt[1, 1], iwt[2, 1], iwt[3, 1],
                        iwt[0, 2], iwt[1, 2], iwt[2, 2], iwt[3, 2],
                        iwt[0, 3], iwt[1, 3], iwt[2, 3], iwt[3, 3]
                    };

                    // Bind all bones that affect vertices to the mesh, so we can reference them
                    // later from the vertexes BoneIndices.
                    var binding = new BoneBinding();
                    binding.BoneName = joints[i].Name;
                    // TODO
                    binding.OBBMin = new float[] { -10, -10, -10 };
                    binding.OBBMax = new float[] { 10, 10, 10 };
                    mesh.BoneBindings.Add(binding);
                    boneToIndexMaps.Add(joints[i], boneToIndexMaps.Count);
                }
            }

            offset = 0;
            for (var vertexIndex = 0; vertexIndex < influenceCounts.Count; vertexIndex++)
            {
                var influenceCount = influenceCounts[vertexIndex];
                for (var i = 0; i < influenceCount; i++)
                {
                    var jointIndex  = influences[offset + jointInputIndex];
                    var weightIndex = influences[offset + weightInputIndex];
                    var joint       = joints[jointIndex];
                    var weight      = weights[weightIndex];
                    foreach (var consolidatedIndex in mesh.OriginalToConsolidatedVertexIndexMap[vertexIndex])
                    {
                        var vertex = mesh.PrimaryVertexData.Vertices[consolidatedIndex];
                        vertex.AddInfluence((byte)boneToIndexMaps[joint], weight);
                    }

                    offset += stride;
                }
            }

            foreach (var vertex in mesh.PrimaryVertexData.Vertices)
            {
                vertex.FinalizeInfluences();
            }

            // Warn if we have vertices that are not influenced by any bone
            int notInfluenced = 0;

            foreach (var vertex in mesh.PrimaryVertexData.Vertices)
            {
                if (vertex.BoneWeights[0] == 0)
                {
                    notInfluenced++;
                }
            }

            if (notInfluenced > 0)
            {
                Utils.Warn(String.Format("{0} vertices are not influenced by any bone", notInfluenced));
            }


            if (skin.bind_shape_matrix != null)
            {
                var bindShapeFloats = skin.bind_shape_matrix.Split(new char[] { ' ' }).Select(s => Single.Parse(s)).ToArray();
                var mat             = ColladaHelpers.FloatsToMatrix(bindShapeFloats);
                mat.Transpose();

                BindShapeMatrix = mat;

                // Deform geometries that were affected by our bind shape matrix
                foreach (var vertex in mesh.PrimaryVertexData.Vertices)
                {
                    vertex.Transform(BindShapeMatrix);
                }
            }
            else
            {
                BindShapeMatrix = Matrix4.Identity;
            }
        }
コード例 #9
0
        private Mesh ImportMesh(geometry geom, mesh mesh, string 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();
            var components = new List <GrannyString>();

            components.Add(new GrannyString("Position"));

            var vertexDesc = Vertex.Description(m.VertexFormat);

            if (vertexDesc.BoneWeights)
            {
                components.Add(new GrannyString("BoneWeights"));
                components.Add(new GrannyString("BoneIndices"));
            }

            if (vertexDesc.Normal)
            {
                components.Add(new GrannyString("Normal"));
            }

            if (vertexDesc.Tangent)
            {
                components.Add(new GrannyString("Tangent"));
            }

            if (vertexDesc.Binormal)
            {
                components.Add(new GrannyString("Binormal"));
            }

            for (int i = 0; i < vertexDesc.DiffuseColors; i++)
            {
                components.Add(new GrannyString("DiffuseColor" + i.ToString()));
            }

            for (int i = 0; i < vertexDesc.TextureCoordinates; i++)
            {
                components.Add(new GrannyString("TextureCoordinate" + i.ToString()));
            }

            m.PrimaryVertexData.VertexComponentNames = components;
            m.PrimaryVertexData.Vertices             = collada.ConsolidatedVertices;

            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;
            Utils.Info(String.Format("Imported {0} mesh ({1} tri groups, {2} tris)", (vertexDesc.BoneWeights ? "skinned" : "rigid"), m.PrimaryTopology.Groups.Count, collada.TriangleCount));

            return(m);
        }
コード例 #10
0
ファイル: ColladaMesh.cs プロジェクト: xenogenesi/lslib
        void computeTangents()
        {
            // Check if the vertex format has at least one UV set
            if (ConsolidatedVertices.Count() > 0)
            {
                var v          = ConsolidatedVertices[0];
                var descriptor = Vertex.Description(v.GetType());
                if (descriptor.TextureCoordinates == 0)
                {
                    throw new InvalidOperationException("At least one UV set is required to recompute tangents");
                }
            }

            foreach (var v in ConsolidatedVertices)
            {
                v.Tangent  = Vector3.Zero;
                v.Binormal = Vector3.Zero;
            }

            for (int i = 0; i < TriangleCount; i++)
            {
                var i1 = ConsolidatedIndices[i * 3 + 0];
                var i2 = ConsolidatedIndices[i * 3 + 1];
                var i3 = ConsolidatedIndices[i * 3 + 2];

                var vert1 = ConsolidatedVertices[i1];
                var vert2 = ConsolidatedVertices[i2];
                var vert3 = ConsolidatedVertices[i3];

                var v1 = vert1.Position;
                var v2 = vert2.Position;
                var v3 = vert3.Position;

                var w1 = vert1.TextureCoordinates0;
                var w2 = vert2.TextureCoordinates0;
                var w3 = vert3.TextureCoordinates0;

                float x1 = v2.X - v1.X;
                float x2 = v3.X - v1.X;
                float y1 = v2.Y - v1.Y;
                float y2 = v3.Y - v1.Y;
                float z1 = v2.Z - v1.Z;
                float z2 = v3.Z - v1.Z;

                float s1 = w2.X - w1.X;
                float s2 = w3.X - w1.X;
                float t1 = w2.Y - w1.Y;
                float t2 = w3.Y - w1.Y;

                float r    = 1.0F / (s1 * t2 - s2 * t1);
                var   sdir = new Vector3(
                    (t2 * x1 - t1 * x2) * r,
                    (t2 * y1 - t1 * y2) * r,
                    (t2 * z1 - t1 * z2) * r
                    );
                var tdir = new Vector3(
                    (s1 * x2 - s2 * x1) * r,
                    (s1 * y2 - s2 * y1) * r,
                    (s1 * z2 - s2 * z1) * r
                    );

                vert1.Tangent += sdir;
                vert2.Tangent += sdir;
                vert3.Tangent += sdir;

                vert1.Binormal += tdir;
                vert2.Binormal += tdir;
                vert3.Binormal += tdir;
            }

            foreach (var v in ConsolidatedVertices)
            {
                var n = v.Normal;
                var t = v.Tangent;
                var b = v.Binormal;

                // Gram-Schmidt orthogonalize
                var tangent = (t - n * Vector3.Dot(n, t)).Normalized();

                // Calculate handedness
                var w        = (Vector3.Dot(Vector3.Cross(n, t), b) < 0.0F) ? 1.0F : -1.0F;
                var binormal = (Vector3.Cross(n, t) * w).Normalized();

                v.Tangent  = tangent;
                v.Binormal = binormal;
            }
        }
コード例 #11
0
        public void ImportFromCollada(mesh mesh, string vertexFormat, bool isSkinned, ExporterOptions options)
        {
            Options = options;
            Mesh    = mesh;
            ImportSources();
            ImportFaces();

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

            VertexType = VertexFormatRegistry.Resolve(vertexFormat);
            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."));
                }
                computeNormals();
            }

            ImportColors();
            ImportUVs();
            if (UVInputIndices.Count() > 0 || ColorInputIndices.Count() > 0)
            {
                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();
                        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 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
                    });
                }
            }

            var description = Vertex.Description(VertexType);

            if ((description.Tangent && description.Binormal) && (!HasTangents || Options.RecalculateTangents))
            {
                if (!HasTangents)
                {
                    Utils.Info(String.Format("Channel 'TANGENT'/'BINROMAL' not found, will rebuild vertex tangents after import."));
                }
                computeTangents();
            }
        }