minecraft-renderer 0.1.48 → 0.1.49

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 (32) hide show
  1. package/dist/mesher.js +1 -1
  2. package/dist/mesher.js.map +2 -2
  3. package/dist/mesherWasm.js +3740 -183
  4. package/dist/minecraft-renderer.js +332 -60
  5. package/dist/minecraft-renderer.js.meta.json +1 -1
  6. package/dist/threeWorker.js +705 -433
  7. package/package.json +1 -1
  8. package/src/graphicsBackend/config.ts +4 -0
  9. package/src/graphicsBackend/playerState.ts +1 -0
  10. package/src/lib/worldrendererCommon.ts +13 -0
  11. package/src/mesher-shared/exportedGeometryTypes.ts +5 -1
  12. package/src/mesher-shared/shared.ts +8 -0
  13. package/src/three/chunkMeshManager.ts +312 -39
  14. package/src/three/globalBlockBuffer.ts +292 -0
  15. package/src/three/menuBackground/config.ts +1 -1
  16. package/src/three/menuBackground/defaultOptions.ts +18 -18
  17. package/src/three/modules/sciFiWorldReveal.ts +162 -68
  18. package/src/three/modules/starfield.ts +9 -1
  19. package/src/three/sectionRaycastAabb.ts +167 -0
  20. package/src/three/shaderCubeMesh.ts +93 -0
  21. package/src/three/shaders/cubeBlockShader.ts +354 -0
  22. package/src/three/shaders/textureIndexMapping.ts +122 -0
  23. package/src/three/shaders/tintPalette.ts +198 -0
  24. package/src/three/worldGeometryExport.ts +53 -25
  25. package/src/three/worldRendererThree.ts +56 -23
  26. package/src/wasm-mesher/bridge/render-from-wasm.ts +62 -185
  27. package/src/wasm-mesher/bridge/shaderCubeBridge.ts +396 -0
  28. package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
  29. package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +58 -0
  30. package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +360 -0
  31. package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +11 -4
  32. package/src/wasm-mesher/worker/mesherWasm.ts +17 -2
