mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-28 21:19:52 +01:00
Compare commits
10
Commits
c130da7f0e
...
d485a463dd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d485a463dd | ||
|
|
3da1ed4d6c | ||
|
|
051e9ec74a | ||
|
|
486171f4db | ||
|
|
72de42a9ab | ||
|
|
404e6625c0 | ||
|
|
32e4bf3fd2 | ||
|
|
0d1088fefb | ||
|
|
1a17d607fb | ||
|
|
dcda627518 |
No files matched your search
@@ -30,7 +30,7 @@
|
||||
"@octokit/plugin-throttling": "^11.0.1",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/bun": "^1.3.1",
|
||||
"@types/bun": "^1.3.14",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/lz4js": "^0.2.1",
|
||||
"@types/three": "^0.180.0",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@octokit/plugin-throttling": "^11.0.1",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/bun": "^1.3.1",
|
||||
"@types/bun": "^1.3.14",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/lz4js": "^0.2.1",
|
||||
"@types/three": "^0.180.0",
|
||||
|
||||
@@ -46,7 +46,15 @@ export abstract class AbstractVFS {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
abstract dir(path: string): Promise<string[]>;
|
||||
protected abstract _dir(path: string): Promise<string[]>;
|
||||
|
||||
async dir(path: string) {
|
||||
const files = await this._dir(path);
|
||||
|
||||
return files.map((file) =>
|
||||
file.endsWith(".dvpl") ? file.slice(0, -5) : file,
|
||||
);
|
||||
}
|
||||
|
||||
async text(path: string) {
|
||||
const file = await this.file(path);
|
||||
@@ -63,7 +71,7 @@ export abstract class AbstractVFS {
|
||||
return this.xmlParser.parse(file) as Type;
|
||||
}
|
||||
|
||||
async _csv(text: string, options: Options = {}) {
|
||||
protected async _csv(text: string, options: Options = {}) {
|
||||
const parsed = parseCSV(text, options);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export class LocalVFS extends AbstractVFS {
|
||||
return array;
|
||||
}
|
||||
|
||||
async dir(path: string) {
|
||||
async _dir(path: string) {
|
||||
try {
|
||||
return await readdir(`${this.base}/${path}`);
|
||||
} catch (_) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class MixedVFS extends AbstractVFS {
|
||||
throw new Error(`File not found: ${path}`);
|
||||
}
|
||||
|
||||
async dir(path: string) {
|
||||
async _dir(path: string) {
|
||||
const files = new Set<string>();
|
||||
|
||||
for (const vfs of this.vfses) {
|
||||
|
||||
@@ -89,7 +89,7 @@ export class SteamVFS extends AbstractVFS {
|
||||
return new Uint8Array(downloaded.file);
|
||||
}
|
||||
|
||||
async dir(path: string) {
|
||||
async _dir(path: string) {
|
||||
const parentSegments = path.split("/").length;
|
||||
const children = new Set<string>();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export class ZipVFS extends AbstractVFS {
|
||||
return new Uint8Array(arrayBuffer);
|
||||
}
|
||||
|
||||
async dir(path: string) {
|
||||
async _dir(path: string) {
|
||||
const parentSegments = path.split("/").length;
|
||||
const children = new Set<string>();
|
||||
|
||||
|
||||
+32
-3
@@ -1,3 +1,4 @@
|
||||
import { blueDark, orangeDark } from "@radix-ui/colors";
|
||||
import { ContactShadows } from "@react-three/drei";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { clamp, times } from "lodash-es";
|
||||
@@ -11,8 +12,11 @@ import {
|
||||
} from "react";
|
||||
import { HemisphereLight, SpotLight, type Group } from "three";
|
||||
import { degToRad, lerp } from "three/src/math/MathUtils.js";
|
||||
import { useModel } from "../../../../../../hooks/useModel";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../../../../../stores/tankopediaPersistent/constants";
|
||||
|
||||
const ANGLE = degToRad(15);
|
||||
const REVEAL_ANIMATION_TIME = 3;
|
||||
@@ -20,12 +24,18 @@ const TRANSITION_ANIMATION_TIME = 0.5;
|
||||
|
||||
const LIGHTS_COUNT = 3;
|
||||
const THETA_OFFSET = degToRad(180 - 45);
|
||||
|
||||
const LIGHT_DISTANCE = 20;
|
||||
const LIGHT_HEIGHT_0 = 7;
|
||||
const LIGHT_HEIGHT_1 = 10;
|
||||
|
||||
const INTENSITY_0 = 2 ** 6;
|
||||
const INTENSITY_1 = 2 ** 3;
|
||||
const HEMISPHERE_INTENSITY = 2 ** 0.7;
|
||||
const NON_PBR_FACTOR = 2;
|
||||
|
||||
const SHADOW_RESOLUTION = 2 ** 9;
|
||||
const SHADOW_FOCUS = 2 ** 0;
|
||||
|
||||
export const transitionEvent = new Quicklime<number>(0);
|
||||
|
||||
@@ -75,7 +85,11 @@ export function Lighting() {
|
||||
)}
|
||||
|
||||
<group ref={wrapper}>
|
||||
<hemisphereLight intensity={0} color="#d2e9ff" groundColor="#85490c" />
|
||||
<hemisphereLight
|
||||
intensity={0}
|
||||
color={blueDark.blue12}
|
||||
groundColor={orangeDark.orange8}
|
||||
/>
|
||||
|
||||
{times(LIGHTS_COUNT, (index) => {
|
||||
const x = index / (LIGHTS_COUNT - 1);
|
||||
@@ -87,8 +101,13 @@ export function Lighting() {
|
||||
] as const;
|
||||
const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
|
||||
|
||||
const light = useRef<SpotLight>(null!);
|
||||
|
||||
// useHelper(light, SpotLightHelper);
|
||||
|
||||
return (
|
||||
<spotLight
|
||||
ref={light}
|
||||
key={index}
|
||||
position={position}
|
||||
intensity={intensity}
|
||||
@@ -97,6 +116,9 @@ export function Lighting() {
|
||||
decay={1}
|
||||
color="#ffffff"
|
||||
angle={0}
|
||||
target-position={[0, 1.5, 0]}
|
||||
shadow-mapSize={[SHADOW_RESOLUTION, SHADOW_RESOLUTION]}
|
||||
shadow-focus={SHADOW_FOCUS}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -116,14 +138,21 @@ interface AnimatorProps {
|
||||
function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) {
|
||||
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||
|
||||
const id = Duel.use((state) => state.protagonist.tank.id);
|
||||
const display = Tankopedia.use((state) => state.display);
|
||||
|
||||
const { hasPbr } = useModel(id);
|
||||
const factor =
|
||||
!hasPbr && display !== TankopediaDisplay.StaticArmor ? NON_PBR_FACTOR : 1;
|
||||
|
||||
const apply = useCallback((t: number) => {
|
||||
if (wrapper.current === null) return;
|
||||
|
||||
for (const child of wrapper.current.children) {
|
||||
if (child instanceof SpotLight) {
|
||||
child.angle = ANGLE * t;
|
||||
child.angle = ANGLE * t * factor;
|
||||
} else if (child instanceof HemisphereLight) {
|
||||
child.intensity = HEMISPHERE_INTENSITY * t;
|
||||
child.intensity = HEMISPHERE_INTENSITY * t * factor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-8
@@ -1,15 +1,15 @@
|
||||
import { mauveDark } from "@radix-ui/colors";
|
||||
import { invalidate } from "@react-three/fiber";
|
||||
import { Quicklime, type QuicklimeEvent } from "quicklime";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Mesh, MeshStandardMaterial } from "three";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { Mesh, type WebGLProgramParametersWithUniforms } from "three";
|
||||
|
||||
const SIZE = 2 ** 5;
|
||||
const SIZE = 2 ** 4;
|
||||
|
||||
export const screenshotReadyEvent = new Quicklime(false);
|
||||
|
||||
export function SceneProps() {
|
||||
const mesh = useRef<Mesh>(null!);
|
||||
const material = useRef<MeshStandardMaterial>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function handleScreenshotReady(event: QuicklimeEvent<boolean>) {
|
||||
@@ -24,20 +24,62 @@ export function SceneProps() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleBeforeCompile = useCallback(
|
||||
(shader: WebGLProgramParametersWithUniforms) => {
|
||||
shader.uniforms.size = { value: SIZE / 2 };
|
||||
|
||||
shader.vertexShader = `
|
||||
varying vec3 vWorldPosition;
|
||||
|
||||
${shader.vertexShader}
|
||||
`.replace(
|
||||
"#include <worldpos_vertex>",
|
||||
|
||||
`
|
||||
#include <worldpos_vertex>
|
||||
vWorldPosition = (modelMatrix * vec4(transformed, 1.0)).xyz;
|
||||
`,
|
||||
);
|
||||
|
||||
shader.fragmentShader = `
|
||||
varying vec3 vWorldPosition;
|
||||
uniform float size;
|
||||
|
||||
${shader.fragmentShader}
|
||||
`.replace(
|
||||
"#include <color_fragment>",
|
||||
|
||||
`
|
||||
#include <color_fragment>
|
||||
|
||||
vec3 scaled = vWorldPosition / size;
|
||||
float radius = length(scaled.xz);
|
||||
|
||||
if (radius >= 1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
diffuseColor.a *= (1.0 - radius);
|
||||
`,
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<mesh
|
||||
position={[0, -(2 ** -8), 0]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
receiveShadow
|
||||
visible={!screenshotReadyEvent.last!}
|
||||
ref={mesh}
|
||||
receiveShadow
|
||||
>
|
||||
<planeGeometry args={[SIZE, SIZE]} />
|
||||
<meshStandardMaterial
|
||||
ref={material}
|
||||
color="black"
|
||||
color={mauveDark.mauve6}
|
||||
roughness={1}
|
||||
metalness={0}
|
||||
transparent
|
||||
onBeforeCompile={handleBeforeCompile}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user