/// <summary> /// Get the configuration data for the given name, which should be identical to the config dat file name without its extension (e.g. "tile").<para/> /// Throws an <see cref="ArgumentException"/> if the given name isn't valid, and an <see cref="ArgumentNullException"/> if the given name is <see langword="null"/>.<para/> /// In many cases this will be an <see cref="Array"/> type, but this is not guaranteed. /// </summary> /// <param name="cfgName">The name of the configuration, identical to its dat name without the extension (e.g. "tile")</param> /// <returns></returns> /// <exception cref="ArgumentException">If the name isn't correct.</exception> /// <exception cref="ArgumentNullException">If the name is null.</exception> public ManagedConfig[] this[string cfgName] { get { if (cfgName == null) { throw new ArgumentNullException("cfgName"); } cfgName = cfgName.ToLower(); if (!ValidNames.Contains(cfgName)) { throw new ArgumentException("The given index is not valid! (" + cfgName + " is not a valid OOO Configuration name)"); } if (References.ContainsKey(cfgName)) { return(References[cfgName]); } XanLogger.WriteLine("One sec! I'm loading up the config data for [" + cfgName + "]..."); XanLogger.UpdateLog(); // UpdateLog call is here since auto-logging might be off here, and this is a status update message // people get antsy when things take time, so it's good to tell them. ManagedConfig[] reference = ConfigReferenceBootstrapper.GetConfigReferenceFromName(cfgName); // ^ calls the setter down here. return(reference); } }
#pragma warning disable CS0419 // Ambiguous reference in cref attribute /// <summary> /// A helper method for the two <see cref="Dereference"/> methods that takes in a Clyde object which is expected to be an animation of any type and resolves its refs. /// </summary> /// <param name="animationRef">A <see cref="ConfigReference"/> pointing to an animation.</param> /// <returns></returns> private static AnimationConfig.Implementation FinalDereference(ConfigReference animationRef) { object animationObj = animationRef.ResolveFile(); if (animationObj is AnimationConfig animation) { AnimationConfig.Implementation impl = animation.implementation; if (impl is AnimationConfig.Derived newDerived) { return(Dereference(newDerived)); } else { return(impl); } } else if (animationObj is AnimationConfig.ComponentAnimation newComponent) { return(Dereference(newComponent)); } else { XanLogger.WriteLine(string.Format(ERR_IMPL_NOT_SUPPORTED, animationObj.GetType().Name), color: Color.DarkGoldenrod); return(null); } }
/// <summary> /// Takes in a derived animation implementation and resolves its references until it gets to a non-derived type.<para/> /// This will also handle if the reference is a <see cref="AnimationConfig.Derived"/>. /// </summary> /// <param name="component">The component animation instance to get a real animation from.</param> /// <returns></returns> private static AnimationConfig.Implementation Dereference(AnimationConfig.ComponentAnimation component) { if (!component.animation.IsFileReference()) { XanLogger.WriteLine("Cannot access animations from configs yet. This requires a direct file reference.", XanLogger.DEBUG, System.Drawing.Color.Orange); return(null); } return(FinalDereference(component.animation)); }
/// <summary> /// Given a <see cref="Type"/> representing a specific config (e.g. <see cref="MaterialConfig"/>), the container for this config and all of its instances will be returned.<para/> /// In many cases this will be an <see cref="Array"/> type, but this is not guaranteed. /// </summary> /// <param name="cfgType"></param> /// <returns></returns> public ManagedConfig[] this[Type cfgType] { get { if (ReferencesByType.ContainsKey(cfgType)) { return(ReferencesByType[cfgType]); } XanLogger.WriteLine("One sec! I'm loading up the config data for [" + cfgType.Name + "]..."); XanLogger.UpdateLog(); // UpdateLog call is here since auto-logging might be off here, and this is a status update message // people get antsy when things take time, so it's good to tell them. ManagedConfig[] reference = ConfigReferenceBootstrapper.GetConfigReferenceFromType(cfgType); // ^ calls the setter on the name variant (which sets the type too) return(reference); } }
public void SetupCosmeticInformation(ViewerAffecterConfig model, DataTreeObject dataTreeParent) { if (dataTreeParent == null) { return; } ViewerEffectConfig effect = model.effect; string cls = JavaClassNameStripper.GetWholeClassName(effect.getClass()); if (cls == null) { XanLogger.WriteLine("WARNING: Attempt to get class of ViewerEffectConfig failed!"); return; } DataTreeObjectProperty implementationPropKey = dataTreeParent.FindSimpleProperty("Implementation"); DataTreeObject implementationProp = dataTreeParent.Properties[implementationPropKey].First(); implementationProp.Text = cls.Replace("$", "::"); if (effect is Skybox skybox) { dataTreeParent.ImageKey = SilkImage.Sky; string name = skybox.model?.getName(); if (name == null && dataTreeParent.Parent != null && dataTreeParent.Parent.ImageKey == SilkImage.Schemed) { dataTreeParent.ImageKey = SilkImage.Scheme; dataTreeParent.AddSimpleProperty("Data Type", "Render Scheme", SilkImage.Scheme); } else { // Name may still be null here. SilkImage target = name == null ? SilkImage.Missing : SilkImage.ModelSet; dataTreeParent.AddSimpleProperty("Model Reference", name, SilkImage.Reference, target, false); } Transform3D newTrs = new Transform3D(skybox.translationOrigin, Quaternion.IDENTITY, skybox.translationScale.x); dataTreeParent.AddSimpleProperty("Transform", newTrs.toString(), SilkImage.Matrix); } }
/// <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) { 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(); } } }
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(); }
/// <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(); }
/// <summary> /// Given a <see cref="ParameterizedConfig"/> and a path from a direct as well as a target value, this will modify the <see cref="ParameterizedConfig"/> so that its fields reflect the given value.<para/> /// If this data cannot be set due to it being on a direct chain (the end value is a <see cref="ConfigReference"/>), that <see cref="ConfigReference"/> will be returned /// </summary> /// <param name="config"></param> /// <param name="path"></param> /// <param name="argValue"></param> /// <param name="setToNull">If true, and if argValue is null, the property will actually be set to null (if this is false, it will skip applying it)</param> public static ConfigReference SetDataOn(ParameterizedConfig config, string path, object argValue, bool setToNull = false) { if (argValue == null && !setToNull) { return(null); } // implementation.material_mappings[0].material["Texture"]["File"] // A bit of a hack to make splitting this path easier: path = path.Replace("[", ".["); // The latest object stored when traversing this direct's path. object latestObject = config; string previousIndex = null; object previousObject = null; // Split it by the segments of this path, and get rid of the implementation word at the start if needed. string[] pathSegments = path.Split('.'); if (pathSegments[0] == "implementation") { latestObject = ReflectionHelper.Get(latestObject, "implementation"); pathSegments = pathSegments.Skip(1).ToArray(); } for (int idx = 0; idx < pathSegments.Length; idx++) { string currentIndex = pathSegments[idx].SnakeToCamel(); string betweenBrackets = currentIndex.BetweenBrackets(); if (betweenBrackets != null) { // This is either an array index, or a reference to a config. // The simple way to test this is that if it's a numeric index, it's an array index. if (int.TryParse(betweenBrackets, out int arrayIndex)) { // Access this array index. It is a number in brackets like [0] previousObject = latestObject; latestObject = ReflectionHelper.GetArray(latestObject, arrayIndex); } else { // Access the config reference. This is branching from a config reference and accesses a parameter ["Parameter Name"] ConfigReference latestAsCfg = (ConfigReference)latestObject; string parameterName = betweenBrackets.Substring(1, betweenBrackets.Length - 2); // First things first: Resolve the config reference. string configRefPath = latestAsCfg.getName(); // cloning this is super important as the tryget method will return a template object. // Do not edit the template! if (!latestAsCfg.IsRealReference()) { // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference. latestAsCfg.getArguments().put(parameterName, argValue); return(null); } ParameterizedConfig referencedConfig = ConfigReferenceBootstrapper.ConfigReferences.TryGetReferenceFromName(configRefPath)?.CloneAs <ParameterizedConfig>(); if (referencedConfig == null) { XanLogger.WriteLine("Something failed to reference a ConfigReference (It tried to search for \"" + configRefPath + "\", which doesn't exist). Some information on this model may not load properly!", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return(null); } // So there's our reference. Now we need to get a parameter from it. Parameter referencedParam = referencedConfig.getParameter(parameterName); if (referencedParam is Parameter.Direct referencedDirect) { ConfigReference[] chainRefs = SetDataOn(referencedConfig, referencedDirect.paths, argValue); if (chainRefs != null) { // We're pointing to other ConfigReferences which means that this is a direct chain. Oh brother. foreach (ConfigReference reference in chainRefs) { if (reference != null) { if (File.Exists(ResourceDirectoryGrabber.ResourceDirectoryPath + configRefPath)) { // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference. latestAsCfg.getArguments().put(parameterName, argValue); } else { ParameterizedConfig forwardRefConfig = reference.Resolve <ParameterizedConfig>(); // Using as because it might be null. if (forwardRefConfig != null) { foreach (Parameter subRefParam in forwardRefConfig.parameters) { if (subRefParam is Parameter.Direct subRefDirect) { WrappedDirect wrappedSubRefDir = new WrappedDirect(forwardRefConfig, subRefDirect); wrappedSubRefDir.SetValue(argValue); } } latestAsCfg.getArguments().put(parameterName, ConfigReferenceConstructor.MakeConfigReferenceTo(forwardRefConfig)); } else { XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed because the target object was not a ParameterizedConfig! Some information may be incorrect on this model.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return(null); } } } } } else { // This is by far one of the most hacky methods I've ever done in OOO stuff. // So basically, a model has a property for something like say, materials. // This property is a ConfigReference to a material object, and that ConfigReference has arguments in it // that tell the referenced material what it should be. // Rather than trying to traverse that ConfigReference and set the data on the remote object (PAINFUL), I // instead decided to write a system that can wrap any ParameterizedConfig into a ConfigReference and just // call it a day. ReflectionHelper.Set(previousObject, previousIndex, ConfigReferenceConstructor.MakeConfigReferenceTo(referencedConfig)); } } else { //throw new NotImplementedException("Cannot set data on referenced parameters that are not Directs (yet)."); XanLogger.WriteLine("Feature Not Implemented: Cannot set data on referenced parameters that aren't directs (e.g. parameters that are choices)", XanLogger.STANDARD, System.Drawing.Color.Orange); return(null); } return(null); } } else { // This is referencing a property. // But wait: If this is the second to last object, then we gotta modify it. if (idx == pathSegments.Length - 1) { // Second to last object. latestObject will contain the property that we want to set. // Let's manually find that field and set it if (currentIndex.BetweenBrackets() == null) { // We're good here. if (argValue is ConfigReference argValueCfg) { // There's some cases when a variant wants to set a config reference. // In these cases, we need to make sure the property is also a config reference so we know it's safe to set. // ... But before that, catch case: Not actually a config. if (argValueCfg.IsRealReference()) { object ptr = ReflectionHelper.Get(previousObject, previousIndex); if (ptr is ConfigReference) { ReflectionHelper.Set(previousObject, previousIndex, argValueCfg); } else { if (ReflectionHelper.GetTypeOfField(latestObject, currentIndex) == argValueCfg.GetType()) { ReflectionHelper.Set(latestObject, currentIndex, argValueCfg); return(null); } else { XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return(null); } } } else { if (ReflectionHelper.GetTypeOfField(latestObject, currentIndex) == argValueCfg.GetType()) { ReflectionHelper.Set(latestObject, currentIndex, argValueCfg); return(null); } else { XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return(null); } } } else { // Contrary to the oddball case above, if the result value at the end of this direct is a ConfigReference // then we need to return it so that whatever called this knows that it has more to traverse. // Ideally, this is only returned in the nested call above. string targetIndex = previousIndex; /*if (int.TryParse(previousIndex.BetweenBrackets(), out int _)) { * // The previous index was an array accessor. This means we want to actually reference the CURRENT index * // on the PREVIOUS object. A bit odd but it's intentional. * // This is done because the previous object will be the result of that indexer, which is identical * // to the current object. As such, we need to use the current index to reference it. * targetIndex = currentIndex; * }*/ if (previousObject == null || targetIndex == null) { return(null); } object ptr = ReflectionHelper.Get(previousObject, targetIndex); if (ptr is ConfigReference cfgRef) { // Special handling. argValue goes to a property on the config reference return(cfgRef); } if (ptr.GetType() == argValue.GetType()) { ReflectionHelper.Set(previousObject, targetIndex, argValue); } else { // In some cases, the object it's pointing to isn't the same time. // In cases where the previous index is used, this *might* mean we need to step forward like so: if (ReflectionHelper.GetTypeOfField(ptr, currentIndex) == argValue.GetType()) { ReflectionHelper.Set(ptr, currentIndex, argValue); } else { // But in other cases, it's not pointing to a prop up ahead. if (ReflectionHelper.Get(ptr, currentIndex) is ConfigReference cfgRefLow) { return(cfgRefLow); } else { XanLogger.WriteLine("ALERT: Model attempted to set value of Direct [" + currentIndex + "] but it failed due to a type mismatch! Certain data on this model might be incorrect.", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return(null); } } } return(null); } } } if (previousIndex != null) { if (int.TryParse(previousIndex.BetweenBrackets(), out int _) && idx == pathSegments.Length - 1) { if (currentIndex.BetweenBrackets() == null) { // We're good here. ReflectionHelper.Set(previousObject, previousIndex, argValue); return(null); } } } previousObject = latestObject; latestObject = ReflectionHelper.Get(latestObject, currentIndex); if (previousObject == null || latestObject == null) { return(null); // Failed to traverse. } } previousIndex = currentIndex; } return(null); }
/// <summary> /// Given a full path from a <see cref="Parameter.Direct"/>, this will traverse it and acquire the data at the end.<para/> /// This will stop if it runs into another direct and instantiate a new <see cref="WrappedDirect"/>. This will occur if there is a reference chain, for instance, in many textures it references material["Texture"] (a direct) followed by a second direct ["File"]. Since each may have multiple paths, it's best to reference a new <see cref="WrappedDirect"/>. /// </summary> /// <param name="path"></param> private void TraverseDirectPath(string path) { // implementation.material_mappings[0].material["Texture"]["File"] // A bit of a hack to make splitting this path easier: path = path.Replace("[", ".["); // The latest object stored when traversing this direct's path. object latestObject = Config; // Split it by the segments of this path, and get rid of the implementation word at the start if needed. string[] pathSegments = path.Split('.'); if (pathSegments[0] == "implementation") { latestObject = ReflectionHelper.Get(latestObject, "implementation"); pathSegments = pathSegments.Skip(1).ToArray(); } for (int idx = 0; idx < pathSegments.Length; idx++) { string currentIndex = pathSegments[idx].SnakeToCamel(); string betweenBrackets = currentIndex.BetweenBrackets(); if (betweenBrackets != null) { // This is either an array index, or a reference to a config. // The simple way to test this is that if it's a numeric index, it's an array index. if (int.TryParse(betweenBrackets, out int arrayIndex)) { // Access this array index. It is a number in brackets like [0] latestObject = ReflectionHelper.GetArray(latestObject, arrayIndex); } else { // Access the config reference. This is branching from a config reference and accesses a parameter ["Parameter Name"] ConfigReference latestAsCfg = (ConfigReference)latestObject; string parameterName = betweenBrackets.Substring(1, betweenBrackets.Length - 2); // First things first: Resolve the config reference (AND CLONE IT. Don't edit the template object!) string configRefPath = latestAsCfg.getName(); if (!latestAsCfg.IsRealReference()) { // Catch case: This isn't actually pointing to a *configuration*, rather a direct object reference. _EndReferences.Add(new DirectEndReference(configRefPath)); return; } ParameterizedConfig referencedConfig = ConfigReferenceBootstrapper.ConfigReferences.TryGetReferenceFromName(configRefPath)?.CloneAs <ParameterizedConfig>(); if (referencedConfig == null) { XanLogger.WriteLine("Something failed to reference a ConfigReference (It tried to search for \"" + configRefPath + "\", which doesn't exist). Some information on this model may not load properly!", XanLogger.DEBUG, System.Drawing.Color.DarkGoldenrod); return; } ArgumentMap args = latestAsCfg.getArguments(); // So there's our reference. Now we need to get a parameter from it. ConfigReferenceContainerName = ConfigReferenceBootstrapper.ConfigReferences.GetCategoryFromEntryName(configRefPath); Parameter referencedParam = referencedConfig.getParameter(parameterName); if (referencedParam is Parameter.Direct referencedDirect) { _EndReferences.Add(new DirectEndReference(new WrappedDirect(referencedConfig, referencedDirect, null, args))); } else if (referencedParam is Parameter.Choice referencedChoice) { _EndReferences.Add(new DirectEndReference(new WrappedChoice(referencedConfig, referencedChoice, args))); } return; } } else { // This is referencing a property. latestObject = ReflectionHelper.Get(latestObject, currentIndex); } } // Now here's something important: Does an argument override this? if (Arguments != null && Arguments.containsKey(Name) && latestObject != null) { // This direct is included as an argument... // And if we're down here, then we're not referencing another direct, we're referencing a property. // But as a final sanity check: if (latestObject.GetType() == Arguments.get(Name)?.GetType()) { // Yep! Our argument is the same type as the latestObject. // Let's set latestObject to that argument. latestObject = Arguments.get(Name); } } _EndReferences.Add(new DirectEndReference(latestObject)); }
/// <summary> /// Translates the given <see cref="GeometryConfig"/> into a <see cref="Model3D"/>. /// </summary> /// <param name="geometry">The <see cref="GeometryConfig"/> storing the applicable data.</param> /// <param name="geometryIndex">A unique identifier generated by the caller of this method used to identify this geometry. If this ID is seen multiple times, the geometry won't be processed and instead the returned model will reference the existing data.</param> /// <param name="rootNode">If called by the <see cref="ArticulatedConfigHandler"/>, this is the root node of the associated <see cref="ArticulatedConfig"/>.</param> /// <returns></returns> public static Model3D GetGeometryInformation(GeometryConfig geometry, string geometryIndex, Node rootNode = null) { // stored // indexed stored // skinned indexed stored Model3D model = new Model3D(); MeshData existingMeshData = MeshData.MeshDataBindings.GetOrDefault(geometryIndex); // model.Mesh = existingMeshData; if (existingMeshData == null) { // Go from the most complex to the least complex, since (presumably) simpler classes can nest. // Let's pull the basic arrays from the geometry data. MeshData meshData = new MeshData(geometryIndex); Debug.WriteLine("Created a new MeshData. Index: " + geometryIndex); float[] vertices; float[] uvs; float[] normals; ushort[] indices; if (geometry is SkinnedIndexedStored skinnedIndexedStored) { vertices = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.vertexArray); uvs = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.texCoordArrays); normals = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.normalArray); indices = GetFromShortBuffer(skinnedIndexedStored.indices); // Also need to handle skinning. if (skinnedIndexedStored.mode != Mode.TRIANGLES) { XanLogger.WriteLine("WARNING: This Articulated model may not export properly! Its mode isn't TRIANGLES, and other behaviors (e.g. TRIANGLESTRIP) haven't been coded in yet! The method used for TRIANGLES will be applied anyway just to try something.", color: Color.DarkGoldenrod); } AttributeArrayConfig[] allArrays = skinnedIndexedStored.vertexAttribArrays; AttributeArrayConfig boneIndicesAttr = GetArrayByName(allArrays, "boneIndices"); AttributeArrayConfig boneWeightsAttr = GetArrayByName(allArrays, "boneWeights"); ushort[] boneIndicesS = skinnedIndexedStored.getFloatArray(false, boneIndicesAttr).ToUshortArray(); float[] boneWeightsF = skinnedIndexedStored.getFloatArray(false, boneWeightsAttr); // Now let's consider this literally: indices and weights for bones are vertex *attribute* arrays. // So presumably this means that we iterate through the indices. // The vertex at vertices[index] is part of bone boneIndices[index]. The index returned by boneIndices is the index of a name. // A vertex can be in up to four groups at once, hence why these are in groups of quads. // If the bone group is 0, it should be ignored. // Apparently, this concept went way over my head in SK Animator Tools and it was a disaster. Part of why the code was so horrifying. // Now for ease in indexing, I'm going to bump all of the elements in the bone name array forward by 1, then set index 0 to null. string[] boneNames = new string[skinnedIndexedStored.bones.Length + 1]; boneNames[0] = null; for (int idx = 0; idx < boneNames.Length - 1; idx++) { boneNames[idx + 1] = skinnedIndexedStored.bones[idx]; } ushort[,] boneIndices = boneIndicesS.As2D(4); float[,] boneWeights = boneWeightsF.As2D(4); meshData.BoneIndicesNative = boneIndicesS; meshData.BoneWeightsNative = boneWeightsF; meshData.BoneNames = boneNames; meshData.BoneIndices = boneIndices; meshData.BoneWeights = boneWeights; meshData.HasBoneData = true; if (rootNode == null) { // The model supplied no root node. This can happen for implementations (e.g. StaticConfig) that has a GeometryConfig // that is skinned. // PRESUMABLY this means that it uses an external reference for its root node (e.g. the knight model has a common root) // But in my case, I really can't read that right now. meshData.UsesExternalRoot = true; } else { meshData.SetBones(rootNode); } } else if (geometry is IndexedStored indexedStored) { vertices = indexedStored.getFloatArray(false, indexedStored.vertexArray); uvs = indexedStored.getFloatArray(false, indexedStored.texCoordArrays); normals = indexedStored.getFloatArray(false, indexedStored.normalArray); indices = GetFromShortBuffer(indexedStored.indices); meshData.HasBoneData = false; } else if (geometry is Stored stored) { vertices = stored.getFloatArray(false, stored.vertexArray); uvs = stored.getFloatArray(false, stored.texCoordArrays); normals = stored.getFloatArray(false, stored.normalArray); indices = new ushort[vertices.Length]; for (ushort i = 0; i < indices.Length; i++) { indices[i] = i; } meshData.HasBoneData = false; } else { throw new InvalidOperationException("The GeometryConfig type is unknown! Type: " + geometry.getClass().getName()); } meshData.Vertices.SetFrom(Vector3.FromFloatArray(vertices)); meshData.UVs.SetFrom(Vector2.FromFloatArray(uvs)); meshData.Normals.SetFrom(Vector3.FromFloatArray(normals)); meshData.Indices.SetFrom(indices); meshData.ConstructGroups(); existingMeshData = meshData; } model.Mesh = existingMeshData; return(model); }
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(); }
// 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(); }