feat: more progress

This commit is contained in:
TrèsAbhi committed 2025-08-28 17:59:31 -05:00
1 parent 94cbd95509
commit aa763f65d1
9 files changed
+82 -161

No files matched your search

@@ -30,7 +30,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const wrapper = useRef<Group>(null);
const turretContainer = useRef<Group>(null);
const gunContainer = useRef<Group>(null);
const protagonist = Duel.use((draft) => draft.protagonist);
const tank = Duel.use((state) => state.protagonist.tank);
const track = Duel.use((state) => state.protagonist.track);
const turret = Duel.use((state) => state.protagonist.turret);
@@ -46,7 +45,6 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const hullOrigin = correctZYTuple(trackModelDefinition.origin);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin);
const mutateDuel = Duel.useMutation();
const canvas = useThree((state) => state.gl.domElement);
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const duelStore = Duel.useStore();
@@ -10,7 +10,7 @@ import {
FrontSide,
LineBasicMaterial,
Mesh,
MeshStandardMaterial,
MeshBasicMaterial,
Object3D,
Plane,
Quaternion,
@@ -108,7 +108,7 @@ export function StaticArmorSceneComponent({
const surfaceMaterial = useMemo(
() =>
new MeshStandardMaterial({
new MeshBasicMaterial({
color,
opacity,
transparent: opacity < 1,
@@ -0,0 +1,20 @@
import type { PointLightProps } from '@react-three/fiber';
interface Props extends PointLightProps {
debug?: boolean;
}
export function HelpingPointLight({ debug, ...props }: Props) {
return (
<>
{debug && (
<mesh position={props.position}>
<icosahedronGeometry args={[0.1, 2]} />
<meshBasicMaterial color={props.color} />
</mesh>
)}
<spotLight {...props} />
</>
);
}
@@ -1,92 +1,20 @@
import { TankType } from '@blitzkit/core';
import { Flex, Heading } from '@radix-ui/themes';
import { Var } from '../../../../../../core/radix/var';
import { useLocale } from '../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../stores/duel';
import type { RadixColor } from '../../../../../../stores/embedState';
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
import { Tracker } from './components/Tracker';
interface TitleProps {
foreground?: boolean;
}
export const NATION_COLORS: Record<
string,
{
text: RadixColor;
background: RadixColor[];
tint: RadixColor;
}
> = {
france: {
text: 'mint',
tint: 'purple',
background: ['mint', 'cyan', 'jade'],
},
germany: {
text: 'sage',
tint: 'blue',
background: ['slate', 'sage', 'mauve'],
},
usa: {
text: 'gold',
tint: 'red',
background: ['brown', 'gold', 'gray'],
},
european: {
text: 'sky',
tint: 'violet',
background: ['cyan', 'blue', 'indigo'],
},
ussr: {
text: 'bronze',
tint: 'red',
background: ['bronze', 'tomato', 'bronze'],
},
uk: {
text: 'gold',
tint: 'red',
background: ['bronze', 'gold', 'brown'],
},
japan: {
text: 'mint',
tint: 'violet',
background: ['green', 'teal', 'jade'],
},
china: {
text: 'mint',
tint: 'pink',
background: ['green', 'teal', 'jade'],
},
other: {
text: 'amber',
tint: 'sky',
background: ['amber', 'yellow', 'gray'],
},
};
export function Title({ foreground }: TitleProps) {
export function Title() {
const { unwrap } = useLocale();
const protagonist = Duel.use((state) => state.protagonist.tank);
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
const revealed = TankopediaEphemeral.use((state) => state.revealed);
const nationColors = NATION_COLORS[protagonist.nation];
const color = Var(
disturbed
? protagonist.type === TankType.COLLECTOR
? 'blue-11'
: protagonist.type === TankType.PREMIUM
? 'amber-11'
: 'gray-12'
: `${nationColors.text}-12`,
);
const name = unwrap(protagonist.name);
const fontSize = revealed
? disturbed
? '2rem'
: `min(65vh, ${125 / name.length}vw)`
: `min(48vh, ${75 / name.length}vw)`;
: `min(16vh, ${125 / name.length}vw)`
: `min(12vh, ${75 / name.length}vw)`;
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
return (
@@ -119,8 +47,7 @@ export function Title({ foreground }: TitleProps) {
pointerEvents: 'none',
fontSize,
whiteSpace: 'nowrap',
color: foreground ? Var('gray-a10') : color,
opacity: foreground && disturbed ? 0 : 1,
opacity: disturbed ? 1 : 0.5,
letterSpacing: disturbed || !revealed ? 0 : '-0.03em',
transition: `
letter-spacing 1.5s ${disturbed ? '' : 'cubic-bezier(0.81, -2, 0.68, 1)'},
@@ -1,59 +0,0 @@
import { Environment } from '@react-three/drei';
import { useEffect, useRef } from 'react';
import { DirectionalLight, Euler } from 'three';
import { degToRad } from 'three/src/math/MathUtils.js';
import { useModel } from '../../../../../../hooks/useModel';
import { Duel } from '../../../../../../stores/duel';
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
const LIGHTS_COUNT = 5;
const SIZE = 2 ** 11;
interface LightingProps {
display: TankopediaDisplay;
}
export function Lighting({ display }: LightingProps) {
const protagonist = Duel.use((state) => state.protagonist);
const { hasPbr } = useModel(protagonist.tank.id);
const isBrighterLighting =
!hasPbr && display !== TankopediaDisplay.StaticArmor;
const light = useRef<DirectionalLight>(null);
useEffect(() => {
if (!light.current) return;
light.current.shadow.mapSize.width = SIZE;
light.current.shadow.mapSize.height = SIZE;
}, []);
return (
<>
<directionalLight
position={[-2, 0.5, -1]}
intensity={5}
castShadow
ref={light}
/>
<directionalLight position={[2, -0.5, 0]} intensity={1} />
<directionalLight position={[0, -0.5, -2]} intensity={0.5} />
<ambientLight intensity={1} />
</>
);
return (
<>
{display !== TankopediaDisplay.StaticArmor && (
<Environment
files="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/1k/winter_river_1k.hdr"
environmentIntensity={isBrighterLighting ? 1.5 : 1.25}
environmentRotation={new Euler(0, degToRad(180), 0)}
/>
)}
{display === TankopediaDisplay.StaticArmor && (
<Environment preset="lobby" />
)}
</>
);
}
@@ -0,0 +1,41 @@
import { useHelper } from '@react-three/drei';
import { times } from 'lodash-es';
import { useRef } from 'react';
import { SpotLight, SpotLightHelper } from 'three';
import { HelpingPointLight } from '../../../../../../HelpingPointLight';
const LIGHTS_COUNT = 5;
const LIGHT_DISTANCE = 8;
const LIGHT_HEIGHT_0 = 4;
const LIGHT_HEIGHT_1 = 2;
const INTENSITY = 500;
export function Lighting() {
const topLight = useRef<SpotLight>(null!);
const frontLight = useRef<SpotLight>(null!);
useHelper(topLight, SpotLightHelper, 'red');
useHelper(frontLight, SpotLightHelper, 'green');
return (
<>
{times(LIGHTS_COUNT, (index) => {
const x = index / (LIGHTS_COUNT - 1);
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) - (7 / 8) * Math.PI;
const position = [
LIGHT_DISTANCE * Math.sin(theta),
LIGHT_HEIGHT_0 * (1 - x) + LIGHT_HEIGHT_1 * x,
LIGHT_DISTANCE * Math.cos(theta),
] as const;
return (
<HelpingPointLight
position={position}
intensity={INTENSITY / LIGHTS_COUNT}
castShadow
/>
);
})}
</>
);
}
@@ -1,27 +1,27 @@
import { useFrame, useLoader } from '@react-three/fiber';
import { useFrame } from '@react-three/fiber';
import { clamp } from 'lodash-es';
import { useRef } from 'react';
import { MeshStandardMaterial, TextureLoader } from 'three';
import { TankopediaEphemeral } from '../../../../../../../stores/tankopediaEphemeral';
import { MeshStandardMaterial } from 'three';
const SIZE = 2 ** 6;
export function SceneProps() {
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
const material = useRef<MeshStandardMaterial>(null);
const texture = useLoader(TextureLoader, '/assets/images/3d/grid.png');
texture.anisotropy = 2;
useFrame(({ camera }) => {
if (!material.current) return;
material.current.opacity = clamp(0.5 * camera.position.y, 0, 1);
});
if (!disturbed) return null;
return (
<mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
<planeGeometry args={[10, 10]} />
<meshStandardMaterial ref={material} map={texture} transparent />
<planeGeometry args={[SIZE, SIZE]} />
<meshStandardMaterial
transparent
ref={material}
color={[0, 0, 0]}
roughness={1}
/>
</mesh>
);
}
@@ -2,7 +2,6 @@ import { Box, Flex } from '@radix-ui/themes';
import { times } from 'lodash-es';
import { Suspense, useEffect, useMemo, useRef } from 'react';
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
import { Var } from '../../../core/radix/var';
import { useFullScreen } from '../../../hooks/useFullScreen';
import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
@@ -10,7 +9,7 @@ import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonCo
import type { ThicknessRange } from '../../Armor/components/StaticArmor';
import { Options } from './components/Options';
import { TankSandbox } from './components/TankSandbox';
import { NATION_COLORS, Title } from './components/TankSandbox/Title';
import { Title } from './components/TankSandbox/Title';
const tankDefinitions = await awaitableTankDefinitions;
@@ -40,7 +39,6 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
const duelStore = Duel.useStore();
const mutateDuel = Duel.useMutation();
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
const nationColors = NATION_COLORS[protagonist.nation];
useEffect(() => {
if (disturbed) {
@@ -85,9 +83,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
left="0"
style={{
opacity: disturbed ? 0 : 1,
background: `linear-gradient(${nationColors.background
.map((color) => Var(`${color}-2`))
.join(',')})`,
background: 'black',
transitionDuration: '1s',
}}
/>
@@ -118,8 +114,6 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
position="relative"
>
<Box position="absolute" width="100%" height="100%" overflow="hidden">
<Title />
<Box
width="100%"
height="100%"
@@ -145,7 +139,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
</Box>
</Box>
<Title foreground />
<Title />
<Options
skeleton={skeleton}
+1 -1
View File
@@ -138,7 +138,7 @@ export const TOOLS: Tool[] = [
compareTool,
performanceTool,
guessTool,
mixerTool,
// mixerTool,
// chartsTool,
galleryTool,
sessionTool,