From da704e6181904e5a7c44ea53d3219952bc4a01a1 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:18:24 -0500 Subject: [PATCH] feat: transition skeleton --- .../components/TransitionSkeleton/index.tsx | 116 ++++++++++++++++++ .../TransitionSkeleton/shaders/fragment.glsl | 9 ++ .../TransitionSkeleton/shaders/vertex.glsl | 17 +++ .../components/TankSandbox/index.tsx | 6 +- .../Tankopedia/HeroSection/index.tsx | 2 +- 5 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/index.tsx create mode 100644 packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/fragment.glsl create mode 100644 packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/vertex.glsl diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/index.tsx new file mode 100644 index 000000000..3416bcd53 --- /dev/null +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/index.tsx @@ -0,0 +1,116 @@ +import { useEffect, useRef } from 'react'; +import { Group, ShaderMaterial, UniformsLib, UniformsUtils } from 'three'; +import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree'; +import { useModel } from '../../../../../../../hooks/useModel'; +import { useTankModelDefinition } from '../../../../../../../hooks/useTankModelDefinition'; +import { useTankTransform } from '../../../../../../../hooks/useTankTransform'; +import { Duel } from '../../../../../../../stores/duel'; +import { + ShootingRangeZoom, + TankopediaEphemeral, +} from '../../../../../../../stores/tankopediaEphemeral'; +import { ModelTankWrapper } from '../../../../../../Armor/components/ModelTankWrapper'; +import fragmentShader from './shaders/fragment.glsl?raw'; +import vertexShader from './shaders/vertex.glsl?raw'; + +const skeletonMaterial = new ShaderMaterial({ + vertexShader, + fragmentShader, + wireframe: true, + fog: true, + transparent: true, + uniforms: UniformsUtils.merge([UniformsLib.common, UniformsLib.fog]), + depthWrite: false, +}); + +export function TransitionSkeleton() { + const protagonist = Duel.use((draft) => draft.protagonist); + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); + const track = Duel.use((state) => state.protagonist.track); + const turret = Duel.use((state) => state.protagonist.turret); + const hullContainer = useRef(null); + const turretContainer = useRef(null); + const gunContainer = useRef(null); + const tankModelDefinition = useTankModelDefinition(); + const turretModelDefinition = + tankModelDefinition.turrets[protagonist.turret.id]; + const gunModelDefinition = + turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id]; + const { gltf } = useModel(protagonist.tank.id); + const nodes = Object.values(gltf.nodes); + const revealed = tankopediaEphemeralStore((state) => state.revealed); + + useEffect(() => { + const unsubscribeShootingRangeZoom = tankopediaEphemeralStore.subscribe( + (state) => state.shootingRangeZoom, + (zoom) => { + if (!hullContainer.current) return; + hullContainer.current.visible = zoom === ShootingRangeZoom.Arcade; + }, + ); + + return unsubscribeShootingRangeZoom; + }, []); + + useTankTransform(track, turret, turretContainer, gunContainer); + + return ( + + {nodes.map((node) => { + const isHull = node.name === 'hull'; + const isWheel = node.name.startsWith('chassis_wheel_'); + const isTrack = node.name.startsWith('chassis_track_'); + const isVisible = isHull || isWheel || isTrack; + + if (!isVisible) return null; + + return jsxTree(node, { + mesh(_, props, key) { + return ; + }, + }); + })} + + + {nodes.map((node) => { + const isCurrentTurret = + node.name === + `turret_${turretModelDefinition.model_id.toString().padStart(2, '0')}`; + const isVisible = isCurrentTurret; + + if (!isVisible) return null; + + return jsxTree(node, { + mesh(_, props, key) { + return ; + }, + }); + })} + + + {nodes.map((node) => { + const isCurrentMantlet = + node.name === + `gun_${gunModelDefinition.model_id + .toString() + .padStart(2, '0')}_mask`; + const isCurrentGun = + node.name === + `gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`; + const isVisible = isCurrentGun || isCurrentMantlet; + + if (!isVisible) return null; + + return jsxTree(node, { + mesh(_, props, key) { + return ( + + ); + }, + }); + })} + + + + ); +} diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/fragment.glsl b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/fragment.glsl new file mode 100644 index 000000000..52a28e62b --- /dev/null +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/fragment.glsl @@ -0,0 +1,9 @@ +#define USE_FOG + +#include + +void main() { + float fogFactor = smoothstep(fogNear, fogFar, vFogDepth); + gl_FragColor = vec4(0.75, 0.75, 0.75, fogFactor); + gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, 1.0 - fogFactor); +} \ No newline at end of file diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/vertex.glsl b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/vertex.glsl new file mode 100644 index 000000000..d80ba8239 --- /dev/null +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/TransitionSkeleton/shaders/vertex.glsl @@ -0,0 +1,17 @@ +#define USE_FOG + +#include + +varying vec3 vNormal; +varying vec3 vViewPosition; + +void main() { + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + + vViewPosition = mvPosition.xyz; + vNormal = normalMatrix * normal; + + gl_Position = projectionMatrix * mvPosition; + + #include +} diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx index a79b1fc32..7133ca8a0 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -32,17 +32,18 @@ import { InitialFogReveal } from './components/InitialFogReveal'; import { Lighting } from './components/Lighting'; import { SceneProps } from './components/SceneProps'; import { TankModel } from './components/TankModel'; +import { TransitionSkeleton } from './components/TransitionSkeleton'; interface TankSandboxProps { thicknessRange: ThicknessRange; naked?: boolean; } -export const forNear0 = 15; +export const forNear0 = 20; export const fogFar0 = 25; export const forNear1 = 0; export const fogFar1 = 0; -export const fogAnimationTime = 0.5; +export const fogAnimationTime = 1.5; export const TankSandbox = forwardRef( ({ thicknessRange, naked }, ref) => { @@ -237,6 +238,7 @@ export const TankSandbox = forwardRef( {(display === TankopediaDisplay.Model || (display === TankopediaDisplay.DynamicArmor && !hideTankModelUnderArmor)) && } + diff --git a/packages/website/src/components/Tankopedia/HeroSection/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/index.tsx index d99e68206..66f911b4b 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/index.tsx @@ -187,7 +187,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {