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

MeltyPlayer / MeltyTool / 20982513833

14 Jan 2026 04:35AM UTC coverage: 41.138% (-0.8%) from 41.907%
20982513833

push

github

MeltyPlayer
Fixed broken shader source tests.

6752 of 18485 branches covered (36.53%)

Branch coverage included in aggregate %.

28650 of 67572 relevant lines covered (42.4%)

64006.02 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.hex;
19

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

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

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

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

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

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

48
      var zSegments = ZSegments.Instance;
×
49

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

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

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

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

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

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

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

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

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

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

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

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

115
          var finBone = finBones[i];
×
116

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

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

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

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

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

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

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

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

179
              rotations.ConvertRadiansToQuaternionImpl =
×
180
                  ConvertRadiansToQuaternionOot_;
×
181

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

193
      return finModel;
×
194
    }
×
195

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

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

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

222
      return Quaternion.Normalize(qz * qy * qx);
×
223
    }
×
224
  }
225
}
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