Skip to content

Commit d43d455

Browse files
committed
fix player inventory head rotation, fix cape
1 parent c9eac95 commit d43d455

File tree

2 files changed

+79
-18
lines changed

2 files changed

+79
-18
lines changed

src/inventoryWindows.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ export const showInventoryPlayer = () => {
5555
height: 70,
5656
scaled: true,
5757
onlyInitialScale: true,
58-
followCursor: true,
58+
},
59+
followCursor: true,
60+
followCursorCenter: {
61+
x: 51,
62+
y: 27,
5963
},
6064
// models: ['https://bucket.mcraft.fun/sitarbuckss.glb'],
6165
// debug: true,

src/react/OverlayModelViewer.tsx

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ export const modelViewerState = proxy({
2424
height: number
2525
scaled?: boolean
2626
onlyInitialScale?: boolean
27-
followCursor?: boolean
27+
}
28+
followCursor?: boolean
29+
followCursorCenter?: {
30+
x: number
31+
y: number
2832
}
2933
modelCustomization?: { [modelUrl: string]: { color?: string, opacity?: number, metalness?: number, roughness?: number, rotation?: { x?: number, y?: number, z?: number } } }
3034
resetRotationOnReleae?: boolean
@@ -33,6 +37,7 @@ export const modelViewerState = proxy({
3337
playModelAnimation?: string
3438
playModelAnimationSpeed?: number
3539
playModelAnimationLoop?: boolean
40+
followCursorCenterDebug?: boolean
3641
}
3742
})
3843
globalThis.modelViewerState = modelViewerState
@@ -105,8 +110,32 @@ export default () => {
105110
globalThis.sceneRef = sceneRef
106111

107112
// Cursor following state
108-
const cursorPosition = useRef({ x: 0, y: 0 })
113+
const cursorPosition = useRef<{ x: number, y: number }>({ x: 0, y: 0 }) // window clientX/clientY in px
109114
const isFollowingCursor = useRef(false)
115+
const getUiScaleFactor = (scaled?: boolean, onlyInitialScale?: boolean) => {
116+
return scaled ? (onlyInitialScale ? initialScale : currentScaling.scale) : 1
117+
}
118+
const windowRef = useRef<HTMLDivElement>(null)
119+
// Shared helper to compute normalized cursor from window clientX/Y taking scale & center into account
120+
const computeNormalizedFromClient = (clientX: number, clientY: number) => {
121+
const { positioning, followCursorCenter } = modelViewerState.model!
122+
const { windowWidth, windowHeight } = positioning
123+
const rect = windowRef.current?.getBoundingClientRect()
124+
const effectiveScale = rect ? (rect.width / windowWidth) : getUiScaleFactor(positioning.scaled, positioning.onlyInitialScale)
125+
126+
const centerPxX = (followCursorCenter?.x ?? (windowWidth / 2)) * effectiveScale
127+
const centerPxY = (followCursorCenter?.y ?? (windowHeight / 2)) * effectiveScale
128+
129+
const localX = rect ? (clientX - rect.left) : clientX
130+
const localY = rect ? (clientY - rect.top) : clientY
131+
132+
const denomX = rect ? (rect.width / 2) : (window.innerWidth / 2)
133+
const denomY = rect ? (rect.height / 2) : (window.innerHeight / 2)
134+
const normalizedX = (localX - centerPxX) / denomX
135+
const normalizedY = (localY - centerPxY) / denomY
136+
return { normalizedX, normalizedY }
137+
}
138+
110139

111140
// Model management state
112141
const loadedModels = useRef<Map<string, THREE.Object3D>>(new Map())
@@ -383,17 +412,19 @@ export default () => {
383412
const { playerObject } = sceneRef.current
384413
const { x, y } = cursorPosition.current
385414

386-
// Convert 0-1 cursor position to normalized coordinates (-1 to 1)
387-
const normalizedX = x * 2 - 1
388-
const normalizedY = y * 2 - 1 // Inverted: top of screen = negative pitch, bottom = positive pitch
415+
// Convert clientX/clientY to normalized coordinates centered by followCursorCenter
416+
const { normalizedX, normalizedY } = computeNormalizedFromClient(x, y)
389417

390418
// Calculate head rotation based on cursor position
391-
// Limit head movement to realistic angles
392-
const maxHeadYaw = Math.PI / 3 // 60 degrees
393-
const maxHeadPitch = Math.PI / 4 // 45 degrees
419+
// Limit head movement to ±60 degrees
420+
const maxHeadYaw = Math.PI * (60 / 180)
421+
const maxHeadPitch = Math.PI * (60 / 180)
422+
423+
const clampedX = THREE.MathUtils.clamp(normalizedX, -1, 1)
424+
const clampedY = THREE.MathUtils.clamp(normalizedY, -1, 1)
394425

395-
const headYaw = normalizedX * maxHeadYaw
396-
const headPitch = normalizedY * maxHeadPitch
426+
const headYaw = clampedX * maxHeadYaw
427+
const headPitch = clampedY * maxHeadPitch
397428

398429
// Apply head rotation with smooth interpolation
399430
const lerpFactor = 0.1 // Smooth interpolation factor
@@ -445,6 +476,8 @@ export default () => {
445476
const { playerObject, wrapper } = createPlayerObject({
446477
scale: 1 // Start with base scale, will adjust below
447478
})
479+
playerObject.ears.visible = false
480+
playerObject.cape.visible = false
448481

449482
// Enable shadows for player object
450483
wrapper.traverse((child) => {
@@ -489,7 +522,7 @@ export default () => {
489522
})
490523

491524
// Set up cursor following if enabled
492-
if (model.positioning.followCursor) {
525+
if (model.followCursor) {
493526
isFollowingCursor.current = true
494527
}
495528
}
@@ -499,12 +532,12 @@ export default () => {
499532
let lastCursorUpdate = 0
500533
let waitingRender = false
501534
const handleWindowPointerMove = (event: PointerEvent) => {
502-
if (!model.positioning.followCursor) return
535+
if (!model.followCursor) return
503536

504-
// Track cursor position as 0-1 across the entire window
537+
// Track cursor position as window clientX/clientY in px
505538
const newPosition = {
506-
x: event.clientX / window.innerWidth,
507-
y: event.clientY / window.innerHeight
539+
x: event.clientX,
540+
y: event.clientY
508541
}
509542
cursorPosition.current = newPosition
510543
globalThis.cursorPosition = newPosition // Expose for debug
@@ -520,7 +553,7 @@ export default () => {
520553
}
521554

522555
// Add window event listeners
523-
if (model.positioning.followCursor) {
556+
if (model.followCursor) {
524557
window.addEventListener('pointermove', handleWindowPointerMove)
525558
isFollowingCursor.current = true
526559
}
@@ -538,7 +571,7 @@ export default () => {
538571
if (!model.continiousRender) {
539572
controls.removeEventListener('change', render)
540573
}
541-
if (model.positioning.followCursor) {
574+
if (model.followCursor) {
542575
window.removeEventListener('pointermove', handleWindowPointerMove)
543576
}
544577
if (rafIdRef.current !== undefined) cancelAnimationFrame(rafIdRef.current)
@@ -628,6 +661,7 @@ export default () => {
628661
}}
629662
>
630663
<div
664+
ref={windowRef}
631665
className='overlay-model-viewer-window'
632666
style={{
633667
width: windowWidth,
@@ -636,6 +670,29 @@ export default () => {
636670
pointerEvents: 'none',
637671
}}
638672
>
673+
{model.followCursor && model.followCursorCenterDebug ? (
674+
(() => {
675+
const { followCursorCenter } = model
676+
const cx = (followCursorCenter?.x ?? (windowWidth / 2))
677+
const cy = (followCursorCenter?.y ?? (windowHeight / 2))
678+
const size = 6
679+
return (
680+
<div
681+
className='overlay-model-viewer-follow-cursor-center-debug'
682+
style={{
683+
position: 'absolute',
684+
left: cx - (size / 2),
685+
top: cy - (size / 2),
686+
width: size,
687+
height: size,
688+
backgroundColor: 'red',
689+
pointerEvents: 'none',
690+
zIndex: 1000,
691+
}}
692+
/>
693+
)
694+
})()
695+
) : null}
639696
<div
640697
ref={containerRef}
641698
className='overlay-model-viewer'

0 commit comments

Comments
 (0)