mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: turret rotation
This commit is contained in:
1 parent
c9d5b46ae8
commit
73f7e84192
3 files changed
+163
-32
No files matched your search
@@ -14,8 +14,7 @@ export function TankAlignment({ model }: TankAlignmentProps) {
|
|||||||
const diameter = Math.max(box.max.x - box.min.x, box.max.z - box.min.z);
|
const diameter = Math.max(box.max.x - box.min.x, box.max.z - box.min.z);
|
||||||
const radius = diameter / 2;
|
const radius = diameter / 2;
|
||||||
|
|
||||||
model.current?.position.set(0, -(box.min.y + box.max.y) / 2, 0);
|
camera.position.set(0, (box.min.y + box.max.y) / 2, -10);
|
||||||
// camera.position.set(radius * -0.25, radius * 0.1, radius * -1.5);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { Environment, OrbitControls } from '@react-three/drei';
|
import { Environment, OrbitControls } from '@react-three/drei';
|
||||||
import { Canvas, useLoader } from '@react-three/fiber';
|
import { Canvas, ThreeEvent, useLoader } from '@react-three/fiber';
|
||||||
import { EffectComposer, SSAO } from '@react-three/postprocessing';
|
import { EffectComposer, SSAO } from '@react-three/postprocessing';
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { use, useEffect, useRef, useState } from 'react';
|
import { use, useEffect, useRef, useState } from 'react';
|
||||||
import { Group, Mesh } from 'three';
|
import { Group, Mesh, Vector3 } from 'three';
|
||||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||||
import { Flag } from '../../../../components/Flag';
|
import { Flag } from '../../../../components/Flag';
|
||||||
import { ModuleButtons } from '../../../../components/ModuleButton';
|
import { ModuleButtons } from '../../../../components/ModuleButton';
|
||||||
@@ -60,7 +60,19 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
const [versusTurret, setVersusTurret] = useState(versusTank.turrets.at(-1)!);
|
const [versusTurret, setVersusTurret] = useState(versusTank.turrets.at(-1)!);
|
||||||
const [versusGun, setVersusGun] = useState(versusTurret.guns.at(-1)!);
|
const [versusGun, setVersusGun] = useState(versusTurret.guns.at(-1)!);
|
||||||
const [versusTankTab, setVersusTankTab] = useState('search');
|
const [versusTankTab, setVersusTankTab] = useState('search');
|
||||||
|
const turretObject3D = useRef<Mesh>(null);
|
||||||
|
const mantletObject3D = useRef<Mesh>(null);
|
||||||
|
const gunObject3D = useRef<Mesh>(null);
|
||||||
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const gltf = useLoader(GLTFLoader, '/test/5137.glb');
|
const gltf = useLoader(GLTFLoader, '/test/5137.glb');
|
||||||
|
const turretOrigin = new Vector3(
|
||||||
|
tank.turretOrigin[0],
|
||||||
|
tank.turretOrigin[1],
|
||||||
|
-tank.turretOrigin[2],
|
||||||
|
).applyAxisAngle(new Vector3(1, 0, 0), Math.PI / 2);
|
||||||
|
const gunOrigin = new Vector3(...turret.gunOrigin);
|
||||||
|
const [turretRotation, setTurretRotation] = useState(0);
|
||||||
|
const [orbitControlsEnabled, setOrbitControlsEnabled] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!turret.guns.some(({ id }) => gun.id === id)) {
|
if (!turret.guns.some(({ id }) => gun.id === id)) {
|
||||||
@@ -110,11 +122,13 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
|
|
||||||
<div style={{ height: '50vh', maxHeight: 576 }}>
|
<div style={{ height: '50vh', maxHeight: 576 }}>
|
||||||
<Canvas
|
<Canvas
|
||||||
|
ref={canvas}
|
||||||
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={model} />
|
||||||
<OrbitControls />
|
<OrbitControls enabled={orbitControlsEnabled} />
|
||||||
|
<gridHelper />
|
||||||
|
|
||||||
{/* I really like apartment, dawn, and sunset */}
|
{/* I really like apartment, dawn, and sunset */}
|
||||||
<Environment preset="apartment" />
|
<Environment preset="apartment" />
|
||||||
@@ -132,34 +146,152 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
</EffectComposer>
|
</EffectComposer>
|
||||||
|
|
||||||
<group ref={model} rotation={[-Math.PI / 2, 0, 0]}>
|
<group ref={model} rotation={[-Math.PI / 2, 0, 0]}>
|
||||||
{gltf.scene.children[0].children.map((child) => (
|
{gltf.scene.children[0].children.map((child) => {
|
||||||
<mesh
|
const isHull = child.name === 'hull';
|
||||||
visible={
|
const isTurret = child.name.startsWith('turret_');
|
||||||
child.name === 'hull' ||
|
const isMantlet =
|
||||||
child.name.startsWith('chassis_track_') ||
|
child.name.startsWith('gun_') &&
|
||||||
child.name.startsWith('chassis_wheel_') ||
|
child.name.endsWith('_mask');
|
||||||
(child.name.startsWith('turret_') &&
|
const isGun = child.name.startsWith('gun_') && !isMantlet;
|
||||||
child.name ===
|
const isVisible =
|
||||||
`turret_${turret.model
|
isHull ||
|
||||||
.toString()
|
child.name.startsWith('chassis_track_') ||
|
||||||
.padStart(2, '0')}`) ||
|
child.name.startsWith('chassis_wheel_') ||
|
||||||
(child.name.startsWith('gun_') &&
|
(child.name.startsWith('turret_') &&
|
||||||
child.name ===
|
|
||||||
`gun_${gun.model.toString().padStart(2, '0')}`) ||
|
|
||||||
child.name ===
|
child.name ===
|
||||||
`gun_${gun.model.toString().padStart(2, '0')}_mask`
|
`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;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
children={resolveJsxTree(child as Mesh)}
|
}
|
||||||
key={child.uuid}
|
function handlePointerMove(event: PointerEvent) {
|
||||||
castShadow
|
event.stopPropagation();
|
||||||
receiveShadow
|
event.preventDefault();
|
||||||
geometry={(child as Mesh).geometry}
|
|
||||||
material={(child as Mesh).material}
|
const rotationSpeed =
|
||||||
position={child.position}
|
(2 * Math.PI) / canvas.current!.width;
|
||||||
rotation={child.rotation}
|
const deltaTurretRotation =
|
||||||
scale={child.scale}
|
event.movementX * rotationSpeed;
|
||||||
/>
|
draftTurretRotation += deltaTurretRotation;
|
||||||
))}
|
|
||||||
|
if (isTurret) {
|
||||||
|
if (turretObject3D.current) {
|
||||||
|
turretObject3D.current.position
|
||||||
|
.sub(turretOrigin)
|
||||||
|
.applyAxisAngle(
|
||||||
|
new Vector3(0, 0, 1),
|
||||||
|
deltaTurretRotation,
|
||||||
|
)
|
||||||
|
.add(turretOrigin);
|
||||||
|
turretObject3D.current.rotation.z =
|
||||||
|
draftTurretRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mantletObject3D.current) {
|
||||||
|
mantletObject3D.current.position
|
||||||
|
.sub(turretOrigin)
|
||||||
|
.applyAxisAngle(
|
||||||
|
new Vector3(0, 0, 1),
|
||||||
|
deltaTurretRotation,
|
||||||
|
)
|
||||||
|
.add(turretOrigin);
|
||||||
|
mantletObject3D.current.rotation.z =
|
||||||
|
draftTurretRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gunObject3D.current) {
|
||||||
|
gunObject3D.current.position
|
||||||
|
.sub(turretOrigin)
|
||||||
|
.applyAxisAngle(
|
||||||
|
new Vector3(0, 0, 1),
|
||||||
|
deltaTurretRotation,
|
||||||
|
)
|
||||||
|
.add(turretOrigin);
|
||||||
|
gunObject3D.current.rotation.z =
|
||||||
|
draftTurretRotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handlePointerUp(event: PointerEvent) {
|
||||||
|
setOrbitControlsEnabled(true);
|
||||||
|
setTurretRotation(draftTurretRotation);
|
||||||
|
|
||||||
|
window.removeEventListener(
|
||||||
|
'pointermove',
|
||||||
|
handlePointerMove,
|
||||||
|
);
|
||||||
|
window.removeEventListener(
|
||||||
|
'pointerup',
|
||||||
|
handlePointerUp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
visible={isVisible}
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
children={resolveJsxTree(child as Mesh)}
|
||||||
|
key={child.uuid}
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
geometry={(child as Mesh).geometry}
|
||||||
|
material={(child as Mesh).material}
|
||||||
|
position={position}
|
||||||
|
rotation={rotation.toArray()}
|
||||||
|
scale={child.scale}
|
||||||
|
ref={
|
||||||
|
isTurret
|
||||||
|
? turretObject3D
|
||||||
|
: isGun
|
||||||
|
? gunObject3D
|
||||||
|
: isMantlet
|
||||||
|
? mantletObject3D
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</group>
|
</group>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export interface TurretDefinition {
|
|||||||
}
|
}
|
||||||
export type GunDefinition = GunDefinitionBase &
|
export type GunDefinition = GunDefinitionBase &
|
||||||
(GunDefinitionRegular | GunDefinitionAutoLoader | GunDefinitionAutoReloader);
|
(GunDefinitionRegular | GunDefinitionAutoLoader | GunDefinitionAutoReloader);
|
||||||
interface GunDefinitionBase {
|
interface GunDefinitionBase {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
tier: Tier;
|
tier: Tier;
|
||||||
|
|||||||
Reference in new issue
Block a user