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

MeltyPlayer / MeltyTool / 21816503747

09 Feb 2026 07:43AM UTC coverage: 41.525% (-0.01%) from 41.539%
21816503747

push

github

MeltyPlayer
Finally found more of my old PMDC files!!

6862 of 18666 branches covered (36.76%)

Branch coverage included in aggregate %.

0 of 62 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

29430 of 68731 relevant lines covered (42.82%)

63293.29 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.io.importers.assimp;
12
using fin.model.util;
13
using fin.scene;
14
using fin.ui.rendering.gl.scene;
15
using fin.util.asserts;
16
using fin.util.enums;
17
using fin.util.sets;
18

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

22
using pmdc.schema.lvl;
23

24

25
namespace pmdc.api;
26

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

30
  public required IReadOnlyTreeFile LvlFile { get; init; }
×
31
  public required IReadOnlyTreeDirectory RootDirectory { get; init; }
×
32
}
33

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

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

45
    var finArea = finScene.AddArea();
×
46

NEW
47
    var roomName = sceneFileBundle.LvlFile.AssertGetParent().Name;
×
48
    if (lvl.HasRoomModel) {
×
NEW
49
      if (lvlFile.AssertGetParent()
×
NEW
50
                 .TryToGetExistingFile("model.omd",
×
NEW
51
                                       out var omdFile)) {
×
NEW
52
        files.Add(omdFile);
×
NEW
53
        var roomModel = new OmdModelImporter().Import(new OmdModelFileBundle {
×
NEW
54
            OmdFile = omdFile
×
NEW
55
        });
×
NEW
56
        finArea.AddRootNode().AddSceneModel(roomModel);
×
NEW
57
      } else if (lvlFile.AssertGetParent()
×
NEW
58
                        .TryToGetExistingFile($"{roomName}.obj",
×
NEW
59
                                              out var objFile)) {
×
NEW
60
        files.Add(objFile);
×
NEW
61
        var roomModel = new AssimpModelImporter().Import(
×
NEW
62
            new AssimpModelFileBundle {
×
NEW
63
                MainFile = objFile
×
NEW
64
            });
×
NEW
65
        finArea.AddRootNode()
×
NEW
66
               .SetRotationDegrees(-90, 180, 0)
×
NEW
67
               .SetScale(lvl.RoomScale)
×
NEW
68
               .AddSceneModel(roomModel);
×
NEW
69
      }
×
UNCOV
70
    }
×
71

72
    var textureDirectory
×
73
        = sceneFileBundle.RootDirectory.AssertGetExistingSubdir("Textures");
×
NEW
74
    var lazyImageMap = new LazyDictionary<string?, IImage?>(imageName
×
NEW
75
        => imageName != null &&
×
NEW
76
           textureDirectory.TryToGetExistingFile(
×
NEW
77
               $"{imageName}.png",
×
NEW
78
               out var textureFile)
×
79
            ? FinImage.FromFile(textureFile)
×
80
            : null);
×
81

82
    if (lvl.FloorBlocks.Count > 0) {
×
83
      foreach (var floorBlockParams in lvl.FloorBlocks) {
×
84
        var (start, end, textureName, type, flags) = floorBlockParams;
×
85

86
        var (floorBlockModel, floorBlockRootBone)
×
87
            = D3dModelImporter.CreateModel();
×
88
        var floorBlockSkin = floorBlockModel.Skin;
×
89
        var floorBlockMesh = floorBlockSkin.AddMesh();
×
90

91
        var shouldRepeat = !flags.CheckFlag(FloorBlockFlags.NO_REPEAT);
×
92
        var floorBlockMaterialManager = floorBlockModel.MaterialManager;
×
93
        IMaterial? floorBlockMaterial = null;
×
94
        if (flags.CheckFlag(FloorBlockFlags.INVISIBLE)) {
×
95
          floorBlockMaterial = floorBlockMaterialManager.AddHiddenMaterial();
×
96
        } else {
×
97
          var image = lazyImageMap[textureName];
×
98
          if (image != null) {
×
99
            (floorBlockMaterial, var floorBlockTexture)
×
100
                = floorBlockMaterialManager.AddSimpleTextureMaterialFromImage(
×
101
                    image,
×
102
                    textureName);
×
103

104
            if (shouldRepeat) {
×
105
              floorBlockTexture.WrapModeU = WrapMode.REPEAT;
×
106
              floorBlockTexture.WrapModeV = WrapMode.REPEAT;
×
107
            }
×
108
          }
×
109
        }
×
110

111
        switch (type) {
×
112
          case FloorBlockType.WALL: {
×
113
            (float, float)? repeat = shouldRepeat
×
114
                ? ((end.Xy() - start.Xy()).Length() / 64,
×
115
                   Math.Abs(end.Z - start.Z) / 64)
×
116
                : null;
×
117
            floorBlockMesh.AddSimpleWall(floorBlockSkin,
×
118
                                         start,
×
119
                                         end,
×
120
                                         floorBlockMaterial,
×
121
                                         floorBlockRootBone,
×
122
                                         repeat);
×
123
            break;
×
124
          }
125
          case FloorBlockType.FLOOR: {
×
126
            (float, float, float)? repeat = shouldRepeat
×
127
                ? (Math.Abs(end.X - start.X) / 64,
×
128
                   Math.Abs(end.Y - start.Y) / 64,
×
129
                   Math.Abs(end.Z - start.Z) / 64)
×
130
                : null;
×
131

132
            floorBlockMesh.AddSimpleCube(floorBlockSkin,
×
133
                                         start,
×
134
                                         end,
×
135
                                         floorBlockMaterial,
×
136
                                         floorBlockRootBone,
×
137
                                         repeat);
×
138
            break;
×
139
          }
140
        }
141

142
        finArea.AddRootNode().AddSceneModel(floorBlockModel);
×
143
      }
×
144
    }
