feat: custom shaders

This commit is contained in:
TrèsAbhi committed 2024-01-09 22:31:34 -06:00
1 parent a7d8f08522
commit 4a6b5e3aa9
9 files changed
+176 -46

No files matched your search

@@ -0,0 +1,19 @@
export function Lighting() {
return (
<>
<directionalLight
position={[1, 1, -1]}
intensity={2}
castShadow
color={'rgb(225, 225, 255)'}
/>
<directionalLight
position={[-1, 1, 1]}
intensity={1}
castShadow
color={'rgb(225, 225, 255)'}
/>
<ambientLight intensity={0.25} />
</>
);
}
@@ -17,9 +17,9 @@ import mutateTankopedia, {
} from '../../../../../../stores/tankopedia';
import { Loader } from '../../../../components/Loader';
import { Controls } from '../Control';
import { Lighting } from '../Lighting';
import { RotationInputs } from '../RotationInputs';
import { SceneProps } from '../SceneProps';
import { TankArmor } from './components/TankArmor';
import { TankModel } from './components/TankModel';
export function TankDisplay() {
@@ -117,9 +117,11 @@ export function TankDisplay() {
ref={canvas}
camera={{ fov: 20 }}
onPointerDown={handlePointerDown}
gl={{ logarithmicDepthBuffer: true }}
>
<Controls />
<SceneProps />
<Lighting />
<Suspense
fallback={
@@ -132,7 +134,6 @@ export function TankDisplay() {
}
>
<TankModel ref={hullContainer} />
{mode === 'armor' && <TankArmor />}
</Suspense>
</Canvas>
</div>
@@ -68,6 +68,7 @@ export const TankArmor = forwardRef<Group>((_props, ref) => {
return (
<mesh
scale={1 + 1e-3}
key={node.uuid}
geometry={(node as Mesh).geometry}
position={position}
@@ -1,6 +1,7 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
import { Group, Mesh, Object3D, Vector3 } from 'three';
import { ArmorHighlightMaterial } from '../../../../../../../../../components/ArmorHighlightMaterial';
import { applyPitchYawLimits } from '../../../../../../../../../core/blitz/applyPitchYawLimits';
import {
InitialTurretRotation,
@@ -15,7 +16,8 @@ interface GunContainerProps {
gunOrigin: Vector3;
pitch: number;
yaw: number;
objects: Object3D[];
modelObjects: Object3D[];
armorObjects: Object3D[];
model: number;
yawLimits?: YawLimits;
pitchLimits: PitchLimits;
@@ -33,10 +35,11 @@ export const GunContainer = forwardRef<Group, GunContainerProps>(
pitch,
initialTurretRotation,
yaw,
objects,
modelObjects,
yawLimits,
onPitchStart,
turretContainer,
armorObjects,
pitchLimits,
model,
onPitchEnd,
@@ -59,7 +62,7 @@ export const GunContainer = forwardRef<Group, GunContainerProps>(
rotation={[pitch, 0, 0]}
ref={gunContainer}
>
{objects.map((object) => {
{modelObjects.map((object) => {
const isCurrentMantlet =
object.name === `gun_${model.toString().padStart(2, '0')}_mask`;
const isCurrentGun =
@@ -143,6 +146,25 @@ export const GunContainer = forwardRef<Group, GunContainerProps>(
/>
);
})}
{armorObjects.map((object) => {
const isCurrentGun = object.name.startsWith(
`gun_${model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentGun;
if (!isVisible) return null;
return (
<mesh
position={turretOrigin.clone().add(gunOrigin)}
key={object.uuid}
geometry={(object as Mesh).geometry}
>
<ArmorHighlightMaterial />
</mesh>
);
})}
</group>
);
},
@@ -1,13 +1,22 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
import { Group, Mesh, MeshStandardMaterial, Object3D, Vector2 } from 'three';
import {
Euler,
Group,
Mesh,
MeshStandardMaterial,
Object3D,
Vector2,
} from 'three';
import { ArmorHighlightMaterial } from '../../../../../../../../../components/ArmorHighlightMaterial';
import { resolveJsxTree } from '../../../../../../../../../core/blitzkrieg/resolveJsxTree';
import { normalizeAnglePI } from '../../../../../../../../../core/math/normalizeAngle180';
interface HullContainerProps {
children: ReactNode;
yaw: number;
objects: Object3D[];
modelObjects: Object3D[];
armorObjects: Object3D[];
onYawStart: () => void;
onYawEnd: (yaw: number) => void;
onTrackStart: () => void;
@@ -16,17 +25,27 @@ interface HullContainerProps {
export const HullContainer = forwardRef<Group, HullContainerProps>(
(
{ children, yaw, objects, onYawStart, onYawEnd, onTrackEnd, onTrackStart },
{
children,
yaw,
armorObjects,
modelObjects,
onYawStart,
onYawEnd,
onTrackEnd,
onTrackStart,
},
ref,
) => {
const canvas = useThree((state) => state.gl.domElement);
const hullContainer = useRef<Group>(null);
const rotation = [-Math.PI / 2, 0, yaw] as unknown as Euler;
useImperativeHandle(ref, () => hullContainer.current!);
return (
<group ref={hullContainer} rotation={[-Math.PI / 2, 0, yaw]}>
{objects.map((object) => {
<group ref={hullContainer} rotation={rotation}>
{modelObjects.map((object) => {
const isHull = object.name === 'hull';
const isWheel = object.name.startsWith('chassis_wheel_');
const isTrack = object.name.startsWith('chassis_track_');
@@ -96,6 +115,19 @@ export const HullContainer = forwardRef<Group, HullContainerProps>(
);
})}
{armorObjects.map((object) => {
const isHull = object.name.startsWith('hull_');
const isVisible = isHull;
if (!isVisible) return null;
return (
<mesh key={object.uuid} geometry={(object as Mesh).geometry}>
<ArmorHighlightMaterial />
</mesh>
);
})}
{children}
</group>
);
@@ -7,6 +7,7 @@ import {
useRef,
} from 'react';
import { Euler, Group, Mesh, Object3D, Vector3 } from 'three';
import { ArmorHighlightMaterial } from '../../../../../../../../../components/ArmorHighlightMaterial';
import { applyPitchYawLimits } from '../../../../../../../../../core/blitz/applyPitchYawLimits';
import {
InitialTurretRotation,
@@ -19,7 +20,8 @@ import { normalizeAnglePI } from '../../../../../../../../../core/math/normalize
interface TurretContainerProps {
turretOrigin: Vector3;
gunOrigin: Vector3;
objects: Object3D[];
modelObjects: Object3D[];
armorObjects: Object3D[];
children: ReactNode;
yaw: number;
pitch: number;
@@ -39,12 +41,13 @@ export const TurretContainer = forwardRef(
turretOrigin,
yaw,
children,
objects,
modelObjects,
yawLimits,
model,
onYawEnd,
pitch,
gunOrigin,
armorObjects,
pitchLimits,
onYawStart,
initialTurretRotation,
@@ -78,7 +81,7 @@ export const TurretContainer = forwardRef(
return (
<group position={position} rotation={rotation} ref={turretContainer}>
{objects.map((object) => {
{modelObjects.map((object) => {
const isTurret = object.name.startsWith('turret_');
const isCurrentTurret =
object.name === `turret_${model.toString().padStart(2, '0')}`;
@@ -162,6 +165,25 @@ export const TurretContainer = forwardRef(
);
})}
{armorObjects.map((object) => {
const isCurrentTurret = object.name.startsWith(
`turret_${model.toString().padStart(2, '0')}`,
);
const isVisible = isCurrentTurret;
if (!isVisible) return null;
return (
<mesh
position={turretOrigin}
key={object.uuid}
geometry={(object as Mesh).geometry}
>
<ArmorHighlightMaterial />
</mesh>
);
})}
{children}
</group>
);
@@ -34,10 +34,14 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
if (!protagonist) return null;
const gltf = useLoader(
const modelGltf = useLoader(
GLTFLoader,
asset(`3d/tanks/models/${protagonist.tank.id}.glb`),
);
const armorGltf = useLoader(
GLTFLoader,
asset(`3d/tanks/armor/${protagonist.tank.id}.glb`),
);
const model = useTankopedia((state) => state.model);
const hullContainer = useRef<Group>(null);
const turretContainer = useRef<Group>(null);
@@ -69,7 +73,8 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
return (
<HullContainer
objects={Object.values(gltf.nodes)}
modelObjects={Object.values(modelGltf.nodes)}
armorObjects={Object.values(armorGltf.nodes)}
yaw={model.hullYaw}
ref={hullContainer}
onYawStart={() =>
@@ -95,10 +100,11 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
}}
>
<TurretContainer
armorObjects={Object.values(armorGltf.nodes)}
initialTurretRotation={tankModelDefinition.turretRotation}
gunOrigin={gunOrigin}
ref={turretContainer}
objects={Object.values(gltf.nodes)}
modelObjects={Object.values(modelGltf.nodes)}
model={turretModelDefinition.model}
onYawStart={() =>
mutateTankopedia((state) => {
@@ -121,6 +127,7 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
>
<GunContainer
ref={gunContainer}
armorObjects={Object.values(armorGltf.nodes)}
initialTurretRotation={tankModelDefinition.turretRotation}
onPitchStart={() => {
mutateTankopedia((state) => {
@@ -141,7 +148,7 @@ export const TankModel = forwardRef<Group>((_props, ref) => {
gunOrigin={gunOrigin}
pitch={model.gunPitch}
model={gunModelDefinition.model}
objects={Object.values(gltf.nodes)}
modelObjects={Object.values(modelGltf.nodes)}
turretOrigin={turretOrigin}
/>
</TurretContainer>
@@ -5,39 +5,23 @@ import { useTankopedia } from '../../../../../stores/tankopedia';
export function SceneProps() {
const showGrid = useTankopedia((state) => state.model.showGrid);
if (!showGrid) return null;
return (
<>
{showGrid && (
<>
<InfiniteGridHelper
size1={1 / 5}
size2={1}
distance={20}
color={new Color('#ffffff')}
/>
<InfiniteGridHelper
position={[0, 1e-4, 0]}
size1={0}
size2={100}
distance={25}
color={new Color('red')}
/>
</>
)}
<directionalLight
position={[1, 1, -1]}
intensity={2}
castShadow
color={'rgb(225, 225, 255)'}
<InfiniteGridHelper
size1={1 / 5}
size2={1}
distance={20}
color={new Color('#ffffff')}
/>
<directionalLight
position={[-1, 1, 1]}
intensity={1}
castShadow
color={'rgb(225, 225, 255)'}
<InfiniteGridHelper
position={[0, 1e-4, 0]}
size1={0}
size2={100}
distance={25}
color={new Color('red')}
/>
<ambientLight intensity={0.25} />
</>
);
}
+42
View File
@@ -0,0 +1,42 @@
import { GreaterEqualDepth } from 'three';
const INFLATE = '0.0';
export function ArmorHighlightMaterial() {
return (
<shaderMaterial
depthFunc={GreaterEqualDepth}
args={[
{
transparent: true,
vertexShader: `
varying vec3 vNormal;
varying vec3 vViewPosition; // Camera position in view space
void main() {
vNormal = normalMatrix * normal; // Transform normal to view space
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; // Camera position in view space
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
// Fragment Shader
varying vec3 vNormal;
varying vec3 vViewPosition;
void main() {
// Use vNormal for your fragment shader calculations
vec3 normal = normalize(vNormal);
float uhhh = dot(normal, vViewPosition) * 0.5 + 1.0;
// ... rest of your fragment shader code ...
gl_FragColor = vec4(1.0, 0.0, 0.0, uhhh);
}
`,
},
]}
/>
);
}