@erik9994857/cag 2.0.0 → 2.0.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erik9994857/cag",
3
- "version": "2.0.0",
3
+ "version": "2.0.3",
4
4
  "description": "CaG — A code library and custom language for building 3D worlds with .cagc files, .bbmodel support, and auto UV mapping",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -72,14 +72,26 @@ class ModelAPI {
72
72
  valid: false
73
73
  };
74
74
 
75
+ let embeddedTextures = [];
76
+
75
77
  if (resource.model) {
76
78
  const modelData = this.parseBBModel(resource.model);
77
79
  result.geometry = modelData.geometry;
78
80
  result.uvMappings = modelData.uvMappings;
81
+ embeddedTextures = modelData.embeddedTextures || [];
79
82
  }
80
83
 
81
84
  if (resource.texture) {
82
85
  result.texture = this.loadTexture(resource.texture);
86
+ } else if (embeddedTextures.length > 0) {
87
+ result.texture = {
88
+ path: null,
89
+ width: result.geometry ? result.geometry.resolution.width : 16,
90
+ height: result.geometry ? result.geometry.resolution.height : 16,
91
+ format: "png",
92
+ size: 0,
93
+ dataURL: embeddedTextures[0].source
94
+ };
83
95
  }
84
96
 
85
97
  if (result.geometry && result.texture && result.uvMappings) {
@@ -148,7 +160,21 @@ class ModelAPI {
148
160
  geometry.bones = this.parseOutliner(data.outliner);
149
161
  }
150
162
 
151
- return { geometry, uvMappings };
163
+ const embeddedTextures = [];
164
+ if (data.textures && Array.isArray(data.textures)) {
165
+ for (let i = 0; i < data.textures.length; i++) {
166
+ const tex = data.textures[i];
167
+ if (tex.source) {
168
+ embeddedTextures.push({
169
+ index: tex.id !== undefined ? tex.id : i,
170
+ name: tex.name || "texture_" + i,
171
+ source: tex.source
172
+ });
173
+ }
174
+ }
175
+ }
176
+
177
+ return { geometry, uvMappings, embeddedTextures };
152
178
  }
153
179
 
154
180
  parseOutliner(outliner) {
@@ -191,12 +217,16 @@ class ModelAPI {
191
217
  }
192
218
  }
193
219
 
220
+ const base64 = buffer.toString("base64");
221
+ const mime = ext === ".png" ? "image/png" : (ext === ".jpg" || ext === ".jpeg") ? "image/jpeg" : "image/" + ext.substring(1);
222
+
194
223
  return {
195
224
  path: filePath,
196
225
  width: width,
197
226
  height: height,
198
227
  format: ext.substring(1),
199
- size: buffer.length
228
+ size: buffer.length,
229
+ dataURL: "data:" + mime + ";base64," + base64
200
230
  };
201
231
  }
202
232
 
@@ -269,6 +299,10 @@ class ModelAPI {
269
299
  isModelLoaded(name) {
270
300
  return this.loadedModels[name] !== undefined;
271
301
  }
302
+
303
+ getLoadedModels() {
304
+ return this.loadedModels;
305
+ }
272
306
  }
273
307
 
274
308
  module.exports = ModelAPI;
