예제 #1
0
        public CMesh CreateMorphedMesh(CMorph morph)
        {
            // shallow copy
            CMesh clone = (CMesh)this.MemberwiseClone();

            clone.IsMorphed    = morph.MorphMesh(clone);
            Shapes[clone.Name] = 0f;  // weight setting 0-1f
            return(clone);
        }
예제 #2
0
        public void AddNormal(float[] xyz) => mNorms.Add(new Vector3DF(xyz));  //.Flip() Actually, Normals makes no difference if imported by collada into blender

        public bool MorphMesh(CMesh targetMesh)
        {
            if ((targetMesh?.mVerts.Count ?? 0) != mVerts.Count || String.IsNullOrWhiteSpace(targetMesh.Name))
            {
                return(false);
            }
            //targetMesh.mVerts.Clear(); might be undesired if data is referenced somewhere else
            //targetMesh.mNorms.Clear();
            targetMesh.mVerts = mVerts;
            targetMesh.mNorms = mNorms;

            // Move morph-target name and set morph shape name.
            targetMesh.OriginName = targetMesh.Name;
            targetMesh.Name       = Name.StartsWith("morph") ? Name : "morph_" + Name;
            return(true); // targetMesh.IsValid();
        }
예제 #3
0
        public void Serialize(Stream dst, Entities data)
        {
            if (null == dst || null == data.meshes || null == data.rootNode)
            {
                throw new ArgumentException("CDaeSerializer::Serialize: Passed 'null'-Argument");
            }

            XmlWriter writer = XmlWriter.Create(dst, new XmlWriterSettings()
            {
                Indent      = true,
                IndentChars = "\t",

                // defaults
                NewLineChars        = "\r\n",
                NewLineHandling     = NewLineHandling.Replace,
                NewLineOnAttributes = false,
                Encoding            = Encoding.UTF8
            });

            var materialNames = data.meshes
                                .SelectMany(msh => msh.MaterialSlots)
                                .Select(mat => mat.name)
                                .Distinct();

            dst.Position = 0;
            string            srcId, baseName;
            List <Mdl.CFrame> nodesList = data.rootNode.Childs;

            if (!String.IsNullOrWhiteSpace(RootName))
            {
                baseName = RootName;
            }
            else if (!String.IsNullOrWhiteSpace(data.rootNode.Name))
            {
                baseName = data.rootNode.Name;
            }
            else
            {
                baseName = "RootNode";
            }

            // XML Header
            writer.WriteStartDocument();
            writer.WriteStartElement("COLLADA", "http://www.collada.org/2005/11/COLLADASchema");            // root with default namespace
            writer.WriteAttributeString("version", "1.4.1");
            writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); // declares a namespace prefixed with xsi

            #region File meta
            writer.WriteStartElement("asset");
            {
                writer.WriteStartElement("contributor");
                {
                    writer.WriteElementString("author", "Nitro+");
                    writer.WriteElementString("authoring_tool", "Nitro Mdl-Converter");
                    writer.WriteEndElement();
                }
                writer.WriteElementString("created", DateTime.Now.ToString("s"));

                // Unit
                writer.WriteStartElement("unit");
                writer.WriteAttributeString(NAME, "millimeter");  // Millimeters?
                writer.WriteAttributeString("meter", "0.001");
                writer.WriteEndElement();

                writer.WriteElementString("up_axis", "Z_UP"); // Geometries seem to be at least on Zup
                writer.WriteEndElement();                     // asset
            }
            #endregion

            // Texture paths (not supported, requires parsing .mtl files)
            writer.WriteStartElement("library_images");
            writer.WriteEndElement();

            #region Material shading
            writer.WriteStartElement("library_effects");
            {
                writer.WriteStartElement("effect");
                { writer.WriteAttributeString(ID, "shadeless-effect");

                  writer.WriteStartElement("profile_COMMON");
                  {
                      writer.WriteStartElement("technique");
                      { writer.WriteAttributeString(SID, "common");

                        writer.WriteStartElement("lambert");
                        {
                            addEffectItem("emission", COLOR, V4_ZERO);
                            addEffectItem("ambient", COLOR, V4_ZERO);
                            addEffectItem("diffuse", COLOR, "1 1 1 1");
                            //addEffectItem("specular", COLOR, V4_HALF);
                            //addEffectItem("shininess", FLOAT, "50");
                            addEffectItem("index_of_refraction", FLOAT, "1");
                            writer.WriteEndElement(); // phong
                        }
                        writer.WriteEndElement();     // technique
                      }
                      writer.WriteEndElement();       // profile
                  }
                  writer.WriteEndElement();           // effect
                }
                writer.WriteEndElement();             // effects

                void addEffectItem(string item, string type, string value)
                {
                    writer.WriteStartElement(item);
                    writer.WriteStartElement(type);
                    writer.WriteAttributeString(SID, item);
                    writer.WriteString(value);
                    writer.WriteEndElement();  // type
                    writer.WriteEndElement();  // item
                }
            }
            #endregion

            #region Materials
            writer.WriteStartElement("library_materials");
            foreach (string name in materialNames)
            {
                writer.WriteStartElement("material");
                writer.WriteAttributeString(ID, $"{name}-material");
                writer.WriteAttributeString(NAME, name);
                writer.WriteStartElement("instance_effect");
                writer.WriteAttributeString("url", "#shadeless-effect");
                writer.WriteEndElement(); // instance_effect
                writer.WriteEndElement(); // material
            }
            writer.WriteEndElement();     // materials
            #endregion

            #region Meshes
            writer.WriteStartElement("library_geometries");
            {
                string    meshId;
                bool      hasUV;
                Mdl.CMesh mesh;
                var       meshGrps = data.meshes.GroupBy(msh => msh.Name);
                foreach (var meshSet in meshGrps)
                {
                    mesh = meshSet.First(msh => msh.IsValid() && !String.IsNullOrWhiteSpace(msh.Name));
                    if ((null == mesh) || mesh.HasInvalidFaces())
                    {
                        Console.WriteLine("CDaeSerializer: Skipped a mesh geometry!");
                        continue;
                    }
                    hasUV = mesh.HasUV();

                    meshId = mesh.IsMorphed ? $"{mesh.OriginName}_{mesh.Name}{_MESH}" : mesh.Name + _MESH;
                    writer.WriteStartElement("geometry");
                    { writer.WriteAttributeString(ID, meshId);
                      writer.WriteAttributeString(NAME, mesh.Name);

                      writer.WriteStartElement("mesh");
                      {
                          // 3D Coordinates (mesh)
                          addSource3D(mesh.Vertices, id: $"{meshId}-positions");
                          addSource3D(mesh.Normals, id: $"{meshId}-normals");

                          // UV mapping
                          if (hasUV)
                          {
                              writer.WriteStartElement(SOURCE);
                              { writer.WriteAttributeString(ID, srcId = $"{meshId}-map-0");

                                var pts2D = mesh.TexCoords;
                                int cnt   = pts2D.Count;
                                writer.WriteStartElement("float_array");
                                { writer.WriteAttributeString(ID, srcId += _ARRAY);
                                    writer.WriteAttributeString(COUNT, (2 * cnt).ToString());

                                    writer.WriteString(pts2D[0].ToString());
                                    for (int vn = 1; vn < cnt; ++vn)
                                    {
                                        writer.WriteWhitespace(SEPARATOR);
                                        writer.WriteString(pts2D[vn].ToString());
                                    }
                                    writer.WriteEndElement();      // float array
                                }
                                addTechCommon(srcId, cnt, 2, FLOAT, "S", "T");
                                writer.WriteEndElement();      // map-0
                              }
                          }  // has uv

                          // Vertices
                          writer.WriteStartElement("vertices");
                          writer.WriteAttributeString("id", $"{meshId}-vertices");
                          writer.WriteAttElement(INPUT, ASTR_SEM_SRC, "POSITION", $"#{meshId}-positions");
                          writer.WriteEndElement();    // vertices

                          // Faces
                          var meshFaces = mesh.Faces;    // creates readOnly on call
                          var matRefs   = mesh.MaterialSlots;
                          for (int mn = 0; mn < matRefs.Count; ++mn)
                          {
                              addTrianglesByMaterial(meshFaces, matRefs[mn]);
                          }
                          writer.WriteEndElement(); // mesh
                      }
                      writer.WriteEndElement();     // geometry
                    }
                }// foreach mesh
                writer.WriteEndElement(); // geometries

                void addSource3D(IReadOnlyList <Vector3DF> pts3d, string id)
                {
                    writer.WriteStartElement(SOURCE);
                    { writer.WriteAttributeString(ID, id);

                      string mpa = id + _ARRAY;
                      writer.WriteStartElement("float_array");
                      { writer.WriteAttributeString(ID, mpa);
                        writer.WriteAttributeString(COUNT, (pts3d.Count * 3).ToString());

                        writer.WriteString(pts3d[0].ToString());      // not in specific xml format
                        for (int vn = 1; vn < pts3d.Count; ++vn)
                        {
                            writer.WriteWhitespace(SEPARATOR);
                            writer.WriteString(pts3d[vn].ToString());
                        }
                        writer.WriteEndElement();      // float array
                      }
                      addTechCommon(mpa, pts3d.Count, 3, FLOAT, "X", "Y", "Z");
                      writer.WriteEndElement();    // source
                    }
                }

                void addTrianglesByMaterial(IReadOnlyList <Triangle> triSrc, Reference matRef)
                {
                    if (matRef.IndexCount() < 1)
                    {// no faces assigned
                        return;
                    }
                    writer.WriteStartElement("triangles");
                    writer.WriteAttributeString("material", $"{matRef.name}-material");
                    writer.WriteAttributeString(COUNT, matRef.IndexCount().ToString());

                    // List mapped components
                    string[] inputAtrb = new string[] { SEMANTIC, SOURCE, "offset", "set" };
                    writer.WriteAttElement(INPUT, inputAtrb, "VERTEX", $"#{meshId}-vertices", "0");
                    writer.WriteAttElement(INPUT, inputAtrb, "NORMAL", $"#{meshId}-normals", "1");
                    if (hasUV)
                    {
                        writer.WriteAttElement(INPUT, inputAtrb, "TEXCOORD", $"#{meshId}-map-0", "2", "0");
                    }

                    // Write indices of each component that is mapped to the triangle edges (interlaced)
                    // Currently handles only rectangular mapping (each mapped component has equal element count)
                    writer.WriteStartElement("p");
                    long upperLim = Math.Min(matRef.iLast, triSrc.Count - 1);

                    for (int faceN = (int)Math.Min(matRef.iFirst, triSrc.Count); faceN <= upperLim; ++faceN)
                    {
                        ushort ia = triSrc[faceN].iVert1;
                        ushort ib = triSrc[faceN].iVert2;
                        ushort ic = triSrc[faceN].iVert3;
                        if (hasUV)
                        {// vertice, normal and texture coordinate indices
                            writer.WriteSpacedValues(ia, ia, ia, ib, ib, ib, ic, ic, ic);
                        }
                        else
                        {
                            writer.WriteSpacedValues(ia, ia, ib, ib, ic, ic);
                        }
                        if (faceN < upperLim)
                        {
                            writer.WriteWhitespace(SEPARATOR);
                        }
                    }
                    writer.WriteEndElement();  // p
                    writer.WriteEndElement();  // triangles
                }
            }
            #endregion

            var baseMeshes     = data.meshes.Where(msh => !msh.IsMorphed);
            int meshNodesCount = baseMeshes.Count();
            #region Skinning
            writer.WriteStartElement("library_controllers");
            if (nodesList.Count > 1)
            {
                IReadOnlyList <VertexGrp> meshWeights;
                int    weightsCnt;
                bool   hasBones, hasWeights;
                string frameName;
                var    frameGrps = nodesList.GetRange(1, nodesList.Count - 1) // leave out first, which is armature
                                   .Where(frm => !String.IsNullOrWhiteSpace(frm.Name))
                                   .GroupBy(frm => frm.Name);
                foreach (var frmSet in frameGrps)
                {
                    Mdl.CMesh mesh = null;
                    frameName = frmSet.First().Name;

                    // index 0 MUST be armature and the order of meshes and related nodes in the list must be same
                    int meshIdx = nodesList.FindIndex(frm => frm.Name == frameName) - 1;
                    if (meshIdx >= 0 && (meshIdx < meshNodesCount))
                    {
                        mesh = baseMeshes.ElementAt(meshIdx);
                    }
                    if (null == mesh)
                    {
                        continue;
                    }

                    srcId = $"{baseName}_{frameName}{_SKIN}";
                    writer.WriteStartElement("controller");
                    { writer.WriteAttributeString(ID, srcId);
                      writer.WriteAttributeString(NAME, baseName);

                      writer.WriteStartElement("skin");
                      { writer.WriteAttributeString(SOURCE, $"#{mesh.Name}{_MESH}");

                        // Transforms object to world space,
                        // also visible as mesh Transform
                        // Effectively moves origin of mesh, but must fit to bone transform
                        writer.WriteStartElement("bind_shape_matrix");
                        writer.WriteSpacedValues(
                            1, 0, 0, 0,
                            0, 1, 0, 0,
                            0, 0, 1, 0,
                            0, 0, 0, 1);          // identity
                        writer.WriteEndElement(); // bind shape

                        meshWeights = mesh.Weights;
                        weightsCnt  = meshWeights.Count;
                        if (weightsCnt < 1)
                        {
                            writer.WriteEndElement(); // skin
                            writer.WriteEndElement(); // controller
                            continue;                 // next mesh
                        }

                        // Bone src
                        var distinctBones = meshWeights
                                            .Select(w => w.groupName)
                                            .Where(gn => !String.IsNullOrWhiteSpace(gn))
                                            .Distinct(); // TODO: Merge identically named groups?
                        if (hasBones = distinctBones.Any())
                        {
                            writer.WriteStartElement(SOURCE);
                            { writer.WriteAttributeString(ID, srcId += "-joints");

                                writer.WriteStartElement("Name_array");
                                writer.WriteAttributeString(ID, srcId += _ARRAY);
                                writer.WriteAttributeString(COUNT, weightsCnt.ToString());

                                writer.WriteValue(distinctBones); // IEnumerable
                                writer.WriteEndElement();         // name array

                                addTechCommon(srcId, weightsCnt, stride: 1, type: NAME, values: "JOINT");
                                writer.WriteEndElement();      // source joints
                            }
                        }

                        // Bind-pose src
                        // transforms bones from world space to space of the root bone
                        srcId = $"{baseName}_{frameName}{_SKIN}";
                        writer.WriteStartElement(SOURCE);
                        { writer.WriteAttributeString(ID, srcId += "-bind_poses");

                            writer.WriteStartElement("float_array");
                            writer.WriteAttributeString(ID, srcId += _ARRAY);
                            writer.WriteAttributeString(COUNT, (weightsCnt * 16).ToString());

                            // TODO: matrices could be undefined
                            writer.WriteString(meshWeights[0].bindMatrix.ToString());      // returns 16 floats as string
                            for (int wn = 1; wn < weightsCnt; ++wn)
                            {
                                writer.WriteWhitespace(SEPARATOR);
                                writer.WriteString(meshWeights[wn].bindMatrix.ToString());
                            }
                            writer.WriteEndElement();      // array

                            addTechCommon(srcId, weightsCnt, stride: 16, type: "float4x4", values: "TRANSFORM");
                            writer.WriteEndElement();      // source binds
                        }

                        // Weight src
                        int weightsLen = mesh.WeightsLenght();
                        if (hasWeights = (weightsLen > 0))
                        {
                            srcId = $"{baseName}_{frameName}{_SKIN}";
                            writer.WriteStartElement(SOURCE);
                            { writer.WriteAttributeString(ID, srcId += "-weights");

                                writer.WriteStartElement("float_array");
                                writer.WriteAttributeString(ID, srcId += _ARRAY);
                                writer.WriteAttributeString(COUNT, weightsLen.ToString());

                                if (!meshWeights[0].IsNullOrEmpty())                  // Has assigned bone, but no weight paint
                                {
                                    writer.WriteValue(meshWeights[0].mapping.Values); // ValueCollection
                                }
                                for (int wn = 1; wn < weightsCnt; ++wn)
                                {
                                    if (!meshWeights[wn].IsNullOrEmpty())
                                    {
                                        writer.WriteWhitespace(SEPARATOR);
                                        writer.WriteValue(meshWeights[wn].mapping.Values);
                                    }
                                }
                                writer.WriteEndElement(); // array
                                addTechCommon(srcId, weightsLen, stride: 1, type: FLOAT, values: "WEIGHT");
                                writer.WriteEndElement(); // source weights
                            }
                        }                                 // if weights assigned

                        // Bone-binding
                        if (hasBones)
                        {
                            srcId = $"#{baseName}_{frameName}{_SKIN}";
                            writer.WriteStartElement("joints");
                            writer.WriteAttElement(INPUT, ASTR_SEM_SRC, "JOINT", srcId + "-joints");
                            writer.WriteAttElement(INPUT, ASTR_SEM_SRC, "INV_BIND_MATRIX", srcId + "-bind_poses");
                            writer.WriteEndElement();      // joints
                        }

                        // Vertex-Weight map
                        if (hasBones && hasWeights)
                        {
                            int vertCnt = mesh.VerticesCount();
                            writer.WriteStartElement("vertex_weights");
                            { writer.WriteAttributeString(COUNT, vertCnt.ToString());

                                string[] inputAtrb = new string[] { SEMANTIC, SOURCE, "offset" };
                                writer.WriteAttElement(INPUT, inputAtrb, "JOINT", srcId + "-joints", "0");
                                writer.WriteAttElement(INPUT, inputAtrb, "WEIGHT", srcId + "-weights", "1");

                                // Collect bindings
                                List <int> vMap          = new List <int>();
                                var        boneWeightSeq = new List <int>();
                                int        idxOffs;
                                writer.WriteStartElement("vcount"); // number of assigned bones per vertex
                                for (int vn = 0; vn < vertCnt; ++vn)
                                {                                   // iterate mesh vertices
                                    vMap.Clear();
                                    idxOffs = 0;
                                    Dictionary <int, float> wMap;
                                    for (int wn = 0; wn < weightsCnt; wn++)
                                    {    // iterate assigned bone influences
                                        wMap = meshWeights[wn].mapping;
                                        if ((wMap?.ContainsKey(vn) ?? false) &&
                                            !String.IsNullOrWhiteSpace(meshWeights[wn].groupName))
                                        {
                                            vMap.Add(wn);      // bone index
                                            int mN = 0;
                                            foreach (var kvp in wMap)
                                            {    // iterate map to find index of weight
                                                 // keys are not in order of addition, but sorted by weight-index
                                                 // the dictionary MUST NOT have changed since writing out the values
                                                if (kvp.Key == vn)
                                                {
                                                    vMap.Add(idxOffs + mN);    // map element index
                                                    break;
                                                }
                                                ++mN;
                                            }
                                        }
                                        idxOffs += wMap.Count;
                                    }
                                    writer.WriteValue(vMap.Count >> 1); writer.WriteWhitespace(SEPARATOR);
                                    boneWeightSeq.AddRange(vMap);
                                }
                                writer.WriteEndElement();      // vcount

                                writer.WriteStartElement("v");
                                writer.WriteValue(boneWeightSeq); // List<T>
                                boneWeightSeq.Clear();
                                writer.WriteEndElement();         // v
                                writer.WriteEndElement();         // vertex weights
                            }
                        }                                         // has bones or weight assigned
                        writer.WriteEndElement();                 // skin
                      }
                      writer.WriteEndElement();                   // controller
                    }
                }// each mesh
            }// if has nodes
            #endregion

            #region Morphes
            foreach (var mesh in baseMeshes)
            {
                var shapes = mesh.Shapes.Keys.ToArray();
                if (null == mesh || shapes.Length < 1)
                {
                    continue;
                }

                // Link shape keys to target mesh
                srcId = mesh.OriginName + "_" + mesh.Name;  // origin or base name?
                writer.WriteStartElement("controller");
                {
                    writer.WriteAttributeString(ID, srcId + "-morph");
                    writer.WriteAttributeString(NAME, mesh.Name);

                    writer.WriteStartElement("morph");
                    { writer.WriteAttributeString(SOURCE, "#" + mesh.Name + _MESH);    //references the geometry
                      writer.WriteAttributeString("method", "NORMALIZED");


                      writer.WriteStartElement(SOURCE);
                      { writer.WriteAttributeString(ID, srcId += "-targets");

                        writer.WriteStartElement("IDREF_array");
                        writer.WriteAttributeString(ID, srcId += _ARRAY);
                        writer.WriteAttributeString(COUNT, shapes.Length.ToString());

                        writer.WriteString($"{mesh.Name}_{shapes[0]}{_MESH}");
                        for (int i = 1; i < shapes.Length; ++i)
                        {
                            writer.WriteWhitespace(SEPARATOR); writer.WriteString($"{mesh.Name}_{shapes[i]}{_MESH}");
                        }
                        writer.WriteEndElement();      // array

                        addTechCommon(srcId, shapes.Length, stride: 1, type: "IDREF", values: "IDREF");
                        writer.WriteEndElement();      // target source
                      }
                      srcId = mesh.OriginName + "_" + mesh.Name;
                      writer.WriteStartElement(SOURCE);
                      { writer.WriteAttributeString(ID, srcId += "-weights");

                        writer.WriteStartElement("float_array");
                        writer.WriteAttributeString(ID, srcId += _ARRAY);
                        writer.WriteAttributeString(COUNT, shapes.Length.ToString());
                        writer.WriteValue(mesh.Shapes.Values);
                        writer.WriteEndElement();      // float array
                        addTechCommon(srcId, shapes.Length, stride: 1, type: FLOAT, values: "MORPH_WEIGHT");
                        writer.WriteEndElement();      // weight source
                      }
                      srcId = "#" + mesh.OriginName + "_" + mesh.Name;
                      writer.WriteStartElement("targets");
                      writer.WriteAttElement(INPUT, ASTR_SEM_SRC, "MORPH_TARGET", srcId + "-targets");
                      writer.WriteAttElement(INPUT, ASTR_SEM_SRC, "MORPH_WEIGHT", srcId + "-weights");
                      writer.WriteEndElement(); //targets
                      writer.WriteEndElement(); //morph
                    }
                    writer.WriteEndElement();   // morph controller
                }
            }// each non-morph

            writer.WriteEndElement();  // controller lib
            #endregion

            #region Object lib
            writer.WriteStartElement("library_visual_scenes");
            {
                writer.WriteStartElement("visual_scene");
                { writer.WriteAttributeString(ID, "Scene");
                  writer.WriteAttributeString(NAME, "Scene");

                  // Object nodes
                  // Origin of the object instances
                  if (data.rootNode.HasChilds())
                  {
                      string node0_Id;    // TODO: find root of multiple armatures

                      // Armature
                      writer.WriteStartElement("node");
                      { writer.WriteAttributeString(ID, baseName);
                        writer.WriteAttributeString(NAME, baseName);
                        writer.WriteAttributeString(TYPE, "NODE");

                        writer.WriteStartElement("matrix");
                        writer.WriteAttributeString(SID, "transform");

                        writer.WriteString(Transform3DF.Identity().ToString()); //data.rootNode.Transform.ToString()
                        writer.WriteEndElement();                               // matrix

                        node0_Id = addJoint(nodesList[0]);                      // first must be skeleton
                        writer.WriteEndElement();                               // node
                      }

                      // Mesh nodes
                      string nodeName;
                      for (int fn = 1; fn < nodesList.Count; ++fn)
                      {
                          if (!String.IsNullOrWhiteSpace(nodeName = nodesList[fn].Name) && (baseMeshes.Count() > (fn - 1)))
                          {
                              // This fails when multiple meshes contain same name fragment:
                              //boundMaterialNames: data.meshes.Find(msh => nodeName.Contains(msh.Name))?.MaterialSlots.Select(slot => slot.name),

                              // This works only when nodes and related meshes in the list appear in same order!
                              addMeshNode(
                                  nodesList[fn],
                                  boundMaterialNames: baseMeshes.ElementAt(fn - 1).MaterialSlots.Select(slot => slot.name),
                                  controllerId: $"{baseName}_{nodeName}{_SKIN}",
                                  skeletionId: node0_Id
                                  );
                          }
                      }
                  }
                  writer.WriteEndElement(); // vis scene
                }
                writer.WriteEndElement();   // vis scene lib

                string addJoint(Mdl.CFrame node)
                {
                    if (null == node)
                    {
                        throw new ArgumentException("CDaeSerializer::Serialize::addJoint: Passed 'null'-argument", "node");
                    }
                    string skeletonId = String.IsNullOrWhiteSpace(baseName) ? node.Name : $"{baseName}_{node.Name}";

                    writer.WriteStartElement("node");
                    writer.WriteAttributeString(ID, skeletonId);
                    writer.WriteAttributeString(NAME, node.Name);
                    writer.WriteAttributeString(SID, node.Name);
                    writer.WriteAttributeString(TYPE, "JOINT");

                    writer.WriteStartElement("matrix");
                    writer.WriteAttributeString(SID, "transform");

                    writer.WriteString(node.Transform.ToString());
                    writer.WriteEndElement();  // matrix

                    if (node.HasChilds())
                    {
                        foreach (var child in node.Childs)
                        {
                            addJoint(child);
                        }
                    }
                    writer.WriteEndElement();  // node
                    return(skeletonId);
                }

                void addMeshNode(Mdl.CFrame node, IEnumerable <string> boundMaterialNames, string controllerId, string skeletionId)
                {
                    if ((null == node) || (null == boundMaterialNames) || (null == controllerId) || (null == skeletionId))
                    {
                        throw new ArgumentException("CDaeSerializer::Serialize::addMeshNode: Passed 'null'-argument");
                    }
                    writer.WriteStartElement("node");
                    writer.WriteAttributeString(ID, node.Name);  // does not support nested meshes
                    writer.WriteAttributeString(NAME, node.Name);
                    writer.WriteAttributeString(TYPE, "NODE");

                    // This seem to have no effect at Blender import
                    //
                    //writer.WriteStartElement("matrix");
                    //{   writer.WriteAttributeString(SID, "transform");

                    //    writer.WriteString(node.Transform.ToString());
                    //    writer.WriteEndElement();  // matrix
                    //}
                    writer.WriteStartElement("instance_controller");
                    { writer.WriteAttributeString("url", "#" + controllerId);

                      writer.WriteElementString("skeleton", "#" + skeletionId);

                      writer.WriteStartElement("bind_material");
                      {
                          writer.WriteStartElement("technique_common");
                          string[] atrb = new string[] { "symbol", "target" };
                          foreach (string mat in boundMaterialNames)
                          {
                              writer.WriteAttElement("instance_material", atrb, mat + "-material", $"#{mat}-material");
                          }
                          writer.WriteEndElement(); // tech
                          writer.WriteEndElement(); // bind mat
                      }
                      writer.WriteEndElement();     // instance controller
                    }
                    writer.WriteEndElement();       // node
                }
            }
            #endregion

            // Instances
            writer.WriteStartElement("scene");
            writer.WriteAttElement("instance_visual_scene", new string[] { "url" }, "#Scene");
            writer.WriteEndElement();  //scene

            writer.Close();
            writer.Dispose();
            return;

            void addTechCommon(string id, int count, int stride, string type, params string[] values)
            {
                writer.WriteStartElement("technique_common");
                writer.WriteStartElement("accessor");
                writer.WriteAttributeString(SOURCE, "#" + id);
                writer.WriteAttributeString(COUNT, count.ToString());
                writer.WriteAttributeString("stride", stride.ToString());
                string[] paramAtrb = new string[] { NAME, TYPE };
                foreach (var val in values)
                {
                    writer.WriteAttElement(PARAM, paramAtrb, val, type);
                }
                writer.WriteEndElement();  // accessor
                writer.WriteEndElement();  // technique
            }
        }// Serialize
