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
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import * as THREE from 'three'
|
|
3
|
+
import * as tweenJs from '@tweenjs/tween.js'
|
|
4
|
+
import PrismarineItem from 'prismarine-item'
|
|
5
|
+
import { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
6
|
+
import { BlockModel } from 'mc-assets'
|
|
7
|
+
import { DebugGui } from '../lib/DebugGui'
|
|
8
|
+
import { SmoothSwitcher } from '../lib/smoothSwitcher'
|
|
9
|
+
import { watchProperty } from '../lib/utils/proxy'
|
|
10
|
+
import { subscribeKey } from 'valtio/utils'
|
|
11
|
+
import { getMyHand } from './handLegacy'
|
|
12
|
+
import { WorldRendererThree } from './worldRendererThree'
|
|
13
|
+
import { disposeObject } from './threeJsUtils'
|
|
14
|
+
import { HandItemBlock, MovementState } from '../playerState/types'
|
|
15
|
+
import { PlayerStateRenderer } from '../playerState/playerState'
|
|
16
|
+
import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
17
|
+
import { IndexedData } from 'minecraft-data'
|
|
18
|
+
import { WorldRendererConfig } from '../graphicsBackend'
|
|
19
|
+
import { IHoldingBlock } from './holdingBlockTypes'
|
|
20
|
+
|
|
21
|
+
const rotationPositionData = {
|
|
22
|
+
itemRight: {
|
|
23
|
+
'rotation': [
|
|
24
|
+
0,
|
|
25
|
+
-90,
|
|
26
|
+
25
|
|
27
|
+
],
|
|
28
|
+
'translation': [
|
|
29
|
+
1.13,
|
|
30
|
+
3.2,
|
|
31
|
+
1.13
|
|
32
|
+
],
|
|
33
|
+
'scale': [
|
|
34
|
+
0.68,
|
|
35
|
+
0.68,
|
|
36
|
+
0.68
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
itemLeft: {
|
|
40
|
+
'rotation': [
|
|
41
|
+
0,
|
|
42
|
+
90,
|
|
43
|
+
-25
|
|
44
|
+
],
|
|
45
|
+
'translation': [
|
|
46
|
+
1.13,
|
|
47
|
+
3.2,
|
|
48
|
+
1.13
|
|
49
|
+
],
|
|
50
|
+
'scale': [
|
|
51
|
+
0.68,
|
|
52
|
+
0.68,
|
|
53
|
+
0.68
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
blockRight: {
|
|
57
|
+
'rotation': [
|
|
58
|
+
0,
|
|
59
|
+
45,
|
|
60
|
+
0
|
|
61
|
+
],
|
|
62
|
+
'translation': [
|
|
63
|
+
0,
|
|
64
|
+
0,
|
|
65
|
+
0
|
|
66
|
+
],
|
|
67
|
+
'scale': [
|
|
68
|
+
0.4,
|
|
69
|
+
0.4,
|
|
70
|
+
0.4
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
blockLeft: {
|
|
74
|
+
'rotation': [
|
|
75
|
+
0,
|
|
76
|
+
225,
|
|
77
|
+
0
|
|
78
|
+
],
|
|
79
|
+
'translation': [
|
|
80
|
+
0,
|
|
81
|
+
0,
|
|
82
|
+
0
|
|
83
|
+
],
|
|
84
|
+
'scale': [
|
|
85
|
+
0.4,
|
|
86
|
+
0.4,
|
|
87
|
+
0.4
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default class HoldingBlockLegacy implements IHoldingBlock {
|
|
93
|
+
// TODO refactor with the tree builder for better visual understanding
|
|
94
|
+
holdingBlock: THREE.Object3D | undefined = undefined
|
|
95
|
+
blockSwapAnimation: {
|
|
96
|
+
switcher: SmoothSwitcher
|
|
97
|
+
// hidden: boolean
|
|
98
|
+
} | undefined = undefined
|
|
99
|
+
cameraGroup = new THREE.Mesh()
|
|
100
|
+
objectOuterGroup = new THREE.Group() // 3
|
|
101
|
+
objectInnerGroup = new THREE.Group() // 4
|
|
102
|
+
holdingBlockInnerGroup = new THREE.Group() // 5
|
|
103
|
+
camera = new THREE.PerspectiveCamera(75, 1, 0.1, 100)
|
|
104
|
+
stopUpdate = false
|
|
105
|
+
lastHeldItem: HandItemBlock | undefined
|
|
106
|
+
isSwinging = false
|
|
107
|
+
nextIterStopCallbacks: Array<() => void> | undefined
|
|
108
|
+
idleAnimator: HandIdleAnimator | undefined
|
|
109
|
+
ready = false
|
|
110
|
+
lastUpdate = 0
|
|
111
|
+
playerHand: THREE.Object3D | undefined
|
|
112
|
+
offHandDisplay = false
|
|
113
|
+
offHandModeLegacy = false
|
|
114
|
+
|
|
115
|
+
swingAnimator: HandSwingAnimator | undefined
|
|
116
|
+
config: WorldRendererConfig
|
|
117
|
+
private disposed = false
|
|
118
|
+
unsubs: Array<() => void> = []
|
|
119
|
+
|
|
120
|
+
constructor(public worldRenderer: WorldRendererThree, public offHand = false) {
|
|
121
|
+
this.initCameraGroup()
|
|
122
|
+
this.unsubs.push(subscribeKey(this.worldRenderer.playerStateReactive, 'heldItemMain', () => {
|
|
123
|
+
if (!this.offHand) {
|
|
124
|
+
this.updateItem()
|
|
125
|
+
}
|
|
126
|
+
}))
|
|
127
|
+
this.unsubs.push(subscribeKey(this.worldRenderer.playerStateReactive, 'heldItemOff', () => {
|
|
128
|
+
if (this.offHand) {
|
|
129
|
+
this.updateItem()
|
|
130
|
+
}
|
|
131
|
+
}))
|
|
132
|
+
this.config = worldRenderer.displayOptions.inWorldRenderingConfig
|
|
133
|
+
|
|
134
|
+
this.offHandDisplay = this.offHand
|
|
135
|
+
// this.offHandDisplay = true
|
|
136
|
+
if (!this.offHand) {
|
|
137
|
+
// load default hand
|
|
138
|
+
void getMyHand().then((hand) => {
|
|
139
|
+
if (this.disposed) return
|
|
140
|
+
this.playerHand = hand
|
|
141
|
+
// trigger update
|
|
142
|
+
this.updateItem()
|
|
143
|
+
}).then(() => {
|
|
144
|
+
if (this.disposed) return
|
|
145
|
+
// now watch over the player skin
|
|
146
|
+
const unsub = watchProperty(
|
|
147
|
+
async () => {
|
|
148
|
+
return getMyHand(this.worldRenderer.playerStateReactive.playerSkin, this.worldRenderer.playerStateReactive.onlineMode ? this.worldRenderer.playerStateReactive.username : undefined)
|
|
149
|
+
},
|
|
150
|
+
this.worldRenderer.playerStateReactive,
|
|
151
|
+
'playerSkin',
|
|
152
|
+
(newHand) => {
|
|
153
|
+
if (newHand) {
|
|
154
|
+
this.playerHand = newHand
|
|
155
|
+
// trigger update
|
|
156
|
+
this.updateItem()
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
(oldHand) => {
|
|
160
|
+
disposeObject(oldHand!, true)
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
this.unsubs.push(unsub)
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
updateItem() {
|
|
169
|
+
if (!this.ready) return
|
|
170
|
+
const item = this.offHand ? this.worldRenderer.playerStateReactive.heldItemOff : this.worldRenderer.playerStateReactive.heldItemMain
|
|
171
|
+
if (item) {
|
|
172
|
+
void this.setNewItem(item)
|
|
173
|
+
} else if (this.offHand) {
|
|
174
|
+
void this.setNewItem()
|
|
175
|
+
} else {
|
|
176
|
+
void this.setNewItem({
|
|
177
|
+
type: 'hand',
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
initCameraGroup() {
|
|
183
|
+
this.cameraGroup = new THREE.Mesh()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
startSwing() {
|
|
187
|
+
this.swingAnimator?.startSwing()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
stopSwing() {
|
|
191
|
+
this.swingAnimator?.stopSwing()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
render(originalCamera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer, ambientLight: THREE.AmbientLight, directionalLight: THREE.DirectionalLight) {
|
|
195
|
+
if (!this.lastHeldItem) return
|
|
196
|
+
const now = performance.now()
|
|
197
|
+
if (this.lastUpdate && now - this.lastUpdate > 50) { // one tick
|
|
198
|
+
void this.replaceItemModel(this.lastHeldItem)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Only update idle animation if not swinging
|
|
202
|
+
if (this.swingAnimator?.isCurrentlySwinging() || this.swingAnimator?.debugParams.animationStage) {
|
|
203
|
+
this.swingAnimator?.update()
|
|
204
|
+
} else {
|
|
205
|
+
this.idleAnimator?.update()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.blockSwapAnimation?.switcher.update()
|
|
209
|
+
|
|
210
|
+
const scene = new THREE.Scene()
|
|
211
|
+
scene.add(this.cameraGroup)
|
|
212
|
+
// if (this.camera.aspect !== originalCamera.aspect) {
|
|
213
|
+
// this.camera.aspect = originalCamera.aspect
|
|
214
|
+
// this.camera.updateProjectionMatrix()
|
|
215
|
+
// }
|
|
216
|
+
this.updateCameraGroup()
|
|
217
|
+
scene.add(ambientLight.clone())
|
|
218
|
+
scene.add(directionalLight.clone())
|
|
219
|
+
|
|
220
|
+
const viewerSize = renderer.getSize(new THREE.Vector2())
|
|
221
|
+
const minSize = Math.min(viewerSize.width, viewerSize.height)
|
|
222
|
+
const x = viewerSize.width - minSize
|
|
223
|
+
|
|
224
|
+
// Mirror the scene for offhand by scaling
|
|
225
|
+
const { offHandDisplay } = this
|
|
226
|
+
if (offHandDisplay) {
|
|
227
|
+
this.cameraGroup.scale.x = -1
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
renderer.autoClear = false
|
|
231
|
+
renderer.clearDepth()
|
|
232
|
+
if (this.offHandDisplay) {
|
|
233
|
+
renderer.setViewport(0, 0, minSize, minSize)
|
|
234
|
+
} else {
|
|
235
|
+
const x = viewerSize.width - minSize
|
|
236
|
+
// if (x) x -= x / 4
|
|
237
|
+
renderer.setViewport(x, 0, minSize, minSize)
|
|
238
|
+
}
|
|
239
|
+
renderer.render(scene, this.camera)
|
|
240
|
+
renderer.setViewport(0, 0, viewerSize.width, viewerSize.height)
|
|
241
|
+
|
|
242
|
+
// Reset the mirroring after rendering
|
|
243
|
+
if (offHandDisplay) {
|
|
244
|
+
this.cameraGroup.scale.x = 1
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// worldTest () {
|
|
249
|
+
// const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshPhongMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 }))
|
|
250
|
+
// mesh.position.set(0.5, 0.5, 0.5)
|
|
251
|
+
// const group = new THREE.Group()
|
|
252
|
+
// group.add(mesh)
|
|
253
|
+
// group.position.set(-0.5, -0.5, -0.5)
|
|
254
|
+
// const outerGroup = new THREE.Group()
|
|
255
|
+
// outerGroup.add(group)
|
|
256
|
+
// outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z)
|
|
257
|
+
// this.scene.add(outerGroup)
|
|
258
|
+
|
|
259
|
+
// new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start()
|
|
260
|
+
// }
|
|
261
|
+
|
|
262
|
+
async playBlockSwapAnimation(forceState: 'appeared' | 'disappeared') {
|
|
263
|
+
this.blockSwapAnimation ??= {
|
|
264
|
+
switcher: new SmoothSwitcher(
|
|
265
|
+
() => ({
|
|
266
|
+
y: this.objectInnerGroup.position.y
|
|
267
|
+
}),
|
|
268
|
+
(property, value) => {
|
|
269
|
+
if (property === 'y') this.objectInnerGroup.position.y = value
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
y: 16 // units per second
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const newState = forceState
|
|
278
|
+
// if (forceState && newState !== forceState) {
|
|
279
|
+
// throw new Error(`forceState does not match current state ${forceState} !== ${newState}`)
|
|
280
|
+
// }
|
|
281
|
+
|
|
282
|
+
const targetY = this.objectInnerGroup.position.y + (this.objectInnerGroup.scale.y * 1.5 * (newState === 'appeared' ? 1 : -1))
|
|
283
|
+
|
|
284
|
+
// if (newState === this.blockSwapAnimation.switcher.transitioningToStateName) {
|
|
285
|
+
// return false
|
|
286
|
+
// }
|
|
287
|
+
|
|
288
|
+
let cancelled = false
|
|
289
|
+
return new Promise<boolean>((resolve) => {
|
|
290
|
+
this.blockSwapAnimation!.switcher.transitionTo(
|
|
291
|
+
{ y: targetY },
|
|
292
|
+
newState,
|
|
293
|
+
() => {
|
|
294
|
+
if (!cancelled) {
|
|
295
|
+
resolve(true)
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
() => {
|
|
299
|
+
cancelled = true
|
|
300
|
+
resolve(false)
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
isDifferentItem(block: HandItemBlock | undefined) {
|
|
307
|
+
const Item = PrismarineItem(this.worldRenderer.version)
|
|
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
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
updateCameraGroup() {
|
|
323
|
+
if (this.stopUpdate) return
|
|
324
|
+
const { camera } = this
|
|
325
|
+
this.cameraGroup.position.copy(camera.position)
|
|
326
|
+
this.cameraGroup.rotation.copy(camera.rotation)
|
|
327
|
+
|
|
328
|
+
// const viewerSize = viewer.renderer.getSize(new THREE.Vector2())
|
|
329
|
+
// const aspect = viewerSize.width / viewerSize.height
|
|
330
|
+
const aspect = 1
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
// Adjust the position based on the aspect ratio
|
|
334
|
+
const { position, scale: scaleData } = this.getHandHeld3d()
|
|
335
|
+
const distance = -position.z
|
|
336
|
+
const side = this.offHandModeLegacy ? -1 : 1
|
|
337
|
+
this.objectOuterGroup.position.set(
|
|
338
|
+
distance * position.x * aspect * side,
|
|
339
|
+
distance * position.y,
|
|
340
|
+
-distance
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
// const scale = Math.min(0.8, Math.max(1, 1 * aspect))
|
|
344
|
+
const scale = scaleData * 2.22 * 0.2
|
|
345
|
+
this.objectOuterGroup.scale.set(scale, scale, scale)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
lastItemModelName: string | undefined
|
|
349
|
+
private async createItemModel(handItem: HandItemBlock): Promise<{ model: THREE.Object3D; type: 'hand' | 'block' | 'item' } | undefined> {
|
|
350
|
+
this.lastUpdate = performance.now()
|
|
351
|
+
if (!handItem || (handItem.type === 'hand' && !this.playerHand)) return undefined
|
|
352
|
+
|
|
353
|
+
let blockInner: THREE.Object3D | undefined
|
|
354
|
+
if (handItem.type === 'item' || handItem.type === 'block') {
|
|
355
|
+
const result = this.worldRenderer.entities.getItemMesh({
|
|
356
|
+
...handItem.fullItem,
|
|
357
|
+
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)
|
|
363
|
+
if (result) {
|
|
364
|
+
const { mesh: itemMesh, isBlock, modelName } = result
|
|
365
|
+
if (isBlock) {
|
|
366
|
+
blockInner = itemMesh
|
|
367
|
+
handItem.type = 'block'
|
|
368
|
+
} else {
|
|
369
|
+
itemMesh.position.set(0.5, 0.5, 0.5)
|
|
370
|
+
blockInner = itemMesh
|
|
371
|
+
handItem.type = 'item'
|
|
372
|
+
}
|
|
373
|
+
this.lastItemModelName = modelName
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
blockInner = this.playerHand!
|
|
377
|
+
}
|
|
378
|
+
if (!blockInner) return
|
|
379
|
+
blockInner.name = 'holdingBlock'
|
|
380
|
+
|
|
381
|
+
const rotationDeg = this.getHandHeld3d().rotation
|
|
382
|
+
blockInner.rotation.x = THREE.MathUtils.degToRad(rotationDeg.x)
|
|
383
|
+
blockInner.rotation.y = THREE.MathUtils.degToRad(rotationDeg.y)
|
|
384
|
+
blockInner.rotation.z = THREE.MathUtils.degToRad(rotationDeg.z)
|
|
385
|
+
|
|
386
|
+
return { model: blockInner, type: handItem.type }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async replaceItemModel(handItem?: HandItemBlock): Promise<void> {
|
|
390
|
+
// if switch animation is in progress, do not replace the item
|
|
391
|
+
if (this.blockSwapAnimation?.switcher.isTransitioning) return
|
|
392
|
+
|
|
393
|
+
if (!handItem) {
|
|
394
|
+
this.holdingBlock?.removeFromParent()
|
|
395
|
+
this.holdingBlock = undefined
|
|
396
|
+
this.swingAnimator?.stopSwing()
|
|
397
|
+
this.swingAnimator = undefined
|
|
398
|
+
this.idleAnimator = undefined
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const result = await this.createItemModel(handItem)
|
|
403
|
+
if (!result) return
|
|
404
|
+
|
|
405
|
+
// Update the model without changing the group structure
|
|
406
|
+
this.holdingBlock?.removeFromParent()
|
|
407
|
+
this.holdingBlock = result.model
|
|
408
|
+
this.holdingBlockInnerGroup.add(result.model)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
testUnknownBlockSwitch() {
|
|
414
|
+
void this.setNewItem({
|
|
415
|
+
type: 'item',
|
|
416
|
+
name: 'minecraft:some-unknown-block',
|
|
417
|
+
id: 0,
|
|
418
|
+
fullItem: {}
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
switchRequest = 0
|
|
423
|
+
async setNewItem(handItem?: HandItemBlock) {
|
|
424
|
+
if (!this.isDifferentItem(handItem)) return
|
|
425
|
+
this.lastItemModelName = undefined
|
|
426
|
+
const switchRequest = ++this.switchRequest
|
|
427
|
+
this.lastHeldItem = handItem
|
|
428
|
+
let playAppearAnimation = false
|
|
429
|
+
if (this.holdingBlock) {
|
|
430
|
+
// play disappear animation
|
|
431
|
+
playAppearAnimation = true
|
|
432
|
+
const result = await this.playBlockSwapAnimation('disappeared')
|
|
433
|
+
if (!result) return
|
|
434
|
+
this.holdingBlock?.removeFromParent()
|
|
435
|
+
this.holdingBlock = undefined
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (!handItem) {
|
|
439
|
+
this.swingAnimator?.stopSwing()
|
|
440
|
+
this.swingAnimator = undefined
|
|
441
|
+
this.idleAnimator = undefined
|
|
442
|
+
this.blockSwapAnimation = undefined
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (switchRequest !== this.switchRequest) return
|
|
447
|
+
const result = await this.createItemModel(handItem)
|
|
448
|
+
if (!result || switchRequest !== this.switchRequest) return
|
|
449
|
+
|
|
450
|
+
const blockOuterGroup = new THREE.Group()
|
|
451
|
+
this.holdingBlockInnerGroup.removeFromParent()
|
|
452
|
+
this.holdingBlockInnerGroup = new THREE.Group()
|
|
453
|
+
this.holdingBlockInnerGroup.add(result.model)
|
|
454
|
+
blockOuterGroup.add(this.holdingBlockInnerGroup)
|
|
455
|
+
this.holdingBlock = result.model
|
|
456
|
+
this.objectInnerGroup = new THREE.Group()
|
|
457
|
+
this.objectInnerGroup.add(blockOuterGroup)
|
|
458
|
+
this.objectInnerGroup.position.set(-0.5, -0.5, -0.5)
|
|
459
|
+
if (playAppearAnimation) {
|
|
460
|
+
this.objectInnerGroup.position.y -= this.objectInnerGroup.scale.y * 1.5
|
|
461
|
+
}
|
|
462
|
+
Object.assign(blockOuterGroup.position, { x: 0.5, y: 0.5, z: 0.5 })
|
|
463
|
+
|
|
464
|
+
this.objectOuterGroup = new THREE.Group()
|
|
465
|
+
this.objectOuterGroup.add(this.objectInnerGroup)
|
|
466
|
+
|
|
467
|
+
this.cameraGroup.add(this.objectOuterGroup)
|
|
468
|
+
const rotationDeg = this.getHandHeld3d().rotation
|
|
469
|
+
this.objectOuterGroup.rotation.y = THREE.MathUtils.degToRad(rotationDeg.yOuter)
|
|
470
|
+
|
|
471
|
+
if (playAppearAnimation) {
|
|
472
|
+
await this.playBlockSwapAnimation('appeared')
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
this.swingAnimator = new HandSwingAnimator(this.holdingBlockInnerGroup)
|
|
476
|
+
this.swingAnimator.type = result.type
|
|
477
|
+
if (this.config.viewBobbing) {
|
|
478
|
+
this.idleAnimator = new HandIdleAnimator(this.holdingBlockInnerGroup, this.worldRenderer.playerStateReactive)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
getHandHeld3d() {
|
|
483
|
+
const type = this.lastHeldItem?.type ?? 'hand'
|
|
484
|
+
const side = this.offHandModeLegacy ? 'Left' : 'Right'
|
|
485
|
+
|
|
486
|
+
let scale = 0.8 * 1.15 // default scale for hand
|
|
487
|
+
let position = {
|
|
488
|
+
x: 0.4,
|
|
489
|
+
y: -0.7,
|
|
490
|
+
z: -0.45
|
|
491
|
+
}
|
|
492
|
+
let rotation = {
|
|
493
|
+
x: -32.4,
|
|
494
|
+
y: 42.8,
|
|
495
|
+
z: -41.3,
|
|
496
|
+
yOuter: 0
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (type === 'item') {
|
|
500
|
+
const itemData = rotationPositionData[`item${side}`]
|
|
501
|
+
position = {
|
|
502
|
+
x: -0.05,
|
|
503
|
+
y: -0.7,
|
|
504
|
+
z: -0.45
|
|
505
|
+
}
|
|
506
|
+
rotation = {
|
|
507
|
+
x: itemData.rotation[0],
|
|
508
|
+
y: itemData.rotation[1],
|
|
509
|
+
z: itemData.rotation[2],
|
|
510
|
+
yOuter: 0
|
|
511
|
+
}
|
|
512
|
+
scale = itemData.scale[0] * 1.15
|
|
513
|
+
} else if (type === 'block') {
|
|
514
|
+
const blockData = rotationPositionData[`block${side}`]
|
|
515
|
+
position = {
|
|
516
|
+
x: 0.4,
|
|
517
|
+
y: -0.7,
|
|
518
|
+
z: -0.45
|
|
519
|
+
}
|
|
520
|
+
rotation = {
|
|
521
|
+
x: blockData.rotation[0],
|
|
522
|
+
y: blockData.rotation[1],
|
|
523
|
+
z: blockData.rotation[2],
|
|
524
|
+
yOuter: 0
|
|
525
|
+
}
|
|
526
|
+
scale = blockData.scale[0] * 1.15
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
rotation,
|
|
531
|
+
position,
|
|
532
|
+
scale
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
dispose() {
|
|
537
|
+
this.disposed = true
|
|
538
|
+
this.ready = false
|
|
539
|
+
this.unsubs.forEach(fn => fn())
|
|
540
|
+
this.unsubs = []
|
|
541
|
+
this.idleAnimator?.destroy()
|
|
542
|
+
this.idleAnimator = undefined
|
|
543
|
+
this.swingAnimator?.stopSwing()
|
|
544
|
+
this.swingAnimator = undefined
|
|
545
|
+
this.blockSwapAnimation?.switcher.forceFinish()
|
|
546
|
+
this.blockSwapAnimation = undefined
|
|
547
|
+
disposeObject(this.cameraGroup, true)
|
|
548
|
+
disposeObject(this.objectOuterGroup, true)
|
|
549
|
+
disposeObject(this.objectInnerGroup, true)
|
|
550
|
+
disposeObject(this.holdingBlockInnerGroup, true)
|
|
551
|
+
this.holdingBlock = undefined
|
|
552
|
+
if (this.playerHand) {
|
|
553
|
+
disposeObject(this.playerHand, true)
|
|
554
|
+
this.playerHand = undefined
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
class HandIdleAnimator {
|
|
560
|
+
globalTime = 0
|
|
561
|
+
lastTime = 0
|
|
562
|
+
currentState: MovementState
|
|
563
|
+
targetState: MovementState
|
|
564
|
+
defaultPosition: { x: number; y: number; z: number; rotationX: number; rotationY: number; rotationZ: number }
|
|
565
|
+
private readonly idleOffset = { y: 0, rotationZ: 0 }
|
|
566
|
+
private readonly tween = new tweenJs.Group()
|
|
567
|
+
private idleTween: tweenJs.Tween<{ y: number; rotationZ: number }> | null = null
|
|
568
|
+
private readonly stateSwitcher: SmoothSwitcher
|
|
569
|
+
|
|
570
|
+
// Debug parameters
|
|
571
|
+
private readonly debugParams = {
|
|
572
|
+
// Transition durations for different state changes
|
|
573
|
+
walkingSpeed: 8,
|
|
574
|
+
sprintingSpeed: 16,
|
|
575
|
+
walkingAmplitude: { x: 1 / 30, y: 1 / 10, rotationZ: 0.25 },
|
|
576
|
+
sprintingAmplitude: { x: 1 / 30, y: 1 / 10, rotationZ: 0.4 }
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
private readonly debugGui: DebugGui
|
|
580
|
+
|
|
581
|
+
constructor(public handMesh: THREE.Object3D, public playerState: PlayerStateRenderer) {
|
|
582
|
+
this.handMesh = handMesh
|
|
583
|
+
this.globalTime = 0
|
|
584
|
+
this.currentState = 'NOT_MOVING'
|
|
585
|
+
this.targetState = 'NOT_MOVING'
|
|
586
|
+
|
|
587
|
+
this.defaultPosition = {
|
|
588
|
+
x: handMesh.position.x,
|
|
589
|
+
y: handMesh.position.y,
|
|
590
|
+
z: handMesh.position.z,
|
|
591
|
+
rotationX: handMesh.rotation.x,
|
|
592
|
+
rotationY: handMesh.rotation.y,
|
|
593
|
+
rotationZ: handMesh.rotation.z
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Initialize state switcher with appropriate speeds
|
|
597
|
+
this.stateSwitcher = new SmoothSwitcher(
|
|
598
|
+
() => {
|
|
599
|
+
return {
|
|
600
|
+
x: this.handMesh.position.x,
|
|
601
|
+
y: this.handMesh.position.y,
|
|
602
|
+
z: this.handMesh.position.z,
|
|
603
|
+
rotationX: this.handMesh.rotation.x,
|
|
604
|
+
rotationY: this.handMesh.rotation.y,
|
|
605
|
+
rotationZ: this.handMesh.rotation.z
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
(property, value) => {
|
|
609
|
+
switch (property) {
|
|
610
|
+
case 'x': this.handMesh.position.x = value; break
|
|
611
|
+
case 'y': this.handMesh.position.y = value; break
|
|
612
|
+
case 'z': this.handMesh.position.z = value; break
|
|
613
|
+
case 'rotationX': this.handMesh.rotation.x = value; break
|
|
614
|
+
case 'rotationY': this.handMesh.rotation.y = value; break
|
|
615
|
+
case 'rotationZ': this.handMesh.rotation.z = value; break
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
x: 2, // units per second
|
|
620
|
+
y: 2,
|
|
621
|
+
z: 2,
|
|
622
|
+
rotation: Math.PI // radians per second
|
|
623
|
+
}
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
// Initialize debug GUI
|
|
627
|
+
this.debugGui = new DebugGui('idle_animator', this.debugParams)
|
|
628
|
+
// this.debugGui.activate()
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
private startIdleAnimation() {
|
|
632
|
+
if (this.idleTween) {
|
|
633
|
+
this.idleTween.stop()
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Start from current position for smooth transition
|
|
637
|
+
this.idleOffset.y = this.handMesh.position.y - this.defaultPosition.y
|
|
638
|
+
this.idleOffset.rotationZ = this.handMesh.rotation.z - this.defaultPosition.rotationZ
|
|
639
|
+
|
|
640
|
+
this.idleTween = new tweenJs.Tween(this.idleOffset, this.tween)
|
|
641
|
+
.to({
|
|
642
|
+
y: 0.05,
|
|
643
|
+
rotationZ: 0.05
|
|
644
|
+
}, 3000)
|
|
645
|
+
.easing(tweenJs.Easing.Sinusoidal.InOut)
|
|
646
|
+
.yoyo(true)
|
|
647
|
+
.repeat(Infinity)
|
|
648
|
+
.start()
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
private stopIdleAnimation() {
|
|
652
|
+
if (this.idleTween) {
|
|
653
|
+
this.idleTween.stop()
|
|
654
|
+
this.idleOffset.y = 0
|
|
655
|
+
this.idleOffset.rotationZ = 0
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
private getStateTransform(state: MovementState, time: number) {
|
|
660
|
+
switch (state) {
|
|
661
|
+
case 'NOT_MOVING':
|
|
662
|
+
case 'SNEAKING':
|
|
663
|
+
return {
|
|
664
|
+
x: this.defaultPosition.x,
|
|
665
|
+
y: this.defaultPosition.y,
|
|
666
|
+
z: this.defaultPosition.z,
|
|
667
|
+
rotationX: this.defaultPosition.rotationX,
|
|
668
|
+
rotationY: this.defaultPosition.rotationY,
|
|
669
|
+
rotationZ: this.defaultPosition.rotationZ
|
|
670
|
+
}
|
|
671
|
+
case 'WALKING':
|
|
672
|
+
case 'SPRINTING': {
|
|
673
|
+
const speed = state === 'SPRINTING' ? this.debugParams.sprintingSpeed : this.debugParams.walkingSpeed
|
|
674
|
+
const amplitude = state === 'SPRINTING' ? this.debugParams.sprintingAmplitude : this.debugParams.walkingAmplitude
|
|
675
|
+
|
|
676
|
+
return {
|
|
677
|
+
x: this.defaultPosition.x + Math.sin(time * speed) * amplitude.x,
|
|
678
|
+
y: this.defaultPosition.y - Math.abs(Math.cos(time * speed)) * amplitude.y,
|
|
679
|
+
z: this.defaultPosition.z,
|
|
680
|
+
rotationX: this.defaultPosition.rotationX,
|
|
681
|
+
rotationY: this.defaultPosition.rotationY,
|
|
682
|
+
// rotationZ: this.defaultPosition.rotationZ + Math.sin(time * speed) * amplitude.rotationZ
|
|
683
|
+
rotationZ: this.defaultPosition.rotationZ
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
setState(newState: MovementState) {
|
|
690
|
+
if (newState === this.targetState) return
|
|
691
|
+
|
|
692
|
+
this.targetState = newState
|
|
693
|
+
const noTransition = false
|
|
694
|
+
if (this.currentState !== newState) {
|
|
695
|
+
// Stop idle animation during state transitions
|
|
696
|
+
this.stopIdleAnimation()
|
|
697
|
+
|
|
698
|
+
// Calculate new state transform
|
|
699
|
+
if (!noTransition) {
|
|
700
|
+
// this.globalTime = 0
|
|
701
|
+
const stateTransform = this.getStateTransform(newState, this.globalTime)
|
|
702
|
+
|
|
703
|
+
// Start transition to new state
|
|
704
|
+
this.stateSwitcher.transitionTo(stateTransform, newState)
|
|
705
|
+
// this.updated = false
|
|
706
|
+
}
|
|
707
|
+
this.currentState = newState
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
updated = false
|
|
712
|
+
update() {
|
|
713
|
+
this.stateSwitcher.update()
|
|
714
|
+
|
|
715
|
+
const now = performance.now()
|
|
716
|
+
const deltaTime = (now - this.lastTime) / 1000
|
|
717
|
+
this.lastTime = now
|
|
718
|
+
|
|
719
|
+
// Update global time based on current state
|
|
720
|
+
if (!this.stateSwitcher.isTransitioning) {
|
|
721
|
+
switch (this.currentState) {
|
|
722
|
+
case 'NOT_MOVING':
|
|
723
|
+
case 'SNEAKING':
|
|
724
|
+
this.globalTime = Math.PI / 4
|
|
725
|
+
break
|
|
726
|
+
case 'SPRINTING':
|
|
727
|
+
case 'WALKING':
|
|
728
|
+
this.globalTime += deltaTime
|
|
729
|
+
break
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Check for state changes from player state
|
|
734
|
+
if (this.playerState) {
|
|
735
|
+
const newState = this.playerState.movementState
|
|
736
|
+
if (newState !== this.targetState) {
|
|
737
|
+
this.setState(newState)
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// If we're not transitioning between states and in a stable state that should have idle animation
|
|
742
|
+
if (!this.stateSwitcher.isTransitioning &&
|
|
743
|
+
(this.currentState === 'NOT_MOVING' || this.currentState === 'SNEAKING')) {
|
|
744
|
+
// Start idle animation if not already running
|
|
745
|
+
if (!this.idleTween?.isPlaying()) {
|
|
746
|
+
this.startIdleAnimation()
|
|
747
|
+
}
|
|
748
|
+
// Update idle animation
|
|
749
|
+
this.tween.update()
|
|
750
|
+
|
|
751
|
+
// Apply idle offsets
|
|
752
|
+
this.handMesh.position.y = this.defaultPosition.y + this.idleOffset.y
|
|
753
|
+
this.handMesh.rotation.z = this.defaultPosition.rotationZ + this.idleOffset.rotationZ
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// If we're in a movement state and not transitioning, update the movement animation
|
|
757
|
+
if (!this.stateSwitcher.isTransitioning &&
|
|
758
|
+
(this.currentState === 'WALKING' || this.currentState === 'SPRINTING')) {
|
|
759
|
+
const stateTransform = this.getStateTransform(this.currentState, this.globalTime)
|
|
760
|
+
Object.assign(this.handMesh.position, stateTransform)
|
|
761
|
+
Object.assign(this.handMesh.rotation, {
|
|
762
|
+
x: stateTransform.rotationX,
|
|
763
|
+
y: stateTransform.rotationY,
|
|
764
|
+
z: stateTransform.rotationZ
|
|
765
|
+
})
|
|
766
|
+
// this.stateSwitcher.transitionTo(stateTransform, this.currentState)
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
getCurrentState() {
|
|
771
|
+
return this.currentState
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
destroy() {
|
|
775
|
+
this.stopIdleAnimation()
|
|
776
|
+
this.stateSwitcher.forceFinish()
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
class HandSwingAnimator {
|
|
781
|
+
private readonly PI = Math.PI
|
|
782
|
+
private animationTimer = 0
|
|
783
|
+
private lastTime = 0
|
|
784
|
+
private isAnimating = false
|
|
785
|
+
private stopRequested = false
|
|
786
|
+
private readonly originalRotation: THREE.Euler
|
|
787
|
+
private readonly originalPosition: THREE.Vector3
|
|
788
|
+
private readonly originalScale: THREE.Vector3
|
|
789
|
+
|
|
790
|
+
readonly debugParams = {
|
|
791
|
+
// Animation timing
|
|
792
|
+
animationTime: 250,
|
|
793
|
+
animationStage: 0,
|
|
794
|
+
useClassicSwing: true,
|
|
795
|
+
|
|
796
|
+
// Item/Block animation parameters
|
|
797
|
+
itemSwingXPosScale: -0.8,
|
|
798
|
+
itemSwingYPosScale: 0.2,
|
|
799
|
+
itemSwingZPosScale: -0.2,
|
|
800
|
+
itemHeightScale: -0.6,
|
|
801
|
+
itemPreswingRotY: 45,
|
|
802
|
+
itemSwingXRotAmount: -30,
|
|
803
|
+
itemSwingYRotAmount: -35,
|
|
804
|
+
itemSwingZRotAmount: -5,
|
|
805
|
+
|
|
806
|
+
// Hand/Arm animation parameters
|
|
807
|
+
armSwingXPosScale: -0.3,
|
|
808
|
+
armSwingYPosScale: 0.4,
|
|
809
|
+
armSwingZPosScale: -0.4,
|
|
810
|
+
armSwingYRotAmount: 70,
|
|
811
|
+
armSwingZRotAmount: -20,
|
|
812
|
+
armHeightScale: -0.6
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
private readonly debugGui: DebugGui
|
|
816
|
+
|
|
817
|
+
public type: 'hand' | 'block' | 'item' = 'hand'
|
|
818
|
+
|
|
819
|
+
constructor(public handMesh: THREE.Object3D) {
|
|
820
|
+
this.handMesh = handMesh
|
|
821
|
+
// Store initial transforms
|
|
822
|
+
this.originalRotation = handMesh.rotation.clone()
|
|
823
|
+
this.originalPosition = handMesh.position.clone()
|
|
824
|
+
this.originalScale = handMesh.scale.clone()
|
|
825
|
+
|
|
826
|
+
// Initialize debug GUI
|
|
827
|
+
this.debugGui = new DebugGui('hand_animator', this.debugParams, undefined, {
|
|
828
|
+
animationStage: {
|
|
829
|
+
min: 0,
|
|
830
|
+
max: 1,
|
|
831
|
+
step: 0.01
|
|
832
|
+
},
|
|
833
|
+
// Add ranges for all animation parameters
|
|
834
|
+
itemSwingXPosScale: { min: -2, max: 2, step: 0.1 },
|
|
835
|
+
itemSwingYPosScale: { min: -2, max: 2, step: 0.1 },
|
|
836
|
+
itemSwingZPosScale: { min: -2, max: 2, step: 0.1 },
|
|
837
|
+
itemHeightScale: { min: -2, max: 2, step: 0.1 },
|
|
838
|
+
itemPreswingRotY: { min: -180, max: 180, step: 5 },
|
|
839
|
+
itemSwingXRotAmount: { min: -180, max: 180, step: 5 },
|
|
840
|
+
itemSwingYRotAmount: { min: -180, max: 180, step: 5 },
|
|
841
|
+
itemSwingZRotAmount: { min: -180, max: 180, step: 5 },
|
|
842
|
+
armSwingXPosScale: { min: -2, max: 2, step: 0.1 },
|
|
843
|
+
armSwingYPosScale: { min: -2, max: 2, step: 0.1 },
|
|
844
|
+
armSwingZPosScale: { min: -2, max: 2, step: 0.1 },
|
|
845
|
+
armSwingYRotAmount: { min: -180, max: 180, step: 5 },
|
|
846
|
+
armSwingZRotAmount: { min: -180, max: 180, step: 5 },
|
|
847
|
+
armHeightScale: { min: -2, max: 2, step: 0.1 }
|
|
848
|
+
})
|
|
849
|
+
// this.debugGui.activate()
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
update() {
|
|
853
|
+
if (!this.isAnimating && !this.debugParams.animationStage) {
|
|
854
|
+
// If not animating, ensure we're at original position
|
|
855
|
+
this.handMesh.rotation.copy(this.originalRotation)
|
|
856
|
+
this.handMesh.position.copy(this.originalPosition)
|
|
857
|
+
this.handMesh.scale.copy(this.originalScale)
|
|
858
|
+
return
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const now = performance.now()
|
|
862
|
+
const deltaTime = (now - this.lastTime) / 1000
|
|
863
|
+
this.lastTime = now
|
|
864
|
+
|
|
865
|
+
// Update animation progress
|
|
866
|
+
this.animationTimer += deltaTime * 1000 // Convert to ms
|
|
867
|
+
|
|
868
|
+
// Calculate animation stage (0 to 1)
|
|
869
|
+
const stage = this.debugParams.animationStage || Math.min(this.animationTimer / this.debugParams.animationTime, 1)
|
|
870
|
+
|
|
871
|
+
if (stage >= 1) {
|
|
872
|
+
// Animation complete
|
|
873
|
+
if (this.stopRequested) {
|
|
874
|
+
// If stop was requested, actually stop now that we've completed a swing
|
|
875
|
+
this.isAnimating = false
|
|
876
|
+
this.stopRequested = false
|
|
877
|
+
this.animationTimer = 0
|
|
878
|
+
this.handMesh.rotation.copy(this.originalRotation)
|
|
879
|
+
this.handMesh.position.copy(this.originalPosition)
|
|
880
|
+
this.handMesh.scale.copy(this.originalScale)
|
|
881
|
+
return
|
|
882
|
+
}
|
|
883
|
+
// Otherwise reset timer and continue
|
|
884
|
+
this.animationTimer = 0
|
|
885
|
+
return
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Start from original transforms
|
|
889
|
+
this.handMesh.rotation.copy(this.originalRotation)
|
|
890
|
+
this.handMesh.position.copy(this.originalPosition)
|
|
891
|
+
this.handMesh.scale.copy(this.originalScale)
|
|
892
|
+
|
|
893
|
+
// Calculate swing progress
|
|
894
|
+
const swingProgress = stage
|
|
895
|
+
const sqrtProgress = Math.sqrt(swingProgress)
|
|
896
|
+
const sinProgress = Math.sin(swingProgress * this.PI)
|
|
897
|
+
const sinSqrtProgress = Math.sin(sqrtProgress * this.PI)
|
|
898
|
+
|
|
899
|
+
if (this.type === 'hand') {
|
|
900
|
+
// Hand animation
|
|
901
|
+
const xOffset = this.debugParams.armSwingXPosScale * sinSqrtProgress
|
|
902
|
+
const yOffset = this.debugParams.armSwingYPosScale * Math.sin(sqrtProgress * this.PI * 2)
|
|
903
|
+
const zOffset = this.debugParams.armSwingZPosScale * sinProgress
|
|
904
|
+
|
|
905
|
+
this.handMesh.position.x += xOffset
|
|
906
|
+
this.handMesh.position.y += yOffset + this.debugParams.armHeightScale * swingProgress
|
|
907
|
+
this.handMesh.position.z += zOffset
|
|
908
|
+
|
|
909
|
+
// Rotations
|
|
910
|
+
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.armSwingYRotAmount * sinSqrtProgress)
|
|
911
|
+
this.handMesh.rotation.z += THREE.MathUtils.degToRad(this.debugParams.armSwingZRotAmount * sinProgress)
|
|
912
|
+
} else {
|
|
913
|
+
// Item/Block animation
|
|
914
|
+
const xOffset = this.debugParams.itemSwingXPosScale * sinSqrtProgress
|
|
915
|
+
const yOffset = this.debugParams.itemSwingYPosScale * Math.sin(sqrtProgress * this.PI * 2)
|
|
916
|
+
const zOffset = this.debugParams.itemSwingZPosScale * sinProgress
|
|
917
|
+
|
|
918
|
+
this.handMesh.position.x += xOffset
|
|
919
|
+
this.handMesh.position.y += yOffset + this.debugParams.itemHeightScale * swingProgress
|
|
920
|
+
this.handMesh.position.z += zOffset
|
|
921
|
+
|
|
922
|
+
// Pre-swing rotation
|
|
923
|
+
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.itemPreswingRotY)
|
|
924
|
+
|
|
925
|
+
// Swing rotations
|
|
926
|
+
this.handMesh.rotation.x += THREE.MathUtils.degToRad(this.debugParams.itemSwingXRotAmount * sinProgress)
|
|
927
|
+
this.handMesh.rotation.y += THREE.MathUtils.degToRad(this.debugParams.itemSwingYRotAmount * sinSqrtProgress)
|
|
928
|
+
this.handMesh.rotation.z += THREE.MathUtils.degToRad(this.debugParams.itemSwingZRotAmount * sinProgress)
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
startSwing() {
|
|
933
|
+
this.stopRequested = false
|
|
934
|
+
if (this.isAnimating) return
|
|
935
|
+
|
|
936
|
+
this.isAnimating = true
|
|
937
|
+
this.animationTimer = 0
|
|
938
|
+
this.lastTime = performance.now()
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
stopSwing() {
|
|
942
|
+
if (!this.isAnimating) return
|
|
943
|
+
this.stopRequested = true
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
isCurrentlySwinging() {
|
|
947
|
+
return this.isAnimating
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
export const getBlockMeshFromModel = (material: THREE.Material, model: BlockModel, name: string, blockProvider: WorldBlockProvider, mcData: IndexedData) => {
|
|
952
|
+
const worldRenderModel = blockProvider.transformModel(model, {
|
|
953
|
+
name,
|
|
954
|
+
properties: {}
|
|
955
|
+
}) as any
|
|
956
|
+
return getThreeBlockModelGroup(material, [[worldRenderModel]], undefined, 'plains', mcData)
|
|
957
|
+
}
|