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); }
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."); } }
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); }