예제 #4
0
        //readonly CMdlFile file;


        //private CFileParser()
        //{
        //    throw new NotImplementedException("CFileParser()");  // Not allowed without file
        //}


        //public CFileParser(CMdlFile file)
        //{
        //    this.file = file ?? throw new ArgumentNullException(nameof(file));
        //}


        #region public Methods

        public static bool TryParse(CMdlFile file, ref Entities hierachy)
        {
            if ((null == file) || !file.IsValid)
            {
                return(false);
            }
            //if (null == hierachy.meshes)  // preserve when reading morphs
            //{
            //    hierachy.meshes = new List<Mdl.CMesh>();
            //}

            string            readNode;
            bool              cancel = false;
            CMdlFileNavigator reader = file.Reader;

            // Read nodes
            reader.MoveDataStart();               // in case of restart
            if (TagInfo.root != reader.ReadTag()) // read and seek
            {                                     // Does not start with RootNode
                return(false);
            }
            reader.SkipWhitespace();  // \r\n
            CFrameParser.Parse(reader, out Mdl.CFrame currFrame);
            //int nodeCount = hierachy.rootNode.Childs.Count;
            //if (file.IsMorph && (null != hierachy.rootNode) && nodeCount > 0)
            //{
            //    hierachy.rootNode.Childs.RemoveRange(1, nodeCount - 1);
            //    hierachy.rootNode.Childs.AddRange(currFrame.Childs);
            //} else {
            //    hierachy.rootNode = currFrame;
            //}
            hierachy.rootNode = currFrame;  // morphs have no skeleton at index 0!
            reader.SkipWhitespace();

            // Read mesh data
            var parsedMeshes = new List <Mdl.CMesh>();

            while (!cancel && !String.IsNullOrEmpty(readNode = reader.ReadTag(mTags)))
            {
                switch (readNode)
                {
                case TagInfo.root:
                    cancel = true;
                    break;

                case TagInfo.mesh:
                    Mdl.CMesh currMesh = null;
                    if (file.IsMorph)
                    {
                        if ((cancel = !CMeshParser.TryParseMorph(reader, out Mdl.CMorph morph)) ||
                            (null == hierachy.meshes) || !hierachy.meshes.Any(msh => msh != null))  //< not a reason to cancel
                        {
                            break;
                        }
                        var matches = hierachy.meshes.Where(
                            msh => msh.VerticesCount() == morph.VerticesCount() &&
                            file.Filename.Contains(msh.Name));
                        if (matches.Any())
                        {
                            currMesh = matches.First()?.CreateMorphedMesh(morph);
                        }
                    }
                    else
                    {
                        if (cancel = !CMeshParser.TryParse(reader, out currMesh))
                        {
                            break;
                        }
                        currMesh.OriginName = file.Filename;
                    }
                    if (null != currMesh && currMesh.IsValid())
                    {
                        parsedMeshes.Add(currMesh);
                    }
                    break;

                case TagInfo.collision:  // skip (text block with 4 line body)
                    reader.SeekBlockStart();
                    reader.SeekBlockEnd();
                    break;

                default:
                    cancel = true;  // not implemented
                    break;
                }

                reader.SkipWhitespace();  // \r\n after each data block
            }
            if (!cancel)
            {
                hierachy.meshes = parsedMeshes;
            }
            return(!cancel);
        }