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

MeltyPlayer / MeltyTool / 21814525774

09 Feb 2026 06:18AM UTC coverage: 41.546% (-0.01%) from 41.556%
21814525774

push

github

MeltyPlayer
Added shadows to the PMDC characters.

6862 of 18652 branches covered (36.79%)

Branch coverage included in aggregate %.

0 of 51 new or added lines in 3 files covered. (0.0%)

1 existing line in 1 file now uncovered.

29423 of 68685 relevant lines covered (42.84%)

63334.63 hits per line

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

0.0
/FinModelUtility/Games/PaperMarioDirectorsCut/PaperMarioDirectorsCut/src/api/LvlSceneImporter.cs
1
using System.Numerics;
2

3
using fin.animation.keyframes;
4
using fin.color;
5
using fin.data.lazy;
6
using fin.image;
7
using fin.io;
8
using fin.math;
9
using fin.math.transform;
10
using fin.model;
11
using fin.model.util;
12
using fin.scene;
13
using fin.ui.rendering.gl.scene;
14
using fin.util.asserts;
15
using fin.util.enums;
16
using fin.util.sets;
17

18
using gm.api;
19
using gm.schema.d3d;
20

21
using pmdc.schema.lvl;
22

23
namespace pmdc.api;
24

25
public sealed class LvlSceneFileBundle : ISceneFileBundle {
26
  public IReadOnlyTreeFile MainFile => this.LvlFile;
×
27

28
  public required IReadOnlyTreeFile LvlFile { get; init; }
×
29
  public required IReadOnlyTreeDirectory RootDirectory { get; init; }
×
30
}
31

