minecraft-renderer 0.1.27 → 0.1.29
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 +22 -22
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +17 -17
- package/dist/minecraft-renderer.js +65 -619
- package/dist/minecraft-renderer.js.meta.json +1 -0
- package/dist/threeWorker.js +453 -1007
- package/package.json +1 -1
- package/src/lib/guiRenderer.ts +0 -1
- package/src/lib/moreBlockDataGenerated.json +8 -0
- package/src/lib/skyLight.ts +125 -0
- package/src/lib/worldrendererCommon.ts +2 -12
- package/src/mesher/models.ts +28 -2
- package/src/mesher/test/mesherTester.ts +1 -1
- package/src/sign-renderer/index.ts +4 -1
- package/src/three/bannerRenderer.ts +5 -4
- package/src/three/cinimaticScript.ts +2 -2
- package/src/three/entities.ts +42 -11
- package/src/three/entity/EntityMesh.ts +1 -1
- package/src/three/entity/entities.json +108 -2
- package/src/three/entity/exportedModels.js +0 -1
- package/src/three/entity/externalTextures.json +1 -1
- package/src/three/fireworks.ts +18 -5
- package/src/three/fireworksRenderer.ts +15 -13
- package/src/three/modules/rain.ts +6 -1
- package/src/three/modules/sciFiWorldReveal.ts +14 -10
- package/src/three/modules/starfield.ts +1 -1
- package/src/three/sceneOrigin.ts +215 -0
- package/src/three/skyboxRenderer.ts +3 -3
- package/src/three/threeJsMedia.ts +12 -6
- package/src/three/threeJsParticles.ts +42 -14
- package/src/three/threeJsSound.ts +3 -3
- package/src/three/waypointSprite.ts +45 -23
- package/src/three/waypoints.ts +12 -4
- package/src/three/world/cursorBlock.ts +5 -5
- package/src/three/worldBlockGeometry.ts +14 -5
- package/src/three/worldGeometryExport.ts +4 -3
- package/src/three/worldRendererThree.ts +155 -30
|
@@ -58,7 +58,7 @@ export class CursorBlock {
|
|
|
58
58
|
this.blockBreakMesh.visible = false
|
|
59
59
|
this.blockBreakMesh.renderOrder = 999
|
|
60
60
|
this.blockBreakMesh.name = 'blockBreakMesh'
|
|
61
|
-
this.worldRenderer.
|
|
61
|
+
this.worldRenderer.sceneOrigin.addAndTrack(this.blockBreakMesh)
|
|
62
62
|
|
|
63
63
|
this.worldRenderer.onReactivePlayerStateUpdated('gameMode', () => {
|
|
64
64
|
this.updateLineMaterial()
|
|
@@ -132,7 +132,7 @@ export class CursorBlock {
|
|
|
132
132
|
return
|
|
133
133
|
}
|
|
134
134
|
if (this.interactionLines !== null) {
|
|
135
|
-
this.worldRenderer.
|
|
135
|
+
this.worldRenderer.sceneOrigin.removeAndUntrack(this.interactionLines.mesh)
|
|
136
136
|
this.interactionLines = null
|
|
137
137
|
}
|
|
138
138
|
if (blockPos === null) {
|
|
@@ -146,12 +146,12 @@ export class CursorBlock {
|
|
|
146
146
|
const geometry = new THREE.BoxGeometry(...scale)
|
|
147
147
|
const lines = new LineSegmentsGeometry().fromEdgesGeometry(new THREE.EdgesGeometry(geometry))
|
|
148
148
|
const wireframe = new Wireframe(lines, this.cursorLineMaterial)
|
|
149
|
-
|
|
150
|
-
wireframe.position.set(pos.x, pos.y, pos.z)
|
|
149
|
+
wireframe.position.set(position.x, position.y, position.z)
|
|
151
150
|
wireframe.computeLineDistances()
|
|
152
151
|
group.add(wireframe)
|
|
153
152
|
}
|
|
154
|
-
this.worldRenderer.
|
|
153
|
+
this.worldRenderer.sceneOrigin.addAndTrack(group)
|
|
154
|
+
group.position.set(blockPos.x, blockPos.y, blockPos.z)
|
|
155
155
|
group.visible = !this.cursorLinesHidden
|
|
156
156
|
this.interactionLines = { blockPos, mesh: group, shapePositions }
|
|
157
157
|
}
|
|
@@ -36,7 +36,7 @@ export class WorldBlockGeometry {
|
|
|
36
36
|
releaseBannerTexture((child as any).bannerTexture)
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
|
-
this.
|
|
39
|
+
this.worldRenderer.sceneOrigin.removeAndUntrackAll(object)
|
|
40
40
|
disposeObject(object)
|
|
41
41
|
delete this.sectionObjects[data.key]
|
|
42
42
|
}
|
|
@@ -66,6 +66,7 @@ export class WorldBlockGeometry {
|
|
|
66
66
|
this.addSectionMemoryUsage(geometry)
|
|
67
67
|
|
|
68
68
|
const mesh = new THREE.Mesh(geometry, this.material)
|
|
69
|
+
this.worldRenderer.sceneOrigin.track(mesh, { updateMatrix: true })
|
|
69
70
|
mesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
|
|
70
71
|
mesh.name = 'mesh'
|
|
71
72
|
object = new THREE.Group()
|
|
@@ -77,9 +78,10 @@ export class WorldBlockGeometry {
|
|
|
77
78
|
new THREE.BoxGeometry(CHUNK_SIZE, sectionHeight, CHUNK_SIZE),
|
|
78
79
|
new THREE.MeshBasicMaterial({ color: 0x00_00_00, transparent: true, opacity: 0 })
|
|
79
80
|
)
|
|
80
|
-
staticChunkMesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
|
|
81
81
|
const boxHelper = new THREE.BoxHelper(staticChunkMesh, 0xff_ff_00)
|
|
82
82
|
boxHelper.name = 'helper'
|
|
83
|
+
this.worldRenderer.sceneOrigin.track(boxHelper, { updateMatrix: true })
|
|
84
|
+
boxHelper.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
|
|
83
85
|
object.add(boxHelper)
|
|
84
86
|
object.name = 'chunk'
|
|
85
87
|
; (object as any).tilesCount = data.geometry.positions.length / 3 / 4
|
|
@@ -127,10 +129,15 @@ export class WorldBlockGeometry {
|
|
|
127
129
|
const bannerTexture = getBannerTexture(this.worldRenderer, blockName, nbt.simplify(bannerBlockEntity))
|
|
128
130
|
if (!bannerTexture) continue
|
|
129
131
|
const banner = createBannerMesh(new Vec3(+x, +y, +z), rotation, isWall, bannerTexture)
|
|
132
|
+
const { x: bwx, y: bwy, z: bwz } = banner.position
|
|
133
|
+
this.worldRenderer.sceneOrigin.track(banner)
|
|
134
|
+
banner.position.set(bwx, bwy, bwz)
|
|
130
135
|
object.add(banner)
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
this.sectionObjects[data.key] = object
|
|
139
|
+
// Store section key on object for easier lookup
|
|
140
|
+
;(object as any).sectionKey = data.key
|
|
134
141
|
if (this.displayOptions.inWorldRenderingConfig._renderByChunks) {
|
|
135
142
|
object.visible = false
|
|
136
143
|
const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
|
|
@@ -144,6 +151,8 @@ export class WorldBlockGeometry {
|
|
|
144
151
|
|
|
145
152
|
this.worldRenderer.updatePosDataChunk(data.key)
|
|
146
153
|
object.matrixAutoUpdate = false
|
|
154
|
+
// Force matrix update after setting camera-relative position (matrixAutoUpdate is false)
|
|
155
|
+
object.updateMatrix()
|
|
147
156
|
mesh.onAfterRender = (renderer, scene, camera, geometry, material, group) => {
|
|
148
157
|
// mesh.matrixAutoUpdate = false
|
|
149
158
|
}
|
|
@@ -222,7 +231,7 @@ export class WorldBlockGeometry {
|
|
|
222
231
|
for (const mesh of Object.values(this.sectionObjects)) {
|
|
223
232
|
// Track memory usage removal for all sections
|
|
224
233
|
this.removeSectionMemoryUsage(mesh)
|
|
225
|
-
this.
|
|
234
|
+
this.worldRenderer.sceneOrigin.removeAndUntrackAll(mesh)
|
|
226
235
|
}
|
|
227
236
|
this.sectionObjects = {}
|
|
228
237
|
this.waitingChunksToDisplay = {}
|
|
@@ -249,7 +258,7 @@ export class WorldBlockGeometry {
|
|
|
249
258
|
releaseBannerTexture((child as any).bannerTexture)
|
|
250
259
|
}
|
|
251
260
|
})
|
|
252
|
-
this.
|
|
261
|
+
this.worldRenderer.sceneOrigin.removeAndUntrackAll(mesh)
|
|
253
262
|
disposeObject(mesh)
|
|
254
263
|
}
|
|
255
264
|
delete this.sectionObjects[key]
|
|
@@ -267,7 +276,7 @@ export class WorldBlockGeometry {
|
|
|
267
276
|
releaseBannerTexture((child as any).bannerTexture)
|
|
268
277
|
}
|
|
269
278
|
})
|
|
270
|
-
this.
|
|
279
|
+
this.worldRenderer.sceneOrigin.removeAndUntrackAll(mesh)
|
|
271
280
|
disposeObject(mesh)
|
|
272
281
|
}
|
|
273
282
|
delete this.sectionObjects[key]
|
|
@@ -52,12 +52,13 @@ export function exportWorldGeometry(
|
|
|
52
52
|
|
|
53
53
|
if (!positionAttr || !indexAttr) continue
|
|
54
54
|
|
|
55
|
+
const wp = worldRenderer.sceneOrigin.getWorldPosition(mesh)
|
|
55
56
|
sections.push({
|
|
56
57
|
key,
|
|
57
58
|
position: {
|
|
58
|
-
x: mesh.position.x,
|
|
59
|
-
y: mesh.position.y,
|
|
60
|
-
z: mesh.position.z
|
|
59
|
+
x: wp?.x ?? worldRenderer.sceneOrigin.toWorldX(mesh.position.x),
|
|
60
|
+
y: wp?.y ?? worldRenderer.sceneOrigin.toWorldY(mesh.position.y),
|
|
61
|
+
z: wp?.z ?? worldRenderer.sceneOrigin.toWorldZ(mesh.position.z)
|
|
61
62
|
},
|
|
62
63
|
geometry: {
|
|
63
64
|
positions: [...positionAttr.array],
|
|
@@ -31,6 +31,7 @@ import { FireworksRenderer } from './fireworksRenderer'
|
|
|
31
31
|
import { CinimaticScriptRunner, CinimaticScript } from './cinimaticScript'
|
|
32
32
|
import { DEFAULT_TEMPERATURE, SkyboxRenderer } from './skyboxRenderer'
|
|
33
33
|
import { FireworksManager } from './fireworks'
|
|
34
|
+
import { SceneOrigin } from './sceneOrigin'
|
|
34
35
|
import { downloadWorldGeometry } from './worldGeometryExport'
|
|
35
36
|
import { WorldBlockGeometry } from './worldBlockGeometry'
|
|
36
37
|
import type { RendererModuleManifest, RegisteredModule, RendererModuleController } from './rendererModuleSystem'
|
|
@@ -67,6 +68,14 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
67
68
|
}
|
|
68
69
|
waypoints: WaypointsRenderer
|
|
69
70
|
cinimaticScript: CinimaticScriptRunner
|
|
71
|
+
/**
|
|
72
|
+
* Three.js camera used for rendering.
|
|
73
|
+
*
|
|
74
|
+
* **WARNING:** `camera.position` is scene-local (near origin due to sceneOrigin rebasing),
|
|
75
|
+
* NOT world-space. In first-person mode it's `(0,0,0)`; in third-person it's `(0,0,zOffset)`.
|
|
76
|
+
*
|
|
77
|
+
* Use `getCameraPosition()` or `cameraWorldPos` for actual world-space coordinates.
|
|
78
|
+
*/
|
|
70
79
|
camera!: THREE.PerspectiveCamera
|
|
71
80
|
renderTimeAvg = 0
|
|
72
81
|
// Memory usage tracking (in bytes)
|
|
@@ -97,10 +106,29 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
97
106
|
DEBUG_RAYCAST = false
|
|
98
107
|
skyboxRenderer: SkyboxRenderer
|
|
99
108
|
fireworks: FireworksManager
|
|
109
|
+
sceneOrigin = new SceneOrigin(this.scene)
|
|
110
|
+
/** Camera world position stored in float64 (JS number) for precision */
|
|
111
|
+
cameraWorldPos = { x: 0, y: 0, z: 0 }
|
|
112
|
+
|
|
113
|
+
/** Whether we've warned about camera.position access (one-time dev warning) */
|
|
114
|
+
private _cameraPositionAccessWarned = false
|
|
115
|
+
|
|
116
|
+
private readonly _tmpCameraPos = new THREE.Vector3()
|
|
100
117
|
|
|
101
|
-
private currentPosTween?: tweenJs.Tween<
|
|
118
|
+
private currentPosTween?: tweenJs.Tween<{ x: number, y: number, z: number }>
|
|
102
119
|
private currentRotTween?: tweenJs.Tween<{ pitch: number, yaw: number }>
|
|
103
120
|
|
|
121
|
+
// Pre-allocated objects for getThirdPersonCamera (avoid per-frame allocs)
|
|
122
|
+
private readonly _tpDirection = new THREE.Vector3()
|
|
123
|
+
private readonly _tpPitchQuat = new THREE.Quaternion()
|
|
124
|
+
private readonly _tpYawQuat = new THREE.Quaternion()
|
|
125
|
+
private readonly _tpFinalQuat = new THREE.Quaternion()
|
|
126
|
+
private readonly _tpScenePos = new THREE.Vector3()
|
|
127
|
+
private readonly _tpAxisX = new THREE.Vector3(1, 0, 0)
|
|
128
|
+
private readonly _tpAxisY = new THREE.Vector3(0, 1, 0)
|
|
129
|
+
private readonly _tpRaycaster = new THREE.Raycaster()
|
|
130
|
+
private readonly _tpChunkWorldPos = new THREE.Vector3()
|
|
131
|
+
|
|
104
132
|
get tilesRendered() {
|
|
105
133
|
return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0)
|
|
106
134
|
}
|
|
@@ -146,13 +174,13 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
146
174
|
(pos, yaw, pitch) => this.setCinimaticCamera(pos, yaw, pitch),
|
|
147
175
|
(fov) => this.setCinimaticFov(fov),
|
|
148
176
|
() => ({
|
|
149
|
-
position: new Vec3(this.
|
|
177
|
+
position: new Vec3(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z),
|
|
150
178
|
yaw: this.cameraShake.getBaseRotation().yaw,
|
|
151
179
|
pitch: this.cameraShake.getBaseRotation().pitch,
|
|
152
180
|
fov: this.camera.fov
|
|
153
181
|
})
|
|
154
182
|
)
|
|
155
|
-
this.fireworks = new FireworksManager(this.scene)
|
|
183
|
+
this.fireworks = new FireworksManager(this.scene, this.sceneOrigin)
|
|
156
184
|
|
|
157
185
|
// this.fountain = new Fountain(this.scene, this.scene, {
|
|
158
186
|
// position: new THREE.Vector3(0, 10, 0),
|
|
@@ -313,10 +341,65 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
313
341
|
return Object.values(this.modules).some(m => m.enabled && m.manifest.requiresHeightmap)
|
|
314
342
|
}
|
|
315
343
|
|
|
344
|
+
/** Returns the active camera container (may differ in VR mode). Used for position resets and rotation. */
|
|
316
345
|
get cameraObject() {
|
|
317
346
|
return this.cameraGroupVr ?? this.cameraContainer
|
|
318
347
|
}
|
|
319
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Wraps camera.position in a Proxy that logs a one-time warning when .set/.setX/.setY/.setZ
|
|
351
|
+
* or .x/.y/.z assignment is used with values that look like world coords (|v| > 20).
|
|
352
|
+
* camera.position is scene-local (0,0,0 or 0,0,zOffset). Use cameraWorldPos + sceneOrigin.update().
|
|
353
|
+
*/
|
|
354
|
+
private _wrapCameraPositionWithWarning() {
|
|
355
|
+
const realPos = this.camera.position
|
|
356
|
+
const self = this
|
|
357
|
+
const WORLD_COORD_THRESHOLD = 20 // our zOffset is ~4, so 20 catches mistaken world coords
|
|
358
|
+
const looksLikeWorldCoords = (x: number, y: number, z: number) =>
|
|
359
|
+
Math.abs(x) > WORLD_COORD_THRESHOLD || Math.abs(y) > WORLD_COORD_THRESHOLD || Math.abs(z) > WORLD_COORD_THRESHOLD
|
|
360
|
+
const warnOnce = () => {
|
|
361
|
+
if (!self._cameraPositionAccessWarned && typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
362
|
+
self._cameraPositionAccessWarned = true
|
|
363
|
+
console.warn(
|
|
364
|
+
'[WorldRendererThree] Do not set camera.position to world coordinates — it is scene-local. ' +
|
|
365
|
+
'Use cameraWorldPos and sceneOrigin.update() to move the camera.'
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const proxy = new Proxy(realPos, {
|
|
370
|
+
set(target, prop, value) {
|
|
371
|
+
if ((prop === 'x' || prop === 'y' || prop === 'z') && typeof value === 'number' && Math.abs(value) > WORLD_COORD_THRESHOLD) {
|
|
372
|
+
warnOnce()
|
|
373
|
+
}
|
|
374
|
+
;(target as any)[prop] = value
|
|
375
|
+
return true
|
|
376
|
+
},
|
|
377
|
+
get(target, prop, receiver) {
|
|
378
|
+
const value = (target as any)[prop]
|
|
379
|
+
if (prop === 'set') {
|
|
380
|
+
return function (x: number, y: number, z: number) {
|
|
381
|
+
if (looksLikeWorldCoords(x, y, z)) warnOnce()
|
|
382
|
+
return (target as THREE.Vector3).set(x, y, z)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (prop === 'setX' || prop === 'setY' || prop === 'setZ') {
|
|
386
|
+
return function (v: number) {
|
|
387
|
+
if (Math.abs(v) > WORLD_COORD_THRESHOLD) warnOnce()
|
|
388
|
+
return (target as any)[prop](v)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (prop === 'copy') {
|
|
392
|
+
return function (v: THREE.Vector3) {
|
|
393
|
+
if (looksLikeWorldCoords(v.x, v.y, v.z)) warnOnce()
|
|
394
|
+
return (target as THREE.Vector3).copy(v)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return typeof value === 'function' ? value.bind(target) : value
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
Object.defineProperty(this.camera, 'position', { value: proxy, configurable: true, enumerable: true })
|
|
401
|
+
}
|
|
402
|
+
|
|
320
403
|
worldSwitchActions() {
|
|
321
404
|
this.onWorldSwitched.push(() => {
|
|
322
405
|
// clear custom blocks
|
|
@@ -333,7 +416,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
333
416
|
}
|
|
334
417
|
|
|
335
418
|
downloadWorldGeometry() {
|
|
336
|
-
downloadWorldGeometry(this, this.
|
|
419
|
+
downloadWorldGeometry(this, new THREE.Vector3(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z), this.cameraShake.getBaseRotation(), 'world-geometry.json')
|
|
337
420
|
}
|
|
338
421
|
|
|
339
422
|
updateEntity(e, isPosUpdate = false) {
|
|
@@ -358,6 +441,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
358
441
|
}
|
|
359
442
|
|
|
360
443
|
resetScene() {
|
|
444
|
+
this.sceneOrigin.update(0, 0, 0)
|
|
445
|
+
this.cameraWorldPos.x = 0
|
|
446
|
+
this.cameraWorldPos.y = 0
|
|
447
|
+
this.cameraWorldPos.z = 0
|
|
448
|
+
|
|
361
449
|
this.scene.matrixAutoUpdate = false // for perf
|
|
362
450
|
this.scene.background = new THREE.Color(this.initOptions.config.sceneBackground)
|
|
363
451
|
this.scene.add(this.ambientLight)
|
|
@@ -367,6 +455,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
367
455
|
|
|
368
456
|
const size = this.renderer.getSize(new THREE.Vector2())
|
|
369
457
|
this.camera = new THREE.PerspectiveCamera(75, size.x / size.y, 0.1, 1000)
|
|
458
|
+
this._wrapCameraPositionWithWarning()
|
|
370
459
|
this.cameraContainer = new THREE.Object3D()
|
|
371
460
|
this.cameraContainer.add(this.camera)
|
|
372
461
|
this.scene.add(this.cameraContainer)
|
|
@@ -396,7 +485,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
396
485
|
})
|
|
397
486
|
this.onReactivePlayerStateUpdated('perspective', (value) => {
|
|
398
487
|
// Update camera perspective when it changes
|
|
399
|
-
const vecPos = new Vec3(this.
|
|
488
|
+
const vecPos = new Vec3(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
400
489
|
this.updateCamera(vecPos, this.cameraShake.getBaseRotation().yaw, this.cameraShake.getBaseRotation().pitch)
|
|
401
490
|
// todo also update camera when block within camera was changed
|
|
402
491
|
})
|
|
@@ -654,10 +743,8 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
654
743
|
return tex
|
|
655
744
|
}
|
|
656
745
|
|
|
657
|
-
getCameraPosition() {
|
|
658
|
-
|
|
659
|
-
this.camera.getWorldPosition(worldPos)
|
|
660
|
-
return worldPos
|
|
746
|
+
getCameraPosition(target?: THREE.Vector3): THREE.Vector3 {
|
|
747
|
+
return (target ?? this._tmpCameraPos).set(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
661
748
|
}
|
|
662
749
|
|
|
663
750
|
getSectionCameraPosition() {
|
|
@@ -686,7 +773,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
686
773
|
}
|
|
687
774
|
|
|
688
775
|
getThirdPersonCamera(pos: THREE.Vector3 | null, yaw: number, pitch: number) {
|
|
689
|
-
pos ??= this.
|
|
776
|
+
pos ??= new THREE.Vector3(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
690
777
|
|
|
691
778
|
// Calculate camera offset based on perspective
|
|
692
779
|
const isBack = this.playerStateReactive.perspective === 'third_person_back'
|
|
@@ -697,12 +784,12 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
697
784
|
|
|
698
785
|
// Create a direction vector that represents where the camera is looking
|
|
699
786
|
// This matches the Three.js camera coordinate system
|
|
700
|
-
const direction =
|
|
787
|
+
const direction = this._tpDirection.set(0, 0, -1) // Forward direction in camera space
|
|
701
788
|
|
|
702
789
|
// Apply the same rotation that's applied to the camera container
|
|
703
|
-
const pitchQuat =
|
|
704
|
-
const yawQuat =
|
|
705
|
-
const finalQuat =
|
|
790
|
+
const pitchQuat = this._tpPitchQuat.setFromAxisAngle(this._tpAxisX, pitch)
|
|
791
|
+
const yawQuat = this._tpYawQuat.setFromAxisAngle(this._tpAxisY, yaw)
|
|
792
|
+
const finalQuat = this._tpFinalQuat.multiplyQuaternions(yawQuat, pitchQuat)
|
|
706
793
|
|
|
707
794
|
// Transform the direction vector by the camera's rotation
|
|
708
795
|
direction.applyQuaternion(finalQuat)
|
|
@@ -718,9 +805,16 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
718
805
|
this.debugRaycast(pos, direction, distance)
|
|
719
806
|
}
|
|
720
807
|
|
|
808
|
+
// Convert world position to scene-relative coordinates for raycasting
|
|
809
|
+
const scenePos = this._tpScenePos.set(
|
|
810
|
+
this.sceneOrigin.toSceneX(pos.x),
|
|
811
|
+
this.sceneOrigin.toSceneY(pos.y),
|
|
812
|
+
this.sceneOrigin.toSceneZ(pos.z)
|
|
813
|
+
)
|
|
814
|
+
|
|
721
815
|
// Perform raycast to avoid camera going through blocks
|
|
722
|
-
const raycaster =
|
|
723
|
-
raycaster.set(
|
|
816
|
+
const raycaster = this._tpRaycaster
|
|
817
|
+
raycaster.set(scenePos, direction)
|
|
724
818
|
raycaster.far = distance // Limit raycast distance
|
|
725
819
|
|
|
726
820
|
// Filter to only nearby chunks for performance
|
|
@@ -732,9 +826,9 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
732
826
|
if (!mesh) return false
|
|
733
827
|
|
|
734
828
|
// Check distance from player position to chunk
|
|
735
|
-
const chunkWorldPos =
|
|
829
|
+
const chunkWorldPos = this._tpChunkWorldPos
|
|
736
830
|
mesh.getWorldPosition(chunkWorldPos)
|
|
737
|
-
const distance =
|
|
831
|
+
const distance = scenePos.distanceTo(chunkWorldPos)
|
|
738
832
|
return distance < 80 // Only check chunks within 80 blocks
|
|
739
833
|
})
|
|
740
834
|
|
|
@@ -776,10 +870,17 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
776
870
|
this.debugHitPoint = undefined
|
|
777
871
|
}
|
|
778
872
|
|
|
873
|
+
// Convert world position to scene-relative coordinates
|
|
874
|
+
const scenePos = new THREE.Vector3(
|
|
875
|
+
this.sceneOrigin.toSceneX(pos.x),
|
|
876
|
+
this.sceneOrigin.toSceneY(pos.y),
|
|
877
|
+
this.sceneOrigin.toSceneZ(pos.z)
|
|
878
|
+
)
|
|
879
|
+
|
|
779
880
|
// Create raycast arrow
|
|
780
881
|
this.debugRaycastHelper = new THREE.ArrowHelper(
|
|
781
882
|
direction.clone().normalize(),
|
|
782
|
-
|
|
883
|
+
scenePos,
|
|
783
884
|
distance,
|
|
784
885
|
0xff_00_00, // Red color
|
|
785
886
|
distance * 0.1,
|
|
@@ -791,7 +892,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
791
892
|
const hitGeometry = new THREE.SphereGeometry(0.2, 8, 8)
|
|
792
893
|
const hitMaterial = new THREE.MeshBasicMaterial({ color: 0x00_ff_00 })
|
|
793
894
|
this.debugHitPoint = new THREE.Mesh(hitGeometry, hitMaterial)
|
|
794
|
-
this.debugHitPoint.position.copy(
|
|
895
|
+
this.debugHitPoint.position.copy(scenePos).add(direction.clone().multiplyScalar(distance))
|
|
795
896
|
this.scene.add(this.debugHitPoint)
|
|
796
897
|
}
|
|
797
898
|
|
|
@@ -799,7 +900,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
799
900
|
|
|
800
901
|
setCinimaticCamera(pos: Vec3, yaw: number, pitch: number): void {
|
|
801
902
|
// Directly set camera position and rotation for cinematic mode
|
|
802
|
-
this.
|
|
903
|
+
this.cameraWorldPos.x = pos.x
|
|
904
|
+
this.cameraWorldPos.y = pos.y
|
|
905
|
+
this.cameraWorldPos.z = pos.z
|
|
906
|
+
this.sceneOrigin.update(pos.x, pos.y, pos.z)
|
|
907
|
+
this.cameraObject.position.set(0, 0, 0)
|
|
803
908
|
this.cameraShake.setBaseRotation(pitch, yaw)
|
|
804
909
|
this.updateCameraSectionPos()
|
|
805
910
|
}
|
|
@@ -831,7 +936,13 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
831
936
|
const tweenDelay = this.displayOptions.inWorldRenderingConfig.instantCameraUpdate
|
|
832
937
|
? 0
|
|
833
938
|
: (this.playerStateUtils.isSpectatingEntity() ? 150 : 50)
|
|
834
|
-
this.currentPosTween = new tweenJs.Tween(this.
|
|
939
|
+
this.currentPosTween = new tweenJs.Tween(this.cameraWorldPos)
|
|
940
|
+
.to({ x: pos.x, y: pos.y, z: pos.z }, tweenDelay)
|
|
941
|
+
.onUpdate(() => {
|
|
942
|
+
this.sceneOrigin.update(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
943
|
+
this.cameraObject.position.set(0, 0, 0)
|
|
944
|
+
})
|
|
945
|
+
.start()
|
|
835
946
|
// this.freeFlyState.position = pos
|
|
836
947
|
}
|
|
837
948
|
|
|
@@ -855,14 +966,14 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
855
966
|
const { perspective } = this.playerStateReactive
|
|
856
967
|
if (perspective === 'third_person_back' || perspective === 'third_person_front') {
|
|
857
968
|
// Use getThirdPersonCamera for proper raycasting with max distance of 4
|
|
858
|
-
const
|
|
969
|
+
const currentWorldPos = new THREE.Vector3(this.cameraWorldPos.x, this.cameraWorldPos.y, this.cameraWorldPos.z)
|
|
859
970
|
const thirdPersonPos = this.getThirdPersonCamera(
|
|
860
|
-
|
|
971
|
+
currentWorldPos,
|
|
861
972
|
yaw,
|
|
862
973
|
pitch
|
|
863
974
|
)
|
|
864
975
|
|
|
865
|
-
const distance =
|
|
976
|
+
const distance = currentWorldPos.distanceTo(new THREE.Vector3(thirdPersonPos.x, thirdPersonPos.y, thirdPersonPos.z))
|
|
866
977
|
// Apply Z offset based on perspective and calculated distance
|
|
867
978
|
const zOffset = perspective === 'third_person_back' ? distance : -distance
|
|
868
979
|
this.camera.position.set(0, 0, zOffset)
|
|
@@ -1014,6 +1125,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1014
1125
|
// move head model down as armor have a different offset than blocks
|
|
1015
1126
|
mesh.position.y -= 23 / 16
|
|
1016
1127
|
group.add(mesh)
|
|
1128
|
+
this.sceneOrigin.track(group)
|
|
1017
1129
|
group.position.set(position.x + 0.5, position.y + 0.045, position.z + 0.5)
|
|
1018
1130
|
group.rotation.set(
|
|
1019
1131
|
0,
|
|
@@ -1065,6 +1177,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1065
1177
|
const height = (isHanging ? 10 : 8) / 16
|
|
1066
1178
|
const heightOffset = (isHanging ? 0 : isWall ? 4.333 : 9.333) / 16
|
|
1067
1179
|
const textPosition = height / 2 + heightOffset
|
|
1180
|
+
this.sceneOrigin.track(group)
|
|
1068
1181
|
group.position.set(position.x + 0.5, position.y + textPosition, position.z + 0.5)
|
|
1069
1182
|
return group
|
|
1070
1183
|
}
|
|
@@ -1180,12 +1293,24 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1180
1293
|
}
|
|
1181
1294
|
|
|
1182
1295
|
shouldObjectVisible(object: THREE.Object3D) {
|
|
1183
|
-
// Get chunk coordinates
|
|
1296
|
+
// Get chunk coordinates - use world coords from userData if available, otherwise convert from scene coords
|
|
1184
1297
|
const CHUNK_SIZE = 16
|
|
1185
1298
|
const sectionHeight = this.getSectionHeight()
|
|
1186
|
-
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1299
|
+
let worldX: number, worldY: number, worldZ: number
|
|
1300
|
+
const wp = this.sceneOrigin.getWorldPosition(object)
|
|
1301
|
+
if (wp) {
|
|
1302
|
+
worldX = wp.x
|
|
1303
|
+
worldY = wp.y
|
|
1304
|
+
worldZ = wp.z
|
|
1305
|
+
} else {
|
|
1306
|
+
// Fallback for untracked objects: convert scene coords back to world
|
|
1307
|
+
worldX = this.sceneOrigin.toWorldX(object.position.x)
|
|
1308
|
+
worldY = this.sceneOrigin.toWorldY(object.position.y)
|
|
1309
|
+
worldZ = this.sceneOrigin.toWorldZ(object.position.z)
|
|
1310
|
+
}
|
|
1311
|
+
const chunkX = Math.floor(worldX / CHUNK_SIZE) * CHUNK_SIZE
|
|
1312
|
+
const chunkZ = Math.floor(worldZ / CHUNK_SIZE) * CHUNK_SIZE
|
|
1313
|
+
const sectionY = Math.floor(worldY / sectionHeight) * sectionHeight
|
|
1189
1314
|
|
|
1190
1315
|
const chunkKey = `${chunkX},${chunkZ}`
|
|
1191
1316
|
const sectionKey = `${chunkX},${sectionY},${chunkZ}`
|
|
@@ -1231,7 +1356,7 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1231
1356
|
}
|
|
1232
1357
|
}
|
|
1233
1358
|
|
|
1234
|
-
// Apply the offset to the section object
|
|
1359
|
+
// Apply the offset to the section object (compose with camera-relative base position)
|
|
1235
1360
|
const section = this.sectionObjects[key]
|
|
1236
1361
|
if (section) {
|
|
1237
1362
|
section.position.set(
|