示例#1
0
        /// <summary>
        ///     Exports a model to the Wavefront OBJ format.
        /// </summary>
        /// <param name="model">The model to be exported</param>
        /// <param name="fileName">The output file name</param>
        /// <param name="modelIndex">The index of the model that should be exported</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex)
        {
            StringBuilder output = new StringBuilder();

            RenderBase.OModel mdl = model.model[modelIndex];

            int faceIndexBase = 1;

            for (int objIndex = 0; objIndex < mdl.mesh.Count; objIndex++)
            {
                output.AppendLine("g " + mdl.mesh[objIndex].name);
                output.AppendLine(null);

                output.AppendLine("usemtl " + mdl.material[mdl.mesh[objIndex].materialId].name0 + ".png");
                output.AppendLine(null);

                MeshUtils.optimizedMesh obj = MeshUtils.optimizeMesh(mdl.mesh[objIndex]);
                foreach (RenderBase.OVertex vertex in obj.vertices)
                {
                    output.AppendLine("v " + getString(vertex.position.x) + " " + getString(vertex.position.y) + " " + getString(vertex.position.z));
                    output.AppendLine("vn " + getString(vertex.normal.x) + " " + getString(vertex.normal.y) + " " + getString(vertex.normal.z));
                    output.AppendLine("vt " + getString(vertex.texture0.x) + " " + getString(vertex.texture0.y));
                }
                output.AppendLine(null);

                for (int i = 0; i < obj.indices.Count; i += 3)
                {
                    output.AppendLine(
                        string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}",
                                      faceIndexBase + obj.indices[i],
                                      faceIndexBase + obj.indices[i + 1],
                                      faceIndexBase + obj.indices[i + 2]));
                }
                faceIndexBase += obj.vertices.Count;
                output.AppendLine(null);
            }

            File.WriteAllText(fileName, output.ToString());
        }
