mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-07-11 14:34:51 +00:00
fix skybox mirroring, tested with DMP2-Anabatic
This commit is contained in:
parent
8b86c2f05d
commit
9121d0a81b
1 changed files with 44 additions and 81 deletions
|
|
@ -1,12 +1,11 @@
|
|||
import { Suspense, useMemo, useEffect, useRef } from "react";
|
||||
import { Suspense, useMemo } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useCubeTexture } from "@react-three/drei";
|
||||
import { Color, ShaderMaterial, BackSide, Euler, ShaderChunk } from "three";
|
||||
import { Color, ShaderMaterial, BackSide, ShaderChunk } from "three";
|
||||
import type { TorqueObject } from "../torqueScript";
|
||||
import { getFloat, getInt, getProperty } from "../mission";
|
||||
import { useSettings } from "./SettingsProvider";
|
||||
import { BASE_URL, loadDetailMapList, textureToUrl } from "../loaders";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { CloudLayers } from "./CloudLayers";
|
||||
|
||||
const FALLBACK_TEXTURE_URL = `${BASE_URL}/black.png`;
|
||||
|
|
@ -46,6 +45,21 @@ if (USE_QUADRATIC_FOG) {
|
|||
installQuadraticFogShader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Tribes 2 color string (space-separated RGB or RGBA values 0-1).
|
||||
* Returns [sRGB Color, linear Color] or undefined if no color string.
|
||||
*/
|
||||
function parseColorString(
|
||||
colorString: string | undefined,
|
||||
): [Color, Color] | undefined {
|
||||
if (!colorString) return undefined;
|
||||
const [r, g, b] = colorString.split(" ").map((s) => parseFloat(s));
|
||||
return [
|
||||
new Color().setRGB(r, g, b),
|
||||
new Color().setRGB(r, g, b).convertSRGBToLinear(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a .dml file, used to list the textures for different faces of a skybox.
|
||||
*/
|
||||
|
|
@ -59,13 +73,9 @@ function useDetailMapList(name: string) {
|
|||
export function SkyBox({
|
||||
materialList,
|
||||
fogColor,
|
||||
fogNear,
|
||||
fogFar,
|
||||
}: {
|
||||
materialList: string;
|
||||
fogColor?: Color;
|
||||
fogNear?: number;
|
||||
fogFar?: number;
|
||||
}) {
|
||||
const { data: detailMapList } = useDetailMapList(materialList);
|
||||
|
||||
|
|
@ -93,21 +103,14 @@ export function SkyBox({
|
|||
|
||||
const skyBox = useCubeTexture(skyBoxFiles, { path: "" });
|
||||
|
||||
// Create a shader material for the skybox with fog
|
||||
const materialRef = useRef<ShaderMaterial>(null!);
|
||||
|
||||
const hasFog = !!fogColor && fogNear != null && fogFar != null;
|
||||
|
||||
const shaderMaterial = useMemo(() => {
|
||||
if (!hasFog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Skybox fog blends toward horizon
|
||||
// Always use a shader to apply the X-axis mirror transformation.
|
||||
// Optionally blend fog toward the horizon.
|
||||
return new ShaderMaterial({
|
||||
uniforms: {
|
||||
skybox: { value: skyBox },
|
||||
fogColor: { value: fogColor },
|
||||
fogColor: { value: fogColor ?? new Color(0, 0, 0) },
|
||||
enableFog: { value: !!fogColor },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec3 vDirection;
|
||||
|
|
@ -121,53 +124,37 @@ export function SkyBox({
|
|||
fragmentShader: `
|
||||
uniform samplerCube skybox;
|
||||
uniform vec3 fogColor;
|
||||
uniform bool enableFog;
|
||||
|
||||
varying vec3 vDirection;
|
||||
|
||||
void main() {
|
||||
vec3 direction = normalize(vDirection);
|
||||
// Swap X and Z to match scene.backgroundRotation used in non-fog path
|
||||
direction = vec3(direction.z, direction.y, direction.x);
|
||||
// Swap X and Z, negate X to mirror across X axis
|
||||
direction = vec3(direction.z, direction.y, -direction.x);
|
||||
vec4 skyColor = textureCube(skybox, direction);
|
||||
|
||||
// Fog increases toward and below horizon
|
||||
// direction.y: -1 = straight down, 0 = horizon, 1 = straight up
|
||||
// Use smoothstep for gradual transition (matches Three.js linear fog feel)
|
||||
float fogFactor = 1.0 - smoothstep(-0.1, 0.5, direction.y);
|
||||
|
||||
vec3 finalColor = mix(skyColor.rgb, fogColor, fogFactor);
|
||||
gl_FragColor = vec4(finalColor, 1.0);
|
||||
if (enableFog) {
|
||||
// Fog increases toward and below horizon
|
||||
// direction.y: -1 = straight down, 0 = horizon, 1 = straight up
|
||||
// Use smoothstep for gradual transition (matches Three.js linear fog feel)
|
||||
float fogFactor = 1.0 - smoothstep(-0.1, 0.5, direction.y);
|
||||
vec3 finalColor = mix(skyColor.rgb, fogColor, fogFactor);
|
||||
gl_FragColor = vec4(finalColor, 1.0);
|
||||
} else {
|
||||
gl_FragColor = skyColor;
|
||||
}
|
||||
}
|
||||
`,
|
||||
side: BackSide,
|
||||
depthWrite: false,
|
||||
});
|
||||
}, [skyBox, fogColor, hasFog]);
|
||||
|
||||
// Update uniforms when fog parameters change
|
||||
useEffect(() => {
|
||||
if (materialRef.current && hasFog && shaderMaterial) {
|
||||
materialRef.current.uniforms.skybox.value = skyBox;
|
||||
materialRef.current.uniforms.fogColor.value = fogColor!;
|
||||
}
|
||||
}, [skyBox, fogColor, hasFog, shaderMaterial]);
|
||||
|
||||
const { scene } = useThree();
|
||||
|
||||
// Rotate background to match the X/Z swap applied in the fog shader path
|
||||
useEffect(() => {
|
||||
scene.backgroundRotation = new Euler(0, Math.PI / 2, 0);
|
||||
}, [scene]);
|
||||
|
||||
// If fog is disabled, just use the skybox as background
|
||||
if (!hasFog) {
|
||||
return <primitive attach="background" object={skyBox} />;
|
||||
}
|
||||
}, [skyBox, fogColor]);
|
||||
|
||||
return (
|
||||
<mesh scale={5000} frustumCulled={false}>
|
||||
<sphereGeometry args={[1, 60, 40]} />
|
||||
<primitive ref={materialRef} object={shaderMaterial} attach="material" />
|
||||
<primitive object={shaderMaterial} attach="material" />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
|
@ -178,21 +165,10 @@ export function Sky({ object }: { object: TorqueObject }) {
|
|||
// Skybox textures
|
||||
const materialList = getProperty(object, "materialList");
|
||||
|
||||
const skySolidColor = useMemo(() => {
|
||||
const colorString = getProperty(object, "SkySolidColor");
|
||||
if (colorString) {
|
||||
// `colorString` might specify an alpha value, but three.js doesn't
|
||||
// support opacity on fog or scene backgrounds, so ignore it.
|
||||
// Note: This is a space-separated string, so we split and parse each component.
|
||||
const [r, g, b] = colorString
|
||||
.split(" ")
|
||||
.map((s: string) => parseFloat(s));
|
||||
return [
|
||||
new Color().setRGB(r, g, b),
|
||||
new Color().setRGB(r, g, b).convertSRGBToLinear(),
|
||||
];
|
||||
}
|
||||
}, [object]);
|
||||
const skySolidColor = useMemo(
|
||||
() => parseColorString(getProperty(object, "SkySolidColor")),
|
||||
[object],
|
||||
);
|
||||
|
||||
const useSkyTextures = getInt(object, "useSkyTextures") ?? 1;
|
||||
|
||||
|
|
@ -213,21 +189,10 @@ export function Sky({ object }: { object: TorqueObject }) {
|
|||
? highVisibleDistance
|
||||
: visibleDistanceBase;
|
||||
|
||||
const fogColor = useMemo(() => {
|
||||
const colorString = getProperty(object, "fogColor");
|
||||
if (colorString) {
|
||||
// `colorString` might specify an alpha value, but three.js doesn't
|
||||
// support opacity on fog or scene backgrounds, so ignore it.
|
||||
// Note: This is a space-separated string, so we split and parse each component.
|
||||
const [r, g, b] = colorString
|
||||
.split(" ")
|
||||
.map((s: string) => parseFloat(s));
|
||||
return [
|
||||
new Color().setRGB(r, g, b),
|
||||
new Color().setRGB(r, g, b).convertSRGBToLinear(),
|
||||
];
|
||||
}
|
||||
}, [object]);
|
||||
const fogColor = useMemo(
|
||||
() => parseColorString(getProperty(object, "fogColor")),
|
||||
[object],
|
||||
);
|
||||
|
||||
const skyColor = skySolidColor || fogColor;
|
||||
|
||||
|
|
@ -245,8 +210,6 @@ export function Sky({ object }: { object: TorqueObject }) {
|
|||
<SkyBox
|
||||
materialList={materialList}
|
||||
fogColor={fogEnabled && hasFogParams ? fogColor?.[1] : undefined}
|
||||
fogNear={fogEnabled && hasFogParams ? fogNear : undefined}
|
||||
fogFar={fogEnabled && hasFogParams ? fogFar : undefined}
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue