public static (string[], string) GetDefaultTexturesAndActive(Imported model, string defFromVisibleMesh)
        {
            string[] textures = new string[model.materialMappings.Length];
            SKAnimatorToolsProxy.IncrementEnd(textures.Length);

            for (int index = 0; index < model.materialMappings.Length; index++)
            {
                MaterialMapping mapping = model.materialMappings[index];
                ConfigReference texRef  = (ConfigReference)mapping.material.getArguments().getOrDefault("Texture", null);
                if (texRef != null)
                {
                    string file = (string)texRef.getArguments().getOrDefault("File", null);
                    if (file != null)
                    {
                        textures[index] = file;
                        if (mapping.texture == defFromVisibleMesh)
                        {
                            defFromVisibleMesh = new FileInfo(file).Name;
                        }
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            return(textures, defFromVisibleMesh);
        }
        /// <summary>
        /// A utility function that iterates through all of the nodes recursively, as some may store mesh data.<para/>
        /// This does not decode the rig. It strictly grabs meshes out of the rig.
        /// </summary>
        /// <param name="baseModel">The <see cref="ModelConfig"/> that contained this <see cref="ArticulatedConfig"/>.</param>
        /// <param name="model">A reference to the <see cref="ArticulatedConfig"/> that contains these nodes.</param>
        /// <param name="sourceFile">The file where the <see cref="ArticulatedConfig"/> is stored.</param>
        /// <param name="parent">The parent node to iterate through.</param>
        /// <param name="models">The <see cref="List{T}"/> of all models ripped from the source .dat file in this current chain (which may include references to other .dat files)</param>
        /// <param name="latestTransform">The latest transform that has been applied. This is used for recursive motion since nodes inherit the transform of their parent.</param>
        /// <param name="initialTransform"></param>
        /// <param name="nodeModels">A lookup from a <see cref="Node"/> to a <see cref="Model3D"/>, which does include potentially empty models for standard, non-mesh nodes.</param>
        /// <param name="fullDepthName">The complete path to this model from rsrc, rsrc included.</param>
        public static void RecursivelyIterateNodes(ModelConfig baseModel, ArticulatedConfig model, FileInfo sourceFile, Node parent, List <Model3D> models, Transform3D latestTransform, Transform3D initialTransform, Dictionary <string, Model3D> nodeModels, string fullDepthName)
        {
            foreach (Node node in parent.children)
            {
                // Transform3D newTransform = latestTransform;
                if (node is MeshNode meshNode)
                {
                    VisibleMesh mesh = meshNode.visible;
                    if (mesh != null)
                    {
                        // "Let's use a node designed to store meshes for something that doesn't contain meshes!"
                        //		-- Some knucklehead at OOO.
                        // ...No offense.

                        string meshTitle = "-MeshNodes[\"" + node.name + "\"]";

                        Model3D meshToModel = GeometryConfigTranslator.GetGeometryInformation(mesh.geometry, fullDepthName + meshTitle);
                        meshToModel.Name    = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile) + meshTitle;
                        meshToModel.RawName = node.name;

                        (List <string> textureFiles, string active) = ModelPropertyUtility.FindTexturesAndActiveFromDirects(baseModel, mesh.texture);
                        meshToModel.Textures.SetFrom(textureFiles);
                        meshToModel.ActiveTexture = active;

                        /*
                         * meshToModel.Textures.SetFrom(ModelPropertyUtility.FindTexturesFromDirects(baseModel));
                         * meshToModel.ActiveTexture = mesh.texture;
                         */

                        // Modify the transform that it already has to the node's transform.
                        meshToModel.Transform.composeLocal(latestTransform);
                        meshToModel.Transform.composeLocal(node.transform);

                        nodeModels[node.name] = meshToModel;
                        models.Add(meshToModel);
                    }
                }
                else
                {
                    string  meshTitle  = "-Nodes[\"" + node.name + "\"]";
                    Model3D emptyModel = Model3D.NewEmpty();
                    emptyModel.Name = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile) + meshTitle;
                    emptyModel.Transform.composeLocal(latestTransform);
                    emptyModel.Transform.composeLocal(node.transform);

                    nodeModels[node.name] = emptyModel;
                    models.Add(emptyModel);
                }

                //VertexGroup group = new VertexGroup();
                //group.Name = node.name;

                SKAnimatorToolsProxy.IncrementProgress();
                if (node.children.Length > 0)
                {
                    RecursivelyIterateNodes(baseModel, model, sourceFile, node, models, latestTransform.compose(node.transform), initialTransform, nodeModels, fullDepthName);
                }
            }
        }
Exemplo n.º 3
0
            public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
            {
                Schemed schemed = (Schemed)baseModel.implementation;

                SchemedModel[] models = schemed.models;
                SKAnimatorToolsProxy.IncrementEnd(models.Length);
                foreach (SchemedModel schemedModel in models)
                {
                    // These shouldn't send the extra data over(?)
                    ConfigReferenceUtil.HandleConfigReference(sourceFile, schemedModel.model, modelCollection, dataTreeParent, globalTransform);
                    SKAnimatorToolsProxy.IncrementProgress();
                }
            }
Exemplo n.º 4
0
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            ConditionalConfig model = (ConditionalConfig)baseModel.implementation;

            SetupCosmeticInformation(model, dataTreeParent);


            if (model.defaultModel != null)
            {
                SKAnimatorToolsProxy.IncrementEnd();
                List <Model3D> mdls = ConfigReferenceUtil.HandleConfigReference(sourceFile, model.defaultModel, modelCollection, dataTreeParent, globalTransform, false, extraData);
                if (mdls != null)
                {
                    foreach (Model3D mdl in mdls)
                    {
                        mdl.ExtraData["ConditionalConfigFlag"]    = true;
                        mdl.ExtraData["ConditionalConfigDefault"] = true;
                        if (model.defaultTransform != null)
                        {
                            mdl.Transform = model.defaultTransform;
                        }
                        modelCollection.Add(mdl);
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            SKAnimatorToolsProxy.IncrementEnd(model.cases.Length);
            foreach (ConditionalConfig.Case condition in model.cases)
            {
                List <Model3D> mdls = ConfigReferenceUtil.HandleConfigReference(sourceFile, condition.model, modelCollection, dataTreeParent, globalTransform, false, extraData);
                if (mdls != null)
                {
                    foreach (Model3D mdl in mdls)
                    {
                        mdl.ExtraData["ConditionalConfigFlag"]  = true;
                        mdl.ExtraData["ConditionalConfigValue"] = true;
                        try {
                            bool state = condition.condition.createEvaluator(new DummyScope(model)).evaluate();
                            mdl.ExtraData["ConditionalConfigValue"] = state;
                        } catch { }
                        if (condition.transform != null)
                        {
                            mdl.Transform = condition.transform;
                        }
                        modelCollection.Add(mdl);
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }
        }
        /// <summary>
        /// Assuming this is a <see cref="ModelConfig"/> storing textures, this will try to find the textures from its parameters. Additionally, it will find the default texture of the model.
        /// </summary>
        /// <param name="cfg"></param>
        /// <param name="defaultTextureFromVisibleMesh"></param>
        /// <returns></returns>
        public static (List <string>, string) FindTexturesAndActiveFromDirects(ModelConfig cfg, string defaultTextureFromVisibleMesh)
        {
            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.ExtraWork);

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

            if (cfg.implementation is Imported imported)
            {
                (string[], string)info = GetDefaultTexturesAndActive(imported, defaultTextureFromVisibleMesh);
                retn.AddRange(info.Item1);
                defaultTextureFromVisibleMesh = info.Item2;
            }


            SKAnimatorToolsProxy.IncrementEnd(cfg.parameters.Length);
            foreach (Parameter param in cfg.parameters)
            {
                if (param is Parameter.Choice choice)
                {
                    WrappedChoice         wChoice  = new WrappedChoice(cfg, choice);
                    ParameterizedConfig[] variants = wChoice.CreateVariantsFromOptions();

                    foreach (ParameterizedConfig variant in variants)
                    {
                        // Each variant is now a dupe of cfg with the given variant information applied to it.
                        if (variant is ModelConfig mdlVariant)
                        {
                            // This should always be true but it's just here for sanity checking.
                            if (mdlVariant.implementation is Imported subImported)
                            {
                                (string[], string)info = GetDefaultTexturesAndActive(subImported, defaultTextureFromVisibleMesh);
                                // retn.AddRange(GetDefaultTextures(subImported));
                                retn.AddRange(info.Item1);
                                defaultTextureFromVisibleMesh = info.Item2;                                 // If it couldn't be found, the method returns what is input.
                                // As such, this will remain unchanged.
                            }
                        }
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.OK);
            return(retn, defaultTextureFromVisibleMesh);
        }
Exemplo n.º 6
0
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            // ModelConfigHandler.SetupCosmeticInformation(baseModel, dataTreeParent);
            CompoundConfig compound = (CompoundConfig)baseModel.implementation;

            SetupCosmeticInformation(compound, dataTreeParent);

            // NEW: Some stuff uses this trick to pick a specific model out of a compound
            // Parameters contain three key bits of data:
            // 1: Directs (short for Directives) -- These store the values that each option edits.
            // 2: Options -- These are the actual options you can pick, and contain a value for each direct that's been defined for this param
            // 3: Choice -- The currently selected option (or default option)

            ComponentModel[] componentModels = compound.models;
            SKAnimatorToolsProxy.IncrementEnd(componentModels.Length);
            foreach (ComponentModel model in componentModels)
            {
                ConfigReferenceUtil.HandleComponentModel(sourceFile, model, modelCollection, dataTreeParent, globalTransform, true, extraData);
                SKAnimatorToolsProxy.IncrementProgress();
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Sends an arbitrary <see cref="TudeySceneModel"/> into the data brancher and processes it.
        /// </summary>
        /// <param name="sourceFile">The file that the given <see cref="TudeySceneModel"/> came from.</param>
        /// <param name="scene">The <see cref="TudeySceneModel"/> itself.</param>
        /// <param name="models">A list containing every processed model from the entire hierarchy.</param>
        /// <param name="currentDataTreeObject">The current element in the data tree hierarchy to use.</param>
        /// <param name="useImplementation">If <see langword="false"/>, the name of the implementation will be displayed instead of the file name. Additionally, it will not have its implementation property.</param>
        /// <param name="transform">Intended to be used by reference loaders, this specifies an offset for referenced models. All models loaded by this method in the given chain / hierarchy will have this transform applied to them. If the value passed in is <see langword="null"/>, it will be substituted with a new <see cref="Transform3D"/>.</param>
        public static void HandleDataFrom(FileInfo sourceFile, TudeySceneModel scene, List <Model3D> models, DataTreeObject currentDataTreeObject = null, bool useImplementation = false, Transform3D transform = null)
        {
            SetupCosmeticData(scene, currentDataTreeObject);
            XanLogger.WriteLine("Iterating through scene entries...", XanLogger.DEBUG);

            SKAnimatorToolsProxy.IncrementEnd();
            string implName = (JavaClassNameStripper.GetWholeClassName(scene.getClass()) ?? scene.getClass().getTypeName()).Replace("$", "::");

            if (currentDataTreeObject != null)
            {
                if (useImplementation)
                {
                    currentDataTreeObject.Text = implName;
                }
                else
                {
                    currentDataTreeObject.Text = sourceFile.Name;
                }
            }

            object[] entries = scene.getEntries().toArray();
            SKAnimatorToolsProxy.IncrementEnd(entries.Length);
            foreach (object entryObj in entries)
            {
                // Now each entry will be one of three types (at least, in the context that we care about)
                Entry entry = (Entry)entryObj;
                if (entry is TileEntry)
                {
                    TileHandler.Instance.HandleEntry(sourceFile, entry, models, currentDataTreeObject, transform);
                }
                else if (entry is PlaceableEntry)
                {
                    PlaceableHandler.Instance.HandleEntry(sourceFile, entry, models, currentDataTreeObject, transform);
                }
                // Other entry types are more for game data and less for the visual scene (e.g. pathfinding nodes, area markers, etc.)
                SKAnimatorToolsProxy.IncrementProgress();
            }
            SKAnimatorToolsProxy.IncrementProgress();
        }
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            // ModelConfigHandler.SetupCosmeticInformation(baseModel, dataTreeParent);
            StaticConfig model = (StaticConfig)baseModel.implementation;

            SetupCosmeticInformation(model, dataTreeParent);

            //Model3D mdl = new Model3D();
            MeshSet meshes = model.meshes;

            VisibleMesh[] renderedMeshes = meshes.visible;

            SKAnimatorToolsProxy.IncrementEnd(renderedMeshes.Length);
            int    idx           = 0;
            string depth1Name    = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile);
            string fullDepthName = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile, -1);

            foreach (VisibleMesh mesh in renderedMeshes)
            {
                string meshTitle = "-Mesh[" + idx + "]";


                Model3D meshToModel = GeometryConfigTranslator.GetGeometryInformation(mesh.geometry, fullDepthName + meshTitle);
                meshToModel.Name      = depth1Name + meshTitle;
                meshToModel.Transform = meshToModel.Transform.compose(globalTransform).compose(new Transform3D(meshes.bounds.getCenter(), Quaternion.IDENTITY, 1f));
                //meshToModel.Textures.SetFrom(ModelConfigHandler.GetTexturesFromModel(sourceFile, model));
                //meshToModel.Textures.SetFrom(ModelPropertyUtility.FindTexturesFromDirects(baseModel));
                //meshToModel.Textures.SetFrom(new List<string>() { mesh.texture });
                //meshToModel.ActiveTexture = mesh.texture;

                (List <string> textureFiles, string active) = ModelPropertyUtility.FindTexturesAndActiveFromDirects(baseModel, mesh.texture);
                meshToModel.Textures.SetFrom(textureFiles);
                meshToModel.ActiveTexture = active;

                modelCollection.Add(meshToModel);
                idx++;
                SKAnimatorToolsProxy.IncrementProgress();
            }
        }
Exemplo n.º 9
0
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            ViewerAffecterConfig vac    = (ViewerAffecterConfig)baseModel.implementation;
            ViewerEffectConfig   effect = vac.effect;

            SetupCosmeticInformation(vac, dataTreeParent);

            SKAnimatorToolsProxy.IncrementEnd();
            if (effect is Skybox skybox)
            {
                string filePathRelativeToRsrc = skybox.model?.getName();
                if (filePathRelativeToRsrc != null)
                {
                    // If this is null, it is okay!
                    // Certain implementations, (for instance, schemed implementations) use this to define their render scheme.

                    if (filePathRelativeToRsrc.StartsWith("/"))
                    {
                        filePathRelativeToRsrc = filePathRelativeToRsrc.Substring(1);
                    }
                    FileInfo referencedModel = new FileInfo(ResourceDirectoryGrabber.ResourceDirectoryPath + filePathRelativeToRsrc);
                    if (!referencedModel.Exists)
                    {
                        throw new ClydeDataReadException($"ViewerEffectConfig::Skybox at [{ResourceDirectoryGrabber.GetFormattedPathFromRsrc(sourceFile, false)}] attempted to reference [{filePathRelativeToRsrc}], but this file could not be found!");
                    }

                    // Note to self: DO NOT USE SCALE.
                    // The scale value of skyboxes is used for a parallax effect (the scale = "how much does the skybox move relative to the camera")
                    // Applying this scale is not proper.
                    Transform3D newTrs = new Transform3D(skybox.translationOrigin, Quaternion.IDENTITY);

                    // Now one thing to note is that transforms do NOT affect skyboxes.
                    // As such, the new translation should NOT be affected by the global transform.
                    ClydeFileHandler.HandleClydeFile(referencedModel, modelCollection, false, dataTreeParent, false, newTrs);
                }
            }
            SKAnimatorToolsProxy.IncrementProgress();
        }
        /// <summary>
        /// Assuming this is a <see cref="ModelConfig"/> storing textures, this will try to find the textures from its parameters.
        /// </summary>
        /// <param name="cfg"></param>
        /// <returns></returns>
        public static List <string> FindTexturesFromDirects(ModelConfig cfg)
        {
            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.ExtraWork);

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

            if (cfg.implementation is Imported imported)
            {
                retn.AddRange(GetDefaultTextures(imported).ToList());
            }

            SKAnimatorToolsProxy.IncrementEnd(cfg.parameters.Length);
            foreach (Parameter param in cfg.parameters)
            {
                if (param is Parameter.Choice choice)
                {
                    WrappedChoice         wChoice  = new WrappedChoice(cfg, choice);
                    ParameterizedConfig[] variants = wChoice.CreateVariantsFromOptions();
                    foreach (ParameterizedConfig variant in variants)
                    {
                        // Each variant is now a dupe of cfg with the given variant information applied to it.
                        if (variant is ModelConfig mdlVariant)
                        {
                            // This should always be true but it's just here for sanity checking.
                            if (mdlVariant.implementation is Imported subImported)
                            {
                                retn.AddRange(GetDefaultTextures(subImported));
                            }
                        }
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.OK);
            return(retn);
        }
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            MergedStaticConfig mergedStatic = (MergedStaticConfig)baseModel.implementation;

            ComponentModel[] componentModels = mergedStatic.models;
            SKAnimatorToolsProxy.IncrementEnd(componentModels.Length);
            foreach (ComponentModel model in componentModels)
            {
                string filePathRelativeToRsrc = model.model.getName();
                if (filePathRelativeToRsrc.StartsWith("/"))
                {
                    filePathRelativeToRsrc = filePathRelativeToRsrc.Substring(1);
                }
                FileInfo referencedModel = new FileInfo(ResourceDirectoryGrabber.ResourceDirectoryPath + filePathRelativeToRsrc);
                if (!referencedModel.Exists)
                {
                    throw new ClydeDataReadException($"CompoundConfig at [{ResourceDirectoryGrabber.GetFormattedPathFromRsrc(sourceFile, false)}] attempted to reference [{filePathRelativeToRsrc}], but this file could not be found!");
                }
                Transform3D newTrs = model.transform;
                newTrs = globalTransform.compose(newTrs);
                ClydeFileHandler.HandleClydeFile(referencedModel, modelCollection, false, dataTreeParent, transform: newTrs);
                SKAnimatorToolsProxy.IncrementProgress();
            }
        }
Exemplo n.º 12
0
        public void HandleEntry(FileInfo sourceFile, Entry entry, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null)
        {
            TileEntry tile = (TileEntry)entry;

            //Transform3D trs = new Transform3D();
            //tile.getTransform(tile.getConfig(scene.getConfigManager()), trs);

            SetupCosmeticInformation(tile, dataTreeParent);
            // TODO: Some config references have SK data that I need. I need to do a hybrid of my shallow implementation on
            // top of the bootstrapper that loads the actual filtered game data so that I can grab everything.
            TileConfig[] tileCfgs = ConfigReferenceBootstrapper.ConfigReferences["tile"].OfType <TileConfig>().ToArray();
            TileConfig   tileCfg  = tileCfgs.GetEntryByName(tile.tile.getName());

            if (tileCfg == null)
            {
                XanLogger.WriteLine($"Unable to find data for tile [{tile.tile.getName()}]!");
                return;
            }


            // First things first: Tiles are offset and in the wrong position. Fix it.

            SKAnimatorToolsProxy.IncrementEnd();
            TileConfig.Original originalImpl;
            do
            {
                if (tileCfg == null)
                {
                    XanLogger.WriteLine("ALERT: A tile was null!", color: System.Drawing.Color.Red);
                    return;
                }
                if (tileCfg.getConfigManager() != null)
                {
                    originalImpl = tileCfg.getOriginal(tileCfg.getConfigManager());
                    break;
                }
                else
                {
                    if (tileCfg.implementation is TileConfig.Original original)
                    {
                        originalImpl = original;
                        break;
                    }
                    else if (tileCfg.implementation is TileConfig.Derived derived)
                    {
                        tileCfg = tileCfgs.GetEntryByName(derived.tile.getName());
                    }
                    else
                    {
                        originalImpl = null;
                        break;
                    }
                }
            } while (true);


            //tile.GetExportTransform(originalImpl, out Transform3D transform);
            // All transforms are relative to the center of the object. the origin of the actual models is in their lower-back-left bounds.
            // I need to add a flag to tell the exporter to move the geometry based on bounds center.
            Transform3D transform = new Transform3D();

            tile.getTransform(originalImpl, transform);
            //transform.getTranslation().addLocal(new Vector3f(0, tile.elevation / 2, 0));


            string relativeModelPath = originalImpl.model.getName();

            XanLogger.WriteLine("Grabbing tile [" + tile.tile.getName() + "] at " + relativeModelPath, XanLogger.DEBUG);
            ConfigReferenceUtil.HandleConfigReference(sourceFile, originalImpl.model, modelCollection, dataTreeParent, globalTransform.compose(transform));
            SKAnimatorToolsProxy.IncrementProgress();
        }
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            StaticSetConfig staticSet = (StaticSetConfig)baseModel.implementation;

            SetupCosmeticInformation(staticSet, dataTreeParent);

            string depth1Name    = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile);
            string fullDepthName = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile, -1);

            if (staticSet.meshes != null)
            {
                SKAnimatorToolsProxy.IncrementEnd(staticSet.meshes.size());

                if (extraData != null && extraData.ContainsKey("DirectArgs"))
                {
                    Dictionary <string, dynamic> directs = extraData["DirectArgs"];
                    SKAnimatorToolsProxy.IncrementEnd(1);
                    SKAnimatorToolsProxy.SetProgressState(ProgressBarState.ExtraWork);
                    bool got = false;
                    foreach (string key in directs.Keys)
                    {
                        Parameter param = baseModel.getParameter(key);
                        if (param is Parameter.Direct direct)
                        {
                            if (direct.paths.Contains("implementation.model"))
                            {
                                staticSet.model = directs[key];
                                XanLogger.WriteLine("Set model to " + staticSet.model, XanLogger.DEBUG);
                                SKAnimatorToolsProxy.IncrementProgress();
                                got = true;
                                break;
                            }
                        }
                        else if (param is Parameter.Choice choice)
                        {
                            foreach (Parameter.Direct dir in choice.directs)
                            {
                                if (dir.paths.Contains("implementation.model"))
                                {
                                    staticSet.model = directs[key];
                                    XanLogger.WriteLine("Set model to " + staticSet.model, XanLogger.DEBUG);
                                    SKAnimatorToolsProxy.IncrementProgress();
                                    got = true;
                                    break;
                                }
                            }
                        }
                    }
                    if (!got)
                    {
                        SKAnimatorToolsProxy.IncrementProgress();                           // Just inc anyway
                    }
                }

                // Export them all!
                object[] keys = staticSet.meshes.keySet().toArray();
                SKAnimatorToolsProxy.IncrementEnd(keys.Length);
                foreach (object key in keys)
                {
                    MeshSet       subModel = (MeshSet)staticSet.meshes.get(key);
                    VisibleMesh[] meshes   = subModel.visible;
                    int           idx      = 0;

                    SKAnimatorToolsProxy.IncrementEnd(meshes.Length);
                    foreach (VisibleMesh mesh in meshes)
                    {
                        string meshTitle = "-MeshSets[" + key.ToString() + "].Mesh[" + idx + "]";

                        Model3D meshToModel = GeometryConfigTranslator.GetGeometryInformation(mesh.geometry, fullDepthName + meshTitle);
                        meshToModel.Name = depth1Name + meshTitle;
                        meshToModel.ExtraData["StaticSetEntryName"] = key.ToString();
                        meshToModel.ExtraData["StaticSetConfig"]    = staticSet;
                        if (globalTransform != null)
                        {
                            meshToModel.Transform = globalTransform.compose(meshToModel.Transform);
                        }

                        //meshToModel.Textures.SetFrom(ModelPropertyUtility.FindTexturesFromDirects(baseModel));
                        //meshToModel.ActiveTexture = mesh.texture;

                        (List <string> textureFiles, string active) = ModelPropertyUtility.FindTexturesAndActiveFromDirects(baseModel, mesh.texture);
                        meshToModel.Textures.SetFrom(textureFiles);
                        meshToModel.ActiveTexture = active;

                        modelCollection.Add(meshToModel);
                        idx++;
                        SKAnimatorToolsProxy.IncrementProgress();
                    }

                    SKAnimatorToolsProxy.IncrementProgress();
                }
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Sends an arbitrary <see cref="ModelConfig"/> into the data brancher and processes it.
        /// </summary>
        /// <param name="sourceFile">The file that the given <see cref="ModelConfig"/> came from.</param>
        /// <param name="model">The <see cref="ModelConfig"/> itself.</param>
        /// <param name="models">A list containing every processed model from the entire hierarchy.</param>
        /// <param name="currentDataTreeObject">The current element in the data tree hierarchy to use.</param>
        /// <param name="useImplementation">If <see langword="false"/>, the name of the implementation will be displayed instead of the file name. Additionally, it will not have its implementation property.</param>
        /// <param name="transform">Intended to be used by reference loaders, this specifies an offset for referenced models. All models loaded by this method in the given chain / hierarchy will have this transform applied to them. If the value passed in is <see langword="null"/>, it will be substituted with a new <see cref="Transform3D"/>.</param>
        /// <param name="extraData">Any extra data that should be included. This is mainly used by references (e.g. a reference is a <see cref="StaticSetConfig"/>, the target model in the set may be included as extra data)</param>
        public static void HandleDataFrom(FileInfo sourceFile, ModelConfig model, List <Model3D> models, DataTreeObject currentDataTreeObject = null, bool useImplementation = false, Transform3D transform = null, Dictionary <string, dynamic> extraData = null)
        {
            transform = transform ?? new Transform3D();
            //transform.promote(Transform3D.GENERAL);

            SKAnimatorToolsProxy.IncrementEnd(2);

            ModelConfig.Implementation implementation = model.implementation;
            if (implementation == null)
            {
                XanLogger.WriteLine("Implementation is null! Sending error.", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.Text     = "Unknown Implementation";
                    currentDataTreeObject.ImageKey = SilkImage.Generic;
                }
                SKAnimatorToolsProxy.SetProgressState(ProgressBarState.Error);
                throw new ClydeDataReadException("This specific model does not have an implementation, which is the data for the model itself. This generally happens if the implementation is from another game that uses Clyde and has defined its own custom model types (e.g. Spiral Knights does this). As a result, the program cannot extract any information from this file. Sorry!", "Can't Read Model", MessageBoxIcon.Error);
            }

            string implName = (JavaClassNameStripper.GetWholeClassName(implementation.getClass()) ?? implementation.getClass().getTypeName()).Replace("$", "::");

            if (currentDataTreeObject != null)
            {
                if (useImplementation)
                {
                    currentDataTreeObject.Text = implName;
                }
                else
                {
                    currentDataTreeObject.Text = sourceFile.Name;
                }
            }

            ModelConfigHandler.SetupCosmeticInformation(model, currentDataTreeObject, useImplementation);

            SKAnimatorToolsProxy.IncrementProgress();             // Got model display data.
            // Next one is to load the data.
            if (implementation is ArticulatedConfig)
            {
                XanLogger.WriteLine("Model is of the type 'ArticulatedConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Articulated;
                }

                // New special case: Is it a knight?
                //if (sourceFile.Directory.Name == "crew" && sourceFile.Name == "model.dat") {
                //	XanLogger.WriteLine("Model was a knight. Performing special behavior for knights...", XanLogger.DEBUG);
                //	NPCKnightHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
                //} else {
                ArticulatedConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
                //}
            }
            else if (implementation is StaticConfig)
            {
                XanLogger.WriteLine("Model is of the type 'StaticConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Static;
                }
                StaticConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is StaticSetConfig)
            {
                XanLogger.WriteLine("Model is of the type 'StaticSetConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.MergedStatic;
                }
                StaticSetConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is MergedStaticConfig)
            {
                XanLogger.WriteLine("Model is of type 'MergedStaticConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.ModelSet;
                }
                MergedStaticConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is CompoundConfig)
            {
                XanLogger.WriteLine("Model is of the type 'CompoundConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.ModelSet;
                }
                CompoundConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is ViewerAffecterConfig)
            {
                XanLogger.WriteLine("Model is of the type 'ViewerAffecterConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.CameraShake;
                }
                ViewerAffecterConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is ModelConfig.Derived)
            {
                XanLogger.WriteLine("Model is of the type 'ModelConfig::Derived'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Derived;
                }
                ModelConfigHandler.DerivedHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is ModelConfig.Schemed)
            {
                XanLogger.WriteLine("Model is of the type 'ModelConfig::Schemed'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Schemed;
                }
                ModelConfigHandler.SchemedHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else if (implementation is ConditionalConfig)
            {
                XanLogger.WriteLine("Model is of the type 'ConditionalConfig'. Accessing handlers...", XanLogger.DEBUG);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Conditional;
                }
                ConditionalConfigHandler.Instance.HandleModelConfig(sourceFile, model, models, currentDataTreeObject, transform, extraData);
            }
            else
            {
                XanLogger.WriteLine($"\nERROR: A ModelConfig had an unknown implementation!\n=> Implementation: {implName}\n=> Referenced In: {sourceFile}\n", XanLogger.DEBUG);
                // AsyncMessageBox.ShowAsync("This specific implementation is valid, but it has no handler! (There's no code that can translate this data for you :c).\nImplementation: " + implementation.getClass().getTypeName(), "Can't Handle Model", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                if (currentDataTreeObject != null)
                {
                    currentDataTreeObject.ImageKey = SilkImage.Generic;
                }
            }
            SKAnimatorToolsProxy.IncrementProgress();
            // Handled model.
        }
        public void HandleModelConfig(FileInfo sourceFile, ModelConfig baseModel, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null, Dictionary <string, dynamic> extraData = null)
        {
            // ModelConfigHandler.SetupCosmeticInformation(baseModel, dataTreeParent);

            // ArticulatedConfig has a lot of steps.
            SKAnimatorToolsProxy.IncrementEnd(4);

            ArticulatedConfig model = (ArticulatedConfig)baseModel.implementation;

            SetupCosmeticInformation(model, dataTreeParent);

            MeshSet meshes = model.skin;

            VisibleMesh[] renderedMeshes = meshes.visible;
            Dictionary <string, Armature> allInstantiatedArmatures = new Dictionary <string, Armature>();

            List <Model3D> allModelsAndNodes = new List <Model3D>();

            // 1
            SKAnimatorToolsProxy.IncrementProgress();

            int    idx           = 0;
            string depth1Name    = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile);
            string fullDepthName = ResourceDirectoryGrabber.GetDirectoryDepth(sourceFile, -1);

            SKAnimatorToolsProxy.IncrementEnd(renderedMeshes.Length);
            foreach (VisibleMesh mesh in renderedMeshes)
            {
                string meshTitle = "-Skin-Mesh[" + idx + "]";

                Model3D meshToModel = GeometryConfigTranslator.GetGeometryInformation(mesh.geometry, fullDepthName + meshTitle, model.root);
                meshToModel.Name = depth1Name + meshTitle;
                if (globalTransform != null)
                {
                    meshToModel.Transform.composeLocal(globalTransform);
                }

                (List <string> textureFiles, string active) = ModelPropertyUtility.FindTexturesAndActiveFromDirects(baseModel, mesh.texture);
                meshToModel.Textures.SetFrom(textureFiles);
                meshToModel.ActiveTexture = active;

                if (meshToModel.Mesh.HasBoneData)
                {
                    XanLogger.WriteLine("Model has bone data, setting that up.", XanLogger.TRACE);
                    // meshToModel.Mesh.SetBones(model.root);
                    // ^ now called by GetGeometryInformation
                    foreach (KeyValuePair <string, Armature> boneNamesToBones in meshToModel.Mesh.AllBones)
                    {
                        allInstantiatedArmatures[boneNamesToBones.Key] = boneNamesToBones.Value;
                    }
                    allModelsAndNodes.Add(meshToModel);
                }
                modelCollection.Add(meshToModel);
                idx++;

                SKAnimatorToolsProxy.IncrementProgress();
            }
            // 2
            SKAnimatorToolsProxy.IncrementProgress();

            SKAnimatorToolsProxy.IncrementEnd(GetNodeCount(model.root));
            Dictionary <string, Model3D> nodeModels = new Dictionary <string, Model3D>();

            RecursivelyIterateNodes(baseModel, model, sourceFile, model.root, modelCollection, globalTransform, globalTransform, nodeModels, fullDepthName);
            allModelsAndNodes.AddRange(nodeModels.Values);

            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.ExtraWork);
            SKAnimatorToolsProxy.IncrementEnd(model.attachments.Length);

            foreach (Attachment attachment in model.attachments)
            {
                List <Model3D> attachmentModels = ConfigReferenceUtil.HandleConfigReference(sourceFile, attachment.model, modelCollection, dataTreeParent, globalTransform);
                if (attachmentModels == null)
                {
                    SKAnimatorToolsProxy.IncrementProgress();
                    continue;                     // A lot of attachments have null models and I'm not sure why.
                }

                // NEW BEHAVIOR: Is the model root-less but rigged?
                // Set its root to *this* model
                foreach (Model3D mdl in attachmentModels)
                {
                    if (mdl.Mesh != null && mdl.Mesh.UsesExternalRoot)
                    {
                        mdl.Mesh.SetBones(model.root);
                    }
                }

                SKAnimatorToolsProxy.IncrementEnd(attachmentModels.Count);
                foreach (Model3D referencedModel in attachmentModels)
                {
                    referencedModel.Transform.composeLocal(attachment.transform);
                    if (allInstantiatedArmatures.ContainsKey(attachment.node ?? string.Empty))
                    {
                        referencedModel.AttachmentNode = allInstantiatedArmatures[attachment.node];
                        XanLogger.WriteLine("Attached [" + referencedModel.Name + "] to [" + attachment.node + "]", XanLogger.TRACE);
                    }
                    else
                    {
                        // New catch case: This might actually be the name of a model!
                        if (nodeModels.ContainsKey(attachment.node ?? string.Empty))
                        {
                            // Indeed it is!

                            referencedModel.AttachmentModel = nodeModels[attachment.node];
                            referencedModel.AttachmentModel.Transform.setScale(1f);                             // TODO: Is this okay?

                            if (referencedModel.Transform.getType() < Transform3D.AFFINE)
                            {
                                float scale = referencedModel.Transform.getScale();
                                referencedModel.Transform.set(new Transform3D(new Vector3f(), Quaternion.IDENTITY, scale));
                            }
                            else
                            {
                                Vector3f scale = referencedModel.Transform.extractScale();
                                referencedModel.Transform.set(new Transform3D(new Vector3f(), Quaternion.IDENTITY, scale));
                            }


                            XanLogger.WriteLine("Attached [" + referencedModel.Name + "] to [" + attachment.node + "]", XanLogger.TRACE);
                        }
                        else
                        {
                            XanLogger.WriteLine("Attachment wanted to attach to node or model [" + attachment.node + "] but it does not exist!");
                        }
                    }
                    SKAnimatorToolsProxy.IncrementProgress();
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            SKAnimatorToolsProxy.SetProgressState(ProgressBarState.OK);
            // 3
            SKAnimatorToolsProxy.IncrementProgress();

            SKAnimatorToolsProxy.IncrementEnd(model.animationMappings.Length);
            foreach (AnimationMapping animationMapping in model.animationMappings)
            {
                ConfigReference animationRef = animationMapping.animation;
                if (animationRef.IsFileReference())
                {
                    object animationObj = animationRef.ResolveFile();
                    if (animationObj is AnimationConfig animation)
                    {
                        SKAnimatorToolsProxy.SetProgressState(ProgressBarState.ExtraWork);
                        AnimationConfigHandler.HandleAnimationImplementation(animationRef, animationMapping.name, animation, animation.implementation, allModelsAndNodes);
                        SKAnimatorToolsProxy.SetProgressState(ProgressBarState.OK);
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            // 4
            SKAnimatorToolsProxy.IncrementProgress();
        }
Exemplo n.º 16
0
        public void HandleEntry(FileInfo sourceFile, Entry entry, List <Model3D> modelCollection, DataTreeObject dataTreeParent = null, Transform3D globalTransform = null)
        {
            PlaceableEntry placeable = (PlaceableEntry)entry;

            SetupCosmeticInformation(placeable, dataTreeParent);

            // TODO: Why the hell does referencing the transform field treat it like a method?
            // Is this some really stupid inheritence problem? I didn't even know this was possible.
            // EDIT: Yeah it is. Entry has a method called "transform", but placeable has a field called "transform". Great.
            // This may have been caused by the transpiler, which is to be expected. I'm actually quite suprised that I've not run into any errors until now.

            SKAnimatorToolsProxy.IncrementEnd();
            // TEST: Is this, by some slim chance, a file ref? (This can happen!)
            if (placeable.placeable.IsRealReference())
            {
                PlaceableConfig[] placeableCfgs = ConfigReferenceBootstrapper.ConfigReferences["placeable"].OfType <PlaceableConfig>().ToArray();
                PlaceableConfig   placeableCfg  = placeableCfgs.GetEntryByName(placeable.placeable.getName());
                if (placeableCfg == null)
                {
                    XanLogger.WriteLine($"Unable to find data for placeable [{placeable.placeable.getName()}]!");
                    return;
                }

                PlaceableConfig.Original originalImpl;
                do
                {
                    if (placeableCfg == null)
                    {
                        XanLogger.WriteLine("ALERT: A placeable was null!", color: System.Drawing.Color.Red);
                        return;
                    }
                    if (placeableCfg.getConfigManager() != null)
                    {
                        originalImpl = placeableCfg.getOriginal(placeableCfg.getConfigManager());
                        break;
                    }
                    else
                    {
                        if (placeableCfg.implementation is PlaceableConfig.Original org)
                        {
                            originalImpl = org;
                            break;
                        }
                        else if (placeableCfg.implementation is PlaceableConfig.Derived der)
                        {
                            placeableCfg = placeableCfgs.GetEntryByName(der.placeable.getName());
                        }
                        else
                        {
                            originalImpl = null;
                            break;
                        }
                    }
                } while (true);
                if (originalImpl != null)
                {
                    Transform3D transform         = GetTransformField(placeable);
                    string      relativeModelPath = originalImpl.model.getName();
                    XanLogger.WriteLine("Grabbing placeable [" + placeable.placeable.getName() + "] at " + relativeModelPath, XanLogger.DEBUG);
                    ConfigReferenceUtil.HandleConfigReference(sourceFile, originalImpl.model, modelCollection, dataTreeParent, globalTransform.compose(transform));
                }
                else
                {
                    XanLogger.WriteLine($"Implementation for placeable [{placeable.placeable.getName()}] does not exist!");
                }
            }
            else
            {
                Transform3D trs = GetTransformField(placeable);
                XanLogger.WriteLine("Grabbing placeable at " + placeable.placeable.getName(), XanLogger.DEBUG);
                ConfigReferenceUtil.HandleConfigReference(sourceFile, placeable.placeable, modelCollection, dataTreeParent, globalTransform.compose(trs));
            }
            SKAnimatorToolsProxy.IncrementProgress();
        }
        /// <summary>
        /// Returns the currently active textures for each of the <see cref="MaterialMapping"/>s within this <see cref="ModelConfig"/>.<para/>
        /// This will only pull the textures actively in use by the model. Any other variants will not be acquired.
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static string[] GetDefaultTextures(Imported model)
        {
            string[] textures = new string[model.materialMappings.Length];
            SKAnimatorToolsProxy.IncrementEnd(textures.Length);

            for (int index = 0; index < model.materialMappings.Length; index++)
            {
                MaterialMapping mapping = model.materialMappings[index];
                ConfigReference texRef  = (ConfigReference)mapping.material.getArguments().getOrDefault("Texture", null);
                if (texRef != null)
                {
                    string file = (string)texRef.getArguments().getOrDefault("File", null);
                    if (file != null)
                    {
                        textures[index] = file;
                    }
                }
                SKAnimatorToolsProxy.IncrementProgress();
            }

            /*
             * for (int index = 0; index < model.materialMappings.Length; index++) {
             *      MaterialMapping mapping = model.materialMappings[index];
             *      MaterialConfig material = mapping.material.ResolveAuto<MaterialConfig>();
             *      if (material != null) {
             *              while (material.implementation is MaterialConfig.Derived derivedMtl) {
             *                      material = derivedMtl.material.ResolveAuto<MaterialConfig>();
             *              }
             *
             *              if (material.implementation is MaterialConfig.Original originalMtl) {
             *                      foreach (TechniqueConfig technique in originalMtl.techniques) {
             *                              TechniqueConfig.Enqueuer enqueuer = technique.enqueuer;
             *                              if (enqueuer is TechniqueConfig.NormalEnqueuer normalEnq) {
             *                                      foreach (PassConfig pass in normalEnq.passes) {
             *                                              // Just find the first pass with a texture. It's not possible to have multiple textures right now in gltf
             *                                              // that is, on one model
             *                                              if (pass.textureState != null) {
             *                                                      TextureStateConfig texState = pass.textureState;
             *                                                      if (texState.units.Length > 0) {
             *                                                              TextureConfig texture = texState.units[0].texture.ResolveAuto<TextureConfig>();
             *                                                              while (texture.implementation is TextureConfig.Derived derivedTexture) {
             *                                                                      texture = derivedTexture.texture.ResolveAuto<TextureConfig>();
             *                                                              }
             *
             *                                                              if (texture.implementation is TextureConfig.Original2D tex2d) {
             *                                                                      TextureConfig.Original2D.Contents contents = tex2d.contents;
             *                                                                      if (contents is TextureConfig.Original2D.ImageFile imageFile) {
             *                                                                              textures[index] = imageFile.file;
             *                                                                      } else {
             *                                                                              // Blank.
             *                                                                              textures[index] = null;
             *                                                                      }
             *                                                              } else {
             *                                                                      XanLogger.WriteLine("Unsupported texture type " + texture.GetType().Name);
             *                                                              }
             *                                                      }
             *                                              }
             *                                      }
             *                              }
             *                      }
             *              }
             *      }
             *
             *      SKAnimatorToolsProxy.IncrementProgress();
             * }
             */
            return(textures);
        }
Exemplo n.º 18
0
        // private const string ERR_PROC_NOT_YET_SUPPORTED = "This Procedural animation [{0}] cannot be loaded yet! Only certain instances of this type work right now.";

        /// <summary>
        /// Handles the given <see cref="AnimationConfig"/> and applies it to the already loaded <see cref="Model3D"/>s.
        /// </summary>
        /// <param name="srcConfig"></param>
        /// <param name="name"></param>
        /// <param name="original"></param>
        /// <param name="animationImplementation"></param>
        /// <param name="attachToModels"></param>
        public static void HandleAnimationImplementation(ConfigReference srcConfig, string name, AnimationConfig original, AnimationConfig.Implementation animationImplementation, List <Model3D> attachToModels)
        {
            // FileInfo srcFile = new FileInfo(ResourceDirectoryGrabber.ResourceDirectoryPath + srcConfig.getName());
            SKAnimatorToolsProxy.IncrementEnd();
            // Clear out any derived references all the way until we dig down to an original implementation.
            if (animationImplementation is AnimationConfig.Derived derived)
            {
                animationImplementation = Dereference(derived);
            }

            if (animationImplementation is AnimationConfig.Imported imported)
            {
                // As OOO says:
                // The transforms for each target, each frame.
                Transform3D[][] transforms = imported.transforms;

                // So presumably this means that...
                // ...It's backwards! First dimension is the frame number, second dimension is the transform for each target.
                // big brain time

                // ... I swear they must've been waiting for someone like me to come along. Purposely being confusing.
                float fps = imported.rate;

                Animation animation     = new Animation(name);
                int       numIterations = transforms.Length;
                if (imported.skipLastFrame)
                {
                    numIterations--;
                }

                SKAnimatorToolsProxy.IncrementEnd(numIterations);
                for (int frameIndex = 0; frameIndex < numIterations; frameIndex++)
                {
                    Transform3D[]      targetFrames = transforms[frameIndex];
                    Animation.Keyframe keyframe     = new Animation.Keyframe();
                    for (int targetIndex = 0; targetIndex < imported.targets.Length; targetIndex++)
                    {
                        string      target    = imported.targets[targetIndex];
                        Transform3D transform = targetFrames[targetIndex];

                        // Catch case: Might be null.
                        // Since I'm hellbent on doing things oddly for this part of the program, I'll manually interpolate lol
                        if (transform == null)
                        {
                            Transform3D nextTransform     = null;
                            Transform3D previousTransform = null;
                            int         prevIndex         = 0;
                            int         nextIndex         = 0;
                            // Start at 1 because if there's a single frame gap, then the result of ^ will be 2
                            // And then this will count as frame 1 instead of 0, causing 1/2 or 0.5.
                            for (int aheadIndex = 0; aheadIndex < numIterations; aheadIndex++)
                            {
                                if (aheadIndex > frameIndex)
                                {
                                    nextTransform = transforms[aheadIndex][targetIndex];
                                    if (nextTransform != null)
                                    {
                                        nextIndex = aheadIndex;
                                    }
                                }
                                else
                                {
                                    Transform3D prev = transforms[aheadIndex][targetIndex];
                                    if (prev != null)
                                    {
                                        previousTransform = prev;
                                        prevIndex         = aheadIndex;
                                    }
                                }
                            }

                            int max = nextIndex - prevIndex;
                            for (float progress = 0; progress < max; progress++)
                            {
                                int trsIndex = prevIndex + (int)progress + 1;
                                transforms[trsIndex][targetIndex] = previousTransform.lerp(nextTransform, progress / max);
                            }
                        }

                        keyframe.Keys.Add(new Animation.Key {
                            Node      = target,
                            Transform = transform
                        });
                        keyframe.Time = frameIndex / fps;
                    }
                    animation.Keyframes.Add(keyframe);
                    SKAnimatorToolsProxy.IncrementProgress();
                }

                foreach (Model3D model in attachToModels)
                {
                    model.Animations.Add(animation);
                }
            }
            else if (animationImplementation is AnimationConfig.Sequential sequential)
            {
                //AnimationConfig.Implementation[] subs = new AnimationConfig.Implementation[sequential.animations.Length];

                SKAnimatorToolsProxy.IncrementEnd(sequential.animations.Length);
                for (int index = 0; index < sequential.animations.Length; index++)
                {
                    AnimationConfig.ComponentAnimation component = sequential.animations[index];
                    HandleAnimationImplementation(srcConfig, name, original, Dereference(component), attachToModels);
                    SKAnimatorToolsProxy.IncrementProgress();
                }

                /*
                 * } else if (animationImplementation is AnimationConfig.Procedural proc) {
                 *
                 *      if (srcFile.Name.StartsWith("rotate_")) {
                 *              // Rotation animation
                 *              // The first transform's first target is the node it attaches to.
                 *
                 *              string axis = srcFile.Name.Substring(7, 1);
                 *              if (axis == "x") {
                 *                      object speed = srcConfig.getArguments().get("Speed");
                 *                      string onNode = (string)srcConfig.getArguments().get("Node");
                 *                      // Big thing: Speed is probably a JAVA float, not a C# float
                 *                      if (speed is java.lang.Float jfloat) speed = jfloat.floatValue();
                 *
                 *                      Animation anim = HardcodedAnimations.CreateRotateX(onNode, (float)(speed ?? 1f));
                 *
                 *                      IEnumerable<Model3D> targetModels = attachToModels.Where(model => model.RawName == onNode);
                 *                      foreach (Model3D model in targetModels) {
                 *                              model.Animations.Add(anim);
                 *                      }
                 *
                 *              } else if (axis == "y") {
                 *                      object speed = srcConfig.getArguments().get("Speed");
                 *                      string onNode = (string)srcConfig.getArguments().get("Node");
                 *                      if (speed is java.lang.Float jfloat) speed = jfloat.floatValue();
                 *
                 *                      Animation anim = HardcodedAnimations.CreateRotateY(onNode,(float)(speed ?? 1f));
                 *                      IEnumerable<Model3D> targetModels = attachToModels.Where(model => model.RawName == onNode);
                 *                      foreach (Model3D model in targetModels) {
                 *                              model.Animations.Add(anim);
                 *                      }
                 *
                 *              } else if (axis == "z") {
                 *                      object speed = srcConfig.getArguments().get("Speed");
                 *                      string onNode = (string)srcConfig.getArguments().get("Node");
                 *                      if (speed is java.lang.Float jfloat) speed = jfloat.floatValue();
                 *
                 *                      Animation anim = HardcodedAnimations.CreateRotateZ(onNode, (float)(speed ?? 1f));
                 *                      IEnumerable<Model3D> targetModels = attachToModels.Where(model => model.RawName == onNode);
                 *                      foreach (Model3D model in targetModels) {
                 *                              model.Animations.Add(anim);
                 *                      }
                 *
                 *              } else {
                 *                      XanLogger.WriteLine(string.Format(ERR_PROC_NOT_YET_SUPPORTED, srcFile.Name), color: Color.DarkGoldenrod);
                 *              }
                 *      } else if (srcFile.Name == "gear_rotation.dat") {
                 *              // same as rotate y but it has an extra value
                 *              object speed = srcConfig.getArguments().get("Speed");
                 *              object sizeRatio = srcConfig.getArguments().get("Size Ratio");
                 *              string onNode = (string)srcConfig.getArguments().get("Node");
                 *              if (speed is java.lang.Float jfloat) speed = jfloat.floatValue();
                 *              if (sizeRatio is java.lang.Float jfloat2) sizeRatio = jfloat2.floatValue();
                 *
                 *              float overallSpeed = (float)(speed ?? 1f) * (float)(sizeRatio ?? 1f);
                 *
                 *              Animation anim = HardcodedAnimations.CreateRotateY(onNode, overallSpeed);
                 *              IEnumerable<Model3D> targetModels = attachToModels.Where(model => model.RawName == onNode);
                 *              foreach (Model3D model in targetModels) {
                 *                      model.Animations.Add(anim);
                 *              }
                 *
                 *      } else {
                 *              XanLogger.WriteLine(string.Format(ERR_PROC_NOT_YET_SUPPORTED, srcFile.Name), color: Color.DarkGoldenrod);
                 *      }
                 */

                /*
                 *      // Something's wrong with directs that I gotta fix.
                 *
                 * } else if (animationImplementation is AnimationConfig.Procedural proc) {
                 *      AnimationConfig.TargetTransform[] targets = proc.transforms;
                 *
                 *
                 *      Animation animation = new Animation(name);
                 *      float timeIncrement = proc.duration / targets.Length;
                 *      int currentFrame = 0;
                 *      foreach (AnimationConfig.TargetTransform targetTrs in targets) {
                 *              Transform3DExpression expr = targetTrs.expression;
                 *              Transform3D transform = (Transform3D)expr.createEvaluator(null).evaluate();
                 *
                 *              Animation.Keyframe keyframe = new Animation.Keyframe();
                 *              for (int targetIndex = 0; targetIndex < targetTrs.targets.Length; targetIndex++) {
                 *
                 *                      string target = targetTrs.targets[targetIndex];
                 *                      XanLogger.WriteLine(target, color: Color.Blue);
                 *                      keyframe.Keys.Add(new Animation.Key {
                 *                              Node = target,
                 *                              Transform = transform
                 *                      });
                 *                      keyframe.Time = currentFrame * timeIncrement;
                 *              }
                 *              animation.Keyframes.Add(keyframe);
                 *              currentFrame++;
                 *      }
                 *
                 *      foreach (Model3D model in attachToModels) {
                 *              model.Animations.Add(animation);
                 *      }
                 */
            }
            else
            {
                XanLogger.WriteLine(string.Format(ERR_IMPL_NOT_SUPPORTED, animationImplementation.GetType().Name), color: Color.DarkGoldenrod);
            }

            SKAnimatorToolsProxy.IncrementProgress();
        }