feat: movable tracks

This commit is contained in:
TrèsAbhi committed 2024-06-29 13:15:24 -05:00
1 parent b032de4a62
commit 5b833bab11
4 files changed
+64 -17

No files matched your search

+1
View File
@@ -89,6 +89,7 @@
"lossrate", "lossrate",
"mantlets", "mantlets",
"metallicness", "metallicness",
"metalness",
"minitelrose", "minitelrose",
"Minotauro", "Minotauro",
"MIPMAPCOUNT", "MIPMAPCOUNT",
@@ -9,7 +9,7 @@ import { modelDefinitions } from '../../../../../core/blitzkit/modelDefinitions'
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose'; import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
import { useAwait } from '../../../../../hooks/useAwait'; import { useAwait } from '../../../../../hooks/useAwait';
import { useDuel } from '../../../../../stores/duel'; import { useDuel } from '../../../../../stores/duel';
import { useTankopediaPersistent } from '../../../../../stores/tankopedia'; import { useTankopediaTemporary } from '../../../../../stores/tankopedia';
const poseDistances: Record<Pose, number> = { const poseDistances: Record<Pose, number> = {
[Pose.HullDown]: 15, [Pose.HullDown]: 15,
@@ -62,8 +62,8 @@ export function Controls() {
}, [camera]); }, [camera]);
useEffect(() => { useEffect(() => {
const unsubscribeTankopedia = useTankopediaPersistent.subscribe( const unsubscribeTankopedia = useTankopediaTemporary.subscribe(
(state) => state.model.visual.controlsEnabled, (state) => state.controlsEnabled,
(enabled) => { (enabled) => {
if (orbitControls.current) orbitControls.current.enabled = enabled; if (orbitControls.current) orbitControls.current.enabled = enabled;
}, },
@@ -182,7 +182,7 @@ export function Controls() {
maxDistance={20} maxDistance={20}
minDistance={5} minDistance={5}
ref={orbitControls} ref={orbitControls}
enabled={useTankopediaPersistent.getState().model.visual.controlsEnabled} enabled={useTankopediaTemporary.getState().controlsEnabled}
rotateSpeed={0.25} rotateSpeed={0.25}
enableDamping={false} enableDamping={false}
autoRotate={autoRotate} autoRotate={autoRotate}
@@ -1,6 +1,13 @@
import { ThreeEvent, useThree } from '@react-three/fiber'; import { ThreeEvent, useThree } from '@react-three/fiber';
import { memo, useEffect, useRef } from 'react'; import { memo, useEffect, useRef } from 'react';
import { Euler, Group, Vector2, Vector3 } from 'three'; import {
Euler,
Group,
Mesh,
MeshStandardMaterial,
Vector2,
Vector3,
} from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis'; import { I_HAT, J_HAT, K_HAT } from '../../../../../../../constants/axis';
import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits'; import { applyPitchYawLimits } from '../../../../../../../core/blitz/applyPitchYawLimits';
@@ -15,9 +22,7 @@ import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngle
import { useAwait } from '../../../../../../../hooks/useAwait'; import { useAwait } from '../../../../../../../hooks/useAwait';
import { useModel } from '../../../../../../../hooks/useModel'; import { useModel } from '../../../../../../../hooks/useModel';
import { mutateDuel, useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
import mutateTankopediaPersistent, { import { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia';
mutateTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
export const TankModel = memo(() => { export const TankModel = memo(() => {
const awaitedModelDefinitions = useAwait(modelDefinitions); const awaitedModelDefinitions = useAwait(modelDefinitions);
@@ -117,11 +122,51 @@ export const TankModel = memo(() => {
const isWheel = node.name.startsWith('chassis_wheel_'); const isWheel = node.name.startsWith('chassis_wheel_');
const isTrack = node.name.startsWith('chassis_track_'); const isTrack = node.name.startsWith('chassis_track_');
const isVisible = isHull || isWheel || isTrack; const isVisible = isHull || isWheel || isTrack;
const position = new Vector2();
const delta = new Vector2();
if (!isVisible) return null; if (!isVisible) return null;
function translateTexture(offset: number) {
const mesh = node.children[0] as Mesh;
const material = mesh?.material as MeshStandardMaterial;
if (!material) return;
if (material.map) material.map.offset.y += offset;
if (material.aoMap) material.aoMap.offset.y += offset;
if (material.normalMap) material.normalMap.offset.y += offset;
if (material.roughnessMap) material.roughnessMap.offset.y += offset;
if (material.metalnessMap) material.metalnessMap.offset.y += offset;
}
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
if (!isTrack) return;
event.stopPropagation(); event.stopPropagation();
mutateTankopediaTemporary((draft) => {
draft.controlsEnabled = false;
});
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
delta.set(event.clientX, event.clientY).sub(position);
position.set(event.clientX, event.clientY);
const deltaX = delta.x / window.innerWidth;
const deltaY = delta.y / window.innerHeight;
translateTexture(deltaX + deltaY);
}
function handlePointerUp() {
mutateTankopediaTemporary((draft) => {
draft.controlsEnabled = true;
});
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
} }
return jsxTree( return jsxTree(
@@ -158,8 +203,8 @@ export const TankModel = memo(() => {
yaw = protagonist.yaw; yaw = protagonist.yaw;
pitch = protagonist.pitch; pitch = protagonist.pitch;
mutateTankopediaPersistent((draft) => { mutateTankopediaTemporary((draft) => {
draft.model.visual.controlsEnabled = false; draft.controlsEnabled = false;
}); });
mutateTankopediaTemporary((draft) => { mutateTankopediaTemporary((draft) => {
draft.shot = undefined; draft.shot = undefined;
@@ -194,8 +239,8 @@ export const TankModel = memo(() => {
draft.protagonist!.pitch = normalizeAngleRad(pitch); draft.protagonist!.pitch = normalizeAngleRad(pitch);
draft.protagonist!.yaw = normalizeAngleRad(yaw); draft.protagonist!.yaw = normalizeAngleRad(yaw);
}); });
mutateTankopediaPersistent((draft) => { mutateTankopediaTemporary((draft) => {
draft.model.visual.controlsEnabled = true; draft.controlsEnabled = true;
}); });
window.removeEventListener('pointermove', handlePointerMove); window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
@@ -234,8 +279,8 @@ export const TankModel = memo(() => {
function onPointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
mutateTankopediaPersistent((draft) => { mutateTankopediaTemporary((draft) => {
draft.model.visual.controlsEnabled = false; draft.controlsEnabled = false;
}); });
mutateTankopediaTemporary((draft) => { mutateTankopediaTemporary((draft) => {
draft.shot = undefined; draft.shot = undefined;
@@ -269,8 +314,8 @@ export const TankModel = memo(() => {
modelTransformEvent.emit({ pitch, yaw }); modelTransformEvent.emit({ pitch, yaw });
} }
function handlePointerUp() { function handlePointerUp() {
mutateTankopediaPersistent((draft) => { mutateTankopediaTemporary((draft) => {
draft.model.visual.controlsEnabled = true; draft.controlsEnabled = true;
}); });
mutateDuel((draft) => { mutateDuel((draft) => {
draft.protagonist!.pitch = normalizeAngleRad(pitch); draft.protagonist!.pitch = normalizeAngleRad(pitch);
+2 -1
View File
@@ -20,7 +20,6 @@ export interface TankopediaPersistent {
wireframe: boolean; wireframe: boolean;
opaque: boolean; opaque: boolean;
environment: (typeof ENVIRONMENTS)[number]; environment: (typeof ENVIRONMENTS)[number];
controlsEnabled: boolean;
showGrid: boolean; showGrid: boolean;
greenPenetration: boolean; greenPenetration: boolean;
showEnvironment: boolean; showEnvironment: boolean;
@@ -45,6 +44,7 @@ export interface TankopediaPersistent {
interface TankopediaTemporary { interface TankopediaTemporary {
shot?: Shot; shot?: Shot;
skills: Record<string, number>; skills: Record<string, number>;
controlsEnabled: boolean;
} }
export interface ShotLayerBase { export interface ShotLayerBase {
@@ -199,6 +199,7 @@ export const useTankopediaPersistent = create<TankopediaPersistent>()(
export const useTankopediaTemporary = create<TankopediaTemporary>()( export const useTankopediaTemporary = create<TankopediaTemporary>()(
subscribeWithSelector<TankopediaTemporary>(() => ({ subscribeWithSelector<TankopediaTemporary>(() => ({
skills: {}, skills: {},
controlsEnabled: true,
})), })),
); );