using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace Mafia2Tool.Parsed
{
    public class ColladaExporter
    {
        public static void ExportModelLod(Model.Lod model, string filename)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            //settings.Encoding = Encoding.ASCII;
            //settings.OmitXmlDeclaration = true;
            settings.Indent = true;
            settings.NewLineChars = "\r\n";

            XmlWriter w = XmlWriter.Create(filename, settings);

            w.WriteStartDocument();
            //w.WriteRaw("<?xml version=\"1.0\"?>\r\n");
            
            w.WriteStartElement("COLLADA", "http://www.collada.org/2005/11/COLLADASchema");
            w.WriteAttributeString("version", "1.4.1");

            string geoID = "exported";

            writeDefaultMaterials(w, model.Parts.Length);
            writeDefaultEffects(w, model.Parts.Length);

            exportGeometry(model, w, geoID);

            if (model.Skeleton != null)
            {
                exportController(model, w, geoID);
            }

            writeDefaultScene(model, w, geoID);

            w.WriteEndElement();
            w.Close();
        }

        private static void exportGeometry(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("library_geometries");

            writer.WriteStartElement("geometry");
            writer.WriteAttributeString("id", string.Format("{0}-lib", geoID));
            writer.WriteAttributeString("name", geoID);

            writer.WriteStartElement("mesh");

            exportPositions(model, writer, geoID);
            exportNormals(model, writer, geoID);
            if (model.NormalMapInfoPresent)
            {
                exportTangents(model, writer, geoID);
                exportBinormals(model, writer, geoID);
            }

            for (int i = 0; i < model.NumUVChannels; i++)
            {
                exportUVs(model, writer, geoID, i);
            }

            writer.WriteStartElement("vertices");
            writer.WriteAttributeString("id", string.Format("{0}-lib-vertices", geoID));
            writer.WriteStartElement("input");
            writer.WriteAttributeString("semantic", "POSITION");   
            writer.WriteAttributeString("source", string.Format("#{0}-lib-positions", geoID));   
            writer.WriteEndElement(); // input
            writer.WriteEndElement(); // vertices

            for (int i = 0; i < model.Parts.Length; i++) exportTriangles(model, writer, geoID, i);

            writer.WriteEndElement(); // mesh
            writer.WriteEndElement(); // geometry
            writer.WriteEndElement(); // library_geometries
        }

        private static void exportPositions(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-positions", geoID));
            writer.WriteAttributeString("name", "position");

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-positions-array", geoID));
            writer.WriteAttributeString("count", (model.Vertices.Length * 3).ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Vertices.Length; i++)
            {
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Position.X);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Position.Y);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Position.Z);
            }

            writer.WriteString(sb.ToString());

            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");
            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Vertices.Length.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-positions-array", geoID));
            writer.WriteAttributeString("stride", "3");

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "X");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Y");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Z");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement(); // accessor
            writer.WriteEndElement(); // technique_common

            writer.WriteEndElement(); // source
        }

        private static void exportNormals(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-normals", geoID));
            writer.WriteAttributeString("name", "normal");

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-normals-array", geoID));
            writer.WriteAttributeString("count", (model.Vertices.Length * 3).ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Vertices.Length; i++)
            {
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Normal.X);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Normal.Y);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Normal.Z);
            }

            writer.WriteString(sb.ToString());

            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");
            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Vertices.Length.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-normals-array", geoID));
            writer.WriteAttributeString("stride", "3");

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "X");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Y");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Z");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement(); // accessor
            writer.WriteEndElement(); // technique_common

            writer.WriteEndElement(); // source
        }

        private static void exportTangents(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-tangents", geoID));
            writer.WriteAttributeString("name", "tangent");

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-tangents-array", geoID));
            writer.WriteAttributeString("count", (model.Vertices.Length * 3).ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Vertices.Length; i++)
            {
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Tangent.X);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Tangent.Y);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Tangent.Z);
            }

            writer.WriteString(sb.ToString());

            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");
            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Vertices.Length.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-tangents-array", geoID));
            writer.WriteAttributeString("stride", "3");

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "X");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Y");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Z");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement(); // accessor
            writer.WriteEndElement(); // technique_common

            writer.WriteEndElement(); // source
        }

        private static void exportBinormals(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-binormals", geoID));
            writer.WriteAttributeString("name", "binormal");

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-binormals-array", geoID));
            writer.WriteAttributeString("count", (model.Vertices.Length * 3).ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Vertices.Length; i++)
            {
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Binormal.X);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Binormal.Y);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].Binormal.Z);
            }

            writer.WriteString(sb.ToString());

            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");
            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Vertices.Length.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-binormals-array", geoID));
            writer.WriteAttributeString("stride", "3");

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "X");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Y");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "Z");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement(); // accessor
            writer.WriteEndElement(); // technique_common

            writer.WriteEndElement(); // source
        }

        private static void exportUVs(Model.Lod model, XmlWriter writer, string geoID, int set)
        {
            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-map{1}", geoID, set));
            writer.WriteAttributeString("name", string.Format("map{0}", set));

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-map{1}-array", geoID, set));
            writer.WriteAttributeString("count", (model.Vertices.Length * 2).ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Vertices.Length; i++)
            {
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].UVs[set].X);
                sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} ", model.Vertices[i].UVs[set].Y);
            }

            writer.WriteString(sb.ToString());

            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");
            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Vertices.Length.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-map{1}-array", geoID, set));
            writer.WriteAttributeString("stride", "2");

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "S");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteStartElement("param");
            writer.WriteAttributeString("name", "T");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement(); // accessor
            writer.WriteEndElement(); // technique_common

            writer.WriteEndElement(); // source
        }
        
        private static void exportTriangles(Model.Lod model, XmlWriter writer, string geoID, int part)
        {
            int numFaces = model.Parts[part].Indices.Length / 3;
            
            writer.WriteStartElement("triangles");
            writer.WriteAttributeString("count", numFaces.ToString());
            writer.WriteAttributeString("material", string.Format("material{0}SG", part));

            writer.WriteStartElement("input");
            writer.WriteAttributeString("offset", "0");
            writer.WriteAttributeString("semantic", "VERTEX");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-vertices", geoID));
            writer.WriteEndElement(); // input

            writer.WriteStartElement("input");
            writer.WriteAttributeString("offset", "0");
            writer.WriteAttributeString("semantic", "NORMAL");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-normals", geoID));
            writer.WriteEndElement(); // input

            if (model.NormalMapInfoPresent)
            {
                writer.WriteStartElement("input");
                writer.WriteAttributeString("offset", "0");
                writer.WriteAttributeString("semantic", "TEXTANGENT");
                writer.WriteAttributeString("source", string.Format("#{0}-lib-tangents", geoID));
                writer.WriteEndElement(); // input

                writer.WriteStartElement("input");
                writer.WriteAttributeString("offset", "0");
                writer.WriteAttributeString("semantic", "TEXBINORMAL");
                writer.WriteAttributeString("source", string.Format("#{0}-lib-binormals", geoID));
                writer.WriteEndElement(); // input
            }

            for (int i = 0; i < model.NumUVChannels; i++)
            {
                writer.WriteStartElement("input");
                writer.WriteAttributeString("offset", "0");
                writer.WriteAttributeString("semantic", "TEXCOORD");
                writer.WriteAttributeString("source", string.Format("#{0}-lib-map{1}", geoID, i));
                writer.WriteAttributeString("set", i.ToString());
                writer.WriteEndElement(); // input
            }

            writer.WriteStartElement("p");
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Parts[part].Indices.Length; i++)
            {
                sb.AppendFormat("{0} ", model.Parts[part].Indices[i]);
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // vcount

            writer.WriteEndElement(); // triangles
        }

        private static void exportSkeleton(Model.Lod model, XmlWriter writer)
        {
            exportJoint(model.Skeleton[0], writer);
        }

        private static void exportJoint(Mafia2Tool.Parsed.SkeletonJoint joint, XmlWriter writer)
        {
            writer.WriteStartElement("node");
            
            string id = joint.Name.ToString().Replace(' ','_');
            writer.WriteAttributeString("id", id);
            writer.WriteAttributeString("name", joint.Name.ToString());
            writer.WriteAttributeString("sid", id);
            writer.WriteAttributeString("type", "JOINT");

            writer.WriteStartElement("matrix");
            writer.WriteAttributeString("sid", "matrix");

            Matrix44 rotworld = new Matrix44(joint.TransformWorldRotation, joint.TransformWorldPos);
            if (joint.ParentJoint != null)
            {
                Matrix44 protworld = new Matrix44(joint.ParentJoint.TransformWorldRotation, joint.ParentJoint.TransformWorldPos);
                protworld.InvertRotTransAffine();
                rotworld = rotworld * protworld;
            }

            rotworld.Transpose();

            writer.WriteString(rotworld.ToString());
            
            writer.WriteEndElement();
            
            if (joint.ChildJoints != null && joint.ChildJoints.Count > 0)
            {
                foreach (var ch in joint.ChildJoints)
                {
                    exportJoint(ch, writer);
                }
            }

            writer.WriteEndElement();
        }

        private static void exportController(Model.Lod model, XmlWriter writer, string geoID)
        {
            int[] vcounts = new int[model.Vertices.Length];
            List<float> weights = new List<float>();
            weights.Add(1.0f);
            weights.Add(0.0f);

            List<int> joints = new List<int>();
            List<int> weightIndices = new List<int>();

            for (int i = 0; i < model.Vertices.Length; i++)
            {
                Model.Vertex v = model.Vertices[i];
                for(int k = 0; k < v.JointIndices.Length; k++)
                {
                    joints.Add(v.JointIndices[k]);

                    if (v.BlendWeights[k] == 1.0f)
                    {
                        weightIndices.Add(0);
                    }
                    else if (v.BlendWeights[k] == 0.0f)
                    {
                        weightIndices.Add(1);
                    }
                    else
                    {
                        weightIndices.Add(weights.Count);
                        weights.Add(v.BlendWeights[k]);
                    }
                }
                vcounts[i] = v.JointIndices.Length;
            }
            
            writer.WriteStartElement("library_controllers");
            writer.WriteStartElement("controller");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin", geoID));
            writer.WriteAttributeString("name", string.Format("{0}-lib-skin", geoID));
            writer.WriteStartElement("skin");
            writer.WriteAttributeString("source", string.Format("#{0}-lib", geoID));
            writer.WriteStartElement("bind_shape_matrix"); writer.WriteString("1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); writer.WriteEndElement();

            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-joints", geoID));

            writer.WriteStartElement("IDREF_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-joints-array", geoID));
            writer.WriteAttributeString("count", model.Skeleton.Count.ToString());

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < model.Skeleton.Count; i++)
            {
                sb.Append(model.Skeleton[i].Name.ToString().Replace(' ','_'));
                sb.Append(" ");
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // IDREF_array

            writer.WriteStartElement("technique_common");

            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Skeleton.Count.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-joints-array", geoID));
            writer.WriteStartElement("param"); writer.WriteAttributeString("name", "JOINT"); writer.WriteAttributeString("type", "IDREF"); writer.WriteEndElement();
            writer.WriteEndElement(); // accessor
            
            writer.WriteEndElement(); // technique_common
            writer.WriteEndElement(); // source

            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-bind_poses", geoID));

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-bind_poses-array", geoID));
            writer.WriteAttributeString("count", (model.Skeleton.Count * 16).ToString());

            sb.Clear();
            for (int i = 0; i < model.Skeleton.Count; i++)
            {
                var joint = model.Skeleton[i];

                Matrix44 rotworld = new Matrix44(joint.TransformWorldRotation, joint.TransformWorldPos);
                rotworld.InvertRotTransAffine();
                rotworld.Transpose();

                sb.Append(rotworld.ToString());
                sb.Append(" ");
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");

            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", model.Skeleton.Count.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-bind_poses-array", geoID));
            writer.WriteAttributeString("stride", "16");
            writer.WriteStartElement("param"); writer.WriteAttributeString("type", "float4x4"); writer.WriteEndElement();
            writer.WriteEndElement(); // accessor

            writer.WriteEndElement(); // technique_common
            writer.WriteEndElement(); // source

            writer.WriteStartElement("source");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-weights", geoID));

            writer.WriteStartElement("float_array");
            writer.WriteAttributeString("id", string.Format("{0}-lib-skin-weights-array", geoID));
            writer.WriteAttributeString("count", weights.Count.ToString());

            sb.Clear();
            for (int i = 0; i < weights.Count; i++)
            {
                sb.Append(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000}", weights[i]));
                sb.Append(" ");
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // float_array

            writer.WriteStartElement("technique_common");

            writer.WriteStartElement("accessor");
            writer.WriteAttributeString("count", weights.Count.ToString());
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-weights-array", geoID));
            writer.WriteStartElement("param"); writer.WriteAttributeString("name", "WEIGHT"); writer.WriteAttributeString("type", "float"); writer.WriteEndElement();
            writer.WriteEndElement(); // accessor

            writer.WriteEndElement(); // technique_common
            writer.WriteEndElement(); // source

            writer.WriteStartElement("joints");
            writer.WriteStartElement("input");
            writer.WriteAttributeString("semantic", "JOINT");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-joints", geoID));
            writer.WriteEndElement(); // input
            writer.WriteStartElement("input");
            writer.WriteAttributeString("semantic", "INV_BIND_MATRIX");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-bind_poses", geoID));
            writer.WriteEndElement(); // input
            writer.WriteEndElement(); // joints

            writer.WriteStartElement("vertex_weights");
            writer.WriteAttributeString("count", vcounts.Length.ToString());
            writer.WriteStartElement("input");
            writer.WriteAttributeString("offset", "0");
            writer.WriteAttributeString("semantic", "JOINT");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-joints", geoID));
            writer.WriteEndElement(); // input
            writer.WriteStartElement("input");
            writer.WriteAttributeString("offset", "1");
            writer.WriteAttributeString("semantic", "WEIGHT");
            writer.WriteAttributeString("source", string.Format("#{0}-lib-skin-weights", geoID));
            writer.WriteEndElement(); // input

            writer.WriteStartElement("vcount");
            sb.Clear();
            for (int i = 0; i < vcounts.Length; i++)
            {
                sb.Append(vcounts[i].ToString());
                sb.Append(" ");
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // vcount

            writer.WriteStartElement("v");
            sb.Clear();
            int j = 0;
            for (int i = 0; i < vcounts.Length; i++)
            {
                for (int k = j; k < j + vcounts[i]; k++)
                {
                    sb.Append(joints[k]);
                    sb.Append(" ");
                    sb.Append(weightIndices[k].ToString());
                    sb.Append(" ");
                }

                j += vcounts[i];
            }
            writer.WriteString(sb.ToString());
            writer.WriteEndElement(); // v

            writer.WriteEndElement(); // vertex_weights

            writer.WriteEndElement(); // skin
            writer.WriteEndElement(); // controller
            writer.WriteEndElement(); // library_controllers
        }

        private static void writeDefaultEffects(XmlWriter writer, int count)
        {
            writer.WriteStartElement("library_effects");

            Random rand = new Random();
            for (int i = 0; i < count; i++)
            {
                writer.WriteStartElement("effect");
                writer.WriteAttributeString("id", string.Format("material{0}-fx", i));

                writer.WriteStartElement("profile_COMMON");

                writer.WriteStartElement("technique");
                writer.WriteAttributeString("sid", "common");

                writer.WriteStartElement("phong");

                writer.WriteStartElement("emission");
                writer.WriteStartElement("color"); writer.WriteString("0 0 0 1 "); writer.WriteEndElement();
                writer.WriteEndElement(); // emission

                writer.WriteStartElement("ambient");
                writer.WriteStartElement("color"); writer.WriteString("0 0 0 1 "); writer.WriteEndElement();
                writer.WriteEndElement(); // ambient

                writer.WriteStartElement("diffuse");
                float r = rand.Next(0, 255) / 255f;
                float g = rand.Next(0, 255) / 255f;
                float b = rand.Next(0, 255) / 255f;

                writer.WriteStartElement("color"); writer.WriteString(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000000} {1:0.000000} {2:0.000000} 1 ", r, g, b)); writer.WriteEndElement();
                writer.WriteEndElement(); // diffuse

                writer.WriteStartElement("specular");
                writer.WriteStartElement("color"); writer.WriteString("0.5 0.5 0.5 1 "); writer.WriteEndElement();
                writer.WriteEndElement(); // specular

                writer.WriteStartElement("shininess");
                writer.WriteStartElement("float"); writer.WriteString("16"); writer.WriteEndElement();
                writer.WriteEndElement(); // shininess

                writer.WriteStartElement("reflective");
                writer.WriteStartElement("float"); writer.WriteString("0.5"); writer.WriteEndElement();
                writer.WriteEndElement(); // reflective

                writer.WriteStartElement("reflectivity");
                writer.WriteStartElement("color"); writer.WriteString("0 0 0 1 "); writer.WriteEndElement();
                writer.WriteEndElement(); // reflectivity

                writer.WriteStartElement("transparent");
                writer.WriteStartElement("color"); writer.WriteString("0 0 0 1 "); writer.WriteEndElement();
                writer.WriteEndElement(); // transparent

                writer.WriteStartElement("transparency");
                writer.WriteStartElement("float"); writer.WriteString("1"); writer.WriteEndElement();
                writer.WriteEndElement(); // transparency

                writer.WriteStartElement("index_of_refraction");
                writer.WriteStartElement("float"); writer.WriteString("0"); writer.WriteEndElement();
                writer.WriteEndElement(); // index_of_refraction

                writer.WriteEndElement(); // phong

                writer.WriteEndElement(); // technique

                writer.WriteEndElement(); // profile_COMMON

                writer.WriteEndElement(); // effect
            }
            writer.WriteEndElement(); // library_effects
        }

        private static void writeDefaultMaterials(XmlWriter writer, int count)
        {
            writer.WriteStartElement("library_materials");

            for (int i = 0; i < count; i++)
            { 
                writer.WriteStartElement("material");
                writer.WriteAttributeString("id", string.Format("material{0}", i));
                writer.WriteAttributeString("name", string.Format("material{0}", i));

                writer.WriteStartElement("instance_effect");
                writer.WriteAttributeString("url", string.Format("#material{0}-fx", i));
                writer.WriteEndElement(); // instance_effect
                
                writer.WriteEndElement(); // material
            }

            writer.WriteEndElement(); // library_materials
        }

        private static void writeDefaultScene(Model.Lod model, XmlWriter writer, string geoID)
        {
            writer.WriteStartElement("library_visual_scenes");

            writer.WriteStartElement("visual_scene");
            writer.WriteAttributeString("id", "VisualSceneNode");
            writer.WriteAttributeString("name", "VisualSceneNode");

            writer.WriteStartElement("node");
            writer.WriteAttributeString("id", geoID);
            writer.WriteAttributeString("name", geoID);

            if (model.Skeleton != null)
            {
                exportSkeleton(model, writer);
                
                writer.WriteStartElement("instance_controller");
                writer.WriteAttributeString("url", "#exported-lib-skin");
                writer.WriteStartElement("skeleton");
                writer.WriteString("#" + model.Skeleton[0].Name.ToString());
                writer.WriteEndElement();

                writer.WriteStartElement("bind_material");
                writer.WriteStartElement("technique_common");
                for (int i = 0; i < model.Parts.Length; i++)
                {

                    writer.WriteStartElement("instance_material");
                    writer.WriteAttributeString("symbol", string.Format("material{0}SG", i));
                    writer.WriteAttributeString("target", string.Format("#material{0}", i));

                    for (int j = 0; j < model.NumUVChannels; j++)
                    {
                        writer.WriteStartElement("bind");
                        writer.WriteAttributeString("semantic", string.Format("UVSET{0}", j));
                        writer.WriteAttributeString("target", string.Format("#{0}-lib-map{1}", geoID, j));
                        writer.WriteEndElement(); // bind
                    }
                    writer.WriteEndElement(); // bind_material
                }
                writer.WriteEndElement(); // instance_material
                writer.WriteEndElement(); // technique_common

                writer.WriteEndElement(); // instance_controller
            }
            else
            {

                writer.WriteStartElement("instance_geometry");
                writer.WriteAttributeString("url", string.Format("#{0}-lib", geoID));

                writer.WriteStartElement("bind_material");
                writer.WriteStartElement("technique_common");
                for (int i = 0; i < model.Parts.Length; i++)
                {
                    writer.WriteStartElement("instance_material");
                    writer.WriteAttributeString("symbol", string.Format("material{0}SG", i));
                    writer.WriteAttributeString("target", string.Format("#material{0}", i));

                    for (int j = 0; j < model.NumUVChannels; j++)
                    {
                        writer.WriteStartElement("bind");
                        writer.WriteAttributeString("semantic", string.Format("UVSET{0}", j));
                        writer.WriteAttributeString("target", string.Format("#{0}-lib-map{1}", geoID, j));
                        writer.WriteEndElement(); // bind
                    }
                    writer.WriteEndElement(); // instance_material
                }
                writer.WriteEndElement(); // technique_common
                writer.WriteEndElement(); // bind_material

                writer.WriteEndElement(); // instance_geometry
            }
            writer.WriteEndElement(); // node
            
            writer.WriteEndElement(); // visual_scene
            
            writer.WriteEndElement(); // library_visual_scenes
        
            writer.WriteStartElement("scene");
            writer.WriteStartElement("instance_visual_scene");
            writer.WriteAttributeString("url", "#VisualSceneNode");
            writer.WriteEndElement(); // instance_visual_scene
            writer.WriteEndElement(); // scene
        }
    }
}
