mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +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,
|
||||
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) => {
|
||||
|
||||
+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 { 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>
|
||||
);
|
||||
}
|
||||
+10
-4
@@ -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%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
+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 { 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>
|
||||
);
|
||||
}
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+2
-2
@@ -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) => {
|
||||
|
||||
+3
-3
@@ -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>();
|
||||
Reference in new issue
Block a user