示例#2
0
        /// <summary>
        ///     Exports a Model to the Collada format.
        ///     See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information.
        /// </summary>
        /// <param name="model">The Model that will be exported</param>
        /// <param name="fileName">The output File Name</param>
        /// <param name="modelIndex">Index of the model to be exported</param>
        /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1)
        {
            RenderBase.OModel mdl = model.model[modelIndex];
            COLLADA           dae = new COLLADA();

            dae.asset.created  = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ");
            dae.asset.modified = dae.asset.created;

            foreach (RenderBase.OTexture tex in model.texture)
            {
                daeImage img = new daeImage();
                img.id        = tex.name + "_id";
                img.name      = tex.name;
                img.init_from = "./" + tex.name + ".png";

                dae.library_images.Add(img);
            }

            foreach (RenderBase.OMaterial mat in mdl.material)
            {
                daeMaterial mtl = new daeMaterial();
                mtl.name = mat.name + "_mat";
                mtl.id   = mtl.name + "_id";
                mtl.instance_effect.url = "#eff_" + mat.name + "_id";

                dae.library_materials.Add(mtl);

                daeEffect eff = new daeEffect();
                eff.id   = "eff_" + mat.name + "_id";
                eff.name = "eff_" + mat.name;

                daeParam surface = new daeParam();
                surface.surface           = new daeParamSurfaceElement();
                surface.sid               = "img_surface";
                surface.surface.type      = "2D";
                surface.surface.init_from = mat.name0 + "_id";
                surface.surface.format    = "PNG";
                eff.profile_COMMON.newparam.Add(surface);

                daeParam sampler = new daeParam();
                sampler.sampler2D        = new daeParamSampler2DElement();
                sampler.sid              = "img_sampler";
                sampler.sampler2D.source = "img_surface";

                switch (mat.textureMapper[0].wrapU)
                {
                case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break;

                case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break;

                case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break;

                case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break;

                default: sampler.sampler2D.wrap_s = "NONE"; break;
                }

                switch (mat.textureMapper[0].wrapV)
                {
                case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break;

                case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break;

                case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break;

                case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break;

                default: sampler.sampler2D.wrap_t = "NONE"; break;
                }

                switch (mat.textureMapper[0].minFilter)
                {
                case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break;

                case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break;

                case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break;

                case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break;

                default: sampler.sampler2D.minfilter = "NONE"; break;
                }

                switch (mat.textureMapper[0].magFilter)
                {
                case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break;

                case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break;

                default: sampler.sampler2D.magfilter = "NONE"; break;
                }

                sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter;

                eff.profile_COMMON.newparam.Add(sampler);

                eff.profile_COMMON.technique.sid = "img_technique";
                eff.profile_COMMON.technique.phong.emission.set(Color.Black);
                eff.profile_COMMON.technique.phong.ambient.set(Color.Black);
                eff.profile_COMMON.technique.phong.specular.set(Color.White);
                eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler";

                dae.library_effects.Add(eff);
            }

            string jointNames   = null;
            string invBindPoses = null;

            for (int index = 0; index < mdl.skeleton.Count; index++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformSkeleton(mdl.skeleton, index, ref transform);

                jointNames += mdl.skeleton[index].name;
                daeMatrix mtx = new daeMatrix();
                mtx.set(transform.invert());
                invBindPoses += mtx.data;
                if (index < mdl.skeleton.Count - 1)
                {
                    jointNames   += " ";
                    invBindPoses += " ";
                }
            }

            int            meshIndex = 0;
            daeVisualScene vs        = new daeVisualScene();

            vs.name = "vs_" + mdl.name;
            vs.id   = vs.name + "_id";
            if (mdl.skeleton.Count > 0)
            {
                writeSkeleton(mdl.skeleton, 0, ref vs.node);
            }
            foreach (RenderBase.OMesh obj in mdl.mesh)
            {
                //Geometry
                daeGeometry geometry = new daeGeometry();

                string meshName = "mesh_" + meshIndex++ + "_" + obj.name;
                geometry.id   = meshName + "_id";
                geometry.name = meshName;

                MeshUtils.optimizedMesh mesh      = MeshUtils.optimizeMesh(obj);
                List <float>            positions = new List <float>();
                List <float>            normals   = new List <float>();
                List <float>            uv0       = new List <float>();
                List <float>            uv1       = new List <float>();
                List <float>            uv2       = new List <float>();
                List <float>            colors    = new List <float>();
                foreach (RenderBase.OVertex vtx in mesh.vertices)
                {
                    positions.Add(vtx.position.x);
                    positions.Add(vtx.position.y);
                    positions.Add(vtx.position.z);

                    if (mesh.hasNormal)
                    {
                        normals.Add(vtx.normal.x);
                        normals.Add(vtx.normal.y);
                        normals.Add(vtx.normal.z);
                    }

                    if (mesh.texUVCount > 0)
                    {
                        uv0.Add(vtx.texture0.x);
                        uv0.Add(vtx.texture0.y);
                    }

                    if (mesh.texUVCount > 1)
                    {
                        uv1.Add(vtx.texture1.x);
                        uv1.Add(vtx.texture1.y);
                    }

                    if (mesh.texUVCount > 2)
                    {
                        uv2.Add(vtx.texture2.x);
                        uv2.Add(vtx.texture2.y);
                    }

                    if (mesh.hasColor)
                    {
                        colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f);
                        colors.Add((vtx.diffuseColor & 0xff) / 255f);
                        colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f);
                    }
                }

                daeSource position = new daeSource();
                position.name           = meshName + "_position";
                position.id             = position.name + "_id";
                position.float_array    = new daeFloatArray();
                position.float_array.id = position.name + "_array_id";
                position.float_array.set(positions);
                position.technique_common.accessor.source = "#" + position.float_array.id;
                position.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                position.technique_common.accessor.stride = 3;
                position.technique_common.accessor.addParam("X", "float");
                position.technique_common.accessor.addParam("Y", "float");
                position.technique_common.accessor.addParam("Z", "float");

                geometry.mesh.source.Add(position);

                daeSource normal = new daeSource();
                if (mesh.hasNormal)
                {
                    normal.name           = meshName + "_normal";
                    normal.id             = normal.name + "_id";
                    normal.float_array    = new daeFloatArray();
                    normal.float_array.id = normal.name + "_array_id";
                    normal.float_array.set(normals);
                    normal.technique_common.accessor.source = "#" + normal.float_array.id;
                    normal.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    normal.technique_common.accessor.stride = 3;
                    normal.technique_common.accessor.addParam("X", "float");
                    normal.technique_common.accessor.addParam("Y", "float");
                    normal.technique_common.accessor.addParam("Z", "float");

                    geometry.mesh.source.Add(normal);
                }

                daeSource[] texUV = new daeSource[3];
                for (int i = 0; i < mesh.texUVCount; i++)
                {
                    texUV[i] = new daeSource();

                    texUV[i].name           = meshName + "_uv" + i;
                    texUV[i].id             = texUV[i].name + "_id";
                    texUV[i].float_array    = new daeFloatArray();
                    texUV[i].float_array.id = texUV[i].name + "_array_id";
                    texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id;
                    texUV[i].technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    texUV[i].technique_common.accessor.stride = 2;
                    texUV[i].technique_common.accessor.addParam("S", "float");
                    texUV[i].technique_common.accessor.addParam("T", "float");

                    geometry.mesh.source.Add(texUV[i]);
                }

                daeSource color = new daeSource();
                if (mesh.hasColor)
                {
                    color.name           = meshName + "_color";
                    color.id             = color.name + "_id";
                    color.float_array    = new daeFloatArray();
                    color.float_array.id = color.name + "_array_id";
                    color.float_array.set(colors);
                    color.technique_common.accessor.source = "#" + color.float_array.id;
                    color.technique_common.accessor.count  = (uint)mesh.vertices.Count;
                    color.technique_common.accessor.stride = 4;
                    color.technique_common.accessor.addParam("R", "float");
                    color.technique_common.accessor.addParam("G", "float");
                    color.technique_common.accessor.addParam("B", "float");
                    color.technique_common.accessor.addParam("A", "float");

                    geometry.mesh.source.Add(color);
                }

                geometry.mesh.vertices.id = meshName + "_vertices_id";
                geometry.mesh.vertices.addInput("POSITION", "#" + position.id);


                geometry.mesh.triangles.material = mdl.material[obj.materialId].name;
                geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id);
                if (mesh.hasNormal)
                {
                    geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id);
                }
                if (mesh.hasColor)
                {
                    geometry.mesh.triangles.addInput("COLOR", "#" + color.id);
                }
                if (mesh.texUVCount > 0)
                {
                    texUV[0].float_array.set(uv0);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id);
                }
                if (mesh.texUVCount > 1)
                {
                    texUV[1].float_array.set(uv1);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1);
                }
                if (mesh.texUVCount > 2)
                {
                    texUV[2].float_array.set(uv2);
                    geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2);
                }
                geometry.mesh.triangles.set(mesh.indices);

                dae.library_geometries.Add(geometry);

                bool hasNode       = obj.vertices[0].node.Count > 0;
                bool hasWeight     = obj.vertices[0].weight.Count > 0;
                bool hasController = hasNode && hasWeight;

                //Controller
                daeController controller = new daeController();
                if (hasController)
                {
                    controller.id = meshName + "_ctrl_id";

                    controller.skin.source = "#" + geometry.id;
                    controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix());

                    daeSource joints = new daeSource();
                    joints.id               = meshName + "_ctrl_joint_names_id";
                    joints.Name_array       = new daeNameArray();
                    joints.Name_array.id    = meshName + "_ctrl_joint_names_array_id";
                    joints.Name_array.count = (uint)mdl.skeleton.Count;
                    joints.Name_array.data  = jointNames;
                    joints.technique_common.accessor.source = "#" + joints.Name_array.id;
                    joints.technique_common.accessor.count  = joints.Name_array.count;
                    joints.technique_common.accessor.stride = 1;
                    joints.technique_common.accessor.addParam("JOINT", "Name");

                    controller.skin.src.Add(joints);

                    daeSource bindPoses = new daeSource();
                    bindPoses.id                = meshName + "_ctrl_inv_bind_poses_id";
                    bindPoses.float_array       = new daeFloatArray();
                    bindPoses.float_array.id    = meshName + "_ctrl_inv_bind_poses_array_id";
                    bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16);
                    bindPoses.float_array.data  = invBindPoses;
                    bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id;
                    bindPoses.technique_common.accessor.count  = (uint)mdl.skeleton.Count;
                    bindPoses.technique_common.accessor.stride = 16;
                    bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4");

                    controller.skin.src.Add(bindPoses);

                    daeSource weights = new daeSource();
                    weights.id             = meshName + "_ctrl_weights_id";
                    weights.float_array    = new daeFloatArray();
                    weights.float_array.id = meshName + "_ctrl_weights_array_id";
                    weights.technique_common.accessor.source = "#" + weights.float_array.id;
                    weights.technique_common.accessor.stride = 1;
                    weights.technique_common.accessor.addParam("WEIGHT", "float");

                    StringBuilder w      = new StringBuilder();
                    StringBuilder vcount = new StringBuilder();
                    StringBuilder v      = new StringBuilder();

                    float[] wLookBack      = new float[32];
                    uint    wLookBackIndex = 0;
                    int     buffLen        = 0;

                    int wIndex = 0;
                    int wCount = 0;
                    foreach (RenderBase.OVertex vtx in mesh.vertices)
                    {
                        int count = Math.Min(vtx.node.Count, vtx.weight.Count);

                        vcount.Append(count + " ");
                        for (int n = 0; n < count; n++)
                        {
                            v.Append(vtx.node[n] + " ");
                            bool found = false;
                            uint bPos  = (wLookBackIndex - 1) & 0x1f;
                            for (int i = 0; i < buffLen; i++)
                            {
                                if (wLookBack[bPos] == vtx.weight[n])
                                {
                                    v.Append(wIndex - (i + 1) + " ");
                                    found = true;
                                    break;
                                }
                                bPos = (bPos - 1) & 0x1f;
                            }

                            if (!found)
                            {
                                v.Append(wIndex++ + " ");
                                w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " ");
                                wCount++;

                                wLookBack[wLookBackIndex] = vtx.weight[n];
                                wLookBackIndex            = (wLookBackIndex + 1) & 0x1f;
                                if (buffLen < wLookBack.Length)
                                {
                                    buffLen++;
                                }
                            }
                        }
                    }

                    weights.float_array.data  = w.ToString().TrimEnd();
                    weights.float_array.count = (uint)wCount;
                    weights.technique_common.accessor.count = (uint)wCount;

                    controller.skin.src.Add(weights);
                    controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd();
                    controller.skin.vertex_weights.v      = v.ToString().TrimEnd();
                    controller.skin.vertex_weights.count  = (uint)mesh.vertices.Count;
                    controller.skin.joints.addInput("JOINT", "#" + joints.id);
                    controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id);

                    controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id);
                    controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1);

                    if (dae.library_controllers == null)
                    {
                        dae.library_controllers = new List <daeController>();
                    }
                    dae.library_controllers.Add(controller);
                }

                //Visual scene node
                daeNode node = new daeNode();
                node.name = "vsn_" + meshName;
                node.id   = node.name + "_id";
                node.matrix.set(new RenderBase.OMatrix());
                if (hasController)
                {
                    node.instance_controller          = new daeInstanceController();
                    node.instance_controller.url      = "#" + controller.id;
                    node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id";
                    node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }
                else
                {
                    node.instance_geometry     = new daeInstanceGeometry();
                    node.instance_geometry.url = "#" + geometry.id;
                    node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name;
                    node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id";
                }

                vs.node.Add(node);
            }
            dae.library_visual_scenes.Add(vs);

            daeInstaceVisualScene scene = new daeInstaceVisualScene();

            scene.url = "#" + vs.id;
            dae.scene.Add(scene);

            XmlWriterSettings settings = new XmlWriterSettings
            {
                Encoding = Encoding.UTF8,
                Indent   = true
            };

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

            ns.Add("", "http://www.collada.org/2005/11/COLLADASchema");
            XmlSerializer serializer = new XmlSerializer(typeof(COLLADA));
            XmlWriter     output     = XmlWriter.Create(new FileStream(fileName, FileMode.Create), settings);

            serializer.Serialize(output, dae, ns);
            output.Close();
        }