×
145

146
    var characterDirectory
×
147
        = sceneFileBundle.RootDirectory.AssertGetExistingSubdir("Characters");
×
148

149
    var lazyCharacterModels
×
150
        = new LazyCaseInvariantStringDictionary<IReadOnlyModel>(characterType
×
151
            => new PmdcCharacterModelImporter().Import(
×
152
                new PmdcCharacterModelFileBundle {
×
153
                    AnimationImageFiles = characterDirectory
×
154
                                          .AssertGetExistingSubdir(
×
155
                                              characterType)
×
156
                                          .GetFilesWithFileType(".gif")
×
157
                                          .ToArray(),
×
158
                    CharactersDirectory = characterDirectory,
×
159
                }));
×
160

161
    if (lvl.Enemies.Count > 0) {
×
162
      foreach (var (npcPosition, npcCharacterType) in lvl.Enemies) {
×
163
        finArea.AddRootNode()
×
164
               .SetPosition(npcPosition.X, npcPosition.Z, npcPosition.Y)
×
165
               .AddComponent(
×
166
                   new SimpleModelRenderComponent(
×
167
                       lazyCharacterModels[npcCharacterType]));
×
168
      }
×
169
    }
×
170

171
    if (lvl.Npcs.Count > 0) {
×
172
      foreach (var (npcPosition, npcName, npcCharacterType) in lvl.Npcs) {
×
173
        var npcNode
×
174
            = finArea.AddRootNode()
×
175
                     .SetPosition(npcPosition.X, npcPosition.Z, npcPosition.Y)
×
176
                     .AddComponent(
×
177
                         new SimpleModelRenderComponent(
×
178
                             lazyCharacterModels[npcCharacterType]));
×
179
        npcNode.Name = npcName;
×
180
      }
×
181
    }
×
182

183
    if (lvl.SaveBlocks.Count > 0) {
×
184
      var saveBlockModel = CreateSaveBlockModel_(sceneFileBundle.RootDirectory);
×
185

186
      foreach (var saveBlockPosition in lvl.SaveBlocks) {
×
187
        finArea.AddRootNode()
×
NEW
188
               .SetPosition(saveBlockPosition.X,
×
NEW
189
                            saveBlockPosition.Z,
×
NEW
190
                            saveBlockPosition.Y)
×
191
               .AddSceneModel(saveBlockModel);
×
192
      }
×
193
    }
×
194

195
    if (lvl.SaveBlocks.Count > 0) {
×
196
      var saveBlockModel = CreateSaveBlockModel_(sceneFileBundle.RootDirectory);
×
197

198
      foreach (var saveBlockPosition in lvl.SaveBlocks) {
×
199
        finArea.AddRootNode()
×
NEW
200
               .SetPosition(saveBlockPosition.X,
×
NEW
201
                            saveBlockPosition.Z,
×
NEW
202
                            saveBlockPosition.Y)
×
203
               .AddSceneModel(saveBlockModel);
×
204
      }
×
205
    }
×
206

207
    if (lvl.Trees.Count > 0) {
×
208
      var treeModel = CreateTreeModel_(sceneFileBundle.RootDirectory);
×
209

210
      foreach (var treePosition in lvl.Trees) {
×
211
        finArea.AddRootNode()
×
212
               .SetPosition(treePosition.X, treePosition.Z, treePosition.Y)
×
213
               .AddSceneModel(treeModel);
×
214
      }
×
215
    }
