@erik9994857/cag 2.0.3 → 2.0.5
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.
- package/package.json +1 -1
- package/src/api/model-api.js +73 -20
- package/src/models/uv-mapper.js +12 -10
- package/src/runtime/electron-runner.js +40 -13
package/package.json
CHANGED
package/src/api/model-api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
|
+
const UVMapper = require("../models/uv-mapper");
|
|
3
4
|
|
|
4
5
|
class ModelAPI {
|
|
5
6
|
constructor(resourceMap, defaultModelsPath) {
|
|
@@ -69,6 +70,7 @@ class ModelAPI {
|
|
|
69
70
|
geometry: null,
|
|
70
71
|
texture: null,
|
|
71
72
|
uvMappings: null,
|
|
73
|
+
allTextures: [],
|
|
72
74
|
valid: false
|
|
73
75
|
};
|
|
74
76
|
|
|
@@ -82,20 +84,24 @@ class ModelAPI {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (resource.texture) {
|
|
85
|
-
|
|
87
|
+
const tex = this.loadTexture(resource.texture);
|
|
88
|
+
result.texture = tex;
|
|
89
|
+
result.allTextures = [tex.dataURL];
|
|
86
90
|
} else if (embeddedTextures.length > 0) {
|
|
87
|
-
result.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
const resW = result.geometry ? result.geometry.resolution.width : 16;
|
|
92
|
+
const resH = result.geometry ? result.geometry.resolution.height : 16;
|
|
93
|
+
result.allTextures = embeddedTextures.map(function (t) { return t.source; });
|
|
94
|
+
if (embeddedTextures.length === 1) {
|
|
95
|
+
result.texture = {
|
|
96
|
+
path: null, width: resW, height: resH,
|
|
97
|
+
format: "png", size: 0, dataURL: embeddedTextures[0].source
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
this.remapToAtlas(result, embeddedTextures, resW, resH);
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
if (result.geometry && result.texture && result.uvMappings) {
|
|
98
|
-
result.uvMappings = this.validateAndFixUVMappings(result.uvMappings, result.texture);
|
|
99
105
|
result.valid = true;
|
|
100
106
|
} else if (result.geometry) {
|
|
101
107
|
result.valid = true;
|
|
@@ -104,6 +110,28 @@ class ModelAPI {
|
|
|
104
110
|
return result;
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
remapToAtlas(result, textures, texW, texH) {
|
|
114
|
+
const numTex = textures.length;
|
|
115
|
+
const atlasW = numTex * texW;
|
|
116
|
+
const atlasH = texH;
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < result.uvMappings.length; i++) {
|
|
119
|
+
const m = result.uvMappings[i];
|
|
120
|
+
const idx = m.textureIndex || 0;
|
|
121
|
+
const texIdx = Math.min(idx, numTex - 1);
|
|
122
|
+
const offsetU = texIdx * texW;
|
|
123
|
+
m.uv = [m.uv[0] + offsetU, m.uv[1], m.uv[2] + offsetU, m.uv[3]];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
result.texture = {
|
|
127
|
+
path: null, width: atlasW, height: atlasH,
|
|
128
|
+
format: "png", size: 0, dataURL: textures[0].source
|
|
129
|
+
};
|
|
130
|
+
if (result.geometry) {
|
|
131
|
+
result.geometry.resolution = { width: atlasW, height: atlasH };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
107
135
|
parseBBModel(filePath) {
|
|
108
136
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
109
137
|
let data;
|
|
@@ -121,6 +149,10 @@ class ModelAPI {
|
|
|
121
149
|
};
|
|
122
150
|
|
|
123
151
|
const uvMappings = [];
|
|
152
|
+
const uvMapper = new UVMapper(geometry.resolution);
|
|
153
|
+
|
|
154
|
+
// Detect box_uv: can be at root level or in meta
|
|
155
|
+
const projectBoxUV = !!(data.box_uv || (data.meta && data.meta.box_uv));
|
|
124
156
|
|
|
125
157
|
if (data.elements && Array.isArray(data.elements)) {
|
|
126
158
|
for (const element of data.elements) {
|
|
@@ -133,20 +165,41 @@ class ModelAPI {
|
|
|
133
165
|
faces: {}
|
|
134
166
|
};
|
|
135
167
|
|
|
136
|
-
|
|
137
|
-
|
|
168
|
+
// Element-level box_uv overrides project-level
|
|
169
|
+
const isBoxUV = element.box_uv !== undefined ? !!element.box_uv : projectBoxUV;
|
|
170
|
+
|
|
171
|
+
// For box UV: compute face UVs from geometry + uv_offset
|
|
172
|
+
let boxUVs = null;
|
|
173
|
+
if (isBoxUV) {
|
|
174
|
+
const uvOff = element.uv_offset || [0, 0];
|
|
175
|
+
boxUVs = uvMapper.autoMap(geomElement.from, geomElement.to, uvOff);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const faceNames = ["north", "south", "east", "west", "up", "down"];
|
|
179
|
+
|
|
180
|
+
if (isBoxUV) {
|
|
181
|
+
// Box UV mode: compute all face UVs from geometry
|
|
182
|
+
for (const faceName of faceNames) {
|
|
183
|
+
const texIdx = (element.faces && element.faces[faceName] && element.faces[faceName].texture !== undefined)
|
|
184
|
+
? element.faces[faceName].texture : 0;
|
|
185
|
+
geomElement.faces[faceName] = { uv: boxUVs[faceName], texture: texIdx };
|
|
186
|
+
uvMappings.push({
|
|
187
|
+
element: geomElement.name, face: faceName,
|
|
188
|
+
uv: boxUVs[faceName], textureIndex: texIdx
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
} else if (element.faces) {
|
|
192
|
+
// Per-face UV mode: read UVs directly from bbmodel
|
|
138
193
|
for (const faceName of faceNames) {
|
|
139
194
|
if (element.faces[faceName]) {
|
|
140
195
|
const face = element.faces[faceName];
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
};
|
|
196
|
+
const faceUV = (face.uv && Array.isArray(face.uv) && face.uv.length >= 4)
|
|
197
|
+
? face.uv : [0, 0, 16, 16];
|
|
198
|
+
const texIdx = face.texture !== undefined ? face.texture : 0;
|
|
199
|
+
geomElement.faces[faceName] = { uv: faceUV, texture: texIdx };
|
|
145
200
|
uvMappings.push({
|
|
146
|
-
element: geomElement.name,
|
|
147
|
-
|
|
148
|
-
uv: face.uv || [0, 0, 16, 16],
|
|
149
|
-
textureIndex: face.texture !== undefined ? face.texture : 0
|
|
201
|
+
element: geomElement.name, face: faceName,
|
|
202
|
+
uv: faceUV, textureIndex: texIdx
|
|
150
203
|
});
|
|
151
204
|
}
|
|
152
205
|
}
|
package/src/models/uv-mapper.js
CHANGED
|
@@ -252,18 +252,20 @@ class UVMapper {
|
|
|
252
252
|
return result;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
autoMap(elementFrom, elementTo) {
|
|
256
|
-
var
|
|
257
|
-
var
|
|
258
|
-
var
|
|
255
|
+
autoMap(elementFrom, elementTo, uvOffset) {
|
|
256
|
+
var W = Math.abs(elementTo[0] - elementFrom[0]);
|
|
257
|
+
var H = Math.abs(elementTo[1] - elementFrom[1]);
|
|
258
|
+
var D = Math.abs(elementTo[2] - elementFrom[2]);
|
|
259
|
+
var ox = uvOffset ? uvOffset[0] : 0;
|
|
260
|
+
var oy = uvOffset ? uvOffset[1] : 0;
|
|
259
261
|
|
|
260
262
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
up:
|
|
266
|
-
down:
|
|
263
|
+
east: [ox, D + oy, D + ox, D + H + oy],
|
|
264
|
+
north: [D + ox, D + oy, D + W + ox, D + H + oy],
|
|
265
|
+
west: [D + W + ox, D + oy, D * 2 + W + ox, D + H + oy],
|
|
266
|
+
south: [D * 2 + W + ox, D + oy, D * 2 + W * 2 + ox, D + H + oy],
|
|
267
|
+
up: [D + W + ox, D + oy, D + ox, oy],
|
|
268
|
+
down: [D + W * 2 + ox, oy, D + W + ox, D + oy]
|
|
267
269
|
};
|
|
268
270
|
}
|
|
269
271
|
|
|
@@ -215,6 +215,7 @@ ElectronRunner.prototype.getRendererSource = function () {
|
|
|
215
215
|
}
|
|
216
216
|
modelDataForRenderer[smName] = {
|
|
217
217
|
textureDataURL: smData.textureDataURL || null,
|
|
218
|
+
allTextures: smData.allTextures || [],
|
|
218
219
|
resW: smData.resolution ? smData.resolution.width : 16,
|
|
219
220
|
resH: smData.resolution ? smData.resolution.height : 16,
|
|
220
221
|
faceUV: faceUVMap
|
|
@@ -448,19 +449,44 @@ ElectronRunner.prototype.getRendererSource = function () {
|
|
|
448
449
|
js.push(" this.blockTexture = gl.createTexture();");
|
|
449
450
|
js.push(" gl.bindTexture(gl.TEXTURE_2D, this.blockTexture);");
|
|
450
451
|
js.push(" gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([128,128,128,255]));");
|
|
452
|
+
js.push(" function uploadTex(source) {");
|
|
453
|
+
js.push(" var g = game.gl;");
|
|
454
|
+
js.push(" g.bindTexture(g.TEXTURE_2D, game.blockTexture);");
|
|
455
|
+
js.push(" g.texImage2D(g.TEXTURE_2D, 0, g.RGBA, g.RGBA, g.UNSIGNED_BYTE, source);");
|
|
456
|
+
js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MIN_FILTER, g.NEAREST);");
|
|
457
|
+
js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MAG_FILTER, g.NEAREST);");
|
|
458
|
+
js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_S, g.CLAMP_TO_EDGE);");
|
|
459
|
+
js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_T, g.CLAMP_TO_EDGE);");
|
|
460
|
+
js.push(" }");
|
|
451
461
|
js.push(" var texKeys = Object.keys(MODELS);");
|
|
452
|
-
js.push(" if (texKeys.length > 0
|
|
453
|
-
js.push(" var
|
|
454
|
-
js.push("
|
|
455
|
-
js.push("
|
|
456
|
-
js.push("
|
|
457
|
-
js.push("
|
|
458
|
-
js.push("
|
|
459
|
-
js.push("
|
|
460
|
-
js.push("
|
|
461
|
-
js.push("
|
|
462
|
-
js.push("
|
|
463
|
-
js.push("
|
|
462
|
+
js.push(" if (texKeys.length > 0) {");
|
|
463
|
+
js.push(" var m = MODELS[texKeys[0]];");
|
|
464
|
+
js.push(" var srcs = m.allTextures && m.allTextures.length > 0 ? m.allTextures : (m.textureDataURL ? [m.textureDataURL] : []);");
|
|
465
|
+
js.push(" if (srcs.length > 1) {");
|
|
466
|
+
js.push(" var loaded = 0, imgs = [];");
|
|
467
|
+
js.push(" for (var ti = 0; ti < srcs.length; ti++) {");
|
|
468
|
+
js.push(" (function(idx) {");
|
|
469
|
+
js.push(" var img = new Image();");
|
|
470
|
+
js.push(" img.onload = function() {");
|
|
471
|
+
js.push(" imgs[idx] = img;");
|
|
472
|
+
js.push(" loaded++;");
|
|
473
|
+
js.push(" if (loaded === srcs.length) {");
|
|
474
|
+
js.push(" var tw = imgs[0].width, th = imgs[0].height;");
|
|
475
|
+
js.push(" var c = document.createElement('canvas');");
|
|
476
|
+
js.push(" c.width = tw * srcs.length; c.height = th;");
|
|
477
|
+
js.push(" var ctx = c.getContext('2d');");
|
|
478
|
+
js.push(" for (var j = 0; j < imgs.length; j++) ctx.drawImage(imgs[j], j * tw, 0);");
|
|
479
|
+
js.push(" uploadTex(c);");
|
|
480
|
+
js.push(" }");
|
|
481
|
+
js.push(" };");
|
|
482
|
+
js.push(" img.src = srcs[idx];");
|
|
483
|
+
js.push(" })(ti);");
|
|
484
|
+
js.push(" }");
|
|
485
|
+
js.push(" } else if (srcs.length === 1) {");
|
|
486
|
+
js.push(" var texImg = new Image();");
|
|
487
|
+
js.push(" texImg.onload = function() { uploadTex(texImg); };");
|
|
488
|
+
js.push(" texImg.src = srcs[0];");
|
|
489
|
+
js.push(" }");
|
|
464
490
|
js.push(" }");
|
|
465
491
|
js.push("");
|
|
466
492
|
js.push(" gl.enable(gl.DEPTH_TEST);");
|
|
@@ -696,7 +722,8 @@ ElectronRunner.extractSettings = function (executor) {
|
|
|
696
722
|
settings.models[mName] = {
|
|
697
723
|
uvMappings: model.uvMappings || [],
|
|
698
724
|
resolution: { width: texW, height: texH },
|
|
699
|
-
textureDataURL: model.texture && model.texture.dataURL ? model.texture.dataURL : null
|
|
725
|
+
textureDataURL: model.texture && model.texture.dataURL ? model.texture.dataURL : null,
|
|
726
|
+
allTextures: model.allTextures || []
|
|
700
727
|
};
|
|
701
728
|
}
|
|
702
729
|
}
|