示例#3
0
        /// <summary>
        ///     Exports a model to the Wavefront OBJ format.
        /// </summary>
        /// <param name="model">The model to be exported</param>
        /// <param name="fileName">The output file name</param>
        /// <param name="modelIndex">The index of the model that should be exported</param>
        public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex)
        {
            for (int i = 0; i < model.texture.Count; i++)
            {
                if (model.texture[i].name.Contains("_alb"))
                {
                    model.texture[i].texture.Save(Path.Combine(Path.GetDirectoryName(fileName), "Textures", model.texture[i].name + ".bmp"));
                }
            }

            if (model.model.Count == 0)
            {
                return;
            }

            StringBuilder output = new StringBuilder();

            RenderBase.OModel mdl = model.model[modelIndex];

            output.AppendLine($"mtllib {Path.GetFileNameWithoutExtension(fileName)}.mtl");

            int faceIndexBase = 1;

            for (int objIndex = 0; objIndex < mdl.mesh.Count; objIndex++)
            {
                output.AppendLine("g " + mdl.mesh[objIndex].name);
                output.AppendLine(null);

                output.AppendLine("usemtl " + mdl.material[mdl.mesh[objIndex].materialId].name0);

                output.AppendLine(null);

                MeshUtils.optimizedMesh obj = MeshUtils.optimizeMesh(mdl.mesh[objIndex]);
                foreach (RenderBase.OVertex vertex in obj.vertices)
                {
                    output.AppendLine("v " + getString(vertex.position.x) + " " + getString(vertex.position.y) + " " + getString(vertex.position.z));
                    output.AppendLine("vn " + getString(vertex.normal.x) + " " + getString(vertex.normal.y) + " " + getString(vertex.normal.z));
                    output.AppendLine("vt " + getString(vertex.texture0.x) + " " + getString(vertex.texture0.y));
                }
                output.AppendLine(null);

                for (int i = 0; i < obj.indices.Count; i += 3)
                {
                    output.AppendLine(
                        string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}",
                                      faceIndexBase + obj.indices[i],
                                      faceIndexBase + obj.indices[i + 1],
                                      faceIndexBase + obj.indices[i + 2]));
                }
                faceIndexBase += obj.vertices.Count;
                output.AppendLine(null);
            }

            File.WriteAllText(fileName, output.ToString());

            if (mdl.material.Count == 0)
            {
                return;
            }

            output = new StringBuilder();

            List <string> MatNames = new List <string>();

            foreach (RenderBase.OMaterial mat in mdl.material)
            {
                if (!MatNames.Contains(mat.name0))
                {
                    MatNames.Add(mat.name0);
                    output.AppendLine(null);
                    output.AppendLine("newmtl " + mat.name0);
                    output.AppendLine("Ka " + string.Format("{0} {1} {2}",
                                                            mat.materialColor.ambient.R.ToString(),
                                                            mat.materialColor.ambient.G.ToString(),
                                                            mat.materialColor.ambient.B.ToString()));
                    output.AppendLine("Kd " + string.Format("{0} {1} {2}",
                                                            mat.materialColor.diffuse.R.ToString(),
                                                            mat.materialColor.diffuse.G.ToString(),
                                                            mat.materialColor.diffuse.B.ToString()));
                    output.AppendLine("Ks 0 0 0");
                    output.AppendLine("d 1");
                    if (mat.name0 != null)
                    {
                        output.AppendLine("map_Kd " + @"Textures\" + mat.name0 + ".bmp");
                    }
                }
            }
            File.WriteAllText(Path.GetDirectoryName(fileName) + "\\" + Path.GetFileNameWithoutExtension(fileName) + ".mtl", output.ToString());
        }
        private mesh createMesh(RenderBase.OMesh input)
        {
            mesh output;

            output.descriptor.nodes      = new List <uint>();
            output.descriptor.attributes = new List <attributeDescriptor>();

            output.descriptor.attributes.Add(new attributeDescriptor(0, 0, 1f)); //Position
            if (input.hasNormal)
            {
                output.descriptor.attributes.Add(new attributeDescriptor(1, 0, 1f));
            }
            if (input.texUVCount > 0)
            {
                output.descriptor.attributes.Add(new attributeDescriptor(3, 0, 1f));
            }
            if (input.hasNode)
            {
                output.descriptor.attributes.Add(new attributeDescriptor(5, 1, 1f));
            }
            if (input.hasWeight)
            {
                output.descriptor.attributes.Add(new attributeDescriptor(6, 1, 0.00392156862f));
            }

            MeshUtils.optimizedMesh optimized = MeshUtils.optimizeMesh(input);
            using (MemoryStream vertexStream = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(vertexStream);

                foreach (RenderBase.OVertex vtx in optimized.vertices)
                {
                    writer.Write(vtx.position.x);
                    writer.Write(vtx.position.y);
                    writer.Write(vtx.position.z);

                    if (optimized.hasNormal)
                    {
                        writer.Write(vtx.normal.x);
                        writer.Write(vtx.normal.y);
                        writer.Write(vtx.normal.z);
                    }

                    if (optimized.texUVCount > 0)
                    {
                        writer.Write(vtx.texture0.x);
                        writer.Write(vtx.texture0.y);
                    }

                    if (optimized.hasNode)
                    {
                        for (int i = 0; i < 2; i++)
                        {
                            if (i < vtx.node.Count)
                            {
                                int nodeIndex = output.descriptor.nodes.IndexOf((uint)vtx.node[i]);
                                if (nodeIndex == -1)
                                {
                                    writer.Write((byte)output.descriptor.nodes.Count);
                                    output.descriptor.nodes.Add((uint)vtx.node[i]);
                                }
                                else
                                {
                                    writer.Write((byte)nodeIndex);
                                }
                            }
                            else
                            {
                                writer.Write((byte)0);
                            }
                        }
                    }

                    if (optimized.hasWeight)
                    {
                        for (int i = 0; i < 2; i++)
                        {
                            if (i < vtx.weight.Count)
                            {
                                writer.Write((byte)(vtx.weight[i] * byte.MaxValue));
                            }
                            else
                            {
                                writer.Write((byte)0);
                            }
                        }
                    }
                }

                output.vertexBuffer = vertexStream.ToArray();
            }

            using (MemoryStream indexStream = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(indexStream);
                foreach (uint index in optimized.indices)
                {
                    writer.Write((ushort)index);
                }
                output.indexBuffer = indexStream.ToArray();
            }

            return(output);
        }