32
public sealed class LvlSceneImporter : ISceneImporter<LvlSceneFileBundle> {
33
  public IScene Import(LvlSceneFileBundle sceneFileBundle) {
×
34
    var lvlFile = sceneFileBundle.LvlFile;
×
35
    var lvl = lvlFile.ReadNewFromText<Lvl>();
×
36

37
    var files = sceneFileBundle.LvlFile.AsFileSet();
×
38
    var finScene = new SceneImpl {
×
39
        FileBundle = sceneFileBundle,
×
40
        Files = files
×
41
    };
×
42

43
    var finArea = finScene.AddArea();
×
44

45
    if (lvl.HasRoomModel) {
×
46
      var modelFile = lvlFile.AssertGetParent()
×
47
                             .AssertGetExistingFile("model.omd");
×
48
      files.Add(modelFile);
×
49

50
      var finModel
×
51
          = new OmdModelImporter().Import(new OmdModelFileBundle {
×
52
              OmdFile = modelFile
×
53
          });
×
54
      finArea.AddRootNode().AddSceneModel(finModel);
×
55
    }
×
56

57
    var textureDirectory
×
58
        = sceneFileBundle.RootDirectory.AssertGetExistingSubdir("Textures");
×
59
    var lazyImageMap = new LazyDictionary<string?, IImage?>(
×
60
        imageName => imageName != null &&
×
61
                     textureDirectory.TryToGetExistingFile(
×
62
                         $"{imageName}.png",
×
63
                         out var textureFile)
×
64
            ? FinImage.FromFile(textureFile)
×
65
            : null);
×
66

67
    if (lvl.FloorBlocks.Count > 0) {
×
68
      foreach (var floorBlockParams in lvl.FloorBlocks) {
×
69
        var (start, end, textureName, type, flags) = floorBlockParams;
×
70

71
        var (floorBlockModel, floorBlockRootBone)
×
72
            = D3dModelImporter.CreateModel();
×
73
        var floorBlockSkin = floorBlockModel.Skin;
×
74
        var floorBlockMesh = floorBlockSkin.AddMesh();
×
75

76
        var shouldRepeat = !flags.CheckFlag(FloorBlockFlags.NO_REPEAT);
×
77
        var floorBlockMaterialManager = floorBlockModel.MaterialManager;
×
78
        IMaterial? floorBlockMaterial = null;
×
79
        if (flags.CheckFlag(FloorBlockFlags.INVISIBLE)) {
×
80
          floorBlockMaterial = floorBlockMaterialManager.AddHiddenMaterial();
×
81
        } else {
×
82
          var image = lazyImageMap[textureName];
×
83
          if (image != null) {
×
84
            (floorBlockMaterial, var floorBlockTexture)
×
85
                = floorBlockMaterialManager.AddSimpleTextureMaterialFromImage(
×
86
                    image,
×
87
                    textureName);
×
88

89
            if (shouldRepeat) {
×
90
              floorBlockTexture.WrapModeU = WrapMode.REPEAT;
×
91
              floorBlockTexture.WrapModeV = WrapMode.REPEAT;
×
92
            }
×
93
          }
×
94
        }
×
95

96
        switch (type) {
×
97
          case FloorBlockType.WALL: {
×
98
            (float, float)? repeat = shouldRepeat
×
99
                ? ((end.Xy() - start.Xy()).Length() / 64,
×
100
                   Math.Abs(end.Z - start.Z) / 64)
×
101
                : null;
×
102
            floorBlockMesh.AddSimpleWall(floorBlockSkin,
×
103
                                         start,
×
104
                                         end,
×
105
                                         floorBlockMaterial,
×
106
                                         floorBlockRootBone,
×
107
                                         repeat);
×
108
            break;
×
109
          }
110
          case FloorBlockType.FLOOR: {
×
111
            (float, float, float)? repeat = shouldRepeat
×
112
                ? (Math.Abs(end.X - start.X) / 64,
×
113
                   Math.Abs(end.Y - start.Y) / 64,
×
114
                   Math.Abs(end.Z - start.Z) / 64)
×
115
                : null;
×
116

117
            floorBlockMesh.AddSimpleCube(floorBlockSkin,
×
118
                                         start,
×
119
                                         end,
×
120
                                         floorBlockMaterial,
×
121
                                         floorBlockRootBone,
×
122
                                         repeat);
×
123
            break;
×
124
          }
125
        }
126

127
        finArea.AddRootNode().AddSceneModel(floorBlockModel);
×
128
      }
×
129
    }
×
130

131
    var characterDirectory
×
132
        = sceneFileBundle.RootDirectory.AssertGetExistingSubdir("Characters");
×
133

134
    if (lvl.Npcs.Count > 0) {
×
135
      var lazyNpcModels
×
136
          = new LazyCaseInvariantStringDictionary<IReadOnlyModel>(characterType
×
137
              => new PmdcCharacterModelImporter().Import(
×
138
                  new PmdcCharacterModelFileBundle {
×
139
                      AnimationImageFiles = characterDirectory
×
140
                                            .AssertGetExistingSubdir(
×
141
                                                characterType)
×
142
                                            .GetFilesWithFileType(".gif")
×
143
                                            .ToArray(),
×
NEW
144
                      CharactersDirectory = characterDirectory,
×
UNCOV
145
                  }));
×
146

147
      foreach (var (npcPosition, npcName, npcCharacterType) in lvl.Npcs) {
×
148
        var npcNode
×
149
            = finArea.AddRootNode()
×
150
                     .SetPosition(npcPosition.X, npcPosition.Z, npcPosition.Y)
×
151
                     .AddComponent(
×
152
                         new SimpleModelRenderComponent(
×
153
                             lazyNpcModels[npcCharacterType]));
×
154
        npcNode.Name = npcName;
×
155
      }
×
156
    }
×
157

158
    if (lvl.SaveBlocks.Count > 0) {
×
159
      var saveBlockModel = CreateSaveBlockModel_(sceneFileBundle.RootDirectory);
×
160

161
      foreach (var saveBlockPosition in lvl.SaveBlocks) {
×
162
        finArea.AddRootNode()
×
163
               .SetPosition(saveBlockPosition.X, saveBlockPosition.Z, saveBlockPosition.Y)
×
164
               .AddSceneModel(saveBlockModel);
×
165
      }
×
166
    }
×
167

168
    if (lvl.SaveBlocks.Count > 0) {
×
169
      var saveBlockModel = CreateSaveBlockModel_(sceneFileBundle.RootDirectory);
×
170

171
      foreach (var saveBlockPosition in lvl.SaveBlocks) {
×
172
        finArea.AddRootNode()
×
173
               .SetPosition(saveBlockPosition.X, saveBlockPosition.Z, saveBlockPosition.Y)
×
174
               .AddSceneModel(saveBlockModel);
×
175
      }
×
176
    }
×
177

178
    if (lvl.Trees.Count > 0) {
×
179
      var treeModel = CreateTreeModel_(sceneFileBundle.RootDirectory);
×
180

181
      foreach (var treePosition in lvl.Trees) {
×
182
        finArea.AddRootNode()
×
183
               .SetPosition(treePosition.X, treePosition.Z, treePosition.Y)
×
184
               .AddSceneModel(treeModel);
×
185
      }
×
186
    }
×
187

188
    if (sceneFileBundle.LvlFile.Name is "battle.lvl") {
×
189
      var battleWallModel = CreateBattleWallModel_(lazyImageMap);
×
190

191
      finArea.AddRootNode()
×
192
             .SetPosition(176, 0, 176)
×
193
             .AddSceneModel(battleWallModel);
×
194
      finArea.AddRootNode()
×
195
             .SetPosition(176, 0, 464)
×
196
             .AddSceneModel(battleWallModel);
×
197

198
      var (battleFloorModel, battleFloorRootBone)
×
199
          = D3dModelImporter.CreateModel();
×
200

201
      var bfSkin = battleFloorModel.Skin;
×
202
      var bfMesh = bfSkin.AddMesh();
×
203
      var bfMaterialManager = battleFloorModel.MaterialManager;
×
204

205
      var frontOfFloorImage = lazyImageMap["bacFrontOfFloor"].AssertNonnull();
×
206
      var frontOfFloorTexture
×
207
          = bfMaterialManager.CreateTexture(frontOfFloorImage);
×
208
      frontOfFloorTexture.WrapModeV = WrapMode.REPEAT;
×
209
      var frontOfFloorMaterial
×
210
          = bfMaterialManager.AddTextureMaterial(frontOfFloorTexture);
×
211

212
      bfMesh.AddSimpleFloor(bfSkin,
×
213
                            new Vector3(0, 16, -64),
×
214
                            new Vector3(64, 640, 0),
×
215
                            frontOfFloorMaterial,
×
216
                            battleFloorRootBone,
×
217
                            (1, 10));
×
218
      finArea.AddRootNode()
×
219
             .AddSceneModel(battleFloorModel);
×
220
    }
×
221

222
    if (lvl.BackgroundName != null) {
×
223
      var backgroundImageFile
×
224
          = sceneFileBundle
×
225
            .RootDirectory.AssertGetExistingSubdir("Backgrounds")
×
226
            .AssertGetExistingFile($"{lvl.BackgroundName}.png");
×
227
      finArea.BackgroundImage = FinImage.FromFile(backgroundImageFile);
×
228
      finArea.CreateCustomSkyboxNode();
×
229
    }
×
230

231
    finScene.CreateDefaultLighting(finArea.AddRootNode());
×
232

233
    return finScene;
×
234
  }
×
235

236
  private static IModel CreateBattleWallModel_(
237
      ILazyDictionary<string?, IImage?> lazyImageMap) {
×
238
    var (battleWallModel, battleWallRootBone)
×
239
        = D3dModelImporter.CreateModel();
×
240

241
    var battleSkin = battleWallModel.Skin;
×
242
    var battleWallMesh = battleSkin.AddMesh();
×
243
    var battleMaterialManager = battleWallModel.MaterialManager;
×
244

245
    var frontOfFloorImage = lazyImageMap["bacFrontOfFloor"].AssertNonnull();
×
246
    var frontOfFloorTexture
×
247
        = battleMaterialManager.CreateTexture(frontOfFloorImage);
×
248
    frontOfFloorTexture.WrapModeU = WrapMode.REPEAT;
×
249
    var frontOfFloorMaterial
×
250
        = battleMaterialManager.AddTextureMaterial(frontOfFloorTexture);
×
251

252
    battleWallMesh.AddSimpleWall(battleSkin,
×
253
                                 new Vector3(-32, -12, 160),
×
254
                                 new Vector3(32, -12, 0),
×
255
                                 frontOfFloorMaterial,
×
256
                                 battleWallRootBone,
×
257
                                 (-1, 1));
×
258
    battleWallMesh.AddSimpleWall(battleSkin,
×
259
                                 new Vector3(-32, 12, 160),
×
260
                                 new Vector3(32, 12, 0),
×
261
                                 frontOfFloorMaterial,
×
262
                                 battleWallRootBone,
×
263
                                 (-1, 1));
×
264

265
    var battleWallImage = lazyImageMap["bacBattleWall"].AssertNonnull();
×
266
    var battleWallTexture
×
267
        = battleMaterialManager.CreateTexture(battleWallImage);
×
268
    battleWallTexture.WrapModeV = WrapMode.REPEAT;
×
269
    var battleWallMaterial
×
270
        = battleMaterialManager.AddTextureMaterial(battleWallTexture);
×
271

272
    battleWallMesh.AddSimpleWall(battleSkin,
×
273
                                 new Vector3(-32, -12, 160),
×
274
                                 new Vector3(-32, 12, 0),
×
275
                                 battleWallMaterial,
×
276
                                 battleWallRootBone,
×
277
                                 (1, 6));
×
278

279
    return battleWallModel;
×
280
  }
×
281

282
  private static IModel CreateTreeModel_(
283
      IReadOnlyTreeDirectory rootDirectory) {
×
284
    var treeDirectory
×
285
        = rootDirectory.AssertGetExistingSubdir(
×
286
            "Models/Tree");
×
287

288
    var (treeModel, treeRootBone) = D3dModelImporter.CreateModel();
×
289
    var treeSkin = treeModel.Skin;
×
290
    var treeMaterialManager = treeModel.MaterialManager;
×
291

292
    // Bark
293
    {
×
294
      var barkRootBone = treeRootBone.AddChild(0, 0, 12);
×
295
      barkRootBone.Transform.SetRotationDegrees(0, 0, 45);
×
296
      barkRootBone.Transform.SetScale(1.5f, 1.5f, 2);
×
297

298
      var barkTexture = treeMaterialManager.CreateTexture(
×
299
          FinImage.FromFile(
×
300
              treeDirectory.AssertGetExistingFile("bacTree.png")));
×
301
      var barkMaterial = treeMaterialManager.AddTextureMaterial(barkTexture);
×
302
      D3dModelImporter.AddToModel(
×
303
          treeDirectory
×
304
              .AssertGetExistingFile("treemodel1.mod")
×
305
              .ReadNewFromText<D3d>(),
×
306
          treeModel,
×
307
          barkRootBone,
×
308
          out _,
×
309
          barkMaterial);
×
310
    }
×
311

312
    // Leaves
313
    {
×
314
      var leavesMesh = treeSkin.AddMesh();
×
315

316
      var leavesTexture = treeMaterialManager.CreateTexture(
×
317
          FinImage.FromFile(
×
318
              treeDirectory.AssertGetExistingFile("bacTreeLeaves1.png")));
×
319
      leavesTexture.WrapModeU = WrapMode.REPEAT;
×
320
      var leavesMaterial
×
321
          = treeMaterialManager.AddTextureMaterial(leavesTexture);
×
322

323
      var leavesRootBone = treeRootBone.AddChild(Vector3.Zero);
×
324

325
      var leavesBone1 = leavesRootBone.AddChild(new Vector3(0, 0, 12));
×
326
      leavesMesh.AddSimpleCylinder(treeSkin,
×
327
                                   new Vector3(-60, -60, 20),
×
328
                                   new Vector3(60, 60, 160),
×
329
                                   8,
×
330
                                   leavesMaterial,
×
331
                                   leavesBone1,
×
332
                                   (2, 1));
×
333

334
      var leavesBone2 = leavesRootBone.AddChild(new Vector3(0, 0, 12));
×
335
      leavesBone2.Transform.LocalEulerRadians = new Vector3(0, 0, MathF.PI / 2);
×
336
      leavesMesh.AddSimpleCylinder(treeSkin,
×
337
                                   new Vector3(-80, -80, 20),
×
338
                                   new Vector3(80, 80, 180),
×
339
                                   8,
×
340
                                   leavesMaterial,
×
341
                                   leavesBone2,
×
342
                                   (2, 1));
×
343

344
      var leavesBone3 = leavesRootBone.AddChild(new Vector3(0, 0, 30));
×
345
      leavesMesh.AddSimpleCylinder(treeSkin,
×
346
                                   new Vector3(-70, -70, 20),
×
347
                                   new Vector3(70, 70, 180),
×
348
                                   8,
×
349
                                   leavesMaterial,
×
350
                                   leavesBone3,
×
351
                                   (2, 1));
×
352
    }
×
353

354
    // Shadow
355
    {
×
356
      var shadowBone = treeRootBone.AddChild(0, 0, 0.05f);
×
357

358
      var shadowTexture = treeMaterialManager.CreateTexture(
×
359
          FinImage.FromFile(
×
360
              treeDirectory.AssertGetExistingFile("bacShadowXL.png")));
×
361
      var shadowMaterial
×
362
          = treeMaterialManager.AddTextureMaterial(shadowTexture);
×
363

364
      var shadowSize = 96;
×
365
      var shadowMesh = treeSkin.AddMesh();
×
366
      shadowMesh.AddSimpleFloor(
×
367
          treeSkin,
×
368
          new Vector3(-shadowSize, -shadowSize, 0),
×
369
          new Vector3(shadowSize, shadowSize, 0),
×
370
          shadowMaterial,
×
371
          shadowBone);
×
372
    }
×
373

374
    return treeModel;
×
375
  }
×
376

377
  private static IModel CreateSaveBlockModel_(
378
      IReadOnlyTreeDirectory rootDirectory) {
×
379
    var saveBlockDirectory
×
380
        = rootDirectory.AssertGetExistingSubdir(
×
381
            "Models/SaveBlock");
×
382

383
    var (saveBlockModel, saveBlockRootBone) = D3dModelImporter.CreateModel();
×
384
    var saveBlockSkin = saveBlockModel.Skin;
×
385
    var saveBlockMaterialManager = saveBlockModel.MaterialManager;
×
386

387
    var saveBlockFloatingBone = saveBlockRootBone.AddChild(0, 0, 39);
×
388

389
    // Star
390
    {
×
391
      var starBone = saveBlockFloatingBone.AddChild(0, 0, 0);
×
392

393
      var starAnimation = saveBlockModel.AnimationManager.AddAnimation();
×
394
      starAnimation.FrameRate = 30;
×
395

396
      var frameCount = 64;
×
397
      starAnimation.FrameCount = frameCount;
×
398

399
      var starBoneTracks = starAnimation.GetOrCreateBoneTracks(starBone);
×
400
      var starBoneRotations = starBoneTracks.UseSeparateEulerRadiansKeyframes();
×
401
      starBoneRotations.Axes[2].Add(new Keyframe<float>(0, 0));
×
402
      starBoneRotations.Axes[2]
×
403
                       .Add(new Keyframe<float>(frameCount / 2, MathF.PI));
×
404
      starBoneRotations.Axes[2]
×
405
                       .Add(new Keyframe<float>(frameCount, 2 * MathF.PI));
×
406

407
      var (starMaterial, _)
×
408
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
409
              saveBlockDirectory.AssertGetExistingFile("bacSaveBlockStar.png"));
×
410

411
      starMaterial.DiffuseColor = FinColor.FromAlphaFloat(.25f).ToSystemColor();
×
412

413
      saveBlockSkin
×
414
          .AddMesh()
×
415
          .AddSimpleWall(saveBlockSkin,
×
416
                         new Vector3(-8, 0, 16),
×
417
                         new Vector3(8, 0, 0),
×
418
                         starMaterial,
×
419
                         starBone);
×
420
    }
×
421

422
    // Block
423
    {
×
424
      var (saveBlockSideMaterial, _)
×
425
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
426
              saveBlockDirectory.AssertGetExistingFile("bacSaveBlock.png"));
×
427
      var (saveBlockTopBottomMaterial, _)
×
428
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
429
              saveBlockDirectory.AssertGetExistingFile(
×
430
                  "bacSaveBlockEmpty.png"));
