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