diff --git a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/scene/SceneModelRenderer.cs b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/scene/SceneModelRenderer.cs index 04958205d..186562004 100644 --- a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/scene/SceneModelRenderer.cs +++ b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/scene/SceneModelRenderer.cs @@ -1,6 +1,7 @@ using System.Numerics; using fin.config; +using fin.data.dictionaries; using fin.math.matrix.four; using fin.model; using fin.scene; @@ -11,7 +12,7 @@ namespace fin.ui.rendering.gl.scene { public class SceneModelRenderer : IRenderable, IDisposable { private readonly ISceneModel sceneModel_; private readonly IModelRenderer modelRenderer_; - private readonly IReadOnlyList children_; + private readonly ListDictionary children_ = []; public SceneModelRenderer(ISceneModel sceneModel, ILighting? lighting) { this.sceneModel_ = sceneModel; @@ -31,10 +32,11 @@ public SceneModelRenderer(ISceneModel sceneModel, ILighting? lighting) { Scale = this.sceneModel_.ViewerScale }; - this.children_ - = sceneModel.Children - .Select(child => new SceneModelRenderer(child, lighting)) - .ToArray(); + foreach (var (bone, boneChildren) in sceneModel.Children) { + foreach (var child in boneChildren) { + this.children_.Add(bone, new SceneModelRenderer(child, lighting)); + } + } } ~SceneModelRenderer() => this.ReleaseUnmanagedResources_(); @@ -46,7 +48,7 @@ public void Dispose() { private void ReleaseUnmanagedResources_() { this.modelRenderer_.Dispose(); - foreach (var child in this.children_) { + foreach (var child in this.children_.SelectMany(pair => pair.Value)) { child.Dispose(); } } @@ -107,8 +109,17 @@ public void Render() { this.SkeletonRenderer.Render(); } - foreach (var child in this.children_) { - child.Render(); + foreach (var (bone, boneChildren) in this.children_) { + GlTransform.PushMatrix(); + + GlTransform.MultMatrix( + this.sceneModel_.BoneTransformManager.GetWorldMatrix(bone).Impl); + + foreach (var child in boneChildren) { + child.Render(); + } + + GlTransform.PopMatrix(); } GlTransform.PopMatrix(); diff --git a/FinModelUtility/Fin/Fin/src/data/dictionaries/CaseInvariantStringDictionary.cs b/FinModelUtility/Fin/Fin/src/data/dictionaries/CaseInvariantStringDictionary.cs index 585ccf68e..cf0c0789b 100644 --- a/FinModelUtility/Fin/Fin/src/data/dictionaries/CaseInvariantStringDictionary.cs +++ b/FinModelUtility/Fin/Fin/src/data/dictionaries/CaseInvariantStringDictionary.cs @@ -17,7 +17,7 @@ public class CaseInvariantStringDictionary : IFinDictionary { public bool TryGetValue(string key, out T value) => this.impl_.TryGetValue(key, out value); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Remove(T key) => this.impl_.Remove(key); + public bool Remove(string key) => this.impl_.Remove(key); public T this[string key] { get => this.impl_[key]; diff --git a/FinModelUtility/Fin/Fin/src/data/dictionaries/ListDictionary.cs b/FinModelUtility/Fin/Fin/src/data/dictionaries/ListDictionary.cs index 15656d3f7..815608054 100644 --- a/FinModelUtility/Fin/Fin/src/data/dictionaries/ListDictionary.cs +++ b/FinModelUtility/Fin/Fin/src/data/dictionaries/ListDictionary.cs @@ -3,12 +3,29 @@ using System.Linq; namespace fin.data.dictionaries { + public interface IReadOnlyListDictionary + : IReadOnlyFinCollection<(TKey Key, IList Value)> { + bool HasList(TKey key); + + IList this[TKey key] { get; } + bool TryGetList(TKey key, out IList? list); + } + + public interface IListDictionary + : IReadOnlyListDictionary, + IFinCollection<(TKey Key, IList Value)> { + void ClearList(TKey key); + void Add(TKey key, TValue value); + } + + /// /// An implementation for a dictionary of lists. Each value added for a key /// will be stored in that key's corresponding list. /// - public class ListDictionary(IFinDictionary> impl) - : IFinCollection<(TKey Key, IList Value)> { + public class ListDictionary( + IFinDictionary> impl) + : IListDictionary { public ListDictionary() : this( new NullFriendlyDictionary>()) { } diff --git a/FinModelUtility/Fin/Fin/src/data/dictionaries/SetDictionary.cs b/FinModelUtility/Fin/Fin/src/data/dictionaries/SetDictionary.cs index e484121b8..7da8126a3 100644 --- a/FinModelUtility/Fin/Fin/src/data/dictionaries/SetDictionary.cs +++ b/FinModelUtility/Fin/Fin/src/data/dictionaries/SetDictionary.cs @@ -3,13 +3,25 @@ using System.Linq; namespace fin.data.dictionaries { + public interface IReadOnlySetDictionary + : IReadOnlyFinCollection<(TKey Key, ISet Value)> { + ISet this[TKey key] { get; } + bool TryGetSet(TKey key, out ISet? list); + } + + public interface ISetDictionary + : IReadOnlySetDictionary, + IFinCollection<(TKey Key, ISet Value)> { + void Add(TKey key, TValue value); + } + /// /// An implementation for a dictionary of sets. Each value added for a key /// will be stored in that key's corresponding set. /// public class SetDictionary( IFinDictionary> impl) - : IFinCollection<(TKey Key, ISet Value)> { + : ISetDictionary { public SetDictionary() : this( new NullFriendlyDictionary>()) { } diff --git a/FinModelUtility/Fin/Fin/src/data/lazy/LazyArray.cs b/FinModelUtility/Fin/Fin/src/data/lazy/LazyArray.cs index 2282fb42b..a320e70a2 100644 --- a/FinModelUtility/Fin/Fin/src/data/lazy/LazyArray.cs +++ b/FinModelUtility/Fin/Fin/src/data/lazy/LazyArray.cs @@ -12,17 +12,13 @@ public class LazyArray : ILazyArray { private readonly bool[] populated_; private readonly Func handler_; - public LazyArray( - int count, - Func handler) { + public LazyArray(int count, Func handler) { this.impl_ = new T[count]; this.populated_ = new bool[count]; this.handler_ = handler; } - public LazyArray( - int count, - Func, int, T> handler) { + public LazyArray(int count, Func, int, T> handler) { this.impl_ = new T[count]; this.populated_ = new bool[count]; this.handler_ = (int key) => handler(this, key); @@ -49,6 +45,19 @@ public bool TryGetValue(int key, out T value) { return false; } + public bool Remove(int key) => this.Remove(key, out _); + + public bool Remove(int key, out T value) { + if (this.ContainsKey(key)) { + value = this.impl_[key]; + this.populated_[key] = false; + return true; + } + + value = default; + return false; + } + public T this[int key] { get { if (this.ContainsKey(key)) { diff --git a/FinModelUtility/Fin/Fin/src/data/lazy/LazyCaseInvariantStringDictionary.cs b/FinModelUtility/Fin/Fin/src/data/lazy/LazyCaseInvariantStringDictionary.cs index 0c4ff484b..dc18ea51b 100644 --- a/FinModelUtility/Fin/Fin/src/data/lazy/LazyCaseInvariantStringDictionary.cs +++ b/FinModelUtility/Fin/Fin/src/data/lazy/LazyCaseInvariantStringDictionary.cs @@ -33,6 +33,8 @@ public LazyCaseInvariantStringDictionary(Func, st public bool ContainsKey(string key) => this.impl_.ContainsKey(key); public bool TryGetValue(string key, out TValue value) => this.impl_.TryGetValue(key, out value); + public bool Remove(string key) => this.impl_.Remove(key); + public TValue this[string key] { get => this.impl_[key]; set => this.impl_[key] = value; diff --git a/FinModelUtility/Fin/Fin/src/data/lazy/LazyDictionary.cs b/FinModelUtility/Fin/Fin/src/data/lazy/LazyDictionary.cs index 6a1c8232c..fe3af2751 100644 --- a/FinModelUtility/Fin/Fin/src/data/lazy/LazyDictionary.cs +++ b/FinModelUtility/Fin/Fin/src/data/lazy/LazyDictionary.cs @@ -31,6 +31,8 @@ public LazyDictionary(Func, TKey, TValue> handler, public bool TryGetValue(TKey key, out TValue value) => this.impl_.TryGetValue(key, out value); + public bool Remove(TKey key) => this.impl_.Remove(key); + public TValue this[TKey key] { get => this.impl_.TryGetValue(key, out var value) ? value diff --git a/FinModelUtility/Fin/Fin/src/data/lazy/LazyList.cs b/FinModelUtility/Fin/Fin/src/data/lazy/LazyList.cs index eb5fda10c..bef8b3fd2 100644 --- a/FinModelUtility/Fin/Fin/src/data/lazy/LazyList.cs +++ b/FinModelUtility/Fin/Fin/src/data/lazy/LazyList.cs @@ -40,6 +40,19 @@ public bool TryGetValue(int key, out T value) { return false; } + public bool Remove(int key) => this.Remove(key, out _); + + public bool Remove(int key, out T value) { + if (this.ContainsKey(key)) { + value = this.impl_[key]; + this.populated_[key] = false; + return true; + } + + value = default; + return false; + } + public T this[int key] { get { if (this.Count > key && this.populated_[key]) { diff --git a/FinModelUtility/Fin/Fin/src/math/BoneTransformManager.cs b/FinModelUtility/Fin/Fin/src/math/BoneTransformManager.cs index 0672bd767..1f9a6fe4e 100644 --- a/FinModelUtility/Fin/Fin/src/math/BoneTransformManager.cs +++ b/FinModelUtility/Fin/Fin/src/math/BoneTransformManager.cs @@ -53,15 +53,9 @@ void ProjectVertexPositionNormalTangent( public class BoneTransformManager : IBoneTransformManager { // TODO: This is going to be slow, can we put this somewhere else for O(1) access? - private readonly IndexableDictionary - bonesToRootMatrices_ = new(); - private readonly IndexableDictionary bonesToWorldMatrices_ = new(); - private readonly IndexableDictionary - bonesToInverseRootMatrices_ = new(); - private readonly IndexableDictionary bonesToInverseWorldMatrices_ = new(); @@ -85,9 +79,7 @@ public BoneTransformManager((IBoneTransformManager, IBone) parent) { } public void Clear() { - this.bonesToRootMatrices_.Clear(); this.bonesToWorldMatrices_.Clear(); - this.bonesToInverseRootMatrices_.Clear(); this.bonesToInverseWorldMatrices_.Clear(); this.boneWeightsToWorldMatrices_.Clear(); this.verticesToWorldMatrices_.Clear(); @@ -137,20 +129,10 @@ public IDictionary CalculateMatrices( var bonesToIndex = new Dictionary(); var boneIndex = -1; - var boneQueue = new Queue<(IBone, Matrix4x4, Matrix4x4)>(); - boneQueue.Enqueue((rootBone, this.ManagerMatrix.Impl, this.ManagerMatrix.Impl)); + var boneQueue = new Queue<(IBone, Matrix4x4)>(); + boneQueue.Enqueue((rootBone, this.ManagerMatrix.Impl)); while (boneQueue.Count > 0) { - var (bone, parentBoneToRootMatrix, parentBoneToWorldMatrix) = boneQueue.Dequeue(); - - if (!this.bonesToRootMatrices_.TryGetValue(bone, out var boneToRootMatrix)) { - this.bonesToRootMatrices_[bone] = boneToRootMatrix = new FinMatrix4x4(); - } - - if (bone.Root == bone) { - boneToRootMatrix.SetIdentity(); - } else { - boneToRootMatrix.CopyFrom(parentBoneToRootMatrix!); - } + var (bone, parentBoneToWorldMatrix) = boneQueue.Dequeue(); if (!this.bonesToWorldMatrices_.TryGetValue(bone, out var boneToWorldMatrix)) { this.bonesToWorldMatrices_[bone] = boneToWorldMatrix = new FinMatrix4x4(); @@ -209,13 +191,8 @@ public IDictionary CalculateMatrices( var localMatrix = SystemMatrix4x4Util.FromTrs(localPosition, localRotation, localScale); - boneToRootMatrix.MultiplyInPlace(localMatrix); boneToWorldMatrix.MultiplyInPlace(localMatrix); } else { - boneToRootMatrix.ApplyTrsWithFancyBoneEffects(bone, - localPosition, - localRotation, - localScale); boneToWorldMatrix.ApplyTrsWithFancyBoneEffects(bone, localPosition, localRotation, @@ -223,14 +200,13 @@ public IDictionary CalculateMatrices( } if (isFirstPass) { - this.bonesToInverseRootMatrices_[bone] = boneToRootMatrix.CloneAndInvert(); this.bonesToInverseWorldMatrices_[bone] = boneToWorldMatrix.CloneAndInvert(); } bonesToIndex[bone] = boneIndex++; foreach (var child in bone.Children) { - boneQueue.Enqueue((child, boneToRootMatrix.Impl, boneToWorldMatrix.Impl)); + boneQueue.Enqueue((child, boneToWorldMatrix.Impl)); } } @@ -248,17 +224,9 @@ public IDictionary CalculateMatrices( var bone = boneWeight.Bone; var weight = boneWeight.Weight; - if (boneWeights.VertexSpace != VertexSpace.RELATIVE_TO_ROOT) { - var inverseMatrix = boneWeight.InverseBindMatrix ?? this.bonesToInverseWorldMatrices_[bone]; - boneWeightMatrix.AddInPlace( - (inverseMatrix.Impl * this.bonesToWorldMatrices_[bone].Impl) * weight); - } else { - boneWeightMatrix.AddInPlace( - ( - this.bonesToWorldMatrices_[bone.Root].Impl * - this.bonesToRootMatrices_[bone].Impl * - this.bonesToInverseRootMatrices_[bone].Impl) * weight); - } + var inverseMatrix = boneWeight.InverseBindMatrix ?? this.bonesToInverseWorldMatrices_[bone]; + boneWeightMatrix.AddInPlace( + (inverseMatrix.Impl * this.bonesToWorldMatrices_[bone].Impl) * weight); } } diff --git a/FinModelUtility/Fin/Fin/src/model/SkinInterfaces.cs b/FinModelUtility/Fin/Fin/src/model/SkinInterfaces.cs index 2ee5969b7..4684826a6 100644 --- a/FinModelUtility/Fin/Fin/src/model/SkinInterfaces.cs +++ b/FinModelUtility/Fin/Fin/src/model/SkinInterfaces.cs @@ -110,7 +110,6 @@ public readonly struct TexCoord { public enum VertexSpace { RELATIVE_TO_WORLD, - RELATIVE_TO_ROOT, RELATIVE_TO_BONE, } diff --git a/FinModelUtility/Fin/Fin/src/scene/SceneInterfaces.cs b/FinModelUtility/Fin/Fin/src/scene/SceneInterfaces.cs index fabda5d7d..52592e7b7 100644 --- a/FinModelUtility/Fin/Fin/src/scene/SceneInterfaces.cs +++ b/FinModelUtility/Fin/Fin/src/scene/SceneInterfaces.cs @@ -3,6 +3,7 @@ using System.Drawing; using fin.animation; +using fin.data.dictionaries; using fin.importers; using fin.math; using fin.model; @@ -86,7 +87,7 @@ ISceneObject SetRotationDegrees(float xDegrees, /// onto bones. /// public interface ISceneModel : IDisposable { - IReadOnlyList Children { get; } + IReadOnlyListDictionary Children { get; } ISceneModel AddModelOntoBone(IModel model, IBone bone); IModel Model { get; } diff --git a/FinModelUtility/Fin/Fin/src/scene/impl/SceneModelImpl.cs b/FinModelUtility/Fin/Fin/src/scene/impl/SceneModelImpl.cs index e45eb17d3..613e288c4 100644 --- a/FinModelUtility/Fin/Fin/src/scene/impl/SceneModelImpl.cs +++ b/FinModelUtility/Fin/Fin/src/scene/impl/SceneModelImpl.cs @@ -1,15 +1,15 @@ using System; -using System.Collections.Generic; using System.Linq; using fin.animation; +using fin.data.dictionaries; using fin.math; using fin.model; namespace fin.scene { public partial class SceneImpl { private class SceneModelImpl : ISceneModel { - private readonly List children_ = []; + private readonly ListDictionary children_ = []; private IModelAnimation? animation_; public SceneModelImpl(IModel model) { @@ -40,24 +40,24 @@ private void Init_() { this.AnimationPlaybackManager.IsPlaying = true; } - ~SceneModelImpl() => ReleaseUnmanagedResources_(); + ~SceneModelImpl() => this.ReleaseUnmanagedResources_(); public void Dispose() { - ReleaseUnmanagedResources_(); + this.ReleaseUnmanagedResources_(); GC.SuppressFinalize(this); } private void ReleaseUnmanagedResources_() { - foreach (var child in this.children_) { + foreach (var child in this.children_.SelectMany(pair => pair.Value)) { child.Dispose(); } } - public IReadOnlyList Children => this.children_; + public IReadOnlyListDictionary Children => this.children_; public ISceneModel AddModelOntoBone(IModel model, IBone bone) { var child = new SceneModelImpl(model, this, bone); - this.children_.Add(child); + this.children_.Add(bone, child); return child; } diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/Program.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/Program.cs index 8950281a4..0f2ea372f 100644 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/Program.cs +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/Program.cs @@ -106,7 +106,7 @@ public void Run(string scratchDirectoryPath, string outputDirectoryPath) { foreach (var visFile in visFiles) { var vis = HWVisResource.FromFile(context, visFile.FullPath); - var finModel = vis.Model; + var finModel = vis.Scene; var outFilePath = visFile.FullPath.Replace(scratchDirectoryPath, @@ -116,10 +116,10 @@ public void Run(string scratchDirectoryPath, string outputDirectoryPath) { parent.Create(); } - var exporter = new AssimpIndirectModelExporter(); + /*var exporter = new AssimpIndirectModelExporter(); exporter.ExportModel(new ModelExporterParams { OutputFile = outFile, Model = finModel - }); + });*/ Console.WriteLine($"Processed {visFile.FullPath}"); } diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWUgxResource.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWUgxResource.cs index c546c138f..54b28a4d0 100644 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWUgxResource.cs +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWUgxResource.cs @@ -15,6 +15,7 @@ using fin.math.rotations; using fin.model; using fin.model.impl; +using fin.scene; using fin.util.asserts; using granny3d; @@ -27,19 +28,13 @@ namespace HaloWarsTools { public class HWUgxResource : HWBinaryResource { - private IDictionary? boneMap_; public ModelImpl Mesh { get; private set; } - public HWVisResource.VisSubModelRef? VisSubModelRef { get; private set; } private bool FlipFaces_ { get; set; } public static HWUgxResource? FromFile( HWContext context, string filename, - (ModelImpl mesh, - HWVisResource.VisSubModelRef? subModelRef, - bool flipFaces, - IDictionary boneMap)? - meshAndBoneMap = null) { + bool flipFaces) { // Set the extension based on the resource type if the filename doesn't have one if (string.IsNullOrEmpty(Path.GetExtension(filename)) && TypeExtensions.TryGetValue(HWResourceType.Ugx, @@ -48,10 +43,8 @@ public class HWUgxResource : HWBinaryResource { } var resource = (HWUgxResource) CreateResource(context, filename); - resource.Mesh = meshAndBoneMap?.mesh ?? new ModelImpl(); - resource.VisSubModelRef = meshAndBoneMap?.subModelRef; - resource.FlipFaces_ = meshAndBoneMap?.flipFaces ?? true; - resource.boneMap_ = meshAndBoneMap?.boneMap; + resource.Mesh = new ModelImpl(); + resource.FlipFaces_ = flipFaces; resource?.Load(File.ReadAllBytes(resource.AbsolutePath)); return resource; @@ -248,23 +241,12 @@ private void ImportMesh(byte[] bytes, ModelImpl finModel) { Asserts.Equal(1, grannyFileInfo.SkeletonHeaderList.Count); var skeletonHeader = grannyFileInfo.SkeletonHeaderList[0]; - var fromBone = this.VisSubModelRef?.FromBone; - if (fromBone != null) { - Asserts.SequenceEqual(fromBone, skeletonHeader.Bones[0].Name); - } - - var rootBone = finModel.Skeleton.Root; - var toBone = this.VisSubModelRef?.ToBone; - if (toBone != null) { - rootBone = boneMap_[toBone]; - } - foreach (var grannyBone in skeletonHeader.Bones) { var parentIndex = grannyBone.ParentIndex; var isRoot = parentIndex == -1; var parentFinBone = isRoot - ? rootBone + ? finModel.Skeleton.Root : localFinBones[parentIndex].Item1; var position = grannyBone.LocalTransform.Position; @@ -292,7 +274,6 @@ private void ImportMesh(byte[] bytes, ModelImpl finModel) { finBone.Name = grannyBone.Name; - boneMap_[finBone.Name] = finBone; localFinBones.Add((finBone, grannyBone)); } @@ -580,11 +561,11 @@ private void ImportMesh(byte[] bytes, ModelImpl finModel) { finVertex.SetBoneWeights( finSkin.GetOrCreateBoneWeights( - VertexSpace.RELATIVE_TO_ROOT, finBoneWeights)); + VertexSpace.RELATIVE_TO_WORLD, finBoneWeights)); } else { finVertex.SetBoneWeights( finSkin.GetOrCreateBoneWeights( - VertexSpace.RELATIVE_TO_ROOT, localFinBones[0].Item1)); + VertexSpace.RELATIVE_TO_WORLD, localFinBones[0].Item1)); } finVertices.Add(finVertex); diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWVisResource.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWVisResource.cs index f3acb1d9d..6dc108b5a 100644 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWVisResource.cs +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/Resources/HWVisResource.cs @@ -1,17 +1,20 @@ #nullable enable +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; +using fin.data.dictionaries; using fin.model; using fin.model.impl; +using fin.scene; namespace HaloWarsTools { public class HWVisResource : HWXmlResource { - public ModelImpl Model { get; private set; } + public SceneImpl Scene { get; private set; } public HWModel[] Models { get; private set; } public static new HWVisResource @@ -22,13 +25,13 @@ public class HWVisResource : HWXmlResource { protected override void Load(byte[] bytes) { base.Load(bytes); - this.Models = ImportModels(); + this.Models = this.ImportModels_(); } - private HWModel[] ImportModels() { + private HWModel[] ImportModels_() { var models = new List(); - this.Model = new ModelImpl(); + this.Scene = new SceneImpl(); var visModels = new List(); var visModelMap = new Dictionary(); @@ -98,12 +101,15 @@ private HWModel[] ImportModels() { visModelMap[modelName] = visModel; } + var sceneArea = this.Scene.AddArea(); + var sceneObj = sceneArea.AddObject(); + var firstModel = visModels[0]; var modelQueue = new Queue<(VisModel, VisSubModelRef?, bool)>(); modelQueue.Enqueue((firstModel, null, true)); - var boneMap = new Dictionary(); + var attachmentPointMap = new NullFriendlyDictionary(); while (modelQueue.Count > 0) { var (visModel, subModelRef, flipFaces) = modelQueue.Dequeue(); @@ -118,9 +124,31 @@ private HWModel[] ImportModels() { // TODO: Sometimes models are missing, why is this?? try { - var resource = HWUgxResource.FromFile( - Context, file, (this.Model, subModelRef, flipFaces, boneMap)); - } catch { } + var ugx = HWUgxResource.FromFile( + Context, + file, + flipFaces); + var model = ugx.Mesh; + + ISceneModel sceneModel; + var attachmentBoneName = subModelRef?.ToBone; + if (attachmentBoneName != null) { + var (parentSceneModel, attachmentPointBone) + = attachmentPointMap[attachmentBoneName]; + sceneModel + = parentSceneModel.AddModelOntoBone( + model, + attachmentPointBone); + } else { + sceneModel = sceneObj.AddSceneModel(model); + } + + foreach (var bone in model.Skeleton) { + attachmentPointMap[bone.Name] = (sceneModel, bone); + } + } catch(Exception e) { + ; + } /*if (resource != null) { models.Add( new HWModel(visModel., resource)); diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/HaloWarsModelImporter.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/HaloWarsModelImporter.cs deleted file mode 100644 index 9f685343d..000000000 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/HaloWarsModelImporter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using fin.model; -using fin.model.io.importers; - -namespace hw.api { - public class HaloWarsModelImporter : IModelImporter { - public IModel Import(IHaloWarsModelFileBundle modelFileBundle) - => modelFileBundle switch { - VisModelFileBundle visModelFileBundle => new VisModelImporter().Import(visModelFileBundle), - XtdModelFileBundle xtdModelFileBundle => new XtdModelImporter().Import(xtdModelFileBundle), - }; - } -} \ No newline at end of file diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsFileBundle.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsFileBundle.cs new file mode 100644 index 000000000..559b0b659 --- /dev/null +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsFileBundle.cs @@ -0,0 +1,5 @@ +using fin.io.bundles; + +namespace hw.api { + public interface IHaloWarsFileBundle : IFileBundle; +} diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsModelFileBundle.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsModelFileBundle.cs deleted file mode 100644 index d5a2d57bc..000000000 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/IHaloWarsModelFileBundle.cs +++ /dev/null @@ -1,6 +0,0 @@ -using fin.model.io; - -namespace hw.api { - public interface IHaloWarsModelFileBundle : IModelFileBundle { - } -} diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIImporter.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIImporter.cs deleted file mode 100644 index e22a5aa3c..000000000 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIImporter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using fin.model; -using fin.model.io.importers; - -using HaloWarsTools; - -namespace hw.api { - public class VisModelImporter : IModelImporter { - public IModel Import(VisModelFileBundle modelFileBundle) { - var visResource = - HWVisResource.FromFile(modelFileBundle.Context, - modelFileBundle.VisFile.FullPath); - return visResource.Model; - } - } -} \ No newline at end of file diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIFileBundle.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneFileBundle.cs similarity index 59% rename from FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIFileBundle.cs rename to FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneFileBundle.cs index 136dfb6d8..07dc74039 100644 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisModeIFileBundle.cs +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneFileBundle.cs @@ -1,10 +1,12 @@ using fin.io; +using fin.scene; using HaloWarsTools; namespace hw.api { - public class VisModelFileBundle : IHaloWarsModelFileBundle { - public VisModelFileBundle(IReadOnlyTreeFile visFile, HWContext context) { + // TODO: Switch this to a scene model or nested model file bundle? + public class VisSceneFileBundle : IHaloWarsFileBundle, ISceneFileBundle { + public VisSceneFileBundle(IReadOnlyTreeFile visFile, HWContext context) { this.VisFile = visFile; this.Context = context; } diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneImporter.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneImporter.cs new file mode 100644 index 000000000..0166e8303 --- /dev/null +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/VisSceneImporter.cs @@ -0,0 +1,14 @@ +using fin.scene; + +using HaloWarsTools; + +namespace hw.api { + public class VisSceneImporter : ISceneImporter { + public IScene Import(VisSceneFileBundle sceneFileBundle) { + var visResource = + HWVisResource.FromFile(sceneFileBundle.Context, + sceneFileBundle.VisFile.FullPath); + return visResource.Scene; + } + } +} \ No newline at end of file diff --git a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/XtdModelFileBundle.cs b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/XtdModelFileBundle.cs index 3ca548c15..856d3a3fd 100644 --- a/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/XtdModelFileBundle.cs +++ b/FinModelUtility/Games/HaloWars/HaloWarsTools/src/api/XtdModelFileBundle.cs @@ -1,7 +1,8 @@ using fin.io; +using fin.model.io; namespace hw.api { - public class XtdModelFileBundle : IHaloWarsModelFileBundle { + public class XtdModelFileBundle : IHaloWarsFileBundle, IModelFileBundle { public XtdModelFileBundle(IReadOnlyTreeFile xtdFile, IReadOnlyTreeFile xttFile) { this.XtdFile = xtdFile; diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/ISceneViewerPanel.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/ISceneViewerPanel.cs index 8b776bbc6..9333dc586 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/ISceneViewerPanel.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/ISceneViewerPanel.cs @@ -1,14 +1,14 @@ using System; using fin.animation; -using fin.io.bundles; +using fin.importers; using fin.model; using fin.scene; using fin.ui.rendering.gl.model; namespace uni.ui { public interface ISceneViewerPanel { - (IFileBundle, IScene)? FileBundleAndScene { get; set; } + (I3dFileBundle, IScene)? FileBundleAndScene { get; set; } ISceneModel? FirstSceneModel { get; } IAnimationPlaybackManager? AnimationPlaybackManager { get; } diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/UniversalAssetToolForm.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/UniversalAssetToolForm.cs index 71eb07e2f..8e64793bb 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/UniversalAssetToolForm.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/UniversalAssetToolForm.cs @@ -163,7 +163,7 @@ private void UpdateModel_(IFileTreeLeafNode? fileNode, } private void UpdateScene_(IFileTreeLeafNode? fileNode, - IFileBundle fileBundle, + I3dFileBundle fileBundle, IScene scene) { this.sceneViewerPanel_.FileBundleAndScene?.Item2.Dispose(); this.sceneViewerPanel_.FileBundleAndScene = (fileBundle, scene); @@ -209,7 +209,7 @@ private void InjectDefaultLightingForScene_( scene.Areas.SelectMany( area => area.Objects.SelectMany(obj => obj.Models))); while (sceneModelQueue.TryDequeue(out var sceneModel)) { - sceneModelQueue.Enqueue(sceneModel.Children); + sceneModelQueue.Enqueue(sceneModel.Children.SelectMany(pair => pair.Value)); var finModel = sceneModel.Model; diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerGlPanel.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerGlPanel.cs index 467e7dfc8..036da9a94 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerGlPanel.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerGlPanel.cs @@ -3,6 +3,7 @@ using System.Windows.Forms; using fin.animation; +using fin.importers; using fin.io.bundles; using fin.model; using fin.scene; @@ -37,11 +38,11 @@ public class SceneViewerGlPanel : BGlPanel, ISceneViewerPanel { private ISceneArea? singleArea_; private SceneAreaRenderer? singleAreaRenderer_; - private IFileBundle? fileBundle_; + private I3dFileBundle? fileBundle_; public TimeSpan FrameTime { get; private set; } - public (IFileBundle, IScene)? FileBundleAndScene { + public (I3dFileBundle, IScene)? FileBundleAndScene { get { var scene = this.scene_; return scene != null diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerPanel.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerPanel.cs index 8d4ba6ca4..03335a14a 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerPanel.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool.Ui/src/ui/winforms/common/scene/SceneViewerPanel.cs @@ -2,6 +2,7 @@ using System.Windows.Forms; using fin.animation; +using fin.importers; using fin.io.bundles; using fin.model; using fin.scene; @@ -13,7 +14,7 @@ public SceneViewerPanel() { this.InitializeComponent(); } - public (IFileBundle, IScene)? FileBundleAndScene { + public (I3dFileBundle, IScene)? FileBundleAndScene { get => this.impl_.FileBundleAndScene; set { var fileBundle = value?.Item1; diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalModelLoader.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalModelLoader.cs index 8be41980d..c32e1f4da 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalModelLoader.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalModelLoader.cs @@ -43,9 +43,8 @@ GeoModelFileBundle geoModelFileBundle => new GeoModelImporter().Import(geoModelFileBundle), GloModelFileBundle gloModelFileBundle => new GloModelImporter().Import(gloModelFileBundle), - IHaloWarsModelFileBundle haloWarsModelFileBundle - => new HaloWarsModelImporter().Import( - haloWarsModelFileBundle), + XtdModelFileBundle xtdModelFileBundle + => new XtdModelImporter().Import(xtdModelFileBundle), ModModelFileBundle modModelFileBundle => new ModModelImporter().Import(modModelFileBundle), OmdModelFileBundle omdModelFileBundle diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalSceneLoader.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalSceneLoader.cs index 68e08017c..5549872ba 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalSceneLoader.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/api/GlobalSceneLoader.cs @@ -3,6 +3,8 @@ using games.pikmin2.api; +using hw.api; + using modl.api; using sm64.api; @@ -18,6 +20,8 @@ Pikmin2SceneFileBundle pikmin2SceneFileBundle Sm64LevelSceneFileBundle sm64LevelSceneFileBundle => new Sm64LevelSceneImporter().Import( sm64LevelSceneFileBundle), + VisSceneFileBundle visSceneFileBundle + => new VisSceneImporter().Import(visSceneFileBundle), _ => throw new ArgumentOutOfRangeException(nameof(sceneFileBundle)) }; } diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/ExporterUtil.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/ExporterUtil.cs index 0f58e1758..2b9fc6ee8 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/ExporterUtil.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/ExporterUtil.cs @@ -176,6 +176,19 @@ public static void ExportAllForCli( Config.Instance.ExporterSettings.ExportedFormats, false); + public static void ExportAllOfTypeForCli( + IAnnotatedFileBundleGatherer gatherer, + IModelImporter reader) + where T : IFileBundle + where TSubType : T, IModelFileBundle + => ExporterUtil.ExportAllForCli_( + gatherer.GatherFileBundles() + .Where(f => f is IAnnotatedFileBundle) + .Select(f => (f as IAnnotatedFileBundle)!), + reader, + Config.Instance.ExporterSettings.ExportedFormats, + false); + private static void ExportAllForCli_( IEnumerable fileBundles, IModelImporter reader, diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsFileBundleGatherer.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsFileBundleGatherer.cs index 9c04e125f..1a2c83aa2 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsFileBundleGatherer.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsFileBundleGatherer.cs @@ -9,11 +9,10 @@ using uni.platforms.desktop; namespace uni.games.halo_wars { - using IAnnotatedHaloWarsBundle = - IAnnotatedFileBundle; + using IAnnotatedHaloWarsBundle = IAnnotatedFileBundle; public class HaloWarsFileBundleGatherer - : IAnnotatedFileBundleGatherer { + : IAnnotatedFileBundleGatherer { public IEnumerable GatherFileBundles() { if (!SteamUtils.TryGetGameDirectory("HaloWarsDE", out var haloWarsSteamDirectory)) { @@ -52,7 +51,7 @@ public IEnumerable GatherFileBundles() { // TODO: Parse UGX files instead, as long as they specify their own animations var visFiles = artSubdir.FilesWithExtension(".vis"); foreach (var visFile in visFiles) { - yield return new VisModelFileBundle(visFile, context).Annotate( + yield return new VisSceneFileBundle(visFile, context).Annotate( visFile); } diff --git a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsMassExporter.cs b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsMassExporter.cs index 361903ff3..164665a2c 100644 --- a/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsMassExporter.cs +++ b/FinModelUtility/UniversalAssetTool/UniversalAssetTool/src/games/halo_wars/HaloWarsMassExporter.cs @@ -3,9 +3,10 @@ namespace uni.games.halo_wars { public class HaloWarsMassExporter : IMassExporter { public void ExportAll() - => ExporterUtil.ExportAllForCli( + => ExporterUtil + .ExportAllOfTypeForCli( new HaloWarsFileBundleGatherer(), - new HaloWarsModelImporter()); + new XtdModelImporter()); /*new HaloWarsTools.Program().Run( DirectoryConstants.ROMS_DIRECTORY.GetSubdir("halo_wars", true).FullName,