@erik9994857/cag 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,320 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ class BBModelParser {
5
+ constructor() {
6
+ this.supportedFormatVersions = ["4.0", "4.5", "4.6", "4.7", "4.8", "4.9", "4.10"];
7
+ this.faceNames = ["north", "south", "east", "west", "up", "down"];
8
+ }
9
+
10
+ parse(filePath) {
11
+ if (!fs.existsSync(filePath)) {
12
+ throw new Error("BBModel file not found: " + filePath);
13
+ }
14
+
15
+ var raw = fs.readFileSync(filePath, "utf-8");
16
+ var data;
17
+
18
+ try {
19
+ data = JSON.parse(raw);
20
+ } catch (e) {
21
+ throw new Error("Invalid BBModel JSON: " + filePath);
22
+ }
23
+
24
+ var result = {
25
+ filePath: filePath,
26
+ fileName: path.basename(filePath, ".bbmodel"),
27
+ formatVersion: this.extractFormatVersion(data),
28
+ name: data.name || path.basename(filePath, ".bbmodel"),
29
+ resolution: this.extractResolution(data),
30
+ elements: this.extractElements(data),
31
+ textures: this.extractTextures(data),
32
+ outliner: this.extractOutliner(data),
33
+ animations: this.extractAnimations(data),
34
+ uvMappings: [],
35
+ valid: false
36
+ };
37
+
38
+ result.uvMappings = this.extractAllUVMappings(result.elements);
39
+ result.valid = this.validate(result);
40
+
41
+ return result;
42
+ }
43
+
44
+ extractFormatVersion(data) {
45
+ if (data.meta && data.meta.format_version) {
46
+ return data.meta.format_version;
47
+ }
48
+
49
+ return "unknown";
50
+ }
51
+
52
+ extractResolution(data) {
53
+ if (data.resolution) {
54
+ return {
55
+ width: data.resolution.width || 16,
56
+ height: data.resolution.height || 16
57
+ };
58
+ }
59
+
60
+ return { width: 16, height: 16 };
61
+ }
62
+
63
+ extractElements(data) {
64
+ if (!data.elements || !Array.isArray(data.elements)) {
65
+ return [];
66
+ }
67
+
68
+ var elements = [];
69
+
70
+ for (var i = 0; i < data.elements.length; i++) {
71
+ var el = data.elements[i];
72
+
73
+ var element = {
74
+ name: el.name || "element_" + i,
75
+ type: el.type || "cube",
76
+ from: el.from || [0, 0, 0],
77
+ to: el.to || [1, 1, 1],
78
+ rotation: el.rotation || [0, 0, 0],
79
+ origin: el.origin || [0, 0, 0],
80
+ inflate: el.inflate || 0,
81
+ visibility: el.visibility !== undefined ? el.visibility : true,
82
+ faces: this.extractFaces(el),
83
+ uuid: el.uuid || null
84
+ };
85
+
86
+ elements.push(element);
87
+ }
88
+
89
+ return elements;
90
+ }
91
+
92
+ extractFaces(element) {
93
+ var faces = {};
94
+
95
+ if (!element.faces) {
96
+ for (var i = 0; i < this.faceNames.length; i++) {
97
+ faces[this.faceNames[i]] = {
98
+ uv: [0, 0, 16, 16],
99
+ texture: 0,
100
+ rotation: 0
101
+ };
102
+ }
103
+ return faces;
104
+ }
105
+
106
+ for (var j = 0; j < this.faceNames.length; j++) {
107
+ var faceName = this.faceNames[j];
108
+
109
+ if (element.faces[faceName]) {
110
+ var face = element.faces[faceName];
111
+ faces[faceName] = {
112
+ uv: face.uv || [0, 0, 16, 16],
113
+ texture: face.texture !== undefined ? face.texture : 0,
114
+ rotation: face.rotation || 0,
115
+ tint: face.tint || -1
116
+ };
117
+ } else {
118
+ faces[faceName] = {
119
+ uv: [0, 0, 16, 16],
120
+ texture: 0,
121
+ rotation: 0
122
+ };
123
+ }
124
+ }
125
+
126
+ return faces;
127
+ }
128
+
129
+ extractTextures(data) {
130
+ if (!data.textures || !Array.isArray(data.textures)) {
131
+ return [];
132
+ }
133
+
134
+ var textures = [];
135
+
136
+ for (var i = 0; i < data.textures.length; i++) {
137
+ var tex = data.textures[i];
138
+
139
+ textures.push({
140
+ id: tex.id !== undefined ? tex.id : i,
141
+ name: tex.name || "texture_" + i,
142
+ folder: tex.folder || "",
143
+ namespace: tex.namespace || "",
144
+ source: tex.source || null,
145
+ width: tex.width || 0,
146
+ height: tex.height || 0,
147
+ uvWidth: tex.uv_width || 16,
148
+ uvHeight: tex.uv_height || 16
149
+ });
150
+ }
151
+
152
+ return textures;
153
+ }
154
+
155
+ extractOutliner(data) {
156
+ if (!data.outliner || !Array.isArray(data.outliner)) {
157
+ return [];
158
+ }
159
+
160
+ return this.parseOutlinerNodes(data.outliner);
161
+ }
162
+
163
+ parseOutlinerNodes(nodes) {
164
+ var result = [];
165
+
166
+ for (var i = 0; i < nodes.length; i++) {
167
+ var node = nodes[i];
168
+
169
+ if (typeof node === "string") {
170
+ result.push({
171
+ type: "element_ref",
172
+ uuid: node
173
+ });
174
+ } else if (typeof node === "object" && node !== null) {
175
+ var bone = {
176
+ type: "group",
177
+ name: node.name || "group_" + i,
178
+ origin: node.origin || [0, 0, 0],
179
+ rotation: node.rotation || [0, 0, 0],
180
+ visibility: node.visibility !== undefined ? node.visibility : true,
181
+ uuid: node.uuid || null,
182
+ children: []
183
+ };
184
+
185
+ if (node.children && Array.isArray(node.children)) {
186
+ bone.children = this.parseOutlinerNodes(node.children);
187
+ }
188
+
189
+ result.push(bone);
190
+ }
191
+ }
192
+
193
+ return result;
194
+ }
195
+
196
+ extractAnimations(data) {
197
+ if (!data.animations || !Array.isArray(data.animations)) {
198
+ return [];
199
+ }
200
+
201
+ var animations = [];
202
+
203
+ for (var i = 0; i < data.animations.length; i++) {
204
+ var anim = data.animations[i];
205
+
206
+ animations.push({
207
+ name: anim.name || "animation_" + i,
208
+ loop: anim.loop || "once",
209
+ length: anim.length || 0,
210
+ snapping: anim.snapping || 24
211
+ });
212
+ }
213
+
214
+ return animations;
215
+ }
216
+
217
+ extractAllUVMappings(elements) {
218
+ var mappings = [];
219
+
220
+ for (var i = 0; i < elements.length; i++) {
221
+ var element = elements[i];
222
+ var faceKeys = Object.keys(element.faces);
223
+
224
+ for (var j = 0; j < faceKeys.length; j++) {
225
+ var faceName = faceKeys[j];
226
+ var face = element.faces[faceName];
227
+
228
+ mappings.push({
229
+ element: element.name,
230
+ elementIndex: i,
231
+ face: faceName,
232
+ uv: face.uv.slice(),
233
+ textureIndex: face.texture,
234
+ rotation: face.rotation || 0,
235
+ supported: true
236
+ });
237
+ }
238
+ }
239
+
240
+ return mappings;
241
+ }
242
+
243
+ validate(result) {
244
+ if (!result.elements || result.elements.length === 0) {
245
+ return false;
246
+ }
247
+
248
+ for (var i = 0; i < result.elements.length; i++) {
249
+ var el = result.elements[i];
250
+
251
+ if (!el.from || el.from.length !== 3) {
252
+ return false;
253
+ }
254
+
255
+ if (!el.to || el.to.length !== 3) {
256
+ return false;
257
+ }
258
+ }
259
+
260
+ for (var j = 0; j < result.uvMappings.length; j++) {
261
+ var mapping = result.uvMappings[j];
262
+
263
+ if (!mapping.uv || mapping.uv.length !== 4) {
264
+ return false;
265
+ }
266
+ }
267
+
268
+ return true;
269
+ }
270
+
271
+ getElementByName(parsedModel, name) {
272
+ for (var i = 0; i < parsedModel.elements.length; i++) {
273
+ if (parsedModel.elements[i].name === name) {
274
+ return parsedModel.elements[i];
275
+ }
276
+ }
277
+
278
+ return null;
279
+ }
280
+
281
+ getUVMappingsForElement(parsedModel, elementName) {
282
+ var result = [];
283
+
284
+ for (var i = 0; i < parsedModel.uvMappings.length; i++) {
285
+ if (parsedModel.uvMappings[i].element === elementName) {
286
+ result.push(parsedModel.uvMappings[i]);
287
+ }
288
+ }
289
+
290
+ return result;
291
+ }
292
+
293
+ calculateBounds(parsedModel) {
294
+ if (parsedModel.elements.length === 0) {
295
+ return { min: [0, 0, 0], max: [0, 0, 0], size: [0, 0, 0] };
296
+ }
297
+
298
+ var min = [Infinity, Infinity, Infinity];
299
+ var max = [-Infinity, -Infinity, -Infinity];
300
+
301
+ for (var i = 0; i < parsedModel.elements.length; i++) {
302
+ var el = parsedModel.elements[i];
303
+
304
+ for (var a = 0; a < 3; a++) {
305
+ if (el.from[a] < min[a]) min[a] = el.from[a];
306
+ if (el.to[a] < min[a]) min[a] = el.to[a];
307
+ if (el.from[a] > max[a]) max[a] = el.from[a];
308
+ if (el.to[a] > max[a]) max[a] = el.to[a];
309
+ }
310
+ }
311
+
312
+ return {
313
+ min: min,
314
+ max: max,
315
+ size: [max[0] - min[0], max[1] - min[1], max[2] - min[2]]
316
+ };
317
+ }
318
+ }
319
+
320
+ module.exports = BBModelParser;
@@ -0,0 +1,315 @@
1
+ class UVMapper {
2
+ constructor(resolution) {
3
+ this.resolution = resolution || { width: 16, height: 16 };
4
+ this.mappings = [];
5
+ this.fixLog = [];
6
+ }
7
+
8
+ setResolution(width, height) {
9
+ this.resolution = { width: width, height: height };
10
+ return this;
11
+ }
12
+
13
+ getResolution() {
14
+ return this.resolution;
15
+ }
16
+
17
+ mapFace(elementName, faceName, uv, textureIndex) {
18
+ var mapping = {
19
+ element: elementName,
20
+ face: faceName,
21
+ uv: uv ? uv.slice() : [0, 0, this.resolution.width, this.resolution.height],
22
+ textureIndex: textureIndex !== undefined ? textureIndex : 0,
23
+ normalized: false,
24
+ fixed: false,
25
+ supported: true
26
+ };
27
+
28
+ this.mappings.push(mapping);
29
+ return mapping;
30
+ }
31
+
32
+ mapElement(element) {
33
+ var result = [];
34
+ var faceNames = ["north", "south", "east", "west", "up", "down"];
35
+
36
+ for (var i = 0; i < faceNames.length; i++) {
37
+ var faceName = faceNames[i];
38
+
39
+ if (element.faces && element.faces[faceName]) {
40
+ var face = element.faces[faceName];
41
+ var mapping = this.mapFace(
42
+ element.name || "unnamed",
43
+ faceName,
44
+ face.uv,
45
+ face.texture
46
+ );
47
+ result.push(mapping);
48
+ }
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ mapAllElements(elements) {
55
+ var allMappings = [];
56
+
57
+ for (var i = 0; i < elements.length; i++) {
58
+ var elementMappings = this.mapElement(elements[i]);
59
+ allMappings = allMappings.concat(elementMappings);
60
+ }
61
+
62
+ return allMappings;
63
+ }
64
+
65
+ normalize(mapping) {
66
+ if (!mapping || !mapping.uv) {
67
+ return mapping;
68
+ }
69
+
70
+ var normalized = {
71
+ element: mapping.element,
72
+ face: mapping.face,
73
+ uv: [
74
+ mapping.uv[0] / this.resolution.width,
75
+ mapping.uv[1] / this.resolution.height,
76
+ mapping.uv[2] / this.resolution.width,
77
+ mapping.uv[3] / this.resolution.height
78
+ ],
79
+ textureIndex: mapping.textureIndex,
80
+ normalized: true,
81
+ fixed: mapping.fixed,
82
+ supported: mapping.supported
83
+ };
84
+
85
+ return normalized;
86
+ }
87
+
88
+ normalizeAll(mappings) {
89
+ var result = [];
90
+
91
+ for (var i = 0; i < mappings.length; i++) {
92
+ result.push(this.normalize(mappings[i]));
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+ denormalize(mapping) {
99
+ if (!mapping || !mapping.uv || !mapping.normalized) {
100
+ return mapping;
101
+ }
102
+
103
+ var denormalized = {
104
+ element: mapping.element,
105
+ face: mapping.face,
106
+ uv: [
107
+ mapping.uv[0] * this.resolution.width,
108
+ mapping.uv[1] * this.resolution.height,
109
+ mapping.uv[2] * this.resolution.width,
110
+ mapping.uv[3] * this.resolution.height
111
+ ],
112
+ textureIndex: mapping.textureIndex,
113
+ normalized: false,
114
+ fixed: mapping.fixed,
115
+ supported: mapping.supported
116
+ };
117
+
118
+ return denormalized;
119
+ }
120
+
121
+ validate(mapping, textureWidth, textureHeight) {
122
+ if (!mapping || !mapping.uv) {
123
+ return { valid: false, issues: ["Missing UV data"] };
124
+ }
125
+
126
+ var issues = [];
127
+ var uv = mapping.uv;
128
+ var tw = textureWidth || this.resolution.width;
129
+ var th = textureHeight || this.resolution.height;
130
+
131
+ if (uv.length !== 4) {
132
+ issues.push("UV array must have exactly 4 values");
133
+ }
134
+
135
+ if (uv[0] < 0) issues.push("UV x1 is negative: " + uv[0]);
136
+ if (uv[1] < 0) issues.push("UV y1 is negative: " + uv[1]);
137
+ if (uv[2] < 0) issues.push("UV x2 is negative: " + uv[2]);
138
+ if (uv[3] < 0) issues.push("UV y2 is negative: " + uv[3]);
139
+
140
+ if (uv[0] > tw) issues.push("UV x1 exceeds texture width: " + uv[0] + " > " + tw);
141
+ if (uv[1] > th) issues.push("UV y1 exceeds texture height: " + uv[1] + " > " + th);
142
+ if (uv[2] > tw) issues.push("UV x2 exceeds texture width: " + uv[2] + " > " + tw);
143
+ if (uv[3] > th) issues.push("UV y2 exceeds texture height: " + uv[3] + " > " + th);
144
+
145
+ if (uv[0] === uv[2]) issues.push("UV has zero width");
146
+ if (uv[1] === uv[3]) issues.push("UV has zero height");
147
+
148
+ return {
149
+ valid: issues.length === 0,
150
+ issues: issues
151
+ };
152
+ }
153
+
154
+ validateAll(mappings, textureWidth, textureHeight) {
155
+ var results = [];
156
+ var allValid = true;
157
+
158
+ for (var i = 0; i < mappings.length; i++) {
159
+ var result = this.validate(mappings[i], textureWidth, textureHeight);
160
+ results.push({
161
+ mapping: mappings[i],
162
+ valid: result.valid,
163
+ issues: result.issues
164
+ });
165
+ if (!result.valid) {
166
+ allValid = false;
167
+ }
168
+ }
169
+
170
+ return {
171
+ allValid: allValid,
172
+ results: results,
173
+ totalMappings: mappings.length,
174
+ invalidCount: results.filter(function (r) { return !r.valid; }).length
175
+ };
176
+ }
177
+
178
+ fix(mapping, textureWidth, textureHeight) {
179
+ if (!mapping || !mapping.uv) {
180
+ return mapping;
181
+ }
182
+
183
+ var tw = textureWidth || this.resolution.width;
184
+ var th = textureHeight || this.resolution.height;
185
+ var uv = mapping.uv.slice();
186
+ var wasFixed = false;
187
+
188
+ for (var i = 0; i < 4; i++) {
189
+ if (uv[i] < 0) {
190
+ uv[i] = 0;
191
+ wasFixed = true;
192
+ }
193
+ }
194
+
195
+ if (uv[0] > tw) { uv[0] = tw; wasFixed = true; }
196
+ if (uv[2] > tw) { uv[2] = tw; wasFixed = true; }
197
+ if (uv[1] > th) { uv[1] = th; wasFixed = true; }
198
+ if (uv[3] > th) { uv[3] = th; wasFixed = true; }
199
+
200
+ if (uv[0] > uv[2]) {
201
+ var temp = uv[0];
202
+ uv[0] = uv[2];
203
+ uv[2] = temp;
204
+ wasFixed = true;
205
+ }
206
+
207
+ if (uv[1] > uv[3]) {
208
+ var temp2 = uv[1];
209
+ uv[1] = uv[3];
210
+ uv[3] = temp2;
211
+ wasFixed = true;
212
+ }
213
+
214
+ if (uv[0] === uv[2]) {
215
+ uv[2] = Math.min(uv[0] + 1, tw);
216
+ wasFixed = true;
217
+ }
218
+
219
+ if (uv[1] === uv[3]) {
220
+ uv[3] = Math.min(uv[1] + 1, th);
221
+ wasFixed = true;
222
+ }
223
+
224
+ if (wasFixed) {
225
+ this.fixLog.push({
226
+ element: mapping.element,
227
+ face: mapping.face,
228
+ originalUV: mapping.uv.slice(),
229
+ fixedUV: uv.slice(),
230
+ timestamp: Date.now()
231
+ });
232
+ }
233
+
234
+ var fixed = {};
235
+ for (var key in mapping) {
236
+ fixed[key] = mapping[key];
237
+ }
238
+ fixed.uv = uv;
239
+ fixed.fixed = wasFixed;
240
+ fixed.supported = true;
241
+
242
+ return fixed;
243
+ }
244
+
245
+ fixAll(mappings, textureWidth, textureHeight) {
246
+ var result = [];
247
+
248
+ for (var i = 0; i < mappings.length; i++) {
249
+ result.push(this.fix(mappings[i], textureWidth, textureHeight));
250
+ }
251
+
252
+ return result;
253
+ }
254
+
255
+ autoMap(elementFrom, elementTo) {
256
+ var sizeX = Math.abs(elementTo[0] - elementFrom[0]);
257
+ var sizeY = Math.abs(elementTo[1] - elementFrom[1]);
258
+ var sizeZ = Math.abs(elementTo[2] - elementFrom[2]);
259
+
260
+ return {
261
+ north: [sizeZ, sizeY + sizeZ, sizeZ + sizeX, sizeY + sizeZ + sizeY],
262
+ south: [sizeZ + sizeX + sizeZ, sizeY + sizeZ, sizeZ + sizeX + sizeZ + sizeX, sizeY + sizeZ + sizeY],
263
+ east: [0, sizeY + sizeZ, sizeZ, sizeY + sizeZ + sizeY],
264
+ west: [sizeZ + sizeX, sizeY + sizeZ, sizeZ + sizeX + sizeZ, sizeY + sizeZ + sizeY],
265
+ up: [sizeZ, 0, sizeZ + sizeX, sizeZ],
266
+ down: [sizeZ + sizeX, 0, sizeZ + sizeX + sizeX, sizeZ]
267
+ };
268
+ }
269
+
270
+ calculateTextureSize(elements) {
271
+ var maxU = 0;
272
+ var maxV = 0;
273
+
274
+ for (var i = 0; i < elements.length; i++) {
275
+ var el = elements[i];
276
+ var faceKeys = Object.keys(el.faces || {});
277
+
278
+ for (var j = 0; j < faceKeys.length; j++) {
279
+ var face = el.faces[faceKeys[j]];
280
+ if (face.uv) {
281
+ if (face.uv[2] > maxU) maxU = face.uv[2];
282
+ if (face.uv[3] > maxV) maxV = face.uv[3];
283
+ }
284
+ }
285
+ }
286
+
287
+ var width = 1;
288
+ while (width < maxU) width *= 2;
289
+
290
+ var height = 1;
291
+ while (height < maxV) height *= 2;
292
+
293
+ return { width: width, height: height };
294
+ }
295
+
296
+ getFixLog() {
297
+ return this.fixLog;
298
+ }
299
+
300
+ clearFixLog() {
301
+ this.fixLog = [];
302
+ return this;
303
+ }
304
+
305
+ getMappings() {
306
+ return this.mappings;
307
+ }
308
+
309
+ clearMappings() {
310
+ this.mappings = [];
311
+ return this;
312
+ }
313
+ }
314
+
315
+ module.exports = UVMapper;