/// <summary>
 /// Applies the deformer to a model
 /// </summary>
 /// <param name="model">The model being deformed</param>
 /// <param name="currentRace">The current model race</param>
 /// <param name="targetRace">The target race to convert the model to</param>
 private static void ApplyDeformers(TTModel model, XivRace currentRace, XivRace targetRace)
 {
     // Current race is already parent node
     // Direct conversion
     // [ Current > (apply deform) > Target ]
     if (currentRace.IsDirectParentOf(targetRace))
     {
         ModelModifiers.ApplyRacialDeform(model, targetRace);
     }
     // Target race is parent node of Current race
     // Convert to parent (invert deform)
     // [ Current > (apply inverse deform) > Target ]
     else if (targetRace.IsDirectParentOf(currentRace))
     {
         ModelModifiers.ApplyRacialDeform(model, currentRace, true);
     }
     // Current race is not parent of Target Race and Current race has parent
     // Make a recursive call with the current races parent race
     // [ Current > (apply inverse deform) > Current.Parent > Recursive Call ]
     else if (currentRace.GetNode().Parent != null)
     {
         ModelModifiers.ApplyRacialDeform(model, currentRace, true);
         ApplyDeformers(model, currentRace.GetNode().Parent.Race, targetRace);
     }
     // Current race has no parent
     // Make a recursive call with the target races parent race
     // [ Target > (apply deform on Target.Parent) > Target.Parent > Recursive Call ]
     else
     {
         ModelModifiers.ApplyRacialDeform(model, targetRace.GetNode().Parent.Race);
         ApplyDeformers(model, targetRace.GetNode().Parent.Race, targetRace);
     }
 }
예제 #2
0
        /// <summary>
        /// Creates and populates a TTModel object from a raw XivMdl.
        /// </summary>
        public static TTModel FromRaw(Model rawMdl, Action <bool, string> loggingFunction = null)
        {
            if (rawMdl == null)
            {
                return(null);
            }

            if (loggingFunction == null)
            {
                loggingFunction = ModelModifiers.NoOp;
            }

            TTModel ttModel = new TTModel();

            ModelModifiers.MergeGeometryData(ttModel, rawMdl, loggingFunction);
            ModelModifiers.MergeAttributeData(ttModel, rawMdl, loggingFunction);
            ModelModifiers.MergeMaterialData(ttModel, rawMdl, loggingFunction);
            try
            {
                ModelModifiers.MergeShapeData(ttModel, rawMdl, loggingFunction);
            }
            catch (Exception ex)
            {
                loggingFunction(true, "Unable to load shape data: " + ex.Message);
                ModelModifiers.ClearShapeData(ttModel, loggingFunction);
            }

            return(ttModel);
        }
 private void ImportButton_Click(object sender, RoutedEventArgs e)
 {
     if (Convert.ToDouble(ScaleComboBox.SelectedValue) != 1.0)
     {
         ModelModifiers.ScaleModel(_newModel, (double)ScaleComboBox.SelectedValue);
     }
     DialogResult = true;
 }
