Beispiel #1
0
        public static Bone FromCollada(node bone, int parentIndex, List <Bone> bones, Dictionary <string, Bone> boneSIDs, Dictionary <string, Bone> boneIDs)
        {
            var transMat    = ColladaHelpers.TransformFromNode(bone);
            var colladaBone = new Bone();

            colladaBone.TransformSID = transMat.TransformSID;
            var myIndex = bones.Count;

            bones.Add(colladaBone);
            boneSIDs.Add(bone.sid, colladaBone);
            if (bone.id != null)
            {
                boneIDs.Add(bone.id, colladaBone);
            }

            colladaBone.ParentIndex       = parentIndex;
            colladaBone.Name              = bone.name;
            colladaBone.LODError          = 0; // TODO
            colladaBone.OriginalTransform = transMat.transform;
            colladaBone.Transform         = Transform.FromMatrix4(transMat.transform);
            colladaBone.UpdateInverseWorldTransform(bones);

            if (bone.node1 != null)
            {
                foreach (var node in bone.node1)
                {
                    if (node.type == NodeType.JOINT)
                    {
                        FromCollada(node, myIndex, bones, boneSIDs, boneIDs);
                    }
                }
            }

            return(colladaBone);
        }
Beispiel #2
0
        private void UpdateOBBs(Skeleton skeleton, Mesh mesh)
        {
            if (mesh.BoneBindings == null || mesh.BoneBindings.Count == 0)
            {
                return;
            }

            var obbs = new List <OBB>(mesh.BoneBindings.Count);

            for (var i = 0; i < mesh.BoneBindings.Count; i++)
            {
                obbs.Add(new OBB
                {
                    Min = new Vector3(1000.0f, 1000.0f, 1000.0f),
                    Max = new Vector3(-1000.0f, -1000.0f, -1000.0f),
                });
            }

            foreach (var vert in mesh.PrimaryVertexData.Vertices)
            {
                for (var i = 0; i < 4; i++)
                {
                    if (vert.BoneWeights[i] > 0)
                    {
                        var bi  = vert.BoneIndices[i];
                        var obb = obbs[bi];
                        obb.NumVerts++;

                        var bone = skeleton.GetBoneByName(mesh.BoneBindings[bi].BoneName);
                        var invWorldTransform = ColladaHelpers.FloatsToMatrix(bone.InverseWorldTransform);
                        var transformed       = Vector3.Transform(vert.Position, invWorldTransform);

                        obb.Min.X = Math.Min(obb.Min.X, transformed.X);
                        obb.Min.Y = Math.Min(obb.Min.Y, transformed.Y);
                        obb.Min.Z = Math.Min(obb.Min.Z, transformed.Z);

                        obb.Max.X = Math.Max(obb.Max.X, transformed.X);
                        obb.Max.Y = Math.Max(obb.Max.Y, transformed.Y);
                        obb.Max.Z = Math.Max(obb.Max.Z, transformed.Z);
                    }
                }
            }

            for (var i = 0; i < obbs.Count; i++)
            {
                var obb = obbs[i];
                if (obb.NumVerts > 0)
                {
                    mesh.BoneBindings[i].OBBMin = new float[] { obb.Min.X, obb.Min.Y, obb.Min.Z };
                    mesh.BoneBindings[i].OBBMax = new float[] { obb.Max.X, obb.Max.Y, obb.Max.Z };
                }
                else
                {
                    mesh.BoneBindings[i].OBBMin = new float[] { 0.0f, 0.0f, 0.0f };
                    mesh.BoneBindings[i].OBBMax = new float[] { 0.0f, 0.0f, 0.0f };
                }
            }
        }
        private void ImportFaces()
        {
            foreach (var item in Mesh.Items)
            {
                if (item is triangles)
                {
                    var tris = item as triangles;
                    TriangleCount = (int)tris.count;
                    Inputs        = tris.input;
                    Indices       = ColladaHelpers.StringsToIntegers(tris.p);
                }
                else if (item is polylist)
                {
                    var plist = item as polylist;
                    TriangleCount = (int)plist.count;
                    Inputs        = plist.input;
                    Indices       = ColladaHelpers.StringsToIntegers(plist.p);
                    var vertexCounts = ColladaHelpers.StringsToIntegers(plist.vcount);
                    foreach (var count in vertexCounts)
                    {
                        if (count != 3)
                        {
                            throw new ParsingException("Non-triangle found in COLLADA polylist. Make sure that all geometries are triangulated.");
                        }
                    }
                }
                else if (item is lines)
                {
                    throw new ParsingException("Lines found in input geometry. Make sure that all geometries are triangulated.");
                }
            }

            if (Indices == null || Inputs == null)
            {
                throw new ParsingException("No valid triangle source found, expected <triangles> or <polylist>");
            }

            InputOffsetCount = 0;
            foreach (var input in Inputs)
            {
                if ((int)input.offset >= InputOffsetCount)
                {
                    InputOffsetCount = (int)input.offset + 1;
                }
            }

            if (Indices.Count % (InputOffsetCount * 3) != 0 || Indices.Count / InputOffsetCount / 3 != TriangleCount)
            {
                throw new ParsingException("Triangle input stride / vertex count mismatch.");
            }
        }
