mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: more progress
This commit is contained in:
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} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
+4
-77
@@ -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)'},
|
||||
|
||||
-59
@@ -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" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+41
@@ -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
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+11
-11
@@ -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}
|
||||
|
||||
@@ -138,7 +138,7 @@ export const TOOLS: Tool[] = [
|
||||
compareTool,
|
||||
performanceTool,
|
||||
guessTool,
|
||||
mixerTool,
|
||||
// mixerTool,
|
||||
// chartsTool,
|
||||
galleryTool,
|
||||
sessionTool,
|
||||
|
||||
Reference in new issue
Block a user