mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-25 03:29:51 +01:00
feat: basic click functionality
This commit is contained in:
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>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
+16
-3
@@ -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>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
+16
-3
@@ -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>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in new issue
Block a user