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

MeltyPlayer / MeltyTool / 23118567285

15 Mar 2026 08:23PM UTC coverage: 41.432% (-0.02%) from 41.449%
23118567285

push

github

MeltyPlayer
Improved accuracy of Labyrinth importer a bit.

7012 of 19003 branches covered (36.9%)

Branch coverage included in aggregate %.

0 of 41 new or added lines in 5 files covered. (0.0%)

29943 of 70192 relevant lines covered (42.66%)

62198.5 hits per line

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

0.0
/FinModelUtility/Games/TheLabyrinthPlusEdition/TheLabyrinthPlusEdition/src/api/ScbModelImporter.cs
1
using System.Numerics;
2

3
using fin.data.dictionaries;
4
using fin.io;
5
using fin.math.transform;
6
using fin.model;
7
using fin.model.impl;
8
using fin.model.io;
9
using fin.model.io.importers;
10
using fin.model.util;
11
using fin.util.sets;
12
using fin.util.strings;
13

14
using schema.text.reader;
15

16
using tlpe.scb;
17

18
namespace tlpe.api;
19

20
public sealed record ScbModelFileBundle(
×
21
    IReadOnlyTreeFile ScbFile,
×
22
    IReadOnlyTreeFile BallsFile,
×
23
    IReadOnlyTreeDirectory TexturesDir)
×
24
    : IModelFileBundle {
25
  public IReadOnlyTreeFile MainFile => this.ScbFile;
×
26
}
27