×
216

217
    if (sceneFileBundle.LvlFile.Name is "battle.lvl") {
×
218
      var battleWallModel = CreateBattleWallModel_(lazyImageMap);
×
219

220
      finArea.AddRootNode()
×
221
             .SetPosition(176, 0, 176)
×
222
             .AddSceneModel(battleWallModel);
×
223
      finArea.AddRootNode()
×
224
             .SetPosition(176, 0, 464)
×
225
             .AddSceneModel(battleWallModel);
×
226

227
      var (battleFloorModel, battleFloorRootBone)
×
228
          = D3dModelImporter.CreateModel();
×
229

230
      var bfSkin = battleFloorModel.Skin;
×
231
      var bfMesh = bfSkin.AddMesh();
×
232
      var bfMaterialManager = battleFloorModel.MaterialManager;
×
233

234
      var frontOfFloorImage = lazyImageMap["bacFrontOfFloor"].AssertNonnull();
×
235
      var frontOfFloorTexture
×
236
          = bfMaterialManager.CreateTexture(frontOfFloorImage);
×
237
      frontOfFloorTexture.WrapModeV = WrapMode.REPEAT;
×
238
      var frontOfFloorMaterial
×
239
          = bfMaterialManager.AddTextureMaterial(frontOfFloorTexture);
×
240

241
      bfMesh.AddSimpleFloor(bfSkin,
×
242
                            new Vector3(0, 16, -64),
×
243
                            new Vector3(64, 640, 0),
×
244
                            frontOfFloorMaterial,
×
245
                            battleFloorRootBone,
×
246
                            (1, 10));
×
247
      finArea.AddRootNode()
×
248
             .AddSceneModel(battleFloorModel);
×
249
    }
×
250

251
    if (lvl.BackgroundName != null) {
×
252
      var backgroundImageFile
×
253
          = sceneFileBundle
×
254
            .RootDirectory.AssertGetExistingSubdir("Backgrounds")
×
255
            .AssertGetExistingFile($"{lvl.BackgroundName}.png");
×
256
      finArea.BackgroundImage = FinImage.FromFile(backgroundImageFile);
×
257
      finArea.CreateCustomSkyboxNode();
×
258
    }
×
259

260
    finScene.CreateDefaultLighting(finArea.AddRootNode());
×
261

262
    return finScene;
×
263
  }
×
264

265
  private static IModel CreateBattleWallModel_(
266
      ILazyDictionary<string?, IImage?> lazyImageMap) {
×
267
    var (battleWallModel, battleWallRootBone)
×
268
        = D3dModelImporter.CreateModel();
×
269

270
    var battleSkin = battleWallModel.Skin;
×
271
    var battleWallMesh = battleSkin.AddMesh();
×
272
    var battleMaterialManager = battleWallModel.MaterialManager;
×
273

274
    var frontOfFloorImage = lazyImageMap["bacFrontOfFloor"].AssertNonnull();
×
275
    var frontOfFloorTexture
×
276
        = battleMaterialManager.CreateTexture(frontOfFloorImage);
×
277
    frontOfFloorTexture.WrapModeU = WrapMode.REPEAT;
×
278
    var frontOfFloorMaterial
×
279
        = battleMaterialManager.AddTextureMaterial(frontOfFloorTexture);
×
280

281
    battleWallMesh.AddSimpleWall(battleSkin,
×
282
                                 new Vector3(-32, -12, 160),
×
283
                                 new Vector3(32, -12, 0),
×
284
                                 frontOfFloorMaterial,
×
285
                                 battleWallRootBone,
×
286
                                 (-1, 1));
×
287
    battleWallMesh.AddSimpleWall(battleSkin,
×
288
                                 new Vector3(-32, 12, 160),
×
289
                                 new Vector3(32, 12, 0),
×
290
                                 frontOfFloorMaterial,
×
291
                                 battleWallRootBone,
×
292
                                 (-1, 1));
×
293

294
    var battleWallImage = lazyImageMap["bacBattleWall"].AssertNonnull();
×
295
    var battleWallTexture
×
296
        = battleMaterialManager.CreateTexture(battleWallImage);
×
297
    battleWallTexture.WrapModeV = WrapMode.REPEAT;
×
298
    var battleWallMaterial
×
299
        = battleMaterialManager.AddTextureMaterial(battleWallTexture);
×
300

301
    battleWallMesh.AddSimpleWall(battleSkin,
×
302
                                 new Vector3(-32, -12, 160),
×
303
                                 new Vector3(-32, 12, 0),
×
304
                                 battleWallMaterial,
×
305
                                 battleWallRootBone,
×
306
                                 (1, 6));
×
307

308
    return battleWallModel;
×
309
  }