예제 #4
0
    void applyTricks(GameObject res, ModelModifiers mods)
    {
        MeshRenderer ren = res.GetComponent <MeshRenderer>();

        if (mods._TrickFlags.HasFlag(TrickFlags.CameraFace))
        {
            res.AddComponent <AlwaysFaceCamera>();
        }

        if (mods._TrickFlags.HasFlag(TrickFlags.LightFace))
        {
            res.AddComponent <AlwaysFaceSun>();
        }
    }
 /// <summary>
 /// Applies the deformer to a model
 /// </summary>
 /// <param name="model">The model being deformed</param>
 /// <param name="itemType">The item type of the model</param>
 /// <param name="currentRace">The current model race</param>
 /// <param name="targetRace">The target race to convert the model to</param>
 private void ApplyDeformers(TTModel model, string itemType, XivRace currentRace, XivRace targetRace)
 {
     try
     {
         // Current race is already parent node
         // Direct conversion
         // [ Current > (apply deform) > Target ]
         if (currentRace.IsDirectParentOf(targetRace))
         {
             ModelModifiers.ApplyRacialDeform(model, targetRace);
         }
         // Target race is parent node of Current race
         // Convert to parent (invert deform)
         // [ Current > (apply inverse deform) > Target ]
         else if (targetRace.IsDirectParentOf(currentRace))
         {
             ModelModifiers.ApplyRacialDeform(model, currentRace, true);
         }
         // Current race is not parent of Target Race and Current race has parent
         // Make a recursive call with the current races parent race
         // [ Current > (apply inverse deform) > Current.Parent > Recursive Call ]
         else if (currentRace.GetNode().Parent != null)
         {
             ModelModifiers.ApplyRacialDeform(model, currentRace, true);
             ApplyDeformers(model, itemType, currentRace.GetNode().Parent.Race, targetRace);
         }
         // Current race has no parent
         // Make a recursive call with the target races parent race
         // [ Target > (apply deform on Target.Parent) > Target.Parent > Recursive Call ]
         else
         {
             ModelModifiers.ApplyRacialDeform(model, targetRace.GetNode().Parent.Race);
             ApplyDeformers(model, itemType, targetRace.GetNode().Parent.Race, targetRace);
         }
     }
     catch (Exception ex)
     {
         // Show a warning that deforms are missing for the target race
         // This mostly happens with Face, Hair, Tails, Ears, and Female > Male deforms
         // The model is still added but no deforms are applied
         FlexibleMessageBox.Show(string.Format(UIMessages.MissingDeforms, targetRace.GetDisplayName(), itemType, ex.Message), UIMessages.MissingDeformsTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning);
     }
 }
        /// <summary>
        /// Updates or Adds the Model to the viewport
        /// </summary>
        /// <param name="model">The TexTools Model</param>
        /// <param name="textureDataDictionary">The textures associated with the model</param>
        /// <param name="item">The item for the model</param>
        /// <param name="modelRace">The race of the model</param>
        /// <param name="targetRace">The target race the model should be</param>
        public void UpdateModel(TTModel model, Dictionary <int, ModelTextureData> textureDataDictionary, IItemModel item, XivRace modelRace, XivRace targetRace)
        {
            _targetRace = targetRace;
            var itemType = $"{item.PrimaryCategory}_{item.SecondaryCategory}";

            // If target race is different than the model race Apply racial deforms
            if (modelRace != targetRace)
            {
                ApplyDeformers(model, itemType, modelRace, targetRace);
            }

            SharpDX.BoundingBox?boundingBox = null;
            ModelModifiers.CalculateTangents(model);

            // Remove any existing models of the same item type
            RemoveModel(itemType);

            var totalMeshCount = model.MeshGroups.Count;

            for (var i = 0; i < totalMeshCount; i++)
            {
                var meshGeometry3D = GetMeshGeometry(model, i);

                var textureData = textureDataDictionary[model.GetMaterialIndex(i)];

                Stream diffuse = null, specular = null, normal = null, alpha = null, emissive = null;

                if (textureData.Diffuse != null && textureData.Diffuse.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Diffuse, textureData.Width, textureData.Height))
                    {
                        diffuse = new MemoryStream();
                        img.Save(diffuse, new PngEncoder());
                    }

                    streamList.Add(diffuse);
                }

                if (textureData.Specular != null && textureData.Specular.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Specular, textureData.Width, textureData.Height))
                    {
                        specular = new MemoryStream();
                        img.Save(specular, new PngEncoder());
                    }

                    streamList.Add(specular);
                }

                if (textureData.Normal != null && textureData.Normal.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Normal, textureData.Width, textureData.Height))
                    {
                        normal = new MemoryStream();
                        img.Save(normal, new PngEncoder());
                    }

                    streamList.Add(normal);
                }

                if (textureData.Alpha != null && textureData.Alpha.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Alpha, textureData.Width, textureData.Height))
                    {
                        alpha = new MemoryStream();
                        img.Save(alpha, new PngEncoder());
                    }

                    streamList.Add(alpha);
                }

                if (textureData.Emissive != null && textureData.Emissive.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Emissive, textureData.Width, textureData.Height))
                    {
                        emissive = new MemoryStream();
                        img.Save(emissive, new PngEncoder());
                    }

                    streamList.Add(emissive);
                }

                var material = new PhongMaterial
                {
                    DiffuseColor      = PhongMaterials.ToColor(1, 1, 1, 1),
                    SpecularShininess = 1f,
                    DiffuseMap        = diffuse,
                    DiffuseAlphaMap   = alpha,
                    SpecularColorMap  = specular,
                    NormalMap         = normal,
                    EmissiveMap       = emissive
                };

                // Geometry that contains skeleton data
                var smgm3d = new CustomBoneSkinMeshGeometry3D
                {
                    Geometry     = meshGeometry3D,
                    Material     = material,
                    ItemType     = itemType,
                    BoneMatrices = GetMatrices(targetRace),
                    BoneList     = model.Bones
                };

                // Keep track of what bones are showing in the view
                foreach (var modelBone in model.Bones)
                {
                    if (!shownBonesList.Contains(modelBone))
                    {
                        shownBonesList.Add(modelBone);
                    }
                }

                boundingBox = meshGeometry3D.Bound;

                smgm3d.CullMode = Properties.Settings.Default.Cull_Mode.Equals("None") ? CullMode.None : CullMode.Back;

                Models.Add(smgm3d);
            }

            SpecularShine = 1;

            var center = boundingBox.GetValueOrDefault().Center;

            _lightX = center.X;
            _lightY = center.Y;
            _lightZ = center.Z;

            Light3Direction    = new Vector3D(_lightX, _lightY, _lightZ);
            Camera.UpDirection = new Vector3D(0, 1, 0);
            Camera.CameraInternal.PropertyChanged += CameraInternal_PropertyChanged;

            // Add the skeleton node for the target race
            AddSkeletonNode(targetRace);

            // Keep track of the models displayed in the viewport
            shownModels.Add(itemType, new DisplayedModelData {
                TtModel = model, ItemModel = item, ModelTextureData = textureDataDictionary
            });
        }
        /// <summary>
        /// Updates the model in the 3D viewport
        /// </summary>
        /// <param name="mdlData">The model data</param>
        /// <param name="textureDataDictionary">The texture dictionary for the model</param>
        public void UpdateModel(TTModel model, Dictionary <int, ModelTextureData> textureDataDictionary)
        {
            SharpDX.BoundingBox?boundingBox = null;
            ModelModifiers.CalculateTangents(model);

            var totalMeshCount = model.MeshGroups.Count;

            for (var i = 0; i < totalMeshCount; i++)
            {
                var meshGeometry3D = GetMeshGeometry(model, i);

                var textureData = textureDataDictionary[model.GetMaterialIndex(i)];

                Stream diffuse = null, specular = null, normal = null, alpha = null, emissive = null;

                if (textureData.Diffuse != null && textureData.Diffuse.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Diffuse, textureData.Width, textureData.Height))
                    {
                        diffuse = new MemoryStream();
                        img.Save(diffuse, new PngEncoder());
                    }

                    streamList.Add(diffuse);
                }

                if (textureData.Specular != null && textureData.Specular.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Specular, textureData.Width, textureData.Height))
                    {
                        specular = new MemoryStream();
                        img.Save(specular, new PngEncoder());
                    }

                    streamList.Add(specular);
                }

                if (textureData.Normal != null && textureData.Normal.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Normal, textureData.Width, textureData.Height))
                    {
                        normal = new MemoryStream();
                        img.Save(normal, new PngEncoder());
                    }

                    streamList.Add(normal);
                }

                if (textureData.Alpha != null && textureData.Alpha.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Alpha, textureData.Width, textureData.Height))
                    {
                        alpha = new MemoryStream();
                        img.Save(alpha, new PngEncoder());
                    }

                    streamList.Add(alpha);
                }

                if (textureData.Emissive != null && textureData.Emissive.Length > 0)
                {
                    using (var img = Image.LoadPixelData <Rgba32>(textureData.Emissive, textureData.Width, textureData.Height))
                    {
                        emissive = new MemoryStream();
                        img.Save(emissive, new PngEncoder());
                    }

                    streamList.Add(emissive);
                }

                var material = new PhongMaterial
                {
                    DiffuseColor      = PhongMaterials.ToColor(1, 1, 1, 1),
                    SpecularShininess = 1f,
                    DiffuseMap        = diffuse,
                    DiffuseAlphaMap   = alpha,
                    SpecularColorMap  = specular,
                    NormalMap         = normal,
                    EmissiveMap       = emissive
                };

                var mgm3d = new CustomMeshGeometryModel3D
                {
                    Geometry = meshGeometry3D,
                    Material = material //,
                                        //IsBody = mdlData.LoDList[0].MeshDataList[i].IsBody
                };

                boundingBox = meshGeometry3D.Bound;

                mgm3d.CullMode = Properties.Settings.Default.Cull_Mode.Equals("None") ? CullMode.None : CullMode.Back;

                Models.Add(mgm3d);
            }

            SpecularShine = 1;

            var center = boundingBox.GetValueOrDefault().Center;

            _lightX = center.X;
            _lightY = center.Y;
            _lightZ = center.Z;

            Light3Direction    = new Vector3D(_lightX, _lightY, _lightZ);
            Camera.UpDirection = new Vector3D(0, 1, 0);
            Camera.CameraInternal.PropertyChanged += CameraInternal_PropertyChanged;
        }
