2026-04-08 23:48:42 -07:00
const _ _vite _ _mapDeps = ( i , m = _ _vite _ _mapDeps , d = ( m . f || ( m . f = [ "assets/PlayerModel-8dF8wZN2.js" , "assets/PlayerModel-D6m6HpJF.js" , "assets/chunk-DECur_0Z.js" , "assets/Html-CXAi5FD_.js" , "assets/extends-lXRikpl0.js" , "assets/react-three-fiber.esm-El6vNTZj.js" , "assets/jsx-runtime-BpGWiA-R.js" , "assets/three.module-DKAirPAO.js" , "assets/traditional-CCqNJZlI.js" , "assets/useQuery-6REtM5HO.js" , "assets/SettingsProvider-BdqQ2Cm4.js" , "assets/engineStore-B1KAgiiF.js" , "assets/manifest-BIDT_vSa.js" , "assets/stringUtils-1MyeFdQ_.js" , "assets/logger-B058WGzf.js" , "assets/AudioEmitter-3VHhCc7Y.js" , "assets/DebugBounds-CZKrvsAw.js" , "assets/loaders-5n1D4iOD.js" , "assets/mission-yeigCtfF.js" , "assets/cameraTourStore-CtH3IrnD.js" , "assets/AudioEmitter-DAQByNim.css" , "assets/DebugSuspense-ChOWTvws.js" , "assets/playbackUtils-DuS6opSR.js" , "assets/textureUtils-Bk_jPZib.js" , "assets/useAnisotropy-D9othEmk.js" , "assets/streamPlaybackStore-D5ldcfU5.js" , "assets/PlayerModel-Bi7C0zGW.css" , "assets/ExplosionShape-DH_M7uUx.js" , "assets/Projectiles-CWChdCSv.js" , "assets/Texture-BYh0PjzP.js" , "assets/ForceFieldBare-CIwodqfs.js" , "assets/AudioEmitter-DaFPiGOy.js" , "assets/WaterBlock-DV6neJfD.js" , "assets/scene-C20n9V3Y.js" , "assets/StreamingController-CEWgeQUH.js" , "assets/index-BZ0wFa-D.js" , "assets/preload-helper-BPkniflS.js" , "assets/streamHelpers-CYLk-lCT.js" , "assets/iconBase-DZ3jidsI.js" , "assets/JoystickContext-B2sO9eYx.js" , "assets/index-CiZqoesx.css" , "assets/gameEntityTypes-CIesm-Ll.js" , "assets/DebugElements-CrsrzkRa.js" , "assets/DebugElements-BP0b5jan.css" , "assets/Mission-BRZHUO2H.js" , "assets/misToScene-BfuEJI8y.js" , "assets/ChatSoundPlayer-D3K8GxX-.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 , o as a , s as o , t as s } from "./react-three-fiber.esm-El6vNTZj.js" ; import { t as c } from "./Html-CXAi5FD_.js" ; import { a as l , i as u } from "./SettingsProvider-BdqQ2Cm4.js" ; import { t as d } from "./useQuery-6REtM5HO.js" ; import { A as f , C as p , Ct as m , D as h , Dt as g , Ht as _ , Kt as v , N as y , Ot as b , S as x , Ut as S , Wt as C , _ as w , b as T , f as E , h as D , j as O , jt as k , k as A , kt as j , m as M , q as N , rt as P , ut as F , v as I , w as L } from "./three.module-DKAirPAO.js" ; import { a as ee , d as te , l as ne , o as R , s as re , u as ie } from "./PlayerModel-D6m6HpJF.js" ; import { S as ae , b as oe , o as se , v as ce , x as le } from "./playbackUtils-DuS6opSR.js" ; import { a as z , c as ue , d as de , i as B , o as V , r as fe , s as pe , t as me , u as H } from "./textureUtils-Bk_jPZib.js" ; import { f as he , o as ge , p as U , s as _e , t as ve , u as ye } from "./loaders-5n1D4iOD.js" ; import { t as be } from "./logger-B058WGzf.js" ; import { n as xe } from "./stringUtils-1MyeFdQ_.js" ; import "./mission-yeigCtfF.js" ; import { a as Se } from "./engineStore-B1KAgiiF.js" ; import { t as Ce } from "./extends-lXRikpl0.js" ; import { t as we } from "./Texture-BYh0PjzP.js" ; import { t as W } from "./preload-helper-BPkniflS.js" ; import { t as Te } from "./useAnisotropy-D9othEmk.js" ; import { f as Ee , u as De } from "./AudioEmitter-3VHhCc7Y.js" ; import { n as Oe , r as ke , t as Ae } from "./cameraTourStore-CtH3IrnD.js" ; import { n as je , t as Me } from "./DebugBounds-CZKrvsAw.js" ; import { t as Ne } from "./DebugSuspense-ChOWTvws.js" ; import { n as Pe } from "./streamPlaybackStore-D5ldcfU5.js" ; import { S as Fe , t as G } from "./streamHelpers-CYLk-lCT.js" ; import { n as Ie , r as Le , t as Re } from "./scene-C20n9V3Y.js" ; import { A as ze , D as Be , F as Ve , I as He , M as Ue , N as We , R as Ge , _ as Ke , a as qe , c as Je , g as Ye , i as Xe , j as Ze , k as Qe , l as $e , m as et , n as tt , o as nt , p as rt , r as it , s as at , t as ot , u as st , z as ct } from "./index-BZ0wFa-D.js" ; import { t as lt } from "./gameEntityTypes-CIesm-Ll.js" ; var K = e ( n ( ) ) ; function ut ( 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 , p = u ? ? c . depthBuffer , m = K . useMemo ( ( ) => { let e = new v ( a , s , { minFilter : N , magFilter : N , type : y , ... d } ) ; return p && ( e . depthTexture = new L ( a , s , f ) ) , e . samples = l , e } , [ ] ) ; return K . useLayoutEffect ( ( ) => { m . setSize ( a , s ) , l && ( m . samples = l ) } , [ l , m , a , s ] ) , K . useEffect ( ( ) => ( ) => m . dispose ( ) , [ ] ) , m } var dt = e => typeof e == ` function ` , ft = K . forwardRef ( ( { envMap : e , resolution : t = 256 , frames : n = 1 / 0 , makeDefault : r , children : a , ... s } , c ) => { let l = o ( ( { set : e } ) => e ) , u = o ( ( { camera : e } ) => e ) , d = o ( ( { size : e } ) => e ) , f = K . useRef ( null ) ; K . useImperativeHandle ( c , ( ) => f . current , [ ] ) ; let p = K . useRef ( null ) , m = ut ( t ) ; K . useLayoutEffect ( ( ) => { s . manual || ( f . current . aspect = d . width / d . height ) } , [ d , s ] ) , K . useLayoutEffect ( ( ) => { f . current . updateProjectionMatrix ( ) } ) ; let h = 0 , g = null , _ = dt ( a ) ; return i ( 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 ++ ) } ) , K . useLayoutEffect ( ( ) => { if ( r ) { let e = u ; return l ( ( ) => ( { camera : f . current } ) ) , ( ) => l ( ( ) => ( { camera : e } ) ) } } , [ f , r , l ] ) , K . createElement ( K . Fragment , null , K . createElement ( ` perspectiveCamera ` , Ce ( { ref : f } , s ) , ! _ && a ) , K . createElement ( ` group ` , { ref : p } , _ && a ( m . texture ) ) ) } ) ; function pt ( e , { path : t } ) { let [ n ] = a ( x , [ e ] , e => e . setPath ( t ) ) ; return n } pt . preload = ( e , { path : t } ) => a . preload ( x , [ e ] , e => e . setPath ( t ) ) ; var q = t ( ) , mt = { sunLightPointsDown : { value : ! 0 } } ; function ht ( e ) { mt . sunLightPointsDown . value = e } var J = r ( ) , gt = be ( ` SceneLighting ` ) ; function _t ( ) { let e = ( 0 , q . c ) ( 6 ) , t = He ( ) , n , r ; if ( e [ 0 ] === t ? ( n = e [ 1 ] , r = e [ 2 ] ) : ( n = ( ) => { t ? gt . 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 ) ) : gt . debug ( ` No sunData — using fallback ambient #888 ` ) } , r = [ t ] , e [ 0 ] = t , e [ 1 ] = n , e [ 2 ] = r ) , ( 0 , K . useEffect ) ( n , r ) , ! t ) { let t ; return e [ 3 ] === Symbol . for ( ` react.memo_cache_sentinel ` ) ? ( t = ( 0 ,
2026-04-08 23:00:53 -07:00
vec3 torqueLinearToSRGB ( vec3 linear ) {
2026-04-05 22:34:39 -07:00
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 ) ) ;
}
2026-04-08 23:00:53 -07:00
vec3 torqueSRGBToLinear ( vec3 srgb ) {
2026-04-05 22:34:39 -07:00
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 ) ) ;
}
2026-04-08 23:00:53 -07:00
` ,Nt= `
float torqueDebugGrid ( vec2 uv , float gridSize , float lineWidth ) {
2026-04-05 22:34:39 -07:00
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 ) ;
}
2026-04-08 23:00:53 -07:00
` ,Pt=256,Ft=512,It=64,Lt=150;function Rt({shader:e,baseTextures:t,alphaTextures:n,visibilityMask:r,tiling:i,detailTexture:a=null,lightmap:o=null}){e.uniforms.sunLightPointsDown=mt.sunLightPointsDown;let s=t.length;t.forEach((t,n)=>{e.uniforms[ ` albedo$ { n } ` ]={value:t}});let c=n.length;if(n.forEach((t,n)=>{e.uniforms[ ` maskPacked$ { 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:It},e.uniforms.detailFadeDistance={value:Lt},e.vertexShader=e.vertexShader.replace( ` # include < common > ` , ` # include < common >
2026-04-05 22:34:39 -07:00
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.vertexShader=e.vertexShader.replace( ` # include < common > ` , ` # include < common >
varying vec2 vTerrainUv ; ` ),e.vertexShader=e.vertexShader.replace( ` # include < uv _vertex > ` , ` # include < uv _vertex >
vTerrainUv = uv ; ` ),e.fragmentShader= `
varying vec2 vTerrainUv ;
$ { Array . from ( { length : s } , ( e , t ) => ` uniform sampler2D albedo ${ t } ; ` ) . join ( `
` )}
$ { Array . from ( { length : c } , ( e , t ) => ` uniform sampler2D maskPacked ${ t } ; ` ) . join ( `
` )}
$ { Array . from ( { length : s } , ( e , t ) => ` uniform float tiling ${ t } ; ` ) . join ( `
` )}
$ { r ? ` uniform sampler2D visibilityMask; ` : ` ` }
$ { o ? ` uniform sampler2D terrainLightmap; ` : ` ` }
uniform bool sunLightPointsDown ;
$ { a ? ` uniform sampler2D detailTexture;
uniform float detailTiling ;
uniform float detailFadeDistance ;
varying vec3 vTerrainWorldPos ; ` : ` ` }
2026-04-08 23:00:53 -07:00
$ { Mt }
$ { Nt }
2026-04-05 22:34:39 -07:00
// 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 , vTerrainUv ) . 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 = vTerrainUv ;
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 from packed RGB textures (3 masks per texture).
// 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.
2026-04-08 23:00:53 -07:00
vec2 alphaUv = baseUv + vec2 ( 0.5 / $ { Pt } . 0 ) ;
2026-04-05 22:34:39 -07:00
vec3 maskRGB0 = texture2D ( maskPacked0 , alphaUv ) . rgb ;
float a0 = maskRGB0 . r ;
$ { s > 1 ? ` float a1 = maskRGB0.g; ` : ` ` }
$ { s > 2 ? ` float a2 = maskRGB0.b; ` : ` ` }
$ { s > 3 ? ` vec3 maskRGB1 = texture2D(maskPacked1, alphaUv).rgb;
float a3 = maskRGB1 . r ; ` : ` ` }
$ { s > 4 ? ` float a4 = maskRGB1.g; ` : ` ` }
$ { s > 5 ? ` float a5 = maskRGB1.b; ` : ` ` }
// 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
2026-04-08 23:00:53 -07:00
` ),e.fragmentShader=e.fragmentShader.replace( ` # include < lights _fragment _begin > ` , ` vec3 terrainPreLightDirect = reflectedLight . directDiffuse ;
# include < lights _fragment _begin >
2026-04-05 22:34:39 -07:00
// 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 >
2026-04-08 23:00:53 -07:00
// Extract dynamic point/spot light contribution by subtracting what was
// there before lights ran. directDiffuse now has sun + point lights;
// terrainPreLightDirect was 0, so the difference is all lights.
// We'll subtract the sun part below and keep just the point/spot part.
vec3 terrainAllLightsLinear = reflectedLight . directDiffuse - terrainPreLightDirect ;
// Clear Three.js lighting - we compute sun/ambient in gamma space
2026-04-05 22:34:39 -07:00
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)
2026-04-08 23:00:53 -07:00
vec3 textureSRGB = torqueLinearToSRGB ( diffuseColor . rgb ) ;
2026-04-05 22:34:39 -07:00
$ { o ? `
// Sample terrain lightmap for smooth NdotL
2026-04-08 23:00:53 -07:00
vec2 lightmapUv = vTerrainUv + vec2 ( 0.5 / $ { Ft } . 0 ) ;
2026-04-05 22:34:39 -07:00
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
2026-04-08 23:00:53 -07:00
outgoingLight = torqueSRGBToLinear ( resultSRGB ) + totalEmissiveRadiance ;
// Add dynamic point/spot light contributions when present.
// terrainAllLightsLinear includes both directional + point from Three.js.
// We only add it when point/spot lights exist to avoid double-counting
// the sun (already computed in gamma space above). The slight sun
// double-count when points are active is acceptable — point light
// intensity dominates near the source.
# if ( NUM _POINT _LIGHTS > 0 || NUM _SPOT _LIGHTS > 0 )
outgoingLight += terrainAllLightsLinear ;
# endif
2026-04-05 22:34:39 -07:00
}
# include < opaque _fragment > ` ),e.fragmentShader=e.fragmentShader.replace( ` # include < tonemapping _fragment > ` , ` # if DEBUG _MODE
// Debug mode: overlay green grid matching terrain grid squares (256x256)
2026-04-08 23:00:53 -07:00
float gridIntensity = torqueDebugGrid ( vTerrainUv , 256.0 , 1.5 ) ;
2026-04-05 22:34:39 -07:00
vec3 gridColor = vec3 ( 0.0 , 0.8 , 0.4 ) ; // Green
gl _FragColor . rgb = mix ( gl _FragColor . rgb , gridColor , gridIntensity * 0.1 ) ;
# endif
2026-04-08 23:00:53 -07:00
# include < tonemapping _fragment > ` )}var zt={0:32,1:32,2:32,3:32,4:32,5:32},Bt=(0,K.memo)(function({displacementMap:e,visibilityMask:t,textureNames:n,alphaTextures:r,detailTextureName:i,lightmap:a}){let{debugMode:o}=u(),s=Te(),c=we(n.map(e=>he(e)),e=>{e.forEach(e=>B(e,{anisotropy:s}))}),l=i?U(i):null,d=we(l??ve,e=>{B(e,{anisotropy:s})}),f=(0,K.useCallback)(e=>{Rt({shader:e,baseTextures:c,alphaTextures:r,visibilityMask:t,tiling:zt,detailTexture:l?d:null,lightmap:a}),H(e,z)},[c,r,t,d,l,a]),p=(0,K.useMemo)(()=>[n.join( ` , ` ),l?? ` none ` ,a?a.id: ` nolm ` ,c.map(e=>e.id).join( ` , ` )].join( ` | ` ),[n,l,a,c]),m=(0,K.useRef)(null);return(0,K.useEffect)(()=>{let e=m.current;e&&(e.defines??={},e.defines.DEBUG_MODE=o?1:0,e.needsUpdate=!0)},[o]),(0,K.useEffect)(()=>{let e=m.current;e&&(e.customProgramCacheKey=()=>p,e.needsUpdate=!0)},[p]),(0,J.jsx)( ` meshLambertMaterial ` ,{ref:m,depthWrite:!0,side:0,defines:{DEBUG_MODE:o?1:0},onBeforeCompile:f}, ` $ { l ? ` detail ` : ` nodetail ` } - $ { a ? ` lightmap ` : ` nolightmap ` } ` )}),Vt=(0,K.memo)(function(e){let t=(0,q.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,J.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,J.jsx)(K.Suspense,{fallback:c,children:(0,J.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}),Ht=(0,K.memo)(function(e){let t=(0,q.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,J.jsx)(Vt,{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,J.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}),Ut=be( ` TerrainBlock ` ),Wt=8,Gt=600,Y=256,Kt=512,X=2048;function qt(e,t){let n=new D,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 M(c,1)),n.setAttribute( ` position ` ,new A(i,3)),n.setAttribute( ` normal ` ,new A(a,3)),n.setAttribute( ` uv ` ,new A(o,2)),n.rotateX(-Math.PI/2),n.rotateY(-Math.PI/2),n}function Jt(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(Y-1,e)),n=Math.max(0,Math.min(Y-1,n)),t[n*Y+e]/65535*X),d=(e,n)=>{e=Math.max(0,Math.min(Y-1,e)),n=Math.max(0,Math.min(Y-1,n));let r=Math.floor(e),i=Math.floor(n),a=Math.min(r+1,Y-1),o=Math.min(i+1,Y-1),s=e-r,c=n-i,l=t[i*Y+r]/65535*X,u=t[i*Y+a]/65535*X,d=t[o*Y+r]/65535*X,f=t[o*Y+a]/65535*X,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*Y)&Y-1,Math.floor(r*Y)&Y-1);o[e*3+1]=i;let a=t*(Y-1),l=r*(Y-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 Yt(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,_=Y*3;for(let e=0;e<_;e++){if(m+=d,h+=f,g+=p,m<0||m>=Y||h
$ { Mt }
$ { Nt }
2026-04-05 22:34:39 -07:00
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)
2026-04-08 23:00:53 -07:00
vec3 textureSRGB = torqueLinearToSRGB ( diffuseColor . rgb ) ;
// Save Three.js computed direct lighting (includes sun + point/spot lights).
// We'll add it back for point/spot light contribution after our gamma-space calc.
vec3 interiorAllLightsLinear = reflectedLight . directDiffuse ;
2026-04-05 22:34:39 -07:00
// 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
2026-04-08 23:00:53 -07:00
lightingSRGB += torqueLinearToSRGB ( lightMapTexel . rgb ) ;
2026-04-05 22:34:39 -07:00
# 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
2026-04-08 23:00:53 -07:00
vec3 resultLinear = torqueSRGBToLinear ( resultSRGB ) ;
2026-04-05 22:34:39 -07:00
// Reassign outgoingLight before opaque_fragment consumes it
2026-04-08 23:00:53 -07:00
// Add dynamic point/spot lights when present (avoid sun double-count otherwise)
2026-04-05 22:34:39 -07:00
outgoingLight = resultLinear + totalEmissiveRadiance ;
2026-04-08 23:00:53 -07:00
# if ( NUM _POINT _LIGHTS > 0 || NUM _SPOT _LIGHTS > 0 )
outgoingLight += interiorAllLightsLinear ;
# endif
2026-04-05 22:34:39 -07:00
# 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
2026-04-08 23:00:53 -07:00
float gridIntensity = torqueDebugGrid ( vMapUv , 4.0 , 1.5 ) ;
2026-04-05 22:34:39 -07:00
gl _FragColor . rgb = mix ( gl _FragColor . rgb , interiorDebugColor , gridIntensity * 0.1 ) ;
# endif
# include < tonemapping _fragment > ` )}var on=be( ` InteriorInstance ` );function sn(e){let t=(0,q.c)(2),n;return t[0]===e?n=t[1]:(n=ge(e),t[0]=e,t[1]=n),ie(n)}function cn({materialName:e,material:t,lightMap:n}){let r=u()?.debugMode??!1,i=Te(),a=we(U(e),e=>B(e,{anisotropy:i})),o=new Set(t?.userData?.flag_names??[]).has( ` SelfIlluminating ` ),s=new Set(t?.userData?.surface_flag_names??[]).has( ` SurfaceOutsideVisible ` ),c=(0,K.useCallback)(e=>{H(e,z),an(e,{surfaceOutsideVisible:s})},[s]),l=(0,K.useRef)(null),d=(0,K.useRef)(null);(0,K.useEffect)(()=>{let e=l.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,J.jsx)( ` meshBasicMaterial ` ,{ref:l,map:a,toneMapped:!1,defines:f,onBeforeCompile:c},p):(0,J.jsx)( ` meshLambertMaterial ` ,{ref:d,map:a,lightMap:n,toneMapped:!1,defines:f,onBeforeCompile:c},p)}function ln(e){if(!e)return null;let t=e.emissiveMap;return t&&(t.colorSpace=j),t??null}function un(e){let t=(0,q.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(dn),t[1]=n.material,t[2]=e),r=e;break bb0}let e;t[3]===n.material?e=t[4]:(e=ln(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,J.jsx)(Ne,{name: ` InteriorTexture : $ { Array . isArray ( n . material ) ? n . material [ 0 ] ? . userData ? . resource _path : n . material ? . userData ? . resource _path ? ? ` ? ` } ` ,fallback:(0,J.jsx)( ` meshStandardMaterial ` ,{color: ` yellow ` ,wireframe:!0}),children:Array.isArray(n.material)?n.material.map((e,t)=>(0,J.jsx)(cn,{materialName:e.userData.resource_path,material:e,lightMap:i[t]},t)):(0,J.jsx)(cn,{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,J.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 dn(e){return ln(e)}var fn=(0,K.memo)(function(e){let t=(0,q.c)(27),{interiorFile:n,ghostIndex:r,isTarget:i}=e,a=sn(n),{nodes:o}=a,s=u()?.debugMode??!1,c;bb0:{if(!i){c=null;break bb0}let e,n;if(t[0]!==a.scene){let r=new E().setFromObject(a.scene);e=new C,n=new C,r.getCenter(e),r.getSize(n),t[0]=a.scene,t[1]=e,t[2]=n}else e=t[1],n=t[2];let r;t[3]!==e.x||t[4]!==e.y||t[5]!==e.z?(r=[e.x,e.y,e.z],t[3]=e.x,t[4]=e.y,t[5]=e.z,t[6]=r):r=t[6];let o=r,s;t[7]!==n.x||t[8]!==n.y||t[9]!==n.z?(s=[n.x,n.y,n.z],t[7]=n.x,t[8]=n.y,t[9]=n.z,t[10]=s):s=t[10];let l=s,u;t[11]!==o||t[12]!==l?(u={center:o,size:l},t[11]=o,t[12]=l,t[13]=u):u=t[13],c=u}let l=c,d;t[14]===Symbol.for( ` react . memo _cache _sentinel ` )?(d=[0,-Math.PI/2,0],t[14]=d):d=t[14];let f;t[15]===o?f=t[16]:(f=Object.entries(o).filter(gn).map(_n),t[15]=o,t[16]=f);let p;t[17]!==s||t[18]!==r||t[19]!==n?(p=s?(0,J.jsxs)(Ee,{children:[r, ` : ` ,n]}):null,t[17]=s,t[18]=r,t[19]=n,t[20]=p):p=t[20];let m;t[21]===l?m=t[22]:(m=l&&(0,J.jsx)( ` group ` ,{position:l.center,children:(0,J.jsx)(Me,{size:l.size})}),t[21]=l,t[22]=m);let h;return t[23]!==f||t[24]!==p||t[25]!==m?(h=(0,J.jsxs)( ` group ` ,{rotation:d,children:[f,p,m]}),t[23]=f,t[24]=p,t[25]=m,t[26]=h):h=t[26],h});function pn(e){let t=(0,q.c)(9),{color:n,label:r}=e,i;t[0]===Symbol.for( ` react . memo _cache _sentinel ` )?(i=(0,J.jsx)( ` boxGeometry ` ,{args:[10,10,10]}),t[0]=i):i=t[0];let a;t[1]===n?a=t[2]:(a=(0,J.jsx)( ` meshStandardMaterial ` ,{color:n,wireframe:!0}),t[1]=n,t[2]=a);let o;t[3]!==n||t[4]!==r?(o=r?(0,J.jsx)(Ee,{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,J.jsxs)( ` mesh ` ,{children:[i,a,o]}),t[6]=a,t[7]=o,t[8]=s):s=t[8],s}function mn(e){let t=(0,q.c)(3),{label:n}=e,r=u()?.debugMode??!1,i;return t[0]!==r||t[1]!==n?(i=r?(0,J.jsx)(pn,{color: ` red ` ,label:n}):null,t[0]=r,t[1]=n,t[2]=i):i=t[2],i}var hn=(0,K.memo)(function(e){let t=(0,q.c)(27),{entity:n}=e,r=n.interiorData,i=ke(n.id),a;t[0]===r.transform.position?a=t[1]:(a=Le(r.transform.position),t[0]=r.transform.posi
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 ;
}
` ,Dn= `
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 On({textureUrl:e,radius:t,heightPercent:n,speed:r,windDirection:a,layerIndex:o}){let{debugMode:s}=u(),{animationEnabled:c}=l(),d=(0,K.useRef)(null),f=we(e,Tn),p=(0,K.useMemo)(()=>Cn(t,n,n-.05,bn),[t,n]);(0,K.useEffect)(()=>()=>{p.dispose()},[p]);let m=(0,K.useMemo)(()=>new k({uniforms:{cloudTexture:{value:f},uvOffset:{value:new S(0,0)},debugMode:{value:s?1:0},layerIndex:{value:o}},vertexShader:En,fragmentShader:Dn,transparent:!0,depthWrite:!1,side:2}),[f,s,o]);return(0,K.useEffect)(()=>()=>{m.dispose()},[m]),i(c?(e,t)=>{let n=t*1e3/32;d.current??=new S(0,0),d.current.x+=a.x*r*n,d.current.y+=a.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)}:vn),(0,J.jsx)( ` mesh ` ,{geometry:p,frustumCulled:!1,renderOrder:10,children:(0,J.jsx)( ` primitive ` ,{object:m,attach: ` material ` })})}var kn=7;function An(e){let t=(0,q.c)(7),n,r;t[0]===e?(n=t[1],r=t[2]):(n=[ ` detailMapList ` ,e],r=()=>_e(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],d(a)}function jn(e){let t=(0,q.c)(18),{scene:n}=e,{data:r}=An(n.materialList||void 0),a=(n.visibleDistance>0?n.visibleDistance:500)*.95,o;t[0]===n.cloudLayers?o=t[1]:(o=n.cloudLayers.map(Nn),t[0]=n.cloudLayers,t[1]=o);let s=o,c;t[2]===n.cloudLayers?c=t[3]:(c=n.cloudLayers.map(Mn),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 S(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 S(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[kn+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,K.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],i(h),!p||p.length===0)return null;let g;return t[14]!==p||t[15]!==a||t[16]!==d?(g=(0,J.jsx)( ` group ` ,{ref:m,children:p.map((e,t)=>(0,J.jsx)(K.Suspense,{children:(0,J.jsx)(On,{textureUrl:U(e.texture),radius:a,heightPercent:e.height,speed:e.speed,windDirection:d,layerIndex:t})},t))}),t[14]=p,t[15]=a,t[16]=d,t[17]=g):g=t[17],g}function Mn(e,t){return e.heightPercent||[.35,.25,.2][t]}function Nn(e,t){return e.speed||[1e-4,2e-4,3e-4][t]}(0,K.createContext)(null),(0,K.createContext)(null);function Pn(e){let t=e.fogDistance,n=e.visibleDistance>0?e.visibleDistance:1e3,{r,g:i,b:a}=e.fogColor,o=new T().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 Fn=be( ` Sky ` ),In=!1;function Ln(e){return[new T().setRGB(e.r,e.g,e.b),new T().setRGB(e.r,e.g,e.b).convertSRGBToLinear()]}function Rn(e){let t=(0,q.c)(8),n;t[0]===e?n=t[1]:(n={queryKey:[ ` detailMapList ` ,e],queryFn:()=>(Fn.debug( ` Loading detail map list : % s ` ,e),_e(e))},t[0]=e,t[1]=n);let r=d(n),i,a;return t[2]!==e||t[3]!==r.data||t[4]!==r.error||t[5]!==r.status?(i=()=>{Fn.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,K.useEffect)(i,a),r}var zn=60;function Bn({skyBoxFiles:e,fogColor:t,fogState:n}){let r=o(e=>e.camera),i=pt(e,{path: ` ` }),a=!!t,s=(0,K.useMemo)(()=>r.projectionMatrixInverse,[r]),c=(0,K.useMemo)(()=>n?V(n.fogVolumes):new Float32Array(12),[n]),l=(0,K.useRef)({skybox:{value:i},fogColor:{value:t??new T(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 Vn(e){let t=(0,q.c)(13),{materialList:n,fogColor:r,fogState:i}=e,{data:a}=Rn(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,c;t[2]===a?.[6]?c=t[3]:(c=()=>{let e=a?.[6];if(!e)return;let t=U(e);if(t===ve)return;let n=me(t,Un);return n.image&&(B(n,{noColorSpace:!0}),le(n)),Hn},t[2]=a?.[6],t[3]=c);let l;t[4]===a?l=t[5]:(l=[a],t[4]=a,t[5]=l),(0,K.useEffect)(c,l);let{debugMode:d}=u(),f,p;if(t[6]===d?(f=t[7],p=t[8]):(f=()=>{ae.shapeEnvMapDebugUV.value=d},p=[d],t[6]=d,t[7]=f,t[8]=p),(0,K.useEffect)(f,p),!s)return null;let m;return t[9]!==r||t[10]!==i||t[11]!==s?(m=(0,J.jsx)(Bn,{skyBoxFiles:s,fogColor:r,fogState:i}),t[9]=r,t[10]=i,t[11]=s,t[12]=m):m=t[12],m}function Hn(){return oe()}function Un(e){B(e,{noColorSpace:!0}),le(e)}function Wn({skyColor:e,fogColor:t,fogState:n}){let r=o(e=>e.camera),i=!!t,a=(0,K.useMemo)(()=>r.projectionMatrixInverse,[r]),s=(0,K.useMemo)(()=>n?V(n.fogVolumes):new Float32Array(12),[n]),c=(0,K.useMemo)(()=>{if(!n)return .18;let e=n.visibleDistance*.95/Math.sqrt(3);return zn/Math.sqrt(e*e+zn*zn)},[n]),l=(0,K.useRef)({skyColor:{value:e},fogColor:{value:t??new T(0,0,0)},enableFog:{value:i},inverseProjectionMatrix:{value:a},cameraMatrixWorld:{value:r.matrixWorld},cameraHeight:z.cameraHeight,fogVolumeData:{value:s},horizonFogHeight:{value:c}});return(0,K.useEffect)(()=>{l.current.skyColor.value=e,l.current.fogColor.value=t??new T(0,0,0),l.current.enableFog.value=i,l.current.fogVolumeData.value=s,l.current.horizonFogHeight.value=c},[e,t,i,s,c]),(0,J.jsxs)( ` mesh ` ,{renderOrder:-1e3,frustumCulled:!1,children:[(0,J.jsxs)( ` bufferGeometry ` ,{children:[(0,J.jsx)( ` bufferAttribute ` ,{attach: ` attributes - position ` ,args:[new Float32Array([-1,-1,0,3,-1,0,-1,3,0]),3],count:3,itemSize:3}),(0,J.jsx)( ` bufferAttribute ` ,{attach: ` attributes - uv ` ,args:[new Float32Array([0,0,2,0,0,2]),2],count:3,itemSize:2})]}),(0,J.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 ) ;
}
2026-04-08 23:48:42 -07:00
` ,depthWrite:!1,depthTest:!1})]})}function Gn(e,t){let{fogDistance:n,visibleDistance:r}=e;return[n,r]}function Kn({fogState:e,enabled:t}){let n=o(e=>e.scene),r=o(e=>e.camera),a=(0,K.useRef)(null),s=(0,K.useMemo)(()=>V(e.fogVolumes),[e.fogVolumes]);return(0,K.useEffect)(()=>{In||=(de(),!0)},[]),(0,K.useEffect)(()=>{pe();let[t,i]=Gn(e,r.position.y),o=new O(e.fogColor,t,i);return n.fog=o,a.current=o,ue(r.position.y,s),()=>{n.fog=null,a.current=null,pe()}},[n,r,e,s]),(0,K.useEffect)(()=>{let n=a.current;if(n)if(t){let[t,i]=Gn(e,r.position.y);n.near=t,n.far=i}else n.near=1e10,n.far=1e10},[t,e,r.position.y]),i(()=>{let n=a.current;if(!n)return;let i=r.position.y;if(ue(i,s,t),t){let[t,r]=Gn(e,i),a=z.fogDistanceScale.value;n.near=a>1?Math.min(t,100):t,n.far=r*a,n.color.copy(e.fogColor)}}),null}var qn=(0,K.memo)(function({entity:e}){let{skyData:t}=e;Fn.debug( ` Rendering : materialList = % s , useSkyTextures = % s ` ,t.materialList,t.useSkyTextures);let{fogEnabled:n}=l(),r=t.materialList||void 0,i=(0,K.useMemo)(()=>Ln(t.skySolidColor),[t.skySolidColor]),a=t.useSkyTextures,s=(0,K.useMemo)(()=>Pn(t),[t]);Fn.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 c=(0,K.useMemo)(()=>Ln(t.fogColor),[t.fogColor]),u=i||c,d=s.enabled&&n,f=s.fogColor,p=o(e=>e.scene),m=o(e=>e.gl);(0,K.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,J.jsxs)(J.Fragment,{children:[r&&a&&r.length>0?(0,J.jsx)(K.Suspense,{children:(0,J.jsx)(Vn,{materialList:r,fogColor:d?f:void 0,fogState:d?s:void 0},r)}):h?(0,J.jsx)(Wn,{skyColor:h,fogColor:d?f:void 0,fogState:d?s:void 0}):null,(0,J.jsx)(K.Suspense,{children:(0,J.jsx)(jn,{scene:t})}),s.enabled?(0,J.jsx)(Kn,{fogState:s,enabled:n}):null]})});function Jn(e){let t=(0,q.c)(3),{children:n}=e,{audioEnabled:r}=l(),i;return t[0]!==r||t[1]!==n?(i=r?(0,J.jsx)(K.Suspense,{children:n}):null,t[0]=r,t[1]=n,t[2]=i):i=t[2],i}var Yn=()=>{};function Xn(e,t){let n=(0,q.c)(4),{animationEnabled:r}=l(),a;n[0]!==r||n[1]!==e.rotate||n[2]!==t?(a=e.rotate&&r?()=>{if(t.current){let e=performance.now()/1e3;t.current.rotation.y=e/3*Math.PI*2}}:Yn,n[0]=r,n[1]=e.rotate,n[2]=t,n[3]=a):a=n[3],i(a)}function Zn(e,t){let n=(0,K.lazy)(()=>t().then(t=>({default:t[e]}))),r=t=>{let r=(0,q.c)(5),{entity:i}=t,a= ` $ { e } : $ { i . id } ` ,o;r[0]===i?o=r[1]:(o=(0,J.jsx)(n,{entity:i}),r[0]=i,r[1]=o);let s;return r[2]!==a||r[3]!==o?(s=(0,J.jsx)(Ne,{name:a,children:o}),r[2]=a,r[3]=o,r[4]=s):s=r[4],s};return r.displayName= ` createLazy ( $ { e } ) ` ,r}var Qn=Zn( ` PlayerModel ` ,()=>W(()=>import( ` . / PlayerModel - 8 dF8wZN2 . 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]))), $ n=Zn( ` ExplosionShape ` ,()=>W(()=>import( ` . / ExplosionShape - DH _M7uUx . js ` ),__vite__mapDeps([27,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]))),er=Zn( ` TracerProjectile ` ,()=>W(()=>import( ` . / Projectiles - CWChdCSv . js ` ),__vite__mapDeps([28,2,29,5,6,7,8,22,23,17,18,14,12,13]))),tr=Zn( ` SpriteProjectile ` ,()=>W(()=>import( ` . / Projectiles - CWChdCSv . js ` ),__vite__mapDeps([28,2,29,5,6,7,8,22,23,17,18,14,12,13]))),nr=Zn( ` ForceFieldBare ` ,()=>W(()=>import( ` . / ForceFieldBare - CIwodqfs . js ` ),__vite__mapDeps([30,2,29,5,6,7,8,10,11,12,13,16,21,14,17,18,19]))),rr=Zn( ` AudioEmitter ` ,()=>W(()=>import( ` . / AudioEmitter - DaFPiGOy . js ` ),__vite__mapDeps([31,10,2,6,11,8,12,13,15,3,4,5,7,14,16,17,18,19,20]))),ir=Zn( ` WaterBlock ` ,()=>W(()=>import( ` . / WaterBlock - DV6neJfD . js ` ),__vite__mapDeps([32,2,4,29,5,6,7,8,10,11,12,13,16,24,23,17,18,14,33,19]))),ar=(0,K.memo)(function(e){let t=(0,q.c)(27),{entity:n,objectMounts:r}=e;switch(n.renderType){case ` Shape ` :{let e;return t[0]!==n||t[1]!==r?(e=(0,J.jsx)(or,{entity:n,objectMounts:r}),t[0]=n,t[1]=r,t[2]=e):e=t[2],e}case ` ForceFieldBare ` :{let e;return t[3]===n?e=t[4]:(e=(0,J.jsx)(nr,{entity:n}),t[3]=n,t[4]=e),