private void ExportCollada_NodeSub(XmlWriter xml, IReadOnlyList<string> boneNames, Kn5Node node) { xml.WriteStartElement("node"); xml.WriteAttributeStringSafe("id", node.Name); xml.WriteAttributeStringSafe("sid", node.Name); xml.WriteAttributeStringSafe("name", node.Name); xml.WriteAttributeString("layer", node.Active ? "Visible" : "Hidden"); xml.WriteAttributeString("type", node.NodeClass == Kn5NodeClass.Base && boneNames.Contains(node.Name) ? "JOINT" : "NODE"); if (node.Children?.FirstOrDefault()?.NodeClass != Kn5NodeClass.SkinnedMesh) { xml.WriteElement("matrix", "sid", "transform", node.NodeClass == Kn5NodeClass.Base ? XmlWriterExtension.MatrixToCollada(node.Transform) : "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0"); } if (IsMultiMaterial(node) && node.Children != null) { xml.WriteStartElement("instance_geometry"); xml.WriteAttributeStringSafe("url", $"#{node.Name}-mesh"); ExportCollada_NodeSub_BindMaterial(xml, node.Children.Select(x => x.MaterialId).ToArray()); xml.WriteEndElement(); } else { ExportCollada_NodeSub_Inner(xml, boneNames, node); } xml.WriteEndElement(); // node }
private void ExportCollada_Mesh(XmlWriter xml, string name, IReadOnlyList<Kn5Node> unsorted) { xml.WriteStartElement("geometry"); xml.WriteAttributeStringSafe("id", $"{name}-mesh"); xml.WriteAttributeStringSafe("name", name); xml.WriteStartElement("mesh"); var nodes = unsorted.Count == 1 ? unsorted : unsorted.OrderBy(x => int.Parse(x.Name.Split(new[] { "_SUB" }, StringSplitOptions.None).Last())).ToList(); /* coordinates */ var vertexCount = nodes.Sum(x => x.Vertices.Length); xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-positions"); xml.WriteStartElement("float_array"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-positions-array"); xml.WriteAttributeString("count", vertexCount * 3); xml.WriteString(nodes.SelectMany(x => x.Vertices).SelectMany(x => x.Co).JoinToString(" ")); xml.WriteEndElement(); // float_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{name}-mesh-positions-array"); xml.WriteAttributeString("count", vertexCount); xml.WriteAttributeString("stride", 3); xml.WriteElement("param", "name", "X", "type", "float"); xml.WriteElement("param", "name", "Y", "type", "float"); xml.WriteElement("param", "name", "Z", "type", "float"); xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source /* normals */ xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-normals"); xml.WriteStartElement("float_array"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-normals-array"); xml.WriteAttributeString("count", vertexCount * 3); xml.WriteString(nodes.SelectMany(x => x.Vertices).SelectMany(x => x.Normal).JoinToString(" ")); xml.WriteEndElement(); // float_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{name}-mesh-normals-array"); xml.WriteAttributeString("count", vertexCount); xml.WriteAttributeString("stride", "3"); xml.WriteElement("param", "name", "X", "type", "float"); xml.WriteElement("param", "name", "Y", "type", "float"); xml.WriteElement("param", "name", "Z", "type", "float"); xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source /* uv */ xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-map-0"); xml.WriteStartElement("float_array"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-map-0-array"); xml.WriteAttributeString("count", vertexCount * 2); xml.WriteString(nodes.SelectMany(x => x.Vertices).SelectMany(x => new[] { x.Uv[0], -x.Uv[1] }).JoinToString(" ")); xml.WriteEndElement(); // float_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{name}-mesh-map-0-array"); xml.WriteAttributeString("count", vertexCount); xml.WriteAttributeString("stride", 2); xml.WriteElement("param", "name", "S", "type", "float"); xml.WriteElement("param", "name", "T", "type", "float"); xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source /* vertices */ xml.WriteStartElement("vertices"); xml.WriteAttributeStringSafe("id", $"{name}-mesh-vertices"); xml.WriteElement("input", "semantic", "POSITION", "source", $"#{name}-mesh-positions"); xml.WriteEndElement(); /* triangles */ var offset = 0; foreach (var node in nodes) { xml.WriteStartElement("triangles"); xml.WriteAttributeStringSafe("original_node", $"{node.Name}"); xml.WriteAttributeStringSafe("material", $"{Materials.Values.ElementAt((int)node.MaterialId).Name}-material"); xml.WriteAttributeString("count", node.Indices.Length / 3); xml.WriteElement("input", "semantic", "VERTEX", "source", $"#{name}-mesh-vertices", "offset", 0); xml.WriteElement("input", "semantic", "NORMAL", "source", $"#{name}-mesh-normals", "offset", 1); xml.WriteElement("input", "semantic", "TEXCOORD", "source", $"#{name}-mesh-map-0", "offset", 2, "set", 0); var inner = offset; xml.WriteElementString("p", node.Indices.SelectMany(x => new[] { x + inner, x + inner, x + inner }).JoinToString(" ")); xml.WriteEndElement(); // triangles offset += node.Vertices.Length; } xml.WriteEndElement(); // mesh xml.WriteEndElement(); // geometry }
private void ExportCollada_NodeSub_Inner(XmlWriter xml, IReadOnlyList<string> boneNames, Kn5Node node) { switch (node.NodeClass) { case Kn5NodeClass.Base: if (node.Children.Count == 1 && node.Children[0].NodeClass != Kn5NodeClass.Base) { ExportCollada_NodeSub_Inner(xml, boneNames, node.Children[0]); } else { foreach (var t in node.Children) { ExportCollada_NodeSub(xml, boneNames, t); } } break; case Kn5NodeClass.Mesh: xml.WriteStartElement("instance_geometry"); xml.WriteAttributeStringSafe("url", $"#{node.Name}-mesh"); ExportCollada_NodeSub_BindMaterial(xml, node.MaterialId); xml.WriteEndElement(); break; case Kn5NodeClass.SkinnedMesh: xml.WriteStartElement("instance_controller"); xml.WriteAttributeStringSafe("url", $"#{node.Name}Controller"); ExportCollada_NodeSub_BindMaterial(xml, node.MaterialId); foreach (var bone in node.Bones) { xml.WriteElementString("skeleton", $"#{bone.Name}"); } xml.WriteEndElement(); // instance_controller break; } }
private void ExportCollada_Skinned(XmlWriter xml, Kn5Node node) { switch (node.NodeClass) { case Kn5NodeClass.Base: foreach (var child in node.Children) { ExportCollada_Skinned(xml, child); } break; case Kn5NodeClass.Mesh: return; case Kn5NodeClass.SkinnedMesh: xml.WriteStartElement("controller"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller"); xml.WriteStartElement("skin"); xml.WriteAttributeStringSafe("source", $"#{node.Name}-mesh"); // xml.WriteElementString("bind_shape_matrix", "1 0 0 0 0 0 1 0 0 -1 0 0 0 0 0 1"); xml.WriteElementString("bind_shape_matrix", "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); // Joints xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Joints"); xml.WriteStartElement("Name_array"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Joints-array"); xml.WriteAttributeString("count", node.Bones.Length); xml.WriteString(node.Bones.Select(x => x.Name).JoinToString(" ")); xml.WriteEndElement(); // Name_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Joints-array"); xml.WriteAttributeString("count", node.Bones.Length); xml.WriteAttributeString("stride", 1); xml.WriteStartElement("param"); xml.WriteAttributeString("name", "JOINT"); xml.WriteAttributeString("type", "name"); xml.WriteEndElement(); // param xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source (Joints) // Matrices xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Matrices"); xml.WriteStartElement("float_array"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Matrices-array"); xml.WriteAttributeString("count", node.Bones.Length * 16); xml.WriteString(node.Bones.Select(x => XmlWriterExtension.MatrixToCollada(x.Transform)) .JoinToString(" ")); xml.WriteEndElement(); // float_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Matrices-array"); xml.WriteAttributeString("count", node.Bones.Length); xml.WriteAttributeString("stride", 16); xml.WriteStartElement("param"); xml.WriteAttributeString("name", "TRANSFORM"); xml.WriteAttributeString("type", "float4x4"); xml.WriteEndElement(); // param xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source (Matrices) // Weights xml.WriteStartElement("source"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Weights"); xml.WriteStartElement("float_array"); xml.WriteAttributeStringSafe("id", $"{node.Name}Controller-Weights-array"); var weights = node.VerticeWeights.SelectMany(x => x.Weights.Where((y, i) => !Equals(x.Indices[i], -1f))).ToList(); xml.WriteAttributeString("count", weights.Count); xml.WriteString(weights.JoinToString(" ")); xml.WriteEndElement(); // float_array xml.WriteStartElement("technique_common"); xml.WriteStartElement("accessor"); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Weights-array"); xml.WriteAttributeString("count", weights.Count); xml.WriteAttributeString("stride", 1); xml.WriteStartElement("param"); xml.WriteAttributeString("name", "WEIGHT"); xml.WriteAttributeString("type", "float"); xml.WriteEndElement(); // param xml.WriteEndElement(); // accessor xml.WriteEndElement(); // technique_common xml.WriteEndElement(); // source (Matrices) xml.WriteStartElement("joints"); xml.WriteStartElement("input"); xml.WriteAttributeString("semantic", "JOINT"); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Joints"); xml.WriteEndElement(); // input xml.WriteStartElement("input"); xml.WriteAttributeString("semantic", "INV_BIND_MATRIX"); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Matrices"); xml.WriteEndElement(); // input xml.WriteEndElement(); // joints xml.WriteStartElement("vertex_weights"); xml.WriteAttributeString("count", node.Vertices.Length); xml.WriteStartElement("input"); xml.WriteAttributeString("semantic", "JOINT"); xml.WriteAttributeString("offset", 0); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Joints"); xml.WriteEndElement(); // input xml.WriteStartElement("input"); xml.WriteAttributeString("semantic", "WEIGHT"); xml.WriteAttributeString("offset", 1); xml.WriteAttributeStringSafe("source", $"#{node.Name}Controller-Weights"); xml.WriteEndElement(); // input xml.WriteElementString("vcount", node.VerticeWeights.Select(x => x.Weights.Where(y => !Equals(y, 0f)).Count()).JoinToString(" ")); var k = 0; xml.WriteElementString("v", node.VerticeWeights.SelectMany(x => x.Indices.Where(y => !Equals(y, -1f)).SelectMany((y, i) => new[] { y, k++ })).JoinToString(" ")); xml.WriteEndElement(); // vertex_weights xml.WriteEndElement(); // skin xml.WriteEndElement(); // controller break; } }
private void ExportCollada_Material(XmlWriter xml, Kn5Material material) { xml.WriteStartElement("material"); xml.WriteAttributeStringSafe("id", $"{material.Name}-material"); xml.WriteAttributeStringSafe("name", material.Name); xml.WriteStartElement("instance_effect"); xml.WriteAttributeStringSafe("url", $"#{material.Name}-effect"); xml.WriteEndElement(); xml.WriteEndElement(); }
private void ExportCollada_MaterialEffect(XmlWriter xml, Kn5Material material) { xml.WriteStartElement("effect"); xml.WriteAttributeStringSafe("id", $"{material.Name}-effect"); xml.WriteStartElement("profile_COMMON"); xml.WriteStartElement("technique"); xml.WriteAttributeString("sid", "common"); xml.WriteStartElement("phong"); xml.WriteStartElement("emission"); xml.WriteStartElement("color"); xml.WriteAttributeString("sid", "emission"); var ksEmissive = material.GetPropertyByName("ksEmissive"); xml.WriteString(ksEmissive == null ? "0 0 0 1" : $"{ksEmissive.ValueC[0].ToString(CultureInfo.InvariantCulture)} {ksEmissive.ValueC[1].ToString(CultureInfo.InvariantCulture)} {ksEmissive.ValueC[2].ToString(CultureInfo.InvariantCulture)} 1"); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteStartElement("ambient"); xml.WriteStartElement("color"); xml.WriteAttributeString("sid", "ambient"); var ksAmbient = material.GetPropertyByName("ksAmbient"); xml.WriteString(ksAmbient == null ? "0.4 0.4 0.4 1" : string.Format("{0} {0} {0} 1", ksAmbient.ValueA.ToString(CultureInfo.InvariantCulture))); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteStartElement("diffuse"); var txDiffuse = material.GetMappingByName("txDiffuse"); if (txDiffuse != null) { ExportCollada_MaterialEffectTexture(xml, txDiffuse); } xml.WriteStartElement("color"); xml.WriteAttributeString("sid", "diffuse"); var ksDiffuse = material.GetPropertyByName("ksDiffuse"); xml.WriteString(ksDiffuse == null ? "0.6 0.6 0.6 1" : string.Format("{0} {0} {0} 1", ksDiffuse.ValueA.ToString(CultureInfo.InvariantCulture))); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteStartElement("specular"); xml.WriteStartElement("color"); xml.WriteAttributeString("sid", "specular"); var ksSpecular = material.GetPropertyByName("ksSpecular"); xml.WriteString(ksSpecular == null ? "0.5 0.5 0.5 1" : string.Format("{0} {0} {0} 1", ksSpecular.ValueA.ToString(CultureInfo.InvariantCulture))); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteStartElement("shininess"); xml.WriteStartElement("float"); xml.WriteAttributeString("sid", "shininess"); xml.WriteString(material.GetPropertyByName("ksSpecularEXP")?.ValueA ?? 50); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteStartElement("index_of_refraction"); xml.WriteStartElement("float"); xml.WriteAttributeString("sid", "index_of_refraction"); xml.WriteString(1f); xml.WriteEndElement(); xml.WriteEndElement(); if (material.BlendMode == Kn5MaterialBlendMode.AlphaBlend) { xml.WriteStartElement("transparent"); xml.WriteAttributeString("opaque", "RGB_ZERO"); if (material.ShaderName == "ksPerPixelNM" || material.ShaderName == "ksPerPixelNM_UV2" || material.ShaderName == "ksPerPixelMultiMap_AT" || material.ShaderName == "ksPerPixelMultiMap_AT_NMDetail") { var txNormal = material.GetMappingByName("txNormal"); if (txNormal != null) { ExportCollada_MaterialEffectTexture(xml, txNormal); } } else if (txDiffuse != null) { ExportCollada_MaterialEffectTexture(xml, txDiffuse); } xml.WriteEndElement(); // transparent } var alpha = 1.0f; switch (material.ShaderName) { case "ksBrokenGlass": alpha = 0f; break; case "ksPerPixelAlpha": alpha = material.GetPropertyByName("alpha")?.ValueA ?? 1f; break; } if (!Equals(alpha, 1f)) { xml.WriteStartElement("transparency"); xml.WriteStartElement("float"); xml.WriteAttributeString("sid", "transparency"); xml.WriteString(alpha); xml.WriteEndElement(); // float xml.WriteEndElement(); // transparency } xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteEndElement(); }
private void ExportCollada_MaterialEffectTexture(XmlWriter xml, Kn5Material.TextureMapping tex) { xml.WriteStartElement("texture"); xml.WriteAttributeStringSafe("texture", $"{tex.Texture}-image"); xml.WriteAttributeString("texcoord", "CHANNEL0"); xml.WriteStartElement("extra"); xml.WriteStartElement("technique"); xml.WriteAttributeString("profile", "MAYA"); xml.WriteStartElement("wrapV"); xml.WriteAttributeString("sid", "wrapV0"); xml.WriteString("TRUE"); xml.WriteEndElement(); xml.WriteStartElement("wrapU"); xml.WriteAttributeString("sid", "wrapU0"); xml.WriteString("TRUE"); xml.WriteEndElement(); xml.WriteStartElement("blend_mode"); xml.WriteString("ADD"); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteEndElement(); xml.WriteEndElement(); }
private void ExportCollada_Texture(XmlWriter xml, Kn5Texture texture) { xml.WriteStartElement("image"); xml.WriteAttributeStringSafe("id", $"{texture.Name}-image"); xml.WriteAttributeStringSafe("name", texture.Name); xml.WriteStartElement("init_from"); xml.WriteString("file://texture/" + texture.Name); xml.WriteEndElement(); xml.WriteEndElement(); }