minecraft-renderer 0.1.72 → 0.1.74

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/mesher.js +81 -81
  3. package/dist/mesher.js.map +3 -3
  4. package/dist/mesherWasm.js +1183 -943
  5. package/dist/minecraft-renderer.js +253 -80
  6. package/dist/minecraft-renderer.js.meta.json +1 -1
  7. package/dist/threeWorker.js +1735 -1002
  8. package/package.json +3 -3
  9. package/src/graphicsBackend/config.ts +4 -0
  10. package/src/graphicsBackend/rendererDefaultOptions.ts +2 -7
  11. package/src/graphicsBackend/rendererOptionsSync.ts +1 -1
  12. package/src/graphicsBackend/types.ts +1 -0
  13. package/src/lib/bakeLegacyLight.ts +17 -0
  14. package/src/lib/bindAbortableListener.ts +1 -1
  15. package/src/lib/blockEntityLightRegistry.test.ts +18 -0
  16. package/src/lib/blockEntityLightRegistry.ts +75 -0
  17. package/src/lib/blockEntityLighting.test.ts +30 -0
  18. package/src/lib/blockEntityLighting.ts +53 -0
  19. package/src/lib/createPlayerObject.ts +1 -1
  20. package/src/lib/worldrendererCommon.reconfigure.test.ts +4 -1
  21. package/src/lib/worldrendererCommon.removeColumn.test.ts +8 -4
  22. package/src/lib/worldrendererCommon.ts +15 -7
  23. package/src/mesher-shared/blockEntityMetadata.test.ts +33 -0
  24. package/src/mesher-shared/blockEntityMetadata.ts +19 -3
  25. package/src/mesher-shared/exportedGeometryTypes.ts +11 -0
  26. package/src/mesher-shared/models.ts +161 -92
  27. package/src/mesher-shared/shared.ts +15 -4
  28. package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +40 -0
  29. package/src/mesher-shared/world.ts +12 -0
  30. package/src/mesher-shared/worldLighting.test.ts +54 -0
  31. package/src/playground/baseScene.ts +1 -1
  32. package/src/three/bannerRenderer.ts +14 -4
  33. package/src/three/chunkMeshManager.ts +663 -69
  34. package/src/three/cubeDrawSpans.ts +74 -0
  35. package/src/three/cubeMultiDraw.ts +119 -0
  36. package/src/three/documentRenderer.ts +0 -2
  37. package/src/three/entities.ts +7 -7
  38. package/src/three/entity/EntityMesh.ts +7 -5
  39. package/src/three/entity/gltfAnimationUtils.ts +5 -3
  40. package/src/three/globalBlockBuffer.ts +208 -12
  41. package/src/three/globalLegacyBuffer.ts +701 -0
  42. package/src/three/graphicsBackendBase.ts +9 -5
  43. package/src/three/itemMesh.ts +6 -3
  44. package/src/three/legacySectionCull.ts +85 -0
  45. package/src/three/modules/rain.ts +22 -21
  46. package/src/three/modules/sciFiWorldReveal.ts +347 -703
  47. package/src/three/modules/starfield.ts +19 -6
  48. package/src/three/sectionRaycastAabb.ts +25 -0
  49. package/src/three/shaders/cubeBlockShader.ts +80 -17
  50. package/src/three/shaders/legacyBlockShader.ts +292 -0
  51. package/src/three/skyboxRenderer.ts +1 -1
  52. package/src/three/tests/chunkMeshManagerLegacy.test.ts +286 -0
  53. package/src/three/tests/cubeDrawSpans.test.ts +73 -0
  54. package/src/three/tests/globalLegacyBuffer.test.ts +360 -0
  55. package/src/three/tests/legacySectionCull.test.ts +80 -0
  56. package/src/three/tests/signTextureCache.test.ts +83 -0
  57. package/src/three/threeJsMedia.ts +2 -2
  58. package/src/three/waypointSprite.ts +2 -2
  59. package/src/three/world/cursorBlock.ts +1 -0
  60. package/src/three/world/vr.ts +2 -2
  61. package/src/three/worldGeometryExport.ts +83 -26
  62. package/src/three/worldRendererThree.ts +100 -30
  63. package/src/wasm-mesher/bridge/render-from-wasm.ts +214 -72
  64. package/src/wasm-mesher/bridge/shaderCubeBridge.ts +18 -6
  65. package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
  66. package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +20 -0
  67. package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +80 -12
  68. package/src/wasm-mesher/worker/mesherWasm.ts +70 -14
  69. package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +11 -0
  70. package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +15 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,7 +55,7 @@
