nova64 0.2.4 → 0.2.6

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.
Files changed (140) hide show
  1. package/README.md +25 -8
  2. package/bin/nova64.js +165 -0
  3. package/dist/assets/console-CY_kygm3.js +14 -0
  4. package/dist/assets/console-CY_kygm3.js.map +1 -0
  5. package/dist/assets/main-l0sNRNKZ.js.map +1 -0
  6. package/dist/assets/sky/studio/nx.png +0 -0
  7. package/dist/assets/sky/studio/ny.png +0 -0
  8. package/dist/assets/sky/studio/nz.png +0 -0
  9. package/dist/assets/sky/studio/px.png +0 -0
  10. package/dist/assets/sky/studio/py.png +0 -0
  11. package/dist/assets/sky/studio/pz.png +0 -0
  12. package/dist/assets/vanilla-Dcuy32gi.js +2 -0
  13. package/dist/assets/vanilla-Dcuy32gi.js.map +1 -0
  14. package/dist/console.html +899 -0
  15. package/dist/docs/BENCHMARK.md +77 -0
  16. package/dist/docs/CHEATSHEET.md +255 -0
  17. package/dist/docs/EFFECTS_API_GUIDE.md +577 -0
  18. package/dist/docs/EFFECTS_QUICK_REFERENCE.md +331 -0
  19. package/dist/docs/FONT_CHARACTER_REFERENCE.md +219 -0
  20. package/dist/docs/FREE_GLB_ASSETS.md +330 -0
  21. package/dist/docs/FULLSCREEN_BUTTON_FEATURE.md +296 -0
  22. package/dist/docs/GAMEPAD_SUPPORT.md +348 -0
  23. package/dist/docs/GAME_IMPROVEMENTS.md +278 -0
  24. package/dist/docs/GAME_QUALITY_STATUS.md +300 -0
  25. package/dist/docs/MIGRATION_GUIDE.md +553 -0
  26. package/dist/docs/NOVA64_3D_API.md +356 -0
  27. package/dist/docs/NOVA64_API_REFERENCE.md +1406 -0
  28. package/dist/docs/NOVA64_UI_API.md +503 -0
  29. package/dist/docs/UI_SYSTEM_SUMMARY.md +445 -0
  30. package/dist/docs/VOXEL_ENGINE_GUIDE.md +662 -0
  31. package/dist/docs/VOXEL_QUICK_REFERENCE.md +386 -0
  32. package/dist/docs/api-3d.html +750 -0
  33. package/dist/docs/api-effects.html +385 -0
  34. package/dist/docs/api-improvements.md +121 -0
  35. package/dist/docs/api-skybox.html +407 -0
  36. package/dist/docs/api-sprites.html +321 -0
  37. package/dist/docs/api-voxel.html +337 -0
  38. package/dist/docs/api.html +543 -0
  39. package/dist/docs/assets.html +306 -0
  40. package/dist/docs/audio.html +340 -0
  41. package/dist/docs/blogs.html +286 -0
  42. package/dist/docs/collision.html +316 -0
  43. package/dist/docs/console.html +247 -0
  44. package/dist/docs/editor.html +297 -0
  45. package/dist/docs/font.html +247 -0
  46. package/dist/docs/framebuffer.html +247 -0
  47. package/dist/docs/fullscreen-button.html +297 -0
  48. package/dist/docs/gpu-systems.html +247 -0
  49. package/dist/docs/index.html +580 -0
  50. package/dist/docs/input.html +491 -0
  51. package/dist/docs/physics.html +311 -0
  52. package/dist/docs/screens.html +311 -0
  53. package/dist/docs/storage.html +311 -0
  54. package/dist/docs/textinput.html +332 -0
  55. package/dist/docs/ui.html +488 -0
  56. package/dist/examples/3d-advanced/code.js +695 -0
  57. package/dist/examples/adventure-comic-3d/code.js +342 -0
  58. package/dist/examples/audio-lab/code.js +150 -0
  59. package/dist/examples/boids-flocking/code.js +270 -0
  60. package/dist/examples/crystal-cathedral-3d/code.js +706 -0
  61. package/dist/examples/cyberpunk-city-3d/code.js +1383 -0
  62. package/dist/examples/demoscene/README.md +192 -0
  63. package/dist/examples/demoscene/code.js +1081 -0
  64. package/dist/examples/demoscene/meta.json +21 -0
  65. package/dist/examples/dungeon-crawler-3d/code.js +1117 -0
  66. package/dist/examples/f-zero-nova-3d/code.js +865 -0
  67. package/dist/examples/f-zero-nova-3d/code_old.js +1555 -0
  68. package/dist/examples/fps-demo-3d/code.js +744 -0
  69. package/dist/examples/game-of-life-3d/code.js +338 -0
  70. package/dist/examples/generative-art/code.js +632 -0
  71. package/dist/examples/hello-3d/code.js +325 -0
  72. package/dist/examples/hello-skybox/code.js +183 -0
  73. package/dist/examples/hello-world/code.js +19 -0
  74. package/dist/examples/input-showcase/code.js +109 -0
  75. package/dist/examples/instancing-demo/code.js +315 -0
  76. package/dist/examples/minecraft-demo/code.js +387 -0
  77. package/dist/examples/model-viewer-3d/code.js +114 -0
  78. package/dist/examples/mystical-realm-3d/code.js +1203 -0
  79. package/dist/examples/nature-explorer-3d/code.js +1318 -0
  80. package/dist/examples/particles-demo/code.js +522 -0
  81. package/dist/examples/pbr-showcase/code.js +140 -0
  82. package/dist/examples/physics-demo-3d/code.js +948 -0
  83. package/dist/examples/screen-demo/code.js +267 -0
  84. package/dist/examples/shooter-demo-3d/code.js +1286 -0
  85. package/dist/examples/space-combat-3d/IMPLEMENTATION_SUMMARY.md +109 -0
  86. package/dist/examples/space-combat-3d/README.md +135 -0
  87. package/dist/examples/space-combat-3d/code.js +1332 -0
  88. package/dist/examples/space-harrier-3d/code.js +923 -0
  89. package/dist/examples/star-fox-nova-3d/code.js +1116 -0
  90. package/dist/examples/star-fox-nova-3d/code_backup.js +410 -0
  91. package/dist/examples/star-fox-nova-3d/code_broken.js +1821 -0
  92. package/dist/examples/storage-quest/code.js +209 -0
  93. package/dist/examples/strider-demo-3d/IMPROVEMENT_OPTIONS.md +285 -0
  94. package/dist/examples/strider-demo-3d/cache-test.html +132 -0
  95. package/dist/examples/strider-demo-3d/code-fixed.js +582 -0
  96. package/dist/examples/strider-demo-3d/code-old.js +1537 -0
  97. package/dist/examples/strider-demo-3d/code.js +1462 -0
  98. package/dist/examples/strider-demo-3d/code.js.bak2 +1169 -0
  99. package/dist/examples/strider-demo-3d/fix-game.sh +53 -0
  100. package/dist/examples/super-plumber-64/README.md +128 -0
  101. package/dist/examples/super-plumber-64/code.js +1185 -0
  102. package/dist/examples/super-plumber-64/index.html +88 -0
  103. package/dist/examples/test-2d-overlay/code.js +32 -0
  104. package/dist/examples/test-font/code.js +51 -0
  105. package/dist/examples/test-minimal/code.js +21 -0
  106. package/dist/examples/ui-demo/code.js +306 -0
  107. package/dist/examples/wing-commander-space/README.md +180 -0
  108. package/dist/examples/wing-commander-space/code.js +1285 -0
  109. package/dist/examples/wizardry-3d/CHANGELOG.md +366 -0
  110. package/dist/examples/wizardry-3d/code.js +3928 -0
  111. package/dist/index.html +666 -0
  112. package/dist/os9-shell/assets/index-DIHfrTaW.css +1 -0
  113. package/dist/os9-shell/assets/index-KchE_ngx.js +483 -0
  114. package/dist/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  115. package/dist/os9-shell/index.html +23 -0
  116. package/dist/os9-shell/nova-icon.svg +12 -0
  117. package/index.html +6 -1
  118. package/package.json +37 -32
  119. package/public/assets/sky/studio/nx.png +0 -0
  120. package/public/assets/sky/studio/ny.png +0 -0
  121. package/public/assets/sky/studio/nz.png +0 -0
  122. package/public/assets/sky/studio/px.png +0 -0
  123. package/public/assets/sky/studio/py.png +0 -0
  124. package/public/assets/sky/studio/pz.png +0 -0
  125. package/public/os9-shell/assets/index-KchE_ngx.js +483 -0
  126. package/public/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  127. package/public/os9-shell/index.html +10 -1
  128. package/runtime/api-2d.js +301 -21
  129. package/runtime/api-3d/pbr.js +45 -1
  130. package/runtime/api-3d.js +1 -0
  131. package/runtime/api-effects.js +90 -3
  132. package/runtime/api-gameutils.js +476 -0
  133. package/runtime/api-generative.js +610 -0
  134. package/runtime/api-skybox.js +54 -0
  135. package/runtime/api-voxel.js +139 -28
  136. package/runtime/gpu-threejs.js +13 -9
  137. package/runtime/ui.js +2 -2
  138. package/src/main.js +24 -1
  139. package/public/os9-shell/assets/index-B1Uvacma.js +0 -32825
  140. package/public/os9-shell/assets/index-B1Uvacma.js.map +0 -1
@@ -37,27 +37,28 @@ export function voxelApi(gpu) {
37
37
  BEDROCK: 14,
38
38
  };
39
39
 
40
- // Block colors (for texture-less rendering)
40
+ // Block colors (for texture-less rendering) — tuned for visual biome distinction
41
41
  const BLOCK_COLORS = {
42
- [BLOCK_TYPES.GRASS]: 0x44aa22,
43
- [BLOCK_TYPES.DIRT]: 0x885533,
44
- [BLOCK_TYPES.STONE]: 0x999999,
42
+ [BLOCK_TYPES.GRASS]: 0x55cc33,
43
+ [BLOCK_TYPES.DIRT]: 0x996644,
44
+ [BLOCK_TYPES.STONE]: 0xaaaaaa,
45
45
  [BLOCK_TYPES.SAND]: 0xffdd88,
46
- [BLOCK_TYPES.WATER]: 0x33aaff,
46
+ [BLOCK_TYPES.WATER]: 0x2288dd,
47
47
  [BLOCK_TYPES.WOOD]: 0x774422,
48
- [BLOCK_TYPES.LEAVES]: 0x228822,
49
- [BLOCK_TYPES.COBBLESTONE]: 0x888888,
48
+ [BLOCK_TYPES.LEAVES]: 0x116622,
49
+ [BLOCK_TYPES.COBBLESTONE]: 0x667788,
50
50
  [BLOCK_TYPES.PLANKS]: 0xddaa55,
51
51
  [BLOCK_TYPES.GLASS]: 0xccffff,
52
52
  [BLOCK_TYPES.BRICK]: 0xcc4433,
53
- [BLOCK_TYPES.SNOW]: 0xffffff,
54
- [BLOCK_TYPES.ICE]: 0xbbffff,
53
+ [BLOCK_TYPES.SNOW]: 0xeeeeff,
54
+ [BLOCK_TYPES.ICE]: 0x99ddff,
55
55
  [BLOCK_TYPES.BEDROCK]: 0x333333,
56
56
  };
