feat: some model

This commit is contained in:
TrèsAbhi committed 2025-08-10 20:36:48 -05:00
1 parent c243ebd19a
commit 8ff26d57f6
11 files changed
+154 -67

No files matched your search

@@ -198,7 +198,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
}
function handlePointerUp() {
mutateDuel((draft) => {
@@ -298,7 +298,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
}
function handlePointerUp() {
mutateTankopediaEphemeral((draft) => {
@@ -386,7 +386,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
}
function handlePointerUp() {
mutateTankopediaEphemeral((draft) => {
@@ -0,0 +1,44 @@
import { invalidate } from '@react-three/fiber';
import type { QuicklimeEvent } from 'quicklime';
import { useEffect, useRef } from 'react';
import type { Group } from 'three';
import {
modelTransformEvent,
type ModelTransformEventData,
} from '../../../../../../../../core/blitzkit/modelTransform';
import { ModelChunk } from './ModelChunk';
export function CanvasContent() {
const hull = useRef<Group>(null);
const gun = useRef<Group>(null);
useEffect(() => {
function handleModelTransformEvent(
event: QuicklimeEvent<ModelTransformEventData>,
) {
if (!gun.current) return;
gun.current.rotation.x = event.data.pitch;
invalidate();
}
modelTransformEvent.on(handleModelTransformEvent);
return () => {
modelTransformEvent.off(handleModelTransformEvent);
};
}, []);
return (
<>
<group ref={hull}>
<ModelChunk />
</group>
<group ref={gun}>
<ModelChunk onlyGun />
</group>
</>
);
}
@@ -1,46 +1,20 @@
import { Canvas } from '@react-three/fiber';
import { awaitableModelDefinitions } from '../../../../../../../../core/awaitables/modelDefinitions';
import { useModel } from '../../../../../../../../hooks/useModel';
import { Duel } from '../../../../../../../../stores/duel';
import { CanvasContent } from './CanvasContent';
const modelDefinitions = await awaitableModelDefinitions;
export const zoom = 60;
export function FlexibilityCanvas() {
const tank = Duel.use((state) => state.protagonist.tank);
const turret = Duel.use((state) => state.protagonist.turret);
const tankModel = modelDefinitions.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const { gltf } = useModel(tank.id);
const zoom = 60;
const originY = tankModel.turret_origin.z + turretModel.gun_origin.z + 1.5;
const originZ = tankModel.turret_origin.y + turretModel.gun_origin.y;
console.log(originY, originZ);
return (
<Canvas
frameloop="demand"
// frameloop="demand"
orthographic
camera={{
zoom,
position: [10, originZ, -originY],
position: [10, 0, -2],
rotation: [0, Math.PI / 2, 0],
}}
>
<ambientLight intensity={2} />
<directionalLight position={[1, 1, 1]} intensity={8} />
{/* <mesh>
<boxGeometry args={[1, 1, 1]} />
<meshNormalMaterial />
</mesh> */}
<group rotation={[-Math.PI / 2, 0, 0]}>
<primitive object={gltf.scene} />
</group>
<CanvasContent />
</Canvas>
);
}
@@ -8,6 +8,7 @@ import {
applyPitchYawLimits,
} from '../../../../../../../../core/blitz/applyPitchYawLimits';
import { hasEquipment } from '../../../../../../../../core/blitzkit/hasEquipment';
import { modelTransformEvent } from '../../../../../../../../core/blitzkit/modelTransform';
import { Var } from '../../../../../../../../core/radix/var';
import { useLocale } from '../../../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../../../stores/duel';
@@ -155,7 +156,7 @@ export function FlexibilityCard() {
const rect = container.current.getBoundingClientRect();
const u = event.clientX - rect.left - rect.width / 2;
const v = event.clientY - rect.top - rect.height / 2;
const yaw = Math.atan2(u, -v);
let yaw = Math.atan2(u, -v);
const {
equipmentMatrix,
@@ -189,9 +190,14 @@ export function FlexibilityCard() {
hasDownImprovedVerticalStabilizer,
);
setYaw(min[1]);
yaw = min[1];
const pitch = min[0];
setYaw(yaw);
setMinPitch(min[0]);
setMaxPitch(max[0]);
modelTransformEvent.dispatch({ pitch, yaw });
}}
mb="0"
>
@@ -229,7 +235,7 @@ export function FlexibilityCard() {
>
<path
fillRule="evenodd"
fill="var(--gray-6)"
fill="var(--gray-5)"
stroke="var(--gray-8)"
strokeWidth="1px"
vectorEffect="non-scaling-stroke"
@@ -262,7 +268,7 @@ export function FlexibilityCard() {
height="14rem"
style={{
borderRadius: Var('radius-1'),
background: `linear-gradient(${Var('gray-a6')}, ${Var('gray-a11')})`,
background: `linear-gradient(${Var('gray-a11')}, ${Var('gray-a6')})`,
transform: 'translateX(-50%)',
}}
/>
@@ -0,0 +1,72 @@
import { mauveDark } from '@radix-ui/colors';
import { LineBasicMaterial, MeshBasicMaterial } from 'three';
import { awaitableModelDefinitions } from '../../../../../../../../core/awaitables/modelDefinitions';
import { jsxTree } from '../../../../../../../../core/blitzkit/jsxTree';
import { useModel } from '../../../../../../../../hooks/useModel';
import { Duel } from '../../../../../../../../stores/duel';
const modelDefinitions = await awaitableModelDefinitions;
const surfaceMaterial = new MeshBasicMaterial({
color: mauveDark.mauve6,
toneMapped: false,
});
const outlineMaterial = new LineBasicMaterial({
color: mauveDark.mauve8,
toneMapped: false,
});
interface Props {
onlyGun?: boolean;
}
export function ModelChunk({ onlyGun = false }: Props) {
const tank = Duel.use((state) => state.protagonist.tank);
const turret = Duel.use((state) => state.protagonist.turret);
const gun = Duel.use((state) => state.protagonist.gun);
const tankModel = modelDefinitions.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
const originZ = tankModel.turret_origin.z + turretModel.gun_origin.z;
const originY = tankModel.turret_origin.y + turretModel.gun_origin.y;
const { gltf } = useModel(tank.id);
const nodes = Object.values(gltf.nodes);
return (
<group position={[0, -originY, originZ]} rotation={[-Math.PI / 2, 0, 0]}>
{nodes.map((node) => {
const isCurrentMantlet =
node.name ===
`gun_${gunModel.model_id.toString().padStart(2, '0')}_mask`;
const isCurrentGun =
node.name === `gun_${gunModel.model_id.toString().padStart(2, '0')}`;
const isVisible = isCurrentGun || isCurrentMantlet;
const isHull = node.name === 'hull';
const isWheel = node.name.startsWith('chassis_wheel_');
const isTrack = node.name.startsWith('chassis_track_');
const isCurrentTurret =
node.name ===
`turret_${turretModel.model_id.toString().padStart(2, '0')}`;
const isAnythingElse = isHull || isWheel || isTrack || isCurrentTurret;
if ((onlyGun && isVisible) || (!onlyGun && isAnythingElse)) {
return jsxTree(node, {
mesh(mesh, props, key) {
return (
<>
<mesh key={key} {...props} material={surfaceMaterial} />
<lineSegments material={outlineMaterial}>
<edgesGeometry args={[mesh.geometry, 30]} />
</lineSegments>
</>
);
},
});
}
})}
</group>
);
}
@@ -1,4 +1,4 @@
import { Box, Flex } from '@radix-ui/themes';
import { Box, Card, Flex } from '@radix-ui/themes';
import type { MaybeSkeletonComponentProps } from '../../../../../../../types/maybeSkeletonComponentProps';
import { FlexibilityCanvas } from './components/FlexibilityCanvas';
import { FlexibilityCard } from './components/FlexibilityCard';
@@ -7,25 +7,16 @@ export function GunFlexibilityVisualizer({
skeleton,
}: MaybeSkeletonComponentProps) {
return (
<Flex direction="column" gap="4">
<FlexibilityCard />
<Box position="relative" style={{ aspectRatio: '2 / 1' }}>
<Box position="absolute" width="100%" height="100%" top="0" left="0">
{!skeleton && <FlexibilityCanvas />}
</Box>
<Box
position="absolute"
width="100%"
height="100%"
top="0"
left="0"
style={{
boxShadow: 'inset 0 -1rem 1rem var(--gray-1)',
}}
/>
<Flex direction="column-reverse" gap="0">
<Box mt="-4" px="2">
<Card variant="classic" style={{ height: '10rem' }}>
<Box position="absolute" width="100%" height="100%" top="0" left="0">
{!skeleton && <FlexibilityCanvas />}
</Box>
</Card>
</Box>
<FlexibilityCard />
</Flex>
);
}
@@ -97,7 +97,7 @@ export function QuickInputs() {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
mutateDuel((state) => {
state.protagonist.pitch = pitch;
state.protagonist.yaw = yaw;
@@ -140,7 +140,7 @@ export function QuickInputs() {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
mutateDuel((state) => {
state.protagonist.pitch = pitch;
state.protagonist.yaw = yaw;
@@ -189,7 +189,7 @@ export function SceneProps() {
dispersion = Math.max(characteristics.dispersion * penalty, dispersion);
}
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
lastPitch = pitch;
lastYaw = yaw;
@@ -189,7 +189,7 @@ export function TankModel() {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
}
function handlePointerUp() {
mutateDuel((draft) => {
@@ -296,7 +296,7 @@ export function TankModel() {
hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
}
function handlePointerUp() {
mutateTankopediaEphemeral((draft) => {
@@ -125,7 +125,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
hasDownImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
@@ -143,7 +143,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
@@ -161,7 +161,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
hasImprovedVerticalStabilizer,
);
modelTransformEvent.emit({ pitch, yaw });
modelTransformEvent.dispatch({ pitch, yaw });
draft.protagonist.pitch = pitch;
draft.protagonist.yaw = yaw;
});
@@ -1,8 +1,8 @@
import { EventManager } from '@blitzkit/core';
import { Quicklime } from 'quicklime';
export interface ModelTransformEventData {
yaw?: number;
pitch: number;
}
export const modelTransformEvent = new EventManager<ModelTransformEventData>();
export const modelTransformEvent = new Quicklime<ModelTransformEventData>();