55
55
  "skinview3d": "^3.4.1",
56
56
  "stats-gl": "^1.0.5",
57
57
  "stats.js": "^0.17.0",
58
- "three": "0.154.0",
58
+ "three": "0.184.0",
59
59
  "three-stdlib": "^2.36.1",
60
60
  "type-fest": "^5.3.0",
61
61
  "typed-emitter": "^2.1.0",
@@ -70,7 +70,7 @@
70
70
  "@types/lodash": "^4.17.21",
71
71
  "@types/react": "^19.2.7",
72
72
  "@types/stats.js": "^0.17.1",
73
- "@types/three": "0.154.0",
73
+ "@types/three": "0.184.0",
74
74
  "@zardoy/react-util": "^0.2.7",
75
75
  "@zardoy/tsconfig": "^1.5.1",
76
76
  "contro-max": "*",
@@ -81,6 +81,10 @@ export const defaultWorldRendererConfig = {
81
81
  isPlayground: false,
82
82
  instantCameraUpdate: false,
83
83
  isRaining: false,
84
+ // rainColor: 'rgb(64, 87, 148)', // original minecraft blue
85
+ rainColor: 'rgb(118, 148, 226)',
86
+ /** Rain particle opacity 0–1. */
87
+ rainOpacity: 0.5,
84
88
 
85
89
  // Module states: 'enabled' = force on, 'disabled' = force off, 'auto' = use autoEnableCheck
86
90
  moduleStates: {} as Record<string, 'enabled' | 'disabled' | 'auto'>
@@ -63,7 +63,6 @@ export const RENDERER_DEFAULT_OPTIONS = {
63
63
  rendererPerfDebugOverlay: false as boolean,
64
64
  disableBlockEntityTextures: false as boolean,
65
65
  rendererMesher: 'wasm' as RendererMesherPipeline,
66
- rendererShaderCubeBlocks: false as boolean,
67
66
  rendererShaderCubeDebugMode: 'off' as RendererShaderCubeDebugMode,
68
67
  showChunkBorders: false as boolean,
69
68
  renderEntities: true as boolean,
@@ -110,6 +109,7 @@ export function migrateRendererOptions(saved: Record<string, unknown>): void {
110
109
  }
111
110
  delete saved.wasmExperimentalMesher
112
111
  delete saved.rendererWasmMesher
112
+ delete saved.rendererShaderCubeBlocks
113
113
 
114
114
  if (saved.menuBackgroundMode === 'futuristic') {
115
115
  saved.menuBackgroundMode = 'v2'
@@ -196,11 +196,6 @@ export const RENDERER_OPTIONS_META: Partial<Record<RendererDefaultOptionKey, Ren
196
196
  tooltip: 'Browser technology for processing world geometry before render. WASM is the fastest; if you see a dead tab icon, reloads, or other errors, switch to Legacy JS.',
197
197
  requiresChunksReload: true,
198
198
  },
199
- rendererShaderCubeBlocks: {
200
- text: '(UNSTABLE) Instanced shader cubes',
201
- tooltip: 'Render full blocks through the global GPU instanced path. Requires WASM mesher and WebGL2.',
202
- requiresChunksReload: true,
203
- },
204
199
  rendererShaderCubeDebugMode: {
205
200
  text: 'Shader cube debug',
206
201
  tooltip: 'Instanced cube path visualization (requires shader cubes enabled).',
@@ -341,7 +336,7 @@ export const RENDERER_RENDER_GUI_SECTIONS: ReadonlyArray<{
341
336
  },
342
337
  {
343
338
  title: 'Mesher',
344
- keys: ['rendererMesher', 'rendererShaderCubeBlocks']
339
+ keys: ['rendererMesher']
345
340
  },
346
341
  {
347
342
  title: 'Renderer debug',
@@ -163,7 +163,7 @@ export function applyRendererOptions(
163
163
  cfg.fetchPlayerSkins = o.loadPlayerSkins
164
164
  cfg.highlightBlockColor = o.highlightBlockColor
165
165
  cfg.wasmMesher = wasmActive
166
- cfg.shaderCubeBlocks = o.rendererShaderCubeBlocks && wasmActive
166
+ cfg.shaderCubeBlocks = wasmActive
167
167
  cfg.disableMesherConversionCache = !!ctx.isSafari
168
168
 
169
169
  setSkinsConfig({ apiEnabled: o.loadPlayerSkins })
@@ -101,6 +101,7 @@ export interface GraphicsInitOptions<S = any> {
101
101
  /** Live app options (e.g. valtio proxy); used for WebGL `gpuPreference` at context creation. */
102
102
  getRendererOptions?: () => RendererStorageOptions
103
103
  rendererSpecificSettings: S
104
+ hello?: boolean
104
105
  callbacks: {
105
106
  displayCriticalError: (error: Error) => void
106
107
  setRendererSpecificSettings: (key: string, value: any) => void
@@ -0,0 +1,17 @@
1
+ //@ts-nocheck
2
+ /** Bake tint×AO colors with sky/block channels for static export (no live u_skyLevel uniform). */
3
+ export function bakeLegacyVertexColors (
4
+ colors: ArrayLike<number>,
5
+ skyLights: ArrayLike<number>,
6
+ blockLights: ArrayLike<number>,
7
+ skyLevel: number,
8
+ ): number[] {
9
+ const vertCount = colors.length / 3
10
+ const out: number[] = []
11
+ for (let v = 0; v < vertCount; v++) {
12
+ const L = Math.max(blockLights[v] ?? 0, Math.min(skyLights[v] ?? 1, skyLevel))
13
+ const i = v * 3
14
+ out.push(colors[i]! * L, colors[i + 1]! * L, colors[i + 2]! * L)
15
+ }
16
+ return out
17
+ }
@@ -10,7 +10,7 @@ import type { WorldViewWorker } from '../worldView'
10
10
  export function bindAbortableListener<E extends keyof WorldViewEvents>(
11
11
  emitter: Pick<WorldViewWorker, 'on' | 'off'>,
12
12
  event: E,
13
- handler: (...args: WorldViewEvents[E]) => void,
13
+ handler: (...args: Parameters<WorldViewEvents[E]>) => void,
14
14
  signal: AbortSignal
15
15
  ): void {
16
16
  emitter.on(event, handler as (...args: any[]) => void)
@@ -0,0 +1,18 @@
1
+ //@ts-nocheck
2
+ import { describe, expect, it } from 'vitest'
3
+ import * as THREE from 'three'
4
+ import { blockEntityBrightness } from './blockEntityLighting'
5
+ import { BlockEntityLightRegistry } from './blockEntityLightRegistry'
6
+
7
+ describe('BlockEntityLightRegistry', () => {
8
+ it('refreshes overlay brightness when sky level changes', () => {
9
+ const registry = new BlockEntityLightRegistry()
10
+ const material = new THREE.MeshBasicMaterial()
11
+ registry.register({ material, blockLightNorm: 0, skyLightNorm: 1 })
12
+ const nightSky = 4 / 15
13
+ registry.setSkyLevel(nightSky)
14
+ expect(material.color.r).toBeCloseTo(blockEntityBrightness(0, 1, nightSky), 5)
15
+ registry.setSkyLevel(1)
16
+ expect(material.color.r).toBe(1)
17
+ })
18
+ })
@@ -0,0 +1,75 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import {
4
+ blockEntityBrightness,
5
+ DEFAULT_LIGHTMAP_PARAMS,
6
+ type BlockLightmapParams,
7
+ } from './blockEntityLighting'
8
+
9
+ export type BlockEntityOverlayLight = {
10
+ material: THREE.MeshBasicMaterial
11
+ blockLightNorm: number
12
+ skyLightNorm: number
13
+ }
14
+
15
+ export class BlockEntityLightRegistry {
16
+ private readonly entries = new Set<BlockEntityOverlayLight>()
17
+ private skyLevel = 1
18
+ private lightmapParams: BlockLightmapParams = { ...DEFAULT_LIGHTMAP_PARAMS }
19
+
20
+ register (entry: BlockEntityOverlayLight): void {
21
+ this.entries.add(entry)
22
+ this.applyBrightness(entry)
23
+ }
24
+
25
+ unregister (material: THREE.Material): void {
26
+ for (const entry of this.entries) {
27
+ if (entry.material === material) {
28
+ this.entries.delete(entry)
29
+ break
30
+ }
31
+ }
32
+ }
33
+
34
+ setSkyLevel (value: number): void {
35
+ this.skyLevel = value
36
+ this.refreshAll()
37
+ }
38
+
39
+ setLightmapParams (params: BlockLightmapParams): void {
40
+ this.lightmapParams = { ...this.lightmapParams, ...params }
41
+ this.refreshAll()
42
+ }
43
+
44
+ getSkyLevel (): number {
45
+ return this.skyLevel
46
+ }
47
+
48
+ private refreshAll (): void {
49
+ for (const entry of this.entries) {
50
+ this.applyBrightness(entry)
51
+ }
52
+ }
53
+
54
+ private applyBrightness (entry: BlockEntityOverlayLight): void {
55
+ const brightness = blockEntityBrightness(
56
+ entry.blockLightNorm,
57
+ entry.skyLightNorm,
58
+ this.skyLevel,
59
+ this.lightmapParams,
60
+ )
61
+ entry.material.color.setScalar(brightness)
62
+ }
63
+ }
64
+
65
+ export function tintBannerMaterial (
66
+ material: THREE.MeshBasicMaterial,
67
+ blockLightNorm: number,
68
+ skyLightNorm: number,
69
+ skyLevel: number,
70
+ lightmapParams: BlockLightmapParams = DEFAULT_LIGHTMAP_PARAMS,
71
+ ): number {
72
+ const brightness = blockEntityBrightness(blockLightNorm, skyLightNorm, skyLevel, lightmapParams)
73
+ material.color.setScalar(brightness)
74
+ return brightness
75
+ }
@@ -0,0 +1,30 @@
1
+ //@ts-nocheck
2
+ import { describe, expect, it } from 'vitest'
3
+ import {
4
+ applyLightmap,
5
+ blockEntityBrightness,
6
+ combinedBlockLight,
7
+ DEFAULT_LIGHTMAP_PARAMS,
8
+ } from './blockEntityLighting'
9
+
10
+ describe('blockEntityLighting', () => {
11
+ it('applyLightmap(1) === 1 for default params', () => {
12
+ expect(applyLightmap(1, DEFAULT_LIGHTMAP_PARAMS)).toBe(1)
13
+ })
14
+
15
+ it('combinedBlockLight caps sky by skyLevel', () => {
16
+ expect(combinedBlockLight(0, 1, 4 / 15)).toBeCloseTo(4 / 15, 5)
17
+ expect(combinedBlockLight(0.5, 1, 4 / 15)).toBeCloseTo(0.5, 5)
18
+ })
19
+
20
+ it('night outdoor blockEntityBrightness matches cap + lightmap', () => {
21
+ const skyLevel = 4 / 15
22
+ const L = combinedBlockLight(0, 1, skyLevel)
23
+ expect(blockEntityBrightness(0, 1, skyLevel)).toBeCloseTo(applyLightmap(L), 5)
24
+ })
25
+
26
+ it('linear curve matches raw L at minBrightness 0', () => {
27
+ const p = { curve: 0, minBrightness: 0, gamma: 1 }
28
+ expect(applyLightmap(0.5, p)).toBeCloseTo(0.5, 5)
29
+ })
30
+ })
@@ -0,0 +1,53 @@
1
+ //@ts-nocheck
2
+ /**
3
+ * Shared block lighting math — keep in sync with APPLY_LIGHTMAP_GLSL in shaders.
4
+ */
5
+
6
+ export type BlockLightmapParams = {
7
+ curve?: number
8
+ minBrightness?: number
9
+ gamma?: number
10
+ }
11
+
12
+ export const DEFAULT_LIGHTMAP_PARAMS: Required<BlockLightmapParams> = {
13
+ curve: 0,
14
+ minBrightness: 0.12,
15
+ gamma: 1,
16
+ }
17
+
18
+ /** GLSL body for applyLightmap — requires u_lightCurve, u_minBrightness, u_lightGamma uniforms. */
19
+ export const APPLY_LIGHTMAP_GLSL = /* glsl */ `
20
+ float applyLightmap(float L) {
21
+ float curved = L / (4.0 - 3.0 * L);
22
+ float shaped = mix(L, curved, u_lightCurve);
23
+ shaped = mix(u_minBrightness, 1.0, shaped);
24
+ return clamp(pow(shaped, u_lightGamma), 0.0, 1.0);
25
+ }
26
+ `
27
+
28
+ export function applyLightmap (L: number, params: BlockLightmapParams = DEFAULT_LIGHTMAP_PARAMS): number {
29
+ const curve = params.curve ?? DEFAULT_LIGHTMAP_PARAMS.curve
30
+ const minBrightness = params.minBrightness ?? DEFAULT_LIGHTMAP_PARAMS.minBrightness
31
+ const gamma = params.gamma ?? DEFAULT_LIGHTMAP_PARAMS.gamma
32
+
33
+ const curved = L / (4 - 3 * L)
34
+ let shaped = L * (1 - curve) + curved * curve
35
+ shaped = minBrightness + shaped * (1 - minBrightness)
36
+ return Math.min(1, Math.max(0, shaped ** gamma))
37
+ }
38
+
39
+ /** Same cap as block shaders: max(block, min(sky, skyLevel)). */
40
+ export function combinedBlockLight (block: number, sky: number, skyLevel: number): number {
41
+ return Math.max(block, Math.min(sky, skyLevel))
42
+ }
43
+
44
+ /** 0..1 brightness for MeshBasicMaterial.color.setScalar on block-entity overlays. */
45
+ export function blockEntityBrightness (
46
+ blockNorm: number,
47
+ skyNorm: number,
48
+ skyLevel: number,
49
+ lightmapParams: BlockLightmapParams = DEFAULT_LIGHTMAP_PARAMS,
50
+ ): number {
51
+ const L = combinedBlockLight(blockNorm, skyNorm, skyLevel)
52
+ return applyLightmap(L, lightmapParams)
53
+ }
@@ -12,7 +12,7 @@ export type PlayerObjectType = PlayerObject & {
12
12
 
13
13
  /** Starfield + log-depth world: cutout skin mats need alphaTest and depthWrite (not mesh traverse). */
14
14
  export function configurePlayerSkinMaterials (playerObject: PlayerObject): void {
15
- const skin = playerObject.skin
15
+ const skin = playerObject.skin as any
16
16
  const materials = [
17
17
  skin.layer1Material,
18
18
  skin.layer1MaterialBiased,
@@ -6,6 +6,7 @@ import { proxy } from 'valtio'
6
6
  import * as worldRendererModule from './worldrendererCommon'
7
7
  import { WorldRendererCommon } from './worldrendererCommon'
8
8
  import { defaultWorldRendererConfig } from '../graphicsBackend/config'
9
+ import { defaultPerformanceInstabilityFactors } from '../performanceMonitor'
9
10
  import { getInitialPlayerState } from '../playerState/playerState'
10
11
  import type { DisplayWorldOptions, GraphicsInitOptions } from '../graphicsBackend/types'
11
12
 
@@ -46,6 +47,8 @@ class TestWorldRenderer extends WorldRendererCommon {
46
47
  updateCamera() {}
47
48
  render() {}
48
49
  updateShowChunksBorder() {}
50
+ updatePlayerEntity() {}
51
+ worldStop() {}
49
52
  }
50
53
 
51
54
  function createRenderer(workerCount = 2, worldView?: DisplayWorldOptions['worldView']) {
@@ -56,7 +59,7 @@ function createRenderer(workerCount = 2, worldView?: DisplayWorldOptions['worldV
56
59
  heightmaps: {} as Record<string, Int16Array>,
57
60
  allChunksLoaded: false,
58
61
  mesherWork: false,
59
- instabilityFactors: {},
62
+ instabilityFactors: defaultPerformanceInstabilityFactors(),
60
63
  intersectMedia: null,
61
64
  },
62
65
  renderer: '',
@@ -5,6 +5,7 @@ import { Vec3 } from 'vec3'
5
5
  import { proxy } from 'valtio'
6
6
  import { WorldRendererCommon } from './worldrendererCommon'
7
7
  import { defaultWorldRendererConfig } from '../graphicsBackend/config'
8
+ import { defaultPerformanceInstabilityFactors } from '../performanceMonitor'
8
9
  import { getInitialPlayerState } from '../playerState/playerState'
9
10
  import type { DisplayWorldOptions, GraphicsInitOptions } from '../graphicsBackend/types'
10
11
 
@@ -45,16 +46,18 @@ class TestWorldRenderer extends WorldRendererCommon {
45
46
  updateCamera() {}
46
47
  render() {}
47
48
  updateShowChunksBorder() {}
49
+ updatePlayerEntity() {}
50
+ worldStop() {}
48
51
  }
49
52
 
50
53
  function createRenderer() {
51
54
  const rendererState = proxy({
52
55
  world: {
53
- chunksLoaded: new Set<string>(),
54
- heightmaps: new Map<string, Int16Array>(),
56
+ chunksLoaded: {} as Record<string, true>,
57
+ heightmaps: {} as Record<string, Int16Array>,
55
58
  allChunksLoaded: false,
56
59
  mesherWork: false,
57
- instabilityFactors: {},
60
+ instabilityFactors: defaultPerformanceInstabilityFactors(),
58
61
  intersectMedia: null,
59
62
  },
60
63
  renderer: '',
@@ -73,6 +76,7 @@ function createRenderer() {
73
76
  avgRenderTime: 0,
74
77
  world: {
75
78
  chunksLoaded: new Set<string>(),
79
+ chunksLoadedCount: 0,
76
80
  chunksTotalNumber: 0,
77
81
  chunksFullInfo: '',
78
82
  },
@@ -93,7 +97,7 @@ function createRenderer() {
93
97
  },
94
98
  }
95
99
 
96
- const renderer = new TestWorldRenderer(displayOptions.resourcesManager, displayOptions, initOptions)
100
+ const renderer = new TestWorldRenderer(displayOptions.resourcesManager, displayOptions as DisplayWorldOptions, initOptions)
97
101
  renderer.active = true
98
102
  renderer.workers = [{ postMessage: vi.fn() }, { postMessage: vi.fn() }]
99
103
  renderer.viewDistance = 16
@@ -33,6 +33,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
33
33
  worldReadyResolvers = Promise.withResolvers<void>()
34
34
  worldReadyPromise = this.worldReadyResolvers.promise
35
35
  timeOfTheDay = 0
36
+ lastMesherSkyLight = 15
36
37
  worldSizeParams = { minY: 0, worldHeight: 256 }
37
38
  reactiveDebugParams = proxy({
38
39
  stopRendering: false,
@@ -695,6 +696,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
695
696
 
696
697
  timeUpdated?(newTime: number): void
697
698
 
699
+ /** Called when day-cycle sky-light bucket changes; Three.js overrides to remesh. */
700
+ protected onDayCycleSkyLightChanged?(_skyLight: number): void
701
+
698
702
  biomeUpdated?(biome: any): void
699
703
 
700
704
  biomeReset?(): void
@@ -1064,16 +1068,20 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
1064
1068
  }, signal)
1065
1069
 
1066
1070
  bindAbortableListener(worldEmitter, 'time', (timeOfDay) => {
1067
- if (!this.worldRendererConfig.dayCycle) return
1071
+ if (!this.worldRendererConfig.dayCycle) {
1072
+ return
1073
+ }
1068
1074
  this.timeUpdated?.(timeOfDay)
1069
1075
 
1070
1076
  this.timeOfTheDay = timeOfDay
1071
1077
 
1072
- // if (this.worldRendererConfig.skyLight === skyLight) return
1073
- // this.worldRendererConfig.skyLight = skyLight
1074
- // if (this instanceof WorldRendererThree) {
1075
- // (this).rerenderAllChunks?.()
1076
- // }
1078
+ const skyLight = (timeOfDay < 0 || timeOfDay > 24_000) ? 15 : calculateSkyLightSimple(timeOfDay)
1079
+ if (this.lastMesherSkyLight === skyLight) return
1080
+ this.lastMesherSkyLight = skyLight
1081
+ if (this.workers.length > 0) {
1082
+ this.sendWorkers({ config: { skyLight } } as WorkerSend)
1083
+ }
1084
+ this.onDayCycleSkyLightChanged?.(skyLight)
1077
1085
  }, signal)
1078
1086
 
1079
1087
  bindAbortableListener(worldEmitter, 'biomeUpdate', ({ biome }) => {
@@ -1435,7 +1443,7 @@ export const initMesherWorker = (onGotMessage: (data: any) => void, workerName =
1435
1443
  let mesherMcDataTintsMissingWarned = false
1436
1444
 
1437
1445
  export const meshersSendMcData = (workers: Worker[], version: string, mcDataKeys = dynamicMcDataFiles, mcDataFull: IndexedData) => {
1438
- const mcData = {
1446
+ const mcData: { version: IndexedData['version']; tints?: unknown; [key: string]: unknown } = {
1439
1447
  version: JSON.parse(JSON.stringify(mcDataFull.version))
1440
1448
  }
1441
1449
  for (const [finalKey, sourceKey] of Object.entries(mcDataKeys)) {
@@ -0,0 +1,33 @@
1
+ //@ts-nocheck
2
+ import { describe, expect, it } from 'vitest'
3
+ import { Vec3 } from 'vec3'
4
+ import { collectBlockEntityMetadata } from './blockEntityMetadata'
5
+
6
+ describe('collectBlockEntityMetadata', () => {
7
+ it('stores channel light norms on banner metadata', () => {
8
+ const target = { signs: {}, heads: {}, banners: {} }
9
+ const block = {
10
+ name: 'pink_banner',
11
+ getProperties: () => ({ rotation: 0 }),
12
+ }
13
+ collectBlockEntityMetadata(
14
+ block,
15
+ 1, 2, 3,
16
+ target,
17
+ {},
18
+ {
19
+ getChannelLightNorm: (pos: Vec3) => {
20
+ expect(pos).toBeInstanceOf(Vec3)
21
+ return { block: 0.4, sky: 0.8 }
22
+ },
23
+ },
24
+ )
25
+ expect(target.banners['1,2,3']).toEqual({
26
+ isWall: false,
27
+ blockName: 'pink_banner',
28
+ rotation: 0,
29
+ blockLightNorm: 0.4,
30
+ skyLightNorm: 0.8,
31
+ })
32
+ })
33
+ })
@@ -1,7 +1,15 @@
1
1
  //@ts-nocheck
2
+ import { Vec3 } from 'vec3'
3
+
2
4
  export interface SignMeta { isWall: boolean; isHanging: boolean; rotation: number }
3
5
  export interface HeadMeta { isWall: boolean; rotation: number }
4
- export interface BannerMeta { isWall: boolean; blockName: string; rotation: number }
6
+ export interface BannerMeta {
7
+ isWall: boolean
8
+ blockName: string
9
+ rotation: number
10
+ blockLightNorm: number
11
+ skyLightNorm: number
12
+ }
5
13
 
6
14
  export interface BlockEntityMetadataTarget {
7
15
  signs: Record<string, SignMeta>
@@ -15,11 +23,16 @@ export interface BlockEntityMetadataOptions {
15
23
 
16
24
  type BlockLike = { name: string; getProperties(): any }
17
25
 
26
+ type LightSampler = {
27
+ getChannelLightNorm(pos: Vec3): { block: number, sky: number }
28
+ }
29
+
18
30
  export function collectBlockEntityMetadata(
19
31
  block: BlockLike,
20
32
  x: number, y: number, z: number,
21
33
  target: BlockEntityMetadataTarget,
22
- options: BlockEntityMetadataOptions
34
+ options: BlockEntityMetadataOptions,
35
+ world?: LightSampler,
23
36
  ): void {
24
37
  if ((block.name.includes('_sign') || block.name === 'sign') && !options.disableBlockEntityTextures) {
25
38
  const key = `${x},${y},${z}`
@@ -61,10 +74,13 @@ export function collectBlockEntityMetadata(
61
74
  'east': 3
62
75
  }
63
76
  const isWall = block.name.endsWith('_wall_banner')
77
+ const light = world?.getChannelLightNorm(new Vec3(x, y, z)) ?? { block: 0, sky: 1 }
64
78
  target.banners[key] = {
65
79
  isWall,
66
80
  blockName: block.name, // Pass block name for base color extraction
67
- rotation: isWall ? facingRotationMap[props.facing] : (props.rotation === undefined ? 0 : +props.rotation)
81
+ rotation: isWall ? facingRotationMap[props.facing] : (props.rotation === undefined ? 0 : +props.rotation),
82
+ blockLightNorm: light.block,
83
+ skyLightNorm: light.sky,
68
84
  }
69
85
  }
70
86
  }
@@ -8,6 +8,17 @@ export interface ExportedSection {
8
8
  positions: number[]
9
9
  normals: number[]
10
10
  colors: number[]
11
+ skyLights: number[]
12
+ blockLights: number[]
13
+ uvs: number[]
14
+ indices: number[]
15
+ }
16
+ blendGeometry?: {
17
+ positions: number[]
18
+ normals: number[]
19
+ colors: number[]
20
+ skyLights: number[]
21
+ blockLights: number[]
11
22
  uvs: number[]
12
23
  indices: number[]
13
24
  }