feat: basic click functionality

This commit is contained in:
TrèsAbhi committed 2024-02-02 10:31:40 -06:00
1 parent 0251bfc842
commit b01a03c6ce
6 files changed
+112 -22

No files matched your search

@@ -21,6 +21,8 @@ import { SceneProps } from '../SceneProps';
import { ModelLoader } from './components/ModelLoader'; import { ModelLoader } from './components/ModelLoader';
import { Options } from './components/Options'; import { Options } from './components/Options';
import { TankArmor } from './components/TankArmor'; import { TankArmor } from './components/TankArmor';
import { ExternalModuleMask } from './components/TankArmor/components/ExternalModuleMask';
import { SpacedArmorDepth } from './components/TankArmor/components/SpacedArmorDepth';
import { TankModel } from './components/TankModel'; import { TankModel } from './components/TankModel';
export function TankSandbox() { export function TankSandbox() {
@@ -173,6 +175,8 @@ export function TankSandbox() {
<Lighting /> <Lighting />
<TankModel /> <TankModel />
<TankArmor /> <TankArmor />
<ExternalModuleMask ornamental />
<SpacedArmorDepth ornamental />
</Suspense> </Suspense>
</Canvas> </Canvas>
) : ( ) : (
@@ -15,6 +15,10 @@ import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDe
import { useDuel } from '../../../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia'; import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
interface ExternalModuleMaskProps {
ornamental?: boolean;
}
/** /**
* When rendered, generates a mask and thickness buffer for external modules. * When rendered, generates a mask and thickness buffer for external modules.
* - R: 1 means external module * - R: 1 means external module
@@ -22,7 +26,8 @@ import { useTankopediaTemporary } from '../../../../../../../../../stores/tankop
* - B: no data * - B: no data
* - A: 1 means is armor; 0 is background * - A: 1 means is armor; 0 is background
*/ */
export const ExternalModuleMask = memo(() => { export const ExternalModuleMask = memo<ExternalModuleMaskProps>(
({ ornamental = false }) => {
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions(); const modelDefinitions = useModelDefinitions();
@@ -79,7 +84,9 @@ export const ExternalModuleMask = memo(() => {
tankModelDefinition.turretRotation.pitch, tankModelDefinition.turretRotation.pitch,
); );
const initialYaw = -degToRad(tankModelDefinition.turretRotation.yaw); const initialYaw = -degToRad(tankModelDefinition.turretRotation.yaw);
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(
tankModelDefinition.turretRotation.roll,
);
turretPosition turretPosition
.applyAxisAngle(I_HAT, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
@@ -169,6 +176,7 @@ export const ExternalModuleMask = memo(() => {
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
ornamental={ornamental}
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
@@ -186,6 +194,7 @@ export const ExternalModuleMask = memo(() => {
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
ornamental={ornamental}
maxThickness={maxThickness} maxThickness={maxThickness}
thickness={tankModelDefinition.trackThickness} thickness={tankModelDefinition.trackThickness}
key={node.uuid} key={node.uuid}
@@ -211,6 +220,7 @@ export const ExternalModuleMask = memo(() => {
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
ornamental={ornamental}
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
@@ -236,6 +246,7 @@ export const ExternalModuleMask = memo(() => {
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
ornamental={ornamental}
exclude exclude
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
@@ -254,6 +265,7 @@ export const ExternalModuleMask = memo(() => {
return ( return (
<ArmorMeshExternalModuleMask <ArmorMeshExternalModuleMask
ornamental={ornamental}
maxThickness={maxThickness} maxThickness={maxThickness}
thickness={gunModelDefinition.barrelThickness} thickness={gunModelDefinition.barrelThickness}
key={node.uuid} key={node.uuid}
@@ -265,4 +277,5 @@ export const ExternalModuleMask = memo(() => {
</group> </group>
</group> </group>
); );
}); },
);
@@ -15,6 +15,10 @@ import { useModelDefinitions } from '../../../../../../../../../hooks/useModelDe
import { useDuel } from '../../../../../../../../../stores/duel'; import { useDuel } from '../../../../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia'; import { useTankopediaTemporary } from '../../../../../../../../../stores/tankopedia';
interface SpacedArmorDepthProps {
ornamental?: boolean;
}
/** /**
* When rendered, this component provides the depth buffer for the armor which * When rendered, this component provides the depth buffer for the armor which
* when compared to the depth of the core armor will give you the distance * when compared to the depth of the core armor will give you the distance
@@ -26,7 +30,8 @@ import { useTankopediaTemporary } from '../../../../../../../../../stores/tankop
* - B: no data * - B: no data
* - A: 1 means is armor; 0 is background * - A: 1 means is armor; 0 is background
*/ */
export const SpacedArmorDepth = memo(() => { export const SpacedArmorDepth = memo<SpacedArmorDepthProps>(
({ ornamental = false }) => {
const protagonist = useDuel((state) => state.protagonist!); const protagonist = useDuel((state) => state.protagonist!);
const wrapper = useRef<Group>(null); const wrapper = useRef<Group>(null);
const modelDefinitions = useModelDefinitions(); const modelDefinitions = useModelDefinitions();
@@ -85,7 +90,9 @@ export const SpacedArmorDepth = memo(() => {
tankModelDefinition.turretRotation.pitch, tankModelDefinition.turretRotation.pitch,
); );
const initialYaw = -degToRad(tankModelDefinition.turretRotation.yaw); const initialYaw = -degToRad(tankModelDefinition.turretRotation.yaw);
const initialRoll = -degToRad(tankModelDefinition.turretRotation.roll); const initialRoll = -degToRad(
tankModelDefinition.turretRotation.roll,
);
turretPosition turretPosition
.applyAxisAngle(I_HAT, initialPitch) .applyAxisAngle(I_HAT, initialPitch)
@@ -175,6 +182,7 @@ export const SpacedArmorDepth = memo(() => {
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
ornamental={ornamental}
include={spaced} include={spaced}
isExternalModule={false} isExternalModule={false}
thickness={thickness} thickness={thickness}
@@ -194,6 +202,7 @@ export const SpacedArmorDepth = memo(() => {
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
ornamental={ornamental}
isExternalModule isExternalModule
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
@@ -218,6 +227,7 @@ export const SpacedArmorDepth = memo(() => {
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
ornamental={ornamental}
include={spaced} include={spaced}
isExternalModule={false} isExternalModule={false}
thickness={thickness} thickness={thickness}
@@ -246,6 +256,7 @@ export const SpacedArmorDepth = memo(() => {
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
ornamental={ornamental}
include={spaced} include={spaced}
isExternalModule={false} isExternalModule={false}
thickness={thickness} thickness={thickness}
@@ -268,6 +279,7 @@ export const SpacedArmorDepth = memo(() => {
return ( return (
<ArmorMeshSpacedArmorDepth <ArmorMeshSpacedArmorDepth
ornamental={ornamental}
isExternalModule isExternalModule
key={node.uuid} key={node.uuid}
geometry={(node as Mesh).geometry} geometry={(node as Mesh).geometry}
@@ -278,4 +290,5 @@ export const SpacedArmorDepth = memo(() => {
</group> </group>
</group> </group>
); );
}); },
);
@@ -6,7 +6,10 @@ import { canSplash } from '../../../../core/blitz/canSplash';
import { isExplosive } from '../../../../core/blitz/isExplosive'; import { isExplosive } from '../../../../core/blitz/isExplosive';
import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration'; import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration';
import { useDuel } from '../../../../stores/duel'; import { useDuel } from '../../../../stores/duel';
import { useTankopediaPersistent } from '../../../../stores/tankopedia'; import {
useTankopediaPersistent,
useTankopediaTemporary,
} from '../../../../stores/tankopedia';
import { externalModuleMaskRenderTarget } from '../ExternalModuleMask'; import { externalModuleMaskRenderTarget } from '../ExternalModuleMask';
import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth'; import { spacedArmorDepthRenderTarget } from '../SpacedArmorDepth';
import fragmentShader from './shaders/fragment.glsl'; import fragmentShader from './shaders/fragment.glsl';
@@ -17,6 +20,11 @@ interface ArmorMeshProps extends MeshProps {
maxThickness: number; maxThickness: number;
} }
export type ArmorMeshUserData = {
type: 'coreArmor' | 'spacedArmor' | 'externalModule';
thickness: number;
};
export function ArmorMesh({ export function ArmorMesh({
thickness, thickness,
maxThickness, maxThickness,
@@ -109,7 +117,26 @@ export function ArmorMesh({
<meshBasicMaterial colorWrite={false} /> <meshBasicMaterial colorWrite={false} />
</mesh> </mesh>
<mesh {...props} renderOrder={1}> <mesh
{...props}
renderOrder={1}
userData={
{
type: 'coreArmor',
thickness,
} satisfies ArmorMeshUserData
}
onClick={(event) => {
if (useTankopediaTemporary.getState().mode !== 'armor') return;
event.intersections
.filter(({ object }) => Boolean(object.userData.type))
.map(({ object }) => {
const data = object.userData as ArmorMeshUserData;
console.log(data.type, data.thickness);
});
}}
>
<shaderMaterial <shaderMaterial
ref={material} ref={material}
fragmentShader={fragmentShader} fragmentShader={fragmentShader}
@@ -1,10 +1,12 @@
import { MeshProps } from '@react-three/fiber'; import { MeshProps } from '@react-three/fiber';
import { Color, WebGLRenderTarget } from 'three'; import { Color, WebGLRenderTarget } from 'three';
import { ArmorMeshUserData } from './Armor';
export const externalModuleMaskRenderTarget = new WebGLRenderTarget(); export const externalModuleMaskRenderTarget = new WebGLRenderTarget();
type ArmorMeshExternalModuleMaskProps = MeshProps & type ArmorMeshExternalModuleMaskProps = MeshProps & {
( ornamental?: boolean;
} & (
| { | {
exclude?: false; exclude?: false;
maxThickness: number; maxThickness: number;
@@ -15,22 +17,34 @@ type ArmorMeshExternalModuleMaskProps = MeshProps &
} }
); );
export function ArmorMeshExternalModuleMask( export function ArmorMeshExternalModuleMask({
props: ArmorMeshExternalModuleMaskProps, ornamental = false,
) { ...props
}: ArmorMeshExternalModuleMaskProps) {
return ( return (
<> <>
{props.exclude && ( {(props.exclude || ornamental) && (
<mesh {...props} renderOrder={0}> <mesh {...props} renderOrder={0}>
<meshBasicMaterial colorWrite={false} /> <meshBasicMaterial colorWrite={false} />
</mesh> </mesh>
)} )}
{!props.exclude && ( {!props.exclude && (
<mesh {...props} renderOrder={1}> <mesh
{...props}
renderOrder={1}
onClick={ornamental ? () => {} : undefined}
userData={
{
type: 'externalModule',
thickness: props.thickness,
} satisfies ArmorMeshUserData
}
>
<meshBasicMaterial <meshBasicMaterial
color={new Color(1, props.thickness / props.maxThickness, 0)} color={new Color(1, props.thickness / props.maxThickness, 0)}
depthWrite={false} depthWrite={false}
colorWrite={!ornamental}
/> />
</mesh> </mesh>
)} )}
@@ -1,12 +1,14 @@
import { MeshProps } from '@react-three/fiber'; import { MeshProps } from '@react-three/fiber';
import { WebGLRenderTarget } from 'three'; import { WebGLRenderTarget } from 'three';
import { ArmorMeshUserData } from '../Armor';
import fragmentShader from './shaders/fragment.glsl'; import fragmentShader from './shaders/fragment.glsl';
import vertexShader from './shaders/vertex.glsl'; import vertexShader from './shaders/vertex.glsl';
export const spacedArmorDepthRenderTarget = new WebGLRenderTarget(); export const spacedArmorDepthRenderTarget = new WebGLRenderTarget();
type ArmorMeshSpacedArmorDepthProps = MeshProps & type ArmorMeshSpacedArmorDepthProps = MeshProps & {
( ornamental?: boolean;
} & (
| { | {
include?: false; include?: false;
isExternalModule: boolean; isExternalModule: boolean;
@@ -18,19 +20,31 @@ type ArmorMeshSpacedArmorDepthProps = MeshProps &
} }
); );
export function ArmorMeshSpacedArmorDepth( export function ArmorMeshSpacedArmorDepth({
props: ArmorMeshSpacedArmorDepthProps, ornamental = false,
) { ...props
}: ArmorMeshSpacedArmorDepthProps) {
return ( return (
<> <>
{!props.include && ( {!props.include && !ornamental && (
<mesh {...props} renderOrder={props.isExternalModule ? 2 : 0}> <mesh {...props} renderOrder={props.isExternalModule ? 2 : 0}>
<meshBasicMaterial colorWrite={false} /> <meshBasicMaterial colorWrite={false} />
</mesh> </mesh>
)} )}
{props.include && ( {props.include && (
<mesh {...props} renderOrder={1}> <mesh
{...props}
renderOrder={1}
onClick={ornamental ? () => {} : undefined}
userData={
{
type: 'spacedArmor',
thickness: props.thickness,
} satisfies ArmorMeshUserData
}
>
{!ornamental && (
<shaderMaterial <shaderMaterial
fragmentShader={fragmentShader} fragmentShader={fragmentShader}
vertexShader={vertexShader} vertexShader={vertexShader}
@@ -39,6 +53,11 @@ export function ArmorMeshSpacedArmorDepth(
maxThickness: { value: props.maxThickness }, maxThickness: { value: props.maxThickness },
}} }}
/> />
)}
{ornamental && (
<meshBasicMaterial colorWrite={false} depthWrite={false} />
)}
</mesh> </mesh>
)} )}
</> </>