minecraft-renderer 0.1.31 → 0.1.32
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/minecraft-renderer.js +57 -57
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +419 -419
- package/package.json +1 -1
- package/src/graphicsBackend/config.ts +1 -0
- package/src/lib/worldrendererCommon.ts +1 -1
- package/src/three/hand.ts +103 -52
- package/src/three/handLegacy.ts +90 -0
- package/src/three/holdingBlock.ts +269 -335
- package/src/three/holdingBlockFactory.ts +16 -0
- package/src/three/holdingBlockLegacy.ts +957 -0
- package/src/three/holdingBlockTypes.ts +22 -0
- package/src/three/worldRendererThree.ts +26 -5
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import * as tweenJs from '@tweenjs/tween.js'
|
|
4
4
|
import PrismarineItem from 'prismarine-item'
|
|
5
|
-
import
|
|
5
|
+
import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
6
6
|
import { BlockModel } from 'mc-assets'
|
|
7
7
|
import { DebugGui } from '../lib/DebugGui'
|
|
8
8
|
import { SmoothSwitcher } from '../lib/smoothSwitcher'
|
|
@@ -10,85 +10,78 @@ import { watchProperty } from '../lib/utils/proxy'
|
|
|
10
10
|
import { getMyHand } from './hand'
|
|
11
11
|
import { WorldRendererThree } from './worldRendererThree'
|
|
12
12
|
import { disposeObject } from './threeJsUtils'
|
|
13
|
+
import type { IHoldingBlock } from './holdingBlockTypes'
|
|
13
14
|
import { HandItemBlock, MovementState } from '../playerState/types'
|
|
14
15
|
import { PlayerStateRenderer } from '../playerState/playerState'
|
|
15
16
|
import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
16
17
|
import { IndexedData } from 'minecraft-data'
|
|
17
|
-
import type { ResourcesManagerTransferred } from '../resourcesManager'
|
|
18
18
|
import { WorldRendererConfig } from '../graphicsBackend'
|
|
19
|
+
import { computeCameraBob, type CameraBobInput } from '../lib/cameraBobbing'
|
|
20
|
+
|
|
21
|
+
const _tempMat = new THREE.Matrix4()
|
|
22
|
+
|
|
23
|
+
// Vanilla renderPlayerArm transform chain
|
|
24
|
+
function buildBareHandMatrix(swingProgress: number, equipProgress: number): THREE.Matrix4 {
|
|
25
|
+
const mat = new THREE.Matrix4()
|
|
26
|
+
const side = 1 // right hand
|
|
27
|
+
|
|
28
|
+
const sqrtSwing = Math.sqrt(swingProgress)
|
|
29
|
+
const swingX = -0.3 * Math.sin(sqrtSwing * Math.PI)
|
|
30
|
+
const swingY = 0.4 * Math.sin(sqrtSwing * 2 * Math.PI)
|
|
31
|
+
const swingZ = -0.4 * Math.sin(swingProgress * Math.PI)
|
|
32
|
+
|
|
33
|
+
// Step 1: Base position with swing
|
|
34
|
+
mat.multiply(_tempMat.makeTranslation(side * (swingX + 0.64), swingY - 0.6 + equipProgress * -0.6, swingZ - 0.72))
|
|
35
|
+
// Step 2: Base Y rotation 45°
|
|
36
|
+
mat.multiply(_tempMat.makeRotationY(side * 45 * Math.PI / 180))
|
|
37
|
+
// Step 3: Swing Y rotation
|
|
38
|
+
mat.multiply(_tempMat.makeRotationY(side * Math.sin(sqrtSwing * Math.PI) * 70 * Math.PI / 180))
|
|
39
|
+
// Step 4: Swing Z rotation
|
|
40
|
+
mat.multiply(_tempMat.makeRotationZ(side * Math.sin(swingProgress * swingProgress * Math.PI) * -20 * Math.PI / 180))
|
|
41
|
+
// Step 5: Second translation
|
|
42
|
+
mat.multiply(_tempMat.makeTranslation(side * -1, 3.6, 3.5))
|
|
43
|
+
// Step 6: Z rotation 120°
|
|
44
|
+
mat.multiply(_tempMat.makeRotationZ(side * 120 * Math.PI / 180))
|
|
45
|
+
// Step 7: X rotation 200°
|
|
46
|
+
mat.multiply(_tempMat.makeRotationX(200 * Math.PI / 180))
|
|
47
|
+
// Step 8: Y rotation -135°
|
|
48
|
+
mat.multiply(_tempMat.makeRotationY(side * -135 * Math.PI / 180))
|
|
49
|
+
// Step 9: Final X offset
|
|
50
|
+
mat.multiply(_tempMat.makeTranslation(side * 5.6, 0, 0))
|
|
51
|
+
// Step 10: translateToHand - arm part position (-5/16, 2/16, 0)
|
|
52
|
+
mat.multiply(_tempMat.makeTranslation(side * -5 / 16, 2 / 16, 0))
|
|
53
|
+
|
|
54
|
+
return mat
|
|
55
|
+
}
|
|
19
56
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
1.13,
|
|
46
|
-
3.2,
|
|
47
|
-
1.13
|
|
48
|
-
],
|
|
49
|
-
'scale': [
|
|
50
|
-
0.68,
|
|
51
|
-
0.68,
|
|
52
|
-
0.68
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
blockRight: {
|
|
56
|
-
'rotation': [
|
|
57
|
-
0,
|
|
58
|
-
45,
|
|
59
|
-
0
|
|
60
|
-
],
|
|
61
|
-
'translation': [
|
|
62
|
-
0,
|
|
63
|
-
0,
|
|
64
|
-
0
|
|
65
|
-
],
|
|
66
|
-
'scale': [
|
|
67
|
-
0.4,
|
|
68
|
-
0.4,
|
|
69
|
-
0.4
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
|
-
blockLeft: {
|
|
73
|
-
'rotation': [
|
|
74
|
-
0,
|
|
75
|
-
225,
|
|
76
|
-
0
|
|
77
|
-
],
|
|
78
|
-
'translation': [
|
|
79
|
-
0,
|
|
80
|
-
0,
|
|
81
|
-
0
|
|
82
|
-
],
|
|
83
|
-
'scale': [
|
|
84
|
-
0.4,
|
|
85
|
-
0.4,
|
|
86
|
-
0.4
|
|
87
|
-
]
|
|
88
|
-
}
|
|
57
|
+
// Vanilla item arm transforms: applyItemArmTransform + applyItemArmAttackTransform
|
|
58
|
+
function buildItemArmMatrix(swingProgress: number, equipProgress: number): THREE.Matrix4 {
|
|
59
|
+
const mat = new THREE.Matrix4()
|
|
60
|
+
const side = 1 // right hand
|
|
61
|
+
|
|
62
|
+
const sqrtSwing = Math.sqrt(swingProgress)
|
|
63
|
+
|
|
64
|
+
// Swing position offsets (from renderArmWithItem default branch)
|
|
65
|
+
const swingX = -0.4 * Math.sin(sqrtSwing * Math.PI)
|
|
66
|
+
const swingY = 0.2 * Math.sin(sqrtSwing * 2 * Math.PI)
|
|
67
|
+
const swingZ = -0.2 * Math.sin(swingProgress * Math.PI)
|
|
68
|
+
mat.multiply(_tempMat.makeTranslation(side * swingX, swingY, swingZ))
|
|
69
|
+
|
|
70
|
+
// applyItemArmTransform: translate(±0.56, -0.52 + equip*-0.6, -0.72)
|
|
71
|
+
mat.multiply(_tempMat.makeTranslation(side * 0.56, -0.52 + equipProgress * -0.6, -0.72))
|
|
72
|
+
|
|
73
|
+
// applyItemArmAttackTransform
|
|
74
|
+
const sinSwingSq = Math.sin(swingProgress * swingProgress * Math.PI)
|
|
75
|
+
const sinSqrtSwing = Math.sin(sqrtSwing * Math.PI)
|
|
76
|
+
mat.multiply(_tempMat.makeRotationY(side * (45 + sinSwingSq * -20) * Math.PI / 180))
|
|
77
|
+
mat.multiply(_tempMat.makeRotationZ(side * sinSqrtSwing * -20 * Math.PI / 180))
|
|
78
|
+
mat.multiply(_tempMat.makeRotationX(sinSqrtSwing * -80 * Math.PI / 180))
|
|
79
|
+
mat.multiply(_tempMat.makeRotationY(side * -45 * Math.PI / 180))
|
|
80
|
+
|
|
81
|
+
return mat
|
|
89
82
|
}
|
|
90
83
|
|
|
91
|
-
export default class HoldingBlock {
|
|
84
|
+
export default class HoldingBlock implements IHoldingBlock {
|
|
92
85
|
// TODO refactor with the tree builder for better visual understanding
|
|
93
86
|
holdingBlock: THREE.Object3D | undefined = undefined
|
|
94
87
|
blockSwapAnimation: {
|
|
@@ -96,36 +89,45 @@ export default class HoldingBlock {
|
|
|
96
89
|
// hidden: boolean
|
|
97
90
|
} | undefined = undefined
|
|
98
91
|
cameraGroup = new THREE.Mesh()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
camera = new THREE.PerspectiveCamera(75, 1, 0.1, 100)
|
|
92
|
+
armTransformGroup = new THREE.Group()
|
|
93
|
+
camera = new THREE.PerspectiveCamera(70, 1, 0.05, 100)
|
|
94
|
+
equipProgress = 0 // 0 = fully visible, 1 = hidden
|
|
103
95
|
stopUpdate = false
|
|
104
96
|
lastHeldItem: HandItemBlock | undefined
|
|
97
|
+
currentDisplayType: 'hand' | 'item' | 'block' = 'hand'
|
|
105
98
|
isSwinging = false
|
|
106
99
|
nextIterStopCallbacks: Array<() => void> | undefined
|
|
107
100
|
idleAnimator: HandIdleAnimator | undefined
|
|
108
101
|
ready = false
|
|
109
102
|
lastUpdate = 0
|
|
103
|
+
xBob = 0
|
|
104
|
+
yBob = 0
|
|
105
|
+
lastBobUpdateTime = 0
|
|
106
|
+
private lastBobWalkDist = 0
|
|
107
|
+
private lastBobTickTime = 0
|
|
110
108
|
playerHand: THREE.Object3D | undefined
|
|
111
109
|
offHandDisplay = false
|
|
112
110
|
offHandModeLegacy = false
|
|
113
111
|
|
|
114
112
|
swingAnimator: HandSwingAnimator | undefined
|
|
115
113
|
config: WorldRendererConfig
|
|
114
|
+
private disposed = false
|
|
115
|
+
private unsubs: Array<() => void> = []
|
|
116
116
|
|
|
117
117
|
constructor(public worldRenderer: WorldRendererThree, public offHand = false) {
|
|
118
118
|
this.initCameraGroup()
|
|
119
|
-
this.
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
this.unsubs.push(
|
|
120
|
+
this.worldRenderer.onReactivePlayerStateUpdated('heldItemMain', () => {
|
|
121
|
+
if (!this.offHand) {
|
|
122
|
+
this.updateItem()
|
|
123
|
+
}
|
|
124
|
+
}, false),
|
|
125
|
+
this.worldRenderer.onReactivePlayerStateUpdated('heldItemOff', () => {
|
|
126
|
+
if (this.offHand) {
|
|
127
|
+
this.updateItem()
|
|
128
|
+
}
|
|
129
|
+
}, false)
|
|
130
|
+
)
|
|
129
131
|
this.config = worldRenderer.displayOptions.inWorldRenderingConfig
|
|
130
132
|
|
|
131
133
|
this.offHandDisplay = this.offHand
|
|
@@ -133,12 +135,14 @@ export default class HoldingBlock {
|
|
|
133
135
|
if (!this.offHand) {
|
|
134
136
|
// load default hand
|
|
135
137
|
void getMyHand().then((hand) => {
|
|
138
|
+
if (this.disposed) return
|
|
136
139
|
this.playerHand = hand
|
|
137
140
|
// trigger update
|
|
138
141
|
this.updateItem()
|
|
139
142
|
}).then(() => {
|
|
143
|
+
if (this.disposed) return
|
|
140
144
|
// now watch over the player skin
|
|
141
|
-
watchProperty(
|
|
145
|
+
const unsub = watchProperty(
|
|
142
146
|
async () => {
|
|
143
147
|
return getMyHand(this.worldRenderer.playerStateReactive.playerSkin, this.worldRenderer.playerStateReactive.onlineMode ? this.worldRenderer.playerStateReactive.username : undefined)
|
|
144
148
|
},
|
|
@@ -155,10 +159,33 @@ export default class HoldingBlock {
|
|
|
155
159
|
disposeObject(oldHand!, true)
|
|
156
160
|
}
|
|
157
161
|
)
|
|
162
|
+
this.unsubs.push(unsub)
|
|
158
163
|
})
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
|
|
167
|
+
dispose() {
|
|
168
|
+
this.disposed = true
|
|
169
|
+
this.unsubs.forEach(fn => fn())
|
|
170
|
+
this.unsubs = []
|
|
171
|
+
this.idleAnimator?.destroy()
|
|
172
|
+
this.idleAnimator = undefined
|
|
173
|
+
this.swingAnimator?.stopSwing()
|
|
174
|
+
this.swingAnimator = undefined
|
|
175
|
+
this.blockSwapAnimation?.switcher.forceFinish()
|
|
176
|
+
this.blockSwapAnimation = undefined
|
|
177
|
+
disposeObject(this.cameraGroup, true)
|
|
178
|
+
if (this.holdingBlock && this.holdingBlock !== this.playerHand) {
|
|
179
|
+
disposeObject(this.holdingBlock, true)
|
|
180
|
+
}
|
|
181
|
+
this.holdingBlock = undefined
|
|
182
|
+
if (this.playerHand) {
|
|
183
|
+
disposeObject(this.playerHand, true)
|
|
184
|
+
this.playerHand = undefined
|
|
185
|
+
}
|
|
186
|
+
this.ready = false
|
|
187
|
+
}
|
|
188
|
+
|
|
162
189
|
updateItem() {
|
|
163
190
|
if (!this.ready) return
|
|
164
191
|
const item = this.offHand ? this.worldRenderer.playerStateReactive.heldItemOff : this.worldRenderer.playerStateReactive.heldItemMain
|
|
@@ -175,6 +202,9 @@ export default class HoldingBlock {
|
|
|
175
202
|
|
|
176
203
|
initCameraGroup() {
|
|
177
204
|
this.cameraGroup = new THREE.Mesh()
|
|
205
|
+
this.armTransformGroup = new THREE.Group()
|
|
206
|
+
this.armTransformGroup.matrixAutoUpdate = false
|
|
207
|
+
this.cameraGroup.add(this.armTransformGroup)
|
|
178
208
|
}
|
|
179
209
|
|
|
180
210
|
startSwing() {
|
|
@@ -192,29 +222,37 @@ export default class HoldingBlock {
|
|
|
192
222
|
void this.replaceItemModel(this.lastHeldItem)
|
|
193
223
|
}
|
|
194
224
|
|
|
195
|
-
// Only update
|
|
225
|
+
// Only update swing animation
|
|
196
226
|
if (this.swingAnimator?.isCurrentlySwinging() || this.swingAnimator?.debugParams.animationStage) {
|
|
197
227
|
this.swingAnimator?.update()
|
|
198
|
-
} else {
|
|
199
|
-
this.idleAnimator?.update()
|
|
200
228
|
}
|
|
229
|
+
// Idle animation disabled temporarily
|
|
201
230
|
|
|
202
231
|
this.blockSwapAnimation?.switcher.update()
|
|
203
232
|
|
|
204
233
|
const scene = new THREE.Scene()
|
|
205
234
|
scene.add(this.cameraGroup)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
235
|
+
const viewerSize = renderer.getSize(new THREE.Vector2())
|
|
236
|
+
const isPortrait = viewerSize.height > viewerSize.width
|
|
237
|
+
|
|
238
|
+
if (isPortrait) {
|
|
239
|
+
// Portrait: use fixed 1:1 aspect with square viewport to keep hand visible
|
|
240
|
+
if (this.camera.aspect !== 1) {
|
|
241
|
+
this.camera.aspect = 1
|
|
242
|
+
this.camera.updateProjectionMatrix()
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
// Landscape: sync aspect with main camera (vanilla full-viewport rendering)
|
|
246
|
+
if (this.camera.aspect !== originalCamera.aspect) {
|
|
247
|
+
this.camera.aspect = originalCamera.aspect
|
|
248
|
+
this.camera.updateProjectionMatrix()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
210
252
|
this.updateCameraGroup()
|
|
211
253
|
scene.add(ambientLight.clone())
|
|
212
254
|
scene.add(directionalLight.clone())
|
|
213
255
|
|
|
214
|
-
const viewerSize = renderer.getSize(new THREE.Vector2())
|
|
215
|
-
const minSize = Math.min(viewerSize.width, viewerSize.height)
|
|
216
|
-
const x = viewerSize.width - minSize
|
|
217
|
-
|
|
218
256
|
// Mirror the scene for offhand by scaling
|
|
219
257
|
const { offHandDisplay } = this
|
|
220
258
|
if (offHandDisplay) {
|
|
@@ -223,15 +261,23 @@ export default class HoldingBlock {
|
|
|
223
261
|
|
|
224
262
|
renderer.autoClear = false
|
|
225
263
|
renderer.clearDepth()
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
264
|
+
|
|
265
|
+
if (isPortrait) {
|
|
266
|
+
// Portrait: render in square viewport anchored to bottom-right (or bottom-left for offhand)
|
|
267
|
+
const minSize = Math.min(viewerSize.width, viewerSize.height)
|
|
268
|
+
if (offHandDisplay) {
|
|
269
|
+
renderer.setViewport(0, 0, minSize, minSize)
|
|
270
|
+
} else {
|
|
271
|
+
renderer.setViewport(viewerSize.width - minSize, 0, minSize, minSize)
|
|
272
|
+
}
|
|
232
273
|
}
|
|
274
|
+
|
|
233
275
|
renderer.render(scene, this.camera)
|
|
234
|
-
|
|
276
|
+
|
|
277
|
+
if (isPortrait) {
|
|
278
|
+
// Restore full viewport
|
|
279
|
+
renderer.setViewport(0, 0, viewerSize.width, viewerSize.height)
|
|
280
|
+
}
|
|
235
281
|
|
|
236
282
|
// Reset the mirroring after rendering
|
|
237
283
|
if (offHandDisplay) {
|
|
@@ -257,37 +303,25 @@ export default class HoldingBlock {
|
|
|
257
303
|
this.blockSwapAnimation ??= {
|
|
258
304
|
switcher: new SmoothSwitcher(
|
|
259
305
|
() => ({
|
|
260
|
-
|
|
306
|
+
progress: this.equipProgress
|
|
261
307
|
}),
|
|
262
308
|
(property, value) => {
|
|
263
|
-
if (property === '
|
|
309
|
+
if (property === 'progress') this.equipProgress = value
|
|
264
310
|
},
|
|
265
311
|
{
|
|
266
|
-
|
|
312
|
+
progress: 4 // speed: units per second
|
|
267
313
|
}
|
|
268
314
|
)
|
|
269
315
|
}
|
|
270
316
|
|
|
271
|
-
const
|
|
272
|
-
// if (forceState && newState !== forceState) {
|
|
273
|
-
// throw new Error(`forceState does not match current state ${forceState} !== ${newState}`)
|
|
274
|
-
// }
|
|
275
|
-
|
|
276
|
-
const targetY = this.objectInnerGroup.position.y + (this.objectInnerGroup.scale.y * 1.5 * (newState === 'appeared' ? 1 : -1))
|
|
277
|
-
|
|
278
|
-
// if (newState === this.blockSwapAnimation.switcher.transitioningToStateName) {
|
|
279
|
-
// return false
|
|
280
|
-
// }
|
|
281
|
-
|
|
317
|
+
const targetProgress = forceState === 'disappeared' ? 1 : 0
|
|
282
318
|
let cancelled = false
|
|
283
319
|
return new Promise<boolean>((resolve) => {
|
|
284
320
|
this.blockSwapAnimation!.switcher.transitionTo(
|
|
285
|
-
{
|
|
286
|
-
|
|
321
|
+
{ progress: targetProgress },
|
|
322
|
+
forceState,
|
|
287
323
|
() => {
|
|
288
|
-
if (!cancelled)
|
|
289
|
-
resolve(true)
|
|
290
|
-
}
|
|
324
|
+
if (!cancelled) resolve(true)
|
|
291
325
|
},
|
|
292
326
|
() => {
|
|
293
327
|
cancelled = true
|
|
@@ -316,27 +350,74 @@ export default class HoldingBlock {
|
|
|
316
350
|
updateCameraGroup() {
|
|
317
351
|
if (this.stopUpdate) return
|
|
318
352
|
const { camera } = this
|
|
353
|
+
|
|
354
|
+
// Hand rotation momentum (xBob/yBob) — vanilla Minecraft inertia effect
|
|
355
|
+
// Use base rotation from CameraShake (actual player view angles)
|
|
356
|
+
const now = performance.now()
|
|
357
|
+
const baseRotation = this.worldRenderer.cameraShake.getBaseRotation()
|
|
358
|
+
const actualPitch = baseRotation.pitch
|
|
359
|
+
const actualYaw = baseRotation.yaw
|
|
360
|
+
if (this.lastBobUpdateTime === 0) {
|
|
361
|
+
this.xBob = actualPitch
|
|
362
|
+
this.yBob = actualYaw
|
|
363
|
+
this.lastBobUpdateTime = now
|
|
364
|
+
} else {
|
|
365
|
+
const dt = Math.min((now - this.lastBobUpdateTime) / 1000, 0.1)
|
|
366
|
+
this.lastBobUpdateTime = now
|
|
367
|
+
const factor = 1 - Math.pow(0.5, dt * 20)
|
|
368
|
+
this.xBob += (actualPitch - this.xBob) * factor
|
|
369
|
+
this.yBob += (actualYaw - this.yBob) * factor
|
|
370
|
+
}
|
|
371
|
+
const pitchOffset = (actualPitch - this.xBob) * -0.1
|
|
372
|
+
const yawOffset = (actualYaw - this.yBob) * -0.1
|
|
373
|
+
|
|
319
374
|
this.cameraGroup.position.copy(camera.position)
|
|
320
375
|
this.cameraGroup.rotation.copy(camera.rotation)
|
|
321
376
|
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
377
|
+
// ─── bobView: Walking hand bob (vanilla-accurate) ───
|
|
378
|
+
const viewBobbing = this.worldRenderer.displayOptions.inWorldRenderingConfig.viewBobbing
|
|
379
|
+
if (viewBobbing) {
|
|
380
|
+
const ps = this.worldRenderer.playerStateReactive
|
|
325
381
|
|
|
382
|
+
// Track tick timing for partialTick (same approach as CameraBobbingModule)
|
|
383
|
+
if (ps.walkDist !== this.lastBobWalkDist) {
|
|
384
|
+
this.lastBobTickTime = now
|
|
385
|
+
this.lastBobWalkDist = ps.walkDist
|
|
386
|
+
}
|
|
387
|
+
const partialTick = Math.min((now - this.lastBobTickTime) / 50, 1)
|
|
388
|
+
|
|
389
|
+
const bob = computeCameraBob({
|
|
390
|
+
walkDist: ps.walkDist,
|
|
391
|
+
prevWalkDist: ps.prevWalkDist,
|
|
392
|
+
bob: ps.bob,
|
|
393
|
+
prevBob: ps.prevBob,
|
|
394
|
+
partialTick
|
|
395
|
+
})
|
|
326
396
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
397
|
+
// Apply bobView position (translate)
|
|
398
|
+
this.cameraGroup.position.x += bob.position.x
|
|
399
|
+
this.cameraGroup.position.y += bob.position.y
|
|
400
|
+
|
|
401
|
+
// Apply bobView rotation (roll Z + pitch X)
|
|
402
|
+
this.cameraGroup.rotation.z += bob.rotation.z
|
|
403
|
+
this.cameraGroup.rotation.x += bob.rotation.x
|
|
404
|
+
}
|
|
336
405
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
406
|
+
this.cameraGroup.rotation.x += pitchOffset
|
|
407
|
+
this.cameraGroup.rotation.y += yawOffset
|
|
408
|
+
|
|
409
|
+
const type = this.currentDisplayType
|
|
410
|
+
const swingProgress = this.swingAnimator?.getSwingProgress() ?? 0
|
|
411
|
+
|
|
412
|
+
let matrix: THREE.Matrix4
|
|
413
|
+
if (type === 'hand') {
|
|
414
|
+
matrix = buildBareHandMatrix(swingProgress, this.equipProgress)
|
|
415
|
+
} else {
|
|
416
|
+
matrix = buildItemArmMatrix(swingProgress, this.equipProgress)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
this.armTransformGroup.matrix.copy(matrix)
|
|
420
|
+
this.armTransformGroup.matrixWorldNeedsUpdate = true
|
|
340
421
|
}
|
|
341
422
|
|
|
342
423
|
lastItemModelName: string | undefined
|
|
@@ -360,7 +441,6 @@ export default class HoldingBlock {
|
|
|
360
441
|
blockInner = itemMesh
|
|
361
442
|
handItem.type = 'block'
|
|
362
443
|
} else {
|
|
363
|
-
itemMesh.position.set(0.5, 0.5, 0.5)
|
|
364
444
|
blockInner = itemMesh
|
|
365
445
|
handItem.type = 'item'
|
|
366
446
|
}
|
|
@@ -370,12 +450,30 @@ export default class HoldingBlock {
|
|
|
370
450
|
blockInner = this.playerHand!
|
|
371
451
|
}
|
|
372
452
|
if (!blockInner) return
|
|
373
|
-
blockInner.name = 'holdingBlock'
|
|
374
453
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
454
|
+
// Apply vanilla firstperson_righthand display transforms (ItemTransform.apply)
|
|
455
|
+
// Vanilla order: translate(÷16) → rotateXYZ → scale, then model centering
|
|
456
|
+
if (handItem.type === 'item' || handItem.type === 'block') {
|
|
457
|
+
const displayGroup = new THREE.Group()
|
|
458
|
+
displayGroup.name = 'displayTransform'
|
|
459
|
+
|
|
460
|
+
if (handItem.type === 'item') {
|
|
461
|
+
// Vanilla item/handheld firstperson_righthand defaults
|
|
462
|
+
// Translation pre-divided by 16 per ItemTransform deserialization
|
|
463
|
+
displayGroup.position.set(1.13 / 16, 3.2 / 16, 1.13 / 16)
|
|
464
|
+
displayGroup.rotation.set(0, THREE.MathUtils.degToRad(-90), THREE.MathUtils.degToRad(25), 'XYZ')
|
|
465
|
+
displayGroup.scale.set(0.68, 0.68, 0.68)
|
|
466
|
+
} else {
|
|
467
|
+
// Vanilla block/block firstperson_righthand defaults
|
|
468
|
+
displayGroup.rotation.set(0, THREE.MathUtils.degToRad(45), 0, 'XYZ')
|
|
469
|
+
displayGroup.scale.set(0.4, 0.4, 0.4)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
displayGroup.add(blockInner)
|
|
473
|
+
blockInner = displayGroup
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
blockInner.name = 'holdingBlock'
|
|
379
477
|
|
|
380
478
|
return { model: blockInner, type: handItem.type }
|
|
381
479
|
}
|
|
@@ -387,6 +485,7 @@ export default class HoldingBlock {
|
|
|
387
485
|
if (!handItem) {
|
|
388
486
|
this.holdingBlock?.removeFromParent()
|
|
389
487
|
this.holdingBlock = undefined
|
|
488
|
+
this.currentDisplayType = 'hand'
|
|
390
489
|
this.swingAnimator?.stopSwing()
|
|
391
490
|
this.swingAnimator = undefined
|
|
392
491
|
this.idleAnimator = undefined
|
|
@@ -399,7 +498,8 @@ export default class HoldingBlock {
|
|
|
399
498
|
// Update the model without changing the group structure
|
|
400
499
|
this.holdingBlock?.removeFromParent()
|
|
401
500
|
this.holdingBlock = result.model
|
|
402
|
-
this.
|
|
501
|
+
this.currentDisplayType = result.type
|
|
502
|
+
this.armTransformGroup.add(result.model)
|
|
403
503
|
|
|
404
504
|
|
|
405
505
|
}
|
|
@@ -419,18 +519,21 @@ export default class HoldingBlock {
|
|
|
419
519
|
this.lastItemModelName = undefined
|
|
420
520
|
const switchRequest = ++this.switchRequest
|
|
421
521
|
this.lastHeldItem = handItem
|
|
522
|
+
|
|
422
523
|
let playAppearAnimation = false
|
|
423
524
|
if (this.holdingBlock) {
|
|
424
|
-
// play disappear animation
|
|
425
525
|
playAppearAnimation = true
|
|
426
526
|
const result = await this.playBlockSwapAnimation('disappeared')
|
|
427
527
|
if (!result) return
|
|
428
|
-
this.holdingBlock
|
|
528
|
+
this.holdingBlock.removeFromParent()
|
|
529
|
+
if (this.holdingBlock !== this.playerHand) {
|
|
530
|
+
disposeObject(this.holdingBlock, false)
|
|
531
|
+
}
|
|
429
532
|
this.holdingBlock = undefined
|
|
430
533
|
}
|
|
431
534
|
|
|
432
535
|
if (!handItem) {
|
|
433
|
-
this.
|
|
536
|
+
this.currentDisplayType = 'hand'
|
|
434
537
|
this.swingAnimator = undefined
|
|
435
538
|
this.idleAnimator = undefined
|
|
436
539
|
this.blockSwapAnimation = undefined
|
|
@@ -441,91 +544,20 @@ export default class HoldingBlock {
|
|
|
441
544
|
const result = await this.createItemModel(handItem)
|
|
442
545
|
if (!result || switchRequest !== this.switchRequest) return
|
|
443
546
|
|
|
444
|
-
const blockOuterGroup = new THREE.Group()
|
|
445
|
-
this.holdingBlockInnerGroup.removeFromParent()
|
|
446
|
-
this.holdingBlockInnerGroup = new THREE.Group()
|
|
447
|
-
this.holdingBlockInnerGroup.add(result.model)
|
|
448
|
-
blockOuterGroup.add(this.holdingBlockInnerGroup)
|
|
449
547
|
this.holdingBlock = result.model
|
|
450
|
-
this.
|
|
451
|
-
this.
|
|
452
|
-
this.objectInnerGroup.position.set(-0.5, -0.5, -0.5)
|
|
453
|
-
if (playAppearAnimation) {
|
|
454
|
-
this.objectInnerGroup.position.y -= this.objectInnerGroup.scale.y * 1.5
|
|
455
|
-
}
|
|
456
|
-
Object.assign(blockOuterGroup.position, { x: 0.5, y: 0.5, z: 0.5 })
|
|
457
|
-
|
|
458
|
-
this.objectOuterGroup = new THREE.Group()
|
|
459
|
-
this.objectOuterGroup.add(this.objectInnerGroup)
|
|
460
|
-
|
|
461
|
-
this.cameraGroup.add(this.objectOuterGroup)
|
|
462
|
-
const rotationDeg = this.getHandHeld3d().rotation
|
|
463
|
-
this.objectOuterGroup.rotation.y = THREE.MathUtils.degToRad(rotationDeg.yOuter)
|
|
548
|
+
this.currentDisplayType = result.type
|
|
549
|
+
this.armTransformGroup.add(this.holdingBlock)
|
|
464
550
|
|
|
465
551
|
if (playAppearAnimation) {
|
|
466
552
|
await this.playBlockSwapAnimation('appeared')
|
|
467
553
|
}
|
|
468
554
|
|
|
469
|
-
this.swingAnimator = new HandSwingAnimator(
|
|
555
|
+
this.swingAnimator = new HandSwingAnimator()
|
|
470
556
|
this.swingAnimator.type = result.type
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
557
|
+
// Idle animation disabled — walking bob is handled by vanilla bobView applied to cameraGroup
|
|
558
|
+
this.idleAnimator = undefined
|
|
474
559
|
}
|
|
475
560
|
|
|
476
|
-
getHandHeld3d() {
|
|
477
|
-
const type = this.lastHeldItem?.type ?? 'hand'
|
|
478
|
-
const side = this.offHandModeLegacy ? 'Left' : 'Right'
|
|
479
|
-
|
|
480
|
-
let scale = 0.8 * 1.15 // default scale for hand
|
|
481
|
-
let position = {
|
|
482
|
-
x: 0.4,
|
|
483
|
-
y: -0.7,
|
|
484
|
-
z: -0.45
|
|
485
|
-
}
|
|
486
|
-
let rotation = {
|
|
487
|
-
x: -32.4,
|
|
488
|
-
y: 42.8,
|
|
489
|
-
z: -41.3,
|
|
490
|
-
yOuter: 0
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (type === 'item') {
|
|
494
|
-
const itemData = rotationPositionData[`item${side}`]
|
|
495
|
-
position = {
|
|
496
|
-
x: -0.05,
|
|
497
|
-
y: -0.7,
|
|
498
|
-
z: -0.45
|
|
499
|
-
}
|
|
500
|
-
rotation = {
|
|
501
|
-
x: itemData.rotation[0],
|
|
502
|
-
y: itemData.rotation[1],
|
|
503
|
-
z: itemData.rotation[2],
|
|
504
|
-
yOuter: 0
|
|
505
|
-
}
|
|
506
|
-
scale = itemData.scale[0] * 1.15
|
|
507
|
-
} else if (type === 'block') {
|
|
508
|
-
const blockData = rotationPositionData[`block${side}`]
|
|
509
|
-
position = {
|
|
510
|
-
x: 0.4,
|
|
511
|
-
y: -0.7,
|
|
512
|
-
z: -0.45
|
|
513
|
-
}
|
|
514
|
-
rotation = {
|
|
515
|
-
x: blockData.rotation[0],
|
|
516
|
-
y: blockData.rotation[1],
|
|
517
|
-
z: blockData.rotation[2],
|
|
518
|
-
yOuter: 0
|
|
519
|
-
}
|
|
520
|
-
scale = blockData.scale[0] * 1.15
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return {
|
|
524
|
-
rotation,
|
|
525
|
-
position,
|
|
526
|
-
scale
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
561
|
}
|
|
530
562
|
|
|
531
563
|
class HandIdleAnimator {
|
|
@@ -750,83 +782,29 @@ class HandIdleAnimator {
|
|
|
750
782
|
}
|
|
751
783
|
|
|
752
784
|
class HandSwingAnimator {
|
|
753
|
-
private readonly PI = Math.PI
|
|
754
785
|
private animationTimer = 0
|
|
755
786
|
private lastTime = 0
|
|
756
787
|
private isAnimating = false
|
|
757
788
|
private stopRequested = false
|
|
758
|
-
private
|
|
759
|
-
|
|
760
|
-
private readonly originalScale: THREE.Vector3
|
|
789
|
+
private swingProgress = 0
|
|
790
|
+
public type: 'hand' | 'block' | 'item' = 'hand'
|
|
761
791
|
|
|
762
792
|
readonly debugParams = {
|
|
763
|
-
// Animation timing
|
|
764
793
|
animationTime: 250,
|
|
765
794
|
animationStage: 0,
|
|
766
|
-
useClassicSwing: true,
|
|
767
|
-
|
|
768
|
-
// Item/Block animation parameters
|
|
769
|
-
itemSwingXPosScale: -0.8,
|
|
770
|
-
itemSwingYPosScale: 0.2,
|
|
771
|
-
itemSwingZPosScale: -0.2,
|
|
772
|
-
itemHeightScale: -0.6,
|
|
773
|
-
itemPreswingRotY: 45,
|
|
774
|
-
itemSwingXRotAmount: -30,
|
|
775
|
-
itemSwingYRotAmount: -35,
|
|
776
|
-
itemSwingZRotAmount: -5,
|
|
777
|
-
|
|
778
|
-
// Hand/Arm animation parameters
|
|
779
|
-
armSwingXPosScale: -0.3,
|
|
780
|
-
armSwingYPosScale: 0.4,
|
|
781
|
-
armSwingZPosScale: -0.4,
|
|
782
|
-
armSwingYRotAmount: 70,
|
|
783
|
-
armSwingZRotAmount: -20,
|
|
784
|
-
armHeightScale: -0.6
|
|
785
795
|
}
|
|
786
796
|
|
|
787
797
|
private readonly debugGui: DebugGui
|
|
788
798
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
constructor(public handMesh: THREE.Object3D) {
|
|
792
|
-
this.handMesh = handMesh
|
|
793
|
-
// Store initial transforms
|
|
794
|
-
this.originalRotation = handMesh.rotation.clone()
|
|
795
|
-
this.originalPosition = handMesh.position.clone()
|
|
796
|
-
this.originalScale = handMesh.scale.clone()
|
|
797
|
-
|
|
798
|
-
// Initialize debug GUI
|
|
799
|
+
constructor() {
|
|
799
800
|
this.debugGui = new DebugGui('hand_animator', this.debugParams, undefined, {
|
|
800
|
-
animationStage: {
|
|
801
|
-
min: 0,
|
|
802
|
-
max: 1,
|
|
803
|
-
step: 0.01
|
|
804
|
-
},
|
|
805
|
-
// Add ranges for all animation parameters
|
|
806
|
-
itemSwingXPosScale: { min: -2, max: 2, step: 0.1 },
|
|
807
|
-
itemSwingYPosScale: { min: -2, max: 2, step: 0.1 },
|
|
808
|
-
itemSwingZPosScale: { min: -2, max: 2, step: 0.1 },
|
|
809
|
-
itemHeightScale: { min: -2, max: 2, step: 0.1 },
|
|
810
|
-
itemPreswingRotY: { min: -180, max: 180, step: 5 },
|
|
811
|
-
itemSwingXRotAmount: { min: -180, max: 180, step: 5 },
|
|
812
|
-
itemSwingYRotAmount: { min: -180, max: 180, step: 5 },
|
|
813
|
-
itemSwingZRotAmount: { min: -180, max: 180, step: 5 },
|
|
814
|
-
armSwingXPosScale: { min: -2, max: 2, step: 0.1 },
|
|
815
|
-
armSwingYPosScale: { min: -2, max: 2, step: 0.1 },
|
|
816
|
-
armSwingZPosScale: { min: -2, max: 2, step: 0.1 },
|
|
817
|
-
armSwingYRotAmount: { min: -180, max: 180, step: 5 },
|
|
818
|
-
armSwingZRotAmount: { min: -180, max: 180, step: 5 },
|
|
819
|
-
armHeightScale: { min: -2, max: 2, step: 0.1 }
|
|
801
|
+
animationStage: { min: 0, max: 1, step: 0.01 },
|
|
820
802
|
})
|
|
821
|
-
// this.debugGui.activate()
|
|
822
803
|
}
|
|
823
804
|
|
|
824
805
|
update() {
|
|
825
806
|
if (!this.isAnimating && !this.debugParams.animationStage) {
|
|
826
|
-
|
|
827
|
-
this.handMesh.rotation.copy(this.originalRotation)
|
|
828
|
-
this.handMesh.position.copy(this.originalPosition)
|
|
829
|
-
this.handMesh.scale.copy(this.originalScale)
|
|
807
|
+
this.swingProgress = 0
|
|
830
808
|
return
|
|
831
809
|
}
|
|
832
810
|
|
|
@@ -834,77 +812,33 @@ class HandSwingAnimator {
|
|
|
834
812
|
const deltaTime = (now - this.lastTime) / 1000
|
|
835
813
|
this.lastTime = now
|
|
836
814
|
|
|
837
|
-
|
|
838
|
-
this.animationTimer += deltaTime * 1000 // Convert to ms
|
|
815
|
+
this.animationTimer += deltaTime * 1000
|
|
839
816
|
|
|
840
|
-
// Calculate animation stage (0 to 1)
|
|
841
817
|
const stage = this.debugParams.animationStage || Math.min(this.animationTimer / this.debugParams.animationTime, 1)
|
|
842
818
|
|
|
843
819
|
if (stage >= 1) {
|
|
844
|
-
// Animation complete
|
|
845
820
|
if (this.stopRequested) {
|
|
846
|
-
// If stop was requested, actually stop now that we've completed a swing
|
|
847
821
|
this.isAnimating = false
|
|
848
822
|
this.stopRequested = false
|
|
849
823
|
this.animationTimer = 0
|
|
850
|
-
this.
|
|
851
|
-
this.handMesh.position.copy(this.originalPosition)
|
|
852
|
-
this.handMesh.scale.copy(this.originalScale)
|
|
824
|
+
this.swingProgress = 0
|
|
853
825
|
return
|
|
854
826
|
}
|
|
855
|
-
// Otherwise reset timer and continue
|
|
856
827
|
this.animationTimer = 0
|
|
828
|
+
this.swingProgress = 0
|
|
857
829
|
return
|
|
858
830
|
}
|
|
859
831
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
this.handMesh.position.copy(this.originalPosition)
|
|
863
|
-
this.handMesh.scale.copy(this.originalScale)
|
|
864
|
-
|
|
865
|
-
// Calculate swing progress
|
|
866
|
-
const swingProgress = stage
|
|
867
|
-
const sqrtProgress = Math.sqrt(swingProgress)
|
|
868
|
-
const sinProgress = Math.sin(swingProgress * this.PI)
|
|
869
|
-
const sinSqrtProgress = Math.sin(sqrtProgress * this.PI)
|
|
870
|
-
|
|
871
|
-
if (this.type === 'hand') {
|
|
872
|
-
// Hand animation
|
|
873
|
-
const xOffset = this.debugParams.armSwingXPosScale * sinSqrtProgress
|
|
874
|
-
const yOffset = this.debugParams.armSwingYPosScale * Math.sin(sqrtProgress * this.PI * 2)
|
|
875
|
-
const zOffset = this.debugParams.armSwingZPosScale * sinProgress
|
|
876
|
-
|
|
877
|
-
this.handMesh.position.x += xOffset
|
|
878
|
-
this.handMesh.position.y += yOffset + this.debugParams.armHeightScale * swingProgress
|
|
879
|
-
this.handMesh.position.z += zOffset
|
|
880
|
-
|
|
881
|
-
// Rotations
|
|
882
|
-
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.armSwingYRotAmount * sinSqrtProgress)
|
|
883
|
-
this.handMesh.rotation.z += THREE.MathUtils.degToRad(this.debugParams.armSwingZRotAmount * sinProgress)
|
|
884
|
-
} else {
|
|
885
|
-
// Item/Block animation
|
|
886
|
-
const xOffset = this.debugParams.itemSwingXPosScale * sinSqrtProgress
|
|
887
|
-
const yOffset = this.debugParams.itemSwingYPosScale * Math.sin(sqrtProgress * this.PI * 2)
|
|
888
|
-
const zOffset = this.debugParams.itemSwingZPosScale * sinProgress
|
|
889
|
-
|
|
890
|
-
this.handMesh.position.x += xOffset
|
|
891
|
-
this.handMesh.position.y += yOffset + this.debugParams.itemHeightScale * swingProgress
|
|
892
|
-
this.handMesh.position.z += zOffset
|
|
893
|
-
|
|
894
|
-
// Pre-swing rotation
|
|
895
|
-
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.itemPreswingRotY)
|
|
832
|
+
this.swingProgress = stage
|
|
833
|
+
}
|
|
896
834
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.itemSwingYRotAmount * sinSqrtProgress)
|
|
900
|
-
this.handMesh.rotation.z += THREE.MathUtils.degToRad(this.debugParams.itemSwingZRotAmount * sinProgress)
|
|
901
|
-
}
|
|
835
|
+
getSwingProgress(): number {
|
|
836
|
+
return this.swingProgress
|
|
902
837
|
}
|
|
903
838
|
|
|
904
839
|
startSwing() {
|
|
905
840
|
this.stopRequested = false
|
|
906
841
|
if (this.isAnimating) return
|
|
907
|
-
|
|
908
842
|
this.isAnimating = true
|
|
909
843
|
this.animationTimer = 0
|
|
910
844
|
this.lastTime = performance.now()
|