Skip to content

Commit

Permalink
Simplified how models are rendered to make things easier to maintain,…
Browse files Browse the repository at this point in the history
… though it means Halo Wars models can't be exported again for the time being.
  • Loading branch information
MeltyPlayer committed Feb 11, 2024
1 parent 9f85143 commit 6c8bddf
Show file tree
Hide file tree
Showing 31 changed files with 205 additions and 155 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<SceneModelRenderer> children_;
private readonly ListDictionary<IBone, SceneModelRenderer> children_ = [];

public SceneModelRenderer(ISceneModel sceneModel, ILighting? lighting) {
this.sceneModel_ = sceneModel;
Expand All @@ -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_();
Expand All @@ -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();
}
}
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class CaseInvariantStringDictionary<T> : IFinDictionary<string, T> {
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];
Expand Down
21 changes: 19 additions & 2 deletions FinModelUtility/Fin/Fin/src/data/dictionaries/ListDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,29 @@
using System.Linq;

namespace fin.data.dictionaries {
public interface IReadOnlyListDictionary<TKey, TValue>
: IReadOnlyFinCollection<(TKey Key, IList<TValue> Value)> {
bool HasList(TKey key);

IList<TValue> this[TKey key] { get; }
bool TryGetList(TKey key, out IList<TValue>? list);
}

public interface IListDictionary<TKey, TValue>
: IReadOnlyListDictionary<TKey, TValue>,
IFinCollection<(TKey Key, IList<TValue> Value)> {
void ClearList(TKey key);
void Add(TKey key, TValue value);
}


/// <summary>
/// An implementation for a dictionary of lists. Each value added for a key
/// will be stored in that key's corresponding list.
/// </summary>
public class ListDictionary<TKey, TValue>(IFinDictionary<TKey, IList<TValue>> impl)
: IFinCollection<(TKey Key, IList<TValue> Value)> {
public class ListDictionary<TKey, TValue>(
IFinDictionary<TKey, IList<TValue>> impl)
: IListDictionary<TKey, TValue> {
public ListDictionary() : this(
new NullFriendlyDictionary<TKey, IList<TValue>>()) { }

Expand Down
14 changes: 13 additions & 1 deletion FinModelUtility/Fin/Fin/src/data/dictionaries/SetDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,25 @@
using System.Linq;

namespace fin.data.dictionaries {
public interface IReadOnlySetDictionary<TKey, TValue>
: IReadOnlyFinCollection<(TKey Key, ISet<TValue> Value)> {
ISet<TValue> this[TKey key] { get; }
bool TryGetSet(TKey key, out ISet<TValue>? list);
}

public interface ISetDictionary<TKey, TValue>
: IReadOnlySetDictionary<TKey, TValue>,
IFinCollection<(TKey Key, ISet<TValue> Value)> {
void Add(TKey key, TValue value);
}

/// <summary>
/// An implementation for a dictionary of sets. Each value added for a key
/// will be stored in that key's corresponding set.
/// </summary>
public class SetDictionary<TKey, TValue>(
IFinDictionary<TKey, ISet<TValue>> impl)
: IFinCollection<(TKey Key, ISet<TValue> Value)> {
: ISetDictionary<TKey, TValue> {
public SetDictionary() : this(
new NullFriendlyDictionary<TKey, ISet<TValue>>()) { }

Expand Down
21 changes: 15 additions & 6 deletions FinModelUtility/Fin/Fin/src/data/lazy/LazyArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@ public class LazyArray<T> : ILazyArray<T> {
private readonly bool[] populated_;
private readonly Func<int, T> 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_ = handler;
}

public LazyArray(
int count,
Func<LazyArray<T>, int, T> handler) {
public LazyArray(int count, Func<LazyArray<T>, int, T> handler) {
this.impl_ = new T[count];
this.populated_ = new bool[count];
this.handler_ = (int key) => handler(this, key);
Expand All @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public LazyCaseInvariantStringDictionary(Func<LazyDictionary<string, TValue>, 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;
Expand Down
2 changes: 2 additions & 0 deletions FinModelUtility/Fin/Fin/src/data/lazy/LazyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public LazyDictionary(Func<LazyDictionary<TKey, TValue>, 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
Expand Down
13 changes: 13 additions & 0 deletions FinModelUtility/Fin/Fin/src/data/lazy/LazyList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down
46 changes: 7 additions & 39 deletions FinModelUtility/Fin/Fin/src/math/BoneTransformManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IBone, IFinMatrix4x4>
bonesToRootMatrices_ = new();

private readonly IndexableDictionary<IBone, IFinMatrix4x4>
bonesToWorldMatrices_ = new();

private readonly IndexableDictionary<IBone, IReadOnlyFinMatrix4x4>
bonesToInverseRootMatrices_ = new();

private readonly IndexableDictionary<IBone, IReadOnlyFinMatrix4x4>
bonesToInverseWorldMatrices_ = new();

Expand All @@ -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();
Expand Down Expand Up @@ -137,20 +129,10 @@ public IDictionary<IBone, int> CalculateMatrices(
var bonesToIndex = new Dictionary<IBone, int>();
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();
Expand Down Expand Up @@ -209,28 +191,22 @@ public IDictionary<IBone, int> 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,
localScale);
}

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));
}
}

Expand All @@ -248,17 +224,9 @@ public IDictionary<IBone, int> 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);
}
}

Expand Down
1 change: 0 additions & 1 deletion FinModelUtility/Fin/Fin/src/model/SkinInterfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public readonly struct TexCoord {

public enum VertexSpace {
RELATIVE_TO_WORLD,
RELATIVE_TO_ROOT,
RELATIVE_TO_BONE,
}

Expand Down
3 changes: 2 additions & 1 deletion FinModelUtility/Fin/Fin/src/scene/SceneInterfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Drawing;

using fin.animation;
using fin.data.dictionaries;
using fin.importers;
using fin.math;
using fin.model;
Expand Down Expand Up @@ -86,7 +87,7 @@ ISceneObject SetRotationDegrees(float xDegrees,
/// onto bones.
/// </summary>
public interface ISceneModel : IDisposable {
IReadOnlyList<ISceneModel> Children { get; }
IReadOnlyListDictionary<IBone, ISceneModel> Children { get; }
ISceneModel AddModelOntoBone(IModel model, IBone bone);

IModel Model { get; }
Expand Down
14 changes: 7 additions & 7 deletions FinModelUtility/Fin/Fin/src/scene/impl/SceneModelImpl.cs
Original file line number Diff line number Diff line change
@@ -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<ISceneModel> children_ = [];
private readonly ListDictionary<IBone, ISceneModel> children_ = [];
private IModelAnimation? animation_;

public SceneModelImpl(IModel model) {
Expand Down Expand Up @@ -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<ISceneModel> Children => this.children_;
public IReadOnlyListDictionary<IBone, ISceneModel> 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;
}

Expand Down
Loading

0 comments on commit 6c8bddf

Please sign in to comment.