mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-04-25 22:35:45 +00:00
526 lines
78 KiB
JavaScript
526 lines
78 KiB
JavaScript
|
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/PlayerModel-Beu7pkFZ.js","assets/chunk-DECur_0Z.js","assets/mission-CFLOK_Oy.js","assets/logger-zy3b0zcG.js","assets/GenericShape-DCImTyf2.js","assets/Texture-CI_Y9elU.js","assets/react-three-fiber.esm-dhSWjERg.js","assets/jsx-runtime-BpGWiA-R.js","assets/three.module-CwgFV8Kd.js","assets/traditional-BTL5qX2E.js","assets/useBaseQuery-CZsAiqUk.js","assets/index-B_QKaSWI.js","assets/preload-helper-BcSNVXZP.js","assets/streamHelpers-Be29sBCp.js","assets/SettingsProvider-2cUUftaX.js","assets/manifest-CmJ-OiDc.js","assets/stringUtils-BfUKbRCH.js","assets/iconBase-BCRUFbxq.js","assets/engineStore-Cio8vU1L.js","assets/scene-B1EyKjda.js","assets/index-UOHJQQT_.css","assets/FloatingLabel-CXLHy5vc.js","assets/Html-C5epU_38.js","assets/extends-C_PM0Yom.js","assets/FloatingLabel-DfptgH-Y.css","assets/globalFogUniforms-DQn9KFk6.js","assets/loaders-C_1cX1lR.js","assets/AudioContext-B-BTQZ7_.js","assets/AudioEmitter-DQcaN3l9.js","assets/DebugSuspense-DTOoTVsz.js","assets/ShapeErrorBoundary-BDMn5cgT.js","assets/streamPlaybackStore-CszjeINn.js","assets/ShapeModel-Dfvda7ou.js","assets/Projectiles-CMqanslS.js","assets/ForceFieldBare-BNF6ljSM.js","assets/WaterBlock-cCfl8B1Q.js","assets/StreamingController-rKlAT2Gw.js","assets/gameEntityTypes-n-ppAY7z.js","assets/DebugElements-Dj9DyZN_.js","assets/DebugElements-BP0b5jan.css","assets/Mission-BRbZNPqp.js","assets/useQuery-CwJPnLi6.js","assets/misToScene-CcfLZFwc.js","assets/ChatSoundPlayer-DgLOsG9m.js"])))=>i.map(i=>d[i]);
|
|||
|
|
import{r as e}from"./chunk-DECur_0Z.js";import{n as t,r as n,t as r}from"./jsx-runtime-BpGWiA-R.js";import{a as i,i as a,o,t as s}from"./react-three-fiber.esm-dhSWjERg.js";import{a as c,i as l}from"./SettingsProvider-2cUUftaX.js";import{t as u}from"./useQuery-CwJPnLi6.js";import{C as d,D as f,E as p,T as m,d as h,n as g,r as _,t as v,u as y,w as b,x}from"./GenericShape-DCImTyf2.js";import{t as S}from"./stringUtils-BfUKbRCH.js";import{t as C}from"./logger-zy3b0zcG.js";import"./traditional-BTL5qX2E.js";import{t as w,x as T}from"./streamHelpers-Be29sBCp.js";import{A as E,At as D,C as O,Dt as k,E as A,Et as j,Gt as M,Ht as N,K as P,M as F,O as I,Ot as L,S as ee,St as R,Ut as z,Vt as te,_ as ne,b as B,f as re,h as ie,k as ae,lt as oe,m as se,nt as ce,v as V,w as le}from"./three.module-CwgFV8Kd.js";import{n as ue,r as de,t as fe}from"./scene-B1EyKjda.js";import"./mission-CFLOK_Oy.js";import{a as pe,i as me}from"./engineStore-Cio8vU1L.js";import{t as H}from"./preload-helper-BcSNVXZP.js";import{t as he}from"./extends-C_PM0Yom.js";import{t as ge}from"./Html-C5epU_38.js";import{t as _e}from"./Texture-CI_Y9elU.js";import{S as ve,_ as ye,a as be,b as xe,g as Se,i as Ce,l as we,m as Te,n as Ee,t as De,x as Oe,y as ke}from"./index-B_QKaSWI.js";import{f as Ae,o as je,p as U,s as Me,t as Ne,u as Pe}from"./loaders-C_1cX1lR.js";import{t as Fe}from"./AudioContext-B-BTQZ7_.js";import{t as Ie}from"./FloatingLabel-CXLHy5vc.js";import{t as Le}from"./DebugSuspense-DTOoTVsz.js";import{t as Re}from"./gameEntityTypes-n-ppAY7z.js";import{n as ze}from"./streamPlaybackStore-CszjeINn.js";import{c as Be,d as Ve,f as He,i as Ue,n as We,o as Ge,r as Ke,s as qe,t as Je}from"./globalFogUniforms-DQn9KFk6.js";import{t as Ye}from"./ShapeErrorBoundary-BDMn5cgT.js";var W=e(n());function Xe(e,t,n){let r=o(e=>e.size),i=o(e=>e.viewport),a=typeof e==`number`?e:r.width*i.dpr,s=typeof t==`number`?t:r.height*i.dpr,c=(typeof e==`number`?n:e)||{},{samples:l=0,depth:u,...d}=c,f=u??c.depthBuffer,p=W.useMemo(()=>{let e=new M(a,s,{minFilter:P,magFilter:P,type:F,...d});return f&&(e.depthTexture=new le(a,s,ae)),e.samples=l,e},[]);return W.useLayoutEffect(()=>{p.setSize(a,s),l&&(p.samples=l)},[l,p,a,s]),W.useEffect(()=>()=>p.dispose(),[]),p}var Ze=e=>typeof e==`function`,Qe=W.forwardRef(({envMap:e,resolution:t=256,frames:n=1/0,makeDefault:r,children:i,...s},c)=>{let l=o(({set:e})=>e),u=o(({camera:e})=>e),d=o(({size:e})=>e),f=W.useRef(null);W.useImperativeHandle(c,()=>f.current,[]);let p=W.useRef(null),m=Xe(t);W.useLayoutEffect(()=>{s.manual||(f.current.aspect=d.width/d.height)},[d,s]),W.useLayoutEffect(()=>{f.current.updateProjectionMatrix()});let h=0,g=null,_=Ze(i);return a(t=>{_&&(n===1/0||h<n)&&(p.current.visible=!1,t.gl.setRenderTarget(m),g=t.scene.background,e&&(t.scene.background=e),t.gl.render(t.scene,f.current),t.scene.background=g,t.gl.setRenderTarget(null),p.current.visible=!0,h++)}),W.useLayoutEffect(()=>{if(r){let e=u;return l(()=>({camera:f.current})),()=>l(()=>({camera:e}))}},[f,r,l]),W.createElement(W.Fragment,null,W.createElement(`perspectiveCamera`,he({ref:f},s),!_&&i),W.createElement(`group`,{ref:p},_&&i(m.texture)))});function $e(e,{path:t}){let[n]=i(ee,[e],e=>e.setPath(t));return n}$e.preload=(e,{path:t})=>i.preload(ee,[e],e=>e.setPath(t));var G=t(),et={sunLightPointsDown:{value:!0}};function tt(e){et.sunLightPointsDown.value=e}var K=r(),nt=C(`SceneLighting`);function rt(){let e=(0,G.c)(6),t=ve(),n,r;if(e[0]===t?(n=e[1],r=e[2]):(n=()=>{t?nt.debug(`sunData: dir=(%s, %s, %s) color=(%s, %s, %s) ambient=(%s, %s, %s)`,t.direction.x.toFixed(3),t.direction.y.toFixed(3),t.direction.z.toFixed(3),t.color.r.toFixed(3),t.color.g.toFixed(3),t.color.b.toFixed(3),t.ambient.r.toFixed(3),t.ambient.g.toFixed(3),t.ambient.b.toFixed(3)):nt.debug(`No sunData — using fallback ambient #888`)},r=[t],e[0]=t,e[1]=n,e[2]=r),(0,W.useEffect)(n,r),!t){let t;return e[3]===Symbol.for(`react.memo_cache_sentinel`)?(t=(0,K.jsx)(`ambientLight`,{color:`#888888`,intensity:1}),e[3]=t):t=e[3],t}let i;return e[4]===t?i=e[5]:(i=(0,K.jsx)(it,{sunData:t}),e[4]=t,e[5]=i),i}function it(e){let t=(0,G
|
|||
|
|
vec3 terrainLinearToSRGB(vec3 linear) {
|
|||
|
|
vec3 higher = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055;
|
|||
|
|
vec3 lower = linear * 12.92;
|
|||
|
|
return mix(lower, higher, step(vec3(0.0031308), linear));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
vec3 terrainSRGBToLinear(vec3 srgb) {
|
|||
|
|
vec3 higher = pow((srgb + 0.055) / 1.055, vec3(2.4));
|
|||
|
|
vec3 lower = srgb / 12.92;
|
|||
|
|
return mix(lower, higher, step(vec3(0.04045), srgb));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Debug grid overlay using screen-space derivatives for sharp, anti-aliased lines
|
|||
|
|
// Returns 1.0 on grid lines, 0.0 elsewhere
|
|||
|
|
float terrainDebugGrid(vec2 uv, float gridSize, float lineWidth) {
|
|||
|
|
vec2 scaledUV = uv * gridSize;
|
|||
|
|
vec2 grid = abs(fract(scaledUV - 0.5) - 0.5) / fwidth(scaledUV);
|
|||
|
|
float line = min(grid.x, grid.y);
|
|||
|
|
return 1.0 - min(line / lineWidth, 1.0);
|
|||
|
|
}
|
|||
|
|
`;function vt({shader:e,baseTextures:t,alphaTextures:n,visibilityMask:r,tiling:i,detailTexture:a=null,lightmap:o=null}){e.uniforms.sunLightPointsDown=et.sunLightPointsDown;let s=t.length;if(t.forEach((t,n)=>{e.uniforms[`albedo${n}`]={value:t}}),n.forEach((t,n)=>{e.uniforms[`mask${n}`]={value:t}}),r&&(e.uniforms.visibilityMask={value:r}),t.forEach((t,n)=>{e.uniforms[`tiling${n}`]={value:i[n]??32}}),o&&(e.uniforms.terrainLightmap={value:o}),a&&(e.uniforms.detailTexture={value:a},e.uniforms.detailTiling={value:ht},e.uniforms.detailFadeDistance={value:gt},e.vertexShader=e.vertexShader.replace(`#include <common>`,`#include <common>
|
|||
|
|
varying vec3 vTerrainWorldPos;`),e.vertexShader=e.vertexShader.replace(`#include <worldpos_vertex>`,`#include <worldpos_vertex>
|
|||
|
|
vec4 _terrainPos = vec4(transformed, 1.0);
|
|||
|
|
#ifdef USE_INSTANCING
|
|||
|
|
_terrainPos = instanceMatrix * _terrainPos;
|
|||
|
|
#endif
|
|||
|
|
vTerrainWorldPos = (modelMatrix * _terrainPos).xyz;`)),e.fragmentShader=`
|
|||
|
|
uniform sampler2D albedo0;
|
|||
|
|
uniform sampler2D albedo1;
|
|||
|
|
uniform sampler2D albedo2;
|
|||
|
|
uniform sampler2D albedo3;
|
|||
|
|
uniform sampler2D albedo4;
|
|||
|
|
uniform sampler2D albedo5;
|
|||
|
|
uniform sampler2D mask0;
|
|||
|
|
uniform sampler2D mask1;
|
|||
|
|
uniform sampler2D mask2;
|
|||
|
|
uniform sampler2D mask3;
|
|||
|
|
uniform sampler2D mask4;
|
|||
|
|
uniform sampler2D mask5;
|
|||
|
|
uniform float tiling0;
|
|||
|
|
uniform float tiling1;
|
|||
|
|
uniform float tiling2;
|
|||
|
|
uniform float tiling3;
|
|||
|
|
uniform float tiling4;
|
|||
|
|
uniform float tiling5;
|
|||
|
|
${r?`uniform sampler2D visibilityMask;`:``}
|
|||
|
|
${o?`uniform sampler2D terrainLightmap;`:``}
|
|||
|
|
uniform bool sunLightPointsDown;
|
|||
|
|
${a?`uniform sampler2D detailTexture;
|
|||
|
|
uniform float detailTiling;
|
|||
|
|
uniform float detailFadeDistance;
|
|||
|
|
varying vec3 vTerrainWorldPos;`:``}
|
|||
|
|
|
|||
|
|
${_t}
|
|||
|
|
|
|||
|
|
// Global variable to store shadow factor from RE_Direct for use in output calculation
|
|||
|
|
float terrainShadowFactor = 1.0;
|
|||
|
|
`+e.fragmentShader,r){let t=`#include <clipping_planes_fragment>`;e.fragmentShader=e.fragmentShader.replace(t,`${t}
|
|||
|
|
// Early discard for invisible areas (before fog/lighting)
|
|||
|
|
float visibility = texture2D(visibilityMask, vMapUv).r;
|
|||
|
|
if (visibility < 0.5) {
|
|||
|
|
discard;
|
|||
|
|
}
|
|||
|
|
`)}e.fragmentShader=e.fragmentShader.replace(`#include <map_fragment>`,`
|
|||
|
|
// Sample base albedo layers (sRGB textures auto-decoded to linear by Three.js)
|
|||
|
|
vec2 baseUv = vMapUv;
|
|||
|
|
vec3 c0 = texture2D(albedo0, baseUv * vec2(tiling0)).rgb;
|
|||
|
|
${s>1?`vec3 c1 = texture2D(albedo1, baseUv * vec2(tiling1)).rgb;`:``}
|
|||
|
|
${s>2?`vec3 c2 = texture2D(albedo2, baseUv * vec2(tiling2)).rgb;`:``}
|
|||
|
|
${s>3?`vec3 c3 = texture2D(albedo3, baseUv * vec2(tiling3)).rgb;`:``}
|
|||
|
|
${s>4?`vec3 c4 = texture2D(albedo4, baseUv * vec2(tiling4)).rgb;`:``}
|
|||
|
|
${s>5?`vec3 c5 = texture2D(albedo5, baseUv * vec2(tiling5)).rgb;`:``}
|
|||
|
|
|
|||
|
|
// Sample alpha masks for all layers (use R channel)
|
|||
|
|
// Add +0.5 texel offset: Torque samples alpha at grid corners (integer indices),
|
|||
|
|
// but GPU linear filtering samples at texel centers. This offset aligns them.
|
|||
|
|
vec2 alphaUv = baseUv + vec2(0.5 / ${pt}.0);
|
|||
|
|
float a0 = texture2D(mask0, alphaUv).r;
|
|||
|
|
${s>1?`float a1 = texture2D(mask1, alphaUv).r;`:``}
|
|||
|
|
${s>2?`float a2 = texture2D(mask2, alphaUv).r;`:``}
|
|||
|
|
${s>3?`float a3 = texture2D(mask3, alphaUv).r;`:``}
|
|||
|
|
${s>4?`float a4 = texture2D(mask4, alphaUv).r;`:``}
|
|||
|
|
${s>5?`float a5 = texture2D(mask5, alphaUv).r;`:``}
|
|||
|
|
|
|||
|
|
// Torque-style additive weighted blending (blender.cc):
|
|||
|
|
// result = tex0 * alpha0 + tex1 * alpha1 + tex2 * alpha2 + ...
|
|||
|
|
// Each layer's alpha map defines its contribution weight.
|
|||
|
|
vec3 blended = c0 * a0;
|
|||
|
|
${s>1?`blended += c1 * a1;`:``}
|
|||
|
|
${s>2?`blended += c2 * a2;`:``}
|
|||
|
|
${s>3?`blended += c3 * a3;`:``}
|
|||
|
|
${s>4?`blended += c4 * a4;`:``}
|
|||
|
|
${s>5?`blended += c5 * a5;`:``}
|
|||
|
|
|
|||
|
|
// Assign to diffuseColor before lighting
|
|||
|
|
vec3 textureColor = blended;
|
|||
|
|
|
|||
|
|
${a?`// Detail texture blending (Torque-style multiplicative blend)
|
|||
|
|
// Sample detail texture at high frequency tiling
|
|||
|
|
vec3 detailColor = texture2D(detailTexture, baseUv * detailTiling).rgb;
|
|||
|
|
|
|||
|
|
// Calculate distance-based fade factor using world positions
|
|||
|
|
// Torque: distFactor = (zeroDetailDistance - distance) / zeroDetailDistance
|
|||
|
|
float distToCamera = distance(vTerrainWorldPos, cameraPosition);
|
|||
|
|
float detailFade = clamp(1.0 - distToCamera / detailFadeDistance, 0.0, 1.0);
|
|||
|
|
|
|||
|
|
// Torque blending: dst * lerp(1.0, detailTexel, fadeFactor)
|
|||
|
|
// Detail textures are authored with bright values (~0.8 mean), not 0.5 gray
|
|||
|
|
// Direct multiplication adds subtle darkening for surface detail
|
|||
|
|
textureColor *= mix(vec3(1.0), detailColor, detailFade);`:``}
|
|||
|
|
|
|||
|
|
// Store blended texture in diffuseColor (still in linear space here)
|
|||
|
|
// We'll convert to sRGB in the output calculation
|
|||
|
|
diffuseColor.rgb = textureColor;
|
|||
|
|
`),o&&(e.fragmentShader=e.fragmentShader.replace(`#include <lights_lambert_pars_fragment>`,`#include <lights_lambert_pars_fragment>
|
|||
|
|
|
|||
|
|
// Override RE_Direct to extract shadow factor for Torque-style gamma-space lighting
|
|||
|
|
#undef RE_Direct
|
|||
|
|
void RE_Direct_TerrainShadow( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {
|
|||
|
|
// Torque lighting (terrLighting.cc): if light points up, terrain gets only ambient
|
|||
|
|
// This prevents shadow acne from light hitting terrain backfaces
|
|||
|
|
if (!sunLightPointsDown) {
|
|||
|
|
terrainShadowFactor = 0.0;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
// directLight.color = sunColor * shadowFactor (shadow already applied by Three.js)
|
|||
|
|
// Extract shadow factor by comparing to original sun color
|
|||
|
|
#if ( NUM_DIR_LIGHTS > 0 )
|
|||
|
|
vec3 originalSunColor = directionalLights[0].color;
|
|||
|
|
float sunMax = max(max(originalSunColor.r, originalSunColor.g), originalSunColor.b);
|
|||
|
|
float shadowedMax = max(max(directLight.color.r, directLight.color.g), directLight.color.b);
|
|||
|
|
terrainShadowFactor = clamp(shadowedMax / max(sunMax, 0.001), 0.0, 1.0);
|
|||
|
|
#endif
|
|||
|
|
// Don't add to reflectedLight - we'll compute lighting in gamma space at output
|
|||
|
|
}
|
|||
|
|
#define RE_Direct RE_Direct_TerrainShadow
|
|||
|
|
|
|||
|
|
`),e.fragmentShader=e.fragmentShader.replace(`#include <lights_fragment_begin>`,`#include <lights_fragment_begin>
|
|||
|
|
// Clear indirect diffuse - we'll compute ambient in gamma space
|
|||
|
|
#if defined( RE_IndirectDiffuse )
|
|||
|
|
irradiance = vec3(0.0);
|
|||
|
|
#endif
|
|||
|
|
`),e.fragmentShader=e.fragmentShader.replace(`#include <lights_fragment_end>`,`#include <lights_fragment_end>
|
|||
|
|
// Clear Three.js lighting - we compute everything in gamma space
|
|||
|
|
reflectedLight.directDiffuse = vec3(0.0);
|
|||
|
|
reflectedLight.indirectDiffuse = vec3(0.0);
|
|||
|
|
`)),e.fragmentShader=e.fragmentShader.replace(`#include <opaque_fragment>`,`// Torque-style terrain lighting: output = clamp(lighting × texture, 0, 1) in sRGB space
|
|||
|
|
{
|
|||
|
|
// Get texture in sRGB space (undo Three.js linear decode)
|
|||
|
|
vec3 textureSRGB = terrainLinearToSRGB(diffuseColor.rgb);
|
|||
|
|
|
|||
|
|
${o?`
|
|||
|
|
// Sample terrain lightmap for smooth NdotL
|
|||
|
|
vec2 lightmapUv = vMapUv + vec2(0.5 / ${mt}.0);
|
|||
|
|
float lightmapNdotL = texture2D(terrainLightmap, lightmapUv).r;
|
|||
|
|
|
|||
|
|
// Get sun and ambient colors from Three.js lights (these ARE sRGB values from mission file)
|
|||
|
|
// Three.js interprets them as linear, but the numerical values are preserved
|
|||
|
|
#if ( NUM_DIR_LIGHTS > 0 )
|
|||
|
|
vec3 sunColorSRGB = directionalLights[0].color;
|
|||
|
|
#else
|
|||
|
|
vec3 sunColorSRGB = vec3(0.7);
|
|||
|
|
#endif
|
|||
|
|
vec3 ambientColorSRGB = ambientLightColor;
|
|||
|
|
|
|||
|
|
// Torque formula (terrLighting.cc:471-483):
|
|||
|
|
// lighting = ambient + NdotL * shadowFactor * sunColor
|
|||
|
|
// Clamp lighting to [0,1] before multiplying by texture
|
|||
|
|
vec3 lightingSRGB = clamp(ambientColorSRGB + lightmapNdotL * terrainShadowFactor * sunColorSRGB, 0.0, 1.0);
|
|||
|
|
`:`
|
|||
|
|
// No lightmap - use simple ambient lighting
|
|||
|
|
vec3 lightingSRGB = ambientLightColor;
|
|||
|
|
`}
|
|||
|
|
|
|||
|
|
// Torque formula: output = clamp(lighting × texture, 0, 1) in sRGB/gamma space
|
|||
|
|
vec3 resultSRGB = clamp(lightingSRGB * textureSRGB, 0.0, 1.0);
|
|||
|
|
|
|||
|
|
// Convert back to linear for Three.js output pipeline
|
|||
|
|
outgoingLight = terrainSRGBToLinear(resultSRGB) + totalEmissiveRadiance;
|
|||
|
|
}
|
|||
|
|
#include <opaque_fragment>`),e.fragmentShader=e.fragmentShader.replace(`#include <tonemapping_fragment>`,`#if DEBUG_MODE
|
|||
|
|
// Debug mode: overlay green grid matching terrain grid squares (256x256)
|
|||
|
|
float gridIntensity = terrainDebugGrid(vMapUv, 256.0, 1.5);
|
|||
|
|
vec3 gridColor = vec3(0.0, 0.8, 0.4); // Green
|
|||
|
|
gl_FragColor.rgb = mix(gl_FragColor.rgb, gridColor, gridIntensity * 0.1);
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#include <tonemapping_fragment>`)}var yt={0:32,1:32,2:32,3:32,4:32,5:32},bt=(0,W.memo)(function({displacementMap:e,visibilityMask:t,textureNames:n,alphaTextures:r,detailTextureName:i,lightmap:a}){let{debugMode:o}=l(),s=Be(),c=_e(n.map(e=>Ae(e)),e=>{e.forEach(e=>He(e,{anisotropy:s}))}),u=i?U(i):null,d=_e(u??Ne,e=>{He(e,{anisotropy:s})}),f=(0,W.useCallback)(e=>{vt({shader:e,baseTextures:c,alphaTextures:r,visibilityMask:t,tiling:yt,detailTexture:u?d:null,lightmap:a}),Ge(e,Je)},[c,r,t,d,u,a]),p=(0,W.useMemo)(()=>[n.join(`,`),u??`none`,a?a.id:`nolm`,c.map(e=>e.id).join(`,`)].join(`|`),[n,u,a,c]),m=(0,W.useRef)(null);return(0,W.useEffect)(()=>{let e=m.current;e&&(e.defines??={},e.defines.DEBUG_MODE=o?1:0,e.needsUpdate=!0)},[o]),(0,W.useEffect)(()=>{let e=m.current;e&&(e.customProgramCacheKey=()=>p,e.needsUpdate=!0)},[p]),(0,K.jsx)(`meshLambertMaterial`,{ref:m,map:e,depthWrite:!0,side:0,defines:{DEBUG_MODE:o?1:0},onBeforeCompile:f},`${u?`detail`:`nodetail`}-${a?`lightmap`:`nolightmap`}`)}),xt=(0,W.memo)(function(e){let t=(0,G.c)(8),{displacementMap:n,visibilityMask:r,textureNames:i,alphaTextures:a,detailTextureName:o,lightmap:s}=e,c;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(c=(0,K.jsx)(`meshLambertMaterial`,{color:`rgb(0, 109, 56)`,wireframe:!0}),t[0]=c):c=t[0];let l;return t[1]!==a||t[2]!==o||t[3]!==n||t[4]!==s||t[5]!==i||t[6]!==r?(l=(0,K.jsx)(W.Suspense,{fallback:c,children:(0,K.jsx)(bt,{displacementMap:n,visibilityMask:r,textureNames:i,alphaTextures:a,detailTextureName:o,lightmap:s})}),t[1]=a,t[2]=o,t[3]=n,t[4]=s,t[5]=i,t[6]=r,t[7]=l):l=t[7],l}),St=(0,W.memo)(function(e){let t=(0,G.c)(15),{tileX:n,tileZ:r,blockSize:i,basePosition:a,textureNames:o,geometry:s,displacementMap:c,visibilityMask:l,alphaTextures:u,detailTextureName:d,lightmap:f,visible:p}=e,m=p===void 0?!0:p,h=i/2,g=a.x+n*i+h,_=a.z+r*i+h,v;t[0]!==g||t[1]!==_?(v=[g,0,_],t[0]=g,t[1]=_,t[2]=v):v=t[2];let y=v,b;t[3]!==u||t[4]!==d||t[5]!==c||t[6]!==f||t[7]!==o||t[8]!==l?(b=(0,K.jsx)(xt,{displacementMap:c,visibilityMask:l,textureNames:o,alphaTextures:u,detailTextureName:d,lightmap:f}),t[3]=u,t[4]=d,t[5]=c,t[6]=f,t[7]=o,t[8]=l,t[9]=b):b=t[9];let x;return t[10]!==s||t[11]!==y||t[12]!==b||t[13]!==m?(x=(0,K.jsx)(`mesh`,{position:y,geometry:s,castShadow:!0,receiveShadow:!0,visible:m,children:b}),t[10]=s,t[11]=y,t[12]=b,t[13]=m,t[14]=x):x=t[14],x}),Ct=C(`TerrainBlock`),wt=8,Tt=600,q=256,J=512,Y=2048;function Et(e,t){let n=new ie,r=(t+1)*(t+1),i=new Float32Array(r*3),a=new Float32Array(r*3),o=new Float32Array(r*2),s=t*t*6,c=new Uint32Array(s),l=0,u=e/t;for(let n=0;n<=t;n++)for(let r=0;r<=t;r++){let s=n*(t+1)+r;i[s*3]=r*u-e/2,i[s*3+1]=e/2-n*u,i[s*3+2]=0,a[s*3]=0,a[s*3+1]=0,a[s*3+2]=1,o[s*2]=r/t,o[s*2+1]=1-n/t}for(let e=0;e<t;e++)for(let n=0;n<t;n++){let r=e*(t+1)+n,i=r+1,a=(e+1)*(t+1)+n,o=a+1;(n^e)&1?(c[l++]=r,c[l++]=a,c[l++]=i,c[l++]=i,c[l++]=a,c[l++]=o):(c[l++]=r,c[l++]=a,c[l++]=o,c[l++]=r,c[l++]=o,c[l++]=i)}return n.setIndex(new se(c,1)),n.setAttribute(`position`,new I(i,3)),n.setAttribute(`normal`,new I(a,3)),n.setAttribute(`uv`,new I(o,2)),n.rotateX(-Math.PI/2),n.rotateY(-Math.PI/2),n}function Dt(e,t,n){let r=e.attributes.position,i=e.attributes.uv,a=e.attributes.normal,o=r.array,s=i.array,c=a.array,l=r.count,u=(e,n)=>(e=Math.max(0,Math.min(q-1,e)),n=Math.max(0,Math.min(q-1,n)),t[n*q+e]/65535*Y),d=(e,n)=>{e=Math.max(0,Math.min(q-1,e)),n=Math.max(0,Math.min(q-1,n));let r=Math.floor(e),i=Math.floor(n),a=Math.min(r+1,q-1),o=Math.min(i+1,q-1),s=e-r,c=n-i,l=t[i*q+r]/65535*Y,u=t[i*q+a]/65535*Y,d=t[o*q+r]/65535*Y,f=t[o*q+a]/65535*Y,p=l*(1-s)+u*s,m=d*(1-s)+f*s;return p*(1-c)+m*c};for(let e=0;e<l;e++){let t=s[e*2],r=s[e*2+1],i=u(Math.floor(t*q)&q-1,Math.floor(r*q)&q-1);o[e*3+1]=i;let a=t*(q-1),l=r*(q-1),f=d(a-1,l),p=d(a+1,l),m=d(a,l+1),h=d(a,l-1),g=(p-f)/2,_=(m-h)/2,v=n,y=g,b=Math.sqrt(_*_+v*v+y*y);b>0?(_/=b,v/=b,y/=b):(_=0,v=1,y=0),c[e*3]=_,c[e*3+1]=v,c[e*3+2]=y}r.needsUpdate=!0,a.needsUpdate=!0}function Ot(e,t,n,r,i,a){let o=r.z/i,s=r.x/i,c=r.y,l=Math.sqrt(o*o+s*s);if(l<1e-4)return 1;let u=.5/l,d=o*u,f=s*u,p=c*u,m=e,h=t,g=n+.1,_=q*3;for(let e=0;e<_;e++){if(m+=d,h+=f,g+=p,m<
|
|||
|
|
vec3 interiorLinearToSRGB(vec3 linear) {
|
|||
|
|
vec3 higher = pow(linear, vec3(1.0/2.4)) * 1.055 - 0.055;
|
|||
|
|
vec3 lower = linear * 12.92;
|
|||
|
|
return mix(lower, higher, step(vec3(0.0031308), linear));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
vec3 interiorSRGBToLinear(vec3 srgb) {
|
|||
|
|
vec3 higher = pow((srgb + 0.055) / 1.055, vec3(2.4));
|
|||
|
|
vec3 lower = srgb / 12.92;
|
|||
|
|
return mix(lower, higher, step(vec3(0.04045), srgb));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Debug grid overlay function using screen-space derivatives for sharp, anti-aliased lines
|
|||
|
|
// Returns 1.0 on grid lines, 0.0 elsewhere
|
|||
|
|
float debugGrid(vec2 uv, float gridSize, float lineWidth) {
|
|||
|
|
vec2 scaledUV = uv * gridSize;
|
|||
|
|
vec2 grid = abs(fract(scaledUV - 0.5) - 0.5) / fwidth(scaledUV);
|
|||
|
|
float line = min(grid.x, grid.y);
|
|||
|
|
return 1.0 - min(line / lineWidth, 1.0);
|
|||
|
|
}
|
|||
|
|
`;function Rt(e,t){let n=t.surfaceOutsideVisible??!1;e.uniforms.useSceneLighting={value:n},e.uniforms.interiorDebugColor={value:n?new z(0,.4,1):new z(1,.2,0)},e.fragmentShader=e.fragmentShader.replace(`#include <common>`,`#include <common>
|
|||
|
|
${Lt}
|
|||
|
|
uniform bool useSceneLighting;
|
|||
|
|
uniform vec3 interiorDebugColor;
|
|||
|
|
`),e.fragmentShader=e.fragmentShader.replace(`#include <lights_fragment_maps>`,`// Lightmap handled in custom output calculation
|
|||
|
|
#ifdef USE_LIGHTMAP
|
|||
|
|
vec4 lightMapTexel = texture2D( lightMap, vLightMapUv );
|
|||
|
|
#endif`),e.fragmentShader=e.fragmentShader.replace(`#include <opaque_fragment>`,`// Torque-style lighting: output = clamp(lighting × texture, 0, 1) in sRGB space
|
|||
|
|
// Get texture in sRGB space (undo Three.js linear decode)
|
|||
|
|
vec3 textureSRGB = interiorLinearToSRGB(diffuseColor.rgb);
|
|||
|
|
|
|||
|
|
// Compute lighting in sRGB space
|
|||
|
|
vec3 lightingSRGB = vec3(0.0);
|
|||
|
|
|
|||
|
|
if (useSceneLighting) {
|
|||
|
|
// Three.js computed: reflectedLight = lighting × texture_linear / PI
|
|||
|
|
// Extract pure lighting: lighting = reflectedLight × PI / texture_linear
|
|||
|
|
vec3 totalLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
|
|||
|
|
vec3 safeTexLinear = max(diffuseColor.rgb, vec3(0.001));
|
|||
|
|
vec3 extractedLighting = totalLight * PI / safeTexLinear;
|
|||
|
|
// NOTE: extractedLighting is ALREADY sRGB values because mission sun/ambient colors
|
|||
|
|
// are sRGB values (Torque used them directly in gamma space). Three.js treats them
|
|||
|
|
// as linear but the numerical values are the same. DO NOT convert to sRGB here!
|
|||
|
|
// IMPORTANT: Torque clamps scene lighting to [0,1] BEFORE adding to lightmap
|
|||
|
|
// (sceneLighting.cc line 1785: tmp.clamp())
|
|||
|
|
lightingSRGB = clamp(extractedLighting, 0.0, 1.0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add lightmap contribution (for BOTH outside and inside surfaces)
|
|||
|
|
// In Torque, scene lighting is ADDED to lightmaps for outside surfaces at mission load
|
|||
|
|
// (stored in .ml files). Inside surfaces only have base lightmap. Both need lightmap here.
|
|||
|
|
#ifdef USE_LIGHTMAP
|
|||
|
|
// Lightmap is stored as linear in Three.js (decoded from sRGB texture), convert back
|
|||
|
|
lightingSRGB += interiorLinearToSRGB(lightMapTexel.rgb);
|
|||
|
|
#endif
|
|||
|
|
// Torque clamps the sum to [0,1] per channel (sceneLighting.cc lines 1817-1827)
|
|||
|
|
lightingSRGB = clamp(lightingSRGB, 0.0, 1.0);
|
|||
|
|
|
|||
|
|
// Torque formula: output = clamp(lighting × texture, 0, 1) in sRGB/gamma space
|
|||
|
|
vec3 resultSRGB = clamp(lightingSRGB * textureSRGB, 0.0, 1.0);
|
|||
|
|
|
|||
|
|
// Convert back to linear for Three.js output pipeline
|
|||
|
|
vec3 resultLinear = interiorSRGBToLinear(resultSRGB);
|
|||
|
|
|
|||
|
|
// Reassign outgoingLight before opaque_fragment consumes it
|
|||
|
|
outgoingLight = resultLinear + totalEmissiveRadiance;
|
|||
|
|
|
|||
|
|
#include <opaque_fragment>`),e.fragmentShader=e.fragmentShader.replace(`#include <tonemapping_fragment>`,`// Debug mode: overlay colored grid on top of normal rendering
|
|||
|
|
// Blue grid = SurfaceOutsideVisible (receives scene ambient light)
|
|||
|
|
// Red grid = inside surface (no scene ambient light)
|
|||
|
|
#if DEBUG_MODE && defined(USE_MAP)
|
|||
|
|
// gridSize=4 creates 4x4 grid per UV tile, lineWidth=1.5 is ~1.5 pixels wide
|
|||
|
|
float gridIntensity = debugGrid(vMapUv, 4.0, 1.5);
|
|||
|
|
gl_FragColor.rgb = mix(gl_FragColor.rgb, interiorDebugColor, gridIntensity * 0.1);
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#include <tonemapping_fragment>`)}var zt=C(`InteriorInstance`);function Bt(e){let t=(0,G.c)(2),n;return t[0]===e?n=t[1]:(n=je(e),t[0]=e,t[1]=n),f(n)}function Vt({materialName:e,material:t,lightMap:n}){let r=l()?.debugMode??!1,i=Be(),a=_e(U(e),e=>He(e,{anisotropy:i})),o=new Set(t?.userData?.flag_names??[]).has(`SelfIlluminating`),s=new Set(t?.userData?.surface_flag_names??[]).has(`SurfaceOutsideVisible`),c=(0,W.useCallback)(e=>{Ge(e,Je),Rt(e,{surfaceOutsideVisible:s})},[s]),u=(0,W.useRef)(null),d=(0,W.useRef)(null);(0,W.useEffect)(()=>{let e=u.current??d.current;e&&(e.defines??={},e.defines.DEBUG_MODE=r?1:0,e.needsUpdate=!0)},[r]);let f={DEBUG_MODE:r?1:0},p=`${s}`;return o?(0,K.jsx)(`meshBasicMaterial`,{ref:u,map:a,toneMapped:!1,defines:f,onBeforeCompile:c},p):(0,K.jsx)(`meshLambertMaterial`,{ref:d,map:a,lightMap:n,toneMapped:!1,defines:f,onBeforeCompile:c},p)}function Ht(e){if(!e)return null;let t=e.emissiveMap;return t&&(t.colorSpace=L),t??null}function Ut(e){let t=(0,G.c)(13),{node:n}=e,r;bb0:{if(!n.material){let e;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=[],t[0]=e):e=t[0],r=e;break bb0}if(Array.isArray(n.material)){let e;t[1]===n.material?e=t[2]:(e=n.material.map(Wt),t[1]=n.material,t[2]=e),r=e;break bb0}let e;t[3]===n.material?e=t[4]:(e=Ht(n.material),t[3]=n.material,t[4]=e);let i;t[5]===e?i=t[6]:(i=[e],t[5]=e,t[6]=i),r=i}let i=r,a;t[7]!==i||t[8]!==n.material?(a=n.material?(0,K.jsx)(Le,{name:`InteriorTexture:${Array.isArray(n.material)?n.material[0]?.userData?.resource_path:n.material?.userData?.resource_path??`?`}`,fallback:(0,K.jsx)(`meshStandardMaterial`,{color:`yellow`,wireframe:!0}),children:Array.isArray(n.material)?n.material.map((e,t)=>(0,K.jsx)(Vt,{materialName:e.userData.resource_path,material:e,lightMap:i[t]},t)):(0,K.jsx)(Vt,{materialName:n.material.userData.resource_path,material:n.material,lightMap:i[0]})}):null,t[7]=i,t[8]=n.material,t[9]=a):a=t[9];let o;return t[10]!==n.geometry||t[11]!==a?(o=(0,K.jsx)(`mesh`,{geometry:n.geometry,castShadow:!0,receiveShadow:!0,children:a}),t[10]=n.geometry,t[11]=a,t[12]=o):o=t[12],o}function Wt(e){return Ht(e)}var Gt=(0,W.memo)(function(e){let t=(0,G.c)(10),{interiorFile:n,ghostIndex:r}=e,{nodes:i}=Bt(n),a=l()?.debugMode??!1,o;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(o=[0,-Math.PI/2,0],t[0]=o):o=t[0];let s;t[1]===i?s=t[2]:(s=Object.entries(i).filter(Yt).map(Xt),t[1]=i,t[2]=s);let c;t[3]!==a||t[4]!==r||t[5]!==n?(c=a?(0,K.jsxs)(Ie,{children:[r,`: `,n]}):null,t[3]=a,t[4]=r,t[5]=n,t[6]=c):c=t[6];let u;return t[7]!==s||t[8]!==c?(u=(0,K.jsxs)(`group`,{rotation:o,children:[s,c]}),t[7]=s,t[8]=c,t[9]=u):u=t[9],u});function Kt(e){let t=(0,G.c)(9),{color:n,label:r}=e,i;t[0]===Symbol.for(`react.memo_cache_sentinel`)?(i=(0,K.jsx)(`boxGeometry`,{args:[10,10,10]}),t[0]=i):i=t[0];let a;t[1]===n?a=t[2]:(a=(0,K.jsx)(`meshStandardMaterial`,{color:n,wireframe:!0}),t[1]=n,t[2]=a);let o;t[3]!==n||t[4]!==r?(o=r?(0,K.jsx)(Ie,{color:n,children:r}):null,t[3]=n,t[4]=r,t[5]=o):o=t[5];let s;return t[6]!==a||t[7]!==o?(s=(0,K.jsxs)(`mesh`,{children:[i,a,o]}),t[6]=a,t[7]=o,t[8]=s):s=t[8],s}function qt(e){let t=(0,G.c)(3),{label:n}=e,r=l()?.debugMode??!1,i;return t[0]!==r||t[1]!==n?(i=r?(0,K.jsx)(Kt,{color:`red`,label:n}):null,t[0]=r,t[1]=n,t[2]=i):i=t[2],i}var Jt=(0,W.memo)(function(e){let t=(0,G.c)(26),{scene:n}=e,r;t[0]===n.transform.position?r=t[1]:(r=de(n.transform.position),t[0]=n.transform.position,t[1]=r);let i=r,a;t[2]===n.transform?a=t[3]:(a=fe(n.transform),t[2]=n.transform,t[3]=a);let o=a,s;t[4]===n.scale?s=t[5]:(s=ue(n.scale),t[4]=n.scale,t[5]=s);let c=s,l=`${n.ghostIndex}: ${n.interiorFile}`,u;t[6]===l?u=t[7]:(u=(0,K.jsx)(qt,{label:l}),t[6]=l,t[7]=u);let f;t[8]===n.interiorFile?f=t[9]:(f=e=>{zt.error(`Failed to load %s: %s`,n.interiorFile,e.message)},t[8]=n.interiorFile,t[9]=f);let p=`InteriorModel:${n.interiorFile}`,m;t[10]===Symbol.for(`react.memo_cache_sentinel`)?(m=(0,K.jsx)(Kt,{color:`orange`}),t[10]=m):m=t[10];let h;t[11]!==n.ghostIndex||t[12]!==n.interiorFile?(h=(0,K.jsx)(Gt,{interiorFile:n.interiorFile,ghostIndex:n.ghostIndex}),t[11]=n.ghostIndex,t[12]=n.interior
|
|||
|
|
attribute float alpha;
|
|||
|
|
|
|||
|
|
uniform vec2 uvOffset;
|
|||
|
|
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
varying float vAlpha;
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
// Apply UV offset for scrolling
|
|||
|
|
vUv = uv + uvOffset;
|
|||
|
|
vAlpha = alpha;
|
|||
|
|
|
|||
|
|
vec4 pos = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|||
|
|
// Set depth to far plane so clouds are always visible and behind other geometry
|
|||
|
|
gl_Position = pos.xyww;
|
|||
|
|
}
|
|||
|
|
`,sn=`
|
|||
|
|
uniform sampler2D cloudTexture;
|
|||
|
|
uniform float debugMode;
|
|||
|
|
uniform int layerIndex;
|
|||
|
|
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
varying float vAlpha;
|
|||
|
|
|
|||
|
|
// Debug grid using screen-space derivatives for sharp, anti-aliased lines
|
|||
|
|
float debugGrid(vec2 uv, float gridSize, float lineWidth) {
|
|||
|
|
vec2 scaledUV = uv * gridSize;
|
|||
|
|
vec2 grid = abs(fract(scaledUV - 0.5) - 0.5) / fwidth(scaledUV);
|
|||
|
|
float line = min(grid.x, grid.y);
|
|||
|
|
return 1.0 - min(line / lineWidth, 1.0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
vec4 texColor = texture2D(cloudTexture, vUv);
|
|||
|
|
|
|||
|
|
// Tribes 2 uses GL_MODULATE: final = texture × vertex color
|
|||
|
|
// Vertex color is white with varying alpha, so:
|
|||
|
|
// Final RGB = Texture RGB × 1.0 = Texture RGB
|
|||
|
|
// Final Alpha = Texture Alpha × Vertex Alpha
|
|||
|
|
float finalAlpha = texColor.a * vAlpha;
|
|||
|
|
vec3 color = texColor.rgb;
|
|||
|
|
|
|||
|
|
// Debug mode: overlay R/G/B grid for layers 0/1/2
|
|||
|
|
if (debugMode > 0.5) {
|
|||
|
|
float gridIntensity = debugGrid(vUv, 4.0, 1.5);
|
|||
|
|
vec3 gridColor;
|
|||
|
|
if (layerIndex == 0) {
|
|||
|
|
gridColor = vec3(1.0, 0.0, 0.0); // Red
|
|||
|
|
} else if (layerIndex == 1) {
|
|||
|
|
gridColor = vec3(0.0, 1.0, 0.0); // Green
|
|||
|
|
} else {
|
|||
|
|
gridColor = vec3(0.0, 0.0, 1.0); // Blue
|
|||
|
|
}
|
|||
|
|
color = mix(color, gridColor, gridIntensity * 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Output clouds with texture color and combined alpha
|
|||
|
|
gl_FragColor = vec4(color, finalAlpha);
|
|||
|
|
}
|
|||
|
|
`;function cn({textureUrl:e,radius:t,heightPercent:n,speed:r,windDirection:i,layerIndex:o}){let{debugMode:s}=l(),{animationEnabled:u}=c(),d=(0,W.useRef)(null),f=_e(e,an),p=(0,W.useMemo)(()=>nn(t,n,n-.05,$t),[t,n]);(0,W.useEffect)(()=>()=>{p.dispose()},[p]);let m=(0,W.useMemo)(()=>new D({uniforms:{cloudTexture:{value:f},uvOffset:{value:new N(0,0)},debugMode:{value:s?1:0},layerIndex:{value:o}},vertexShader:on,fragmentShader:sn,transparent:!0,depthWrite:!1,side:2}),[f,s,o]);return(0,W.useEffect)(()=>()=>{m.dispose()},[m]),a(u?(e,t)=>{let n=t*1e3/32;d.current??=new N(0,0),d.current.x+=i.x*r*n,d.current.y+=i.y*r*n,d.current.x-=Math.floor(d.current.x),d.current.y-=Math.floor(d.current.y),m.uniforms.uvOffset.value.copy(d.current)}:Zt),(0,K.jsx)(`mesh`,{geometry:p,frustumCulled:!1,renderOrder:10,children:(0,K.jsx)(`primitive`,{object:m,attach:`material`})})}var ln=7;function un(e){let t=(0,G.c)(7),n,r;t[0]===e?(n=t[1],r=t[2]):(n=[`detailMapList`,e],r=()=>Me(e),t[0]=e,t[1]=n,t[2]=r);let i=!!e,a;return t[3]!==n||t[4]!==r||t[5]!==i?(a={queryKey:n,queryFn:r,enabled:i},t[3]=n,t[4]=r,t[5]=i,t[6]=a):a=t[6],u(a)}function dn(e){let t=(0,G.c)(18),{scene:n}=e,{data:r}=un(n.materialList||void 0),i=(n.visibleDistance>0?n.visibleDistance:500)*.95,o;t[0]===n.cloudLayers?o=t[1]:(o=n.cloudLayers.map(pn),t[0]=n.cloudLayers,t[1]=o);let s=o,c;t[2]===n.cloudLayers?c=t[3]:(c=n.cloudLayers.map(fn),t[2]=n.cloudLayers,t[3]=c);let l=c,u;bb0:{let{x:e,y:r}=n.windVelocity;if(e!==0||r!==0){let n;t[4]!==e||t[5]!==r?(n=new N(r,-e).normalize(),t[4]=e,t[5]=r,t[6]=n):n=t[6],u=n;break bb0}let i;t[7]===Symbol.for(`react.memo_cache_sentinel`)?(i=new N(1,0),t[7]=i):i=t[7],u=i}let d=u,f;bb1:{if(!r){let e;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(e=[],t[8]=e):e=t[8],f=e;break bb1}let e;if(t[9]!==l||t[10]!==s||t[11]!==r){e=[];for(let t=0;t<3;t++){let n=r[ln+t];n&&e.push({texture:n,height:l[t],speed:s[t]})}t[9]=l,t[10]=s,t[11]=r,t[12]=e}else e=t[12];f=e}let p=f,m=(0,W.useRef)(null),h;if(t[13]===Symbol.for(`react.memo_cache_sentinel`)?(h=e=>{let{camera:t}=e;m.current&&m.current.position.copy(t.position)},t[13]=h):h=t[13],a(h),!p||p.length===0)return null;let g;return t[14]!==p||t[15]!==i||t[16]!==d?(g=(0,K.jsx)(`group`,{ref:m,children:p.map((e,t)=>(0,K.jsx)(W.Suspense,{children:(0,K.jsx)(cn,{textureUrl:U(e.texture),radius:i,heightPercent:e.height,speed:e.speed,windDirection:d,layerIndex:t})},t))}),t[14]=p,t[15]=i,t[16]=d,t[17]=g):g=t[17],g}function fn(e,t){return e.heightPercent||[.35,.25,.2][t]}function pn(e,t){return e.speed||[1e-4,2e-4,3e-4][t]}(0,W.createContext)(null),(0,W.createContext)(null);function mn(e){let t=e.fogDistance,n=e.visibleDistance>0?e.visibleDistance:1e3,{r,g:i,b:a}=e.fogColor,o=new B().setRGB(r,i,a).convertSRGBToLinear(),s=[];for(let t of e.fogVolumes)t.visibleDistance<=0||t.maxHeight<=t.minHeight||s.push({visibleDistance:t.visibleDistance,minHeight:t.minHeight,maxHeight:t.maxHeight,percentage:1});return{fogDistance:t,visibleDistance:n,fogColor:o,fogVolumes:s,fogLine:s.reduce((e,t)=>Math.max(e,t.maxHeight),0),enabled:n>t}}var hn=C(`Sky`),gn=!1;function _n(e){return[new B().setRGB(e.r,e.g,e.b),new B().setRGB(e.r,e.g,e.b).convertSRGBToLinear()]}function vn(e){let t=(0,G.c)(8),n;t[0]===e?n=t[1]:(n={queryKey:[`detailMapList`,e],queryFn:()=>(hn.debug(`Loading detail map list: %s`,e),Me(e))},t[0]=e,t[1]=n);let r=u(n),i,a;return t[2]!==e||t[3]!==r.data||t[4]!==r.error||t[5]!==r.status?(i=()=>{hn.debug(`DML query status: %s%s%s file=%s`,r.status,r.error?` error=${r.error.message}`:``,r.data?` (${r.data.length} entries)`:` (no data)`,e)},a=[r.status,r.error,r.data,e],t[2]=e,t[3]=r.data,t[4]=r.error,t[5]=r.status,t[6]=i,t[7]=a):(i=t[6],a=t[7]),(0,W.useEffect)(i,a),r}var yn=60;function bn({skyBoxFiles:e,fogColor:t,fogState:n}){let r=o(e=>e.camera),i=$e(e,{path:``}),a=!!t,s=(0,W.useMemo)(()=>r.projectionMatrixInverse,[r]),c=(0,W.useMemo)(()=>n?We(n.fogVolumes):new Float32Array(12),[n]),l=(0,W.useRef)({skybox:{value:i},fogColor:{value:t??new B(0,0,0)},enableFog:{value:a},inverseProjectionMatrix:{value:s},cameraMatrixWorld:{value:r.matrixWorld},cameraHeigh
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
vUv = uv;
|
|||
|
|
gl_Position = vec4(position.xy, 0.9999, 1.0);
|
|||
|
|
}
|
|||
|
|
`,fragmentShader:`
|
|||
|
|
uniform samplerCube skybox;
|
|||
|
|
uniform vec3 fogColor;
|
|||
|
|
uniform bool enableFog;
|
|||
|
|
uniform mat4 inverseProjectionMatrix;
|
|||
|
|
uniform mat4 cameraMatrixWorld;
|
|||
|
|
uniform float cameraHeight;
|
|||
|
|
uniform float fogVolumeData[12];
|
|||
|
|
uniform float horizonFogHeight;
|
|||
|
|
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
|
|||
|
|
// Convert linear to sRGB for display
|
|||
|
|
// shaderMaterial does NOT get automatic linear->sRGB output conversion
|
|||
|
|
// Use proper sRGB transfer function (not simplified gamma 2.2) to match Three.js
|
|||
|
|
vec3 linearToSRGB(vec3 linear) {
|
|||
|
|
vec3 low = linear * 12.92;
|
|||
|
|
vec3 high = 1.055 * pow(linear, vec3(1.0 / 2.4)) - 0.055;
|
|||
|
|
return mix(low, high, step(vec3(0.0031308), linear));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
vec2 ndc = vUv * 2.0 - 1.0;
|
|||
|
|
vec4 viewPos = inverseProjectionMatrix * vec4(ndc, 1.0, 1.0);
|
|||
|
|
viewPos.xyz /= viewPos.w;
|
|||
|
|
vec3 direction = normalize((cameraMatrixWorld * vec4(viewPos.xyz, 0.0)).xyz);
|
|||
|
|
direction = vec3(direction.z, direction.y, -direction.x);
|
|||
|
|
// Sample skybox - Three.js CubeTexture with SRGBColorSpace auto-converts to linear
|
|||
|
|
vec4 skyColor = textureCube(skybox, direction);
|
|||
|
|
vec3 finalColor;
|
|||
|
|
|
|||
|
|
if (enableFog) {
|
|||
|
|
vec3 effectiveFogColor = fogColor;
|
|||
|
|
|
|||
|
|
// Calculate how much fog volume the ray passes through
|
|||
|
|
// For skybox at "infinite" distance, the relevant height is how much
|
|||
|
|
// of the volume is above/below camera depending on view direction
|
|||
|
|
float volumeFogInfluence = 0.0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 3; i++) {
|
|||
|
|
int offset = i * 4;
|
|||
|
|
float volVisDist = fogVolumeData[offset + 0];
|
|||
|
|
float volMinH = fogVolumeData[offset + 1];
|
|||
|
|
float volMaxH = fogVolumeData[offset + 2];
|
|||
|
|
float volPct = fogVolumeData[offset + 3];
|
|||
|
|
|
|||
|
|
if (volVisDist <= 0.0) continue;
|
|||
|
|
|
|||
|
|
// Check if camera is inside this volume
|
|||
|
|
if (cameraHeight >= volMinH && cameraHeight <= volMaxH) {
|
|||
|
|
// Camera is inside the fog volume
|
|||
|
|
// Looking horizontally or up at shallow angles means ray travels
|
|||
|
|
// through more fog before exiting the volume
|
|||
|
|
float heightAboveCamera = volMaxH - cameraHeight;
|
|||
|
|
float heightBelowCamera = cameraHeight - volMinH;
|
|||
|
|
float volumeHeight = volMaxH - volMinH;
|
|||
|
|
|
|||
|
|
// For horizontal rays (direction.y ≈ 0), maximum fog influence
|
|||
|
|
// For rays going up steeply, less fog (exits volume quickly)
|
|||
|
|
// For rays going down, more fog (travels through volume below)
|
|||
|
|
float rayInfluence;
|
|||
|
|
if (direction.y >= 0.0) {
|
|||
|
|
// Looking up: influence based on how steep we're looking
|
|||
|
|
// Shallow angles = long path through fog = high influence
|
|||
|
|
rayInfluence = 1.0 - smoothstep(0.0, 0.3, direction.y);
|
|||
|
|
} else {
|
|||
|
|
// Looking down: always high fog (into the volume)
|
|||
|
|
rayInfluence = 1.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Scale by percentage and volume depth factor
|
|||
|
|
volumeFogInfluence += rayInfluence * volPct;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Base fog factor from view direction (for haze at horizon)
|
|||
|
|
// In Torque, the fog "bans" (bands) are rendered as geometry from
|
|||
|
|
// height 0 (HORIZON) to height 60 (OFFSET_HEIGHT) on the skybox.
|
|||
|
|
// The skybox corner is at mSkyBoxPt.x = mRadius / sqrt(3).
|
|||
|
|
//
|
|||
|
|
// horizonFogHeight is the direction.y value where the fog band ends:
|
|||
|
|
// horizonFogHeight = 60 / sqrt(skyBoxPt.x^2 + 60^2)
|
|||
|
|
//
|
|||
|
|
// For Firestorm (visDist=600): mRadius=570, skyBoxPt.x=329, horizonFogHeight≈0.18
|
|||
|
|
//
|
|||
|
|
// Torque renders the fog bands as geometry with linear vertex alpha
|
|||
|
|
// interpolation. We use a squared curve (t^2) to create a gentler
|
|||
|
|
// falloff at the top of the gradient, matching Tribes 2's appearance.
|
|||
|
|
float baseFogFactor;
|
|||
|
|
if (direction.y <= 0.0) {
|
|||
|
|
// Looking at or below horizon: full fog
|
|||
|
|
baseFogFactor = 1.0;
|
|||
|
|
} else if (direction.y >= horizonFogHeight) {
|
|||
|
|
// Above fog band: no fog
|
|||
|
|
baseFogFactor = 0.0;
|
|||
|
|
} else {
|
|||
|
|
// Within fog band: squared curve for gentler falloff at top
|
|||
|
|
float t = direction.y / horizonFogHeight;
|
|||
|
|
baseFogFactor = (1.0 - t) * (1.0 - t);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Combine base fog with volume fog influence
|
|||
|
|
// When inside a volume, increase fog intensity
|
|||
|
|
float finalFogFactor = min(1.0, baseFogFactor + volumeFogInfluence * 0.5);
|
|||
|
|
|
|||
|
|
finalColor = mix(skyColor.rgb, effectiveFogColor, finalFogFactor);
|
|||
|
|
} else {
|
|||
|
|
finalColor = skyColor.rgb;
|
|||
|
|
}
|
|||
|
|
// Convert linear result to sRGB for display
|
|||
|
|
gl_FragColor = vec4(linearToSRGB(finalColor), 1.0);
|
|||
|
|
}
|
|||
|
|
`,depthWrite:!1,depthTest:!1})]})}function xn(e){let t=(0,G.c)(6),{materialList:n,fogColor:r,fogState:i}=e,{data:a}=vn(n),o;t[0]===a?o=t[1]:(o=a?[U(a[1]),U(a[3]),U(a[4]),U(a[5]),U(a[0]),U(a[2])]:null,t[0]=a,t[1]=o);let s=o;if(!s)return null;let c;return t[2]!==r||t[3]!==i||t[4]!==s?(c=(0,K.jsx)(bn,{skyBoxFiles:s,fogColor:r,fogState:i}),t[2]=r,t[3]=i,t[4]=s,t[5]=c):c=t[5],c}function Sn({skyColor:e,fogColor:t,fogState:n}){let r=o(e=>e.camera),i=!!t,a=(0,W.useMemo)(()=>r.projectionMatrixInverse,[r]),s=(0,W.useMemo)(()=>n?We(n.fogVolumes):new Float32Array(12),[n]),c=(0,W.useMemo)(()=>{if(!n)return .18;let e=n.visibleDistance*.95/Math.sqrt(3);return yn/Math.sqrt(e*e+yn*yn)},[n]),l=(0,W.useRef)({skyColor:{value:e},fogColor:{value:t??new B(0,0,0)},enableFog:{value:i},inverseProjectionMatrix:{value:a},cameraMatrixWorld:{value:r.matrixWorld},cameraHeight:Je.cameraHeight,fogVolumeData:{value:s},horizonFogHeight:{value:c}});return(0,W.useEffect)(()=>{l.current.skyColor.value=e,l.current.fogColor.value=t??new B(0,0,0),l.current.enableFog.value=i,l.current.fogVolumeData.value=s,l.current.horizonFogHeight.value=c},[e,t,i,s,c]),(0,K.jsxs)(`mesh`,{renderOrder:-1e3,frustumCulled:!1,children:[(0,K.jsxs)(`bufferGeometry`,{children:[(0,K.jsx)(`bufferAttribute`,{attach:`attributes-position`,args:[new Float32Array([-1,-1,0,3,-1,0,-1,3,0]),3],count:3,itemSize:3}),(0,K.jsx)(`bufferAttribute`,{attach:`attributes-uv`,args:[new Float32Array([0,0,2,0,0,2]),2],count:3,itemSize:2})]}),(0,K.jsx)(`shaderMaterial`,{uniforms:l.current,vertexShader:`
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
vUv = uv;
|
|||
|
|
gl_Position = vec4(position.xy, 0.9999, 1.0);
|
|||
|
|
}
|
|||
|
|
`,fragmentShader:`
|
|||
|
|
uniform vec3 skyColor;
|
|||
|
|
uniform vec3 fogColor;
|
|||
|
|
uniform bool enableFog;
|
|||
|
|
uniform mat4 inverseProjectionMatrix;
|
|||
|
|
uniform mat4 cameraMatrixWorld;
|
|||
|
|
uniform float cameraHeight;
|
|||
|
|
uniform float fogVolumeData[12];
|
|||
|
|
uniform float horizonFogHeight;
|
|||
|
|
|
|||
|
|
varying vec2 vUv;
|
|||
|
|
|
|||
|
|
// Convert linear to sRGB for display
|
|||
|
|
vec3 linearToSRGB(vec3 linear) {
|
|||
|
|
vec3 low = linear * 12.92;
|
|||
|
|
vec3 high = 1.055 * pow(linear, vec3(1.0 / 2.4)) - 0.055;
|
|||
|
|
return mix(low, high, step(vec3(0.0031308), linear));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void main() {
|
|||
|
|
vec2 ndc = vUv * 2.0 - 1.0;
|
|||
|
|
vec4 viewPos = inverseProjectionMatrix * vec4(ndc, 1.0, 1.0);
|
|||
|
|
viewPos.xyz /= viewPos.w;
|
|||
|
|
vec3 direction = normalize((cameraMatrixWorld * vec4(viewPos.xyz, 0.0)).xyz);
|
|||
|
|
direction = vec3(direction.z, direction.y, -direction.x);
|
|||
|
|
|
|||
|
|
vec3 finalColor;
|
|||
|
|
|
|||
|
|
if (enableFog) {
|
|||
|
|
// Calculate volume fog influence (same logic as SkyBoxTexture)
|
|||
|
|
float volumeFogInfluence = 0.0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 3; i++) {
|
|||
|
|
int offset = i * 4;
|
|||
|
|
float volVisDist = fogVolumeData[offset + 0];
|
|||
|
|
float volMinH = fogVolumeData[offset + 1];
|
|||
|
|
float volMaxH = fogVolumeData[offset + 2];
|
|||
|
|
float volPct = fogVolumeData[offset + 3];
|
|||
|
|
|
|||
|
|
if (volVisDist <= 0.0) continue;
|
|||
|
|
|
|||
|
|
if (cameraHeight >= volMinH && cameraHeight <= volMaxH) {
|
|||
|
|
float rayInfluence;
|
|||
|
|
if (direction.y >= 0.0) {
|
|||
|
|
rayInfluence = 1.0 - smoothstep(0.0, 0.3, direction.y);
|
|||
|
|
} else {
|
|||
|
|
rayInfluence = 1.0;
|
|||
|
|
}
|
|||
|
|
volumeFogInfluence += rayInfluence * volPct;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Base fog factor from view direction
|
|||
|
|
float baseFogFactor;
|
|||
|
|
if (direction.y <= 0.0) {
|
|||
|
|
baseFogFactor = 1.0;
|
|||
|
|
} else if (direction.y >= horizonFogHeight) {
|
|||
|
|
baseFogFactor = 0.0;
|
|||
|
|
} else {
|
|||
|
|
float t = direction.y / horizonFogHeight;
|
|||
|
|
baseFogFactor = (1.0 - t) * (1.0 - t);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Combine base fog with volume fog influence
|
|||
|
|
float finalFogFactor = min(1.0, baseFogFactor + volumeFogInfluence * 0.5);
|
|||
|
|
|
|||
|
|
finalColor = mix(skyColor, fogColor, finalFogFactor);
|
|||
|
|
} else {
|
|||
|
|
finalColor = skyColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
gl_FragColor = vec4(linearToSRGB(finalColor), 1.0);
|
|||
|
|
}
|
|||
|
|
`,depthWrite:!1,depthTest:!1})]})}function Cn(e,t){let{fogDistance:n,visibleDistance:r}=e;return[n,r]}function wn({fogState:e,enabled:t}){let n=o(e=>e.scene),r=o(e=>e.camera),i=(0,W.useRef)(null),s=(0,W.useMemo)(()=>We(e.fogVolumes),[e.fogVolumes]);return(0,W.useEffect)(()=>{gn||=(qe(),!0)},[]),(0,W.useEffect)(()=>{Ke();let[t,a]=Cn(e,r.position.y),o=new E(e.fogColor,t,a);return n.fog=o,i.current=o,Ue(r.position.y,s),()=>{n.fog=null,i.current=null,Ke()}},[n,r,e,s]),(0,W.useEffect)(()=>{let n=i.current;if(n)if(t){let[t,i]=Cn(e,r.position.y);n.near=t,n.far=i}else n.near=1e10,n.far=1e10},[t,e,r.position.y]),a(()=>{let n=i.current;if(!n)return;let a=r.position.y;if(Ue(a,s,t),t){let[t,r]=Cn(e,a);n.near=t,n.far=r,n.color.copy(e.fogColor)}}),null}var Tn=(0,W.memo)(function({entity:e}){let{skyData:t}=e;hn.debug(`Rendering: materialList=%s, useSkyTextures=%s`,t.materialList,t.useSkyTextures);let{fogEnabled:n}=c(),r=t.materialList||void 0,i=(0,W.useMemo)(()=>_n(t.skySolidColor),[t.skySolidColor]),a=t.useSkyTextures,s=(0,W.useMemo)(()=>mn(t),[t]);hn.debug(`fogState: fogColor=(%s, %s, %s) visibleDistance=%d fogDistance=%d enabled=%s volumes=%d`,t.fogColor.r.toFixed(3),t.fogColor.g.toFixed(3),t.fogColor.b.toFixed(3),t.visibleDistance,t.fogDistance,s.enabled,s.fogVolumes.length);let l=(0,W.useMemo)(()=>_n(t.fogColor),[t.fogColor]),u=i||l,d=s.enabled&&n,f=s.fogColor,p=o(e=>e.scene),m=o(e=>e.gl);(0,W.useEffect)(()=>{if(d){let e=f.clone();p.background=e,m.setClearColor(e)}else if(u){let e=u[0].clone();p.background=e,m.setClearColor(e)}else p.background=null;return()=>{p.background=null}},[p,m,d,f,u]);let h=i?.[1];return(0,K.jsxs)(K.Fragment,{children:[r&&a&&r.length>0?(0,K.jsx)(W.Suspense,{children:(0,K.jsx)(xn,{materialList:r,fogColor:d?f:void 0,fogState:d?s:void 0},r)}):h?(0,K.jsx)(Sn,{skyColor:h,fogColor:d?f:void 0,fogState:d?s:void 0}):null,(0,K.jsx)(W.Suspense,{children:(0,K.jsx)(dn,{scene:t})}),s.enabled?(0,K.jsx)(wn,{fogState:s,enabled:n}):null]})});function En(e){let t=(0,G.c)(3),{children:n}=e,{audioEnabled:r}=c(),i;return t[0]!==r||t[1]!==n?(i=r?(0,K.jsx)(W.Suspense,{children:n}):null,t[0]=r,t[1]=n,t[2]=i):i=t[2],i}function Z(e,t){let n=(0,W.lazy)(()=>t().then(t=>({default:t[e]}))),r=t=>{let r=(0,G.c)(5),{entity:i}=t,a=`${e}:${i.id}`,o;r[0]===i?o=r[1]:(o=(0,K.jsx)(n,{entity:i}),r[0]=i,r[1]=o);let s;return r[2]!==a||r[3]!==o?(s=(0,K.jsx)(Le,{name:a,children:o}),r[2]=a,r[3]=o,r[4]=s):s=r[4],s};return r.displayName=`createLazy(${e})`,r}var Dn=Z(`PlayerModel`,()=>H(()=>import(`./PlayerModel-Beu7pkFZ.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]))),On=Z(`ExplosionShape`,()=>H(()=>import(`./ShapeModel-Dfvda7ou.js`),__vite__mapDeps([32,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,31]))),kn=Z(`TracerProjectile`,()=>H(()=>import(`./Projectiles-CMqanslS.js`),__vite__mapDeps([33,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]))),An=Z(`SpriteProjectile`,()=>H(()=>import(`./Projectiles-CMqanslS.js`),__vite__mapDeps([33,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]))),jn=Z(`ForceFieldBare`,()=>H(()=>import(`./ForceFieldBare-BNF6ljSM.js`),__vite__mapDeps([34,1,2,3,5,6,7,8,9,14,15,16,29,26]))),Mn=Z(`AudioEmitter`,()=>H(()=>import(`./AudioEmitter-DQcaN3l9.js`),__vite__mapDeps([28,1,2,3,6,7,8,9,14,15,16,18,27,21,22,11,12,13,17,19,20,23,24,26]))),Nn=Z(`WaterBlock`,()=>H(()=>import(`./WaterBlock-cCfl8B1Q.js`),__vite__mapDeps([35,1,2,3,23,5,6,7,8,9,14,15,16,25,26,19]))),Pn=Z(`WeaponModel`,()=>H(()=>import(`./ShapeModel-Dfvda7ou.js`),__vite__mapDeps([32,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,31]))),Fn=(0,W.memo)(function(e){let t=(0,G.c)(26),{entity:n}=e;switch(n.renderType){case`Shape`:{let e;return t[0]===n?e=t[1]:(e=(0,K.jsx)(In,{entity:n}),t[0]=n,t[1]=e),e}case`ForceFieldBare`:{let e;return t[2]===n?e=t[3]:(e=(0,K.jsx)(jn,{entity:n}),t[2]=n,t[3]=e),e}case`Player`:{let e;return t[4]===n?e=t[5]:(e=(0,K.jsx)(Dn,{entity:n}),t[4]=n,t[5]=e),e}case`Explosion`:{let e;return t[6]===n?e=
|