minecraft-renderer 0.1.21 → 0.1.23
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/dist/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +5 -5
- package/dist/minecraft-renderer.js +53 -53
- package/dist/threeWorker.js +405 -405
- package/package.json +6 -1
- package/src/graphicsBackend/config.ts +8 -4
- package/src/graphicsBackend/types.ts +2 -2
- package/src/lib/worldrendererCommon.ts +45 -9
- package/src/mesher/mesher.ts +2 -2
- package/src/mesher/shared.ts +1 -1
- package/src/playground/allEntitiesDebug.ts +6 -4
- package/src/playground/scenes/allEntities.ts +1 -1
- package/src/playground/scenes/floorRandom.ts +1 -1
- package/src/three/appShared.ts +2 -2
- package/src/three/documentRenderer.ts +2 -2
- package/src/three/graphicsBackendBase.ts +2 -0
- package/src/three/holdingBlock.ts +1 -1
- package/src/three/modules/index.ts +2 -0
- package/src/three/modules/rain.ts +185 -0
- package/src/three/modules/sciFiWorldReveal.ts +9 -0
- package/src/three/modules/starfield.ts +9 -0
- package/src/three/renderSlot.ts +9 -9
- package/src/three/rendererModuleSystem.ts +4 -0
- package/src/three/worldRendererThree.ts +114 -23
- package/src/wasm-lib/render-from-wasm.ts +161 -161
- package/src/worldView/worldView.ts +1 -1
- package/wasm/wasm_mesher.d.ts +2 -2
- package/wasm/wasm_mesher.js +7 -4
|
@@ -33,7 +33,7 @@ import { DEFAULT_TEMPERATURE, SkyboxRenderer } from './skyboxRenderer'
|
|
|
33
33
|
import { FireworksManager } from './fireworks'
|
|
34
34
|
import { downloadWorldGeometry } from './worldGeometryExport'
|
|
35
35
|
import { WorldBlockGeometry } from './worldBlockGeometry'
|
|
36
|
-
import type { RendererModuleManifest, RegisteredModule } from './rendererModuleSystem'
|
|
36
|
+
import type { RendererModuleManifest, RegisteredModule, RendererModuleController } from './rendererModuleSystem'
|
|
37
37
|
import { BUILTIN_MODULES } from './modules/index'
|
|
38
38
|
|
|
39
39
|
type SectionKey = string
|
|
@@ -73,7 +73,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
73
73
|
return this.worldBlockGeometry.estimatedMemoryUsage
|
|
74
74
|
}
|
|
75
75
|
// Module system
|
|
76
|
-
private modules =
|
|
76
|
+
private modules = {} as Record<string, RegisteredModule>
|
|
77
77
|
sectionsOffsetsAnimations = {} as {
|
|
78
78
|
[chunkKey: string]: {
|
|
79
79
|
time: number,
|
|
@@ -170,7 +170,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
170
170
|
* Register a renderer module
|
|
171
171
|
*/
|
|
172
172
|
registerModule(manifest: RendererModuleManifest): void {
|
|
173
|
-
if (this.modules
|
|
173
|
+
if (manifest.id in this.modules) {
|
|
174
174
|
console.warn(`Module ${manifest.id} is already registered`)
|
|
175
175
|
return
|
|
176
176
|
}
|
|
@@ -181,12 +181,13 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
181
181
|
manifest,
|
|
182
182
|
controller,
|
|
183
183
|
enabled: false,
|
|
184
|
+
toggle: () => this.toggleModule(manifest.id),
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
this.modules
|
|
187
|
+
this.modules[manifest.id] = registered
|
|
187
188
|
|
|
188
189
|
if (manifest.enabledDefault) {
|
|
189
|
-
this.
|
|
190
|
+
this.toggleModule(manifest.id, true)
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -195,7 +196,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
195
196
|
* Enable a module
|
|
196
197
|
*/
|
|
197
198
|
enableModule(moduleId: string): void {
|
|
198
|
-
const module = this.modules
|
|
199
|
+
const module = this.modules[moduleId]
|
|
199
200
|
if (!module) {
|
|
200
201
|
console.warn(`Module ${moduleId} not found`)
|
|
201
202
|
return
|
|
@@ -216,7 +217,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
216
217
|
* Disable a module
|
|
217
218
|
*/
|
|
218
219
|
disableModule(moduleId: string): void {
|
|
219
|
-
const module = this.modules
|
|
220
|
+
const module = this.modules[moduleId]
|
|
220
221
|
if (!module) {
|
|
221
222
|
console.warn(`Module ${moduleId} not found`)
|
|
222
223
|
return
|
|
@@ -241,32 +242,74 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Toggle a module on/off, or force a specific state
|
|
247
|
+
*/
|
|
248
|
+
toggleModule(moduleId: string, forceState?: boolean): boolean {
|
|
249
|
+
const module = this.modules[moduleId]
|
|
250
|
+
if (!module) {
|
|
251
|
+
console.warn(`Module ${moduleId} not found`)
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const targetState = forceState !== undefined ? forceState : !module.enabled
|
|
256
|
+
|
|
257
|
+
if (targetState === module.enabled) return module.enabled
|
|
258
|
+
|
|
259
|
+
if (!targetState && module.manifest.cannotBeDisabled) {
|
|
260
|
+
console.warn(`Module ${moduleId} cannot be disabled`)
|
|
261
|
+
return true
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.enabled = targetState
|
|
265
|
+
|
|
266
|
+
if (targetState) {
|
|
267
|
+
module.controller.enable()
|
|
268
|
+
// Register render callback if provided
|
|
269
|
+
if (module.controller.render && !this.onRender.includes(module.controller.render)) {
|
|
270
|
+
this.onRender.push(module.controller.render)
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
module.controller.disable()
|
|
274
|
+
// Unregister render callback if provided
|
|
275
|
+
if (module.controller.render) {
|
|
276
|
+
const index = this.onRender.indexOf(module.controller.render)
|
|
277
|
+
if (index > -1) {
|
|
278
|
+
this.onRender.splice(index, 1)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return targetState
|
|
284
|
+
}
|
|
285
|
+
|
|
244
286
|
/**
|
|
245
287
|
* Dispose all modules
|
|
246
288
|
*/
|
|
247
289
|
private disposeModules(): void {
|
|
248
|
-
for (const module of this.modules
|
|
290
|
+
for (const module of Object.values(this.modules)) {
|
|
249
291
|
module.controller.dispose()
|
|
250
292
|
}
|
|
251
|
-
this.modules
|
|
293
|
+
this.modules = {}
|
|
252
294
|
}
|
|
253
295
|
|
|
254
296
|
/**
|
|
255
297
|
* Initialize all registered modules
|
|
256
298
|
*/
|
|
257
299
|
private initializeModules(): void {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
this.enableModule(id)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
300
|
+
// Use updateModulesFromConfig to handle initial state correctly (respects force states and auto-enable)
|
|
301
|
+
this.updateModulesFromConfig()
|
|
263
302
|
}
|
|
264
303
|
|
|
265
304
|
/**
|
|
266
305
|
* Get a module controller by ID
|
|
267
306
|
*/
|
|
268
307
|
getModule<T = any>(moduleId: string): T | undefined {
|
|
269
|
-
return this.modules
|
|
308
|
+
return this.modules[moduleId]?.controller as T | undefined
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
protected override anyModuleRequiresHeightmap(): boolean {
|
|
312
|
+
return Object.values(this.modules).some(m => m.enabled && m.manifest.requiresHeightmap)
|
|
270
313
|
}
|
|
271
314
|
|
|
272
315
|
get cameraObject() {
|
|
@@ -366,6 +409,61 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
366
409
|
this.onReactiveConfigUpdated('defaultSkybox', (value) => {
|
|
367
410
|
this.skyboxRenderer.updateDefaultSkybox(value)
|
|
368
411
|
})
|
|
412
|
+
|
|
413
|
+
// Watch for config changes that affect modules
|
|
414
|
+
this.onReactiveConfigUpdated('*' as any, () => {
|
|
415
|
+
this.updateModulesFromConfig()
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
// Initial update
|
|
419
|
+
this.updateModulesFromConfig()
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Update module states based on config (force states and auto-enable checks)
|
|
424
|
+
*/
|
|
425
|
+
private updateModulesFromConfig(): void {
|
|
426
|
+
const { moduleStates } = this.worldRendererConfig
|
|
427
|
+
|
|
428
|
+
for (const [moduleId, module] of Object.entries(this.modules)) {
|
|
429
|
+
const forceState = moduleStates[moduleId]
|
|
430
|
+
|
|
431
|
+
// Check force states first
|
|
432
|
+
if (forceState === 'enabled') {
|
|
433
|
+
if (!module.enabled) {
|
|
434
|
+
this.toggleModule(moduleId, true)
|
|
435
|
+
}
|
|
436
|
+
continue
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (forceState === 'disabled') {
|
|
440
|
+
if (module.enabled && !module.manifest.cannotBeDisabled) {
|
|
441
|
+
this.toggleModule(moduleId, false)
|
|
442
|
+
}
|
|
443
|
+
continue
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Auto mode: use autoEnableCheck if available, otherwise use enabledDefault
|
|
447
|
+
if (forceState === 'auto' || forceState === undefined) {
|
|
448
|
+
if (module.controller.autoEnableCheck) {
|
|
449
|
+
const shouldEnable = module.controller.autoEnableCheck()
|
|
450
|
+
|
|
451
|
+
if (shouldEnable && !module.enabled) {
|
|
452
|
+
this.toggleModule(moduleId, true)
|
|
453
|
+
} else if (!shouldEnable && module.enabled && !module.manifest.cannotBeDisabled) {
|
|
454
|
+
this.toggleModule(moduleId, false)
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
// No autoEnableCheck: use enabledDefault
|
|
458
|
+
const shouldEnable = module.manifest.enabledDefault ?? false
|
|
459
|
+
if (shouldEnable && !module.enabled) {
|
|
460
|
+
this.toggleModule(moduleId, true)
|
|
461
|
+
} else if (!shouldEnable && module.enabled && !module.manifest.cannotBeDisabled) {
|
|
462
|
+
this.toggleModule(moduleId, false)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
369
467
|
}
|
|
370
468
|
|
|
371
469
|
changeHandSwingingState(isAnimationPlaying: boolean, isLeft = false) {
|
|
@@ -486,7 +584,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
486
584
|
text += `TE: ${formatBigNumber(this.renderer.info.memory.textures)} `
|
|
487
585
|
text += `F: ${formatBigNumber(this.tilesRendered)} `
|
|
488
586
|
text += `B: ${formatBigNumber(this.blocksRendered)} `
|
|
489
|
-
text += `MEM: ${this.getEstimatedMemoryUsage().readable}`
|
|
587
|
+
text += `MEM: ${this.worldBlockGeometry.getEstimatedMemoryUsage().readable}`
|
|
490
588
|
pane.updateText(text)
|
|
491
589
|
this.backendInfoReport = text
|
|
492
590
|
}
|
|
@@ -1144,11 +1242,4 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1144
1242
|
reloadWorld() {
|
|
1145
1243
|
this.entities.reloadEntities()
|
|
1146
1244
|
}
|
|
1147
|
-
|
|
1148
|
-
/**
|
|
1149
|
-
* Get estimated memory usage in a human-readable format
|
|
1150
|
-
*/
|
|
1151
|
-
getEstimatedMemoryUsage(): { bytes: number; readable: string } {
|
|
1152
|
-
return this.worldBlockGeometry.getEstimatedMemoryUsage()
|
|
1153
|
-
}
|
|
1154
1245
|
}
|
|
@@ -466,187 +466,187 @@ export function renderWasmOutputToGeometry(
|
|
|
466
466
|
if (!cachedModel) continue
|
|
467
467
|
|
|
468
468
|
if (false) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
// For now, use first model variant (can be extended later)
|
|
470
|
+
const modelVariant = cachedModel!.modelVariants[0]
|
|
471
|
+
if (!modelVariant) continue
|
|
472
472
|
|
|
473
|
-
|
|
473
|
+
const { model, globalMatrix, globalShift, elements } = modelVariant
|
|
474
474
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
475
|
+
// Get biome for tint calculation if world is provided
|
|
476
|
+
let biome: string | undefined
|
|
477
|
+
if (world) {
|
|
478
|
+
const blockObj = world!.getBlock(new Vec3(bx, by, bz))
|
|
479
|
+
biome = blockObj?.biome?.name
|
|
480
|
+
}
|
|
481
481
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
482
|
+
// Process faces in the same order as TypeScript (iterate through model's faces)
|
|
483
|
+
// TypeScript uses: for (const face in element.faces)
|
|
484
|
+
// We need to match this order to get the same vertex ordering
|
|
485
485
|
|
|
486
|
-
|
|
487
|
-
|
|
486
|
+
// Find the element that contains faces (use cached element data)
|
|
487
|
+
const faceElements = elements.filter(elemData => elemData.element.faces && Object.keys(elemData.element.faces).length > 0)
|
|
488
488
|
|
|
489
|
-
|
|
489
|
+
if (faceElements.length === 0) continue
|
|
490
490
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
491
|
+
// Map face names to their index in WASM output
|
|
492
|
+
const faceNameToIndex: Record<string, number> = {
|
|
493
|
+
'up': 0,
|
|
494
|
+
'down': 1,
|
|
495
|
+
'east': 2,
|
|
496
|
+
'west': 3,
|
|
497
|
+
'south': 4,
|
|
498
|
+
'north': 5
|
|
499
|
+
}
|
|
500
500
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
501
|
+
// WASM processes faces in fixed order: [up, down, east, west, south, north]
|
|
502
|
+
// Build a mapping from WASM face order to data index
|
|
503
|
+
const wasmFaceOrder = ['up', 'down', 'east', 'west', 'south', 'north']
|
|
504
|
+
const wasmFaceToDataIndex: Record<string, number> = {}
|
|
505
|
+
let dataIndex = 0
|
|
506
|
+
for (const faceName of wasmFaceOrder) {
|
|
507
|
+
const faceIdx = faceNameToIndex[faceName]
|
|
508
|
+
if ((block.visible_faces & (1 << faceIdx)) !== 0) {
|
|
509
|
+
wasmFaceToDataIndex[faceName] = dataIndex++
|
|
510
|
+
}
|
|
510
511
|
}
|
|
511
|
-
}
|
|
512
512
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
513
|
+
// Process faces in the order they appear in the model (matching TS)
|
|
514
|
+
for (const elemData of faceElements) {
|
|
515
|
+
const element = elemData.element
|
|
516
|
+
const localMatrix = elemData.localMatrix
|
|
517
|
+
const localShift = elemData.localShift
|
|
518
518
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
519
|
+
// eslint-disable-next-line guard-for-in
|
|
520
|
+
for (const faceName in element.faces) {
|
|
521
|
+
const faceIdx = faceNameToIndex[faceName]
|
|
522
|
+
if (faceIdx === undefined) continue
|
|
523
523
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
524
|
+
// Check if this face is visible in WASM output
|
|
525
|
+
if ((block.visible_faces & (1 << faceIdx)) === 0) {
|
|
526
|
+
continue
|
|
527
|
+
}
|
|
528
528
|
|
|
529
|
-
|
|
530
|
-
|
|
529
|
+
const matchingEFace = element.faces[faceName]
|
|
530
|
+
const { dir, corners, mask1, mask2 } = elemFaces[faceName]
|
|
531
531
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
// Get the correct data index for this face based on WASM's processing order
|
|
533
|
+
const faceDataIndex = wasmFaceToDataIndex[faceName]
|
|
534
|
+
if (faceDataIndex === undefined) continue
|
|
535
535
|
|
|
536
|
-
|
|
537
|
-
|
|
536
|
+
const aoValues = block.ao_data[faceDataIndex]
|
|
537
|
+
const lightValues = block.light_data[faceDataIndex]
|
|
538
538
|
|
|
539
|
-
|
|
539
|
+
log(`[WASM] Face ${faceIdx} (${faceName}): dir=[${dir.join(',')}], ao=[${aoValues.join(',')}], light=[${lightValues.map(l => l.toFixed(3)).join(',')}]`)
|
|
540
540
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
541
|
+
const texture = matchingEFace.texture as any
|
|
542
|
+
const u = texture.u || 0
|
|
543
|
+
const v = texture.v || 0
|
|
544
|
+
const su = texture.su || 1
|
|
545
|
+
const sv = texture.sv || 1
|
|
546
546
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// Get tint (use cached model data and world if available)
|
|
556
|
-
const tint = getTint(matchingEFace, cachedModel.blockName, cachedModel.blockProps, biome, world)
|
|
557
|
-
|
|
558
|
-
const minx = element.from[0]
|
|
559
|
-
const miny = element.from[1]
|
|
560
|
-
const minz = element.from[2]
|
|
561
|
-
const maxx = element.to[0]
|
|
562
|
-
const maxy = element.to[1]
|
|
563
|
-
const maxz = element.to[2]
|
|
564
|
-
|
|
565
|
-
// Calculate transformed direction
|
|
566
|
-
const transformedDir = matmul3(globalMatrix, dir)
|
|
567
|
-
|
|
568
|
-
// Add 4 vertices for this face
|
|
569
|
-
const baseIndex = currentIndex
|
|
570
|
-
for (let cornerIdx = 0; cornerIdx < 4; cornerIdx++) {
|
|
571
|
-
const pos = corners[cornerIdx]
|
|
572
|
-
|
|
573
|
-
// Calculate vertex position (matching reference)
|
|
574
|
-
let vertex = [
|
|
575
|
-
(pos[0] ? maxx : minx),
|
|
576
|
-
(pos[1] ? maxy : miny),
|
|
577
|
-
(pos[2] ? maxz : minz)
|
|
578
|
-
]
|
|
579
|
-
|
|
580
|
-
// Apply element rotation
|
|
581
|
-
vertex = vecadd3(matmul3(localMatrix, vertex), localShift)
|
|
582
|
-
// Apply model rotation
|
|
583
|
-
vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift)
|
|
584
|
-
// Convert to block coordinates (0-1)
|
|
585
|
-
vertex = vertex.map(v => v / 16)
|
|
586
|
-
|
|
587
|
-
// World position (relative to section)
|
|
588
|
-
const worldPos = [
|
|
589
|
-
vertex[0] + (bx & 15) - 8,
|
|
590
|
-
vertex[1] + (by & 15) - 8,
|
|
591
|
-
vertex[2] + (bz & 15) - 8
|
|
592
|
-
]
|
|
593
|
-
|
|
594
|
-
log(`[WASM] Corner ${cornerIdx}: corner=[${pos.join(',')}], vertex=[${vertex.map(v => v.toFixed(3)).join(',')}], worldPos=[${worldPos.map(v => v.toFixed(3)).join(',')}]`)
|
|
595
|
-
|
|
596
|
-
positions.push(...worldPos)
|
|
597
|
-
|
|
598
|
-
// Normal (transformed direction)
|
|
599
|
-
normals.push(transformedDir[0], transformedDir[1], transformedDir[2])
|
|
600
|
-
|
|
601
|
-
// Color (with AO and light from WASM) - matching TS formula exactly
|
|
602
|
-
const ao = aoValues[cornerIdx]
|
|
603
|
-
|
|
604
|
-
// TS calculation:
|
|
605
|
-
// baseLight = world.getLight(neighborPos, ...) / 15 (0-1 range)
|
|
606
|
-
// cornerLightResult = baseLight * 15 (0-15 range, or interpolated if smooth lighting)
|
|
607
|
-
// light = (ao + 1) / 4 * (cornerLightResult / 15)
|
|
608
|
-
// finalColor = baseLight * tint * light
|
|
609
|
-
|
|
610
|
-
// WASM provides lightValues in 0-1 range (already divided by 15)
|
|
611
|
-
// But WASM light calculation seems to return 0.0, so we need to handle that
|
|
612
|
-
// In the test case, TypeScript gets baseLight = 1.0 (full brightness)
|
|
613
|
-
// So we should use 1.0 as the base light value when WASM returns 0
|
|
614
|
-
const baseLight = lightValues[cornerIdx]
|
|
615
|
-
const cornerLightResult = baseLight * 15
|
|
616
|
-
|
|
617
|
-
const light = (ao + 1) / 4 * (cornerLightResult / 15)
|
|
618
|
-
|
|
619
|
-
colors.push(tint[0] * light, tint[1] * light, tint[2] * light)
|
|
620
|
-
|
|
621
|
-
// UV calculation (matching reference exactly)
|
|
622
|
-
const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5
|
|
623
|
-
const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5
|
|
624
|
-
const finalU = baseu * su + u
|
|
625
|
-
const finalV = basev * sv + v
|
|
626
|
-
log(`[WASM] UV: cornerUV=[${pos[3]},${pos[4]}], baseUV=[${baseu.toFixed(6)},${basev.toFixed(6)}], finalUV=[${finalU.toFixed(6)},${finalV.toFixed(6)}], texture=[u=${u},v=${v},su=${su},sv=${sv}], rotation=${r}`)
|
|
627
|
-
uvs.push(finalU, finalV)
|
|
628
|
-
|
|
629
|
-
currentIndex++
|
|
630
|
-
}
|
|
547
|
+
// UV rotation (matching reference implementation)
|
|
548
|
+
let r = matchingEFace.rotation || 0
|
|
549
|
+
if (faceName === 'down') {
|
|
550
|
+
r += 180
|
|
551
|
+
}
|
|
552
|
+
const uvcs = Math.cos(r * Math.PI / 180)
|
|
553
|
+
const uvsn = -Math.sin(r * Math.PI / 180)
|
|
631
554
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
555
|
+
// Get tint (use cached model data and world if available)
|
|
556
|
+
const tint = getTint(matchingEFace, cachedModel!.blockName, cachedModel!.blockProps, biome, world)
|
|
557
|
+
|
|
558
|
+
const minx = element.from[0]
|
|
559
|
+
const miny = element.from[1]
|
|
560
|
+
const minz = element.from[2]
|
|
561
|
+
const maxx = element.to[0]
|
|
562
|
+
const maxy = element.to[1]
|
|
563
|
+
const maxz = element.to[2]
|
|
564
|
+
|
|
565
|
+
// Calculate transformed direction
|
|
566
|
+
const transformedDir = matmul3(globalMatrix, dir)
|
|
567
|
+
|
|
568
|
+
// Add 4 vertices for this face
|
|
569
|
+
const baseIndex = currentIndex
|
|
570
|
+
for (let cornerIdx = 0; cornerIdx < 4; cornerIdx++) {
|
|
571
|
+
const pos = corners[cornerIdx]
|
|
572
|
+
|
|
573
|
+
// Calculate vertex position (matching reference)
|
|
574
|
+
let vertex = [
|
|
575
|
+
(pos[0] ? maxx : minx),
|
|
576
|
+
(pos[1] ? maxy : miny),
|
|
577
|
+
(pos[2] ? maxz : minz)
|
|
578
|
+
]
|
|
579
|
+
|
|
580
|
+
// Apply element rotation
|
|
581
|
+
vertex = vecadd3(matmul3(localMatrix, vertex), localShift)
|
|
582
|
+
// Apply model rotation
|
|
583
|
+
vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift)
|
|
584
|
+
// Convert to block coordinates (0-1)
|
|
585
|
+
vertex = vertex.map(v => v / 16)
|
|
586
|
+
|
|
587
|
+
// World position (relative to section)
|
|
588
|
+
const worldPos = [
|
|
589
|
+
vertex[0] + (bx & 15) - 8,
|
|
590
|
+
vertex[1] + (by & 15) - 8,
|
|
591
|
+
vertex[2] + (bz & 15) - 8
|
|
592
|
+
]
|
|
593
|
+
|
|
594
|
+
log(`[WASM] Corner ${cornerIdx}: corner=[${pos.join(',')}], vertex=[${vertex.map(v => v.toFixed(3)).join(',')}], worldPos=[${worldPos.map(v => v.toFixed(3)).join(',')}]`)
|
|
595
|
+
|
|
596
|
+
positions.push(...worldPos)
|
|
597
|
+
|
|
598
|
+
// Normal (transformed direction)
|
|
599
|
+
normals.push(transformedDir[0], transformedDir[1], transformedDir[2])
|
|
600
|
+
|
|
601
|
+
// Color (with AO and light from WASM) - matching TS formula exactly
|
|
602
|
+
const ao = aoValues[cornerIdx]
|
|
603
|
+
|
|
604
|
+
// TS calculation:
|
|
605
|
+
// baseLight = world.getLight(neighborPos, ...) / 15 (0-1 range)
|
|
606
|
+
// cornerLightResult = baseLight * 15 (0-15 range, or interpolated if smooth lighting)
|
|
607
|
+
// light = (ao + 1) / 4 * (cornerLightResult / 15)
|
|
608
|
+
// finalColor = baseLight * tint * light
|
|
609
|
+
|
|
610
|
+
// WASM provides lightValues in 0-1 range (already divided by 15)
|
|
611
|
+
// But WASM light calculation seems to return 0.0, so we need to handle that
|
|
612
|
+
// In the test case, TypeScript gets baseLight = 1.0 (full brightness)
|
|
613
|
+
// So we should use 1.0 as the base light value when WASM returns 0
|
|
614
|
+
const baseLight = lightValues[cornerIdx]
|
|
615
|
+
const cornerLightResult = baseLight * 15
|
|
616
|
+
|
|
617
|
+
const light = (ao + 1) / 4 * (cornerLightResult / 15)
|
|
618
|
+
|
|
619
|
+
colors.push(tint[0] * light, tint[1] * light, tint[2] * light)
|
|
620
|
+
|
|
621
|
+
// UV calculation (matching reference exactly)
|
|
622
|
+
const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5
|
|
623
|
+
const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5
|
|
624
|
+
const finalU = baseu * su + u
|
|
625
|
+
const finalV = basev * sv + v
|
|
626
|
+
log(`[WASM] UV: cornerUV=[${pos[3]},${pos[4]}], baseUV=[${baseu.toFixed(6)},${basev.toFixed(6)}], finalUV=[${finalU.toFixed(6)},${finalV.toFixed(6)}], texture=[u=${u},v=${v},su=${su},sv=${sv}], rotation=${r}`)
|
|
627
|
+
uvs.push(finalU, finalV)
|
|
628
|
+
|
|
629
|
+
currentIndex++
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Add indices (2 triangles) - matching TS AO-optimized winding
|
|
633
|
+
// TS uses: if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) { optimized } else { standard }
|
|
634
|
+
let tri1: number[], tri2: number[]
|
|
635
|
+
if (aoValues[0] + aoValues[3] >= aoValues[1] + aoValues[2]) {
|
|
636
|
+
// AO-optimized winding
|
|
637
|
+
tri1 = [baseIndex, baseIndex + 3, baseIndex + 2]
|
|
638
|
+
tri2 = [baseIndex, baseIndex + 1, baseIndex + 3]
|
|
639
|
+
log(`[WASM] Indices (AO optimized): tri1=[${tri1.join(',')}], tri2=[${tri2.join(',')}], aos=[${aoValues.join(',')}]`)
|
|
640
|
+
} else {
|
|
641
|
+
// Standard winding
|
|
642
|
+
tri1 = [baseIndex, baseIndex + 1, baseIndex + 2]
|
|
643
|
+
tri2 = [baseIndex + 2, baseIndex + 1, baseIndex + 3]
|
|
644
|
+
log(`[WASM] Indices (standard): tri1=[${tri1.join(',')}], tri2=[${tri2.join(',')}], aos=[${aoValues.join(',')}]`)
|
|
645
|
+
}
|
|
646
|
+
indices.push(...tri1, ...tri2)
|
|
647
|
+
}
|
|
647
648
|
}
|
|
648
649
|
}
|
|
649
|
-
}
|
|
650
650
|
|
|
651
651
|
const models = cachedModel.models
|
|
652
652
|
if (!models || models.length == 0) continue
|
|
@@ -363,7 +363,7 @@ export class WorldView extends (EventEmitter as new () => TypedEmitter<WorldView
|
|
|
363
363
|
|
|
364
364
|
// Unload chunks that are no longer in view
|
|
365
365
|
const chunksToUnload: Vec3[] = []
|
|
366
|
-
const viewDistanceWithBuffer = this.viewDistance + this.keepChunksDistance
|
|
366
|
+
const viewDistanceWithBuffer = force ? this.viewDistance : this.viewDistance + this.keepChunksDistance
|
|
367
367
|
|
|
368
368
|
for (const coords of Object.keys(this.loadedChunks)) {
|
|
369
369
|
const [x, z] = coords.split(',').map(Number)
|
package/wasm/wasm_mesher.d.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* Input: Serialized chunk data as TypedArrays
|
|
8
8
|
* Output: Geometry data (positions, normals, colors, uvs, indices)
|
|
9
9
|
*/
|
|
10
|
-
export function generate_geometry(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
|
|
10
|
+
export function generate_geometry(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, section_data_start_y: number, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
|
|
11
11
|
|
|
12
|
-
export function generate_geometry_multi(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, chunk_xs: Int32Array, chunk_zs: Int32Array, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
|
|
12
|
+
export function generate_geometry_multi(section_x: number, section_y: number, section_z: number, section_height: number, world_min_y: number, world_max_y: number, section_data_start_y: number, chunk_xs: Int32Array, chunk_zs: Int32Array, block_states: Uint16Array, block_light: Uint8Array, sky_light: Uint8Array, biomes: Uint8Array, invisible_blocks: Uint16Array, transparent_blocks: Uint16Array, no_ao_blocks: Uint16Array, cull_identical_blocks: Uint16Array, occluding_blocks: Uint16Array, enable_lighting: boolean, smooth_lighting: boolean, sky_light_value: number): any;
|
|
13
13
|
|
|
14
14
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
15
15
|
|
package/wasm/wasm_mesher.js
CHANGED
|
@@ -227,6 +227,7 @@ let WASM_VECTOR_LEN = 0;
|
|
|
227
227
|
* @param {number} section_height
|
|
228
228
|
* @param {number} world_min_y
|
|
229
229
|
* @param {number} world_max_y
|
|
230
|
+
* @param {number} section_data_start_y
|
|
230
231
|
* @param {Uint16Array} block_states
|
|
231
232
|
* @param {Uint8Array} block_light
|
|
232
233
|
* @param {Uint8Array} sky_light
|
|
@@ -241,13 +242,14 @@ let WASM_VECTOR_LEN = 0;
|
|
|
241
242
|
* @param {number} sky_light_value
|
|
242
243
|
* @returns {any}
|
|
243
244
|
*/
|
|
244
|
-
export function generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
|
|
245
|
+
export function generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
|
|
245
246
|
_assertNum(section_x);
|
|
246
247
|
_assertNum(section_y);
|
|
247
248
|
_assertNum(section_z);
|
|
248
249
|
_assertNum(section_height);
|
|
249
250
|
_assertNum(world_min_y);
|
|
250
251
|
_assertNum(world_max_y);
|
|
252
|
+
_assertNum(section_data_start_y);
|
|
251
253
|
const ptr0 = passArray16ToWasm0(block_states, wasm.__wbindgen_malloc);
|
|
252
254
|
const len0 = WASM_VECTOR_LEN;
|
|
253
255
|
const ptr1 = passArray8ToWasm0(block_light, wasm.__wbindgen_malloc);
|
|
@@ -269,7 +271,7 @@ export function generate_geometry(section_x, section_y, section_z, section_heigh
|
|
|
269
271
|
_assertBoolean(enable_lighting);
|
|
270
272
|
_assertBoolean(smooth_lighting);
|
|
271
273
|
_assertNum(sky_light_value);
|
|
272
|
-
const ret = wasm.generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, enable_lighting, smooth_lighting, sky_light_value);
|
|
274
|
+
const ret = wasm.generate_geometry(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, enable_lighting, smooth_lighting, sky_light_value);
|
|
273
275
|
return ret;
|
|
274
276
|
}
|
|
275
277
|
|
|
@@ -296,13 +298,14 @@ export function generate_geometry(section_x, section_y, section_z, section_heigh
|
|
|
296
298
|
* @param {number} sky_light_value
|
|
297
299
|
* @returns {any}
|
|
298
300
|
*/
|
|
299
|
-
export function generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, chunk_xs, chunk_zs, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
|
|
301
|
+
export function generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, chunk_xs, chunk_zs, block_states, block_light, sky_light, biomes, invisible_blocks, transparent_blocks, no_ao_blocks, cull_identical_blocks, occluding_blocks, enable_lighting, smooth_lighting, sky_light_value) {
|
|
300
302
|
_assertNum(section_x);
|
|
301
303
|
_assertNum(section_y);
|
|
302
304
|
_assertNum(section_z);
|
|
303
305
|
_assertNum(section_height);
|
|
304
306
|
_assertNum(world_min_y);
|
|
305
307
|
_assertNum(world_max_y);
|
|
308
|
+
_assertNum(section_data_start_y);
|
|
306
309
|
const ptr0 = passArray32ToWasm0(chunk_xs, wasm.__wbindgen_malloc);
|
|
307
310
|
const len0 = WASM_VECTOR_LEN;
|
|
308
311
|
const ptr1 = passArray32ToWasm0(chunk_zs, wasm.__wbindgen_malloc);
|
|
@@ -328,7 +331,7 @@ export function generate_geometry_multi(section_x, section_y, section_z, section
|
|
|
328
331
|
_assertBoolean(enable_lighting);
|
|
329
332
|
_assertBoolean(smooth_lighting);
|
|
330
333
|
_assertNum(sky_light_value);
|
|
331
|
-
const ret = wasm.generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, ptr9, len9, ptr10, len10, enable_lighting, smooth_lighting, sky_light_value);
|
|
334
|
+
const ret = wasm.generate_geometry_multi(section_x, section_y, section_z, section_height, world_min_y, world_max_y, section_data_start_y, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, ptr8, len8, ptr9, len9, ptr10, len10, enable_lighting, smooth_lighting, sky_light_value);
|
|
332
335
|
return ret;
|
|
333
336
|
}
|
|
334
337
|
|