×
310

311
  private static IModel CreateTreeModel_(
312
      IReadOnlyTreeDirectory rootDirectory) {
×
313
    var treeDirectory
×
314
        = rootDirectory.AssertGetExistingSubdir(
×
315
            "Models/Tree");
×
316

317
    var (treeModel, treeRootBone) = D3dModelImporter.CreateModel();
×
318
    var treeSkin = treeModel.Skin;
×
319
    var treeMaterialManager = treeModel.MaterialManager;
×
320

321
    // Bark
322
    {
×
323
      var barkRootBone = treeRootBone.AddChild(0, 0, 12);
×
324
      barkRootBone.Transform.SetRotationDegrees(0, 0, 45);
×
325
      barkRootBone.Transform.SetScale(1.5f, 1.5f, 2);
×
326

327
      var barkTexture = treeMaterialManager.CreateTexture(
×
328
          FinImage.FromFile(
×
329
              treeDirectory.AssertGetExistingFile("bacTree.png")));
×
330
      var barkMaterial = treeMaterialManager.AddTextureMaterial(barkTexture);
×
331
      D3dModelImporter.AddToModel(
×
332
          treeDirectory
×
333
              .AssertGetExistingFile("treemodel1.mod")
×
334
              .ReadNewFromText<D3d>(),
×
335
          treeModel,
×
336
          barkRootBone,
×
337
          out _,
×
338
          barkMaterial);
×
339
    }
×
340

341
    // Leaves
342
    {
×
343
      var leavesMesh = treeSkin.AddMesh();
×
344

345
      var leavesTexture = treeMaterialManager.CreateTexture(
×
346
          FinImage.FromFile(
×
347
              treeDirectory.AssertGetExistingFile("bacTreeLeaves1.png")));
×
348
      leavesTexture.WrapModeU = WrapMode.REPEAT;
×
349
      var leavesMaterial
×
350
          = treeMaterialManager.AddTextureMaterial(leavesTexture);
×
351

352
      var leavesRootBone = treeRootBone.AddChild(Vector3.Zero);
×
353

354
      var leavesBone1 = leavesRootBone.AddChild(new Vector3(0, 0, 12));
×
355
      leavesMesh.AddSimpleCylinder(treeSkin,
×
356
                                   new Vector3(-60, -60, 20),
×
357
                                   new Vector3(60, 60, 160),
×
358
                                   8,
×
359
                                   leavesMaterial,
×
360
                                   leavesBone1,
×
361
                                   (2, 1));
×
362

363
      var leavesBone2 = leavesRootBone.AddChild(new Vector3(0, 0, 12));
×
364
      leavesBone2.Transform.LocalEulerRadians = new Vector3(0, 0, MathF.PI / 2);
×
365
      leavesMesh.AddSimpleCylinder(treeSkin,
×
366
                                   new Vector3(-80, -80, 20),
×
367
                                   new Vector3(80, 80, 180),
×
368
                                   8,
×
369
                                   leavesMaterial,
×
370
                                   leavesBone2,
×
371
                                   (2, 1));
×
372

373
      var leavesBone3 = leavesRootBone.AddChild(new Vector3(0, 0, 30));
×
374
      leavesMesh.AddSimpleCylinder(treeSkin,
×
375
                                   new Vector3(-70, -70, 20),
×
376
                                   new Vector3(70, 70, 180),
×
377
                                   8,
×
378
                                   leavesMaterial,
×
379
                                   leavesBone3,
×
380
                                   (2, 1));
×
381
    }
×
382

383
    // Shadow
384
    {
×
385
      var shadowBone = treeRootBone.AddChild(0, 0, 0.05f);
×
386

387
      var shadowTexture = treeMaterialManager.CreateTexture(
×
388
          FinImage.FromFile(
×
389
              treeDirectory.AssertGetExistingFile("bacShadowXL.png")));
×
390
      var shadowMaterial
×
391
          = treeMaterialManager.AddTextureMaterial(shadowTexture);
×
392

393
      var shadowSize = 96;
×
394
      var shadowMesh = treeSkin.AddMesh();
×
395
      shadowMesh.AddSimpleFloor(
×
396
          treeSkin,
×
397
          new Vector3(-shadowSize, -shadowSize, 0),
×
398
          new Vector3(shadowSize, shadowSize, 0),
×
399
          shadowMaterial,
×
400
          shadowBone);
×
401
    }
×
402

403
    return treeModel;
×
404
  }