57
57
 
58
58
  // World data
59
59
  const chunks = new Map(); // key: "x,z" -> Chunk
60
60
  const chunkMeshes = new Map(); // key: "x,z" -> THREE.Mesh
61
+ let worldSeed = Math.floor(Math.random() * 50000); // random start for biome variety
61
62
 
62
63
  // Noise function for terrain generation (simple Perlin-like)
63
64
  function noise2D(x, z) {
@@ -160,39 +161,101 @@ export function voxelApi(gpu) {
160
161
  const baseX = chunk.chunkX * CHUNK_SIZE;
161
162
  const baseZ = chunk.chunkZ * CHUNK_SIZE;
162
163
 
164
+ // Pending trees to place after terrain fill (avoid overwriting terrain)
165
+ const pendingTrees = [];
166
+
163
167
  for (let x = 0; x < CHUNK_SIZE; x++) {
164
168
  for (let z = 0; z < CHUNK_SIZE; z++) {
165
169
  const worldX = baseX + x;
166
170
  const worldZ = baseZ + z;
167
171
 
168
- // Generate height using Perlin noise
169
- const height = Math.floor(perlinNoise(worldX, worldZ, 4, 0.5) * 20 + 32);
172
+ // Biome selection via temperature + moisture noise
173
+ const temperature = perlinNoise(worldX * 0.5 + worldSeed, worldZ * 0.5 + worldSeed, 2, 0.5);
174
+ const moisture = perlinNoise(
175
+ worldX * 0.3 + 1000 + worldSeed,
176
+ worldZ * 0.3 + 1000 + worldSeed,
177
+ 2,
178
+ 0.5
179
+ );
180
+
181
+ // Per-biome height profile — each biome has unique surface, terrain shape, and density
182
+ let heightScale = 20;
183
+ let heightBase = 32;
184
+ let surfaceBlock = BLOCK_TYPES.GRASS;
185
+ let subBlock = BLOCK_TYPES.DIRT;
186
+ let treeChance = 0;
187
+ let waterLevel = 30;
188
+
189
+ if (temperature < 0.2) {
190
+ // ── Frozen Tundra ── flat icy plains
191
+ surfaceBlock = BLOCK_TYPES.SNOW;
192
+ subBlock = BLOCK_TYPES.ICE;
193
+ heightScale = 6;
194
+ heightBase = 33;
195
+ treeChance = 0;
196
+ } else if (temperature < 0.35 && moisture > 0.5) {
197
+ // ── Taiga ── gray rocky forest
198
+ surfaceBlock = BLOCK_TYPES.COBBLESTONE;
199
+ subBlock = BLOCK_TYPES.STONE;
200
+ heightScale = 18;
201
+ heightBase = 34;
202
+ treeChance = 0.06;
203
+ } else if (temperature > 0.7 && moisture < 0.25) {
204
+ // ── Desert ── flat sand dunes
205
+ surfaceBlock = BLOCK_TYPES.SAND;
206
+ subBlock = BLOCK_TYPES.SAND;
207
+ heightScale = 4;
208
+ heightBase = 31;
209
+ treeChance = 0;
210
+ } else if (temperature > 0.6 && moisture > 0.6) {
211
+ // ── Jungle ── dense dark-green valleys
212
+ surfaceBlock = BLOCK_TYPES.LEAVES;
213
+ subBlock = BLOCK_TYPES.DIRT;
214
+ heightScale = 22;
215
+ heightBase = 28;
216
+ treeChance = 0.15;
217
+ } else if (moisture < 0.3) {
218
+ // ── Savanna ── dry brown earth
219
+ surfaceBlock = BLOCK_TYPES.DIRT;
220
+ subBlock = BLOCK_TYPES.SAND;
221
+ heightScale = 5;
222
+ heightBase = 33;
223
+ treeChance = 0.005;
224
+ } else if (temperature > 0.4 && moisture > 0.4) {
225
+ // ── Forest ── classic green hills
226
+ surfaceBlock = BLOCK_TYPES.GRASS;
227
+ subBlock = BLOCK_TYPES.DIRT;
228
+ heightScale = 14;
229
+ heightBase = 32;
230
+ treeChance = 0.08;
231
+ } else if (temperature < 0.35) {
232
+ // ── Snowy Hills ── tall snowy mountains
233
+ surfaceBlock = BLOCK_TYPES.SNOW;
234
+ subBlock = BLOCK_TYPES.STONE;
235
+ heightScale = 35;
236
+ heightBase = 30;
237
+ treeChance = 0.02;
238
+ } else {
239
+ // ── Plains ── gentle rolling grassland
240
+ surfaceBlock = BLOCK_TYPES.GRASS;
241
+ subBlock = BLOCK_TYPES.DIRT;
242
+ heightScale = 6;
243
+ heightBase = 32;
244
+ treeChance = 0.015;
245
+ }
170
246
 
171
- // Biome selection
172
- const temperature = perlinNoise(worldX * 0.5, worldZ * 0.5, 2, 0.5);
173
- const moisture = perlinNoise(worldX * 0.3 + 1000, worldZ * 0.3 + 1000, 2, 0.5);
247
+ const height = Math.floor(perlinNoise(worldX, worldZ, 4, 0.5) * heightScale + heightBase);
174
248
 
175
249
  for (let y = 0; y < CHUNK_HEIGHT; y++) {
176
250
  if (y === 0) {
177
- // Bedrock layer
178
251
  chunk.setBlock(x, y, z, BLOCK_TYPES.BEDROCK);
179
252
  } else if (y < height - 3) {
180
- // Stone layer
181
253
  chunk.setBlock(x, y, z, BLOCK_TYPES.STONE);
182
254
  } else if (y < height - 1) {
183
- // Dirt layer
184
- chunk.setBlock(x, y, z, BLOCK_TYPES.DIRT);
255
+ chunk.setBlock(x, y, z, subBlock);
185
256
  } else if (y === height - 1) {
186
- // Top layer based on biome
187
- if (temperature < 0.3) {
188
- chunk.setBlock(x, y, z, BLOCK_TYPES.SNOW);
189
- } else if (moisture < 0.3) {
190
- chunk.setBlock(x, y, z, BLOCK_TYPES.SAND);
191
- } else {
192
- chunk.setBlock(x, y, z, BLOCK_TYPES.GRASS);
193
- }
194
- } else if (y < 30 && y >= height) {
195
- // Water level
257
+ chunk.setBlock(x, y, z, surfaceBlock);
258
+ } else if (y < waterLevel && y >= height) {
196
259
  chunk.setBlock(x, y, z, BLOCK_TYPES.WATER);
197
260
  }
198
261
 
@@ -204,6 +267,38 @@ export function voxelApi(gpu) {
204
267
  }
205
268
  }
206
269
  }
270
+
271
+ // Random tree placement (seeded by world position)
272
+ if (height > waterLevel && treeChance > 0) {
273
+ const treeSeed = Math.sin(worldX * 12.9898 + worldZ * 78.233) * 43758.5453;
274
+ const treeRoll = treeSeed - Math.floor(treeSeed);
275
+ if (treeRoll < treeChance && x > 2 && x < CHUNK_SIZE - 3 && z > 2 && z < CHUNK_SIZE - 3) {
276
+ pendingTrees.push({ x, y: height, z });
277
+ }
278
+ }
279
+ }
280
+ }
281
+
282
+ // Place trees after terrain is fully generated
283
+ for (const t of pendingTrees) {
284
+ const trunkHeight = 4 + Math.floor(Math.abs(Math.sin(t.x * 7 + t.z * 13)) * 3);
285
+ for (let i = 0; i < trunkHeight; i++) {
286
+ chunk.setBlock(t.x, t.y + i, t.z, BLOCK_TYPES.WOOD);
287
+ }
288
+ const leafY = t.y + trunkHeight;
289
+ for (let dx = -2; dx <= 2; dx++) {
290
+ for (let dy = -2; dy <= 2; dy++) {
291
+ for (let dz = -2; dz <= 2; dz++) {
292
+ if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) < 4) {
293
+ const lx = t.x + dx,
294
+ ly = leafY + dy,
295
+ lz = t.z + dz;
296
+ if (lx >= 0 && lx < CHUNK_SIZE && lz >= 0 && lz < CHUNK_SIZE && ly < CHUNK_HEIGHT) {
297
+ chunk.setBlock(lx, ly, lz, BLOCK_TYPES.LEAVES);
298
+ }
299
+ }
300
+ }
301
+ }
207
302
  }