@@ -0,0 +1,292 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import { VERTICES_PER_FACE } from './shaders/cubeBlockShader'
4
+ import { packWord2Empty } from '../wasm-mesher/bridge/shaderCubeBridge'
5
+
6
+ const INITIAL_CAPACITY_FACES = 2_000_000
7
+ const EMPTY_W2 = packWord2Empty()
8
+
9
+ export type GlobalBlockBufferShaderData = {
10
+ words: Uint32Array
11
+ count: number
12
+ }
13
+
14
+ /**
15
+ * Single GPU instanced mesh for all shader-cube faces in the world.
16
+ * Camera-relative positioning via u_cameraOrigin; no sceneOrigin tracking.
17
+ */
18
+ export class GlobalBlockBuffer {
19
+ readonly mesh: THREE.Mesh<THREE.InstancedBufferGeometry, THREE.ShaderMaterial>
20
+
21
+ private capacityFaces: number
22
+ private w0: Uint32Array
23
+ private w1: Uint32Array
24
+ private w2: Uint32Array
25
+ private w3: Uint32Array
26
+ private readonly sectionSlots = new Map<string, { start: number, count: number }>()
27
+ private freeList: Array<{ start: number, count: number }> = []
28
+ private highWatermark = 0
29
+ private dirtyMin = Infinity
30
+ private dirtyMax = -1
31
+
32
+ constructor (
33
+ material: THREE.ShaderMaterial,
34
+ scene: THREE.Object3D,
35
+ ) {
36
+ this.capacityFaces = INITIAL_CAPACITY_FACES
37
+ this.w0 = new Uint32Array(this.capacityFaces)
38
+ this.w1 = new Uint32Array(this.capacityFaces)
39
+ this.w2 = new Uint32Array(this.capacityFaces)
40
+ this.w3 = new Uint32Array(this.capacityFaces)
41
+
42
+ const geometry = new THREE.InstancedBufferGeometry()
43
+ const positions = new Float32Array(VERTICES_PER_FACE * 3)
44
+ geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
45
+
46
+ const mkAttr = (arr: Uint32Array) => {
47
+ const attr = new THREE.InstancedBufferAttribute(arr, 1)
48
+ attr.setUsage(THREE.DynamicDrawUsage)
49
+ return attr
50
+ }
51
+ geometry.setAttribute('a_w0', mkAttr(this.w0))
52
+ geometry.setAttribute('a_w1', mkAttr(this.w1))
53
+ geometry.setAttribute('a_w2', mkAttr(this.w2))
54
+ geometry.setAttribute('a_w3', mkAttr(this.w3))
55
+
56
+ geometry.instanceCount = 0
57
+
58
+ this.mesh = new THREE.Mesh(geometry, material)
59
+ this.mesh.name = 'globalShaderCubes'
60
+ this.mesh.frustumCulled = false
61
+ this.mesh.matrixAutoUpdate = false
62
+ this.mesh.matrix.identity()
63
+ this.mesh.position.set(0, 0, 0)
64
+ scene.add(this.mesh)
65
+ }
66
+
67
+ addSection (sectionKey: string, words: Uint32Array, faceCount: number): void {
68
+ if (faceCount <= 0) {
69
+ this.removeSection(sectionKey)
70
+ return
71
+ }
72
+
73
+ if (this.sectionSlots.has(sectionKey)) {
74
+ this.removeSection(sectionKey)
75
+ }
76
+
77
+ if (faceCount > this.capacityFaces) {
78
+ this.growCapacity(faceCount)
79
+ }
80
+
81
+ let slot = this.takeFreeSlot(faceCount)
82
+ if (!slot) {
83
+ if (this.highWatermark + faceCount > this.capacityFaces) {
84
+ this.growCapacity(this.highWatermark + faceCount)
85
+ }
86
+ slot = { start: this.highWatermark, count: faceCount }
87
+ this.highWatermark += faceCount
88
+ }
89
+
90
+ const stride = 4
91
+ for (let i = 0; i < faceCount; i++) {
92
+ const dst = slot.start + i
93
+ const src = i * stride
94
+ this.w0[dst] = words[src]!
95
+ this.w1[dst] = words[src + 1]!
96
+ this.w2[dst] = words[src + 2]!
97
+ this.w3[dst] = words[src + 3]!
98
+ }
99
+
100
+ this.sectionSlots.set(sectionKey, slot)
101
+ this.markDirty(slot.start, slot.start + faceCount - 1)
102
+ this.mesh.geometry.instanceCount = this.highWatermark
103
+ }
104
+
105
+ hasSection (sectionKey: string): boolean {
106
+ return this.sectionSlots.has(sectionKey)
107
+ }
108
+
109
+ /** Copy live GPU words and remove the section (sci-fi reveal hide / restore). */
110
+ takeSectionData (sectionKey: string): GlobalBlockBufferShaderData | undefined {
111
+ const slot = this.sectionSlots.get(sectionKey)
112
+ if (!slot) return undefined
113
+
114
+ const stride = 4
115
+ const words = new Uint32Array(slot.count * stride)
116
+ for (let i = 0; i < slot.count; i++) {
117
+ const dst = slot.start + i
118
+ const src = i * stride
119
+ words[src] = this.w0[dst]!
120
+ words[src + 1] = this.w1[dst]!
121
+ words[src + 2] = this.w2[dst]!
122
+ words[src + 3] = this.w3[dst]!
123
+ }
124
+ this.removeSection(sectionKey)
125
+ return { words, count: slot.count }
126
+ }
127
+
128
+ removeSection (sectionKey: string): void {
129
+ const slot = this.sectionSlots.get(sectionKey)
130
+ if (!slot) return
131
+
132
+ for (let i = slot.start; i < slot.start + slot.count; i++) {
133
+ this.w0[i] = 0
134
+ this.w1[i] = 0
135
+ this.w2[i] = EMPTY_W2
136
+ this.w3[i] = 0
137
+ }
138
+
139
+ this.markDirty(slot.start, slot.start + slot.count - 1)
140
+ this.sectionSlots.delete(sectionKey)
141
+ this.insertFreeSlot(slot)
142
+ this.shrinkHighWatermark()
143
+ this.mesh.geometry.instanceCount = this.highWatermark
144
+ }
145
+
146
+ uploadDirtyRange (): void {
147
+ if (this.dirtyMin > this.dirtyMax) return
148
+
149
+ const offset = this.dirtyMin
150
+ const count = this.dirtyMax - this.dirtyMin + 1
151
+ const geometry = this.mesh.geometry
152
+
153
+ for (const name of ['a_w0', 'a_w1', 'a_w2', 'a_w3'] as const) {
154
+ const attr = geometry.getAttribute(name) as THREE.InstancedBufferAttribute
155
+ attr.updateRange.offset = offset
156
+ attr.updateRange.count = count
157
+ attr.needsUpdate = true
158
+ }
159
+
160
+ this.dirtyMin = Infinity
161
+ this.dirtyMax = -1
162
+ }
163
+
164
+ setCameraOrigin (x: number, y: number, z: number): void {
165
+ // Integer + fractional parts — see cubeBlockShader position math.
166
+ const ix = Math.floor(x)
167
+ const iy = Math.floor(y)
168
+ const iz = Math.floor(z)
169
+ const u = this.mesh.material.uniforms.u_cameraOrigin
170
+ if (u?.value?.set) {
171
+ u.value.set(ix, iy, iz)
172
+ }
173
+ const uf = this.mesh.material.uniforms.u_cameraOriginFrac
174
+ if (uf?.value?.set) {
175
+ uf.value.set(x - ix, y - iy, z - iz)
176
+ }
177
+ }
178
+
179
+ reset (): void {
180
+ this.sectionSlots.clear()
181
+ this.freeList.length = 0
182
+ this.highWatermark = 0
183
+ this.dirtyMin = Infinity
184
+ this.dirtyMax = -1
185
+ this.w0.fill(0)
186
+ this.w1.fill(0)
187
+ this.w2.fill(EMPTY_W2)
188
+ this.w3.fill(0)
189
+ this.mesh.geometry.instanceCount = 0
190
+ }
191
+
192
+ dispose (): void {
193
+ this.mesh.parent?.remove(this.mesh)
194
+ this.mesh.geometry.dispose()
195
+ this.reset()
196
+ }
197
+
198
+ private markDirty (start: number, end: number): void {
199
+ if (start < this.dirtyMin) this.dirtyMin = start
200
+ if (end > this.dirtyMax) this.dirtyMax = end
201
+ }
202
+
203
+ private takeFreeSlot (count: number): { start: number, count: number } | undefined {
204
+ for (let i = 0; i < this.freeList.length; i++) {
205
+ const slot = this.freeList[i]!
206
+ if (slot.count >= count) {
207
+ this.freeList.splice(i, 1)
208
+ if (slot.count === count) return slot
209
+ const used = { start: slot.start, count }
210
+ this.insertFreeSlot({ start: slot.start + count, count: slot.count - count })
211
+ return used
212
+ }
213
+ }
214
+ return undefined
215
+ }
216
+
217
+ private insertFreeSlot (slot: { start: number, count: number }): void {
218
+ this.freeList.push(slot)
219
+ this.freeList.sort((a, b) => a.start - b.start)
220
+ this.mergeFreeList()
221
+ }
222
+
223
+ private mergeFreeList (): void {
224
+ if (this.freeList.length < 2) return
225
+ const merged: Array<{ start: number, count: number }> = []
226
+ let cur = this.freeList[0]!
227
+ for (let i = 1; i < this.freeList.length; i++) {
228
+ const next = this.freeList[i]!
229
+ if (cur.start + cur.count === next.start) {
230
+ cur = { start: cur.start, count: cur.count + next.count }
231
+ } else {
232
+ merged.push(cur)
233
+ cur = next
234
+ }
235
+ }
236
+ merged.push(cur)
237
+ this.freeList = merged
238
+ }
239
+
240
+ private shrinkHighWatermark (): void {
241
+ while (this.highWatermark > 0) {
242
+ const tail = this.highWatermark - 1
243
+ const free = this.freeList.find(s => s.start <= tail && s.start + s.count > tail)
244
+ if (!free || free.start + free.count !== this.highWatermark) break
245
+ this.highWatermark = free.start
246
+ const idx = this.freeList.indexOf(free)
247
+ this.freeList.splice(idx, 1)
248
+ }
249
+ }
250
+
251
+ private growCapacity (minFaces: number): void {
252
+ let newCap = this.capacityFaces
253
+ while (newCap < minFaces) newCap *= 2
254
+
255
+ const nw0 = new Uint32Array(newCap)
256
+ const nw1 = new Uint32Array(newCap)
257
+ const nw2 = new Uint32Array(newCap)
258
+ const nw3 = new Uint32Array(newCap)
259
+ nw0.set(this.w0)
260
+ nw1.set(this.w1)
261
+ nw2.set(this.w2)
262
+ nw3.set(this.w3)
263
+ nw2.fill(EMPTY_W2, this.w0.length)
264
+
265
+ this.w0 = nw0
266
+ this.w1 = nw1
267
+ this.w2 = nw2
268
+ this.w3 = nw3
269
+ this.capacityFaces = newCap
270
+
271
+ const geometry = this.mesh.geometry
272
+ const mkAttr = (arr: Uint32Array, name: string) => {
273
+ const prev = geometry.getAttribute(name)
274
+ if (prev) {
275
+ geometry.deleteAttribute(name)
276
+ if ('dispose' in prev && typeof (prev as { dispose?: () => void }).dispose === 'function') {
277
+ (prev as { dispose: () => void }).dispose()
278
+ }
279
+ }
280
+ const attr = new THREE.InstancedBufferAttribute(arr, 1)
281
+ attr.setUsage(THREE.DynamicDrawUsage)
282
+ geometry.setAttribute(name, attr)
283
+ }
284
+ mkAttr(this.w0, 'a_w0')
285
+ mkAttr(this.w1, 'a_w1')
286
+ mkAttr(this.w2, 'a_w2')
287
+ mkAttr(this.w3, 'a_w3')
288
+
289
+ this.dirtyMin = 0
290
+ this.dirtyMax = this.highWatermark - 1
291
+ }
292
+ }
@@ -5,7 +5,7 @@ import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } fro
5
5
  /** Single source of truth for menu-background defaults (settings + runtime fallbacks). */
