mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
lighting
This commit is contained in:
1 parent
91ac93670f
commit
e3cf2d51cc
5 files changed
+120
-50
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user