mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: hull rotation
This commit is contained in:
1 parent
b40b7b2472
commit
88cc37a6af
1 file changed
+205
-85
@@ -53,7 +53,8 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
const [crew, setCrew] = useState(100);
|
const [crew, setCrew] = useState(100);
|
||||||
const [mode, setMode] = useState('model');
|
const [mode, setMode] = useState('model');
|
||||||
const versusTankSearchInput = useRef<HTMLInputElement>(null);
|
const versusTankSearchInput = useRef<HTMLInputElement>(null);
|
||||||
const model = useRef<Group>(null);
|
const hullContainer = useRef<Group>(null);
|
||||||
|
const turretContainer = useRef<Group>(null);
|
||||||
const [versusTankSearchResults, setVersusTankSearchResults] = useState<
|
const [versusTankSearchResults, setVersusTankSearchResults] = useState<
|
||||||
number[]
|
number[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -73,8 +74,10 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
).applyAxisAngle(new Vector3(1, 0, 0), Math.PI / 2);
|
).applyAxisAngle(new Vector3(1, 0, 0), Math.PI / 2);
|
||||||
const gunOrigin = new Vector3(...turret.gunOrigin);
|
const gunOrigin = new Vector3(...turret.gunOrigin);
|
||||||
const [turretRotation, setTurretRotation] = useState(0);
|
const [turretRotation, setTurretRotation] = useState(0);
|
||||||
|
const [hullRotation, setHullRotation] = useState(0);
|
||||||
const [orbitControlsEnabled, setOrbitControlsEnabled] = useState(true);
|
const [orbitControlsEnabled, setOrbitControlsEnabled] = useState(true);
|
||||||
const turretRotationInput = useRef<HTMLInputElement>(null);
|
const turretRotationInput = useRef<HTMLInputElement>(null);
|
||||||
|
const hullRotationInput = useRef<HTMLInputElement>(null);
|
||||||
const gunRotationInput = useRef<HTMLInputElement>(null);
|
const gunRotationInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -97,6 +100,11 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
turretRotation * (180 / Math.PI),
|
turretRotation * (180 / Math.PI),
|
||||||
)}`;
|
)}`;
|
||||||
}, [turretRotation]);
|
}, [turretRotation]);
|
||||||
|
useEffect(() => {
|
||||||
|
hullRotationInput.current!.value = `${Math.round(
|
||||||
|
hullRotation * (180 / Math.PI),
|
||||||
|
)}`;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="purple">
|
<PageWrapper color="purple">
|
||||||
@@ -134,7 +142,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
onPointerDown={(event) => event.preventDefault()}
|
onPointerDown={(event) => event.preventDefault()}
|
||||||
camera={{ fov: 20, position: [4, 0, -16] }}
|
camera={{ fov: 20, position: [4, 0, -16] }}
|
||||||
>
|
>
|
||||||
<TankAlignment model={model} />
|
<TankAlignment model={hullContainer} />
|
||||||
<OrbitControls enabled={orbitControlsEnabled} />
|
<OrbitControls enabled={orbitControlsEnabled} />
|
||||||
<gridHelper />
|
<gridHelper />
|
||||||
|
|
||||||
@@ -153,57 +161,24 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
/>
|
/>
|
||||||
</EffectComposer>
|
</EffectComposer>
|
||||||
|
|
||||||
<group ref={model} rotation={[-Math.PI / 2, 0, 0]}>
|
<group
|
||||||
|
ref={hullContainer}
|
||||||
|
rotation={[-Math.PI / 2, 0, hullRotation]}
|
||||||
|
>
|
||||||
{gltf.scene.children[0].children.map((child) => {
|
{gltf.scene.children[0].children.map((child) => {
|
||||||
const isHull = child.name === 'hull';
|
const isHull = child.name === 'hull';
|
||||||
const isTurret = child.name.startsWith('turret_');
|
const isWheel = child.name.startsWith('chassis_wheel_');
|
||||||
const isMantlet =
|
const isTrack = child.name.startsWith('chassis_track_');
|
||||||
child.name.startsWith('gun_') &&
|
const isVisible = isHull || isWheel || isTrack;
|
||||||
child.name.endsWith('_mask');
|
|
||||||
const isGun = child.name.startsWith('gun_') && !isMantlet;
|
|
||||||
const isVisible =
|
|
||||||
isHull ||
|
|
||||||
child.name.startsWith('chassis_track_') ||
|
|
||||||
child.name.startsWith('chassis_wheel_') ||
|
|
||||||
(child.name.startsWith('turret_') &&
|
|
||||||
child.name ===
|
|
||||||
`turret_${turret.model
|
|
||||||
.toString()
|
|
||||||
.padStart(2, '0')}`) ||
|
|
||||||
(child.name.startsWith('gun_') &&
|
|
||||||
child.name ===
|
|
||||||
`gun_${gun.model.toString().padStart(2, '0')}`) ||
|
|
||||||
child.name ===
|
|
||||||
`gun_${gun.model.toString().padStart(2, '0')}_mask`;
|
|
||||||
let draftTurretRotation = 0;
|
let draftTurretRotation = 0;
|
||||||
const position = child.position.clone();
|
|
||||||
const rotation = new Vector3().setFromEuler(
|
|
||||||
child.rotation,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isVisible) return null;
|
|
||||||
|
|
||||||
if (isTurret || isMantlet) {
|
|
||||||
position
|
|
||||||
.sub(turretOrigin)
|
|
||||||
.applyAxisAngle(new Vector3(0, 0, 1), turretRotation)
|
|
||||||
.add(turretOrigin);
|
|
||||||
rotation.add(new Vector3(0, 0, turretRotation));
|
|
||||||
} else if (isGun) {
|
|
||||||
position
|
|
||||||
.sub(turretOrigin)
|
|
||||||
.applyAxisAngle(new Vector3(0, 0, 1), turretRotation)
|
|
||||||
.add(turretOrigin);
|
|
||||||
rotation.add(new Vector3(0, 0, turretRotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePointerDown(
|
function handlePointerDown(
|
||||||
event: ThreeEvent<PointerEvent>,
|
event: ThreeEvent<PointerEvent>,
|
||||||
) {
|
) {
|
||||||
if (isTurret) {
|
if (isHull) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
draftTurretRotation = turretRotation;
|
draftTurretRotation = hullRotation;
|
||||||
|
|
||||||
setOrbitControlsEnabled(false);
|
setOrbitControlsEnabled(false);
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
@@ -223,7 +198,125 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
event.movementX * rotationSpeed;
|
event.movementX * rotationSpeed;
|
||||||
draftTurretRotation += deltaTurretRotation;
|
draftTurretRotation += deltaTurretRotation;
|
||||||
|
|
||||||
if (isTurret) {
|
if (hullContainer.current) {
|
||||||
|
hullContainer.current.rotation.z =
|
||||||
|
draftTurretRotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handlePointerUp(event: PointerEvent) {
|
||||||
|
setOrbitControlsEnabled(true);
|
||||||
|
setHullRotation(normalizeAngle180(draftTurretRotation));
|
||||||
|
|
||||||
|
window.removeEventListener(
|
||||||
|
'pointermove',
|
||||||
|
handlePointerMove,
|
||||||
|
);
|
||||||
|
window.removeEventListener(
|
||||||
|
'pointerup',
|
||||||
|
handlePointerUp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
visible={isVisible}
|
||||||
|
children={resolveJsxTree(child as Mesh)}
|
||||||
|
key={child.uuid}
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
geometry={(child as Mesh).geometry}
|
||||||
|
material={(child as Mesh).material}
|
||||||
|
position={child.position}
|
||||||
|
rotation={child.rotation}
|
||||||
|
scale={child.scale}
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<group
|
||||||
|
ref={turretContainer}
|
||||||
|
// rotation={[-Math.PI / 2, 0, 0]}
|
||||||
|
>
|
||||||
|
{gltf.scene.children[0].children.map((child) => {
|
||||||
|
const isTurret = child.name.startsWith('turret_');
|
||||||
|
const isCurrentTurret =
|
||||||
|
isTurret &&
|
||||||
|
child.name ===
|
||||||
|
`turret_${turret.model
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0')}`;
|
||||||
|
const isMantlet =
|
||||||
|
child.name.startsWith('gun_') &&
|
||||||
|
child.name.endsWith('_mask');
|
||||||
|
const isCurrentMantlet =
|
||||||
|
child.name ===
|
||||||
|
`gun_${gun.model.toString().padStart(2, '0')}_mask`;
|
||||||
|
const isGun =
|
||||||
|
child.name.startsWith('gun_') && !isMantlet;
|
||||||
|
const isCurrentGun =
|
||||||
|
isGun &&
|
||||||
|
child.name ===
|
||||||
|
`gun_${gun.model.toString().padStart(2, '0')}`;
|
||||||
|
const isVisible =
|
||||||
|
isCurrentTurret || isCurrentGun || isCurrentMantlet;
|
||||||
|
let draftTurretRotation = 0;
|
||||||
|
const position = child.position.clone();
|
||||||
|
const rotation = new Vector3().setFromEuler(
|
||||||
|
child.rotation,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
|
if (isTurret || isMantlet) {
|
||||||
|
position
|
||||||
|
.sub(turretOrigin)
|
||||||
|
.applyAxisAngle(
|
||||||
|
new Vector3(0, 0, 1),
|
||||||
|
turretRotation,
|
||||||
|
)
|
||||||
|
.add(turretOrigin);
|
||||||
|
rotation.add(new Vector3(0, 0, turretRotation));
|
||||||
|
} else if (isGun) {
|
||||||
|
position
|
||||||
|
.sub(turretOrigin)
|
||||||
|
.applyAxisAngle(
|
||||||
|
new Vector3(0, 0, 1),
|
||||||
|
turretRotation,
|
||||||
|
)
|
||||||
|
.add(turretOrigin);
|
||||||
|
rotation.add(new Vector3(0, 0, turretRotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerDown(
|
||||||
|
event: ThreeEvent<PointerEvent>,
|
||||||
|
) {
|
||||||
|
if (isTurret) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
draftTurretRotation = turretRotation;
|
||||||
|
|
||||||
|
setOrbitControlsEnabled(false);
|
||||||
|
window.addEventListener(
|
||||||
|
'pointermove',
|
||||||
|
handlePointerMove,
|
||||||
|
);
|
||||||
|
window.addEventListener(
|
||||||
|
'pointerup',
|
||||||
|
handlePointerUp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handlePointerMove(event: PointerEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const rotationSpeed =
|
||||||
|
(2 * Math.PI) / canvas.current!.width;
|
||||||
|
const deltaTurretRotation =
|
||||||
|
event.movementX * rotationSpeed;
|
||||||
|
draftTurretRotation += deltaTurretRotation;
|
||||||
|
|
||||||
if (turretObject3D.current) {
|
if (turretObject3D.current) {
|
||||||
turretObject3D.current.position
|
turretObject3D.current.position
|
||||||
.sub(turretOrigin)
|
.sub(turretOrigin)
|
||||||
@@ -260,48 +353,48 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
draftTurretRotation;
|
draftTurretRotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
function handlePointerUp(event: PointerEvent) {
|
||||||
function handlePointerUp(event: PointerEvent) {
|
setOrbitControlsEnabled(true);
|
||||||
setOrbitControlsEnabled(true);
|
setTurretRotation(
|
||||||
setTurretRotation(
|
normalizeAngle180(draftTurretRotation),
|
||||||
normalizeAngle180(draftTurretRotation),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
window.removeEventListener(
|
window.removeEventListener(
|
||||||
'pointermove',
|
'pointermove',
|
||||||
handlePointerMove,
|
handlePointerMove,
|
||||||
);
|
);
|
||||||
window.removeEventListener(
|
window.removeEventListener(
|
||||||
'pointerup',
|
'pointerup',
|
||||||
handlePointerUp,
|
handlePointerUp,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh
|
<mesh
|
||||||
visible={isVisible}
|
visible={isVisible}
|
||||||
onPointerDown={handlePointerDown}
|
onPointerDown={handlePointerDown}
|
||||||
children={resolveJsxTree(child as Mesh)}
|
children={resolveJsxTree(child as Mesh)}
|
||||||
key={child.uuid}
|
key={child.uuid}
|
||||||
castShadow
|
castShadow
|
||||||
receiveShadow
|
receiveShadow
|
||||||
geometry={(child as Mesh).geometry}
|
geometry={(child as Mesh).geometry}
|
||||||
material={(child as Mesh).material}
|
material={(child as Mesh).material}
|
||||||
position={position}
|
position={position}
|
||||||
rotation={rotation.toArray()}
|
rotation={rotation.toArray()}
|
||||||
scale={child.scale}
|
scale={child.scale}
|
||||||
ref={
|
ref={
|
||||||
isTurret
|
isTurret
|
||||||
? turretObject3D
|
? turretObject3D
|
||||||
: isGun
|
: isGun
|
||||||
? gunObject3D
|
? gunObject3D
|
||||||
: isMantlet
|
: isMantlet
|
||||||
? mantletObject3D
|
? mantletObject3D
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
@@ -322,6 +415,33 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
bottom: 16,
|
bottom: 16,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<TextField.Root
|
||||||
|
variant="surface"
|
||||||
|
style={{
|
||||||
|
width: 115,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextField.Slot>Hull</TextField.Slot>
|
||||||
|
<TextField.Input
|
||||||
|
onBlur={() => {
|
||||||
|
setHullRotation(
|
||||||
|
normalizeAngle180(
|
||||||
|
Number(hullRotationInput.current?.value) *
|
||||||
|
(Math.PI / 180),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
hullRotationInput.current?.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
ref={hullRotationInput}
|
||||||
|
style={{ textAlign: 'right' }}
|
||||||
|
/>
|
||||||
|
<TextField.Slot>°</TextField.Slot>
|
||||||
|
</TextField.Root>
|
||||||
|
|
||||||
<TextField.Root
|
<TextField.Root
|
||||||
variant="surface"
|
variant="surface"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
Reference in new issue
Block a user