feat: complete transition

This commit is contained in:
TrèsAbhi committed 2025-11-27 13:42:37 -06:00
1 parent cd4fa366a0
commit 1427f61c52
6 files changed
+41 -15

No files matched your search

@@ -67,7 +67,9 @@ export const PrimaryArmorScene = memo(() => {
<group ref={turretContainer}>
{armorNodes.map((node) => {
const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`
`turret_${turretModelDefinition.model_id
.toString()
.padStart(2, "0")}`
);
const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name);
@@ -4,6 +4,7 @@ import {
resolvePenetrationCoefficient,
} from "@blitzkit/core";
import { invalidate, useFrame } from "@react-three/fiber";
import type { QuicklimeEvent } from "quicklime";
import { useEffect } from "react";
import {
MeshBasicMaterial,
@@ -21,6 +22,7 @@ import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
import { Tankopedia } from "../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import fragmentShader from "./shaders/fragment.glsl?raw";
import vertexShader from "./shaders/vertex.glsl?raw";
import { spacedArmorRenderTarget } from "./target";
@@ -72,6 +74,8 @@ export function PrimaryArmorSceneComponent({
resolution: { value: new Vector2() },
spacedArmorBuffer: { value: null },
spacedArmorDepth: { value: null },
opacity: { value: 0 },
},
]),
});
@@ -154,6 +158,10 @@ export function PrimaryArmorSceneComponent({
if (!noInvalidate) invalidate();
}
function handleTransitionEvent(event: QuicklimeEvent<number>) {
material.uniforms.opacity.value = event.data;
}
handleShellChange();
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
handleAdvancedHighlightingChange(
@@ -164,6 +172,8 @@ export function PrimaryArmorSceneComponent({
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
transitionEvent.on(handleTransitionEvent);
const unsubscribes = [
Duel.on((state) => state.antagonist.shell, handleShellChange),
Tankopedia.on((state) => state.customShell, handleShellChange),
@@ -184,12 +194,13 @@ export function PrimaryArmorSceneComponent({
(state) => state.antagonist.equipmentMatrix,
(equipment) => handleAntagonistEquipmentChange(equipment)
),
() => transitionEvent.off(handleTransitionEvent),
];
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
});
}, []);
useFrame(({ gl, camera }) => {
gl.getSize(material.uniforms.resolution.value).multiplyScalar(
@@ -1,12 +1,9 @@
precision mediump float;
#define USE_FOG
#include <fog_pars_fragment>
varying vec3 vNormal;
varying vec3 vViewPosition;
uniform float opacity;
uniform float thickness;
uniform float penetration;
uniform float caliber;
@@ -119,5 +116,5 @@ void main() {
gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha);
}
#include <fog_fragment>
gl_FragColor.a *= opacity;
}
@@ -1,9 +1,5 @@
precision mediump float;
#define USE_FOG
#include <fog_pars_vertex>
varying vec3 vNormal;
varying vec3 vViewPosition;
@@ -14,6 +10,4 @@ void main() {
vNormal = normalMatrix * normal;
gl_Position = projectionMatrix * mvPosition;
#include <fog_vertex>
}
@@ -24,6 +24,7 @@ import {
} from "../../../core/blitzkit/modelTransform";
import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
import { Tankopedia } from "../../../stores/tankopedia";
import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { ArmorType } from "./SpacedArmorScene";
import type {
ArmorUserData,
@@ -108,10 +109,11 @@ export function StaticArmorSceneComponent({
() =>
new MeshBasicMaterial({
color,
opacity,
transparent: opacity < 1,
transparent: true,
depthWrite,
...(props.clip ? { clippingPlanes: [props.clip] } : {}),
opacity: 0,
userData: { opacity0: opacity },
}),
[thickness]
);
@@ -121,6 +123,8 @@ export function StaticArmorSceneComponent({
color: color
.clone()
.multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
opacity: 0,
transparent: true,
}),
[thickness]
);
@@ -163,11 +167,22 @@ export function StaticArmorSceneComponent({
surfaceMaterial.needsUpdate = true;
}
function handleTransitionEvent(event: QuicklimeEvent<number>) {
surfaceMaterial.opacity = surfaceMaterial.userData.opacity0 * event.data;
outlineMaterial.opacity = event.data;
surfaceMaterial.transparent = surfaceMaterial.opacity < 1;
outlineMaterial.transparent = outlineMaterial.opacity < 1;
}
transitionEvent.on(handleTransitionEvent);
const unsubscribes = [
Tankopedia.on(
(state) => state.highlightArmor?.name,
handleHighlightArmor
),
() => transitionEvent.off(handleTransitionEvent),
];
if (props.clip) {
@@ -1,5 +1,6 @@
import { useFrame, useThree } from "@react-three/fiber";
import { times } from "lodash-es";
import { Quicklime } from "quicklime";
import { useEffect, useRef } from "react";
import { SpotLight, type Group } from "three";
import { clamp, degToRad, lerp } from "three/src/math/MathUtils.js";
@@ -19,6 +20,8 @@ const LIGHT_HEIGHT_1 = 10;
const INTENSITY_0 = 60;
const INTENSITY_1 = 30;
export const transitionEvent = new Quicklime<number>(0);
export function Lighting() {
const wrapper = useRef<Group>(null!);
const t0 = useRef(0);
@@ -50,6 +53,10 @@ export function Lighting() {
);
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
if (t !== transitionEvent.last) {
transitionEvent.dispatch(t);
}
for (const child of wrapper.current.children) {
if (child instanceof SpotLight) {
child.angle = ANGLE * t;