28
public sealed class ScbModelImporter : IModelImporter<ScbModelFileBundle> {
29
  public IModel Import(ScbModelFileBundle fileBundle) {
×
30
    var scb = fileBundle.ScbFile.ReadNew<Scb>();
×
31

32
    var files = fileBundle.ScbFile.AsFileSet();
×
33
    var finModel = new ModelImpl {
×
34
        FileBundle = fileBundle,
×
35
        Files = files
×
36
    };
×
37

38
    var allBallAttributes = ReadAllBallAttributes_(fileBundle.BallsFile);
×
39
    var ballAttributesByGeometry
×
40
        = allBallAttributes
×
NEW
41
            .ToListDictionary(a => a.Geometry?.ToLower() ?? "ball.scb");
×
42

43
    var textureFiles = new List<IReadOnlyTreeFile>();
×
44
    if (ballAttributesByGeometry.TryGetList(
×
45
            fileBundle.ScbFile.Name.ToString().ToLower(),
×
46
            out var matchingBallAttributes)) {
×
47
      foreach (var ballAttributes in matchingBallAttributes) {
×
48
        if (ballAttributes.Texture != null) {
×
49
          var textureFile = fileBundle.TexturesDir.AssertGetExistingFile(
×
50
              ballAttributes.Texture);
×
51
          textureFiles.Add(textureFile);
×
52
        }
×
53
      }
×
54
    }
×
55

56
    foreach (var scbSection in scb.Sections) {
×
57
      if (scbSection is Section6 section6) {
×
NEW
58
        var textureName = section6.TextureName;
×
59
        if (textureName.Length > 0) {
×
60
          var textureFile = fileBundle.ScbFile.AssertGetParent()
×
61
                                      .AssertGetExistingFile(textureName);
×
62
          textureFiles.Add(textureFile);
×
63
        }
×
64
      }
×
65
    }
×
66

67
    IMaterial[] textureMaterials
×
68
        = textureFiles
×
69
          .Select(textureFile => {
×
NEW
70
            (var finMaterial, var finTexture)
×
71
                = finModel.MaterialManager.AddSimpleTextureMaterialFromFile(
×
72
                    textureFile);
×
NEW
73

×
NEW
74
            finTexture.WrapModeU = finTexture.WrapModeV = WrapMode.REPEAT;
×
NEW
75

×
76
            return finMaterial;
×
77
          })
×
78
          .ToArray();
×
79

80
    var finMaterial = textureMaterials.Length > 0
×
81
        ? textureMaterials[0]
×
82
        : finModel.MaterialManager.AddNullMaterial();
×
83

84
    var finSkin = finModel.Skin;
×
85

NEW
86
    IBone? currentBone = null;
×
NEW
87
    IReadOnlyBoneWeights? currentBoneWeights = null;
×
88

NEW
89
    var finBoneByName = new CaseInvariantStringDictionary<IBone>();
×
90

91
    foreach (var scbSection in scb.Sections) {
×
92
      switch (scbSection) {
×
NEW
93
        case JointSection joint: {
×
NEW
94
          if (!finBoneByName.TryGetValue(joint.ParentName, out var parentBone)) {
×
NEW
95
            parentBone = finModel.Skeleton.Root;
×
NEW
96
          }
×
97

NEW
98
          currentBone = parentBone.AddChild(AdjustVector3_(joint.Translation));
×
NEW
99
          currentBone.Transform.SetRotationRadians(
×
NEW
100
              AdjustVector3_(joint.Rotation));
×
NEW
101
          currentBone.Transform.SetScale(AdjustVector3_(joint.Scale));
×
NEW
102
          currentBone.Name = joint.Name;
×
103

NEW
104
          finBoneByName[currentBone.Name] = currentBone;
×
105

NEW
106
          currentBoneWeights
×
NEW
107
              = finModel.Skin.GetOrCreateBoneWeights(
×
NEW
108
                  VertexSpace.RELATIVE_TO_BONE,
×
NEW
109
                  currentBone);
×
110

NEW
111
          break;
×
112
        }
113
        case MeshSection meshSection: {
×
114
          var finMesh = finSkin.AddMesh();
×
NEW
115
          finMesh.Name = currentBone?.Name;
×
116

117

118
          var scbVertices = meshSection.Vertices;
×
119
          var finVertices = new IReadOnlyVertex[scbVertices.Length];
×
120
          for (var i = 0; i < scbVertices.Length; ++i) {
×
121
            var scbVertex = scbVertices[i];
×
122

NEW
123
            var finVertex
×
NEW
124
                = finSkin.AddVertex(AdjustVector3_(scbVertex.Position));
×
125
            finVertex.SetLocalNormal(AdjustVector3_(scbVertex.Normal));
×
126
            finVertex.SetUv(0, AdjustVector2_(scbVertex.Uv0));
×
127
            finVertex.SetUv(1, AdjustVector2_(scbVertex.Uv1));
×
128

NEW
129
            if (currentBoneWeights != null) {
×
NEW
130
              finVertex.SetBoneWeights(currentBoneWeights);
×
NEW
131
            }
×
132

133
            finVertices[scbVertices.Length - 1 - i] = finVertex;
×
134
          }
×
135

136
          var triangles
×
137
              = meshSection
×
138
                .Faces.Select(f => {
×
139
                  var v0 = finVertices[f.Vertex0];
×
140
                  var v1 = finVertices[f.Vertex1];
×
141
                  var v2 = finVertices[f.Vertex2];
×
142

×
143
                  return (v0, v1, v2);
×
144
                })
×
145
                .ToArray();
×
146

147
          var finPrimitive = finMesh.AddTriangles(triangles);
×
148
          finPrimitive.SetMaterial(finMaterial);
×
149

150
          break;
×
151
        }
152
      }
153
    }
×
154

155
    return finModel;
×
156
  }
×
157

158
  private class BallAttributes {
159
    public string Id { get; set; }
×
160
    public string? Geometry { get; set; }
×
161
    public string? Texture { get; set; }
×
162
    public bool MapFromFile { get; set; }
×
163
    public bool NoEnvMap { get; set; }
×
164
  }
165

166
  private static Vector2 AdjustVector2_(Vector2 input)
167
    => input with { Y = 1 - input.Y };
×
168

169
  private static Vector3 AdjustVector3_(Vector3 input)
170
    => new(input.X, input.Z, input.Y);
×
171

172
  private static IList<BallAttributes> ReadAllBallAttributes_(
173
      IReadOnlyTreeFile file) {
×
174
    using var tr = new SchemaTextReader(file.OpenRead());
×
175

176
    var allAttributes = new List<BallAttributes>();
×
177

178
    BallAttributes currentAttributes = default!;
×
179
    while (!tr.Eof) {
×
180
      var line = tr.ReadLine().Trim();
×
181

182
      if (line.StartsWith('[')) {
×
183
        var id = line[1..].SubstringUpTo(']');
×
184
        currentAttributes = new BallAttributes { Id = id };
×
185
        allAttributes.Add(currentAttributes);
×
186
      } else {
×
187
        var equalsParts = line.Split('=');
×
188
        if (equalsParts.Length > 1) {
×
189
          var before = equalsParts[0].Trim();
×
190
          var after = equalsParts[1].Trim();
×
191

192
          switch (before) {
×
193
            case "Geometry": {
×
194
              currentAttributes.Geometry = after;
×
195
              break;
×
196
            }
197
            case "MapFromFile": {
×
198
              currentAttributes.MapFromFile = after == "1";
×
199
              break;
×
200
            }
201
            case "NoEnvMap": {
×
202
              currentAttributes.NoEnvMap = after == "1";
×
203
              break;
×
204
            }
205
            case "Texture": {
×
206
              currentAttributes.Texture = after;
×
207
              break;
×
208
            }
209
          }
210
        }
×
211
      }
×
212
    }
×
213

214
    return allAttributes;
×
215
  }
×
216
}
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