@@ -72,9 +72,6 @@ class CagCLI {
72
72
 
73
73
  this.printLine("Project: " + engine.getInfo().ID);
74
74
  this.printLine("Version: " + engine.getInfo().version);
75
- this.printLine("Dependencies: " + engine.getDependencies().join(", "));
76
- this.printLine("Code files: " + engine.getCodeFiles().length);
77
- this.printLine("Resources: " + Object.keys(engine.getResourceMap()).length);
78
75
  this.printLine("");
79
76
 
80
77
  const CagcParser = require("../parser/cagc-parser");
@@ -91,26 +88,39 @@ class CagCLI {
91
88
  const fileName = path.basename(filePath);
92
89
  const content = fs.readFileSync(filePath, "utf-8");
93
90
 
94
- this.printLine("Executing: " + fileName);
95
-
96
91
  const parsed = cagcParser.parse(content, fileName);
97
92
  const result = executor.execute(parsed);
98
93
 
99
94
  if (result.errors && result.errors.length > 0) {
100
95
  for (let j = 0; j < result.errors.length; j++) {
101
- this.printError(" " + result.errors[j]);
96
+ this.printError(result.errors[j]);
102
97
  }
103
98
  }
99
+ }
104
100
 
105
- if (result.outputs && result.outputs.length > 0) {
106
- for (let j = 0; j < result.outputs.length; j++) {
107
- this.printLine(" > " + result.outputs[j]);
101
+ const registry = executor.getRegistry();
102
+ if (registry.has("Model.API")) {
103
+ const modelAPI = registry.get("Model.API");
104
+ const loadedModels = modelAPI.getLoadedModels();
105
+ const modelNames = Object.keys(loadedModels);
106
+ if (modelNames.length > 0) {
107
+ this.printLine("Reading UV mapping...");
108
+ for (let i = 0; i < modelNames.length; i++) {
109
+ const name = modelNames[i];
110
+ const model = loadedModels[name];
111
+ if (model && model.valid && model.texture) {
112
+ this.printSuccess("Auto Mapped Custom UV for " + name);
113
+ } else if (model && model.valid) {
114
+ this.printLine(" " + name + " (no texture paired)");
115
+ } else {
116
+ this.printError("Failed to map UV for " + name);
117
+ }
108
118
  }
119
+ this.printLine("");
109
120
  }
110
121
  }
111
122
 
112
- this.printLine("");
113
- this.printSuccess("Execution complete. Launching game...");
123
+ this.printLine("Launching game...");
114
124
  this.printLine("");
115
125
 
116
126
  const settings = ElectronRunner.extractSettings(executor);
@@ -18,35 +18,37 @@ ElectronRunner.prototype.run = function () {
18
18
  };
19
19
 
20
20
  ElectronRunner.prototype.ensureDirectories = function () {
21
- if (fs.existsSync(this.tempDir)) {
22
- this.removeDirectory(this.tempDir);
23
- }
24
21
  fs.mkdirSync(this.tempDir, { recursive: true });
25
22
  };
26
23
 
27
24
  ElectronRunner.prototype.generateApp = function () {
28
- var pkg = {
29
- name: "cag-runner",
30
- version: "1.0.0",
31
- main: "main.js",
32
- dependencies: {}
33
- };
34
- fs.writeFileSync(path.join(this.tempDir, "package.json"), JSON.stringify(pkg, null, 2));
25
+ var pkgPath = path.join(this.tempDir, "package.json");
26
+ if (!fs.existsSync(pkgPath)) {
27
+ var pkg = {
28
+ name: "cag-runner",
29
+ version: "1.0.0",
30
+ main: "main.js",
31
+ dependencies: {}
32
+ };
33
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
34
+ }
35
35
  fs.writeFileSync(path.join(this.tempDir, "main.js"), this.getMainProcessSource());
36
36
  fs.writeFileSync(path.join(this.tempDir, "index.html"), this.getRendererSource());
37
37
  };
38
38
 
39
39
  ElectronRunner.prototype.installAndLaunch = function () {
40
- childProcess.execSync("npm install electron@latest --save --no-audit --no-fund", {
41
- cwd: this.tempDir,
42
- stdio: "inherit"
43
- });
44
-
45
40
  var electronBin = path.join(this.tempDir, "node_modules", ".bin", "electron");
46
41
  if (process.platform === "win32") {
47
42
  electronBin = electronBin + ".cmd";
48
43
  }
49
44
 
45
+ if (!fs.existsSync(electronBin)) {
46
+ childProcess.execSync("npm install electron --save --no-audit --no-fund", {
47
+ cwd: this.tempDir,
48
+ stdio: "inherit"
49
+ });
50
+ }
51
+
50
52
  childProcess.execSync("\"" + electronBin + "\" .", {
51
53
  cwd: this.tempDir,
52
54
  stdio: "inherit",
@@ -147,15 +149,15 @@ ElectronRunner.prototype.getRendererSource = function () {
147
149
 
148
150
  var vsSrc = [
149
151
  "attribute vec3 aPos;",
150
- "attribute vec3 aColor;",
152
+ "attribute vec2 aUV;",
151
153
  "attribute vec3 aNormal;",
152
154
  "uniform mat4 uProj;",
153
155
  "uniform mat4 uView;",
154
- "varying vec3 vColor;",
156
+ "varying vec2 vUV;",
155
157
  "varying vec3 vNormal;",
156
158
  "varying vec3 vWorldPos;",
157
159
  "void main() {",
158
- " vColor = aColor;",
160
+ " vUV = aUV;",
159
161
  " vNormal = aNormal;",
160
162
  " vWorldPos = aPos;",
161
163
  " gl_Position = uProj * uView * vec4(aPos, 1.0);",
@@ -164,9 +166,10 @@ ElectronRunner.prototype.getRendererSource = function () {
164
166
 
165
167
  var fsSrc = [
166
168
  "precision mediump float;",
167
- "varying vec3 vColor;",
169
+ "varying vec2 vUV;",
168
170
  "varying vec3 vNormal;",
169
171
  "varying vec3 vWorldPos;",
172
+ "uniform sampler2D uTexture;",
170
173
  "uniform vec3 uSunDir;",
171
174
  "uniform vec3 uSunColor;",
172
175
  "uniform vec3 uAmbient;",
@@ -175,13 +178,14 @@ ElectronRunner.prototype.getRendererSource = function () {
175
178
  "uniform float uFogFar;",
176
179
  "uniform vec3 uCamPos;",
177
180
  "void main() {",
181
+ " vec4 texColor = texture2D(uTexture, vUV);",
178
182
  " vec3 norm = normalize(vNormal);",
179
183
  " float diff = max(dot(norm, normalize(uSunDir)), 0.0);",
180
- " vec3 lit = vColor * (uAmbient + uSunColor * diff);",
184
+ " vec3 lit = texColor.rgb * (uAmbient + uSunColor * diff);",
181
185
  " float dist = length(vWorldPos - uCamPos);",
182
186
  " float fog = clamp((dist - uFogNear) / (uFogFar - uFogNear), 0.0, 1.0);",
183
187
  " vec3 final = mix(lit, uFogColor, fog);",
184
- " gl_FragColor = vec4(final, 1.0);",
188
+ " gl_FragColor = vec4(final, texColor.a);",
185
189
  "}"
186
190
  ].join("\\n");
187
191
 
@@ -193,6 +197,33 @@ ElectronRunner.prototype.getRendererSource = function () {
193
197
  js.push("var VS_SRC = \"" + vsSrc + "\";");
194
198
  js.push("var FS_SRC = \"" + fsSrc + "\";");
195
199
  js.push("");
200
+
201
+ var modelDataForRenderer = {};
202
+ if (s.models) {
203
+ var sModelNames = Object.keys(s.models);
204
+ for (var mi = 0; mi < sModelNames.length; mi++) {
205
+ var smName = sModelNames[mi];
206
+ var smData = s.models[smName];
207
+ var faceUVMap = {};
208
+ if (smData.uvMappings) {
209
+ for (var ui = 0; ui < smData.uvMappings.length; ui++) {
210
+ var mapping = smData.uvMappings[ui];
211
+ if (!faceUVMap[mapping.face]) {
212
+ faceUVMap[mapping.face] = mapping.uv;
213
+ }
214
+ }
215
+ }
216
+ modelDataForRenderer[smName] = {
217
+ textureDataURL: smData.textureDataURL || null,
218
+ resW: smData.resolution ? smData.resolution.width : 16,
219
+ resH: smData.resolution ? smData.resolution.height : 16,
220
+ faceUV: faceUVMap
221
+ };
222
+ }
223
+ }
224
+ js.push("var MODELS = " + JSON.stringify(modelDataForRenderer) + ";");
225
+ js.push("");
226
+
196
227
  js.push("var game = {");
197
228
  js.push(" gl: null, canvas: null, program: null,");
198
229
  js.push(" camX: " + spX + ", camY: " + spY + ", camZ: " + spZ + ",");
@@ -225,34 +256,67 @@ ElectronRunner.prototype.getRendererSource = function () {
225
256
  js.push("");
226
257
 
227
258
  // Face helper
228
- js.push("function addFace(pos, col, nrm, v0, v1, v2, v3, color, normal) {");
259
+ js.push("function addFace(pos, uvArr, nrm, v0, v1, v2, v3, fuv, normal) {");
229
260
  js.push(" pos.push(v0[0],v0[1],v0[2], v1[0],v1[1],v1[2], v2[0],v2[1],v2[2]);");
230
261
  js.push(" pos.push(v0[0],v0[1],v0[2], v2[0],v2[1],v2[2], v3[0],v3[1],v3[2]);");
262
+ js.push(" uvArr.push(fuv[0],fuv[1], fuv[2],fuv[1], fuv[2],fuv[3]);");
263
+ js.push(" uvArr.push(fuv[0],fuv[1], fuv[2],fuv[3], fuv[0],fuv[3]);");
231
264
  js.push(" for (var i = 0; i < 6; i++) {");
232
- js.push(" col.push(color[0], color[1], color[2]);");
233
265
  js.push(" nrm.push(normal[0], normal[1], normal[2]);");
234
266
  js.push(" }");
235
267
  js.push("}");
236
268
  js.push("");
237
269
 
270
+ // UV normalization helper
271
+ js.push("function normUV(faceUV, resW, resH) {");
272
+ js.push(" if (!faceUV) return [0, 1, 1, 0];");
273
+ js.push(" return [faceUV[0]/resW, 1.0-faceUV[1]/resH, faceUV[2]/resW, 1.0-faceUV[3]/resH];");
274
+ js.push("}");
275
+ js.push("");
276
+
277
+ // Get UV coordinates for a model by name (case-insensitive)
278
+ js.push("function getModelUVs(name) {");
279
+ js.push(" var ln = name.toLowerCase();");
280
+ js.push(" var keys = Object.keys(MODELS);");
281
+ js.push(" for (var i = 0; i < keys.length; i++) {");
282
+ js.push(" if (keys[i].toLowerCase() === ln) {");
283
+ js.push(" var m = MODELS[keys[i]];");
284
+ js.push(" var f = m.faceUV || {};");
285
+ js.push(" return {");
286
+ js.push(" up: normUV(f.up, m.resW, m.resH),");
287
+ js.push(" down: normUV(f.down, m.resW, m.resH),");
288
+ js.push(" north: normUV(f.north, m.resW, m.resH),");
289
+ js.push(" south: normUV(f.south, m.resW, m.resH),");
290
+ js.push(" east: normUV(f.east, m.resW, m.resH),");
291
+ js.push(" west: normUV(f.west, m.resW, m.resH)");
292
+ js.push(" };");
293
+ js.push(" }");
294
+ js.push(" }");
295
+ js.push(" return { up:[0,1,1,0], down:[0,1,1,0], north:[0,1,1,0], south:[0,1,1,0], east:[0,1,1,0], west:[0,1,1,0] };");
296
+ js.push("}");
297
+ js.push("");
298
+
299
+ // Flip UV V-axis for side faces (v0/v1 are bottom vertices but addFace assigns fuv[1] to them)
300
+ js.push("function flipV(uv) { return [uv[0], uv[3], uv[2], uv[1]]; }");
301
+ js.push("");
302
+
238
303
  // Build mesh for a chunk
239
304
  js.push("function buildChunkMesh(blocks, blockMap) {");
240
- js.push(" var pos = [], col = [], nrm = [];");
241
- js.push(" var topC = [0.36, 0.63, 0.21];");
242
- js.push(" var sideC = [0.50, 0.38, 0.22];");
243
- js.push(" var botC = [0.45, 0.32, 0.17];");
305
+ js.push(" var pos = [], uvArr = [], nrm = [];");
306
+ js.push(" var uv = getModelUVs('Grass');");
307
+ js.push(" var sN = flipV(uv.north), sS = flipV(uv.south), sE = flipV(uv.east), sW = flipV(uv.west);");
244
308
  js.push(" for (var i = 0; i < blocks.length; i++) {");
245
309
  js.push(" var b = blocks[i];");
246
310
  js.push(" var x = b.x, y = b.y, z = b.z;");
247
311
  js.push(" var k = function(bx,by,bz) { return bx+','+by+','+bz; };");
248
- js.push(" if (!blockMap[k(x,y+1,z)]) addFace(pos,col,nrm, [x,y+1,z],[x+1,y+1,z],[x+1,y+1,z+1],[x,y+1,z+1], topC, [0,1,0]);");
249
- js.push(" if (!blockMap[k(x,y-1,z)]) addFace(pos,col,nrm, [x,y,z+1],[x+1,y,z+1],[x+1,y,z],[x,y,z], botC, [0,-1,0]);");
250
- js.push(" if (!blockMap[k(x,y,z-1)]) addFace(pos,col,nrm, [x,y,z],[x+1,y,z],[x+1,y+1,z],[x,y+1,z], sideC, [0,0,-1]);");
251
- js.push(" if (!blockMap[k(x,y,z+1)]) addFace(pos,col,nrm, [x+1,y,z+1],[x,y,z+1],[x,y+1,z+1],[x+1,y+1,z+1], sideC, [0,0,1]);");
252
- js.push(" if (!blockMap[k(x-1,y,z)]) addFace(pos,col,nrm, [x,y,z+1],[x,y,z],[x,y+1,z],[x,y+1,z+1], sideC, [-1,0,0]);");
253
- js.push(" if (!blockMap[k(x+1,y,z)]) addFace(pos,col,nrm, [x+1,y,z],[x+1,y,z+1],[x+1,y+1,z+1],[x+1,y+1,z], sideC, [1,0,0]);");
312
+ js.push(" if (!blockMap[k(x,y+1,z)]) addFace(pos,uvArr,nrm, [x,y+1,z],[x+1,y+1,z],[x+1,y+1,z+1],[x,y+1,z+1], uv.up, [0,1,0]);");
313
+ js.push(" if (!blockMap[k(x,y-1,z)]) addFace(pos,uvArr,nrm, [x,y,z+1],[x+1,y,z+1],[x+1,y,z],[x,y,z], uv.down, [0,-1,0]);");
314
+ js.push(" if (!blockMap[k(x,y,z-1)]) addFace(pos,uvArr,nrm, [x,y,z],[x+1,y,z],[x+1,y+1,z],[x,y+1,z], sN, [0,0,-1]);");
315
+ js.push(" if (!blockMap[k(x,y,z+1)]) addFace(pos,uvArr,nrm, [x+1,y,z+1],[x,y,z+1],[x,y+1,z+1],[x+1,y+1,z+1], sS, [0,0,1]);");
316
+ js.push(" if (!blockMap[k(x+1,y,z)]) addFace(pos,uvArr,nrm, [x+1,y,z],[x+1,y,z+1],[x+1,y+1,z+1],[x+1,y+1,z], sE, [1,0,0]);");
317
+ js.push(" if (!blockMap[k(x-1,y,z)]) addFace(pos,uvArr,nrm, [x,y,z+1],[x,y,z],[x,y+1,z],[x,y+1,z+1], sW, [-1,0,0]);");
254
318
  js.push(" }");
255
- js.push(" return { positions: pos, colors: col, normals: nrm };");
319
+ js.push(" return { positions: pos, uvs: uvArr, normals: nrm };");
256
320
  js.push("}");
257
321
  js.push("");
258
322
 
@@ -286,17 +350,16 @@ ElectronRunner.prototype.getRendererSource = function () {
286
350
  js.push(" var vertCount = mesh.positions.length / 3;");
287
351
  js.push(" var gl = game.gl;");
288
352
  js.push(" var vbo = gl.createBuffer();");
289
- js.push(" var data = new Float32Array(vertCount * 9);");
353
+ js.push(" var data = new Float32Array(vertCount * 8);");
290
354
  js.push(" for (var v = 0; v < vertCount; v++) {");
291
- js.push(" data[v*9+0] = mesh.positions[v*3+0];");
292
- js.push(" data[v*9+1] = mesh.positions[v*3+1];");
293
- js.push(" data[v*9+2] = mesh.positions[v*3+2];");
294
- js.push(" data[v*9+3] = mesh.colors[v*3+0];");
295
- js.push(" data[v*9+4] = mesh.colors[v*3+1];");
296
- js.push(" data[v*9+5] = mesh.colors[v*3+2];");
297
- js.push(" data[v*9+6] = mesh.normals[v*3+0];");
298
- js.push(" data[v*9+7] = mesh.normals[v*3+1];");
299
- js.push(" data[v*9+8] = mesh.normals[v*3+2];");
355
+ js.push(" data[v*8+0] = mesh.positions[v*3+0];");
356
+ js.push(" data[v*8+1] = mesh.positions[v*3+1];");
357
+ js.push(" data[v*8+2] = mesh.positions[v*3+2];");
358
+ js.push(" data[v*8+3] = mesh.uvs[v*2+0];");
359
+ js.push(" data[v*8+4] = mesh.uvs[v*2+1];");
360
+ js.push(" data[v*8+5] = mesh.normals[v*3+0];");
361
+ js.push(" data[v*8+6] = mesh.normals[v*3+1];");
362
+ js.push(" data[v*8+7] = mesh.normals[v*3+2];");
300
363
  js.push(" }");
301
364
  js.push(" gl.bindBuffer(gl.ARRAY_BUFFER, vbo);");
302
365
  js.push(" gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);");
@@ -359,7 +422,7 @@ ElectronRunner.prototype.getRendererSource = function () {
359
422
  js.push(" gl.attachShader(this.program, vs);");
360
423
  js.push(" gl.attachShader(this.program, fsh);");
361
424
  js.push(" gl.bindAttribLocation(this.program, 0, 'aPos');");
362
- js.push(" gl.bindAttribLocation(this.program, 1, 'aColor');");
425
+ js.push(" gl.bindAttribLocation(this.program, 1, 'aUV');");
363
426
  js.push(" gl.bindAttribLocation(this.program, 2, 'aNormal');");
364
427
  js.push(" gl.linkProgram(this.program);");
365
428
  js.push(" gl.useProgram(this.program);");
@@ -373,6 +436,7 @@ ElectronRunner.prototype.getRendererSource = function () {
373
436
  js.push(" this.uFogNear = gl.getUniformLocation(this.program, 'uFogNear');");
374
437
  js.push(" this.uFogFar = gl.getUniformLocation(this.program, 'uFogFar');");
375
438
  js.push(" this.uCamPos = gl.getUniformLocation(this.program, 'uCamPos');");
439
+ js.push(" this.uTexture = gl.getUniformLocation(this.program, 'uTexture');");
376
440
  js.push("");
377
441
  js.push(" gl.uniform3f(this.uSunDir, " + sunD.x + ", " + sunD.y + ", " + sunD.z + ");");
378
442
  js.push(" gl.uniform3f(this.uSunColor, 1.0, 0.95, 0.8);");
@@ -381,6 +445,24 @@ ElectronRunner.prototype.getRendererSource = function () {
381
445
  js.push(" gl.uniform1f(this.uFogNear, " + fogNear + ");");
382
446
  js.push(" gl.uniform1f(this.uFogFar, " + fogFar + ");");
383
447
  js.push("");
448
+ js.push(" this.blockTexture = gl.createTexture();");
449
+ js.push(" gl.bindTexture(gl.TEXTURE_2D, this.blockTexture);");
450
+ js.push(" gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([128,128,128,255]));");
451
+ js.push(" var texKeys = Object.keys(MODELS);");
452
+ js.push(" if (texKeys.length > 0 && MODELS[texKeys[0]].textureDataURL) {");
453
+ js.push(" var texImg = new Image();");
454
+ js.push(" texImg.onload = function() {");
455
+ js.push(" var g = game.gl;");
456
+ js.push(" g.bindTexture(g.TEXTURE_2D, game.blockTexture);");
457
+ js.push(" g.texImage2D(g.TEXTURE_2D, 0, g.RGBA, g.RGBA, g.UNSIGNED_BYTE, texImg);");
458
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MIN_FILTER, g.NEAREST);");
459
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MAG_FILTER, g.NEAREST);");
460
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_S, g.CLAMP_TO_EDGE);");
461
+ js.push(" g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_T, g.CLAMP_TO_EDGE);");
462
+ js.push(" };");
463
+ js.push(" texImg.src = MODELS[texKeys[0]].textureDataURL;");
464
+ js.push(" }");
465
+ js.push("");
384
466
  js.push(" gl.enable(gl.DEPTH_TEST);");
385
467
  js.push(" gl.enable(gl.CULL_FACE);");
386
468
  js.push(" gl.cullFace(gl.BACK);");
@@ -407,16 +489,8 @@ ElectronRunner.prototype.getRendererSource = function () {
407
489
  js.push("");
408
490
  js.push(" setInterval(function () { game.fps = game.frames; game.frames = 0; }, 1000);");
409
491
  js.push("");
410
- js.push(" var loadingEl = document.getElementById('loading');");
411
- js.push(" loadingEl.classList.add('fade-out');");
412
- js.push(" var overlayEl = document.getElementById('click-overlay');");
413
- js.push(" overlayEl.style.display = 'flex';");
414
- js.push(" overlayEl.addEventListener('click', function () {");
415
- js.push(" overlayEl.style.display = 'none';");
416
- js.push(" game.canvas.requestPointerLock();");
417
- js.push(" game.started = true;");
418
- js.push(" game.loop();");
419
- js.push(" });");
492
+ js.push(" game.started = true;");
493
+ js.push(" game.loop();");
420
494
  js.push("};");
421
495
  js.push("");
422
496
 
@@ -495,7 +569,11 @@ ElectronRunner.prototype.getRendererSource = function () {
495
569
  js.push(" gl.uniformMatrix4fv(this.uView, false, view);");
496
570
  js.push(" gl.uniform3f(this.uCamPos, this.camX, this.camY, this.camZ);");
497
571
  js.push("");
498
- js.push(" var stride = 9 * 4;");
572
+ js.push(" gl.activeTexture(gl.TEXTURE0);");
573
+ js.push(" gl.bindTexture(gl.TEXTURE_2D, this.blockTexture);");
574
+ js.push(" gl.uniform1i(this.uTexture, 0);");
575
+ js.push("");
576
+ js.push(" var stride = 8 * 4;");
499
577
  js.push(" var keys = Object.keys(this.chunks);");
500
578
  js.push(" for (var i = 0; i < keys.length; i++) {");
501
579
  js.push(" var chunk = this.chunks[keys[i]];");
@@ -505,35 +583,13 @@ ElectronRunner.prototype.getRendererSource = function () {
505
583
  js.push(" gl.enableVertexAttribArray(1);");
506
584
  js.push(" gl.enableVertexAttribArray(2);");
507
585
  js.push(" gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, 0);");
508
- js.push(" gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, 12);");
509
- js.push(" gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, 24);");
586
+ js.push(" gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, 12);");
587
+ js.push(" gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, 20);");
510
588
  js.push(" gl.drawArrays(gl.TRIANGLES, 0, chunk.vertCount);");
511
589
  js.push(" }");
512
590
  js.push("};");
513
591
  js.push("");
514
592
 
515
- // HUD
516
- js.push("game.updateHUD = function () {");
517
- js.push(" var pcx = Math.floor(this.camX / CHUNK_SIZE);");
518
- js.push(" var pcz = Math.floor(this.camZ / CHUNK_SIZE);");
519
- js.push(" var loadedCount = Object.keys(this.chunks).length;");
520
- js.push(" var sprint = this.keys['ShiftLeft'] || this.keys['ShiftRight'];");
521
- js.push(" var html = '<div class=\"hud-title\">' + INFO_ID + '</div>';");
522
- js.push(" html += '<div>v' + INFO_VERSION + '</div>';");
523
- js.push(" html += '<div class=\"hud-sep\"></div>';");
524
- js.push(" html += '<div>FPS: ' + this.fps + '</div>';");
525
- js.push(" html += '<div>Triangles: ' + Math.floor(this.totalVerts / 3) + '</div>';");
526
- js.push(" html += '<div>Blocks: ' + this.totalBlocks + '</div>';");
527
- js.push(" html += '<div>Chunks: ' + loadedCount + '</div>';");
528
- js.push(" html += '<div class=\"hud-sep\"></div>';");
529
- js.push(" html += '<div>X: ' + this.camX.toFixed(1) + '</div>';");
530
- js.push(" html += '<div>Y: ' + this.camY.toFixed(1) + '</div>';");
531
- js.push(" html += '<div>Z: ' + this.camZ.toFixed(1) + '</div>';");
532
- js.push(" html += '<div>Chunk: ' + pcx + ', ' + pcz + '</div>';");
533
- js.push(" html += '<div class=\"hud-sep\"></div>';");
534
- js.push(" html += '<div>Sprint: ' + (sprint ? 'ON' : 'OFF') + '</div>';");
535
- js.push(" document.getElementById('hud-info').innerHTML = html;");
536
- js.push("};");
537
593
  js.push("");
538
594
 
539
595
  // Game loop
@@ -543,35 +599,18 @@ ElectronRunner.prototype.getRendererSource = function () {
543
599
  js.push(" game.update();");
544
600
  js.push(" updateChunks();");
545
601
  js.push(" game.render();");
546
- js.push(" game.updateHUD();");
547
602
  js.push("};");
548
603
  js.push("");
549
604
 
550
- // Loading animation
551
- js.push("var bar = document.getElementById('bar-inner');");
552
- js.push("var status = document.getElementById('load-status');");
553
- js.push("setTimeout(function () { bar.style.width = '25%'; status.textContent = 'Compiling shaders...'; }, 200);");
554
- js.push("setTimeout(function () { bar.style.width = '50%'; status.textContent = 'Generating chunks...'; }, 600);");
555
- js.push("setTimeout(function () { bar.style.width = '75%'; status.textContent = 'Building meshes...'; }, 1000);");
556
- js.push("setTimeout(function () { bar.style.width = '100%'; status.textContent = 'Ready!'; status.style.color = '#4ecca3'; }, 1400);");
557
- js.push("setTimeout(function () { game.init(); }, 1800);");
605
+ js.push("game.init();");
558
606
 
559
607
  var html = "<!DOCTYPE html>\n<html>\n<head>\n";
560
608
  html += "<meta charset='utf-8'>\n";
561
609
  html += "<title>" + windowTitle + " - CaG Engine</title>\n";
562
610
  html += "<style>\n" + css + "\n</style>\n";
563
611
  html += "</head>\n<body>\n";
564
- html += "<div id='loading'>\n";
565
- html += " <h1>CaG</h1>\n";
566
- html += " <h2>" + infoID + "</h2>\n";
567
- html += " <div id='load-status'>Starting...</div>\n";
568
- html += " <div id='bar-outer'><div id='bar-inner'></div></div>\n";
569
- html += " <div class='version-tag'>v" + infoVer + "</div>\n";
570
- html += "</div>\n";
571
- html += "<div id='click-overlay' style='display:none'><div class='play-icon'><div class='play-triangle'></div></div><span>CLICK TO PLAY</span></div>\n";
572
612
  html += "<div id='crosshair' style='display:none'></div>\n";
573
- html += "<div id='hud'><div id='hud-box'><div id='hud-info'></div></div></div>\n";
574
- html += "<div id='controls-hint'><b>WASD</b> Move &nbsp; <b>Mouse</b> Look &nbsp; <b>Space</b> Jump &nbsp; <b>Shift</b> Sprint &nbsp; <b>F11</b> Fullscreen &nbsp; <b>Esc</b> Release</div>\n";
613
+ html += "";
575
614
  html += "<canvas id='cag-canvas'></canvas>\n";
576
615
  html += "<script>\n" + js.join("\n") + "\n</script>\n";
577
616
  html += "</body>\n</html>";
@@ -638,6 +677,32 @@ ElectronRunner.extractSettings = function (executor) {
638
677
  settings.renderDistance = chunkAPI.renderDistance;
639
678
  }
640
679
 
680
+ if (registry.has("Model.API")) {
681
+ var modelAPI = registry.get("Model.API");
682
+ var loadedModels = modelAPI.getLoadedModels();
683
+ settings.models = {};
684
+ if (loadedModels) {
685
+ var modelNames = Object.keys(loadedModels);
686
+ for (var mi = 0; mi < modelNames.length; mi++) {
687
+ var mName = modelNames[mi];
688
+ var model = loadedModels[mName];
689
+ if (model && model.valid) {
690
+ var texW = model.texture ? model.texture.width : 0;
691
+ var texH = model.texture ? model.texture.height : 0;
692
+ if (!texW || !texH) {
693
+ texW = model.geometry ? model.geometry.resolution.width : 16;
694
+ texH = model.geometry ? model.geometry.resolution.height : 16;
695
+ }
696
+ settings.models[mName] = {
697
+ uvMappings: model.uvMappings || [],
698
+ resolution: { width: texW, height: texH },
699
+ textureDataURL: model.texture && model.texture.dataURL ? model.texture.dataURL : null
700
+ };
701
+ }
702
+ }
703
+ }
704
+ }
705
+
641
706
  return settings;
642
707
  };
643
708