minecraft-renderer 0.1.35 → 0.1.37
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 +64 -64
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +1 -1
- package/dist/minecraft-renderer.js +52 -52
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +393 -393
- package/package.json +1 -1
- package/src/graphicsBackend/config.ts +4 -0
- package/src/graphicsBackend/playerState.ts +1 -0
- package/src/lib/worldrendererCommon.ts +6 -0
- package/src/mesher/models.ts +15 -6
- package/src/mesher/shared.ts +3 -1
- package/src/playerState/playerState.ts +2 -0
- package/src/three/chunkMeshManager.ts +1 -1
- package/src/three/holdingBlock.ts +39 -35
- package/src/three/holdingBlockItemIdentity.test.ts +43 -0
- package/src/three/holdingBlockItemIdentity.ts +30 -0
- package/src/three/holdingBlockLegacy.ts +22 -25
- package/src/three/modules/sciFiWorldReveal.ts +2 -2
- package/src/three/sceneOrigin.ts +2 -2
- package/src/three/worldRendererThree.ts +108 -14
package/package.json
CHANGED
|
@@ -32,6 +32,10 @@ export const defaultWorldRendererConfig = {
|
|
|
32
32
|
autoLowerRenderDistance: false,
|
|
33
33
|
|
|
34
34
|
// Rendering engine settings
|
|
35
|
+
/** Face shading: vanilla Minecraft vs higher-contrast client look */
|
|
36
|
+
shadingTheme: 'high-contrast' as 'vanilla' | 'high-contrast',
|
|
37
|
+
/** Synced from player reactive state (dimension / nether) — consumed by mesher */
|
|
38
|
+
cardinalLight: 'default' as string,
|
|
35
39
|
dayCycle: true,
|
|
36
40
|
smoothLighting: true,
|
|
37
41
|
enableLighting: true,
|
|
@@ -124,6 +124,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
124
124
|
soundSystem: SoundSystem | undefined
|
|
125
125
|
|
|
126
126
|
abstract changeBackgroundColor(color: [number, number, number]): void
|
|
127
|
+
abstract changeCardinalLight(cardinalLight: string): void
|
|
127
128
|
|
|
128
129
|
/** Override in subclass to check if any enabled module requires heightmap data */
|
|
129
130
|
protected anyModuleRequiresHeightmap(): boolean {
|
|
@@ -310,6 +311,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
310
311
|
this.onReactivePlayerStateUpdated('backgroundColor', (value) => {
|
|
311
312
|
this.changeBackgroundColor(value)
|
|
312
313
|
})
|
|
314
|
+
this.onReactivePlayerStateUpdated('cardinalLight', (value) => {
|
|
315
|
+
this.changeCardinalLight(value)
|
|
316
|
+
})
|
|
313
317
|
}
|
|
314
318
|
|
|
315
319
|
watchReactiveConfig() {
|
|
@@ -549,6 +553,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
549
553
|
enableLighting: this.worldRendererConfig.enableLighting,
|
|
550
554
|
skyLight,
|
|
551
555
|
smoothLighting: this.worldRendererConfig.smoothLighting,
|
|
556
|
+
shadingTheme: this.worldRendererConfig.shadingTheme,
|
|
557
|
+
cardinalLight: this.worldRendererConfig.cardinalLight,
|
|
552
558
|
outputFormat: this.outputFormat,
|
|
553
559
|
// textureSize: this.resourcesManager.currentResources!.blocksAtlasParser.atlas.latest.width,
|
|
554
560
|
debugModelVariant: this.worldRendererConfig.debugModelVariant,
|
package/src/mesher/models.ts
CHANGED
|
@@ -395,7 +395,16 @@ function renderElement(world: World, cursor: Vec3, element: BlockElement, doAO:
|
|
|
395
395
|
const aos: number[] = []
|
|
396
396
|
const neighborPos = position.plus(new Vec3(...dir))
|
|
397
397
|
// 10%
|
|
398
|
-
const
|
|
398
|
+
const { smoothLighting, shadingTheme, cardinalLight } = world.config
|
|
399
|
+
const faceLight = world.getLight(neighborPos, undefined, undefined, block.name)
|
|
400
|
+
const sideShading = (shadingTheme === 'high-contrast')
|
|
401
|
+
? (0.8 + 0.5 * Math.max(0, 0.66 * dir[0] + 0.66 * dir[1] + 0.33 * dir[2])) // old directional light behavior
|
|
402
|
+
: (
|
|
403
|
+
cardinalLight === 'nether'
|
|
404
|
+
? (0.5 + Math.abs(0.1 * dir[0] + 0.4 * dir[1] + 0.3 * dir[2]))
|
|
405
|
+
: (0.75 + 0.25 * dir[1] + 0.05 * (Math.abs(dir[2]) - 3 * Math.abs(dir[0])))
|
|
406
|
+
)
|
|
407
|
+
const baseLight = sideShading * faceLight / 15
|
|
399
408
|
for (const pos of corners) {
|
|
400
409
|
let vertex = [
|
|
401
410
|
(pos[0] ? maxx : minx),
|
|
@@ -429,8 +438,6 @@ function renderElement(world: World, cursor: Vec3, element: BlockElement, doAO:
|
|
|
429
438
|
}
|
|
430
439
|
|
|
431
440
|
let light = 1
|
|
432
|
-
const { smoothLighting } = world.config
|
|
433
|
-
// const smoothLighting = true
|
|
434
441
|
if (doAO) {
|
|
435
442
|
const dx = pos[0] * 2 - 1
|
|
436
443
|
const dy = pos[1] * 2 - 1
|
|
@@ -442,7 +449,7 @@ function renderElement(world: World, cursor: Vec3, element: BlockElement, doAO:
|
|
|
442
449
|
const side2 = world.getBlock(cursor.offset(...side2Dir))
|
|
443
450
|
const corner = world.getBlock(cursor.offset(...cornerDir))
|
|
444
451
|
|
|
445
|
-
let cornerLightResult =
|
|
452
|
+
let cornerLightResult = faceLight
|
|
446
453
|
|
|
447
454
|
if (smoothLighting) {
|
|
448
455
|
const dirVec = new Vec3(...dir)
|
|
@@ -459,7 +466,7 @@ function renderElement(world: World, cursor: Vec3, element: BlockElement, doAO:
|
|
|
459
466
|
const cornerLightDir = getVec(new Vec3(...cornerDir))
|
|
460
467
|
const cornerLight = world.getLight(cursor.plus(cornerLightDir))
|
|
461
468
|
// interpolate
|
|
462
|
-
const lights = [side1Light, side2Light, cornerLight,
|
|
469
|
+
const lights = [side1Light, side2Light, cornerLight, faceLight]
|
|
463
470
|
cornerLightResult = lights.reduce((acc, cur) => acc + cur, 0) / lights.length
|
|
464
471
|
}
|
|
465
472
|
|
|
@@ -470,8 +477,10 @@ function renderElement(world: World, cursor: Vec3, element: BlockElement, doAO:
|
|
|
470
477
|
// TODO: correctly interpolate ao light based on pos (evaluate once for each corner of the block)
|
|
471
478
|
|
|
472
479
|
const ao = (side1Block && side2Block) ? 0 : (3 - (side1Block + side2Block + cornerBlock))
|
|
480
|
+
const ao_bias = (shadingTheme === 'high-contrast') ? 0.25 : 0.4
|
|
481
|
+
const ao_scale = (shadingTheme === 'high-contrast') ? 0.25 : 0.2
|
|
473
482
|
// todo light should go upper on lower blocks
|
|
474
|
-
light = (ao +
|
|
483
|
+
light = sideShading * (ao * ao_scale + ao_bias) * (cornerLightResult / 15)
|
|
475
484
|
aos.push(ao)
|
|
476
485
|
|
|
477
486
|
// Log AO and light for this corner (corner index is aos.length - 1)
|
package/src/mesher/shared.ts
CHANGED
|
@@ -11,7 +11,9 @@ export const defaultMesherConfig = {
|
|
|
11
11
|
worldMinY: 0,
|
|
12
12
|
enableLighting: true,
|
|
13
13
|
skyLight: 15,
|
|
14
|
-
smoothLighting:
|
|
14
|
+
smoothLighting: true,
|
|
15
|
+
shadingTheme: 'high-contrast' as 'vanilla' | 'high-contrast',
|
|
16
|
+
cardinalLight: 'default' as string,
|
|
15
17
|
outputFormat: 'threeJs' as 'threeJs' | 'webgpu',
|
|
16
18
|
// textureSize: 1024, // for testing
|
|
17
19
|
debugModelVariant: undefined as undefined | number[],
|
|
@@ -39,6 +39,8 @@ export const getInitialPlayerState = () => proxy({
|
|
|
39
39
|
itemUsageTicks: 0,
|
|
40
40
|
username: '',
|
|
41
41
|
onlineMode: false,
|
|
42
|
+
/** Dimension ambient lighting preset (e.g. nether) — from login/respawn dimension data when available */
|
|
43
|
+
cardinalLight: 'default' as string,
|
|
42
44
|
lightingDisabled: false,
|
|
43
45
|
shouldHideHand: false,
|
|
44
46
|
heldItemMain: undefined as HandItemBlock | undefined,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import * as tweenJs from '@tweenjs/tween.js'
|
|
4
|
-
import PrismarineItem from 'prismarine-item'
|
|
5
4
|
import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
6
5
|
import { BlockModel } from 'mc-assets'
|
|
7
6
|
import { DebugGui } from '../lib/DebugGui'
|
|
@@ -17,8 +16,14 @@ import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
|
17
16
|
import { IndexedData } from 'minecraft-data'
|
|
18
17
|
import { WorldRendererConfig } from '../graphicsBackend'
|
|
19
18
|
import { computeCameraBob, type CameraBobInput } from '../lib/cameraBobbing'
|
|
19
|
+
import { getFirstPersonItemSpecificProps, getHandItemRenderKey } from './holdingBlockItemIdentity'
|
|
20
20
|
|
|
21
21
|
const _tempMat = new THREE.Matrix4()
|
|
22
|
+
const wrapPi = (a: number) => {
|
|
23
|
+
a = (a + Math.PI) % (Math.PI * 2)
|
|
24
|
+
if (a < 0) a += Math.PI * 2
|
|
25
|
+
return a - Math.PI
|
|
26
|
+
}
|
|
22
27
|
|
|
23
28
|
// Vanilla renderPlayerArm transform chain
|
|
24
29
|
function buildBareHandMatrix(swingProgress: number, equipProgress: number): THREE.Matrix4 {
|
|
@@ -94,6 +99,7 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
94
99
|
equipProgress = 0 // 0 = fully visible, 1 = hidden
|
|
95
100
|
stopUpdate = false
|
|
96
101
|
lastHeldItem: HandItemBlock | undefined
|
|
102
|
+
lastHeldItemRenderKey: string | undefined
|
|
97
103
|
currentDisplayType: 'hand' | 'item' | 'block' = 'hand'
|
|
98
104
|
isSwinging = false
|
|
99
105
|
nextIterStopCallbacks: Array<() => void> | undefined
|
|
@@ -116,6 +122,7 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
116
122
|
|
|
117
123
|
constructor(public worldRenderer: WorldRendererThree, public offHand = false) {
|
|
118
124
|
this.initCameraGroup()
|
|
125
|
+
this.swingAnimator = new HandSwingAnimator()
|
|
119
126
|
this.unsubs.push(
|
|
120
127
|
this.worldRenderer.onReactivePlayerStateUpdated('heldItemMain', () => {
|
|
121
128
|
if (!this.offHand) {
|
|
@@ -332,44 +339,32 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
332
339
|
}
|
|
333
340
|
|
|
334
341
|
isDifferentItem(block: HandItemBlock | undefined) {
|
|
335
|
-
|
|
336
|
-
if (!this.lastHeldItem) {
|
|
337
|
-
return true
|
|
338
|
-
}
|
|
339
|
-
if (this.lastHeldItem.name !== block?.name) {
|
|
340
|
-
return true
|
|
341
|
-
}
|
|
342
|
-
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
|
343
|
-
if (!Item.equal(this.lastHeldItem.fullItem, block?.fullItem ?? {}) || JSON.stringify(this.lastHeldItem.fullItem.components) !== JSON.stringify(block?.fullItem?.components)) {
|
|
344
|
-
return true
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return false
|
|
342
|
+
return this.lastHeldItemRenderKey !== getHandItemRenderKey(this.worldRenderer, block)
|
|
348
343
|
}
|
|
349
344
|
|
|
350
345
|
updateCameraGroup() {
|
|
351
346
|
if (this.stopUpdate) return
|
|
352
347
|
const { camera } = this
|
|
353
348
|
|
|
354
|
-
// Hand rotation momentum (xBob/yBob) — vanilla Minecraft inertia effect
|
|
355
|
-
// Use base rotation from CameraShake (actual player view angles)
|
|
356
349
|
const now = performance.now()
|
|
357
350
|
const baseRotation = this.worldRenderer.cameraShake.getBaseRotation()
|
|
358
351
|
const actualPitch = baseRotation.pitch
|
|
359
352
|
const actualYaw = baseRotation.yaw
|
|
353
|
+
|
|
360
354
|
if (this.lastBobUpdateTime === 0) {
|
|
361
355
|
this.xBob = actualPitch
|
|
362
356
|
this.yBob = actualYaw
|
|
363
|
-
this.lastBobUpdateTime = now
|
|
364
357
|
} else {
|
|
365
358
|
const dt = Math.min((now - this.lastBobUpdateTime) / 1000, 0.1)
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
this.xBob += (actualPitch - this.xBob) *
|
|
369
|
-
this.yBob += (actualYaw - this.yBob) *
|
|
359
|
+
const pitchFactor = 1 - Math.pow(0.5, dt * 28)
|
|
360
|
+
const yawFactor = 1 - Math.pow(0.5, dt * 36)
|
|
361
|
+
this.xBob += (actualPitch - this.xBob) * pitchFactor
|
|
362
|
+
this.yBob += wrapPi(actualYaw - this.yBob) * yawFactor
|
|
370
363
|
}
|
|
371
|
-
|
|
372
|
-
|
|
364
|
+
|
|
365
|
+
this.lastBobUpdateTime = now
|
|
366
|
+
const pitchOffset = (actualPitch - this.xBob) * -0.05
|
|
367
|
+
const yawOffset = wrapPi(actualYaw - this.yBob) * -0.035
|
|
373
368
|
|
|
374
369
|
this.cameraGroup.position.copy(camera.position)
|
|
375
370
|
this.cameraGroup.rotation.copy(camera.rotation)
|
|
@@ -431,11 +426,7 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
431
426
|
const result = this.worldRenderer.entities.getItemMesh({
|
|
432
427
|
...handItem.fullItem,
|
|
433
428
|
itemId: handItem.id,
|
|
434
|
-
},
|
|
435
|
-
'minecraft:display_context': 'firstperson',
|
|
436
|
-
'minecraft:use_duration': this.worldRenderer.playerStateReactive.itemUsageTicks,
|
|
437
|
-
'minecraft:using_item': !!this.worldRenderer.playerStateReactive.itemUsageTicks,
|
|
438
|
-
}, false, this.lastItemModelName)
|
|
429
|
+
}, getFirstPersonItemSpecificProps(this.worldRenderer), false, this.lastItemModelName)
|
|
439
430
|
if (result) {
|
|
440
431
|
const { mesh: itemMesh, isBlock, modelName } = result
|
|
441
432
|
if (isBlock) {
|
|
@@ -487,8 +478,11 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
487
478
|
this.holdingBlock?.removeFromParent()
|
|
488
479
|
this.holdingBlock = undefined
|
|
489
480
|
this.currentDisplayType = 'hand'
|
|
490
|
-
this.swingAnimator
|
|
491
|
-
|
|
481
|
+
const swingAnimator = this.swingAnimator
|
|
482
|
+
swingAnimator?.stopSwing()
|
|
483
|
+
if (swingAnimator) {
|
|
484
|
+
swingAnimator.type = 'hand'
|
|
485
|
+
}
|
|
492
486
|
this.idleAnimator = undefined
|
|
493
487
|
return
|
|
494
488
|
}
|
|
@@ -516,10 +510,14 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
516
510
|
|
|
517
511
|
switchRequest = 0
|
|
518
512
|
async setNewItem(handItem?: HandItemBlock) {
|
|
519
|
-
|
|
513
|
+
const nextRenderKey = getHandItemRenderKey(this.worldRenderer, handItem)
|
|
514
|
+
const itemChanged = this.lastHeldItemRenderKey !== nextRenderKey
|
|
515
|
+
this.lastHeldItem = handItem
|
|
516
|
+
if (!itemChanged) return
|
|
517
|
+
|
|
518
|
+
this.lastHeldItemRenderKey = nextRenderKey
|
|
520
519
|
this.lastItemModelName = undefined
|
|
521
520
|
const switchRequest = ++this.switchRequest
|
|
522
|
-
this.lastHeldItem = handItem
|
|
523
521
|
|
|
524
522
|
let playAppearAnimation = false
|
|
525
523
|
if (this.holdingBlock) {
|
|
@@ -535,7 +533,11 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
535
533
|
|
|
536
534
|
if (!handItem) {
|
|
537
535
|
this.currentDisplayType = 'hand'
|
|
538
|
-
|
|
536
|
+
const swingAnimator = this.swingAnimator
|
|
537
|
+
swingAnimator?.stopSwing()
|
|
538
|
+
if (swingAnimator) {
|
|
539
|
+
swingAnimator.type = 'hand'
|
|
540
|
+
}
|
|
539
541
|
this.idleAnimator = undefined
|
|
540
542
|
this.blockSwapAnimation = undefined
|
|
541
543
|
return
|
|
@@ -553,8 +555,10 @@ export default class HoldingBlock implements IHoldingBlock {
|
|
|
553
555
|
await this.playBlockSwapAnimation('appeared')
|
|
554
556
|
}
|
|
555
557
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
+
const swingAnimator = this.swingAnimator
|
|
559
|
+
if (swingAnimator) {
|
|
560
|
+
swingAnimator.type = result.type
|
|
561
|
+
}
|
|
558
562
|
// Idle animation disabled — walking bob is handled by vanilla bobView applied to cameraGroup
|
|
559
563
|
this.idleAnimator = undefined
|
|
560
564
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { expect, test, vi } from 'vitest'
|
|
3
|
+
import { getHandItemRenderKey } from './holdingBlockItemIdentity'
|
|
4
|
+
|
|
5
|
+
test('hand item render key ignores live use state but keeps first-person display context', () => {
|
|
6
|
+
const getItemRenderData = vi.fn(() => ({ modelName: 'item/bow' }))
|
|
7
|
+
const worldRenderer = {
|
|
8
|
+
playerStateReactive: {
|
|
9
|
+
itemUsageTicks: 20,
|
|
10
|
+
},
|
|
11
|
+
resourcesManager: {
|
|
12
|
+
currentResources: {},
|
|
13
|
+
},
|
|
14
|
+
getItemRenderData,
|
|
15
|
+
} as any
|
|
16
|
+
|
|
17
|
+
const handItem = {
|
|
18
|
+
type: 'item',
|
|
19
|
+
id: 261,
|
|
20
|
+
name: 'minecraft:bow',
|
|
21
|
+
fullItem: {
|
|
22
|
+
count: 1,
|
|
23
|
+
},
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
const activeKey = getHandItemRenderKey(worldRenderer, handItem)
|
|
27
|
+
worldRenderer.playerStateReactive.itemUsageTicks = 0
|
|
28
|
+
const idleKey = getHandItemRenderKey(worldRenderer, handItem)
|
|
29
|
+
|
|
30
|
+
expect(activeKey).toBe(idleKey)
|
|
31
|
+
expect(getItemRenderData).toHaveBeenNthCalledWith(1, {
|
|
32
|
+
...handItem.fullItem,
|
|
33
|
+
itemId: handItem.id,
|
|
34
|
+
}, {
|
|
35
|
+
'minecraft:display_context': 'firstperson',
|
|
36
|
+
})
|
|
37
|
+
expect(getItemRenderData).toHaveBeenNthCalledWith(2, {
|
|
38
|
+
...handItem.fullItem,
|
|
39
|
+
itemId: handItem.id,
|
|
40
|
+
}, {
|
|
41
|
+
'minecraft:display_context': 'firstperson',
|
|
42
|
+
})
|
|
43
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import type { HandItemBlock, ItemSpecificContextProperties } from '../playerState/types'
|
|
3
|
+
import type { WorldRendererThree } from './worldRendererThree'
|
|
4
|
+
|
|
5
|
+
export const getFirstPersonItemSpecificProps = (worldRenderer: WorldRendererThree): ItemSpecificContextProperties => ({
|
|
6
|
+
'minecraft:display_context': 'firstperson',
|
|
7
|
+
'minecraft:use_duration': worldRenderer.playerStateReactive.itemUsageTicks,
|
|
8
|
+
'minecraft:using_item': !!worldRenderer.playerStateReactive.itemUsageTicks,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const getFirstPersonItemIdentityProps = (): ItemSpecificContextProperties => ({
|
|
12
|
+
'minecraft:display_context': 'firstperson',
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const getHandItemRenderKey = (worldRenderer: WorldRendererThree, handItem?: HandItemBlock) => {
|
|
16
|
+
if (!handItem) return 'empty'
|
|
17
|
+
if (handItem.type === 'hand') return 'hand'
|
|
18
|
+
|
|
19
|
+
const itemIdentifier = handItem.name ?? (handItem.id !== undefined ? `#${handItem.id}` : 'unknown')
|
|
20
|
+
if (!worldRenderer.resourcesManager.currentResources) {
|
|
21
|
+
return `${handItem.type}:${itemIdentifier}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const renderData = worldRenderer.getItemRenderData({
|
|
25
|
+
...handItem.fullItem,
|
|
26
|
+
itemId: handItem.id,
|
|
27
|
+
}, getFirstPersonItemIdentityProps())
|
|
28
|
+
|
|
29
|
+
return `${handItem.type}:${itemIdentifier}:${renderData.modelName}`
|
|
30
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import * as tweenJs from '@tweenjs/tween.js'
|
|
4
|
-
import PrismarineItem from 'prismarine-item'
|
|
5
4
|
import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
6
5
|
import { BlockModel } from 'mc-assets'
|
|
7
6
|
import { DebugGui } from '../lib/DebugGui'
|
|
@@ -17,6 +16,7 @@ import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
|
17
16
|
import { IndexedData } from 'minecraft-data'
|
|
18
17
|
import { WorldRendererConfig } from '../graphicsBackend'
|
|
19
18
|
import { IHoldingBlock } from './holdingBlockTypes'
|
|
19
|
+
import { getFirstPersonItemSpecificProps, getHandItemRenderKey } from './holdingBlockItemIdentity'
|
|
20
20
|
|
|
21
21
|
const rotationPositionData = {
|
|
22
22
|
itemRight: {
|
|
@@ -103,6 +103,7 @@ export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
|
103
103
|
camera = new THREE.PerspectiveCamera(75, 1, 0.1, 100)
|
|
104
104
|
stopUpdate = false
|
|
105
105
|
lastHeldItem: HandItemBlock | undefined
|
|
106
|
+
lastHeldItemRenderKey: string | undefined
|
|
106
107
|
isSwinging = false
|
|
107
108
|
nextIterStopCallbacks: Array<() => void> | undefined
|
|
108
109
|
idleAnimator: HandIdleAnimator | undefined
|
|
@@ -304,19 +305,7 @@ export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
|
304
305
|
}
|
|
305
306
|
|
|
306
307
|
isDifferentItem(block: HandItemBlock | undefined) {
|
|
307
|
-
|
|
308
|
-
if (!this.lastHeldItem) {
|
|
309
|
-
return true
|
|
310
|
-
}
|
|
311
|
-
if (this.lastHeldItem.name !== block?.name) {
|
|
312
|
-
return true
|
|
313
|
-
}
|
|
314
|
-
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
|
315
|
-
if (!Item.equal(this.lastHeldItem.fullItem, block?.fullItem ?? {}) || JSON.stringify(this.lastHeldItem.fullItem.components) !== JSON.stringify(block?.fullItem?.components)) {
|
|
316
|
-
return true
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return false
|
|
308
|
+
return this.lastHeldItemRenderKey !== getHandItemRenderKey(this.worldRenderer, block)
|
|
320
309
|
}
|
|
321
310
|
|
|
322
311
|
updateCameraGroup() {
|
|
@@ -355,11 +344,7 @@ export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
|
355
344
|
const result = this.worldRenderer.entities.getItemMesh({
|
|
356
345
|
...handItem.fullItem,
|
|
357
346
|
itemId: handItem.id,
|
|
358
|
-
},
|
|
359
|
-
'minecraft:display_context': 'firstperson',
|
|
360
|
-
'minecraft:use_duration': this.worldRenderer.playerStateReactive.itemUsageTicks,
|
|
361
|
-
'minecraft:using_item': !!this.worldRenderer.playerStateReactive.itemUsageTicks,
|
|
362
|
-
}, false, this.lastItemModelName)
|
|
347
|
+
}, getFirstPersonItemSpecificProps(this.worldRenderer), false, this.lastItemModelName)
|
|
363
348
|
if (result) {
|
|
364
349
|
const { mesh: itemMesh, isBlock, modelName } = result
|
|
365
350
|
if (isBlock) {
|
|
@@ -421,10 +406,14 @@ export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
|
421
406
|
|
|
422
407
|
switchRequest = 0
|
|
423
408
|
async setNewItem(handItem?: HandItemBlock) {
|
|
424
|
-
|
|
409
|
+
const nextRenderKey = getHandItemRenderKey(this.worldRenderer, handItem)
|
|
410
|
+
const itemChanged = this.lastHeldItemRenderKey !== nextRenderKey
|
|
411
|
+
this.lastHeldItem = handItem
|
|
412
|
+
if (!itemChanged) return
|
|
413
|
+
|
|
414
|
+
this.lastHeldItemRenderKey = nextRenderKey
|
|
425
415
|
this.lastItemModelName = undefined
|
|
426
416
|
const switchRequest = ++this.switchRequest
|
|
427
|
-
this.lastHeldItem = handItem
|
|
428
417
|
let playAppearAnimation = false
|
|
429
418
|
if (this.holdingBlock) {
|
|
430
419
|
// play disappear animation
|
|
@@ -472,7 +461,8 @@ export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
|
472
461
|
await this.playBlockSwapAnimation('appeared')
|
|
473
462
|
}
|
|
474
463
|
|
|
475
|
-
this.swingAnimator
|
|
464
|
+
this.swingAnimator ??= new HandSwingAnimator(this.holdingBlockInnerGroup)
|
|
465
|
+
this.swingAnimator.setHandMesh(this.holdingBlockInnerGroup)
|
|
476
466
|
this.swingAnimator.type = result.type
|
|
477
467
|
if (this.config.viewBobbing) {
|
|
478
468
|
this.idleAnimator = new HandIdleAnimator(this.holdingBlockInnerGroup, this.worldRenderer.playerStateReactive)
|
|
@@ -783,9 +773,9 @@ class HandSwingAnimator {
|
|
|
783
773
|
private lastTime = 0
|
|
784
774
|
private isAnimating = false
|
|
785
775
|
private stopRequested = false
|
|
786
|
-
private
|
|
787
|
-
private
|
|
788
|
-
private
|
|
776
|
+
private originalRotation: THREE.Euler
|
|
777
|
+
private originalPosition: THREE.Vector3
|
|
778
|
+
private originalScale: THREE.Vector3
|
|
789
779
|
|
|
790
780
|
readonly debugParams = {
|
|
791
781
|
// Animation timing
|
|
@@ -849,6 +839,13 @@ class HandSwingAnimator {
|
|
|
849
839
|
// this.debugGui.activate()
|
|
850
840
|
}
|
|
851
841
|
|
|
842
|
+
setHandMesh(handMesh: THREE.Object3D) {
|
|
843
|
+
this.handMesh = handMesh
|
|
844
|
+
this.originalRotation.copy(handMesh.rotation)
|
|
845
|
+
this.originalPosition.copy(handMesh.position)
|
|
846
|
+
this.originalScale.copy(handMesh.scale)
|
|
847
|
+
}
|
|
848
|
+
|
|
852
849
|
update() {
|
|
853
850
|
if (!this.isAnimating && !this.debugParams.animationStage) {
|
|
854
851
|
// If not animating, ensure we're at original position
|
|
@@ -58,7 +58,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
|
|
|
58
58
|
// Store original methods for patching
|
|
59
59
|
private originalFinishChunk: ((chunkKey: string) => void) | null = null
|
|
60
60
|
private originalDestroy: (() => void) | null = null
|
|
61
|
-
private originalSceneAdd: ((...object: THREE.Object3D[]) => THREE.
|
|
61
|
+
private originalSceneAdd: ((...object: THREE.Object3D[]) => THREE.Scene) | null = null
|
|
62
62
|
private originalHandleWorkerMessage: ((data: { geometry: MesherGeometryOutput; key: string; type: string }) => void) | null = null
|
|
63
63
|
|
|
64
64
|
private configEnabled = true
|
|
@@ -163,7 +163,7 @@ export class SciFiWorldRevealModule implements RendererModuleController {
|
|
|
163
163
|
|
|
164
164
|
// Patch scene.add to intercept mesh additions
|
|
165
165
|
this.originalSceneAdd = wr.scene.add.bind(wr.scene)
|
|
166
|
-
wr.scene.add = (...objects: THREE.Object3D[]): THREE.
|
|
166
|
+
wr.scene.add = (...objects: THREE.Object3D[]): THREE.Scene => {
|
|
167
167
|
// Call original add first
|
|
168
168
|
const result = this.originalSceneAdd!(...objects)
|
|
169
169
|
|
package/src/three/sceneOrigin.ts
CHANGED
|
@@ -174,7 +174,7 @@ export class SceneOrigin {
|
|
|
174
174
|
/** Untrack an Object3D and remove it from the scene */
|
|
175
175
|
removeAndUntrack(obj: Object3D): void {
|
|
176
176
|
this.untrack(obj)
|
|
177
|
-
|
|
177
|
+
obj.removeFromParent()
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** Untrack an Object3D and all its descendants, then remove from the scene */
|
|
@@ -182,7 +182,7 @@ export class SceneOrigin {
|
|
|
182
182
|
obj.traverse((child) => {
|
|
183
183
|
this.untrack(child)
|
|
184
184
|
})
|
|
185
|
-
|
|
185
|
+
obj.removeFromParent()
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/** Get stored world position for a tracked object */
|