mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: some model
This commit is contained in:
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,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateDuel((draft) => {
|
mutateDuel((draft) => {
|
||||||
@@ -298,7 +298,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
@@ -386,7 +386,7 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
|
|||||||
+44
@@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
+5
-31
@@ -1,46 +1,20 @@
|
|||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas } from '@react-three/fiber';
|
||||||
import { awaitableModelDefinitions } from '../../../../../../../../core/awaitables/modelDefinitions';
|
import { CanvasContent } from './CanvasContent';
|
||||||
import { useModel } from '../../../../../../../../hooks/useModel';
|
|
||||||
import { Duel } from '../../../../../../../../stores/duel';
|
|
||||||
|
|
||||||
const modelDefinitions = await awaitableModelDefinitions;
|
export const zoom = 60;
|
||||||
|
|
||||||
export function FlexibilityCanvas() {
|
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 (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
frameloop="demand"
|
// frameloop="demand"
|
||||||
orthographic
|
orthographic
|
||||||
camera={{
|
camera={{
|
||||||
zoom,
|
zoom,
|
||||||
position: [10, originZ, -originY],
|
position: [10, 0, -2],
|
||||||
rotation: [0, Math.PI / 2, 0],
|
rotation: [0, Math.PI / 2, 0],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ambientLight intensity={2} />
|
<CanvasContent />
|
||||||
<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>
|
|
||||||
</Canvas>
|
</Canvas>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+10
-4
@@ -8,6 +8,7 @@ import {
|
|||||||
applyPitchYawLimits,
|
applyPitchYawLimits,
|
||||||
} from '../../../../../../../../core/blitz/applyPitchYawLimits';
|
} from '../../../../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { hasEquipment } from '../../../../../../../../core/blitzkit/hasEquipment';
|
import { hasEquipment } from '../../../../../../../../core/blitzkit/hasEquipment';
|
||||||
|
import { modelTransformEvent } from '../../../../../../../../core/blitzkit/modelTransform';
|
||||||
import { Var } from '../../../../../../../../core/radix/var';
|
import { Var } from '../../../../../../../../core/radix/var';
|
||||||
import { useLocale } from '../../../../../../../../hooks/useLocale';
|
import { useLocale } from '../../../../../../../../hooks/useLocale';
|
||||||
import { Duel } from '../../../../../../../../stores/duel';
|
import { Duel } from '../../../../../../../../stores/duel';
|
||||||
@@ -155,7 +156,7 @@ export function FlexibilityCard() {
|
|||||||
const rect = container.current.getBoundingClientRect();
|
const rect = container.current.getBoundingClientRect();
|
||||||
const u = event.clientX - rect.left - rect.width / 2;
|
const u = event.clientX - rect.left - rect.width / 2;
|
||||||
const v = event.clientY - rect.top - rect.height / 2;
|
const v = event.clientY - rect.top - rect.height / 2;
|
||||||
const yaw = Math.atan2(u, -v);
|
let yaw = Math.atan2(u, -v);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
equipmentMatrix,
|
equipmentMatrix,
|
||||||
@@ -189,9 +190,14 @@ export function FlexibilityCard() {
|
|||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
|
|
||||||
setYaw(min[1]);
|
yaw = min[1];
|
||||||
|
const pitch = min[0];
|
||||||
|
|
||||||
|
setYaw(yaw);
|
||||||
setMinPitch(min[0]);
|
setMinPitch(min[0]);
|
||||||
setMaxPitch(max[0]);
|
setMaxPitch(max[0]);
|
||||||
|
|
||||||
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}}
|
}}
|
||||||
mb="0"
|
mb="0"
|
||||||
>
|
>
|
||||||
@@ -229,7 +235,7 @@ export function FlexibilityCard() {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
fill="var(--gray-6)"
|
fill="var(--gray-5)"
|
||||||
stroke="var(--gray-8)"
|
stroke="var(--gray-8)"
|
||||||
strokeWidth="1px"
|
strokeWidth="1px"
|
||||||
vectorEffect="non-scaling-stroke"
|
vectorEffect="non-scaling-stroke"
|
||||||
@@ -262,7 +268,7 @@ export function FlexibilityCard() {
|
|||||||
height="14rem"
|
height="14rem"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: Var('radius-1'),
|
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%)',
|
transform: 'translateX(-50%)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+72
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
+10
-19
@@ -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 type { MaybeSkeletonComponentProps } from '../../../../../../../types/maybeSkeletonComponentProps';
|
||||||
import { FlexibilityCanvas } from './components/FlexibilityCanvas';
|
import { FlexibilityCanvas } from './components/FlexibilityCanvas';
|
||||||
import { FlexibilityCard } from './components/FlexibilityCard';
|
import { FlexibilityCard } from './components/FlexibilityCard';
|
||||||
@@ -7,25 +7,16 @@ export function GunFlexibilityVisualizer({
|
|||||||
skeleton,
|
skeleton,
|
||||||
}: MaybeSkeletonComponentProps) {
|
}: MaybeSkeletonComponentProps) {
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="4">
|
<Flex direction="column-reverse" gap="0">
|
||||||
<FlexibilityCard />
|
<Box mt="-4" px="2">
|
||||||
|
<Card variant="classic" style={{ height: '10rem' }}>
|
||||||
<Box position="relative" style={{ aspectRatio: '2 / 1' }}>
|
<Box position="absolute" width="100%" height="100%" top="0" left="0">
|
||||||
<Box position="absolute" width="100%" height="100%" top="0" left="0">
|
{!skeleton && <FlexibilityCanvas />}
|
||||||
{!skeleton && <FlexibilityCanvas />}
|
</Box>
|
||||||
</Box>
|
</Card>
|
||||||
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
top="0"
|
|
||||||
left="0"
|
|
||||||
style={{
|
|
||||||
boxShadow: 'inset 0 -1rem 1rem var(--gray-1)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<FlexibilityCard />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -97,7 +97,7 @@ export function QuickInputs() {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
mutateDuel((state) => {
|
mutateDuel((state) => {
|
||||||
state.protagonist.pitch = pitch;
|
state.protagonist.pitch = pitch;
|
||||||
state.protagonist.yaw = yaw;
|
state.protagonist.yaw = yaw;
|
||||||
@@ -140,7 +140,7 @@ export function QuickInputs() {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
mutateDuel((state) => {
|
mutateDuel((state) => {
|
||||||
state.protagonist.pitch = pitch;
|
state.protagonist.pitch = pitch;
|
||||||
state.protagonist.yaw = yaw;
|
state.protagonist.yaw = yaw;
|
||||||
|
|||||||
+1
-1
@@ -189,7 +189,7 @@ export function SceneProps() {
|
|||||||
dispersion = Math.max(characteristics.dispersion * penalty, dispersion);
|
dispersion = Math.max(characteristics.dispersion * penalty, dispersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
lastPitch = pitch;
|
lastPitch = pitch;
|
||||||
lastYaw = yaw;
|
lastYaw = yaw;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -189,7 +189,7 @@ export function TankModel() {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateDuel((draft) => {
|
mutateDuel((draft) => {
|
||||||
@@ -296,7 +296,7 @@ export function TankModel() {
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
}
|
}
|
||||||
function handlePointerUp() {
|
function handlePointerUp() {
|
||||||
mutateTankopediaEphemeral((draft) => {
|
mutateTankopediaEphemeral((draft) => {
|
||||||
|
|||||||
+3
-3
@@ -125,7 +125,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
hasDownImprovedVerticalStabilizer,
|
hasDownImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
draft.protagonist.pitch = pitch;
|
||||||
draft.protagonist.yaw = yaw;
|
draft.protagonist.yaw = yaw;
|
||||||
});
|
});
|
||||||
@@ -143,7 +143,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
draft.protagonist.pitch = pitch;
|
||||||
draft.protagonist.yaw = yaw;
|
draft.protagonist.yaw = yaw;
|
||||||
});
|
});
|
||||||
@@ -161,7 +161,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
hasImprovedVerticalStabilizer,
|
hasImprovedVerticalStabilizer,
|
||||||
);
|
);
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.dispatch({ pitch, yaw });
|
||||||
draft.protagonist.pitch = pitch;
|
draft.protagonist.pitch = pitch;
|
||||||
draft.protagonist.yaw = yaw;
|
draft.protagonist.yaw = yaw;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { EventManager } from '@blitzkit/core';
|
import { Quicklime } from 'quicklime';
|
||||||
|
|
||||||
export interface ModelTransformEventData {
|
export interface ModelTransformEventData {
|
||||||
yaw?: number;
|
yaw?: number;
|
||||||
pitch: number;
|
pitch: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modelTransformEvent = new EventManager<ModelTransformEventData>();
|
export const modelTransformEvent = new Quicklime<ModelTransformEventData>();
|
||||||
Reference in new issue
Block a user