예제 #8
0
    private static void convertMaterials(MeshRenderer ren, Model mdl, GameObject tgt)
    {
        ModelModifiers        model_trick = mdl.trck_node;
        GeometryModifiersData geom_trick  = mdl.src_mod;
        RuntimeData           rd          = RuntimeData.get();
        MaterialDescriptor    descriptor;

        string model_base_name = mdl.name.Split(new[] { "__" }, StringSplitOptions.None)[0];
        string mesh_path       = mdl.geoset.full_geo_path;
        int    obj_lib_idx     = mesh_path.IndexOf("object_library");

        if (obj_lib_idx != -1)
        {
            mesh_path = "Assets/Materials/" + mesh_path.Substring(obj_lib_idx);
        }
        string material_base_path = mesh_path + "/" + Path.GetFileNameWithoutExtension(mdl.name);

        if (mdl.name == "Crate1_med_Wood__TintCrates")
        {
            Debug.LogFormat("Crate {0}", mdl.BlendMode.ToString());
        }
        if (model_trick != null && model_trick._TrickFlags.HasFlag(TrickFlags.ColorOnly))
        {
//        result = result.Clone(result.GetName()+"Colored");
//        result.SetShaderParameter("MatDiffColor",Vector4(1.0, 1.0, 0.2f, 1.0f));
        }

        // Select material based on the model blend state
        // Modify material based on the applied model tricks
        Color onlyColor;
        Color tint1    = new Color(1, 1, 1, 1); // Shader Constant 0
        Color tint2    = new Color(1, 1, 1, 1); // Shader Constant 1
        float alphaRef = 0.0f;

        descriptor.depthWrite    = true;
        descriptor.isAdditive    = false;
        descriptor.targetCulling = UnityEngine.Rendering.CullMode.Back;
        descriptor.depthTest     = CompareFunction.LessEqual;
        descriptor.targetCulling = UnityEngine.Rendering.CullMode.Back;

        string shader_to_use = "";

        if (null != model_trick && model_trick._TrickFlags != 0)
        {
            var tflags = model_trick._TrickFlags;
            if (tflags.HasFlag(TrickFlags.Additive))
            {
                descriptor.isAdditive = true;
            }

            if (tflags.HasFlag(TrickFlags.ColorOnly))
            {
                onlyColor = model_trick.TintColor0;
            }
            if (tflags.HasFlag(TrickFlags.DoubleSided))
            {
                descriptor.targetCulling = UnityEngine.Rendering.CullMode.Off;
            }
            if (tflags.HasFlag(TrickFlags.NoZTest))
            {
                // simulate disabled Z test
                descriptor.depthTest = CompareFunction.Always;
                //depthTest = CompareFunction.Always;
                descriptor.depthWrite = false;
            }

            if (tflags.HasFlag(TrickFlags.NoZWrite))
            {
                descriptor.depthWrite = false;
            }
            if (tflags.HasFlag(TrickFlags.SetColor))
            {
                tint1   = model_trick.TintColor0;
                tint2   = model_trick.TintColor1;
                tint1.a = 1.0f;
                tint2.a = 1.0f;
            }

            if (tflags.HasFlag(TrickFlags.ReflectTex0) || tflags.HasFlag(TrickFlags.ReflectTex1))
            {
                shader_to_use = "Custom/ReflectGen";
                if (mdl.flags.HasFlag(ModelFlags.OBJ_CUBEMAP))
                {
                    Debug.Log("Unhandled Cubemap");
                }
            }

            if (tflags.HasFlag(TrickFlags.AlphaRef))
            {
                //qDebug() << "Unhandled alpha ref";
                alphaRef = geom_trick.AlphaRef;
            }

            if (tflags.HasFlag(TrickFlags.TexBias))
            {
                Debug.Log("Unhandled TexBias");
            }
        }

        CompareFunction depthTest     = CompareFunction.LessEqual;
        TextureWrapper  whitetex      = GeoSet.loadTexHeader("white");
        var             pixel_defines = new List <string>();

        pixel_defines.Add("DIFFMAP");
        pixel_defines.Add("ALPHAMASK");
        switch (mdl.BlendMode)
        {
        case CoHBlendMode.MULTIPLY:
            pixel_defines.Add("COH_MULTIPLY");
            break;

        case CoHBlendMode.MULTIPLY_REG:
            if (!descriptor.depthWrite && descriptor.isAdditive)
            {
                descriptor.targetCulling = UnityEngine.Rendering.CullMode.Off;
            }

            pixel_defines.Add("COH_MULTIPLY");
            break;

        case CoHBlendMode.COLORBLEND_DUAL:
            pixel_defines.Add("COH_COLOR_BLEND_DUAL");
            break;

        case CoHBlendMode.ADDGLOW:
            pixel_defines.Add("COH_ADDGLOW");
            break;

        case CoHBlendMode.ALPHADETAIL:
            pixel_defines.Add("COH_ALPHADETAIL");
            break;

        case CoHBlendMode.BUMPMAP_MULTIPLY:
            pixel_defines.Add("COH_MULTIPLY");
            break;

        case CoHBlendMode.BUMPMAP_COLORBLEND_DUAL:
            pixel_defines.Add("COH_COLOR_BLEND_DUAL");
            break;
        }

        if (mdl.flags.HasFlag(ModelFlags.OBJ_TREE))
        {
            shader_to_use = "CoH/Vegetation";
            alphaRef      = 0.4f;
            tint1.a       = 254.0f / 255.0f;
        }

        Material[] materials = new Material[mdl.texture_bind_info.Count];
        int        idx       = 0;

        Tools.EnsureDirectoryExists(material_base_path);
        var sup = tgt.GetComponent <ModelNodeMods>();

        sup.GeomTricks = geom_trick;
        VBOPointers vbo      = mdl.vbo;
        Shader      selected = null;

        if (descriptor.isAdditive)
        {
            if (shader_to_use == "Custom/ReflectGen")
            {
                selected = Shader.Find("Custom/ReflectGen");
            }
            else
            {
                selected = Shader.Find("Unlit/Additive");
            }
        }
        else
        {
            if (!String.IsNullOrEmpty(shader_to_use))
            {
                selected = Shader.Find(shader_to_use);
            }
            else
            {
                selected = Shader.Find("CoH/CoHMult");
            }
        }

        sup.TexWrappers = vbo.assigned_textures;

        foreach (TextureWrapper wrap in vbo.assigned_textures)
        {
            string   path_material_name = String.Format("{0}/{1}_{2}.mat", material_base_path, idx, descriptor.GetHashCode());
            Material available          = AssetDatabase.LoadAssetAtPath <Material>(path_material_name);
            if (available == null)
            {
                if (wrap != null)
                {
                    TextureWrapper detail = null;
                    if (!String.IsNullOrEmpty(wrap.detailname))
                    {
                        detail = GeoSet.loadTexHeader(wrap.detailname);
                    }


                    Material mat = new Material(selected);
                    mat.enableInstancing = true;
                    mat.SetColor("_Color", tint1);
                    mat.SetColor("_Color2", tint2);
                    mat.SetFloat("_AlphaRef", alphaRef);
                    mat.SetInt("_CoHMod", (int)mdl.BlendMode);

                    mat.SetTexture("_MainTex", wrap.tex);
                    if (detail != null)
                    {
                        mat.SetTexture("_Detail", detail.tex);
                    }
                    mat.SetInt("_ZWrite", descriptor.depthWrite ? 1 : 0);
                    mat.SetInt(ZTest, (int)descriptor.depthTest);
                    mat.SetInt(CullMode, (int)descriptor.targetCulling);
                    mat.SetTextureScale(MainTex, wrap.scaleUV1);
                    mat.SetTextureScale(Detail, wrap.scaleUV0);
                    AssetDatabase.CreateAsset(mat, path_material_name);
                    AssetDatabase.SaveAssets();
                    available = AssetDatabase.LoadAssetAtPath <Material>(path_material_name);
                }
            }
            materials[idx++] = available;
        }

        if (mdl.flags.HasFlag(ModelFlags.OBJ_HIDE))
        {
            ren.enabled = false;
        }

        ren.sharedMaterials = materials;
    }
