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.
- package/README.md +1 -1
- package/dist/mesher.js +81 -81
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +1183 -943
- package/dist/minecraft-renderer.js +253 -80
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +1735 -1002
- package/package.json +3 -3
- package/src/graphicsBackend/config.ts +4 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +2 -7
- package/src/graphicsBackend/rendererOptionsSync.ts +1 -1
- package/src/graphicsBackend/types.ts +1 -0
- package/src/lib/bakeLegacyLight.ts +17 -0
- package/src/lib/bindAbortableListener.ts +1 -1
- package/src/lib/blockEntityLightRegistry.test.ts +18 -0
- package/src/lib/blockEntityLightRegistry.ts +75 -0
- package/src/lib/blockEntityLighting.test.ts +30 -0
- package/src/lib/blockEntityLighting.ts +53 -0
- package/src/lib/createPlayerObject.ts +1 -1
- package/src/lib/worldrendererCommon.reconfigure.test.ts +4 -1
- package/src/lib/worldrendererCommon.removeColumn.test.ts +8 -4
- package/src/lib/worldrendererCommon.ts +15 -7
- package/src/mesher-shared/blockEntityMetadata.test.ts +33 -0
- package/src/mesher-shared/blockEntityMetadata.ts +19 -3
- package/src/mesher-shared/exportedGeometryTypes.ts +11 -0
- package/src/mesher-shared/models.ts +161 -92
- package/src/mesher-shared/shared.ts +15 -4
- package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +40 -0
- package/src/mesher-shared/world.ts +12 -0
- package/src/mesher-shared/worldLighting.test.ts +54 -0
- package/src/playground/baseScene.ts +1 -1
- package/src/three/bannerRenderer.ts +14 -4
- package/src/three/chunkMeshManager.ts +663 -69
- package/src/three/cubeDrawSpans.ts +74 -0
- package/src/three/cubeMultiDraw.ts +119 -0
- package/src/three/documentRenderer.ts +0 -2
- package/src/three/entities.ts +7 -7
- package/src/three/entity/EntityMesh.ts +7 -5
- package/src/three/entity/gltfAnimationUtils.ts +5 -3
- package/src/three/globalBlockBuffer.ts +208 -12
- package/src/three/globalLegacyBuffer.ts +701 -0
- package/src/three/graphicsBackendBase.ts +9 -5
- package/src/three/itemMesh.ts +6 -3
- package/src/three/legacySectionCull.ts +85 -0
- package/src/three/modules/rain.ts +22 -21
- package/src/three/modules/sciFiWorldReveal.ts +347 -703
- package/src/three/modules/starfield.ts +19 -6
- package/src/three/sectionRaycastAabb.ts +25 -0
- package/src/three/shaders/cubeBlockShader.ts +80 -17
- package/src/three/shaders/legacyBlockShader.ts +292 -0
- package/src/three/skyboxRenderer.ts +1 -1
- package/src/three/tests/chunkMeshManagerLegacy.test.ts +286 -0
- package/src/three/tests/cubeDrawSpans.test.ts +73 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +360 -0
- package/src/three/tests/legacySectionCull.test.ts +80 -0
- package/src/three/tests/signTextureCache.test.ts +83 -0
- package/src/three/threeJsMedia.ts +2 -2
- package/src/three/waypointSprite.ts +2 -2
- package/src/three/world/cursorBlock.ts +1 -0
- package/src/three/world/vr.ts +2 -2
- package/src/three/worldGeometryExport.ts +83 -26
- package/src/three/worldRendererThree.ts +100 -30
- package/src/wasm-mesher/bridge/render-from-wasm.ts +214 -72
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +18 -6
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +20 -0
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +80 -12
- package/src/wasm-mesher/worker/mesherWasm.ts +70 -14
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +11 -0
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +15 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { test, expect } from 'vitest'
|
|
3
|
+
import * as THREE from 'three'
|
|
4
|
+
import { sectionIntersectsFrustum, setupLegacySectionMatrix, updateLegacySectionCullState } from '../legacySectionCull'
|
|
5
|
+
|
|
6
|
+
test('setupLegacySectionMatrix: translation set once and stable across frames', () => {
|
|
7
|
+
const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial())
|
|
8
|
+
mesh.matrixAutoUpdate = false
|
|
9
|
+
|
|
10
|
+
setupLegacySectionMatrix(mesh, 100, 64, -200, { x: 0, y: 0, z: 0 })
|
|
11
|
+
|
|
12
|
+
expect(mesh.matrix.elements[12]).toBe(100)
|
|
13
|
+
expect(mesh.matrix.elements[13]).toBe(64)
|
|
14
|
+
expect(mesh.matrix.elements[14]).toBe(-200)
|
|
15
|
+
expect(mesh.frustumCulled).toBe(false)
|
|
16
|
+
|
|
17
|
+
const before = mesh.matrix.elements.slice()
|
|
18
|
+
// No per-frame matrix write in 2a — matrix must stay unchanged across frames.
|
|
19
|
+
expect(mesh.matrix.elements.slice()).toEqual(before)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('updateLegacySectionCullState: frustum hit sets visible and nearer section sorts later', () => {
|
|
23
|
+
const meshNear = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial())
|
|
24
|
+
const meshFar = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial())
|
|
25
|
+
|
|
26
|
+
const frustum = { intersectsBox: () => true } as unknown as THREE.Frustum
|
|
27
|
+
|
|
28
|
+
const box = new THREE.Box3()
|
|
29
|
+
const boxMin = new THREE.Vector3()
|
|
30
|
+
const boxMax = new THREE.Vector3()
|
|
31
|
+
|
|
32
|
+
updateLegacySectionCullState(meshNear, 0, 0, 0, 0, 0, 0, frustum, box, boxMin, boxMax)
|
|
33
|
+
updateLegacySectionCullState(meshFar, 32, 0, 0, 0, 0, 0, frustum, box, boxMin, boxMax)
|
|
34
|
+
|
|
35
|
+
expect(meshNear.visible).toBe(true)
|
|
36
|
+
expect(meshFar.visible).toBe(true)
|
|
37
|
+
expect(meshFar.renderOrder).toBeLessThan(meshNear.renderOrder)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('sectionIntersectsFrustum: returns distSq and visibility', () => {
|
|
41
|
+
const frustum = { intersectsBox: () => true } as unknown as THREE.Frustum
|
|
42
|
+
const box = new THREE.Box3()
|
|
43
|
+
const boxMin = new THREE.Vector3()
|
|
44
|
+
const boxMax = new THREE.Vector3()
|
|
45
|
+
|
|
46
|
+
const result = sectionIntersectsFrustum(10, 0, 0, 0, 0, 0, frustum, box, boxMin, boxMax)
|
|
47
|
+
expect(result.visible).toBe(true)
|
|
48
|
+
expect(result.distSq).toBe(100)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('updateLegacySectionCullState: outside frustum hides mesh', () => {
|
|
52
|
+
const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial())
|
|
53
|
+
const frustum = new THREE.Frustum()
|
|
54
|
+
const proj = new THREE.Matrix4()
|
|
55
|
+
const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 100)
|
|
56
|
+
camera.position.set(0, 0, 0)
|
|
57
|
+
camera.lookAt(0, 0, -1)
|
|
58
|
+
camera.updateMatrixWorld()
|
|
59
|
+
proj.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
|
|
60
|
+
frustum.setFromProjectionMatrix(proj)
|
|
61
|
+
|
|
62
|
+
const box = new THREE.Box3()
|
|
63
|
+
const boxMin = new THREE.Vector3()
|
|
64
|
+
const boxMax = new THREE.Vector3()
|
|
65
|
+
|
|
66
|
+
updateLegacySectionCullState(mesh, 500, 0, 0, 0, 0, 0, frustum, box, boxMin, boxMax)
|
|
67
|
+
|
|
68
|
+
expect(mesh.visible).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('setupLegacySectionMatrix: non-zero render origin stores world minus R', () => {
|
|
72
|
+
const mesh = new THREE.Mesh(new THREE.BufferGeometry(), new THREE.MeshBasicMaterial())
|
|
73
|
+
mesh.matrixAutoUpdate = false
|
|
74
|
+
|
|
75
|
+
setupLegacySectionMatrix(mesh, 100, 64, -200, { x: 16, y: 0, z: 16 })
|
|
76
|
+
|
|
77
|
+
expect(mesh.matrix.elements[12]).toBe(84)
|
|
78
|
+
expect(mesh.matrix.elements[13]).toBe(64)
|
|
79
|
+
expect(mesh.matrix.elements[14]).toBe(-216)
|
|
80
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { test, expect, vi, beforeEach } from 'vitest'
|
|
3
|
+
import * as THREE from 'three'
|
|
4
|
+
import { Vec3 } from 'vec3'
|
|
5
|
+
|
|
6
|
+
vi.mock('../entity/EntityMesh', () => ({
|
|
7
|
+
getMesh: vi.fn(),
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
const renderSignMock = vi.fn()
|
|
11
|
+
vi.mock('../../sign-renderer', () => ({
|
|
12
|
+
renderSign: (...args: unknown[]) => renderSignMock(...args),
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
vi.mock('prismarine-chat', () => ({
|
|
16
|
+
default: () => () => ({}),
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
import { ChunkMeshManager } from '../chunkMeshManager'
|
|
20
|
+
import type { WorldRendererThree } from '../worldRendererThree'
|
|
21
|
+
|
|
22
|
+
function createManager (): ChunkMeshManager {
|
|
23
|
+
const scene = new THREE.Scene()
|
|
24
|
+
const material = new THREE.MeshBasicMaterial()
|
|
25
|
+
const worldRenderer = {
|
|
26
|
+
version: '1.20',
|
|
27
|
+
shaderCubeBlocksEnabled: () => false,
|
|
28
|
+
getModule: () => undefined,
|
|
29
|
+
sceneOrigin: {
|
|
30
|
+
track: () => {},
|
|
31
|
+
removeAndUntrack: () => {},
|
|
32
|
+
removeAndUntrackAll: () => {},
|
|
33
|
+
},
|
|
34
|
+
blockEntities: {},
|
|
35
|
+
worldRendererConfig: {},
|
|
36
|
+
} as unknown as WorldRendererThree
|
|
37
|
+
return new ChunkMeshManager(worldRenderer, scene, material, 256, 1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stubCanvas () {
|
|
41
|
+
return { width: 64, height: 32 } as HTMLCanvasElement
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
renderSignMock.mockReset()
|
|
46
|
+
renderSignMock.mockImplementation(() => stubCanvas())
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('getSignTexture: same blockEntity returns cached texture without re-render', () => {
|
|
50
|
+
const manager = createManager()
|
|
51
|
+
const signHeadsRenderer = (manager as unknown as { signHeadsRenderer: { getSignTexture: Function } }).signHeadsRenderer
|
|
52
|
+
const pos = new Vec3(10, 64, 10)
|
|
53
|
+
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
54
|
+
|
|
55
|
+
const tex1 = signHeadsRenderer.getSignTexture(pos, blockEntity, false)
|
|
56
|
+
const tex2 = signHeadsRenderer.getSignTexture(pos, blockEntity, false)
|
|
57
|
+
|
|
58
|
+
expect(tex1).toBeDefined()
|
|
59
|
+
expect(tex2).toBe(tex1)
|
|
60
|
+
expect(renderSignMock).toHaveBeenCalledTimes(1)
|
|
61
|
+
|
|
62
|
+
manager.dispose()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('getSignTexture: changed blockEntity disposes old texture and renders anew', () => {
|
|
66
|
+
const manager = createManager()
|
|
67
|
+
const signHeadsRenderer = (manager as unknown as { signHeadsRenderer: { getSignTexture: Function } }).signHeadsRenderer
|
|
68
|
+
const pos = new Vec3(10, 64, 10)
|
|
69
|
+
const blockEntity = { Text1: '{"text":"Hello"}' }
|
|
70
|
+
|
|
71
|
+
const tex1 = signHeadsRenderer.getSignTexture(pos, blockEntity, false)!
|
|
72
|
+
const disposeSpy = vi.spyOn(tex1, 'dispose')
|
|
73
|
+
|
|
74
|
+
const changed = { Text1: '{"text":"World"}' }
|
|
75
|
+
const tex2 = signHeadsRenderer.getSignTexture(pos, changed, false)
|
|
76
|
+
|
|
77
|
+
expect(tex2).toBeDefined()
|
|
78
|
+
expect(tex2).not.toBe(tex1)
|
|
79
|
+
expect(disposeSpy).toHaveBeenCalledTimes(1)
|
|
80
|
+
expect(renderSignMock).toHaveBeenCalledTimes(2)
|
|
81
|
+
|
|
82
|
+
manager.dispose()
|
|
83
|
+
})
|
|
@@ -81,7 +81,7 @@ export class ThreeJsMedia {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
private createErrorTexture(width: number, height: number, background = 0xff_ff_ff, error = 'Failed to load'): THREE.CanvasTexture {
|
|
84
|
+
private createErrorTexture(width: number, height: number, background = 0xff_ff_ff, error = 'Failed to load'): THREE.CanvasTexture<OffscreenCanvas> {
|
|
85
85
|
const canvas = new OffscreenCanvas(100, 100)
|
|
86
86
|
const MAX_DIMENSION = 100
|
|
87
87
|
|
|
@@ -111,7 +111,7 @@ export class ThreeJsMedia {
|
|
|
111
111
|
return texture
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
private createBackgroundTexture(width: number, height: number, color = 0x00_00_00, opacity = 1): THREE.CanvasTexture {
|
|
114
|
+
private createBackgroundTexture(width: number, height: number, color = 0x00_00_00, opacity = 1): THREE.CanvasTexture<OffscreenCanvas> {
|
|
115
115
|
const canvas = new OffscreenCanvas(1, 1)
|
|
116
116
|
canvas.width = 1
|
|
117
117
|
canvas.height = 1
|
|
@@ -89,7 +89,7 @@ export function createWaypointSprite (options: {
|
|
|
89
89
|
const labelCanvas = createCanvas(getLabelCanvasSize(), getLabelCanvasSize())
|
|
90
90
|
drawCombinedOntoCanvas(labelCanvas, displayColor, options.label ?? '', '0m', visualScale)
|
|
91
91
|
|
|
92
|
-
const labelTexture = new THREE.CanvasTexture(labelCanvas)
|
|
92
|
+
const labelTexture: THREE.CanvasTexture<OffscreenCanvas> = new THREE.CanvasTexture(labelCanvas)
|
|
93
93
|
labelTexture.anisotropy = 1
|
|
94
94
|
labelTexture.magFilter = THREE.LinearFilter
|
|
95
95
|
labelTexture.minFilter = THREE.LinearFilter
|
|
@@ -112,7 +112,7 @@ export function createWaypointSprite (options: {
|
|
|
112
112
|
let arrowSprite: THREE.Sprite | undefined
|
|
113
113
|
let arrowCanvas: OffscreenCanvas | undefined
|
|
114
114
|
let arrowCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | undefined
|
|
115
|
-
let arrowTexture: THREE.CanvasTexture | undefined
|
|
115
|
+
let arrowTexture: THREE.CanvasTexture<OffscreenCanvas> | undefined
|
|
116
116
|
let arrowParent: THREE.Object3D | null = null
|
|
117
117
|
let arrowEnabled = WAYPOINT_CONFIG.ARROW.enabledDefault
|
|
118
118
|
|
|
@@ -52,6 +52,7 @@ export class CursorBlock {
|
|
|
52
52
|
const breakMaterial = new THREE.MeshBasicMaterial({
|
|
53
53
|
transparent: true,
|
|
54
54
|
blending: THREE.MultiplyBlending,
|
|
55
|
+
premultipliedAlpha: true,
|
|
55
56
|
alphaTest: 0.5,
|
|
56
57
|
})
|
|
57
58
|
this.blockBreakMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), breakMaterial)
|
package/src/three/world/vr.ts
CHANGED
|
@@ -124,14 +124,14 @@ export async function initVR(worldRenderer: WorldRendererThree, documentRenderer
|
|
|
124
124
|
let hand1: any = controllerModelFactory.createControllerModel(controller1)
|
|
125
125
|
controller1.addEventListener('connected', (event) => {
|
|
126
126
|
hand1.xrInputSource = event.data
|
|
127
|
-
manageXrInputSource(event.data, 'left')
|
|
127
|
+
manageXrInputSource(event.data as { gamepad: Gamepad | undefined, handedness?: string }, 'left')
|
|
128
128
|
user.add(controller1)
|
|
129
129
|
})
|
|
130
130
|
controller1.add(hand1)
|
|
131
131
|
let hand2: any = controllerModelFactory.createControllerModel(controller2)
|
|
132
132
|
controller2.addEventListener('connected', (event) => {
|
|
133
133
|
hand2.xrInputSource = event.data
|
|
134
|
-
manageXrInputSource(event.data, 'right')
|
|
134
|
+
manageXrInputSource(event.data as { gamepad: Gamepad | undefined, handedness?: string }, 'right')
|
|
135
135
|
user.add(controller2)
|
|
136
136
|
})
|
|
137
137
|
controller2.add(hand2)
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import type { WorldRendererThree } from './worldRendererThree'
|
|
4
4
|
import type { ExportedSection, ExportedWorldGeometry } from '../mesher-shared/exportedGeometryTypes'
|
|
5
|
+
import { calculateSkyLightSimple } from '../lib/skyLight'
|
|
6
|
+
import { bakeLegacyVertexColors } from '../lib/bakeLegacyLight'
|
|
5
7
|
import { getShaderCubeResources } from '../wasm-mesher/bridge/shaderCubeBridge'
|
|
6
|
-
import { createCubeBlockMaterial } from './shaders/cubeBlockShader'
|
|
8
|
+
import { createCubeBlockMaterial, setCubeSkyLevel } from './shaders/cubeBlockShader'
|
|
7
9
|
import { createShaderCubeMesh } from './shaderCubeMesh'
|
|
8
10
|
|
|
9
11
|
export type { ExportedSection, ExportedWorldGeometry } from '../mesher-shared/exportedGeometryTypes'
|
|
@@ -20,35 +22,79 @@ export function exportWorldGeometry(
|
|
|
20
22
|
includeTexture = false
|
|
21
23
|
): ExportedWorldGeometry {
|
|
22
24
|
const sections: ExportedSection[] = []
|
|
25
|
+
const skyLevel = calculateSkyLightSimple(worldRenderer.timeOfTheDay) / 15
|
|
26
|
+
|
|
27
|
+
const globalLegacy = worldRenderer.chunkMeshManager.globalLegacyBuffer
|
|
23
28
|
|
|
24
29
|
for (const [key, sectionObject] of Object.entries(worldRenderer.sectionObjects)) {
|
|
25
|
-
const
|
|
26
|
-
|
|
30
|
+
const positions: number[] = []
|
|
31
|
+
const normals: number[] = []
|
|
32
|
+
const colors: number[] = []
|
|
33
|
+
const skyLights: number[] = []
|
|
34
|
+
const blockLights: number[] = []
|
|
35
|
+
const uvs: number[] = []
|
|
36
|
+
const indices: number[] = []
|
|
27
37
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const globalSlot = globalLegacy?.getSectionGeometryData(key)
|
|
39
|
+
if (globalSlot) {
|
|
40
|
+
positions.push(...globalSlot.positions)
|
|
41
|
+
colors.push(...bakeLegacyVertexColors(globalSlot.colors, globalSlot.skyLights, globalSlot.blockLights, skyLevel))
|
|
42
|
+
skyLights.push(...globalSlot.skyLights)
|
|
43
|
+
blockLights.push(...globalSlot.blockLights)
|
|
44
|
+
uvs.push(...globalSlot.uvs)
|
|
45
|
+
indices.push(...globalSlot.indices)
|
|
46
|
+
}
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
const blendMesh = sectionObject.children.find(child => child.name === 'mesh') as THREE.Mesh | undefined
|
|
49
|
+
if (blendMesh?.geometry) {
|
|
50
|
+
const { geometry } = blendMesh
|
|
51
|
+
const positionAttr = geometry.getAttribute('position') as THREE.BufferAttribute
|
|
52
|
+
const normalAttr = geometry.getAttribute('normal') as THREE.BufferAttribute
|
|
53
|
+
const colorAttr = geometry.getAttribute('color') as THREE.BufferAttribute
|
|
54
|
+
const skyAttr = geometry.getAttribute('a_skyLight') as THREE.BufferAttribute | undefined
|
|
55
|
+
const blockAttr = geometry.getAttribute('a_blockLight') as THREE.BufferAttribute | undefined
|
|
56
|
+
const uvAttr = geometry.getAttribute('uv') as THREE.BufferAttribute
|
|
57
|
+
const indexAttr = geometry.index
|
|
58
|
+
if (positionAttr && indexAttr && colorAttr) {
|
|
59
|
+
const vertOffset = positions.length / 3
|
|
60
|
+
const vertCount = positionAttr.count
|
|
61
|
+
const rawColors = Array.from(colorAttr.array as Float32Array)
|
|
62
|
+
const rawSky = skyAttr
|
|
63
|
+
? Array.from(skyAttr.array as Float32Array)
|
|
64
|
+
: new Array(vertCount).fill(1)
|
|
65
|
+
const rawBlock = blockAttr
|
|
66
|
+
? Array.from(blockAttr.array as Float32Array)
|
|
67
|
+
: new Array(vertCount).fill(0)
|
|
68
|
+
positions.push(...Array.from(positionAttr.array))
|
|
69
|
+
if (normalAttr) normals.push(...Array.from(normalAttr.array))
|
|
70
|
+
colors.push(...bakeLegacyVertexColors(rawColors, rawSky, rawBlock, skyLevel))
|
|
71
|
+
skyLights.push(...rawSky)
|
|
72
|
+
blockLights.push(...rawBlock)
|
|
73
|
+
if (uvAttr) uvs.push(...Array.from(uvAttr.array))
|
|
74
|
+
for (const idx of Array.from(indexAttr.array)) {
|
|
75
|
+
indices.push(idx + vertOffset)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (positions.length === 0 || indices.length === 0) continue
|
|
36
81
|
|
|
37
|
-
const wp = worldRenderer.sceneOrigin.getWorldPosition(mesh)
|
|
38
82
|
sections.push({
|
|
39
83
|
key,
|
|
40
84
|
position: {
|
|
41
|
-
x:
|
|
42
|
-
y:
|
|
43
|
-
z:
|
|
85
|
+
x: sectionObject.worldX ?? 0,
|
|
86
|
+
y: sectionObject.worldY ?? 0,
|
|
87
|
+
z: sectionObject.worldZ ?? 0,
|
|
44
88
|
},
|
|
45
89
|
geometry: {
|
|
46
|
-
positions
|
|
47
|
-
normals
|
|
48
|
-
colors
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
90
|
+
positions,
|
|
91
|
+
normals,
|
|
92
|
+
colors,
|
|
93
|
+
skyLights,
|
|
94
|
+
blockLights,
|
|
95
|
+
uvs,
|
|
96
|
+
indices,
|
|
97
|
+
},
|
|
52
98
|
})
|
|
53
99
|
}
|
|
54
100
|
|
|
@@ -65,7 +111,7 @@ export function exportWorldGeometry(
|
|
|
65
111
|
// Optionally include texture atlas as data URL
|
|
66
112
|
if (includeTexture && worldRenderer.material.map) {
|
|
67
113
|
const canvas = document.createElement('canvas')
|
|
68
|
-
const texture = worldRenderer.material.map
|
|
114
|
+
const texture = worldRenderer.material.map as THREE.Texture<HTMLImageElement | ImageBitmap>
|
|
69
115
|
const { image } = texture
|
|
70
116
|
if (image) {
|
|
71
117
|
canvas.width = image.width
|
|
@@ -117,12 +163,13 @@ export async function loadWorldGeometryFromUrl(url: string): Promise<ExportedWor
|
|
|
117
163
|
* Recreate THREE.js meshes from exported geometry
|
|
118
164
|
* Returns an array of mesh groups that can be added to a scene
|
|
119
165
|
*/
|
|
120
|
-
function shaderMaterialForExport(legacyMaterial: THREE.Material): THREE.ShaderMaterial | null {
|
|
166
|
+
function shaderMaterialForExport(legacyMaterial: THREE.Material, skyLevel: number): THREE.ShaderMaterial | null {
|
|
121
167
|
const atlas = (legacyMaterial as THREE.MeshBasicMaterial).map
|
|
122
168
|
?? (legacyMaterial as THREE.MeshLambertMaterial).map
|
|
123
169
|
if (!atlas) return null
|
|
124
170
|
const shaderMat = createCubeBlockMaterial()
|
|
125
171
|
shaderMat.uniforms.u_atlas.value = atlas
|
|
172
|
+
setCubeSkyLevel(shaderMat, skyLevel)
|
|
126
173
|
const resources = getShaderCubeResources()
|
|
127
174
|
if (!resources) return null
|
|
128
175
|
const { tintPalette } = resources
|
|
@@ -135,9 +182,10 @@ export function createMeshesFromExport(
|
|
|
135
182
|
exportData: ExportedWorldGeometry,
|
|
136
183
|
material: THREE.Material,
|
|
137
184
|
shaderMaterial?: THREE.ShaderMaterial | null,
|
|
185
|
+
skyLevel = 1,
|
|
138
186
|
): THREE.Group[] {
|
|
139
187
|
const groups: THREE.Group[] = []
|
|
140
|
-
const resolvedShaderMat = shaderMaterial ?? shaderMaterialForExport(material)
|
|
188
|
+
const resolvedShaderMat = shaderMaterial ?? shaderMaterialForExport(material, skyLevel)
|
|
141
189
|
|
|
142
190
|
for (const section of exportData.sections) {
|
|
143
191
|
const group = new THREE.Group()
|
|
@@ -151,7 +199,15 @@ export function createMeshesFromExport(
|
|
|
151
199
|
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(section.geometry.normals, 3))
|
|
152
200
|
}
|
|
153
201
|
if (section.geometry.colors.length) {
|
|
154
|
-
|
|
202
|
+
const baked = section.geometry.skyLights?.length
|
|
203
|
+
? bakeLegacyVertexColors(
|
|
204
|
+
section.geometry.colors,
|
|
205
|
+
section.geometry.skyLights,
|
|
206
|
+
section.geometry.blockLights,
|
|
207
|
+
skyLevel,
|
|
208
|
+
)
|
|
209
|
+
: section.geometry.colors
|
|
210
|
+
geometry.setAttribute('color', new THREE.Float32BufferAttribute(baked, 3))
|
|
155
211
|
}
|
|
156
212
|
if (section.geometry.uvs.length) {
|
|
157
213
|
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(section.geometry.uvs, 2))
|
|
@@ -249,10 +305,11 @@ export async function applyWorldGeometryExport(
|
|
|
249
305
|
material = rendererMaterial
|
|
250
306
|
}
|
|
251
307
|
|
|
308
|
+
const skyLevel = calculateSkyLightSimple(worldRenderer.timeOfTheDay) / 15
|
|
252
309
|
const shaderMat = exportData.sections.some(s => (s.shaderCubes?.count ?? 0) > 0)
|
|
253
|
-
? shaderMaterialForExport(material)
|
|
310
|
+
? shaderMaterialForExport(material, skyLevel)
|
|
254
311
|
: null
|
|
255
|
-
const groups = createMeshesFromExport(exportData, material, shaderMat)
|
|
312
|
+
const groups = createMeshesFromExport(exportData, material, shaderMat, skyLevel)
|
|
256
313
|
const container = new THREE.Group()
|
|
257
314
|
container.name = GEOMETRY_EXPORT_GROUP_NAME
|
|
258
315
|
if (hasEmbeddedTexture) {
|