This commit is contained in:
tresabhi committed 2026-06-12 20:59:43 -05:00
1 parent 91ac93670f
commit e3cf2d51cc
5 files changed
+120 -50

No files matched your search

+4 -1
View File
@@ -138,7 +138,10 @@ public partial class GameInterface
{
var path = texturePaths[name];
var texture = provider.LoadPackageObject<UTexture2D>(path);
return provider.Image(texture);
var isRMAO = name.EndsWith("_RMAO");
var postProcess = isRMAO ? ImagePostProcess.RMAO : ImagePostProcess.None;
return provider.Image(texture, postProcess);
}
public HashSet<string> Files => files;
+31 -1
View File
@@ -17,6 +17,12 @@ using SkiaSharp;
namespace BlitzKit.Game.Models;
public enum ImagePostProcess
{
None,
RMAO,
}
public partial class BlitzFileProvider : DefaultFileProvider
{
[GeneratedRegex(@"\(GameplayTags=\(\(TagName=""(.+)""\)\)\)")]
@@ -87,13 +93,37 @@ public partial class BlitzFileProvider : DefaultFileProvider
private readonly SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Webp;
private readonly int imageQuality = 80;
public byte[] Image(UTexture2D uTexture)
public byte[] Image(UTexture2D uTexture, ImagePostProcess postProcess = ImagePostProcess.None)
{
var cTexture = uTexture.Decode(ETexturePlatform.DesktopMobile);
ArgumentNullException.ThrowIfNull(cTexture);
var bitmap = cTexture.ToSkBitmap();
return Image(bitmap, postProcess);
}
private byte[] Image(SKBitmap bitmap, ImagePostProcess postProcess)
{
if (postProcess != ImagePostProcess.None)
{
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
var color = bitmap.GetPixel(x, y);
switch (postProcess)
{
case ImagePostProcess.RMAO:
bitmap.SetPixel(x, y, new SKColor(color.Blue, color.Red, color.Green, color.Alpha));
break;
}
}
}
}
var data = bitmap.Encode(imageFormat, imageQuality);
return data.ToArray();
@@ -0,0 +1,53 @@
import { times } from "lodash-es";
import { Quicklime } from "quicklime";
import { degToRad, lerp } from "three/src/math/MathUtils.js";
const ANGLE = Math.PI * 2 ** -2;
const LIGHTS_COUNT = 5;
const THETA_OFFSET = degToRad(-152);
const LIGHT_DISTANCE = 13;
const LIGHT_HEIGHT_0 = 4;
const LIGHT_HEIGHT_1 = 2;
const INTENSITY_0 = 3.5;
const INTENSITY_1 = 1.5;
const HEMISPHERE_INTENSITY = 2;
export const transitionEvent = new Quicklime<number>(0);
export function TankopediaLighting() {
return (
<group>
<hemisphereLight
intensity={HEMISPHERE_INTENSITY}
color="#ffffff"
groundColor="#afafaf"
/>
{times(LIGHTS_COUNT, (index) => {
const x = index / (LIGHTS_COUNT - 1);
const theta = 2 * Math.PI * (index / LIGHTS_COUNT) + THETA_OFFSET;
const position = [
LIGHT_DISTANCE * Math.sin(theta),
lerp(LIGHT_HEIGHT_0, LIGHT_HEIGHT_1, x),
LIGHT_DISTANCE * Math.cos(theta),
] as const;
const intensity = lerp(INTENSITY_0, INTENSITY_1, x);
return (
<spotLight
castShadow
userData={{ index }}
key={index}
position={position}
intensity={intensity}
penumbra={1}
decay={0}
color="#ffffff"
angle={ANGLE}
/>
);
})}
</group>
);
}
@@ -1,14 +1,11 @@
import {
VisualChanges_TankPart,
type StageParameters,
} from "@protos/blitz_static_tank_upgrade_single_stage";
import { type StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage";
import { OrbitControls } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import { useProtagonist } from "../../hooks/useProtagonist";
import { useTankChassisModel } from "../../hooks/useTankChassisModel";
import { useTankGunModel } from "../../hooks/useTankGunModel";
import { useTankHullModel } from "../../hooks/useTankHullModel";
import { useTankTurretModel } from "../../hooks/useTankTurretModel";
import { TankopediaLighting } from "../TankopediaLighting";
import styles from "./index.module.css";
interface TankopediaSandboxProps {
@@ -33,17 +30,6 @@ export function TankopediaSandbox({ parameters }: TankopediaSandboxProps) {
}
function Content({ parameters }: TankopediaSandboxProps) {
const tank = useProtagonist();
const selectedTurret = parameters.visual_changes.find(
({ tank_part }) => tank_part === VisualChanges_TankPart.TANK_PART_TURRET,
)!.name;
const selectedGun = parameters.visual_changes.find(
({ tank_part }) => tank_part === VisualChanges_TankPart.TANK_PART_GUN,
)!.name;
const collision = false;
const hull = useTankHullModel();
const chassis = useTankChassisModel();
const turret = useTankTurretModel(parameters);
@@ -64,23 +50,11 @@ function Content({ parameters }: TankopediaSandboxProps) {
<primitive object={gun} />
</group>
<spotLight
castShadow
position={[3, 10, 5]}
intensity={2 ** 10}
penumbra={1}
/>
<spotLight
castShadow
position={[-3, -10, -5]}
intensity={2 ** 10}
penumbra={1}
/>
<ambientLight />
<TankopediaLighting />
<mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[2 ** 8, 2 ** 8]} />
<meshStandardMaterial color="gray" />
<planeGeometry args={[2 ** 9, 2 ** 9]} />
<meshStandardMaterial color="black" />
</mesh>
<OrbitControls />
+27 -17
View File
@@ -1,33 +1,43 @@
import { useLoader } from "@react-three/fiber";
import { useEffect } from "react";
import { Mesh, MeshStandardMaterial } from "three";
import { GLTFLoader } from "three-stdlib";
import { OnObjectUpdate } from "three/src/nodes/utils/EventNode.js";
export function useTankModel(url: string, map: Record<string, boolean>) {
const gltf = useLoader(GLTFLoader, url);
const [root] = gltf.scene.children;
root.traverse((child) => {
if (
child instanceof Mesh &&
child.material instanceof MeshStandardMaterial
) {
// TODO: remove this hack and fix the gltf
child.material.vertexColors = false;
useEffect(() => {
for (const material of Object.values(gltf.materials)) {
if (material instanceof MeshStandardMaterial) {
// TODO: remove this hack and fix the gltf
material.vertexColors = false;
material.needsUpdate = true;
}
}
child.castShadow = true;
child.receiveShadow = true;
});
console.log(gltf.nodes);
for (const child of root.children) {
if (child.name in map) {
child.visible = map[child.name];
continue;
for (const node of Object.values(gltf.nodes)) {
if (node instanceof Mesh) {
node.castShadow = true;
node.receiveShadow = true;
}
}
}, [url]);
child.visible = false;
console.warn(`Root child ${child.name} isn't recognized`);
}
useEffect(() => {
for (const child of root.children) {
if (child.name in map) {
child.visible = map[child.name];
continue;
}
child.visible = false;
console.warn(`Root child ${child.name} isn't recognized`);
}
}, [url, map]);
return root;
}