Beispiel #4
0
        private void ImportSkin(Root root, 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 (!mesh.VertexFormat.HasBoneWeights)
            {
                var msg = String.Format("Tried to apply skin to mesh ({0}) with non-skinned vertices",
                                        mesh.Name);
                throw new ParsingException(msg);
            }

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

            foreach (var source in skin.source)
            {
                var src = ColladaSource.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");
                }

                ColladaSource 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 = root.Skeletons[0];
                    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");
                    }

                    ColladaSource 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 > 127)
            {
                throw new ParsingException("D:OS supports at most 127 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
                    // Use small bounding box values, as it interferes with object placement
                    // in D:OS 2 (after the Gift Bag 2 update)
                    binding.OBBMin = new float[] { -0.1f, -0.1f, -0.1f };
                    binding.OBBMax = new float[] { 0.1f, 0.1f, 0.1f };
                    mesh.BoneBindings.Add(binding);
                    boneToIndexMaps.Add(joints[i], boneToIndexMaps.Count);
                }
            }

            offset = 0;
            for (var vertexIndex = 0; vertexIndex < influenceCounts.Count; vertexIndex++)
            {
                var   influenceCount = influenceCounts[vertexIndex];
                float influenceSum   = 0.0f;
                for (var i = 0; i < influenceCount; i++)
                {
                    var weightIndex = influences[offset + i * stride + weightInputIndex];
                    influenceSum += weights[weightIndex];
                }

                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] / influenceSum;
                    // Not all vertices are actually used in triangles, we may have unused verts in the
                    // source list (though this is rare) which won't show up in the consolidated vertex map.
                    if (mesh.OriginalToConsolidatedVertexIndexMap.TryGetValue(vertexIndex, out List <int> consolidatedIndices))
                    {
                        foreach (var consolidatedIndex in consolidatedIndices)
                        {
                            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.Trim().Split(new char[] { ' ' }).Select(s => Single.Parse(s)).ToArray();
                var bindShapeMat    = ColladaHelpers.FloatsToMatrix(bindShapeFloats);
                bindShapeMat.Transpose();

                // Deform geometries that were affected by our bind shape matrix
                mesh.PrimaryVertexData.Transform(bindShapeMat);
            }

            if (Options.RecalculateOBBs)
            {
                UpdateOBBs(root.Skeletons.Single(), mesh);
            }
        }
        private void ImportVertices()
        {
            var vertexSemantics = new Dictionary <String, List <Vector3> >();

            foreach (var input in Mesh.vertices.input)
            {
                ColladaSource inputSource = FindSource(input.source);
                var           vertices    = ColladaHelpers.SourceToPositions(inputSource);
                vertexSemantics.Add(input.semantic, vertices);
            }

            List <Vector3> vertexPositions    = null;
            List <Vector3> perVertexNormals   = null;
            List <Vector3> perVertexTangents  = null;
            List <Vector3> perVertexBinormals = null;

            vertexSemantics.TryGetValue("POSITION", out vertexPositions);
            vertexSemantics.TryGetValue("NORMAL", out perVertexNormals);
            vertexSemantics.TryGetValue("TANGENT", out perVertexTangents);
            vertexSemantics.TryGetValue("BINORMAL", out perVertexBinormals);

            foreach (var input in Inputs)
            {
                if (input.semantic == "VERTEX")
                {
                    VertexInputIndex = (int)input.offset;
                }
                else if (input.semantic == "NORMAL")
                {
                    var normalsSource = FindSource(input.source);
                    Normals           = ColladaHelpers.SourceToPositions(normalsSource);
                    NormalsInputIndex = (int)input.offset;
                }
                else if (input.semantic == "TANGENT")
                {
                    var tangentsSource = FindSource(input.source);
                    Tangents           = ColladaHelpers.SourceToPositions(tangentsSource);
                    TangentsInputIndex = (int)input.offset;
                }
                else if (input.semantic == "BINORMAL")
                {
                    var binormalsSource = FindSource(input.source);
                    Binormals           = ColladaHelpers.SourceToPositions(binormalsSource);
                    BinormalsInputIndex = (int)input.offset;
                }
            }

            if (VertexInputIndex == -1)
            {
                throw new ParsingException("Required triangle input semantic missing: VERTEX");
            }

            Vertices = new List <Vertex>(vertexPositions.Count);
            for (var vert = 0; vert < vertexPositions.Count; vert++)
            {
                var vertex = OutputVertexType.CreateInstance();
                vertex.Position = vertexPositions[vert];

                if (perVertexNormals != null)
                {
                    vertex.Normal = perVertexNormals[vert];
                }

                if (perVertexTangents != null)
                {
                    vertex.Tangent = perVertexTangents[vert];
                }

                if (perVertexBinormals != null)
                {
                    vertex.Binormal = perVertexBinormals[vert];
                }

                Vertices.Add(vertex);
            }

            HasNormals  = perVertexNormals != null || NormalsInputIndex != -1;
            HasTangents = (perVertexTangents != null || TangentsInputIndex != -1) &&
                          (perVertexBinormals != null || BinormalsInputIndex != -1);
        }
        private skin ExportSkin(Mesh mesh, List <Bone> bones, Dictionary <string, Bone> nameMaps, string geometryId)
        {
            var sources = new List <source>();
            var joints  = new List <string>();
            var poses   = new List <float>();

            var boundBones   = new HashSet <string>();
            var orderedBones = new List <Bone>();

            foreach (var boneBinding in mesh.BoneBindings)
            {
                boundBones.Add(boneBinding.BoneName);
                orderedBones.Add(nameMaps[boneBinding.BoneName]);
            }

            /*
             * Append all bones to the end of the bone list, even if they're not influencing the mesh.
             * We need this because some tools (eg. Blender) expect all bones to be present, otherwise their
             * inverse world transform would reset to identity.
             */
            foreach (var bone in bones)
            {
                if (!boundBones.Contains(bone.Name))
                {
                    orderedBones.Add(bone);
                }
            }

            foreach (var bone in orderedBones)
            {
                boundBones.Add(bone.Name);
                joints.Add(bone.Name);

                var invWorldTransform = ColladaHelpers.FloatsToMatrix(bone.InverseWorldTransform);
                invWorldTransform.Transpose();

                poses.AddRange(new float[] {
                    invWorldTransform.M11, invWorldTransform.M12, invWorldTransform.M13, invWorldTransform.M14,
                    invWorldTransform.M21, invWorldTransform.M22, invWorldTransform.M23, invWorldTransform.M24,
                    invWorldTransform.M31, invWorldTransform.M32, invWorldTransform.M33, invWorldTransform.M34,
                    invWorldTransform.M41, invWorldTransform.M42, invWorldTransform.M43, invWorldTransform.M44
                });
            }

            var jointSource   = ColladaUtils.MakeNameSource(mesh.Name, "joints", new string[] { "JOINT" }, joints.ToArray());
            var poseSource    = ColladaUtils.MakeFloatSource(mesh.Name, "poses", new string[] { "TRANSFORM" }, poses.ToArray(), 16, "float4x4");
            var weightsSource = mesh.PrimaryVertexData.MakeBoneWeights(mesh.Name);

            var vertices = mesh.PrimaryVertexData.Deduplicator.DeduplicatedPositions;
            var vertexInfluenceCounts = new List <int>(vertices.Count);
            var vertexInfluences      = new List <int>(vertices.Count);
            int weightIdx             = 0;

            foreach (var vertex in vertices)
            {
                int influences = 0;
                var indices    = vertex.BoneIndices;
                var weights    = vertex.BoneWeights;
                for (int i = 0; i < 4; i++)
                {
                    if (weights[i] > 0)
                    {
                        influences++;
                        vertexInfluences.Add(indices[i]);
                        vertexInfluences.Add(weightIdx++);
                    }
                }

                vertexInfluenceCounts.Add(influences);
            }

            var jointOffsets = new InputLocalOffset();

            jointOffsets.semantic = "JOINT";
            jointOffsets.source   = "#" + jointSource.id;
            jointOffsets.offset   = 0;

            var weightOffsets = new InputLocalOffset();

            weightOffsets.semantic = "WEIGHT";
            weightOffsets.source   = "#" + weightsSource.id;
            weightOffsets.offset   = 1;

            var vertWeights = new skinVertex_weights();

            vertWeights.count  = (ulong)vertices.Count;
            vertWeights.input  = new InputLocalOffset[] { jointOffsets, weightOffsets };
            vertWeights.v      = string.Join(" ", vertexInfluences.Select(x => x.ToString()).ToArray());
            vertWeights.vcount = string.Join(" ", vertexInfluenceCounts.Select(x => x.ToString()).ToArray());

            var skin = new skin();

            skin.source1           = "#" + geometryId;
            skin.bind_shape_matrix = "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1";

            var skinJoints     = new skinJoints();
            var skinJointInput = new InputLocal();

            skinJointInput.semantic = "JOINT";
            skinJointInput.source   = "#" + jointSource.id;
            var skinInvBindInput = new InputLocal();

            skinInvBindInput.semantic = "INV_BIND_MATRIX";
            skinInvBindInput.source   = "#" + poseSource.id;
            skinJoints.input          = new InputLocal[] { skinJointInput, skinInvBindInput };

            skin.joints         = skinJoints;
            skin.source         = new source[] { jointSource, poseSource, weightsSource };
            skin.vertex_weights = vertWeights;

            return(skin);
        }