mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
Merge branch 'main' into dev
This commit is contained in:
commit
54b73d11ce
27 files changed
+203
-199
No files matched your search
@@ -69,7 +69,6 @@
|
|||||||
"@types/bun": "^1.3.1",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/steam-user": "^5.1.0",
|
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
@@ -79,7 +78,6 @@
|
|||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
"protobufjs": "^7.5.4",
|
"protobufjs": "^7.5.4",
|
||||||
"steam-user": "^5.2.3",
|
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
@@ -107,6 +105,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/progress": "^2.0.7",
|
"@types/progress": "^2.0.7",
|
||||||
|
"@types/steam-user": "^5.1.0",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"csv-parse": "^6.1.0",
|
"csv-parse": "^6.1.0",
|
||||||
@@ -120,6 +119,7 @@
|
|||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"puppeteer": "^24.22.3",
|
"puppeteer": "^24.22.3",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
|
"steam-user": "^5.2.3",
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
@@ -2613,10 +2613,6 @@
|
|||||||
|
|
||||||
"@blitzkit/bot/prisma": ["[email protected]", "", { "dependencies": { "@prisma/config": "6.17.1", "@prisma/engines": "6.17.1" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g=="],
|
"@blitzkit/bot/prisma": ["[email protected]", "", { "dependencies": { "@prisma/config": "6.17.1", "@prisma/engines": "6.17.1" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g=="],
|
||||||
|
|
||||||
"@blitzkit/scripts/@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
|
||||||
|
|
||||||
"@blitzkit/scripts/fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
|
||||||
|
|
||||||
"@blitzkit/website/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="],
|
"@blitzkit/website/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="],
|
||||||
|
|
||||||
"@discordjs/rest/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
"@discordjs/rest/@discordjs/collection": ["@discordjs/[email protected]", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||||
@@ -2785,8 +2781,6 @@
|
|||||||
|
|
||||||
"@blitzkit/bot/prisma/@prisma/engines": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.17.1", "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", "@prisma/fetch-engine": "6.17.1", "@prisma/get-platform": "6.17.1" } }, "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw=="],
|
"@blitzkit/bot/prisma/@prisma/engines": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.17.1", "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", "@prisma/fetch-engine": "6.17.1", "@prisma/get-platform": "6.17.1" } }, "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw=="],
|
||||||
|
|
||||||
"@blitzkit/scripts/@types/bun/bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# BlitzKit 1.29.5
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added new lighting to be a bit softer on tanks
|
||||||
|
- Added a button with the sun icon to toggle high graphics
|
||||||
|
- Added a new reveal and transition animation between tabs
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Track wheels on some tanks no longer look translucent
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkit",
|
"name": "blitzkit",
|
||||||
"version": "1.29.4",
|
"version": "1.29.5",
|
||||||
"license": "BlitzKit License",
|
"license": "BlitzKit License",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"@types/bun": "^1.3.1",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/steam-user": "^5.1.0",
|
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
@@ -23,7 +22,6 @@
|
|||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"pickleparser": "^0.2.1",
|
"pickleparser": "^0.2.1",
|
||||||
"protobufjs": "^7.5.4",
|
"protobufjs": "^7.5.4",
|
||||||
"steam-user": "^5.2.3",
|
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1"
|
"yaml": "^2.8.1"
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ export * from "./patreon";
|
|||||||
export * from "./protobuf";
|
export * from "./protobuf";
|
||||||
export * from "./protos";
|
export * from "./protos";
|
||||||
export * from "./statistics";
|
export * from "./statistics";
|
||||||
export * from "./steam";
|
|
||||||
export * from "./streams";
|
export * from "./streams";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./vfs";
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/lz4js": "^0.2.1",
|
"@types/lz4js": "^0.2.1",
|
||||||
"@types/progress": "^2.0.7",
|
"@types/progress": "^2.0.7",
|
||||||
|
"@types/steam-user": "^5.1.0",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
"csv-parse": "^6.1.0",
|
"csv-parse": "^6.1.0",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"progress": "^2.0.3",
|
"progress": "^2.0.3",
|
||||||
"puppeteer": "^24.22.3",
|
"puppeteer": "^24.22.3",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
|
"steam-user": "^5.2.3",
|
||||||
"three": "^0.180.0",
|
"three": "^0.180.0",
|
||||||
"ts-proto": "^2.7.7",
|
"ts-proto": "^2.7.7",
|
||||||
"yaml": "^2.8.1"
|
"yaml": "^2.8.1"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { assertSecret, SteamVFS } from "@blitzkit/core";
|
import { assertSecret } from "@blitzkit/core";
|
||||||
|
import { SteamVFS } from "../core/steam/vfs";
|
||||||
|
|
||||||
const APP = 444200;
|
const APP = 444200;
|
||||||
const DEPOT = 444202;
|
const DEPOT = 444202;
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import {
|
|||||||
Hierarchy,
|
Hierarchy,
|
||||||
Sc2ReadStream,
|
Sc2ReadStream,
|
||||||
ScgReadStream,
|
ScgReadStream,
|
||||||
SteamVFS,
|
|
||||||
VertexAttribute,
|
VertexAttribute,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { Accessor, Document, Node, Scene } from "@gltf-transform/core";
|
import { Accessor, Document, Node, Scene } from "@gltf-transform/core";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
|
import { SteamVFS } from "../steam/vfs";
|
||||||
import {
|
import {
|
||||||
vertexAttributeGLTFName,
|
vertexAttributeGLTFName,
|
||||||
vertexAttributeGltfVectorSizes,
|
vertexAttributeGltfVectorSizes,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
Hierarchy,
|
Hierarchy,
|
||||||
Sc2ReadStream,
|
Sc2ReadStream,
|
||||||
ScgReadStream,
|
ScgReadStream,
|
||||||
SteamVFS,
|
|
||||||
Textures,
|
Textures,
|
||||||
VertexAttribute,
|
VertexAttribute,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
@@ -10,6 +9,7 @@ import { Document, Material, Node, Scene } from "@gltf-transform/core";
|
|||||||
import { dedup, prune } from "@gltf-transform/functions";
|
import { dedup, prune } from "@gltf-transform/functions";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
|
import { SteamVFS } from "../../steam/vfs";
|
||||||
import { readBaseColor } from "../readBaseColor";
|
import { readBaseColor } from "../readBaseColor";
|
||||||
import { readNormal } from "../readNormal";
|
import { readNormal } from "../readNormal";
|
||||||
import { readRoughnessMetallic } from "../readRoughnessMetallic";
|
import { readRoughnessMetallic } from "../readRoughnessMetallic";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { readDVPL } from "@blitzkit/core/src/blitz";
|
||||||
import { XMLParser } from "fast-xml-parser";
|
import { XMLParser } from "fast-xml-parser";
|
||||||
import { normalize } from "path/posix";
|
import { normalize } from "path/posix";
|
||||||
import SteamUser, { EConnectionProtocol } from "steam-user";
|
import SteamUser, { EConnectionProtocol } from "steam-user";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
import { readDVPL } from "../blitz";
|
|
||||||
|
|
||||||
interface SteamManifestFile {
|
interface SteamManifestFile {
|
||||||
chunks: {
|
chunks: {
|
||||||
Binary file not shown.
@@ -67,7 +67,9 @@ export const PrimaryArmorScene = memo(() => {
|
|||||||
<group ref={turretContainer}>
|
<group ref={turretContainer}>
|
||||||
{armorNodes.map((node) => {
|
{armorNodes.map((node) => {
|
||||||
const isCurrentTurret = node.name.startsWith(
|
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 isVisible = isCurrentTurret;
|
||||||
const armorId = nameToArmorId(node.name);
|
const armorId = nameToArmorId(node.name);
|
||||||
|
|||||||
+12
-1
@@ -4,6 +4,7 @@ import {
|
|||||||
resolvePenetrationCoefficient,
|
resolvePenetrationCoefficient,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { invalidate, useFrame } from "@react-three/fiber";
|
import { invalidate, useFrame } from "@react-three/fiber";
|
||||||
|
import type { QuicklimeEvent } from "quicklime";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
@@ -21,6 +22,7 @@ import { jsxTree } from "../../../../core/blitzkit/jsxTree";
|
|||||||
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
|
import { Duel, type EquipmentMatrix } from "../../../../stores/duel";
|
||||||
import { Tankopedia } from "../../../../stores/tankopedia";
|
import { Tankopedia } from "../../../../stores/tankopedia";
|
||||||
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
|
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
|
||||||
|
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
|
||||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||||
import { spacedArmorRenderTarget } from "./target";
|
import { spacedArmorRenderTarget } from "./target";
|
||||||
@@ -72,6 +74,8 @@ export function PrimaryArmorSceneComponent({
|
|||||||
resolution: { value: new Vector2() },
|
resolution: { value: new Vector2() },
|
||||||
spacedArmorBuffer: { value: null },
|
spacedArmorBuffer: { value: null },
|
||||||
spacedArmorDepth: { value: null },
|
spacedArmorDepth: { value: null },
|
||||||
|
|
||||||
|
opacity: { value: 0 },
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
@@ -154,6 +158,10 @@ export function PrimaryArmorSceneComponent({
|
|||||||
if (!noInvalidate) invalidate();
|
if (!noInvalidate) invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTransitionEvent(event: QuicklimeEvent<number>) {
|
||||||
|
material.uniforms.opacity.value = event.data;
|
||||||
|
}
|
||||||
|
|
||||||
handleShellChange();
|
handleShellChange();
|
||||||
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
|
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
|
||||||
handleAdvancedHighlightingChange(
|
handleAdvancedHighlightingChange(
|
||||||
@@ -164,6 +172,8 @@ export function PrimaryArmorSceneComponent({
|
|||||||
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
|
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix);
|
||||||
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
|
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix);
|
||||||
|
|
||||||
|
transitionEvent.on(handleTransitionEvent);
|
||||||
|
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
Duel.on((state) => state.antagonist.shell, handleShellChange),
|
||||||
Tankopedia.on((state) => state.customShell, handleShellChange),
|
Tankopedia.on((state) => state.customShell, handleShellChange),
|
||||||
@@ -184,12 +194,13 @@ export function PrimaryArmorSceneComponent({
|
|||||||
(state) => state.antagonist.equipmentMatrix,
|
(state) => state.antagonist.equipmentMatrix,
|
||||||
(equipment) => handleAntagonistEquipmentChange(equipment)
|
(equipment) => handleAntagonistEquipmentChange(equipment)
|
||||||
),
|
),
|
||||||
|
() => transitionEvent.off(handleTransitionEvent),
|
||||||
];
|
];
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||||
};
|
};
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
useFrame(({ gl, camera }) => {
|
useFrame(({ gl, camera }) => {
|
||||||
gl.getSize(material.uniforms.resolution.value).multiplyScalar(
|
gl.getSize(material.uniforms.resolution.value).multiplyScalar(
|
||||||
|
|||||||
+2
-5
@@ -1,12 +1,9 @@
|
|||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define USE_FOG
|
|
||||||
|
|
||||||
#include <fog_pars_fragment>
|
|
||||||
|
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
varying vec3 vViewPosition;
|
varying vec3 vViewPosition;
|
||||||
|
|
||||||
|
uniform float opacity;
|
||||||
uniform float thickness;
|
uniform float thickness;
|
||||||
uniform float penetration;
|
uniform float penetration;
|
||||||
uniform float caliber;
|
uniform float caliber;
|
||||||
@@ -119,5 +116,5 @@ void main() {
|
|||||||
gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha);
|
gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <fog_fragment>
|
gl_FragColor.a *= opacity;
|
||||||
}
|
}
|
||||||
-6
@@ -1,9 +1,5 @@
|
|||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define USE_FOG
|
|
||||||
|
|
||||||
#include <fog_pars_vertex>
|
|
||||||
|
|
||||||
varying vec3 vNormal;
|
varying vec3 vNormal;
|
||||||
varying vec3 vViewPosition;
|
varying vec3 vViewPosition;
|
||||||
|
|
||||||
@@ -14,6 +10,4 @@ void main() {
|
|||||||
vNormal = normalMatrix * normal;
|
vNormal = normalMatrix * normal;
|
||||||
|
|
||||||
gl_Position = projectionMatrix * mvPosition;
|
gl_Position = projectionMatrix * mvPosition;
|
||||||
|
|
||||||
#include <fog_vertex>
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from "../../../core/blitzkit/modelTransform";
|
} from "../../../core/blitzkit/modelTransform";
|
||||||
import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
|
import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
|
||||||
import { Tankopedia } from "../../../stores/tankopedia";
|
import { Tankopedia } from "../../../stores/tankopedia";
|
||||||
|
import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
|
||||||
import { ArmorType } from "./SpacedArmorScene";
|
import { ArmorType } from "./SpacedArmorScene";
|
||||||
import type {
|
import type {
|
||||||
ArmorUserData,
|
ArmorUserData,
|
||||||
@@ -108,10 +109,11 @@ export function StaticArmorSceneComponent({
|
|||||||
() =>
|
() =>
|
||||||
new MeshBasicMaterial({
|
new MeshBasicMaterial({
|
||||||
color,
|
color,
|
||||||
opacity,
|
transparent: true,
|
||||||
transparent: opacity < 1,
|
|
||||||
depthWrite,
|
depthWrite,
|
||||||
...(props.clip ? { clippingPlanes: [props.clip] } : {}),
|
...(props.clip ? { clippingPlanes: [props.clip] } : {}),
|
||||||
|
opacity: 0,
|
||||||
|
userData: { opacity0: opacity },
|
||||||
}),
|
}),
|
||||||
[thickness]
|
[thickness]
|
||||||
);
|
);
|
||||||
@@ -121,6 +123,8 @@ export function StaticArmorSceneComponent({
|
|||||||
color: color
|
color: color
|
||||||
.clone()
|
.clone()
|
||||||
.multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
|
.multiplyScalar(props.type === ArmorType.Spaced ? 2 ** 2 : 2 ** -1),
|
||||||
|
opacity: 0,
|
||||||
|
transparent: true,
|
||||||
}),
|
}),
|
||||||
[thickness]
|
[thickness]
|
||||||
);
|
);
|
||||||
@@ -163,11 +167,22 @@ export function StaticArmorSceneComponent({
|
|||||||
surfaceMaterial.needsUpdate = true;
|
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 = [
|
const unsubscribes = [
|
||||||
Tankopedia.on(
|
Tankopedia.on(
|
||||||
(state) => state.highlightArmor?.name,
|
(state) => state.highlightArmor?.name,
|
||||||
handleHighlightArmor
|
handleHighlightArmor
|
||||||
),
|
),
|
||||||
|
() => transitionEvent.off(handleTransitionEvent),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (props.clip) {
|
if (props.clip) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Box, Text } from "@radix-ui/themes";
|
||||||
|
import { Html } from "@react-three/drei";
|
||||||
import { memo, type ComponentProps } from "react";
|
import { memo, type ComponentProps } from "react";
|
||||||
|
|
||||||
interface Props extends ComponentProps<"spotLight"> {
|
interface Props extends ComponentProps<"spotLight"> {
|
||||||
@@ -8,10 +10,20 @@ export const HelpingSpotLight = memo<Props>(({ debug, ...props }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{debug && (
|
{debug && (
|
||||||
<mesh position={props.position}>
|
<group position={props.position}>
|
||||||
<icosahedronGeometry args={[0.1, 2]} />
|
<mesh>
|
||||||
<meshBasicMaterial color={props.color} />
|
<icosahedronGeometry args={[0.1, 2]} />
|
||||||
</mesh>
|
<meshBasicMaterial color={props.color} />
|
||||||
|
</mesh>
|
||||||
|
|
||||||
|
<Html center>
|
||||||
|
<Box pb="9">
|
||||||
|
<Text wrap="nowrap">
|
||||||
|
{props.intensity} @ {JSON.stringify(props.userData)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Html>
|
||||||
|
</group>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<spotLight {...props} />
|
<spotLight {...props} />
|
||||||
|
|||||||
+24
-6
@@ -12,6 +12,7 @@ import {
|
|||||||
EyeOpenIcon,
|
EyeOpenIcon,
|
||||||
ShadowInnerIcon,
|
ShadowInnerIcon,
|
||||||
ShadowNoneIcon,
|
ShadowNoneIcon,
|
||||||
|
SunIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -55,7 +56,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
const hasCustomShell = Tankopedia.use(
|
const hasCustomShell = Tankopedia.use(
|
||||||
(state) => state.customShell !== undefined
|
(state) => state.customShell !== undefined
|
||||||
);
|
);
|
||||||
const display = Tankopedia.use((state) => state.display);
|
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||||
const isFullScreen = useFullScreen();
|
const isFullScreen = useFullScreen();
|
||||||
const advancedHighlighting = TankopediaPersistent.use(
|
const advancedHighlighting = TankopediaPersistent.use(
|
||||||
(state) => state.advancedHighlighting
|
(state) => state.advancedHighlighting
|
||||||
@@ -73,6 +74,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
const { strings, unwrap } = useLocale();
|
const { strings, unwrap } = useLocale();
|
||||||
const revealed = Tankopedia.use((state) => state.revealed);
|
const revealed = Tankopedia.use((state) => state.revealed);
|
||||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||||
|
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -84,7 +86,9 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
gap="2"
|
gap="2"
|
||||||
direction="column"
|
direction="column"
|
||||||
top="50%"
|
top="50%"
|
||||||
right={display === TankopediaDisplay.DynamicArmor ? "3" : "-4rem"}
|
right={
|
||||||
|
requestedDisplay === TankopediaDisplay.DynamicArmor ? "3" : "-4rem"
|
||||||
|
}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
transform: "translateY(-50%)",
|
transform: "translateY(-50%)",
|
||||||
@@ -296,7 +300,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
>
|
>
|
||||||
{disturbed && (
|
{disturbed && (
|
||||||
<>
|
<>
|
||||||
{display === TankopediaDisplay.DynamicArmor && (
|
{requestedDisplay === TankopediaDisplay.DynamicArmor && (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="2"
|
size="2"
|
||||||
highContrast
|
highContrast
|
||||||
@@ -349,6 +353,20 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="2"
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankopediaPersistent.mutate((draft) => {
|
||||||
|
draft.highGraphics = !draft.highGraphics;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color={highGraphics ? undefined : "gray"}
|
||||||
|
variant={highGraphics ? "solid" : "surface"}
|
||||||
|
>
|
||||||
|
<SunIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
<ScreenshotButton
|
<ScreenshotButton
|
||||||
color="gray"
|
color="gray"
|
||||||
size="2"
|
size="2"
|
||||||
@@ -384,11 +402,11 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
<Flex gap="3" align="center" mt="2">
|
<Flex gap="3" align="center" mt="2">
|
||||||
<SegmentedControl.Root
|
<SegmentedControl.Root
|
||||||
variant="classic"
|
variant="classic"
|
||||||
value={`${disturbed ? display : -1}`}
|
value={`${disturbed ? requestedDisplay : -1}`}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
Tankopedia.mutate((draft) => {
|
Tankopedia.mutate((draft) => {
|
||||||
draft.disturbed = true;
|
draft.disturbed = true;
|
||||||
draft.display = Number(value);
|
draft.requestedDisplay = Number(value);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -430,7 +448,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
</SegmentedControl.Item>
|
</SegmentedControl.Item>
|
||||||
</SegmentedControl.Root>
|
</SegmentedControl.Root>
|
||||||
|
|
||||||
{display === TankopediaDisplay.DynamicArmor && (
|
{requestedDisplay === TankopediaDisplay.DynamicArmor && (
|
||||||
<Dialog.Root
|
<Dialog.Root
|
||||||
open={antagonistSelectorOpen}
|
open={antagonistSelectorOpen}
|
||||||
onOpenChange={setAntagonistSelectorOpen}
|
onOpenChange={setAntagonistSelectorOpen}
|
||||||
|
|||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
import { invalidate, useThree } from "@react-three/fiber";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import type { Fog } from "three";
|
|
||||||
import { fogAnimationTime, fogFar0, fogFar1, forNear0, forNear1 } from "..";
|
|
||||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
|
||||||
|
|
||||||
export function InitialFogReveal() {
|
|
||||||
const fog = useThree((state) => state.scene.fog as Fog | null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
Tankopedia.mutate((draft) => {
|
|
||||||
draft.revealed = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!fog) return;
|
|
||||||
|
|
||||||
const t0 = Date.now();
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const t = (Date.now() - t0) / 1000;
|
|
||||||
const x = t / fogAnimationTime;
|
|
||||||
const near = forNear1 + (forNear0 - forNear1) * x;
|
|
||||||
const far = fogFar1 + (fogFar0 - fogFar1) * x;
|
|
||||||
|
|
||||||
fog.near = near;
|
|
||||||
fog.far = far;
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (x >= 1) {
|
|
||||||
fog.near = forNear0;
|
|
||||||
fog.far = fogFar0;
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
+77
-41
@@ -1,82 +1,118 @@
|
|||||||
import { useHelper } from "@react-three/drei";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useFrame } from "@react-three/fiber";
|
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { useRef, useState } from "react";
|
import { Quicklime } from "quicklime";
|
||||||
import { Color, SpotLight, SpotLightHelper } from "three";
|
import { useEffect, useRef } from "react";
|
||||||
import { degToRad } from "three/src/math/MathUtils.js";
|
import { HemisphereLight, SpotLight, type Group } from "three";
|
||||||
import { isHalloween } from "../../../../../../core/blitzkit/isHalloween";
|
import { clamp, degToRad, lerp } from "three/src/math/MathUtils.js";
|
||||||
|
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||||
|
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
|
||||||
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
|
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
|
||||||
|
|
||||||
|
const ANGLE = Math.PI * 2 ** -2;
|
||||||
|
const REVEAL_ANIMATION_TIME = 3;
|
||||||
|
const TRANSITION_ANIMATION_TIME = 0.5;
|
||||||
|
|
||||||
const LIGHTS_COUNT = 5;
|
const LIGHTS_COUNT = 5;
|
||||||
|
const THETA_OFFSET = degToRad(-152);
|
||||||
const LIGHT_DISTANCE = 13;
|
const LIGHT_DISTANCE = 13;
|
||||||
const LIGHT_HEIGHT_0 = 4;
|
const LIGHT_HEIGHT_0 = 4;
|
||||||
const LIGHT_HEIGHT_1 = 2;
|
const LIGHT_HEIGHT_1 = 2;
|
||||||
const INTENSITY = 50;
|
const INTENSITY_0 = 3.5;
|
||||||
|
const INTENSITY_1 = 1.5;
|
||||||
|
const HEMISPHERE_INTENSITY = 2;
|
||||||
|
|
||||||
const N = 128;
|
export const transitionEvent = new Quicklime<number>(0);
|
||||||
const TARGET_FRAME_RATE = 30;
|
|
||||||
|
|
||||||
export function Lighting() {
|
export function Lighting() {
|
||||||
const [cast, setCast] = useState(true);
|
const wrapper = useRef<Group>(null!);
|
||||||
|
|
||||||
const t0 = useRef(0);
|
const t0 = useRef(0);
|
||||||
const topLight = useRef<SpotLight>(null!);
|
const transition = useRef(false);
|
||||||
const frontLight = useRef<SpotLight>(null!);
|
const clock = useThree((state) => state.clock);
|
||||||
|
const display = Tankopedia.use((state) => state.display);
|
||||||
|
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||||
|
const animationTime = useRef(REVEAL_ANIMATION_TIME);
|
||||||
|
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
|
||||||
|
|
||||||
useHelper(topLight, SpotLightHelper, "red");
|
useEffect(() => {
|
||||||
useHelper(frontLight, SpotLightHelper, "green");
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.revealed = true;
|
||||||
|
});
|
||||||
|
|
||||||
const last = useRef<number>(0);
|
t0.current = clock.elapsedTime - REVEAL_ANIMATION_TIME;
|
||||||
const samples = useRef<number[]>([]);
|
}, []);
|
||||||
|
|
||||||
useFrame(({ clock, frameloop }) => {
|
useFrame(({ invalidate }) => {
|
||||||
if (frameloop === "never") {
|
if (transition.current) {
|
||||||
|
transition.current = false;
|
||||||
t0.current = clock.elapsedTime;
|
t0.current = clock.elapsedTime;
|
||||||
samples.current = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!cast || clock.elapsedTime - t0.current < TARGET_FRAME_RATE * N) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dt = clock.elapsedTime - last.current;
|
const x = clamp(
|
||||||
last.current = clock.elapsedTime;
|
(clock.elapsedTime - t0.current) / animationTime.current,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
|
||||||
|
|
||||||
const fps = 1 / dt;
|
if (t !== transitionEvent.last) {
|
||||||
samples.current.push(fps);
|
transitionEvent.dispatch(t);
|
||||||
|
}
|
||||||
|
|
||||||
if (samples.current.length > N) samples.current.shift();
|
for (const child of wrapper.current.children) {
|
||||||
|
if (child instanceof SpotLight) {
|
||||||
|
child.angle = ANGLE * t;
|
||||||
|
} else if (child instanceof HemisphereLight) {
|
||||||
|
child.intensity = HEMISPHERE_INTENSITY * t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const avg =
|
if (x < 2) invalidate();
|
||||||
samples.current.reduce((a, b) => a + b, 0) / samples.current.length;
|
|
||||||
|
|
||||||
if (avg < TARGET_FRAME_RATE) setCast(false);
|
if (
|
||||||
|
x > 1 &&
|
||||||
|
Tankopedia.state.display !== Tankopedia.state.requestedDisplay
|
||||||
|
) {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.display = requestedDisplay;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (requestedDisplay === display) return;
|
||||||
|
|
||||||
|
transition.current = true;
|
||||||
|
animationTime.current = TRANSITION_ANIMATION_TIME;
|
||||||
|
}, [requestedDisplay]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<group ref={wrapper}>
|
||||||
|
<hemisphereLight intensity={0} color="#ffffff" groundColor="#afafaf" />
|
||||||
|
|
||||||
{times(LIGHTS_COUNT, (index) => {
|
{times(LIGHTS_COUNT, (index) => {
|
||||||
const x = index / (LIGHTS_COUNT - 1);
|
const x = index / (LIGHTS_COUNT - 1);
|
||||||
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) - degToRad(152);
|
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) + THETA_OFFSET;
|
||||||
const position = [
|
const position = [
|
||||||
LIGHT_DISTANCE * Math.sin(theta),
|
LIGHT_DISTANCE * Math.sin(theta),
|
||||||
LIGHT_HEIGHT_0 * (1 - x) + LIGHT_HEIGHT_1 * x,
|
lerp(LIGHT_HEIGHT_0, LIGHT_HEIGHT_1, x),
|
||||||
LIGHT_DISTANCE * Math.cos(theta),
|
LIGHT_DISTANCE * Math.cos(theta),
|
||||||
] as const;
|
] as const;
|
||||||
const color = new Color();
|
const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HelpingSpotLight
|
<HelpingSpotLight
|
||||||
|
userData={{ index }}
|
||||||
key={index}
|
key={index}
|
||||||
position={position}
|
position={position}
|
||||||
intensity={INTENSITY}
|
intensity={intensity}
|
||||||
penumbra={1}
|
penumbra={1}
|
||||||
castShadow={cast}
|
castShadow={highGraphics}
|
||||||
decay={1}
|
decay={0}
|
||||||
color={color}
|
color="#ffffff"
|
||||||
|
angle={0}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+3
-2
@@ -4,7 +4,7 @@ import { Quicklime, type QuicklimeEvent } from "quicklime";
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Mesh, MeshStandardMaterial } from "three";
|
import { Mesh, MeshStandardMaterial } from "three";
|
||||||
|
|
||||||
const SIZE = 2 ** 5;
|
const SIZE = 2 ** 6;
|
||||||
|
|
||||||
export const screenshotReadyEvent = new Quicklime(false);
|
export const screenshotReadyEvent = new Quicklime(false);
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ export function SceneProps() {
|
|||||||
receiveShadow
|
receiveShadow
|
||||||
visible={!screenshotReadyEvent.last!}
|
visible={!screenshotReadyEvent.last!}
|
||||||
ref={mesh}
|
ref={mesh}
|
||||||
|
renderOrder={-1}
|
||||||
>
|
>
|
||||||
<planeGeometry args={[SIZE, SIZE]} />
|
<planeGeometry args={[SIZE, SIZE]} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
@@ -43,7 +44,7 @@ export function SceneProps() {
|
|||||||
ref={material}
|
ref={material}
|
||||||
color="black"
|
color="black"
|
||||||
roughness={1}
|
roughness={1}
|
||||||
metalness={1}
|
metalness={0}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -106,7 +106,9 @@ export function TankModel() {
|
|||||||
const isTurret = node.name.startsWith("turret_");
|
const isTurret = node.name.startsWith("turret_");
|
||||||
const isCurrentTurret =
|
const isCurrentTurret =
|
||||||
node.name ===
|
node.name ===
|
||||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`;
|
`turret_${turretModelDefinition.model_id
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`;
|
||||||
const isVisible = isCurrentTurret;
|
const isVisible = isCurrentTurret;
|
||||||
const position = new Vector2();
|
const position = new Vector2();
|
||||||
const delta = new Vector2();
|
const delta = new Vector2();
|
||||||
|
|||||||
+6
-45
@@ -1,12 +1,10 @@
|
|||||||
import { BLITZKIT_TANK_ICON_SIZE } from "@blitzkit/core";
|
import { BLITZKIT_TANK_ICON_SIZE } from "@blitzkit/core";
|
||||||
import { invalidate } from "@react-three/fiber";
|
|
||||||
import {
|
import {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
Suspense,
|
Suspense,
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Fog } from "three";
|
import { Fog } from "three";
|
||||||
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
|
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
|
||||||
@@ -29,26 +27,17 @@ import { SmartCanvas } from "../../../../SmartCanvas";
|
|||||||
import { AutoClear } from "./components/AutoClear";
|
import { AutoClear } from "./components/AutoClear";
|
||||||
import { Controls } from "./components/Control";
|
import { Controls } from "./components/Control";
|
||||||
import { InitialAligner } from "./components/InitialAligner";
|
import { InitialAligner } from "./components/InitialAligner";
|
||||||
import { InitialFogReveal } from "./components/InitialFogReveal";
|
|
||||||
import { Lighting } from "./components/Lighting";
|
import { Lighting } from "./components/Lighting";
|
||||||
import { SceneProps } from "./components/SceneProps";
|
import { SceneProps } from "./components/SceneProps";
|
||||||
import { TankModel } from "./components/TankModel";
|
import { TankModel } from "./components/TankModel";
|
||||||
import { TransitionSkeleton } from "./components/TransitionSkeleton";
|
|
||||||
|
|
||||||
interface TankSandboxProps {
|
interface TankSandboxProps {
|
||||||
thicknessRange: ThicknessRange;
|
thicknessRange: ThicknessRange;
|
||||||
naked?: boolean;
|
naked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forNear0 = 30;
|
|
||||||
export const fogFar0 = 50;
|
|
||||||
export const forNear1 = 0;
|
|
||||||
export const fogFar1 = 0;
|
|
||||||
export const fogAnimationTime = 2.5;
|
|
||||||
|
|
||||||
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||||
({ thicknessRange, naked }, ref) => {
|
({ thicknessRange, naked }, ref) => {
|
||||||
const fog = useRef(new Fog("black", forNear1, fogFar1));
|
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||||
@@ -58,40 +47,11 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
const turretModelDefinition =
|
const turretModelDefinition =
|
||||||
tankModelDefinition.turrets[protagonistTurret.id];
|
tankModelDefinition.turrets[protagonistTurret.id];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
||||||
const rawDisplay = Tankopedia.use((state) => state.display);
|
const display = Tankopedia.use((state) => state.display);
|
||||||
const [display, setDisplay] = useState(rawDisplay);
|
|
||||||
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
||||||
(state) => state.hideTankModelUnderArmor
|
(state) => state.hideTankModelUnderArmor
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (rawDisplay === display) return;
|
|
||||||
|
|
||||||
const t0 = Date.now();
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
const t = (Date.now() - t0) / 1000;
|
|
||||||
const x = t / fogAnimationTime;
|
|
||||||
const y = Math.sin(Math.PI * x);
|
|
||||||
|
|
||||||
const near = forNear0 + (forNear1 - forNear0) * y;
|
|
||||||
const far = fogFar0 + (fogFar1 - fogFar0) * y;
|
|
||||||
|
|
||||||
fog.current.near = near;
|
|
||||||
fog.current.far = far;
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (rawDisplay !== display && x >= 0.5) setDisplay(rawDisplay);
|
|
||||||
if (x >= 1) {
|
|
||||||
fog.current.near = forNear0;
|
|
||||||
fog.current.far = fogFar0;
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000 / 60);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, [rawDisplay]);
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => canvas.current!, []);
|
useImperativeHandle(ref, () => canvas.current!, []);
|
||||||
|
|
||||||
function handlePointerDown() {
|
function handlePointerDown() {
|
||||||
@@ -188,10 +148,12 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
}
|
}
|
||||||
}, [display]);
|
}, [display]);
|
||||||
|
|
||||||
|
console.log(TankopediaDisplay[display]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SmartCanvas
|
<SmartCanvas
|
||||||
ref={canvas}
|
ref={canvas}
|
||||||
scene={{ fog: naked ? undefined : fog.current }}
|
scene={{ fog: naked ? undefined : new Fog("black", 30, 50) }}
|
||||||
gl={{
|
gl={{
|
||||||
clippingPlanes: [],
|
clippingPlanes: [],
|
||||||
localClippingEnabled: true,
|
localClippingEnabled: true,
|
||||||
@@ -212,11 +174,12 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
outline: naked ? "1rem red solid" : undefined,
|
outline: naked ? "1rem red solid" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Lighting />
|
||||||
|
|
||||||
{!naked && <SceneProps />}
|
{!naked && <SceneProps />}
|
||||||
{(display === TankopediaDisplay.Model ||
|
{(display === TankopediaDisplay.Model ||
|
||||||
(display === TankopediaDisplay.DynamicArmor &&
|
(display === TankopediaDisplay.DynamicArmor &&
|
||||||
!hideTankModelUnderArmor)) && <TankModel />}
|
!hideTankModelUnderArmor)) && <TankModel />}
|
||||||
<TransitionSkeleton />
|
|
||||||
<ShotDisplay />
|
<ShotDisplay />
|
||||||
<ArmorPlateDisplay />
|
<ArmorPlateDisplay />
|
||||||
<AutoClear />
|
<AutoClear />
|
||||||
@@ -224,13 +187,11 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
|||||||
<Suspense>
|
<Suspense>
|
||||||
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
|
{/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */}
|
||||||
<Controls naked={naked} />
|
<Controls naked={naked} />
|
||||||
<InitialFogReveal />
|
|
||||||
|
|
||||||
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
{display === TankopediaDisplay.DynamicArmor && <Armor />}
|
||||||
{display === TankopediaDisplay.StaticArmor && (
|
{display === TankopediaDisplay.StaticArmor && (
|
||||||
<StaticArmor thicknessRange={thicknessRange} />
|
<StaticArmor thicknessRange={thicknessRange} />
|
||||||
)}
|
)}
|
||||||
<Lighting />
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<InitialAligner />
|
<InitialAligner />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Box, Flex } from "@radix-ui/themes";
|
import { Box, Flex } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Suspense, useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
||||||
import { useFullScreen } from "../../../hooks/useFullScreen";
|
import { useFullScreen } from "../../../hooks/useFullScreen";
|
||||||
import { Duel } from "../../../stores/duel";
|
import { Duel } from "../../../stores/duel";
|
||||||
@@ -99,11 +99,11 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
isFullScreen
|
isFullScreen
|
||||||
? "100vh"
|
? "100vh"
|
||||||
: disturbed
|
: disturbed
|
||||||
? "calc(100svh - 8rem)"
|
? "calc(100svh - 8rem)"
|
||||||
: {
|
: {
|
||||||
initial: "28rem",
|
initial: "28rem",
|
||||||
md: "calc(100svh - 14rem)",
|
md: "calc(100svh - 14rem)",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maxWidth={isFullScreen ? undefined : "120rem"}
|
maxWidth={isFullScreen ? undefined : "120rem"}
|
||||||
flexGrow="1"
|
flexGrow="1"
|
||||||
@@ -120,30 +120,17 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
||||||
<Box
|
<Box width="100%" height="100%" position="relative">
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
position="relative"
|
|
||||||
style={{
|
|
||||||
transitionDuration: "1s",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
<Box
|
||||||
style={{
|
position="absolute"
|
||||||
width: "100%",
|
width="100%"
|
||||||
height: "100%",
|
height="100%"
|
||||||
transitionDuration: "2s",
|
top="0"
|
||||||
opacity: revealed ? 1 : 0,
|
left="0"
|
||||||
filter: disturbed
|
|
||||||
? undefined
|
|
||||||
: `drop-shadow(0 1rem 1rem black)`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Suspense>
|
{!skeleton && (
|
||||||
{!skeleton && (
|
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
||||||
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
|
)}
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ interface Tankopedia {
|
|||||||
);
|
);
|
||||||
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
||||||
customShell?: ShellDefinition;
|
customShell?: ShellDefinition;
|
||||||
|
requestedDisplay: TankopediaDisplay;
|
||||||
display: TankopediaDisplay;
|
display: TankopediaDisplay;
|
||||||
statSearch?: string;
|
statSearch?: string;
|
||||||
}
|
}
|
||||||
@@ -114,5 +115,6 @@ export const Tankopedia = new Varuna<Tankopedia, ModelDefinition>((model) => ({
|
|||||||
skills: createDefaultSkills(skillDefinitions),
|
skills: createDefaultSkills(skillDefinitions),
|
||||||
model,
|
model,
|
||||||
xpMultiplier: 1,
|
xpMultiplier: 1,
|
||||||
|
requestedDisplay: TankopediaDisplay.Model,
|
||||||
display: TankopediaDisplay.Model,
|
display: TankopediaDisplay.Model,
|
||||||
}));
|
}));
|
||||||
@@ -15,6 +15,7 @@ export interface TankopediaPersistent {
|
|||||||
showPrimaryArmor: boolean;
|
showPrimaryArmor: boolean;
|
||||||
recentlyViewed: number[];
|
recentlyViewed: number[];
|
||||||
hideTankModelUnderArmor: boolean;
|
hideTankModelUnderArmor: boolean;
|
||||||
|
highGraphics: boolean;
|
||||||
|
|
||||||
sort: {
|
sort: {
|
||||||
by: TankopediaSortBy;
|
by: TankopediaSortBy;
|
||||||
@@ -33,6 +34,7 @@ export const TankopediaPersistent = new Varuna<TankopediaPersistent>(
|
|||||||
showExternalModules: true,
|
showExternalModules: true,
|
||||||
showPrimaryArmor: true,
|
showPrimaryArmor: true,
|
||||||
recentlyViewed: [],
|
recentlyViewed: [],
|
||||||
|
highGraphics: true,
|
||||||
sort: {
|
sort: {
|
||||||
by: "meta.none",
|
by: "meta.none",
|
||||||
direction: "ascending",
|
direction: "ascending",
|
||||||
|
|||||||
Reference in new issue
Block a user