From 6d8387fa9d91126b04f693149d1bcd6f9d8d2769 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Mon, 14 Apr 2025 18:52:49 -0500 Subject: [PATCH] feat: foggy transition --- .../PrimaryArmorSceneComponent/index.tsx | 51 ++++++++++++------- .../shaders/fragment.glsl | 8 +++ .../shaders/vertex.glsl | 14 +++-- .../website/src/components/Armor/index.tsx | 10 +++- .../TankSandbox/components/Lighting.tsx | 8 +-- .../components/TankSandbox/index.tsx | 48 +++++++++++++++-- .../Tankopedia/HeroSection/index.tsx | 10 +--- 7 files changed, 109 insertions(+), 40 deletions(-) diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx index 45a326318..69830885a 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx @@ -5,7 +5,14 @@ import { } from '@blitzkit/core'; import { invalidate, useFrame } from '@react-three/fiber'; import { useEffect } from 'react'; -import { MeshBasicMaterial, Object3D, ShaderMaterial, Vector2 } from 'three'; +import { + MeshBasicMaterial, + Object3D, + ShaderMaterial, + UniformsLib, + UniformsUtils, + Vector2, +} from 'three'; import { degToRad } from 'three/src/math/MathUtils.js'; import { hasEquipment } from '../../../../core/blitzkit/hasEquipment'; import { jsxTree } from '../../../../core/blitzkit/jsxTree'; @@ -36,27 +43,33 @@ export function PrimaryArmorSceneComponent({ fragmentShader, vertexShader, + fog: true, transparent: true, - uniforms: { - thickness: { value: null }, - penetration: { value: null }, - caliber: { value: null }, - ricochet: { value: null }, - normalization: { value: null }, - isExplosive: { value: null }, - canSplash: { value: null }, - damage: { value: null }, - explosionRadius: { value: null }, - greenPenetration: { value: null }, - advancedHighlighting: { value: null }, - opaque: { value: null }, + uniforms: UniformsUtils.merge([ + UniformsLib.common, + UniformsLib.fog, - inverseProjectionMatrix: { value: null }, - resolution: { value: new Vector2() }, - spacedArmorBuffer: { value: null }, - spacedArmorDepth: { value: null }, - }, + { + thickness: { value: null }, + penetration: { value: null }, + caliber: { value: null }, + ricochet: { value: null }, + normalization: { value: null }, + isExplosive: { value: null }, + canSplash: { value: null }, + damage: { value: null }, + explosionRadius: { value: null }, + greenPenetration: { value: null }, + advancedHighlighting: { value: null }, + opaque: { value: null }, + + inverseProjectionMatrix: { value: null }, + resolution: { value: new Vector2() }, + spacedArmorBuffer: { value: null }, + spacedArmorDepth: { value: null }, + }, + ]), }); useEffect(() => { diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl index 3b3fef938..916e480ec 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl @@ -1,3 +1,7 @@ +#define USE_FOG + +#include + varying vec3 vNormal; varying vec3 vViewPosition; @@ -39,6 +43,8 @@ vec3 getPenetrationColor(bool isThreeCalibersRule, bool couldHaveRicochet) { } void main() { + #include + vec2 screenCoord = gl_FragCoord.xy / resolution; vec4 spacedArmorData = texture2D(spacedArmorBuffer, screenCoord); bool isUnderSpacedArmor = spacedArmorData.a != 0.0; @@ -111,4 +117,6 @@ void main() { } else { gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha); } + + #include } \ No newline at end of file diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/vertex.glsl b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/vertex.glsl index 82f94e255..d80ba8239 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/vertex.glsl +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/vertex.glsl @@ -1,11 +1,17 @@ +#define USE_FOG + +#include + varying vec3 vNormal; varying vec3 vViewPosition; void main() { - vec4 vViewPosition4 = modelViewMatrix * vec4(position, 1.0); + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); - vViewPosition = vViewPosition4.xyz; + vViewPosition = mvPosition.xyz; vNormal = normalMatrix * normal; - gl_Position = projectionMatrix * vViewPosition4; -} \ No newline at end of file + gl_Position = projectionMatrix * mvPosition; + + #include +} diff --git a/packages/website/src/components/Armor/index.tsx b/packages/website/src/components/Armor/index.tsx index fe1927fd5..580133ab8 100644 --- a/packages/website/src/components/Armor/index.tsx +++ b/packages/website/src/components/Armor/index.tsx @@ -1,4 +1,4 @@ -import { createPortal, useFrame } from '@react-three/fiber'; +import { createPortal, useFrame, useThree } from '@react-three/fiber'; import { memo, useState } from 'react'; import { DepthTexture, Scene, Vector2 } from 'three'; import { PrimaryArmorScene } from './components/PrimaryArmorScene'; @@ -6,8 +6,14 @@ import { spacedArmorRenderTarget } from './components/PrimaryArmorSceneComponent import { SpacedArmorScene } from './components/SpacedArmorScene'; export const Armor = memo(() => { + const rootScene = useThree((state) => state.scene); const [spacedArmorScene] = useState(() => new Scene()); - const [primaryArmorScene] = useState(() => new Scene()); + const [primaryArmorScene] = useState(() => { + const primaryArmorScene = new Scene(); + primaryArmorScene.fog = rootScene.fog; + + return primaryArmorScene; + }); const spacedArmorPortal = createPortal( , spacedArmorScene, diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx index ba61d56cf..aca126f8f 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Lighting.tsx @@ -4,14 +4,16 @@ import { Euler } from 'three'; import { degToRad } from 'three/src/math/MathUtils.js'; import { useModel } from '../../../../../../hooks/useModel'; import { Duel } from '../../../../../../stores/duel'; -import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants'; const LIGHTS_COUNT = 5; -export function Lighting() { +interface LightingProps { + display: TankopediaDisplay; +} + +export function Lighting({ display }: LightingProps) { const protagonist = Duel.use((state) => state.protagonist); - const display = TankopediaEphemeral.use((state) => state.display); const { hasPbr } = useModel(protagonist.tank.id); const isBrighterLighting = !hasPbr && display !== TankopediaDisplay.StaticArmor; 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 fb383bcd1..e50b155d5 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -1,12 +1,14 @@ import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core'; -import { Canvas } from '@react-three/fiber'; +import { Canvas, invalidate } from '@react-three/fiber'; import { forwardRef, Suspense, useEffect, useImperativeHandle, useRef, + useState, } from 'react'; +import { Fog } from 'three'; import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits'; import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform'; import { Pose, poseEvent } from '../../../../../core/blitzkit/pose'; @@ -36,8 +38,15 @@ interface TankSandboxProps { naked?: boolean; } +const near0 = 15; +const far0 = 25; +const near1 = 0; +const far1 = 0; +const animationTime = 0.5; + export const TankSandbox = forwardRef( ({ thicknessRange, naked }, ref) => { + const fog = useRef(new Fog('black', near0, far0)); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const canvas = useRef(null); const hasImprovedVerticalStabilizer = useEquipment(122); @@ -50,12 +59,42 @@ export const TankSandbox = forwardRef( tankModelDefinition.turrets[protagonistTurret.id]; const gunModelDefinition = turretModelDefinition.guns[protagonistGun.gun_type!.value.base.id]; - const display = TankopediaEphemeral.use((state) => state.display); + const rawDisplay = TankopediaEphemeral.use((state) => state.display); + const [display, setDisplay] = useState(rawDisplay); const hideTankModelUnderArmor = TankopediaPersistent.use( (state) => state.hideTankModelUnderArmor, ); const onScreen = useOnScreen(canvas); + useEffect(() => { + if (rawDisplay === display) return; + + const t0 = Date.now(); + + const interval = setInterval((e) => { + const t = (Date.now() - t0) / 1000; + const x = t / animationTime; + const y = Math.cbrt(-2 * Math.abs(x - 0.5)) + 1; + + const near = near0 + (near1 - near0) * y; + const far = far0 + (far1 - far0) * y; + + fog.current.near = near; + fog.current.far = far; + + invalidate(); + + if (rawDisplay !== display && x >= 0.5) setDisplay(rawDisplay); + if (x >= 1) { + fog.current.near = near0; + fog.current.far = far0; + clearInterval(interval); + } + }, 1000 / 60); + + return () => clearInterval(interval); + }, [rawDisplay]); + useImperativeHandle(ref, () => canvas.current!, []); function handlePointerDown() { @@ -166,6 +205,8 @@ export const TankSandbox = forwardRef( return ( ( preserveDrawingBuffer: true, }} shadows - ref={canvas} onPointerDown={handlePointerDown} onPointerMissed={() => { mutateTankopediaEphemeral((draft) => { @@ -208,7 +248,7 @@ export const TankSandbox = forwardRef( {display === TankopediaDisplay.StaticArmor && ( )} - + ); diff --git a/packages/website/src/components/Tankopedia/HeroSection/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/index.tsx index 2b653382d..06482662a 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/index.tsx @@ -4,7 +4,6 @@ import { times } from 'lodash-es'; import { Suspense, useEffect, useMemo, useRef } from 'react'; import { NAVBAR_HEIGHT } from '../../../constants/navbar'; import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions'; -import { Var } from '../../../core/radix/var'; import { useFullScreen } from '../../../hooks/useFullScreen'; import { useLocale } from '../../../hooks/useLocale'; import { Duel } from '../../../stores/duel'; @@ -74,15 +73,10 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) { }, []); return ( - +