/// <summary>
        /// Set a vertex by its MESH RELEVANT vertex id.
        /// </summary>
        /// <param name="vertex"></param>
        public void SetVertexAt(int vertexId, TTVertex vertex)
        {
            int verticesSoFar = 0;

            foreach (var p in Parts)
            {
                if (vertexId >= verticesSoFar + p.Vertices.Count)
                {
                    // Need to keep looping.
                    verticesSoFar += p.Vertices.Count;
                    continue;
                }

                var relevantVertex = vertexId - verticesSoFar;
                p.Vertices[relevantVertex] = vertex;
            }
        }
Example #2
0
        /// <summary>
        /// Loads a TTModel file from a given SQLite3 DB filepath.
        /// </summary>
        public static TTModel FromDb(FileInfo file, Action <bool, string> loggingFunction = null)
        {
            if (loggingFunction == null)
            {
                loggingFunction = ModelModifiers.NoOp;
            }

            string  connectionString = "Data Source=" + file + ";";
            TTModel model            = new TTModel();

            model.Source = file.FullName;

            // Spawn a DB connection to do the raw queries.
            using (SqliteConnection db = new SqliteConnection(connectionString))
            {
                db.Open();

                // Load Mesh Groups
                string query = "select * from meshes order by mesh asc;";
                using (SqliteCommand cmd = new SqliteCommand(query, db))
                {
                    using (CacheReader reader = new CacheReader(cmd.ExecuteReader()))
                    {
                        while (reader.NextRow())
                        {
                            int meshNum = reader.GetInt32("mesh");

                            // Spawn mesh groups as needed.
                            while (model.MeshGroups.Count <= meshNum)
                            {
                                model.MeshGroups.Add(new TTMeshGroup());
                            }

                            model.MeshGroups[meshNum].Name = reader.GetString("name");
                        }
                    }
                }

                // Load Mesh Parts
                query = "select * from parts order by mesh asc, part asc;";
                using (SqliteCommand cmd = new SqliteCommand(query, db))
                {
                    using (CacheReader reader = new CacheReader(cmd.ExecuteReader()))
                    {
                        while (reader.NextRow())
                        {
                            int meshNum = reader.GetInt32("mesh");
                            int partNum = reader.GetInt32("part");

                            // Spawn mesh groups if needed.
                            while (model.MeshGroups.Count <= meshNum)
                            {
                                model.MeshGroups.Add(new TTMeshGroup());
                            }

                            // Spawn parts as needed.
                            while (model.MeshGroups[meshNum].Parts.Count <= partNum)
                            {
                                model.MeshGroups[meshNum].Parts.Add(new TTMeshPart());
                            }

                            model.MeshGroups[meshNum].Parts[partNum].Name = reader.GetString("name");
                        }
                    }
                }

                // Load Bones
                query = "select * from bones where mesh >= 0 order by mesh asc, bone_id asc;";
                using (SqliteCommand cmd = new SqliteCommand(query, db))
                {
                    using (CacheReader reader = new CacheReader(cmd.ExecuteReader()))
                    {
                        while (reader.NextRow())
                        {
                            int meshId = reader.GetInt32("mesh");
                            model.MeshGroups[meshId].Bones.Add(reader.GetString("name"));
                        }
                    }
                }
            }

            // Loop for each part, to populate their internal data structures.
            for (int mId = 0; mId < model.MeshGroups.Count; mId++)
            {
                TTMeshGroup m = model.MeshGroups[mId];
                for (int pId = 0; pId < m.Parts.Count; pId++)
                {
                    TTMeshPart p = m.Parts[pId];
                    WhereClause where = new WhereClause();
                    WhereClause mWhere = new WhereClause();
                    mWhere.Column = "mesh";
                    mWhere.Value  = mId;
                    WhereClause pWhere = new WhereClause();
                    pWhere.Column = "part";
                    pWhere.Value  = pId;

                    where.Inner.Add(mWhere);
                    where.Inner.Add(pWhere);

                    // Load Vertices
                    // The reader handles coalescing the null types for us.
                    p.Vertices = BuildListFromTable(connectionString, "vertices", where, (reader) =>
                    {
                        TTVertex vertex = new TTVertex();

                        // Positions
                        vertex.Position.X = reader.GetFloat("position_x");
                        vertex.Position.Y = reader.GetFloat("position_y");
                        vertex.Position.Z = reader.GetFloat("position_z");

                        // Normals
                        vertex.Normal.X = reader.GetFloat("normal_x");
                        vertex.Normal.Y = reader.GetFloat("normal_y");
                        vertex.Normal.Z = reader.GetFloat("normal_z");

                        // Vertex Colors - Vertex color is RGBA
                        vertex.VertexColor[0] = (byte)Math.Round(reader.GetFloat("color_r") * 255);
                        vertex.VertexColor[1] = (byte)Math.Round(reader.GetFloat("color_g") * 255);
                        vertex.VertexColor[2] = (byte)Math.Round(reader.GetFloat("color_b") * 255);
                        vertex.VertexColor[3] = (byte)Math.Round(reader.GetFloat("color_a") * 255);

                        // UV Coordinates
                        vertex.UV1.X = reader.GetFloat("uv_1_u");
                        vertex.UV1.Y = reader.GetFloat("uv_1_v");
                        vertex.UV2.X = reader.GetFloat("uv_2_u");
                        vertex.UV2.Y = reader.GetFloat("uv_2_v");

                        // Bone Ids
                        vertex.BoneIds[0] = (byte)reader.GetByte("bone_1_id");
                        vertex.BoneIds[1] = (byte)reader.GetByte("bone_2_id");
                        vertex.BoneIds[2] = (byte)reader.GetByte("bone_3_id");
                        vertex.BoneIds[3] = (byte)reader.GetByte("bone_4_id");

                        // Weights
                        vertex.Weights[0] = (byte)Math.Round(reader.GetFloat("bone_1_weight") * 255);
                        vertex.Weights[1] = (byte)Math.Round(reader.GetFloat("bone_2_weight") * 255);
                        vertex.Weights[2] = (byte)Math.Round(reader.GetFloat("bone_3_weight") * 255);
                        vertex.Weights[3] = (byte)Math.Round(reader.GetFloat("bone_4_weight") * 255);

                        return(vertex);
                    });

                    p.TriangleIndices = BuildListFromTable(connectionString, "indices", where, (reader) =>
                    {
                        try
                        {
                            return(reader.GetInt32("vertex_id"));
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                    });
                }
            }

            // Spawn a DB connection to do the raw queries.
            using (SqliteConnection db = new SqliteConnection(connectionString))
            {
                db.Open();

                // Load Shape Verts
                string query = "select * from shape_vertices order by shape asc, mesh asc, part asc, vertex_id asc;";
                using (SqliteCommand cmd = new SqliteCommand(query, db))
                {
                    using (CacheReader reader = new CacheReader(cmd.ExecuteReader()))
                    {
                        while (reader.NextRow())
                        {
                            string shapeName = reader.GetString("shape");
                            int    meshNum   = reader.GetInt32("mesh");
                            int    partNum   = reader.GetInt32("part");
                            int    vertexId  = reader.GetInt32("vertex_id");

                            TTMeshPart part = model.MeshGroups[meshNum].Parts[partNum];

                            // Copy the original vertex and update position.
                            TTVertex vertex = (TTVertex)part.Vertices[vertexId].Clone();
                            vertex.Position.X = reader.GetFloat("position_x");
                            vertex.Position.Y = reader.GetFloat("position_y");
                            vertex.Position.Z = reader.GetFloat("position_z");

                            TTVertex repVert = part.Vertices[vertexId];
                            if (repVert.Position.Equals(vertex.Position))
                            {
                                // Skip morphology which doesn't actually change anything.
                                continue;
                            }

                            if (!part.ShapeParts.ContainsKey(shapeName))
                            {
                                TTShapePart shpPt = new TTShapePart();
                                shpPt.Name = shapeName;
                                part.ShapeParts.Add(shapeName, shpPt);
                            }

                            part.ShapeParts[shapeName].VertexReplacements.Add(vertexId, part.ShapeParts[shapeName].Vertices.Count);
                            part.ShapeParts[shapeName].Vertices.Add(vertex);
                        }
                    }
                }
            }

            // Convert the model to FFXIV's internal weirdness.
            ModelModifiers.MakeImportReady(model, loggingFunction);
            return(model);
        }
Example #3
0
        /// <summary>
        /// Saves the TTModel to a .DB file for use with external importers/exporters.
        /// </summary>
        public static void ToDb(TTModel model, FileInfo file, bool useAllBones, Action <bool, string> loggingFunction = null)
        {
            if (loggingFunction == null)
            {
                loggingFunction = ModelModifiers.NoOp;
            }

            file.Delete();
            string directory = file.DirectoryName;

            ModelModifiers.MakeExportReady(model, loggingFunction);

            string connectionString = "Data Source=" + file + ";";

            try
            {
                List <string> bones = useAllBones ? null : model.Bones;

                // TODO: Get bones for skeletons
                Console.WriteLine("WARNING: NO SKELETONS");
                Dictionary <string, SkeletonData> boneDict = new Dictionary <string, SkeletonData>();               ////model.ResolveBoneHeirarchy(null, TTModel.XivRace.All_Races, bones, loggingFunction);

                const string creationScript = "CreateImportDB.sql";

                // Spawn a DB connection to do the raw queries.
                // Using statements help ensure we don't accidentally leave any connections open and lock the file handle.
                using (SqliteConnection db = new SqliteConnection(connectionString))
                {
                    db.Open();

                    // Create the DB
                    string[] lines  = File.ReadAllLines("Resources\\SQL\\" + creationScript);
                    string   sqlCmd = string.Join("\n", lines);

                    using (SqliteCommand cmd = new SqliteCommand(sqlCmd, db))
                    {
                        cmd.ExecuteScalar();
                    }

                    // Write the Data.
                    using (SqliteTransaction transaction = db.BeginTransaction())
                    {
                        // Metadata.
                        string query = @"insert into meta (key, value) values ($key, $value)";
                        using (SqliteCommand cmd = new SqliteCommand(query, db))
                        {
                            // FFXIV stores stuff in Meters.
                            cmd.Parameters.AddWithValue("key", "unit");
                            cmd.Parameters.AddWithValue("value", "meter");
                            cmd.ExecuteScalar();

                            // Application that created the db.
                            cmd.Parameters.AddWithValue("key", "application");
                            cmd.Parameters.AddWithValue("value", "FfxivResourceConverter");
                            cmd.ExecuteScalar();

                            cmd.Parameters.AddWithValue("key", "version");
                            cmd.Parameters.AddWithValue("value", typeof(TTModelDb).Assembly.GetName().Version);
                            cmd.ExecuteScalar();

                            // Axis information
                            cmd.Parameters.AddWithValue("key", "up");
                            cmd.Parameters.AddWithValue("value", "y");
                            cmd.ExecuteScalar();

                            cmd.Parameters.AddWithValue("key", "front");
                            cmd.Parameters.AddWithValue("value", "z");
                            cmd.ExecuteScalar();

                            cmd.Parameters.AddWithValue("key", "handedness");
                            cmd.Parameters.AddWithValue("value", "r");
                            cmd.ExecuteScalar();

                            // FFXIV stores stuff in Meters.
                            cmd.Parameters.AddWithValue("key", "root_name");
                            cmd.Parameters.AddWithValue("value", Path.GetFileNameWithoutExtension(model.Source));
                            cmd.ExecuteScalar();
                        }

                        // Skeleton
                        query = @"insert into skeleton (name, parent, matrix_0, matrix_1, matrix_2, matrix_3, matrix_4, matrix_5, matrix_6, matrix_7, matrix_8, matrix_9, matrix_10, matrix_11, matrix_12, matrix_13, matrix_14, matrix_15) 
											 values ($name, $parent, $matrix_0, $matrix_1, $matrix_2, $matrix_3, $matrix_4, $matrix_5, $matrix_6, $matrix_7, $matrix_8, $matrix_9, $matrix_10, $matrix_11, $matrix_12, $matrix_13, $matrix_14, $matrix_15);"                                            ;

                        using (SqliteCommand cmd = new SqliteCommand(query, db))
                        {
                            foreach (KeyValuePair <string, SkeletonData> b in boneDict)
                            {
                                KeyValuePair <string, SkeletonData> parent = boneDict.FirstOrDefault(x => x.Value.BoneNumber == b.Value.BoneParent);
                                string parentName = parent.Key == null ? null : parent.Key;
                                cmd.Parameters.AddWithValue("name", b.Value.BoneName);
                                cmd.Parameters.AddWithValue("parent", parentName);

                                for (int i = 0; i < 16; i++)
                                {
                                    cmd.Parameters.AddWithValue("matrix_" + i.ToString(), b.Value.PoseMatrix[i]);
                                }

                                cmd.ExecuteScalar();
                            }
                        }

                        int           modelIdx = 0;
                        List <string> models   = new List <string>()
                        {
                            Path.GetFileNameWithoutExtension(model.Source)
                        };
                        foreach (string mdl in models)
                        {
                            query = @"insert into models (model, name) values ($model, $name);";
                            using (SqliteCommand cmd = new SqliteCommand(query, db))
                            {
                                cmd.Parameters.AddWithValue("model", modelIdx);
                                cmd.Parameters.AddWithValue("name", mdl);
                                cmd.ExecuteScalar();
                            }

                            modelIdx++;
                        }

                        int matIdx = 0;
                        foreach (string material in model.Materials)
                        {
                            // Materials
                            query = @"insert into materials (material_id, name, diffuse, normal, specular, opacity, emissive) values ($material_id, $name, $diffuse, $normal, $specular, $opacity, $emissive);";
                            using (SqliteCommand cmd = new SqliteCommand(query, db))
                            {
                                string mtrl_prefix = directory + "\\" + Path.GetFileNameWithoutExtension(material.Substring(1)) + "_";
                                string mtrl_suffix = ".png";
                                string name        = material;
                                try
                                {
                                    name = Path.GetFileNameWithoutExtension(material);
                                }
                                catch
                                {
                                }

                                cmd.Parameters.AddWithValue("material_id", matIdx);
                                cmd.Parameters.AddWithValue("name", name);
                                cmd.Parameters.AddWithValue("diffuse", mtrl_prefix + "d" + mtrl_suffix);
                                cmd.Parameters.AddWithValue("normal", mtrl_prefix + "n" + mtrl_suffix);
                                cmd.Parameters.AddWithValue("specular", mtrl_prefix + "s" + mtrl_suffix);
                                cmd.Parameters.AddWithValue("emissive", mtrl_prefix + "e" + mtrl_suffix);
                                cmd.Parameters.AddWithValue("opacity", mtrl_prefix + "o" + mtrl_suffix);
                                cmd.ExecuteScalar();
                            }

                            matIdx++;
                        }

                        int meshIdx = 0;
                        foreach (TTMeshGroup m in model.MeshGroups)
                        {
                            // Bones
                            query = @"insert into bones (mesh, bone_id, name) values ($mesh, $bone_id, $name);";
                            int bIdx = 0;
                            foreach (string b in m.Bones)
                            {
                                using (SqliteCommand cmd = new SqliteCommand(query, db))
                                {
                                    cmd.Parameters.AddWithValue("name", b);
                                    cmd.Parameters.AddWithValue("bone_id", bIdx);
                                    cmd.Parameters.AddWithValue("parent_id", null);
                                    cmd.Parameters.AddWithValue("mesh", meshIdx);
                                    cmd.ExecuteScalar();
                                }

                                bIdx++;
                            }

                            // Groups
                            query = @"insert into meshes (mesh, name, material_id, model) values ($mesh, $name, $material_id, $model);";
                            using (SqliteCommand cmd = new SqliteCommand(query, db))
                            {
                                cmd.Parameters.AddWithValue("name", m.Name);
                                cmd.Parameters.AddWithValue("mesh", meshIdx);

                                // This is always 0 for now.  Support element for Liinko's work on multi-model export.
                                cmd.Parameters.AddWithValue("model", 0);
                                cmd.Parameters.AddWithValue("material_id", model.GetMaterialIndex(meshIdx));
                                cmd.ExecuteScalar();
                            }

                            // Parts
                            int partIdx = 0;
                            foreach (TTMeshPart p in m.Parts)
                            {
                                // Parts
                                query = @"insert into parts (mesh, part, name) values ($mesh, $part, $name);";
                                using (SqliteCommand cmd = new SqliteCommand(query, db))
                                {
                                    cmd.Parameters.AddWithValue("name", p.Name);
                                    cmd.Parameters.AddWithValue("part", partIdx);
                                    cmd.Parameters.AddWithValue("mesh", meshIdx);
                                    cmd.ExecuteScalar();
                                }

                                // Vertices
                                int vIdx = 0;
                                foreach (TTVertex v in p.Vertices)
                                {
                                    query = @"insert into vertices ( mesh,  part,  vertex_id,  position_x,  position_y,  position_z,  normal_x,  normal_y,  normal_z,  color_r,  color_g,  color_b,  color_a,  uv_1_u,  uv_1_v,  uv_2_u,  uv_2_v,  bone_1_id,  bone_1_weight,  bone_2_id,  bone_2_weight,  bone_3_id,  bone_3_weight,  bone_4_id,  bone_4_weight) 
														values ($mesh, $part, $vertex_id, $position_x, $position_y, $position_z, $normal_x, $normal_y, $normal_z, $color_r, $color_g, $color_b, $color_a, $uv_1_u, $uv_1_v, $uv_2_u, $uv_2_v, $bone_1_id, $bone_1_weight, $bone_2_id, $bone_2_weight, $bone_3_id, $bone_3_weight, $bone_4_id, $bone_4_weight);"                                                        ;
                                    using (SqliteCommand cmd = new SqliteCommand(query, db))
                                    {
                                        cmd.Parameters.AddWithValue("part", partIdx);
                                        cmd.Parameters.AddWithValue("mesh", meshIdx);
                                        cmd.Parameters.AddWithValue("vertex_id", vIdx);

                                        cmd.Parameters.AddWithValue("position_x", v.Position.X);
                                        cmd.Parameters.AddWithValue("position_y", v.Position.Y);
                                        cmd.Parameters.AddWithValue("position_z", v.Position.Z);

                                        cmd.Parameters.AddWithValue("normal_x", v.Normal.X);
                                        cmd.Parameters.AddWithValue("normal_y", v.Normal.Y);
                                        cmd.Parameters.AddWithValue("normal_z", v.Normal.Z);

                                        cmd.Parameters.AddWithValue("color_r", v.VertexColor[0] / 255f);
                                        cmd.Parameters.AddWithValue("color_g", v.VertexColor[1] / 255f);
                                        cmd.Parameters.AddWithValue("color_b", v.VertexColor[2] / 255f);
                                        cmd.Parameters.AddWithValue("color_a", v.VertexColor[3] / 255f);

                                        cmd.Parameters.AddWithValue("uv_1_u", v.UV1.X);
                                        cmd.Parameters.AddWithValue("uv_1_v", v.UV1.Y);
                                        cmd.Parameters.AddWithValue("uv_2_u", v.UV2.X);
                                        cmd.Parameters.AddWithValue("uv_2_v", v.UV2.Y);

                                        cmd.Parameters.AddWithValue("bone_1_id", v.BoneIds[0]);
                                        cmd.Parameters.AddWithValue("bone_1_weight", v.Weights[0] / 255f);

                                        cmd.Parameters.AddWithValue("bone_2_id", v.BoneIds[1]);
                                        cmd.Parameters.AddWithValue("bone_2_weight", v.Weights[1] / 255f);

                                        cmd.Parameters.AddWithValue("bone_3_id", v.BoneIds[2]);
                                        cmd.Parameters.AddWithValue("bone_3_weight", v.Weights[2] / 255f);

                                        cmd.Parameters.AddWithValue("bone_4_id", v.BoneIds[3]);
                                        cmd.Parameters.AddWithValue("bone_4_weight", v.Weights[3] / 255f);

                                        cmd.ExecuteScalar();
                                        vIdx++;
                                    }
                                }

                                // Indices
                                for (int i = 0; i < p.TriangleIndices.Count; i++)
                                {
                                    query = @"insert into indices (mesh, part, index_id, vertex_id) values ($mesh, $part, $index_id, $vertex_id);";
                                    using (SqliteCommand cmd = new SqliteCommand(query, db))
                                    {
                                        cmd.Parameters.AddWithValue("part", partIdx);
                                        cmd.Parameters.AddWithValue("mesh", meshIdx);
                                        cmd.Parameters.AddWithValue("index_id", i);
                                        cmd.Parameters.AddWithValue("vertex_id", p.TriangleIndices[i]);
                                        cmd.ExecuteScalar();
                                    }
                                }

                                // Shape Parts
                                foreach (KeyValuePair <string, TTShapePart> shpKv in p.ShapeParts)
                                {
                                    if (!shpKv.Key.StartsWith("shp_"))
                                    {
                                        continue;
                                    }
                                    TTShapePart shp = shpKv.Value;

                                    query = @"insert into shape_vertices ( mesh,  part,  shape,  vertex_id,  position_x,  position_y,  position_z) 
																   values($mesh, $part, $shape, $vertex_id, $position_x, $position_y, $position_z);"                                                                ;
                                    using (SqliteCommand cmd = new SqliteCommand(query, db))
                                    {
                                        foreach (KeyValuePair <int, int> vKv in shp.VertexReplacements)
                                        {
                                            TTVertex v = shp.Vertices[vKv.Value];
                                            cmd.Parameters.AddWithValue("part", partIdx);
                                            cmd.Parameters.AddWithValue("mesh", meshIdx);
                                            cmd.Parameters.AddWithValue("shape", shpKv.Key);
                                            cmd.Parameters.AddWithValue("vertex_id", vKv.Key);

                                            cmd.Parameters.AddWithValue("position_x", v.Position.X);
                                            cmd.Parameters.AddWithValue("position_y", v.Position.Y);
                                            cmd.Parameters.AddWithValue("position_z", v.Position.Z);

                                            cmd.ExecuteScalar();
                                            vIdx++;
                                        }
                                    }
                                }

                                partIdx++;
                            }

                            meshIdx++;
                        }

                        transaction.Commit();
                    }
                }
            }
            catch (Exception)
            {
                ModelModifiers.MakeImportReady(model, loggingFunction);
                throw;
            }

            // Undo the export ready at the start.
            ModelModifiers.MakeImportReady(model, loggingFunction);
        }