208
303
  }
209
304
  }
@@ -494,6 +589,18 @@ export function voxelApi(gpu) {
494
589
  keysToRemove.forEach(key => chunkMeshes.delete(key));
495
590
  }
496
591
 
592
+ // Reset entire world (clear all chunks and meshes)
593
+ function resetWorld() {
594
+ for (const mesh of chunkMeshes.values()) {
595
+ gpu.scene.remove(mesh);
596
+ mesh.geometry.dispose();
597
+ mesh.material.dispose();
598
+ }
599
+ chunkMeshes.clear();
600
+ chunks.clear();
601
+ worldSeed += 5000 + Math.floor(Math.random() * 10000);
602
+ }
603
+
497
604
  // Raycast to find block player is looking at
498
605
  function raycastBlock(origin, direction, maxDistance = 10) {
499
606
  const step = 0.1;
@@ -587,6 +694,9 @@ export function voxelApi(gpu) {
587
694
  // Structures
588
695
  placeTree,
589
696
 
697
+ // World reset
698
+ resetWorld,
699
+
590
700
  // Expose to global game context
591
701
  exposeTo: function (g) {
592
702
  g.BLOCK_TYPES = BLOCK_TYPES;
@@ -596,6 +706,7 @@ export function voxelApi(gpu) {
596
706
  g.raycastVoxelBlock = raycastBlock;
597
707
  g.checkVoxelCollision = checkCollision;
598
708
  g.placeVoxelTree = placeTree;
709
+ g.resetVoxelWorld = resetWorld;
599
710
  },
600
711
  };
601
712
  }
@@ -29,7 +29,7 @@ export class GpuThreeJS {
29
29
 
30
30
  // Dramatically enhanced visual rendering setup
31
31
  this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
32
- this.renderer.toneMappingExposure = 1.6;
32
+ this.renderer.toneMappingExposure = 1.25;
33
33
  this.renderer.shadowMap.enabled = true;
34
34
  // Note: PCFSoftShadowMap is deprecated in r182, PCFShadowMap now provides soft shadows
35
35
  this.renderer.shadowMap.type = THREE.PCFShadowMap;
@@ -84,11 +84,11 @@ export class GpuThreeJS {
84
84
 
85
85
  setupN64Lighting() {
86
86
  // Multi-layered ambient lighting for rich atmosphere
87
- const ambientLight = new THREE.AmbientLight(0x404060, 0.3);
87
+ const ambientLight = new THREE.AmbientLight(0x606080, 0.5);
88
88
  this.scene.add(ambientLight);
89
89
 
90
90
  // Hemisphere light for more natural lighting
91
- const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.4);
91
+ const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.6);
92
92
  this.scene.add(hemisphereLight);
93
93
 
94
94
  // Main directional light with ultra-high quality shadows
@@ -153,8 +153,8 @@ export class GpuThreeJS {
153
153
  // Envmap setup is non-critical; silently skip on unsupported renderers
154
154
  }
155
155
 
156
- // Dramatic volumetric fog with color gradients
157
- this.scene.fog = new THREE.FogExp2(0x202050, 0.008);
156
+ // Atmospheric fog with subtle color
157
+ this.scene.fog = new THREE.FogExp2(0x202050, 0.005);
158
158
 
159
159
  // Store lights for dynamic control
160
160
  this.lights = {
@@ -541,7 +541,11 @@ export class GpuThreeJS {
541
541
 
542
542
  // N64-style post-processing
543
543
  enablePixelation(factor = 2) {
544
- this.renderer.setPixelRatio(1 / factor);
544
+ if (factor <= 0) {
545
+ this.renderer.setPixelRatio(window.devicePixelRatio || 1);
546
+ } else {
547
+ this.renderer.setPixelRatio(1 / factor);
548
+ }
545
549
  }
546
550
 
547
551
  enableDithering(enabled = true) {
@@ -551,9 +555,9 @@ export class GpuThreeJS {
551
555
  enableBloom(enabled = true) {
552
556
  // For now, just increase exposure for bloom-like effect
553
557
  if (enabled) {
554
- this.renderer.toneMappingExposure = 1.8;
558
+ this.renderer.toneMappingExposure = 1.2;
555
559
  } else {
556
- this.renderer.toneMappingExposure = 1.4;
560
+ this.renderer.toneMappingExposure = 1.0;
557
561
  }
558
562
  }
559
563
 
@@ -621,7 +625,7 @@ export class GpuThreeJS {
621
625
 
622
626
  // Fog animation for atmospheric depth
623
627
  if (this.scene.fog && this.scene.fog.density) {
624
- this.scene.fog.density = 0.008 + Math.sin(time * 0.5) * 0.002;
628
+ this.scene.fog.density = 0.005 + Math.sin(time * 0.5) * 0.001;
625
629
  }
626
630
  }
627
631
 
package/runtime/ui.js CHANGED
@@ -83,8 +83,8 @@ export function uiApi(gpu, g) {
83
83
  removeButton: ctx.removeButton,
84
84
  clearButtons: ctx.clearButtons,
85
85
 
86
- // Progress bars
87
- drawProgressBar: ctx.drawProgressBar,
86
+ // Progress bars (as uiProgressBar to avoid overwriting the api-2d drawProgressBar)
87
+ uiProgressBar: ctx.drawProgressBar,
88
88
 
89
89
  // Advanced shapes
90
90
  drawRoundedRect: ctx.drawRoundedRect,
package/src/main.js CHANGED
@@ -21,6 +21,8 @@ import { createFullscreenButton } from '../runtime/fullscreen-button.js';
21
21
  import { storeApi } from '../runtime/store.js';
22
22
  import { api2d } from '../runtime/api-2d.js';
23
23
  import { presetsApi } from '../runtime/api-presets.js';
24
+ import { generativeApi } from '../runtime/api-generative.js';
25
+ import { gameUtilsApi } from '../runtime/api-gameutils.js';
24
26
 
25
27
  const canvas = document.getElementById('screen');
26
28
 
@@ -53,6 +55,8 @@ const vxApi = voxelApi(gpu);
53
55
  const storeApiInst = storeApi();
54
56
  const api2dInst = api2d(gpu);
55
57
  const presetsInst = presetsApi(gpu);
58
+ const genArtInst = generativeApi(gpu);
59
+ const gameUtilsInst = gameUtilsApi();
56
60
 
57
61
  // Create UI API - needs to be created after api is fully initialized
58
62
  let uiApiInstance;
@@ -76,6 +80,8 @@ vxApi.exposeTo(g);
76
80
  storeApiInst.exposeTo(g);
77
81
  api2dInst.exposeTo(g);
78
82
  presetsInst.exposeTo(g);
83
+ genArtInst.exposeTo(g);
84
+ gameUtilsInst.exposeTo(g);
79
85
 
80
86
  // Now create UI API after g has rgba8 and other functions
81
87
  uiApiInstance = uiApi(gpu, g);
@@ -146,13 +152,14 @@ function loop() {
146
152
  last = now;
147
153
 
148
154
  if (!paused || stepOnce) {
149
- iApi.step();
150
155
  const u0 = performance.now();
151
156
 
152
157
  // Tick the global novaStore time counter
153
158
  storeApiInst.tick(dt);
154
159
  // Auto-animate skybox if enabled
155
160
  skyApi._tick(dt);
161
+ // Advance generative art frame counter
162
+ genArtInst._advanceFrame();
156
163
  // Update post-processing shader uniforms (time, etc.)
157
164
  fxApi.update(dt);
158
165
 
@@ -198,6 +205,10 @@ function loop() {
198
205
  } catch (e) {
199
206
  console.error('❌ gpu.endFrame() error:', e.message, e.stack);
200
207
  }
208
+
209
+ // Snapshot input state at END of frame so keyp/isKeyPressed correctly
210
+ // detect transitions on the next frame (key pressed between frames).
211
+ iApi.step();
201
212
  }
202
213
  if (stepOnce) {
203
214
  stepOnce = false;
@@ -238,6 +249,11 @@ const gameMap = {
238
249
  demoscene: '/examples/demoscene/code.js',
239
250
  'space-combat': '/examples/star-fox-nova-3d/code.js',
240
251
  minecraft: '/examples/minecraft-demo/code.js',
252
+ boids: '/examples/boids-flocking/code.js',
253
+ 'game-of-life': '/examples/game-of-life-3d/code.js',
254
+ nature: '/examples/nature-explorer-3d/code.js',
255
+ dungeon: '/examples/dungeon-crawler-3d/code.js',
256
+ wizardry: '/examples/wizardry-3d/code.js',
241
257
  };
242
258
 
243
259
  // Map demo names (from ?demo= URL param) to paths
@@ -270,6 +286,13 @@ const demoMap = {
270
286
  'space-combat-3d': '/examples/space-combat-3d/code.js',
271
287
  'model-viewer-3d': '/examples/model-viewer-3d/code.js',
272
288
  '3d-advanced': '/examples/3d-advanced/code.js',
289
+ 'pbr-showcase': '/examples/pbr-showcase/code.js',
290
+ 'generative-art': '/examples/generative-art/code.js',
291
+ 'boids-flocking': '/examples/boids-flocking/code.js',
292
+ 'game-of-life-3d': '/examples/game-of-life-3d/code.js',
293
+ 'nature-explorer-3d': '/examples/nature-explorer-3d/code.js',
294
+ 'dungeon-crawler-3d': '/examples/dungeon-crawler-3d/code.js',
295
+ 'wizardry-3d': '/examples/wizardry-3d/code.js',
273
296
  };
274
297
 
275
298
  // default cart - load from URL param or default to space-harrier-3d