• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MeltyPlayer / MeltyTool / 19622977055

24 Nov 2025 04:05AM UTC coverage: 41.989% (+2.1%) from 39.89%
19622977055

push

github

MeltyPlayer
Switched float precision to fix broken tests.

6747 of 18131 branches covered (37.21%)

Branch coverage included in aggregate %.

28639 of 66144 relevant lines covered (43.3%)

65384.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/FinModelUtility/Utility%20of%20Time%20CSharp/api/OotModelImporter.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Numerics;
5

6
using f3dzex2.displaylist;
7
using f3dzex2.displaylist.opcodes;
8
using f3dzex2.displaylist.opcodes.f3dzex2;
9
using f3dzex2.image;
10
using f3dzex2.io;
11
using f3dzex2.model;
12

13
using fin.animation.keyframes;
14
using fin.animation.types.quaternion;
15
using fin.data.queues;
16
using fin.model;
17
using fin.model.io.importers;
18
using fin.util.enumerables;
19
using fin.util.hex;
20

21
using UoT.hacks;
22
using UoT.memory;
23
using UoT.model;
24

25
namespace UoT.api {
26
  public enum OotSegmentIndex : uint {
27
    GAMEPLAY_KEEP = 4,
28
    GAMEPLAY_FIELD_KEEP = 5,
29
    ZOBJECT = 6,
30
    LINK_ANIMETION = 7,
31
  }
32

33
  public sealed class OotModelImporter : IModelImporter<OotModelFileBundle> {
34
    public IModel Import(OotModelFileBundle modelFileBundle) {
×
35
      var zFile = modelFileBundle.ZFile;
×
36
      var isLink = zFile.FileName is "object_link_boy"
×
37
                                     or "object_link_child"
×
38
                                     or "object_torch2";
×
39

40
      var n64Memory = new N64Memory(modelFileBundle.OotRom);
×
41

42
      var n64Hardware = new N64Hardware<N64Memory>();
×
43
      n64Hardware.Memory = n64Memory;
×
44
      n64Hardware.Rdp = new Rdp { Tmem = new NoclipTmem(n64Hardware) };
×
45
      n64Hardware.Rsp = new Rsp();
×
46

47
      n64Hardware.Rsp.GeometryMode = (GeometryMode) 0x22405;
×
48

49
      var zSegments = ZSegments.Instance;
×
50

51
      var gameplayKeep =
×
52
          zSegments.Others.Single(other => other.FileName is "gameplay_keep");
×
53
      n64Memory.AddSegment((uint) OotSegmentIndex.GAMEPLAY_KEEP,
×
54
                           gameplayKeep.Segment);
×
55

56
      // TODO: Use "gameplay dangeon keep" when applicable
57
      var gameplayFieldKeep =
×
58
          zSegments.Others.Single(
×
59
              other => other.FileName is "gameplay_field_keep");
×
60
      n64Memory.AddSegment((uint) OotSegmentIndex.GAMEPLAY_FIELD_KEEP,
×
61
                           gameplayFieldKeep.Segment);
×
62

63
      n64Memory.AddSegment((uint) OotSegmentIndex.ZOBJECT,
×
64
                           zFile.Segment);
×
65

66
      var linkAnimetion =
×
67
          zSegments.Others.SingleOrDefault(
×
68
              other => other.FileName is "link_animetion");
×
69
      if (isLink) {
×
70
        n64Memory.AddSegment((uint) OotSegmentIndex.LINK_ANIMETION,
×
71
                             linkAnimetion.Segment);
×
72
      }
×
73

74
      Hacks.ApplyHacks(n64Hardware, modelFileBundle.ZFile.FileName);
×
75

76
      var dlModelBuilder = new DlModelBuilder(n64Hardware);
×
77
      var finModel = dlModelBuilder.Model;
×
78

79
      var ootLimbs =
×
80
          new LimbHierarchyReader2().GetHierarchies(n64Memory, isLink);
×
81
      if (ootLimbs != null) {
×
82
        var finBones = new IBone[ootLimbs.Count];
×
83
        var ootLimbQueue =
×
84
            new FinTuple2Queue<IBone, int>((finModel.Skeleton.Root, 0));
×
85
        while (ootLimbQueue.TryDequeue(out var parentFinBone,
×
86
                                       out var ootLimbIndex)) {
×
87
          var ootLimb = ootLimbs[ootLimbIndex];
×
88
          var finBone = parentFinBone.AddChild(ootLimb.X, ootLimb.Y, ootLimb.Z);
×
89
          finBones[ootLimbIndex] = finBone;
×
90

91
          // TODO: Handle DLs
92
          // TODO: Handle animations
93

94
          var firstChildIndex = ootLimb.FirstChildIndex;
×
95
          if (firstChildIndex != -1) {
×
96
            ootLimbQueue.Enqueue((finBone, firstChildIndex));
×
97
          }
×
98

99
          var nextSiblingIndex = ootLimb.NextSiblingIndex;
×
100
          if (nextSiblingIndex != -1) {
×
101
            ootLimbQueue.Enqueue((parentFinBone, nextSiblingIndex));
×
102
          }
×
103
        }
×
104

105
        var visibleFinBonesAndOotLimbs = new List<(IBone, ILimb2)>();
×
106
        for (var i = 0; i < finBones.Length; ++i) {
×
107
          var ootLimb = ootLimbs[i];
×
108
          IoUtils.SplitSegmentedAddress(ootLimb.DisplayListSegmentedAddress,
×
109
                                        out var dlSegmentIndex,
×
110
                                        out _);
×
111

112
          if (dlSegmentIndex == 0) {
×
113
            continue;
×
114
          }
115

116
          var finBone = finBones[i];
×
117

118
          n64Hardware.Rsp.BoneMapper.SetBoneAtSegmentedAddress(
×
119
              (uint) 0x0d000000 +
×
120
              (uint) (0x40 * visibleFinBonesAndOotLimbs.Count),
×
121
              finBone);
×
122
          visibleFinBonesAndOotLimbs.Add((finBone, ootLimb));
×
123
        }
×
124

125
        foreach (var (finBone, ootLimb) in visibleFinBonesAndOotLimbs) {
×
126
          n64Hardware.Rsp.ActiveBoneWeights =
×
127
              finModel.Skin.GetOrCreateBoneWeights(
×
128
                  VertexSpace.RELATIVE_TO_BONE,
×
129
                  finBone);
×
130

131
          var displayList =
×
132
              new DisplayListReader().ReadDisplayList(
×
133
                  n64Memory,
×
134
                  new F3dzex2OpcodeParser(),
×
135
                  ootLimb.DisplayListSegmentedAddress);
×
136
          dlModelBuilder.AddDl(displayList);
×
137
        }
×
138

139
        var animationReader = new AnimationReader2();
×
140
        IList<IAnimation>? ootAnimations;
141
        if (isLink) {
×
142
          ootAnimations = animationReader.GetLinkAnimations(n64Memory,
×
143
            gameplayKeep,
×
144
            ootLimbs.Count);
×
145
        } else {
×
146
          ootAnimations = animationReader.GetCommonAnimations(
×
147
              n64Memory,
×
148
              AnimationOffsets.GetFor(zFile, zSegments),
×
149
              ootLimbs.Count);
×
150
        }
×
151

152
        var animationIndex = 0;
×
153
        if (ootAnimations != null) {
×
154
          var rootBone = finBones[0];
×
155

156
          foreach (var ootAnimation in ootAnimations) {
×
157
            var finAnimation = finModel.AnimationManager.AddAnimation();
×
158
            finAnimation.Name = ootAnimation.Offset.ToHex();
×
159
            finAnimation.FrameRate = 20;
×
160
            var frameCount = finAnimation.FrameCount = ootAnimation.FrameCount;
×
161

162
            var rootAnimationTracks = finAnimation.GetOrCreateBoneTracks(rootBone);
×
163
            var positions
×
164
                = rootAnimationTracks.UseCombinedTranslationKeyframes(
×
165
                    frameCount);
×
166
            for (var f = 0; f < frameCount; ++f) {
×
167
              var pos = ootAnimation.GetPosition(f);
×
168
              positions.SetKeyframe(f, new Vector3(pos.X, pos.Y, pos.Z));
×
169
            }
×
170

171
            for (var i = 0; i < ootLimbs.Count; ++i) {
×
172
              var finBone = finBones[i];
×
173
              var animationTracks = i == 0
×
174
                  ? rootAnimationTracks
×
175
                  : finAnimation.GetOrCreateBoneTracks(finBone);
×
176
              var rotations
×
177
                  = animationTracks
×
178
                      .UseSeparateEulerRadiansKeyframes(frameCount);
×
179

180
              rotations.ConvertRadiansToQuaternionImpl =
×
181
                  ConvertRadiansToQuaternionOot_;
×
182

183
              for (var a = 0; a < 3; ++a) {
×
184
                AddOotAnimationTrackToFin_(
×
185
                    ootAnimation.GetTrack(i * 3 + a),
×
186
                    a,
×
187
                    rotations);
×
188
              }
×
189
            }
×
190
          }
×
191
        }
×
192
      }
×
193

194
      return finModel;
×
195
    }
×
196

197
    private static void AddOotAnimationTrackToFin_(
198
        IAnimationTrack ootAnimationTrack,
199
        int axis,
200
        ISeparateEulerRadiansKeyframes<Keyframe<float>> rotations) {
×
201
      for (var f = 0; f < ootAnimationTrack.Frames.Count; ++f) {
×
202
        rotations
×
203
            .Axes[axis]
×
204
            .SetKeyframe(f,
×
205
                         (float) ((ootAnimationTrack.Frames[f] * 360.0) /
×
206
                                  0xFFFF));
×
207
      }
×
208
    }
×
209

210
    private static Quaternion ConvertRadiansToQuaternionOot_(
211
        float xRadians,
212
        float yRadians,
213
        float zRadians) {
×
214
      var r2d = MathF.PI / 180;
×
215
      var xDegrees = xRadians * r2d;
×
216
      var yDegrees = yRadians * r2d;
×
217
      var zDegrees = zRadians * r2d;
×
218

219
      var qz = Quaternion.CreateFromYawPitchRoll(0, 0, zDegrees);
×
220
      var qy = Quaternion.CreateFromYawPitchRoll(yDegrees, 0, 0);
×
221
      var qx = Quaternion.CreateFromYawPitchRoll(0, xDegrees, 0);
×
222

223
      return Quaternion.Normalize(qz * qy * qx);
×
224
    }
×
225
  }
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc