Skip to content

Commit

Permalink
Kind of got loading animation files working, though the axes are stil…
Browse files Browse the repository at this point in the history
…l really jacked up.
  • Loading branch information
MeltyPlayer committed Nov 5, 2023
1 parent 0a22cb2 commit 2136b12
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 294 deletions.
36 changes: 21 additions & 15 deletions FinModelUtility/Formats/Dat/Dat/src/api/DatModelImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ public class DatModelImporter : IModelImporter<DatModelFileBundle> {
public unsafe IModel ImportModel(DatModelFileBundle modelFileBundle) {
var primaryDat =
modelFileBundle.PrimaryDatFile.ReadNew<Dat>(Endianness.BigEndian);
var primaryDatSubfile = primaryDat.Subfiles.First();

var animationDat =
modelFileBundle.AnimationDatFile?.ReadNew<Dat>(Endianness.BigEndian);

var finModel = new ModelImpl();
var finSkin = finModel.Skin;

// Adds skeleton
var jObjByOffset = primaryDat.JObjByOffset;
var jObjByOffset = primaryDatSubfile.JObjByOffset;
var finBoneByJObj = new Dictionary<JObj, IBone>();
var boneWeightsByJObj = new Dictionary<JObj, IBoneWeights>();
var inverseBindMatrixByJObj =
new Dictionary<JObj, IReadOnlyFinMatrix4x4>();
var boneQueue = new Queue<(IBone finParentBone, JObj datBone)>();
foreach (var datRootBone in primaryDat.RootJObjs) {
foreach (var datRootBone in primaryDatSubfile.RootJObjs) {
boneQueue.Enqueue((finModel.Skeleton.Root, datRootBone));
}

Expand Down Expand Up @@ -85,22 +87,26 @@ public unsafe IModel ImportModel(DatModelFileBundle modelFileBundle) {
var finAnimation = finModel.AnimationManager.AddAnimation();
finAnimation.Name = $"Animation {i}";

finAnimation.FrameRate = 30;
finAnimation.FrameRate = 60;

return finAnimation;
});

var i = 0;
foreach (var figaTree in animationDat.GetRootNodesOfType<FigaTree>()) {
var finAnimation = lazyFinAnimations[i++];
finAnimation.FrameCount =
Math.Max(finAnimation.FrameCount, (int) figaTree.FrameCount);

foreach (var (jObj, trackNode) in primaryDat.JObjs.Zip(
figaTree.TrackNodes)) {
var finBone = finBoneByJObj[jObj];
var boneTracks = finAnimation.AddBoneTracks(finBone);
DatBoneTracksHelper.AddDatKeyframesToBoneTracks(trackNode, boneTracks);
foreach (var animationDatSubfile in animationDat.Subfiles) {
foreach (var figaTree in animationDatSubfile
.GetRootNodesOfType<FigaTree>()) {
var finAnimation = lazyFinAnimations[i++];
finAnimation.FrameCount = (int) figaTree.FrameCount;

foreach (var (jObj, trackNode) in primaryDatSubfile.JObjs.Zip(
figaTree.TrackNodes)) {
var finBone = finBoneByJObj[jObj];
var boneTracks = finAnimation.AddBoneTracks(finBone);
DatBoneTracksHelper.AddDatKeyframesToBoneTracks(
trackNode,
boneTracks);
}
}
}

Expand All @@ -127,7 +133,7 @@ public unsafe IModel ImportModel(DatModelFileBundle modelFileBundle) {
// Adds mesh and materials
var mObjByOffset = new Dictionary<uint, MObj>();
var tObjByOffset = new Dictionary<uint, TObj>();
foreach (var jObj in primaryDat.JObjs) {
foreach (var jObj in primaryDatSubfile.JObjs) {
foreach (var dObj in jObj.DObjs) {
var mObj = dObj.MObj;
if (mObj != null) {
Expand Down Expand Up @@ -311,7 +317,7 @@ public unsafe IModel ImportModel(DatModelFileBundle modelFileBundle) {

// Sorts all dObjs so that the opaque ones are rendered first, and then the translucent (XLU) ones
var allJObjsAndDObjs =
primaryDat
primaryDatSubfile
.JObjs.SelectMany(jObj => jObj.DObjs.Select(dObj => (jObj, dObj)))
.ToArray();
var sortedJObjsAndDObjs =
Expand Down
294 changes: 17 additions & 277 deletions FinModelUtility/Formats/Dat/Dat/src/schema/Dat.cs
Original file line number Diff line number Diff line change
@@ -1,286 +1,26 @@
using dat.schema.animation;

using fin.data.queues;
using fin.util.asserts;
using fin.util.linq;

using schema.binary;
using schema.binary.attributes;
using schema.binary;

namespace dat.schema {
// CObj: camera
// IObj: image
// SObj: Scene object

/// <summary>
/// References:
/// - https://github.com/jam1garner/Smash-Forge/blob/c0075bca364366bbea2d3803f5aeae45a4168640/Smash%20Forge/Filetypes/Melee/DAT.cs
/// </summary>
public class Dat : IBinaryDeserializable {
private readonly List<RootNode> rootNodes_ = new();
private readonly HashSet<uint> validOffsets_ = new();

private uint dataBlockOffset_;
private uint relocationTableOffset_;
private uint rootNodeOffset_;
private uint referenceNodeOffset_;
private uint stringTableOffset_;

public List<IDatNode> RootNodes { get; } = new();

public IEnumerable<TNode> GetRootNodesOfType<TNode>()
where TNode : IDatNode
=> this.RootNodes.WhereIs<IDatNode, TNode>();

public IEnumerable<JObj> RootJObjs => this.GetRootNodesOfType<JObj>();

private readonly Dictionary<uint, JObj> jObjByOffset_ = new();
public IReadOnlyDictionary<uint, JObj> JObjByOffset => this.jObjByOffset_;

public IEnumerable<JObj> JObjs => this.RootJObjs.SelectMany(
DatNodeExtensions.GetSelfAndChildrenAndSiblings);

public LinkedList<DatSubfile> Subfiles { get; } = new();

public void Read(IBinaryReader br) {
var fileHeader = br.ReadNew<FileHeader>();

this.dataBlockOffset_ = 0x20;
this.relocationTableOffset_ =
this.dataBlockOffset_ + fileHeader.DataBlockSize;
this.rootNodeOffset_ =
this.relocationTableOffset_ + 4 * fileHeader.RelocationTableCount;
this.referenceNodeOffset_ =
this.rootNodeOffset_ + 8 * fileHeader.RootNodeCount;
this.stringTableOffset_ =
this.referenceNodeOffset_ + 8 * fileHeader.ReferenceNodeCount;

// Reads relocation table
this.validOffsets_.Clear();
for (var i = 0; i < fileHeader.RelocationTableCount; ++i) {
br.Position = this.relocationTableOffset_ + 4 * i;
var relocationTableEntryOffset = br.ReadUInt32();

br.Position = this.dataBlockOffset_ + relocationTableEntryOffset;
var relocationTableValue = br.ReadUInt32();

this.validOffsets_.Add(relocationTableValue);
}

// Reads root nodes
this.rootNodes_.Clear();
for (var i = 0; i < fileHeader.RootNodeCount; i++) {
br.Position = this.rootNodeOffset_ + 8 * i;

var rootNode = new RootNode();
rootNode.Data.Read(br);

br.SubreadAt(this.stringTableOffset_ + rootNode.Data.StringOffset,
sbr => { rootNode.Name = sbr.ReadStringNT(); });

this.rootNodes_.Add(rootNode);
}

// TODO: Handle reference nodes

// Reads root bone structures
foreach (var rootNode in this.rootNodes_) {
if (rootNode.Type == RootNodeType.UNDEFINED) {
;
}
}

this.ReadRootNodeObjects_(br);
this.ReadNames_(br);
}

private void ReadRootNodeObjects_(IBinaryReader br) {
br.Position = this.dataBlockOffset_;
br.PushLocalSpace();

var jObjQueue = new FinTuple2Queue<uint, JObj>();

this.RootNodes.Clear();
foreach (var rootNode in this.rootNodes_) {
var rootNodeOffset = rootNode.Data.DataOffset;
br.Position = rootNodeOffset;

switch (rootNode.Type) {
case RootNodeType.JOBJ: {
var jObj = br.ReadNew<JObj>();
this.RootNodes.Add(jObj);

jObjQueue.Enqueue((rootNodeOffset, jObj));
break;
}
case RootNodeType.MATANIM_JOINT: {
this.RootNodes.Add(br.ReadNew<MatAnimJoint>());
break;
}
case RootNodeType.FIGATREE: {
this.RootNodes.Add(br.ReadNew<FigaTree>());
break;
}
}
}

br.PopLocalSpace();

while (jObjQueue.TryDequeue(out var jObjOffset, out var jObj)) {
this.jObjByOffset_[jObjOffset] = jObj;

if (jObj.FirstChild != null) {
jObjQueue.Enqueue((jObj.FirstChildBoneOffset, jObj.FirstChild));
}

if (jObj.NextSibling != null) {
jObjQueue.Enqueue((jObj.NextSiblingBoneOffset, jObj.NextSibling));
}
}
}

private void ReadNames_(IBinaryReader br) {
br.Position = this.stringTableOffset_;
br.PushLocalSpace();

foreach (var jObj in this.JObjs) {
var jObjStringOffset = jObj.StringOffset;
if (jObjStringOffset != 0) {
br.Position = jObjStringOffset;
jObj.Name = br.ReadStringNT();
do {
var offset = br.Position;
try {
br.PushLocalSpace();
var subfile = br.ReadNew<DatSubfile>();
br.PopLocalSpace();

this.Subfiles.AddLast(subfile);
br.Position = offset + subfile.FileSize;
br.Align(0x20);
} catch {
br.PopLocalSpace();
br.Position = offset;
break;
}

foreach (var dObj in jObj.DObjs) {
var dObjStringOffset = dObj.StringOffset;
if (dObjStringOffset != 0) {
br.Position = dObjStringOffset;
dObj.Name = br.ReadStringNT();
}

var mObj = dObj.MObj;
if (mObj != null) {
var mObjStringOffset = mObj.StringOffset;
if (mObjStringOffset != 0) {
br.Position = mObjStringOffset;
mObj.Name = br.ReadStringNT();
}

foreach (var (_, tObj) in mObj.TObjsAndOffsets) {
var tObjStringOffset = tObj.StringOffset;
if (tObjStringOffset != 0) {
br.Position = tObj.StringOffset;
tObj.Name = br.ReadStringNT();
}
}
}
}
}

br.PopLocalSpace();
}
}

[BinarySchema]
public partial class FileHeader : IBinaryConvertible {
public uint FileSize { get; set; }
public uint DataBlockSize { get; set; }

public uint RelocationTableCount { get; set; }
public uint RootNodeCount { get; set; }
public uint ReferenceNodeCount { get; set; }

[StringLengthSource(4)]
public string Version { get; set; }

public uint Padding1 { get; set; }
public uint Padding2 { get; set; }


[Ignore]
public uint DataBlockOffset => 0x20;

[Ignore]
public uint RelocationTableOffset => DataBlockOffset + DataBlockSize;

[Ignore]
public uint RootNodeOffset =>
RelocationTableOffset + 4 * RelocationTableCount;

[Ignore]
public uint ReferenceNodeOffset => RootNodeOffset + 8 * RootNodeCount;

[Ignore]
public uint StringTableOffset =>
ReferenceNodeOffset + 8 * ReferenceNodeCount;
}

[BinarySchema]
public partial class RootNodeData : IBinaryConvertible {
public uint DataOffset { get; set; }
public uint StringOffset { get; set; }
}

public enum RootNodeType {
UNDEFINED,
JOBJ,
MATANIM_JOINT,
FIGATREE,
IMAGE,
SCENE_DATA,
SCENE_MODELSET,
TLUT,
TLUT_DESC,
}

public class RootNode {
private string name_;

public RootNodeData Data { get; } = new();

public string Name {
get => this.name_;
set => this.Type = RootNode.GetTypeFromName_(this.name_ = value);
}

public override string ToString() => $"[{Type}]: {Name}";

public RootNodeType Type { get; private set; }

private static RootNodeType GetTypeFromName_(string name) {
// TODO: Use flags for this instead
if (name.EndsWith("_joint") && !name.Contains("matanim") &&
!name.Contains("anim_joint")) {
return RootNodeType.JOBJ;
}

if (name.EndsWith("_matanim_joint")) {
return RootNodeType.MATANIM_JOINT;
}

if (name.EndsWith("_figatree")) {
return RootNodeType.FIGATREE;
}

if (name.EndsWith("_image")) {
return RootNodeType.IMAGE;
}

if (name.EndsWith("_scene_data")) {
return RootNodeType.SCENE_DATA;
}

if (name.EndsWith("_scene_modelset")) {
return RootNodeType.SCENE_MODELSET;
}

if (name.EndsWith("_tlut")) {
return RootNodeType.TLUT;
}

if (name.EndsWith("_tlut_desc")) {
return RootNodeType.TLUT_DESC;
}

return RootNodeType.UNDEFINED;
} while (!br.Eof);
}
}
}
Loading

0 comments on commit 2136b12

Please sign in to comment.