예제 #9
0
    private static void convertModel(SceneNode n, GameObject res, bool convert_editor_markers)
    {
        string mesh_path;
        string model_path;
        int    obj_lib_idx;

        Model          mdl         = n.m_model;
        ModelModifiers model_trick = mdl.trck_node;

        mesh_path   = mdl.geoset.full_geo_path;
        obj_lib_idx = mesh_path.IndexOf("object_library");
        if (obj_lib_idx != -1)
        {
            mesh_path = "Assets/Meshes/" + mesh_path.Substring(obj_lib_idx);
        }
        model_path = GetSafeFilename(mesh_path + "/" + Path.GetFileNameWithoutExtension(mdl.name) + ".asset");

        MeshFilter   mf  = res.AddComponent <MeshFilter>();
        MeshRenderer ren = res.AddComponent <MeshRenderer>();
        var          sup = res.AddComponent <ModelNodeMods>();

        if (model_trick != null)
        {
            sup.ModelMod = model_trick;
        }

        mf.sharedMesh = AssetDatabase.LoadAssetAtPath <Mesh>(model_path);
        if (model_trick != null)
        {
            ren.shadowCastingMode = ShadowCastingMode.Off;
            if (model_trick._TrickFlags.HasFlag(TrickFlags.NoDraw))
            {
                ren.receiveShadows = false;
                res.tag            = "NoDraw";
                res.layer          = 9;
            }

            if (model_trick._TrickFlags.HasFlag(TrickFlags.EditorVisible))
            {
                ren.receiveShadows = false;
                res.layer          = 9;
            }

            if (model_trick._TrickFlags.HasFlag(TrickFlags.CastShadow))
            {
                ren.receiveShadows    = false;
                ren.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
            }

            if (model_trick._TrickFlags.HasFlag(TrickFlags.ParticleSys))
            {
                Debug.Log("Not converting particle sys:" + mdl.name);
                return;
            }
        }
        if (!mdl.geoset.data_loaded)
        {
            mdl.geoset.LoadData();
            if (mdl.geoset.subs.Count != 0)
            {
                mdl.geoset.createEngineModelsFromPrefabSet();
            }
        }
        if (mf.sharedMesh == null)
        {
            UnityModel  res_static;
            RuntimeData rd = RuntimeData.get();
            if (!rd.s_coh_model_to_engine_model.TryGetValue(mdl, out res_static))
            {
                return;
            }
            if (res_static == null)
            {
                return;
            }
            SEGSRuntime.Tools.EnsureDirectoryExists(mesh_path);
            AssetDatabase.CreateAsset(res_static.m_mesh, model_path);
            AssetDatabase.SaveAssets();
            mf.sharedMesh = AssetDatabase.LoadAssetAtPath <Mesh>(model_path);
        }

        convertMaterials(ren, mdl, res);
    }
예제 #10
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);
        }
예제 #11
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);
        }