feat: code split containers

This commit is contained in:
TrèsAbhi committed 2024-01-03 13:07:45 -06:00
1 parent 39e4bce254
commit 56d8392f12
8 files changed
+395 -308

No files matched your search

@@ -0,0 +1,135 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { clamp } from 'lodash';
import { RefObject, useRef } from 'react';
import { Group, Mesh, Object3D, Vector3 } from 'three';
import {
PitchLimits,
YawLimits,
} from '../../../../../core/blitzkrieg/modelDefinitions';
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
interface GunContainerProps {
turretOrigin: Vector3;
gunOrigin: Vector3;
gunPitch: number;
turretYaw: number;
objects: Object3D[];
model: number;
yawLimits?: YawLimits;
pitchLimits: PitchLimits;
onPitchStart: () => void;
onPitchEnd: (pitch: number, yaw: number) => void;
turretContainer: RefObject<Group>;
}
export function GunContainer({
gunOrigin,
turretOrigin,
gunPitch,
turretYaw,
objects,
yawLimits,
onPitchStart,
turretContainer,
pitchLimits,
model,
onPitchEnd,
}: GunContainerProps) {
const canvas = useThree((state) => state.gl.domElement);
const gunContainer = useRef<Group>(null);
return (
<group
position={new Vector3()
.sub(turretOrigin)
.sub(gunOrigin)
.applyAxisAngle(new Vector3(1, 0, 0), gunPitch)
.add(turretOrigin)
.add(gunOrigin)}
rotation={[gunPitch, 0, 0]}
ref={gunContainer}
>
{objects.map((object) => {
const isCurrentMantlet =
object.name === `gun_${model.toString().padStart(2, '0')}_mask`;
const isCurrentGun =
object.name === `gun_${model.toString().padStart(2, '0')}`;
const isVisible = isCurrentGun || isCurrentMantlet;
let draftMantletPitch = 0;
let draftTurretYaw = 0;
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation();
onPitchStart();
draftMantletPitch = gunPitch;
draftTurretYaw = turretYaw;
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
if (yawLimits) {
draftTurretYaw = clamp(
draftTurretYaw + event.movementX * (Math.PI / canvas.width),
-yawLimits.max * (Math.PI / 180),
-yawLimits.min * (Math.PI / 180),
);
} else {
draftTurretYaw += event.movementX * ((2 * Math.PI) / canvas.width);
}
draftMantletPitch = clamp(
draftMantletPitch - event.movementY * (Math.PI / canvas.height),
-pitchLimits.max * (Math.PI / 180),
-pitchLimits.min * (Math.PI / 180),
);
if (gunContainer.current) {
gunContainer.current.position
.set(0, 0, 0)
.sub(turretOrigin)
.sub(gunOrigin)
.applyAxisAngle(new Vector3(1, 0, 0), draftMantletPitch)
.add(turretOrigin)
.add(gunOrigin);
gunContainer.current.rotation.x = draftMantletPitch;
}
if (turretContainer.current) {
turretContainer.current.position
.set(0, 0, 0)
.sub(turretOrigin)
.applyAxisAngle(new Vector3(0, 0, 1), draftTurretYaw)
.add(turretOrigin);
turretContainer.current.rotation.z = draftTurretYaw;
}
}
function handlePointerUp() {
onPitchEnd(
normalizeAnglePI(draftMantletPitch),
normalizeAnglePI(draftTurretYaw),
);
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
}
if (!isVisible) return null;
return (
<mesh
visible={isVisible}
onPointerDown={handlePointerDown}
children={resolveJsxTree(object)}
key={object.uuid}
castShadow
receiveShadow
geometry={(object as Mesh).geometry}
material={(object as Mesh).material}
position={object.position}
rotation={object.rotation}
scale={object.scale}
/>
);
})}
</group>
);
}
@@ -0,0 +1,74 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
import { Group, Mesh, Object3D } from 'three';
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
interface HullContainerProps {
children: ReactNode;
yaw: number;
objects: Object3D[];
onYawStart: () => void;
onYawEnd: (yaw: number) => void;
}
export const HullContainer = forwardRef<Group, HullContainerProps>(
({ children, yaw, objects, onYawStart, onYawEnd }, ref) => {
const canvas = useThree((state) => state.gl.domElement);
const hullContainer = useRef<Group>(null);
useImperativeHandle(ref, () => hullContainer.current!);
return (
<group ref={hullContainer} rotation={[-Math.PI / 2, 0, yaw]}>
{objects.map((object) => {
const isHull = object.name === 'hull';
const isWheel = object.name.startsWith('chassis_wheel_');
const isTrack = object.name.startsWith('chassis_track_');
const isVisible = isHull || isWheel || isTrack;
let draftHullYaw = 0;
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation();
draftHullYaw = yaw;
onYawStart();
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
draftHullYaw += event.movementX * ((2 * Math.PI) / canvas.width);
if (hullContainer.current) {
hullContainer.current.rotation.z = draftHullYaw;
}
}
function handlePointerUp() {
onYawEnd(normalizeAnglePI(draftHullYaw));
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
}
if (!isVisible) return null;
return (
<mesh
children={resolveJsxTree(object)}
key={object.uuid}
castShadow
receiveShadow
geometry={(object as Mesh).geometry}
material={(object as Mesh).material}
position={object.position}
rotation={object.rotation}
scale={object.scale}
onPointerDown={handlePointerDown}
/>
);
})}
{children}
</group>
);
},
);
@@ -0,0 +1,26 @@
import { Color } from 'three';
import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper';
export function SceneProps() {
return (
<>
<InfiniteGridHelper
size1={1 / 5}
size2={1}
distance={20}
color={new Color('#ffffff')}
/>
<InfiniteGridHelper
position={[0, 1e-4, 0]}
size1={0}
size2={100}
distance={25}
color={new Color('red')}
/>
<directionalLight position={[1, 1, -1]} intensity={1} castShadow />
<directionalLight position={[-1, 1, 1]} intensity={2} castShadow />
<ambientLight intensity={0.25} />
</>
);
}
@@ -0,0 +1,102 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
import { Group, Mesh, Object3D, Vector3 } from 'three';
import { resolveJsxTree } from '../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
interface TurretContainerProps {
origin: Vector3;
objects: Object3D[];
children: ReactNode;
yaw: number;
model: number;
onYawStart: () => void;
onYawEnd: (yaw: number) => void;
}
export const TurretContainer = forwardRef(
(
{
origin,
yaw,
children,
objects,
model,
onYawEnd,
onYawStart,
}: TurretContainerProps,
ref,
) => {
const canvas = useThree((state) => state.gl.domElement);
const turretContainer = useRef<Group>(null);
useImperativeHandle(ref, () => turretContainer.current!);
return (
<group
position={new Vector3()
.sub(origin)
.applyAxisAngle(new Vector3(0, 0, 1), yaw)
.add(origin)}
rotation={[0, 0, yaw]}
ref={turretContainer}
>
{objects.map((object) => {
const isTurret = object.name.startsWith('turret_');
const isCurrentTurret =
object.name === `turret_${model.toString().padStart(2, '0')}`;
const isVisible = isCurrentTurret;
let draftYaw = 0;
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation();
if (isTurret) {
draftYaw = yaw;
onYawStart();
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}
}
function handlePointerMove(event: PointerEvent) {
draftYaw += event.movementX * ((2 * Math.PI) / canvas.width);
if (turretContainer.current) {
turretContainer.current.position
.set(0, 0, 0)
.sub(origin)
.applyAxisAngle(new Vector3(0, 0, 1), draftYaw)
.add(origin);
turretContainer.current.rotation.z = draftYaw;
}
}
function handlePointerUp() {
onYawEnd(normalizeAnglePI(draftYaw));
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
}
if (!isVisible) return null;
return (
<mesh
visible={isVisible}
onPointerDown={handlePointerDown}
children={resolveJsxTree(object)}
key={object.uuid}
castShadow
receiveShadow
geometry={(object as Mesh).geometry}
material={(object as Mesh).material}
position={object.position}
rotation={object.rotation}
scale={object.scale}
/>
);
})}
{children}
</group>
);
},
);