×
431

432
      saveBlockSideMaterial.DiffuseColor
×
433
          = saveBlockTopBottomMaterial.DiffuseColor
×
434
              = FinColor.FromAlphaFloat(.7f).ToSystemColor();
×
435

436
      saveBlockSkin
×
437
          .AddMesh()
×
438
          .AddSimpleCube(saveBlockSkin,
×
439
                         new Vector3(-8, -8, 16),
×
440
                         new Vector3(8, 8, 0),
×
441
                         saveBlockTopBottomMaterial,
×
442
                         saveBlockSideMaterial,
×
443
                         saveBlockFloatingBone);
×
444
    }
×
445

446
    // Shadow
447
    {
×
448
      var shadowBone = saveBlockRootBone.AddChild(0, 0, 1);
×
449

450
      var shadowTexture = saveBlockMaterialManager.CreateTexture(
×
451
          FinImage.FromFile(rootDirectory.AssertGetExistingFile(
×
452
                                "Textures/bacSquareShadow.png")));
×
453
      var shadowMaterial
×
454
          = saveBlockMaterialManager.AddTextureMaterial(shadowTexture);
×
455

456
      var shadowSize = 9;
×
457
      var shadowMesh = saveBlockSkin.AddMesh();
×
458
      shadowMesh.AddSimpleFloor(
×
459
          saveBlockSkin,
×
460
          new Vector3(-shadowSize, -shadowSize, 0),
×
461
          new Vector3(shadowSize, shadowSize, 0),
×
462
          shadowMaterial,
×
463
          shadowBone);
×
464
    }
×
465

466
    return saveBlockModel;
×
467
  }
×
468
}
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