fix: use new model format

This commit is contained in:
TrèsAbhi committed 2024-02-22 12:20:29 -06:00
1 parent 1c3e98678f
commit c68df74943
5 files changed
+108 -185

No files matched your search

@@ -1,5 +1,5 @@
import { memo, useEffect, useRef } from 'react'; import { memo, useEffect, useRef } from 'react';
import { Euler, Group, Mesh, Vector3 } from 'three'; import { Euler, Group, Vector3 } from 'three';
import { degToRad } from 'three/src/math/MathUtils'; import { degToRad } from 'three/src/math/MathUtils';
import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh'; import { ArmorMeshExternalModuleMask } from '../../../../../../../../../components/ArmorMesh';
import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis'; import { I_HAT, J_HAT, K_HAT } from '../../../../../../../../../constants/axis';
@@ -181,7 +181,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
ornamental={ornamental} ornamental={ornamental}
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} node={node}
/> />
); );
})} })}
@@ -200,7 +200,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
maxThickness={maxThickness} maxThickness={maxThickness}
thickness={trackModelDefinition.thickness} thickness={trackModelDefinition.thickness}
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} node={node}
/> />
); );
})} })}
@@ -221,13 +221,14 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
if (!isVisible || thickness === undefined || spaced) return null; if (!isVisible || thickness === undefined || spaced) return null;
return ( return (
<ArmorMeshExternalModuleMask <group position={turretOrigin}>
ornamental={ornamental} <ArmorMeshExternalModuleMask
exclude ornamental={ornamental}
key={node.uuid} exclude
geometry={(node as Mesh).geometry} key={node.uuid}
position={turretOrigin} node={node}
/> />
</group>
); );
})} })}
</group> </group>
@@ -247,13 +248,14 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
if (!isVisible || thickness === undefined || spaced) return null; if (!isVisible || thickness === undefined || spaced) return null;
return ( return (
<ArmorMeshExternalModuleMask <group position={turretOrigin.clone().add(gunOrigin)}>
ornamental={ornamental} <ArmorMeshExternalModuleMask
exclude ornamental={ornamental}
key={node.uuid} exclude
geometry={(node as Mesh).geometry} key={node.uuid}
position={turretOrigin.clone().add(gunOrigin)} node={node}
/> />
</group>
); );
})} })}
@@ -271,7 +273,7 @@ export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
maxThickness={maxThickness} maxThickness={maxThickness}
thickness={gunModelDefinition.thickness} thickness={gunModelDefinition.thickness}
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} node={node}
/> />
); );
})} })}
@@ -1,16 +1,10 @@
import { ThreeEvent, useThree } from '@react-three/fiber'; import { ThreeEvent, useThree } from '@react-three/fiber';
import { memo, useEffect, useRef, useState } from 'react'; import { memo, useEffect, useRef, useState } from 'react';
import { import { Euler, Group, Vector3 } from 'three';
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';
import { jsxTree } from '../../../../../../../core/blitzkrieg/jsxTree';
import { import {
ModelDefinitions, ModelDefinitions,
modelDefinitions, modelDefinitions,
@@ -19,7 +13,6 @@ import {
ModelTransformEventData, ModelTransformEventData,
modelTransformEvent, modelTransformEvent,
} from '../../../../../../../core/blitzkrieg/modelTransform'; } from '../../../../../../../core/blitzkrieg/modelTransform';
import { resolveJsxTree } from '../../../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180'; import { normalizeAnglePI } from '../../../../../../../core/math/normalizeAngle180';
import { useModel } from '../../../../../../../hooks/useModel'; import { useModel } from '../../../../../../../hooks/useModel';
import { useDuel } from '../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../stores/duel';
@@ -138,58 +131,12 @@ export const TankModel = memo(() => {
const isTrack = node.name.startsWith('chassis_track_'); const isTrack = node.name.startsWith('chassis_track_');
const isVisible = isHull || isWheel || isTrack; const isVisible = isHull || isWheel || isTrack;
function handlePointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation();
if (isTrack) {
mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = false;
});
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
});
window.addEventListener('pointermove', handlePointerMove);
window.addEventListener('pointerup', handlePointerUp);
}
}
function handlePointerMove(event: PointerEvent) {
const mesh = node as Mesh;
const material = mesh.material as MeshStandardMaterial;
const offset = new Vector2(
0,
-event.movementY * (6 / canvas.height) -
event.movementX * (6 / canvas.width),
);
material.map?.offset.add(offset);
material.roughnessMap?.offset.add(offset);
material.normalMap?.offset.add(offset);
}
function handlePointerUp() {
mutateTankopediaPersistent((draft) => {
draft.model.visual.controlsEnabled = true;
});
window.removeEventListener('pointermove', handlePointerMove);
window.removeEventListener('pointerup', handlePointerUp);
}
if (!isVisible) return null; if (!isVisible) return null;
return ( return jsxTree(node, {
<mesh castShadow: true,
children={resolveJsxTree(node)} receiveShadow: true,
key={node.uuid} });
castShadow
receiveShadow
geometry={(node as Mesh).geometry}
material={(node as Mesh).material}
position={node.position}
rotation={node.rotation}
scale={node.scale}
onPointerDown={handlePointerDown}
/>
);
})} })}
<group ref={turretContainer}> <group ref={turretContainer}>
@@ -199,10 +146,13 @@ export const TankModel = memo(() => {
node.name === node.name ===
`turret_${turretModelDefinition.model.toString().padStart(2, '0')}`; `turret_${turretModelDefinition.model.toString().padStart(2, '0')}`;
const isVisible = isCurrentTurret; const isVisible = isCurrentTurret;
if (!isVisible) return null;
let pitch = 0; let pitch = 0;
let yaw = 0; let yaw = 0;
function handlePointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
if (isTurret) { if (isTurret) {
@@ -240,23 +190,11 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
if (!isVisible) return null; return jsxTree(node, {
castShadow: true,
return ( receiveShadow: true,
<mesh onPointerDown,
visible={isVisible} });
onPointerDown={handlePointerDown}
children={resolveJsxTree(node)}
key={node.uuid}
castShadow
receiveShadow
geometry={(node as Mesh).geometry}
material={(node as Mesh).material}
position={node.position}
rotation={node.rotation}
scale={node.scale}
/>
);
})} })}
<group ref={gunContainer}> <group ref={gunContainer}>
@@ -270,10 +208,13 @@ export const TankModel = memo(() => {
node.name === node.name ===
`gun_${gunModelDefinition.model.toString().padStart(2, '0')}`; `gun_${gunModelDefinition.model.toString().padStart(2, '0')}`;
const isVisible = isCurrentGun || isCurrentMantlet; const isVisible = isCurrentGun || isCurrentMantlet;
if (!isVisible) return null;
let pitch = 0; let pitch = 0;
let yaw = 0; let yaw = 0;
function handlePointerDown(event: ThreeEvent<PointerEvent>) { function onPointerDown(event: ThreeEvent<PointerEvent>) {
event.stopPropagation(); event.stopPropagation();
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
@@ -308,23 +249,11 @@ export const TankModel = memo(() => {
window.removeEventListener('pointerup', handlePointerUp); window.removeEventListener('pointerup', handlePointerUp);
} }
if (!isVisible) return null; return jsxTree(node, {
castShadow: true,
return ( receiveShadow: true,
<mesh onPointerDown,
visible={isVisible} });
onPointerDown={handlePointerDown}
children={resolveJsxTree(node)}
key={node.uuid}
castShadow
receiveShadow
geometry={(node as Mesh).geometry}
material={(node as Mesh).material}
position={node.position}
rotation={node.rotation}
scale={node.scale}
/>
);
})} })}
</group> </group>
</group> </group>
@@ -1,53 +1,52 @@
import { MeshProps } from '@react-three/fiber'; import { Color, MeshBasicMaterial, Object3D, WebGLRenderTarget } from 'three';
import { Color, WebGLRenderTarget } from 'three'; import { jsxTree } from '../../../core/blitzkrieg/jsxTree';
import { ArmorMeshUserData } from './Armor'; import { ArmorMeshUserData } from './Armor';
export const externalModuleMaskRenderTarget = new WebGLRenderTarget(); export const externalModuleMaskRenderTarget = new WebGLRenderTarget();
type ArmorMeshExternalModuleMaskProps = MeshProps & { type ArmorMeshExternalModuleMaskProps = {
ornamental?: boolean; ornamental?: boolean;
node: Object3D;
} & ( } & (
| { | {
exclude?: false; exclude?: false;
maxThickness: number; maxThickness: number;
thickness: number; thickness: number;
} }
| { | {
exclude: true; exclude: true;
} }
); );
const excludeMaterial = new MeshBasicMaterial({
colorWrite: false,
});
export function ArmorMeshExternalModuleMask({ export function ArmorMeshExternalModuleMask({
ornamental = false, ornamental = false,
node,
...props ...props
}: ArmorMeshExternalModuleMaskProps) { }: ArmorMeshExternalModuleMaskProps) {
return ( return (
<> <>
{props.exclude && !ornamental && ( {props.exclude &&
<mesh {...props} renderOrder={0}> !ornamental &&
<meshBasicMaterial colorWrite={false} /> jsxTree(node, { renderOrder: 0, material: excludeMaterial })}
</mesh>
)}
{!props.exclude && ( {!props.exclude &&
<mesh jsxTree(node, {
{...props} renderOrder: 1,
renderOrder={1} onClick: ornamental ? () => {} : undefined,
onClick={ornamental ? () => {} : undefined} userData: {
userData={ type: 'externalModule',
{ thickness: props.thickness,
type: 'externalModule', } satisfies ArmorMeshUserData,
thickness: props.thickness, material: new MeshBasicMaterial({
} satisfies ArmorMeshUserData color: new Color(1, props.thickness / props.maxThickness, 0),
} depthWrite: false,
> colorWrite: !ornamental,
<meshBasicMaterial }),
color={new Color(1, props.thickness / props.maxThickness, 0)} })}
depthWrite={false}
colorWrite={!ornamental}
/>
</mesh>
)}
</> </>
); );
} }
+29
View File
@@ -0,0 +1,29 @@
import { MeshProps } from '@react-three/fiber';
import { Mesh, Object3D } from 'three';
export function jsxTree(node: Object3D, mesh?: MeshProps) {
if (node instanceof Mesh) {
return (
<mesh
geometry={node.geometry}
material={node.material}
position={node.position}
rotation={node.rotation}
scale={node.scale}
{...mesh}
/>
);
} else if (node instanceof Object3D) {
return (
<group
position={node.position}
rotation={node.rotation}
scale={node.scale}
>
{node.children.map((child) => jsxTree(child, mesh))}
</group>
);
}
return null;
}
-36
View File
@@ -1,36 +0,0 @@
import { Mesh, Object3D } from 'three';
export function resolveJsxTree(parent: Object3D) {
return parent.children.map((child) => {
if (child.type === 'Mesh') {
return (
<mesh
key={child.uuid}
castShadow
receiveShadow
geometry={(child as Mesh).geometry}
material={(child as Mesh).material}
position={child.position}
rotation={child.rotation}
scale={child.scale}
children={resolveJsxTree(child)}
/>
);
}
if (child.type === 'Object3D') {
return (
<group
key={child.uuid}
position={child.position}
rotation={child.rotation}
scale={child.scale}
children={resolveJsxTree(child)}
/>
);
}
throw new TypeError(
`Unhandled three jsx type ${child.type} (${child.name})`,
);
});
}