6
6
  export const MENU_BACKGROUND_OPTION_DEFAULTS = {
7
7
  mode: 'futuristic' as MenuBackgroundMode,
8
- minecraftTextures: true,
8
+ minecraftTextures: true as boolean,
9
9
  futuristicScene: 'light' as FuturisticSceneId,
10
10
  futuristicCamera: 'dive' as FuturisticCameraId,
11
11
  futuristicBlockGroup: 'stainedGlass' as MinecraftBlockGroupId,
@@ -41,36 +41,36 @@ const MB = MENU_BACKGROUND_OPTION_DEFAULTS
41
41
  export const RENDERER_DEFAULT_OPTIONS = {
42
42
  rendererWorldPerformance: 'normal' as 'low-energy' | 'normal' | 'maximum',
43
43
  rendererMeshersCountOverride: null as number | null,
44
- starfieldRendering: true,
45
- defaultSkybox: true,
44
+ starfieldRendering: true as boolean,
45
+ defaultSkybox: true as boolean,
46
46
  menuBackgroundMode: MB.mode,
47
- menuBackgroundMinecraftTextures: MB.minecraftTextures,
47
+ menuBackgroundMinecraftTextures: MB.minecraftTextures as boolean,
48
48
  menuBackgroundFuturisticScene: MB.futuristicScene,
49
49
  menuBackgroundFuturisticCamera: MB.futuristicCamera,
50
50
  menuBackgroundFuturisticBlockGroup: MB.futuristicBlockGroup,
51
51
  menuBackgroundFuturisticCameraSpeed: MB.futuristicCameraSpeedPercent,
52
52
  menuBackgroundFuturisticBlockSpeed: MB.futuristicBlockSpeedPercent,
53
- rendererFuturisticReveal: false,
54
- rendererPerfDebugOverlay: false,
55
- disableBlockEntityTextures: false,
53
+ rendererFuturisticReveal: false as boolean,
54
+ rendererPerfDebugOverlay: false as boolean,
55
+ disableBlockEntityTextures: false as boolean,
56
56
  rendererMesher: 'wasm' as RendererMesherPipeline,
57
- showChunkBorders: false,
58
- renderEntities: true,
57
+ showChunkBorders: false as boolean,
58
+ renderEntities: true as boolean,
59
59
  renderDebug: 'basic' as 'none' | 'basic' | 'advanced',
60
60
  frameLimit: false as number | false,
61
61
  backgroundRendering: '20fps' as 'full' | '20fps' | '5fps',
62
- vanillaLook: false,
63
- smoothLighting: true,
64
- newVersionsLighting: false,
65
- vrSupport: true,
66
- vrPageGameRendering: false,
62
+ vanillaLook: false as boolean,
63
+ smoothLighting: true as boolean,
64
+ newVersionsLighting: false as boolean,
65
+ vrSupport: true as boolean,
66
+ vrPageGameRendering: false as boolean,
67
67
  clipWorldBelowY: undefined as number | undefined,
68
68
  highlightBlockColor: 'auto' as 'auto' | 'blue' | 'classic',
69
- loadPlayerSkins: true,
70
- renderEars: true,
71
- showHand: true,
72
- viewBobbing: true,
73
- dayCycleAndLighting: true,
69
+ loadPlayerSkins: true as boolean,
70
+ renderEars: true as boolean,
71
+ showHand: true as boolean,
72
+ viewBobbing: true as boolean,
73
+ dayCycleAndLighting: true as boolean,
74
74
  keepChunksDistance: 1,
75
75
  gpuPreference: 'default' as RendererGpuPreference,
76
76
  fov: 75