Exemplo n.º 1
0
        private void ConformMeshBoneBindings(Mesh mesh, Mesh conformToMesh)
        {
            foreach (var conformBone in conformToMesh.BoneBindings)
            {
                BoneBinding inputBone = null;
                foreach (var bone in mesh.BoneBindings)
                {
                    if (bone.BoneName == conformBone.BoneName)
                    {
                        inputBone = bone;
                        break;
                    }
                }

                if (inputBone == null)
                {
                    // Create a new "dummy" binding if it does not exist in the new mesh
                    inputBone          = new BoneBinding();
                    inputBone.BoneName = conformBone.BoneName;
                    mesh.BoneBindings.Add(inputBone);
                }

                // The bones match, copy relevant parameters from the conforming binding to the input.
                inputBone.OBBMin = conformBone.OBBMin;
                inputBone.OBBMax = conformBone.OBBMax;
            }
        }
Exemplo n.º 2
0
        private void GenerateDummySkeleton(Root root)
        {
            foreach (var model in root.Models)
            {
                if (model.Skeleton == null)
                {
                    Utils.Info($"Generating dummy skeleton for model '{model.Name}'");
                    var skeleton = new Skeleton();
                    skeleton.Name    = model.Name;
                    skeleton.LODType = 1;
                    skeleton.IsDummy = true;
                    root.Skeletons.Add(skeleton);

                    var bone = new Bone();
                    bone.Name        = model.Name;
                    bone.ParentIndex = -1;
                    skeleton.Bones   = new List <Bone> {
                        bone
                    };
                    bone.Transform = new Transform();

                    // TODO: Transform / IWT is not always identity on dummy bones!
                    skeleton.UpdateWorldTransforms();
                    model.Skeleton = skeleton;

                    foreach (var mesh in model.MeshBindings)
                    {
                        if (mesh.Mesh.BoneBindings != null && mesh.Mesh.BoneBindings.Count > 0)
                        {
                            throw new ParsingException("Failed to generate dummy skeleton: Mesh already has bone bindings.");
                        }

                        var binding = new BoneBinding();
                        binding.BoneName = bone.Name;
                        // TODO: Calculate bounding box!
                        // 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.Mesh.BoneBindings = new List <BoneBinding> {
                            binding
                        };
                    }
                }
            }
        }
Exemplo n.º 3
0
        private void GenerateDummySkeleton(Root root)
        {
            foreach (var model in root.Models)
            {
                if (model.Skeleton == null)
                {
                    Utils.Info(String.Format("Generating dummy skeleton for model '{0}'", model.Name));
                    var skeleton = new Skeleton();
                    skeleton.Name    = model.Name;
                    skeleton.LODType = 0;
                    skeleton.IsDummy = true;
                    root.Skeletons.Add(skeleton);

                    var bone = new Bone();
                    bone.Name        = model.Name;
                    bone.ParentIndex = -1;
                    skeleton.Bones   = new List <Bone> {
                        bone
                    };
                    bone.Transform = new Transform();

                    // TODO: Transform / IWT is not always identity on dummy bones!
                    skeleton.UpdateInverseWorldTransforms();
                    model.Skeleton = skeleton;

                    foreach (var mesh in model.MeshBindings)
                    {
                        if (mesh.Mesh.BoneBindings != null && mesh.Mesh.BoneBindings.Count > 0)
                        {
                            throw new ParsingException("Failed to generate dummy skeleton: Mesh already has bone bindings.");
                        }

                        var binding = new BoneBinding();
                        binding.BoneName = bone.Name;
                        // TODO: Calculate bounding box!
                        binding.OBBMin         = new float[] { -10, -10, -10 };
                        binding.OBBMax         = new float[] { 10, 10, 10 };
                        mesh.Mesh.BoneBindings = new List <BoneBinding> {
                            binding
                        };
                    }
                }
            }
        }
Exemplo n.º 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);
            }
        }