private void ImportSources() { Sources = new Dictionary <String, ColladaSource>(); foreach (var source in Mesh.source) { var src = ColladaSource.FromCollada(source); Sources.Add(src.id, src); } }
private ColladaSource FindSource(string id) { if (id.Length == 0 || id[0] != '#') { throw new ParsingException("Only ID references are supported for input sources: " + id); } ColladaSource inputSource = null; if (!Sources.TryGetValue(id.Substring(1), out inputSource)) { throw new ParsingException("Input source does not exist: " + id); } return(inputSource); }
private void ImportUVs() { bool flip = Options.FlipUVs; UVInputIndices.Clear(); UVs = new List <List <Vector2> >(); foreach (var input in Inputs) { if (input.semantic == "TEXCOORD") { UVInputIndices.Add((int)input.offset); if (input.source[0] != '#') { throw new ParsingException("Only ID references are supported for UV input sources"); } ColladaSource inputSource = null; if (!Sources.TryGetValue(input.source.Substring(1), out inputSource)) { throw new ParsingException("UV input source does not exist: " + input.source); } List <Single> s = null, t = null; if (!inputSource.FloatParams.TryGetValue("S", out s) || !inputSource.FloatParams.TryGetValue("T", out t)) { throw new ParsingException("UV input source " + input.source + " must have S, T float attributes"); } var uvs = new List <Vector2>(); UVs.Add(uvs); for (var i = 0; i < s.Count; i++) { if (flip) { t[i] = 1.0f - t[i]; } uvs.Add(new Vector2(s[i], t[i])); } } } }
private void ImportColors() { ColorInputIndices.Clear(); Colors = new List <List <Vector4> >(); foreach (var input in Inputs) { if (input.semantic == "COLOR") { ColorInputIndices.Add((int)input.offset); if (input.source[0] != '#') { throw new ParsingException("Only ID references are supported for color input sources"); } ColladaSource inputSource = null; if (!Sources.TryGetValue(input.source.Substring(1), out inputSource)) { throw new ParsingException("Color input source does not exist: " + input.source); } List <Single> r = null, g = null, b = null; if (!inputSource.FloatParams.TryGetValue("R", out r) || !inputSource.FloatParams.TryGetValue("G", out g) || !inputSource.FloatParams.TryGetValue("B", out b)) { if (!inputSource.FloatParams.TryGetValue("X", out r) || !inputSource.FloatParams.TryGetValue("Y", out g) || !inputSource.FloatParams.TryGetValue("Z", out b)) { throw new ParsingException("Color input source " + input.source + " must have R, G, B float attributes"); } } var colors = new List <Vector4>(); Colors.Add(colors); for (var i = 0; i < r.Count; i++) { colors.Add(new Vector4(r[i], g[i], b[i], 1.0f)); } } } }
public static List <Vector3> SourceToPositions(ColladaSource source) { List <Single> x = null, y = null, z = null; if (!source.FloatParams.TryGetValue("X", out x) || !source.FloatParams.TryGetValue("Y", out y) || !source.FloatParams.TryGetValue("Z", out z)) { throw new ParsingException("Position source " + source.id + " must have X, Y, Z float attributes"); } var positions = new List <Vector3>(x.Count); for (var i = 0; i < x.Count; i++) { positions.Add(new Vector3(x[i], y[i], z[i])); } return(positions); }
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); } }
public static ColladaSource FromCollada(source src) { var source = new ColladaSource(); source.id = src.id; var accessor = src.technique_common.accessor; // TODO: check src.#ID? float_array floats = null; Name_array names = null; if (src.Item is float_array) { floats = src.Item as float_array; // Workaround for empty arrays being null if (floats.Values == null) { floats.Values = new double[] { } } ; if ((int)floats.count != floats.Values.Length || floats.count < accessor.stride * accessor.count + accessor.offset) { throw new ParsingException("Float source data size mismatch. Check source and accessor item counts."); } } else if (src.Item is Name_array) { names = src.Item as Name_array; // Workaround for empty arrays being null if (names.Values == null) { names.Values = new string[] { } } ; if ((int)names.count != names.Values.Length || names.count < accessor.stride * accessor.count + accessor.offset) { throw new ParsingException("Name source data size mismatch. Check source and accessor item counts."); } } else { throw new ParsingException("Unsupported source data format."); } var paramOffset = 0; foreach (var param in accessor.param) { if (param.name == null) { param.name = "default"; } if (param.type == "float" || param.type == "double") { var items = new List <Single>((int)accessor.count); var offset = (int)accessor.offset; for (var i = 0; i < (int)accessor.count; i++) { items.Add((float)floats.Values[offset + paramOffset]); offset += (int)accessor.stride; } source.FloatParams.Add(param.name, items); } else if (param.type == "float4x4") { var items = new List <Matrix4>((int)accessor.count); var offset = (int)accessor.offset; for (var i = 0; i < (int)accessor.count; i++) { var itemOff = offset + paramOffset; var mat = new Matrix4( (float)floats.Values[itemOff + 0], (float)floats.Values[itemOff + 1], (float)floats.Values[itemOff + 2], (float)floats.Values[itemOff + 3], (float)floats.Values[itemOff + 4], (float)floats.Values[itemOff + 5], (float)floats.Values[itemOff + 6], (float)floats.Values[itemOff + 7], (float)floats.Values[itemOff + 8], (float)floats.Values[itemOff + 9], (float)floats.Values[itemOff + 10], (float)floats.Values[itemOff + 11], (float)floats.Values[itemOff + 12], (float)floats.Values[itemOff + 13], (float)floats.Values[itemOff + 14], (float)floats.Values[itemOff + 15] ); items.Add(mat); offset += (int)accessor.stride; } source.MatrixParams.Add(param.name, items); } else if (param.type.ToLower() == "name") { var items = new List <String>((int)accessor.count); var offset = (int)accessor.offset; for (var i = 0; i < (int)accessor.count; i++) { items.Add(names.Values[offset + paramOffset]); offset += (int)accessor.stride; } source.NameParams.Add(param.name, items); } else { throw new ParsingException("Unsupported accessor param type: " + param.type); } paramOffset++; } return(source); } }
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 void ImportVertices() { var vertexSemantics = new Dictionary <String, List <Vector3> >(); foreach (var input in Mesh.vertices.input) { if (input.source[0] != '#') { throw new ParsingException("Only ID references are supported for vertex input sources"); } ColladaSource inputSource = null; if (!Sources.TryGetValue(input.source.Substring(1), out inputSource)) { throw new ParsingException("Vertex input source does not exist: " + input.source); } List <Single> x = null, y = null, z = null; if (!inputSource.FloatParams.TryGetValue("X", out x) || !inputSource.FloatParams.TryGetValue("Y", out y) || !inputSource.FloatParams.TryGetValue("Z", out z)) { throw new ParsingException("Vertex input source " + input.source + " must have X, Y, Z float attributes"); } var vertices = new List <Vector3>(x.Count); for (var i = 0; i < x.Count; i++) { vertices.Add(new Vector3(x[i], y[i], z[i])); } vertexSemantics.Add(input.semantic, vertices); } List <Vector3> positions = null; List <Vector3> normals = null; List <Vector3> tangents = null; List <Vector3> binormals = null; vertexSemantics.TryGetValue("POSITION", out positions); vertexSemantics.TryGetValue("NORMAL", out normals); vertexSemantics.TryGetValue("TANGENT", out tangents); vertexSemantics.TryGetValue("BINORMAL", out binormals); int normalInputIndex = -1; foreach (var input in Inputs) { if (input.semantic == "VERTEX") { VertexInputIndex = (int)input.offset; } else if (input.semantic == "NORMAL") { normals = new List <Vector3>(); normalInputIndex = (int)input.offset; if (input.source[0] != '#') { throw new ParsingException("Only ID references are supported for Normal input sources"); } ColladaSource inputSource = null; if (!Sources.TryGetValue(input.source.Substring(1), out inputSource)) { throw new ParsingException("Normal input source does not exist: " + input.source); } List <Single> x = null, y = null, z = null; if (!inputSource.FloatParams.TryGetValue("X", out x) || !inputSource.FloatParams.TryGetValue("Y", out y) || !inputSource.FloatParams.TryGetValue("Z", out z)) { throw new ParsingException("Normal input source " + input.source + " must have X, Y, Z float attributes"); } for (var i = 0; i < x.Count; i++) { normals.Add(new Vector3(x[i], y[i], z[i])); } } } if (VertexInputIndex == -1) { throw new ParsingException("Required triangle input semantic missing: VERTEX"); } Vertices = new List <Vertex>(positions.Count); var vertexCtor = GR2.Helpers.GetConstructor(VertexType); for (var vert = 0; vert < positions.Count; vert++) { Vertex vertex = vertexCtor() as Vertex; vertex.Position = positions[vert]; if (tangents != null) { vertex.Tangent = tangents[vert]; } if (binormals != null) { vertex.Binormal = binormals[vert]; } if (normals != null && normalInputIndex == -1) { vertex.Normal = normals[vert]; } Vertices.Add(vertex); } if (normalInputIndex != -1) { for (var vert = 0; vert < TriangleCount * 3; vert++) { var vertexIndex = Indices[vert * InputOffsetCount + VertexInputIndex]; var normalIndex = Indices[vert * InputOffsetCount + normalInputIndex]; Vertex vertex = Vertices[vertexIndex]; vertex.Normal = normals[normalIndex]; } } HasNormals = normals != null; HasTangents = tangents != null && binormals != null; }