mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: outlines
This commit is contained in:
1 parent
1d928e8b72
commit
0c37550c05
6 files changed
+204
-350
No files matched your search
@@ -900,7 +900,7 @@
|
||||
"element": {
|
||||
"primary": "Primary",
|
||||
"spaced": "Spaced",
|
||||
"external": "External",
|
||||
"modules": "Modules",
|
||||
"gap": "Gap",
|
||||
"gun": "Gun",
|
||||
"track": "Tracks"
|
||||
@@ -922,7 +922,7 @@
|
||||
"name": "Static armor",
|
||||
"primary": "Primary",
|
||||
"spaced": "Spaced",
|
||||
"external": "External",
|
||||
"modules": "Modules",
|
||||
"edit": {
|
||||
"label": "Edit",
|
||||
"reset": "Reset"
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { useGLTF } from '@react-three/drei';
|
||||
import { memo } from 'react';
|
||||
import { Color, Material, MeshBasicMaterial } from 'three';
|
||||
import { jsxTree } from '../../../core/blitzkit/jsxTree';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { StaticArmorSceneComponents } from './StaticArmorSceneComponents';
|
||||
|
||||
export interface ThicknessRange {
|
||||
@@ -55,11 +49,16 @@ export const StaticArmor = memo(() => {
|
||||
|
||||
// useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
|
||||
return (
|
||||
<group>
|
||||
<StaticArmorSceneComponents group="hull" />
|
||||
|
||||
<StaticArmorSceneComponents group="chassis" />
|
||||
<group position={[0.7, 1.6, 0]}>
|
||||
<StaticArmorSceneComponents group="turret_01" />
|
||||
<group position={[1, 0.36, 0]}>
|
||||
<StaticArmorSceneComponents group="gun_01" />
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
});
|
||||
@@ -1,39 +1,45 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { useGLTF } from '@react-three/drei';
|
||||
import { Color, Material, MeshStandardMaterial } from 'three';
|
||||
import {
|
||||
Color,
|
||||
LineBasicMaterial,
|
||||
Material,
|
||||
MeshStandardMaterial,
|
||||
} from 'three';
|
||||
import { clamp } from 'three/src/math/MathUtils.js';
|
||||
import { jsxTree } from '../../../core/blitzkit/jsxTree';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
|
||||
|
||||
type StaticArmorSceneComponentsProps = {
|
||||
interface StaticArmorSceneComponentsProps {
|
||||
group: string;
|
||||
};
|
||||
}
|
||||
|
||||
const unselectedColor = new Color(0x404040);
|
||||
const externalModuleColor = new Color(192 / 255, 192 / 255, 192 / 255);
|
||||
const moduleColor = new Color(192 / 255, 192 / 255, 192 / 255);
|
||||
const groupNameRegex = /([a-z]+)(_\d+)?/;
|
||||
|
||||
export function StaticArmorSceneComponents({
|
||||
group,
|
||||
}: StaticArmorSceneComponentsProps) {
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const showModules = TankopediaPersistent.use((state) => state.showModules);
|
||||
const showArmorScreen = TankopediaPersistent.use(
|
||||
(state) => state.showArmorScreen,
|
||||
);
|
||||
const showArmor = TankopediaPersistent.use((state) => state.showArmor);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const gltf = useGLTF(asset(`tanks/${tank.id}/collision/${group}.glb`));
|
||||
const armor = TankopediaEphemeral.use((state) => state.armor);
|
||||
const groupArmor = armor.groups[group];
|
||||
const groupName = group.match(groupNameRegex)![1];
|
||||
const groupArmor = armor.groups[groupName];
|
||||
const thicknessRange = TankopediaEphemeral.use(
|
||||
(state) => state.thicknessRange,
|
||||
);
|
||||
|
||||
return jsxTree(gltf.scene, {
|
||||
mesh(mesh) {
|
||||
mesh(mesh, props) {
|
||||
if (!(mesh.material instanceof Material)) {
|
||||
return { visible: false };
|
||||
return null;
|
||||
}
|
||||
|
||||
const armorName = mesh.material.name;
|
||||
@@ -44,216 +50,67 @@ export function StaticArmorSceneComponents({
|
||||
|
||||
switch (armorPlate.type) {
|
||||
case 'Armor':
|
||||
if (!showArmor) return null;
|
||||
|
||||
if (x > 1) {
|
||||
material.color = new Color(clamp(2 - x, 0.5, 1), 0, 0);
|
||||
} else {
|
||||
material.color = new Color(-((1 - x) ** 2) + 1, -(x ** 2) + 1, 0);
|
||||
}
|
||||
material.opacity = 1;
|
||||
material.visible = showArmor;
|
||||
break;
|
||||
|
||||
case 'ArmorScreen':
|
||||
if (!showArmorScreen) return null;
|
||||
|
||||
material.color = new Color(
|
||||
clamp(1 - (7 / 8) * xClamped, 0, 1),
|
||||
0,
|
||||
clamp(1 - (1 / 8) * xClamped, 0, 1),
|
||||
);
|
||||
material.opacity = clamp(x + 1 / 2, 0, 1);
|
||||
material.visible = showArmorScreen;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
material.color = externalModuleColor;
|
||||
material.opacity = 7 / 8;
|
||||
material.depthWrite = false;
|
||||
material.visible = showModules;
|
||||
if (!showModules) return null;
|
||||
|
||||
material.color = moduleColor;
|
||||
material.opacity = 1 / 2;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const outlineMaterial = new LineBasicMaterial({
|
||||
color: material.color.clone(),
|
||||
});
|
||||
|
||||
switch (armorPlate.type) {
|
||||
case 'Armor':
|
||||
outlineMaterial.color.multiplyScalar(0.5);
|
||||
break;
|
||||
|
||||
case 'ArmorScreen':
|
||||
outlineMaterial.color.multiplyScalar(4);
|
||||
break;
|
||||
|
||||
default:
|
||||
outlineMaterial.color.multiplyScalar(0.5);
|
||||
break;
|
||||
}
|
||||
|
||||
material.opacity = clamp(material.opacity, 0, 1);
|
||||
material.transparent = material.opacity < 1;
|
||||
|
||||
return { material };
|
||||
return (
|
||||
<>
|
||||
<mesh {...props} material={material} />
|
||||
|
||||
<lineSegments material={outlineMaterial}>
|
||||
<edgesGeometry args={[props.geometry, 45]} />
|
||||
</lineSegments>
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// const mutateTankopediaEphemeralStore = TankopediaEphemeral.useMutation();
|
||||
// const tankopediaEphemeralStore = TankopediaEphemeral.useStore();
|
||||
// const camera = useThree((state) => state.camera);
|
||||
// const x = thickness / thicknessRange.value;
|
||||
// const xClamped = clamp(x, 0, 1);
|
||||
// let color: Color;
|
||||
// let opacity: number;
|
||||
// let depthWrite = true;
|
||||
// switch (props.type) {
|
||||
// case ArmorType.Primary:
|
||||
// if (x > 1) {
|
||||
// color = new Color(clamp(2 - x, 0.5, 1), 0, 0);
|
||||
// } else {
|
||||
// color = new Color(-((1 - x) ** 2) + 1, -(x ** 2) + 1, 0);
|
||||
// }
|
||||
// opacity = 1;
|
||||
// break;
|
||||
// case ArmorType.Spaced:
|
||||
// color = new Color(
|
||||
// clamp(1 - (7 / 8) * xClamped, 0, 1),
|
||||
// 0,
|
||||
// clamp(1 - (1 / 8) * xClamped, 0, 1),
|
||||
// );
|
||||
// opacity = clamp(x + 1 / 2, 0, 1);
|
||||
// break;
|
||||
// case ArmorType.External:
|
||||
// color = externalModuleColor;
|
||||
// opacity = 1 / 8;
|
||||
// depthWrite = false;
|
||||
// break;
|
||||
// }
|
||||
// opacity = clamp(opacity, 0, 1);
|
||||
// const surfaceMaterial = useMemo(
|
||||
// () =>
|
||||
// new MeshStandardMaterial({
|
||||
// color,
|
||||
// opacity,
|
||||
// transparent: opacity < 1,
|
||||
// depthWrite,
|
||||
// ...(props.clip ? { clippingPlanes: [props.clip] } : {}),
|
||||
// }),
|
||||
// [thickness],
|
||||
// );
|
||||
// const outlineMaterial = useMemo(
|
||||
// () =>
|
||||
// new LineBasicMaterial({
|
||||
// color: color
|
||||
// .clone()
|
||||
// .multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
|
||||
// }),
|
||||
// [thickness],
|
||||
// );
|
||||
// useEffect(() => {
|
||||
// function handleHighlightArmor(selectedName?: string) {
|
||||
// if (selectedName === undefined) {
|
||||
// // nothing selected, go back to defaults
|
||||
// surfaceMaterial.opacity = opacity;
|
||||
// surfaceMaterial.transparent = opacity < 1;
|
||||
// surfaceMaterial.color = color;
|
||||
// surfaceMaterial.depthWrite = props.type !== ArmorType.External;
|
||||
// surfaceMaterial.side = FrontSide;
|
||||
// outlineMaterial.visible = true;
|
||||
// } else if (
|
||||
// selectedName === name ||
|
||||
// (name.startsWith('chassis_') && selectedName.startsWith('chassis_')) ||
|
||||
// (name.startsWith('gun_') &&
|
||||
// selectedName.startsWith('gun_') &&
|
||||
// !name.includes('_armor_') &&
|
||||
// !selectedName.includes('_armor_'))
|
||||
// ) {
|
||||
// // this selected, stand out!
|
||||
// surfaceMaterial.opacity = 1;
|
||||
// surfaceMaterial.transparent = false;
|
||||
// surfaceMaterial.color = color;
|
||||
// surfaceMaterial.depthWrite = true;
|
||||
// surfaceMaterial.side = DoubleSide;
|
||||
// outlineMaterial.visible = true;
|
||||
// } else {
|
||||
// // something else selected, become background
|
||||
// surfaceMaterial.opacity = 1 / 4;
|
||||
// surfaceMaterial.transparent = true;
|
||||
// surfaceMaterial.color = unselectedColor;
|
||||
// surfaceMaterial.depthWrite = props.type !== ArmorType.External;
|
||||
// surfaceMaterial.side = FrontSide;
|
||||
// outlineMaterial.visible = false;
|
||||
// }
|
||||
// surfaceMaterial.needsUpdate = true;
|
||||
// }
|
||||
// const unsubscribes = [
|
||||
// tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.highlightArmor?.name,
|
||||
// handleHighlightArmor,
|
||||
// ),
|
||||
// ];
|
||||
// if (props.clip) {
|
||||
// /**
|
||||
// * hook inside an if statement?? don't panic! I assure you the clip prop
|
||||
// * never mutates :)
|
||||
// */
|
||||
// const { clip } = props;
|
||||
// const gunOrigin = unrotateDavaVector(props.gunOrigin.clone());
|
||||
// const neckOrigin = unrotateDavaVector(props.hullOrigin.clone()).add(
|
||||
// unrotateDavaVector(props.turretOrigin.clone()),
|
||||
// );
|
||||
// const barrelOrigin = neckOrigin.clone().add(gunOrigin);
|
||||
// const distanceToBarrel = clip.distanceToPoint(barrelOrigin);
|
||||
// const point = new Vector3();
|
||||
// function handleModelTransform(data: ModelTransformEventData) {
|
||||
// clip.normal
|
||||
// .set(0, 0, -1)
|
||||
// .applyAxisAngle(I_HAT, data.pitch)
|
||||
// .applyAxisAngle(J_HAT, data.yaw ?? 0);
|
||||
// clip.constant = 0;
|
||||
// point
|
||||
// .copy(gunOrigin)
|
||||
// .applyAxisAngle(J_HAT, data.yaw ?? 0)
|
||||
// .add(clip.normal.multiplyScalar(-distanceToBarrel))
|
||||
// .add(neckOrigin);
|
||||
// clip.normal.normalize();
|
||||
// clip.constant -= clip.distanceToPoint(point);
|
||||
// }
|
||||
// modelTransformEvent.on(handleModelTransform);
|
||||
// unsubscribes.push(() => modelTransformEvent.off(handleModelTransform));
|
||||
// }
|
||||
// return () => {
|
||||
// unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
// };
|
||||
// }, [thickness]);
|
||||
// return (
|
||||
// <>
|
||||
// {jsxTree(node, {
|
||||
// mesh: {
|
||||
// material: surfaceMaterial,
|
||||
// onPointerDown,
|
||||
// userData: {
|
||||
// type: props.type,
|
||||
// variant: props.type === ArmorType.External ? props.variant : 'gun',
|
||||
// thickness,
|
||||
// } satisfies ArmorUserData,
|
||||
// onClick(event) {
|
||||
// if (discardClippingPlane(event.object, event.point)) return;
|
||||
// event.stopPropagation();
|
||||
// const { editStatic } = tankopediaEphemeralStore.getState();
|
||||
// const bounds = new Box3().setFromObject(event.object);
|
||||
// const point = bounds.min
|
||||
// .clone()
|
||||
// .add(bounds.max)
|
||||
// .divideScalar(2)
|
||||
// .setY(bounds.max.y);
|
||||
// const cameraNormal = camera.position.clone().sub(point).normalize();
|
||||
// const surfaceNormal = event
|
||||
// .normal!.clone()
|
||||
// .applyQuaternion(
|
||||
// event.object.getWorldQuaternion(new Quaternion()),
|
||||
// );
|
||||
// const angle = surfaceNormal.angleTo(cameraNormal);
|
||||
// const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle);
|
||||
// mutateTankopediaEphemeralStore((draft) => {
|
||||
// draft.highlightArmor = {
|
||||
// type: props.type,
|
||||
// name,
|
||||
// point,
|
||||
// thickness,
|
||||
// thicknessAngled,
|
||||
// angle,
|
||||
// color: `#${color.getHexString()}`,
|
||||
// editingPlate: editStatic,
|
||||
// };
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
// })}
|
||||
// {node instanceof Mesh && (
|
||||
// <lineSegments material={outlineMaterial}>
|
||||
// <edgesGeometry args={[node.geometry, 45]} />
|
||||
// </lineSegments>
|
||||
// )}
|
||||
// </>
|
||||
// );
|
||||
}
|
||||
+1
-1
@@ -125,7 +125,7 @@ export function Thicknesses() {
|
||||
>
|
||||
<Flex direction="column" align="end" justify="between">
|
||||
<Text size="1">
|
||||
{strings.website.tools.tanks.sandbox.static.external}
|
||||
{strings.website.tools.tanks.sandbox.static.modules}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
|
||||
+115
-111
@@ -1,13 +1,16 @@
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { PerspectiveCamera } from 'three';
|
||||
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
||||
import { Pose } from '../../../../../../core/blitzkit/pose';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import {
|
||||
SHOOTING_RANGE_ZOOM_COEFFICIENTS,
|
||||
TankopediaEphemeral,
|
||||
} from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
@@ -80,11 +83,12 @@ export function Controls({ naked }: ControlsProps) {
|
||||
// protagonistHullOrigin.y +
|
||||
// protagonistTurretOrigin.y +
|
||||
// protagonistGunOrigin.y;
|
||||
// const inspectModeInitialPosition = [
|
||||
// -8,
|
||||
// gunHeight + (naked ? 10 : 4),
|
||||
// -13,
|
||||
// ] as const;
|
||||
const gunHeight = 2;
|
||||
const inspectModeInitialPosition = [
|
||||
-8,
|
||||
gunHeight + (naked ? 10 : 4),
|
||||
-13,
|
||||
] as const;
|
||||
// const protagonistGunOriginOnlyY = new Vector3(
|
||||
// 0,
|
||||
// protagonistTurretModelDefinition.gun_origin.y,
|
||||
@@ -206,131 +210,131 @@ export function Controls({ naked }: ControlsProps) {
|
||||
// };
|
||||
// }, [camera, protagonistTank.id, antagonistTank.id]);
|
||||
|
||||
// useEffect(() => {
|
||||
// let isInitialIteration = true;
|
||||
useEffect(() => {
|
||||
let isInitialIteration = true;
|
||||
|
||||
// function handleDisturbance() {
|
||||
// setAutoRotate(false);
|
||||
// }
|
||||
function handleDisturbance() {
|
||||
setAutoRotate(false);
|
||||
}
|
||||
|
||||
// function handleKeyDown(event: KeyboardEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange || event.repeat) return;
|
||||
// function handleKeyDown(event: KeyboardEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange || event.repeat) return;
|
||||
|
||||
// if (event.key === 'Shift') {
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom =
|
||||
// draft.shootingRangeZoom === ShootingRangeZoom.Arcade
|
||||
// ? ShootingRangeZoom.Zoom0
|
||||
// : ShootingRangeZoom.Arcade;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// if (event.key === 'Shift') {
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom =
|
||||
// draft.shootingRangeZoom === ShootingRangeZoom.Arcade
|
||||
// ? ShootingRangeZoom.Zoom0
|
||||
// : ShootingRangeZoom.Arcade;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// function handleWheel(event: WheelEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
// function handleWheel(event: WheelEvent) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
|
||||
// event.preventDefault();
|
||||
// event.preventDefault();
|
||||
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom = clamp(
|
||||
// draft.shootingRangeZoom - Math.sign(event.deltaY),
|
||||
// ShootingRangeZoom.Arcade,
|
||||
// ShootingRangeZoom.Zoom2,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// mutateTankopediaEphemeral((draft) => {
|
||||
// draft.shootingRangeZoom = clamp(
|
||||
// draft.shootingRangeZoom - Math.sign(event.deltaY),
|
||||
// ShootingRangeZoom.Arcade,
|
||||
// ShootingRangeZoom.Zoom2,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// function handleScroll(event: Event) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
// function handleScroll(event: Event) {
|
||||
// if (display !== TankopediaDisplay.ShootingRange) return;
|
||||
|
||||
// event.preventDefault();
|
||||
// }
|
||||
// event.preventDefault();
|
||||
// }
|
||||
|
||||
// poseEvent.on(handleDisturbance);
|
||||
// canvas.addEventListener('pointerdown', handleDisturbance);
|
||||
// window.addEventListener('keydown', handleKeyDown);
|
||||
// canvas.addEventListener('wheel', handleWheel);
|
||||
// document.body.addEventListener('scroll', handleScroll);
|
||||
// poseEvent.on(handleDisturbance);
|
||||
canvas.addEventListener('pointerdown', handleDisturbance);
|
||||
// window.addEventListener('keydown', handleKeyDown);
|
||||
// canvas.addEventListener('wheel', handleWheel);
|
||||
// document.body.addEventListener('scroll', handleScroll);
|
||||
|
||||
// function updateCamera() {
|
||||
// if (!orbitControls.current) return;
|
||||
function updateCamera() {
|
||||
if (!orbitControls.current) return;
|
||||
|
||||
// orbitControls.current.rotateSpeed = 0.25;
|
||||
orbitControls.current.rotateSpeed = 0.25;
|
||||
|
||||
// if (display === TankopediaDisplay.ShootingRange) {
|
||||
// const { shootingRangeZoom } = tankopediaEphemeralStore.getState();
|
||||
if (display === TankopediaDisplay.ShootingRange) {
|
||||
const { shootingRangeZoom } = tankopediaEphemeralStore.getState();
|
||||
|
||||
// orbitControls.current.enablePan = false;
|
||||
// orbitControls.current.enableZoom = false;
|
||||
// orbitControls.current.rotateSpeed *=
|
||||
// SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
// camera.fov =
|
||||
// ARCADE_MODE_FOV * SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
orbitControls.current.enablePan = false;
|
||||
orbitControls.current.enableZoom = false;
|
||||
orbitControls.current.rotateSpeed *=
|
||||
SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
camera.fov =
|
||||
ARCADE_MODE_FOV * SHOOTING_RANGE_ZOOM_COEFFICIENTS[shootingRangeZoom];
|
||||
|
||||
// if (shootingRangeZoom === ShootingRangeZoom.Arcade) {
|
||||
// orbitControls.current.target.set(0, gunHeight + 3, 0);
|
||||
// if (shootingRangeZoom === ShootingRangeZoom.Arcade) {
|
||||
// orbitControls.current.target.set(0, gunHeight + 3, 0);
|
||||
|
||||
// if (isInitialIteration) {
|
||||
// isInitialIteration = false;
|
||||
// camera.position.set(
|
||||
// 0,
|
||||
// gunHeight + ARCADE_MODE_DISTANCE * Math.sin(ARCADE_MODE_ANGLE),
|
||||
// ARCADE_MODE_DISTANCE * Math.cos(ARCADE_MODE_ANGLE),
|
||||
// );
|
||||
// } else {
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(orbitControls.current.target)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-ARCADE_MODE_DISTANCE)
|
||||
// .add(orbitControls.current.target);
|
||||
// }
|
||||
// } else {
|
||||
// orbitControls.current.target.copy(shellOrigin);
|
||||
// if (isInitialIteration) {
|
||||
// isInitialIteration = false;
|
||||
// camera.position.set(
|
||||
// 0,
|
||||
// gunHeight + ARCADE_MODE_DISTANCE * Math.sin(ARCADE_MODE_ANGLE),
|
||||
// ARCADE_MODE_DISTANCE * Math.cos(ARCADE_MODE_ANGLE),
|
||||
// );
|
||||
// } else {
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(orbitControls.current.target)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-ARCADE_MODE_DISTANCE)
|
||||
// .add(orbitControls.current.target);
|
||||
// }
|
||||
// } else {
|
||||
// orbitControls.current.target.copy(shellOrigin);
|
||||
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(shellOrigin)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-Number.EPSILON)
|
||||
// .add(shellOrigin);
|
||||
// }
|
||||
// } else {
|
||||
// camera.position.set(...inspectModeInitialPosition);
|
||||
// orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||
// orbitControls.current.enablePan = true;
|
||||
// orbitControls.current.enableZoom = true;
|
||||
// camera.fov = naked ? 20 : 25;
|
||||
// }
|
||||
// camera.position
|
||||
// .copy(aimTarget)
|
||||
// .sub(shellOrigin)
|
||||
// .normalize()
|
||||
// .multiplyScalar(-Number.EPSILON)
|
||||
// .add(shellOrigin);
|
||||
// }
|
||||
} else {
|
||||
camera.position.set(...inspectModeInitialPosition);
|
||||
orbitControls.current.target.set(0, gunHeight / (naked ? 4 : 2), 0);
|
||||
orbitControls.current.enablePan = true;
|
||||
orbitControls.current.enableZoom = true;
|
||||
camera.fov = naked ? 20 : 25;
|
||||
}
|
||||
|
||||
// camera.updateProjectionMatrix();
|
||||
// }
|
||||
camera.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
// updateCamera();
|
||||
updateCamera();
|
||||
|
||||
// const unsubscribeDisplay = tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.display,
|
||||
// () => {
|
||||
// handleDisturbance();
|
||||
// updateCamera();
|
||||
// },
|
||||
// );
|
||||
const unsubscribeDisplay = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.display,
|
||||
() => {
|
||||
handleDisturbance();
|
||||
updateCamera();
|
||||
},
|
||||
);
|
||||
|
||||
// const unsubscribeShootingRangeZoom = tankopediaEphemeralStore.subscribe(
|
||||
// (state) => state.shootingRangeZoom,
|
||||
// updateCamera,
|
||||
// );
|
||||
const unsubscribeShootingRangeZoom = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.shootingRangeZoom,
|
||||
updateCamera,
|
||||
);
|
||||
|
||||
// return () => {
|
||||
// canvas.removeEventListener('pointerdown', handleDisturbance);
|
||||
// poseEvent.off(handleDisturbance);
|
||||
// unsubscribeDisplay();
|
||||
// unsubscribeShootingRangeZoom();
|
||||
// window.removeEventListener('keydown', handleKeyDown);
|
||||
// canvas.removeEventListener('wheel', handleWheel);
|
||||
// document.body.removeEventListener('scroll', handleScroll);
|
||||
// };
|
||||
// }, [display === TankopediaDisplay.ShootingRange]);
|
||||
return () => {
|
||||
canvas.removeEventListener('pointerdown', handleDisturbance);
|
||||
// poseEvent.off(handleDisturbance);
|
||||
unsubscribeDisplay();
|
||||
unsubscribeShootingRangeZoom();
|
||||
// window.removeEventListener('keydown', handleKeyDown);
|
||||
// canvas.removeEventListener('wheel', handleWheel);
|
||||
// document.body.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [display === TankopediaDisplay.ShootingRange]);
|
||||
|
||||
return (
|
||||
<OrbitControls
|
||||
|
||||
@@ -1,44 +1,38 @@
|
||||
import type { GroupProps, MeshProps } from '@react-three/fiber';
|
||||
import type { MeshProps, Object3DProps } from '@react-three/fiber';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Mesh, Object3D } from 'three';
|
||||
|
||||
export function jsxTree(
|
||||
node: Object3D,
|
||||
mergers?: {
|
||||
mesh?: MeshProps | ((mesh: Mesh) => MeshProps);
|
||||
group?: GroupProps | ((object3d: Object3D) => GroupProps);
|
||||
mesh?: (mesh: Mesh, props: MeshProps) => ReactNode;
|
||||
group?: (object3d: Object3D, props: Object3DProps) => ReactNode;
|
||||
},
|
||||
key?: string,
|
||||
) {
|
||||
): ReactNode {
|
||||
if (node instanceof Mesh) {
|
||||
return (
|
||||
<mesh
|
||||
key={key}
|
||||
geometry={node.geometry}
|
||||
material={node.material}
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={node.scale}
|
||||
{...(mergers?.mesh &&
|
||||
(typeof mergers.mesh === 'function'
|
||||
? mergers.mesh(node)
|
||||
: mergers.mesh))}
|
||||
/>
|
||||
);
|
||||
const props = {
|
||||
key,
|
||||
geometry: node.geometry,
|
||||
material: node.material,
|
||||
position: node.position,
|
||||
rotation: node.rotation,
|
||||
scale: node.scale,
|
||||
};
|
||||
|
||||
return mergers?.mesh?.(node, props) ?? <mesh {...props} />;
|
||||
} else if (node instanceof Object3D) {
|
||||
return (
|
||||
<group
|
||||
key={key}
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={node.scale}
|
||||
{...(mergers?.group &&
|
||||
(typeof mergers.group === 'function'
|
||||
? mergers.group(node)
|
||||
: mergers.group))}
|
||||
>
|
||||
{node.children.map((child) => jsxTree(child, mergers, child.uuid))}
|
||||
</group>
|
||||
);
|
||||
const props = {
|
||||
key,
|
||||
position: node.position,
|
||||
rotation: node.rotation,
|
||||
scale: node.scale,
|
||||
children: node.children.map((child) =>
|
||||
jsxTree(child, mergers, child.uuid),
|
||||
),
|
||||
};
|
||||
|
||||
return mergers?.group?.(node, props) ?? <group {...props} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user