mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: transition skeleton
This commit is contained in:
1 parent
64d346ccb6
commit
da704e6181
5 files changed
+147
-3
No files matched your search
+116
@@ -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<Group>(null);
|
||||
const turretContainer = useRef<Group>(null);
|
||||
const gunContainer = useRef<Group>(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 (
|
||||
<ModelTankWrapper ref={hullContainer} visible={revealed}>
|
||||
{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 <mesh {...props} key={key} material={skeletonMaterial} />;
|
||||
},
|
||||
});
|
||||
})}
|
||||
|
||||
<group ref={turretContainer}>
|
||||
{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 <mesh {...props} key={key} material={skeletonMaterial} />;
|
||||
},
|
||||
});
|
||||
})}
|
||||
|
||||
<group ref={gunContainer}>
|
||||
{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 (
|
||||
<mesh {...props} key={key} material={skeletonMaterial} />
|
||||
);
|
||||
},
|
||||
});
|
||||
})}
|
||||
</group>
|
||||
</group>
|
||||
</ModelTankWrapper>
|
||||
);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#define USE_FOG
|
||||
|
||||
#include <fog_pars_fragment>
|
||||
|
||||
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);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#define USE_FOG
|
||||
|
||||
#include <fog_pars_vertex>
|
||||
|
||||
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 <fog_vertex>
|
||||
}
|
||||
+4
-2
@@ -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<HTMLCanvasElement, TankSandboxProps>(
|
||||
({ thicknessRange, naked }, ref) => {
|
||||
@@ -237,6 +238,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
{(display === TankopediaDisplay.Model ||
|
||||
(display === TankopediaDisplay.DynamicArmor &&
|
||||
!hideTankModelUnderArmor)) && <TankModel />}
|
||||
<TransitionSkeleton />
|
||||
<ShotDisplay />
|
||||
<ArmorPlateDisplay />
|
||||
<AutoClear />
|
||||
|
||||
@@ -187,7 +187,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
</Heading>
|
||||
|
||||
<Heading
|
||||
weight="regular"
|
||||
weight="medium"
|
||||
size={
|
||||
disturbed
|
||||
? { initial: '7', lg: '8' }
|
||||
|
||||
Reference in new issue
Block a user