×
405

406
  private static IModel CreateSaveBlockModel_(
407
      IReadOnlyTreeDirectory rootDirectory) {
×
408
    var saveBlockDirectory
×
409
        = rootDirectory.AssertGetExistingSubdir(
×
410
            "Models/SaveBlock");
×
411

412
    var (saveBlockModel, saveBlockRootBone) = D3dModelImporter.CreateModel();
×
413
    var saveBlockSkin = saveBlockModel.Skin;
×
414
    var saveBlockMaterialManager = saveBlockModel.MaterialManager;
×
415

416
    var saveBlockFloatingBone = saveBlockRootBone.AddChild(0, 0, 39);
×
417

418
    // Star
419
    {
×
420
      var starBone = saveBlockFloatingBone.AddChild(0, 0, 0);
×
421

422
      var starAnimation = saveBlockModel.AnimationManager.AddAnimation();
×
423
      starAnimation.FrameRate = 30;
×
424

425
      var frameCount = 64;
×
426
      starAnimation.FrameCount = frameCount;
×
427

428
      var starBoneTracks = starAnimation.GetOrCreateBoneTracks(starBone);
×
429
      var starBoneRotations = starBoneTracks.UseSeparateEulerRadiansKeyframes();
×
430
      starBoneRotations.Axes[2].Add(new Keyframe<float>(0, 0));
×
431
      starBoneRotations.Axes[2]
×
432
                       .Add(new Keyframe<float>(frameCount / 2, MathF.PI));
×
433
      starBoneRotations.Axes[2]
×
434
                       .Add(new Keyframe<float>(frameCount, 2 * MathF.PI));
×
435

436
      var (starMaterial, _)
×
437
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
438
              saveBlockDirectory.AssertGetExistingFile("bacSaveBlockStar.png"));
×
439

440
      starMaterial.DiffuseColor = FinColor.FromAlphaFloat(.25f).ToSystemColor();
×
441

442
      saveBlockSkin
×
443
          .AddMesh()
×
444
          .AddSimpleWall(saveBlockSkin,
×
445
                         new Vector3(-8, 0, 16),
×
446
                         new Vector3(8, 0, 0),
×
447
                         starMaterial,
×
448
                         starBone);
×
449
    }
×
450

451
    // Block
452
    {
×
453
      var (saveBlockSideMaterial, _)
×
454
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
455
              saveBlockDirectory.AssertGetExistingFile("bacSaveBlock.png"));
×
456
      var (saveBlockTopBottomMaterial, _)
×
457
          = saveBlockMaterialManager.AddSimpleTextureMaterialFromFile(
×
458
              saveBlockDirectory.AssertGetExistingFile(
×
459
                  "bacSaveBlockEmpty.png"));
×
460

461
      saveBlockSideMaterial.DiffuseColor
×
462
          = saveBlockTopBottomMaterial.DiffuseColor
×
463
              = FinColor.FromAlphaFloat(.7f).ToSystemColor();
×
464

465
      saveBlockSkin
×
466
          .AddMesh()
×
467
          .AddSimpleCube(saveBlockSkin,
×
468
                         new Vector3(-8, -8, 16),
×
469
                         new Vector3(8, 8, 0),
×
470
                         saveBlockTopBottomMaterial,
×
471
                         saveBlockSideMaterial,
×
472
                         saveBlockFloatingBone);
×
473
    }
×
474

475
    // Shadow
476
    {
×
477
      var shadowBone = saveBlockRootBone.AddChild(0, 0, 1);
×
478

479
      var shadowTexture = saveBlockMaterialManager.CreateTexture(
×
480
          FinImage.FromFile(rootDirectory.AssertGetExistingFile(
×
481
                                "Textures/bacSquareShadow.png")));
×
482
      var shadowMaterial
×
483
          = saveBlockMaterialManager.AddTextureMaterial(shadowTexture);
×
484

485
      var shadowSize = 9;
×
486
      var shadowMesh = saveBlockSkin.AddMesh();
×
487
      shadowMesh.AddSimpleFloor(
×
488
          saveBlockSkin,
×
489
          new Vector3(-shadowSize, -shadowSize, 0),
×
490
          new Vector3(shadowSize, shadowSize, 0),
×
491
          shadowMaterial,
×
492
          shadowBone);
×
493
    }
×
494

495
    return saveBlockModel;
×
496
  }
×
497
}
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