2025-12-17 01:31:13 +00:00
( globalThis . TURBOPACK || ( globalThis . TURBOPACK = [ ] ) ) . push ( [ "object" == typeof document ? document . currentScript : void 0 , 38360 , ( e , t , r ) => { var n = { À : "A" , Á : "A" ,  : "A" , à : "A" , Ä : "A" , Å : "A" , Ấ : "A" , Ắ : "A" , Ẳ : "A" , Ẵ : "A" , Ặ : "A" , Æ : "AE" , Ầ : "A" , Ằ : "A" , Ȃ : "A" , Ả : "A" , Ạ : "A" , Ẩ : "A" , Ẫ : "A" , Ậ : "A" , Ç : "C" , Ḉ : "C" , È : "E" , É : "E" , Ê : "E" , Ë : "E" , Ế : "E" , Ḗ : "E" , Ề : "E" , Ḕ : "E" , Ḝ : "E" , Ȇ : "E" , Ẻ : "E" , Ẽ : "E" , Ẹ : "E" , Ể : "E" , Ễ : "E" , Ệ : "E" , Ì : "I" , Í : "I" , Î : "I" , Ï : "I" , Ḯ : "I" , Ȋ : "I" , Ỉ : "I" , Ị : "I" , Ð : "D" , Ñ : "N" , Ò : "O" , Ó : "O" , Ô : "O" , Õ : "O" , Ö : "O" , Ø : "O" , Ố : "O" , Ṍ : "O" , Ṓ : "O" , Ȏ : "O" , Ỏ : "O" , Ọ : "O" , Ổ : "O" , Ỗ : "O" , Ộ : "O" , Ờ : "O" , Ở : "O" , Ỡ : "O" , Ớ : "O" , Ợ : "O" , Ù : "U" , Ú : "U" , Û : "U" , Ü : "U" , Ủ : "U" , Ụ : "U" , Ử : "U" , Ữ : "U" , Ự : "U" , Ý : "Y" , à : "a" , á : "a" , â : "a" , ã : "a" , ä : "a" , å : "a" , ấ : "a" , ắ : "a" , ẳ : "a" , ẵ : "a" , ặ : "a" , æ : "ae" , ầ : "a" , ằ : "a" , ȃ : "a" , ả : "a" , ạ : "a" , ẩ : "a" , ẫ : "a" , ậ : "a" , ç : "c" , ḉ : "c" , è : "e" , é : "e" , ê : "e" , ë : "e" , ế : "e" , ḗ : "e" , ề : "e" , ḕ : "e" , ḝ : "e" , ȇ : "e" , ẻ : "e" , ẽ : "e" , ẹ : "e" , ể : "e" , ễ : "e" , ệ : "e" , ì : "i" , í : "i" , î : "i" , ï : "i" , ḯ : "i" , ȋ : "i" , ỉ : "i" , ị : "i" , ð : "d" , ñ : "n" , ò : "o" , ó : "o" , ô : "o" , õ : "o" , ö : "o" , ø : "o" , ố : "o" , ṍ : "o" , ṓ : "o" , ȏ : "o" , ỏ : "o" , ọ : "o" , ổ : "o" , ỗ : "o" , ộ : "o" , ờ : "o" , ở : "o" , ỡ : "o" , ớ : "o" , ợ : "o" , ù : "u" , ú : "u" , û : "u" , ü : "u" , ủ : "u" , ụ : "u" , ử : "u" , ữ : "u" , ự : "u" , ý : "y" , ÿ : "y" , Ā : "A" , ā : "a" , Ă : "A" , ă : "a" , Ą : "A" , ą : "a" , Ć : "C" , ć : "c" , Ĉ : "C" , ĉ : "c" , Ċ : "C" , ċ : "c" , Č : "C" , č : "c" , C ̆ : "C" , c ̆ : "c" , Ď : "D" , ď : "d" , Đ : "D" , đ : "d" , Ē : "E" , ē : "e" , Ĕ : "E" , ĕ : "e" , Ė : "E" , ė : "e" , Ę : "E" , ę : "e" , Ě : "E" , ě : "e" , Ĝ : "G" , Ǵ : "G" , ĝ : "g" , ǵ : "g" , Ğ : "G" , ğ : "g" , Ġ : "G" , ġ : "g" , Ģ : "G" , ģ : "g" , Ĥ : "H" , ĥ : "h" , Ħ : "H" , ħ : "h" , Ḫ : "H" , ḫ : "h" , Ĩ : "I" , ĩ : "i" , Ī : "I" , ī : "i" , Ĭ : "I" , ĭ : "i" , Į : "I" , į : "i" , İ : "I" , ı : "i" , IJ : "IJ" , ij : "ij" , Ĵ : "J" , ĵ : "j" , Ķ : "K" , ķ : "k" , Ḱ : "K" , ḱ : "k" , K ̆ : "K" , k ̆ : "k" , Ĺ : "L" , ĺ : "l" , Ļ : "L" , ļ : "l" , Ľ : "L" , ľ : "l" , Ŀ : "L" , ŀ : "l" , Ł : "l" , ł : "l" , Ḿ : "M" , ḿ : "m" , M ̆ : "M" , m ̆ : "m" , Ń : "N" , ń : "n" , Ņ : "N" , ņ : "n" , Ň : "N" , ň : "n" , ʼn : "n" , N ̆ : "N" , n ̆ : "n" , Ō : "O" , ō : "o" , Ŏ : "O" , ŏ : "o" , Ő : "O" , ő : "o" , Œ : "OE" , œ : "oe" , P ̆ : "P" , p ̆ : "p" , Ŕ : "R" , ŕ : "r" , Ŗ : "R" , ŗ : "r" , Ř : "R" , ř : "r" , R ̆ : "R" , r ̆ : "r" , Ȓ : "R" , ȓ : "r" , Ś : "S" , ś : "s" , Ŝ : "S" , ŝ : "s" , Ş : "S" , Ș : "S" , ș : "s" , ş : "s" , Š : "S" , š : "s" , Ţ : "T" , ţ : "t" , ț : "t" , Ț : "T" , Ť : "T" , ť : "t" , Ŧ : "T" , ŧ : "t" , T ̆ : "T" , t ̆ : "t" , Ũ : "U" , ũ : "u" , Ū : "U" , ū : "u" , Ŭ : "U" , ŭ : "u" , Ů : "U" , ů : "u" , Ű : "U" , ű : "u" , Ų : "U" , ų : "u" , Ȗ : "U" , ȗ : "u" , V ̆ : "V" , v ̆ : "v" , Ŵ : "W" , ŵ : "w" , Ẃ : "W" , ẃ : "w" , X ̆ : "X" , x ̆ : "x" , Ŷ : "Y" , ŷ : "y" , Ÿ : "Y" , Y ̆ : "Y" , y ̆ : "y" , Ź : "Z" , ź : "z" , Ż : "Z" , ż : "z" , Ž : "Z" , ž : "z" , ſ : "s" , ƒ : "f" , Ơ : "O" , ơ : "o" , Ư : "U" , ư : "u" , Ǎ : "A" , ǎ : "a" , Ǐ : "I" , ǐ : "i" , Ǒ : "O" , ǒ : "o" , Ǔ : "U" , ǔ : "u" , Ǖ : "U" , ǖ : "u" , Ǘ : "U" , ǘ : "u" , Ǚ : "U" , ǚ : "u" , Ǜ : "U" , ǜ : "u" , Ứ : "U" , ứ : "u" , Ṹ : "U" , ṹ : "u" , Ǻ : "A" , ǻ : "a" , Ǽ : "AE" , ǽ : "ae" , Ǿ : "O" , ǿ : "o" , Þ : "TH" , þ : "th" , Ṕ : "P" , ṕ : "p" , Ṥ : "S" , ṥ : "s" , X ́ : "X" , x ́ : "x" , Ѓ : "Г" , ѓ : "г " , Ќ : "К " , ќ : "к" , A ̋ : "A" , a ̋ : "a" , E ̋ : "E" , e ̋ : "e" , I ̋ : "I" , i ̋ : "i" , Ǹ : "N" , ǹ : "n" , Ồ : "O" , ồ : "o" , Ṑ : "O" , ṑ : "o" , Ừ : "U" , ừ : "u" , Ẁ : "W" , ẁ : "w" , Ỳ : "Y" , ỳ : "y" , Ȁ : "A" , ȁ : "a" , Ȅ : "E" , ȅ : "e" , Ȉ : "I" , ȉ : "i" , Ȍ : "O" , ȍ : "o" , Ȑ : "R" , ȑ : "r" , Ȕ : "U" , ȕ : "u" , B ̌ : "B" , b ̌ : "b" , Č ̣ : "C" , č ̣ : "c" , Ê ̌ : "E" , ê ̌ : "e" , F ̌ : "F" , f ̌ : "f" , Ǧ : "G" , ǧ : "g" , Ȟ : "H" , ȟ : "h" , J ̌ : "J" , ǰ : "j" , Ǩ : "K" , ǩ : "k" , M ̌ : "M" , m ̌ : "m" , P ̌ : "P" , p ̌ : "p" , Q ̌ : "Q" , q ̌ : "q" , Ř ̩ : "R" , ř ̩ : "r" , Ṧ : "S" , ṧ : "s" , V ̌ : "V" , v ̌ : "v" , W ̌ : "W" , w ̌ : "w" , X ̌ : "X" , x ̌ : "x" , Y ̌ : "Y" , y ̌ : "y" , A ̧ : "A" , a ̧ : "a" , B ̧ : "B" , b ̧ : "b" , Ḑ : "D" , ḑ : "d" , Ȩ : "E" , ȩ : "e" , Ɛ ̧ : "E" , ɛ ̧ : "e" , Ḩ : "H" , ḩ : "h" , I ̧ : "I" , i ̧ : "i" , Ɨ ̧ : "I" , ɨ ̧ : "i" , M ̧ : "M" , m ̧ : "m" , O ̧ : "O" , o ̧ : "o" , Q ̧ : "Q" , q ̧ : "q" , U ̧ : "U" , u ̧ : "u" , X ̧ : "X" , x ̧ : "x" , Z ̧ : "Z" , z ̧ : "z" , й : "и" , Й : "И" , ё : "е " , Ё : "Е " } , i = Object . keys ( n ) . join ( "|" ) , a = RegExp ( i , "g" ) , o = RegExp ( i , "" ) ; function s ( e ) { return n [ e ] } var l = function ( e ) { return e . replace ( a , s ) } ; t . exports = l , t . exports . has = function ( e ) { return ! ! e . match ( o ) } , t . exports . remove = l } , 29402 , ( e , t , r ) => { var n , i , a , o , s = "__lodash_hash_undefined__" , l = 1 / 0 , u = "[object Arguments]" , c = "[object Array]" , d = "[object Boolean]" , f = "[object Date]" , h = "[object Error]" , m = "[object Function]" , p = "[object Map]" , A = "[object Number]" , g = "[object Object]" , v = "[object Promise]" , C = "[object RegExp]" , B = "[object Set]" , y = " [ object String
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 ) ;
}
` ;var eS=e.i(79123),eF=e.i(47021),eT=e.i(48066);let eR={0:32,1:32,2:32,3:32,4:32,5:32};function ew({displacementMap:e,visibilityMask:t,textureNames:r,alphaTextures:n,detailTextureName:i,lightmap:a}){let{debugMode:o}=(0,eS.useDebug)(),s=(0,ex.useTexture)(r.map(e=>(0,ev.terrainTextureToUrl)(e)),e=>{e.forEach(e=>(0,eb.setupTexture)(e))}),l=i?(0,ev.textureToUrl)(i):null,u=(0,ex.useTexture)(l??ev.FALLBACK_TEXTURE_URL,e=>{(0,eb.setupTexture)(e)}),c=(0,f.useCallback)(e=>{!function({shader:e,baseTextures:t,alphaTextures:r,visibilityMask:n,tiling:i,detailTexture:a=null,lightmap:o=null}){e.uniforms.sunLightPointsDown=eE;let s=t.length;if(t.forEach((t,r)=>{e.uniforms[ ` albedo$ { r } ` ]={value:t}}),r.forEach((t,r)=>{e.uniforms[ ` mask$ { r } ` ]={value:t}}),n&&(e.uniforms.visibilityMask={value:n}),t.forEach((t,r)=>{e.uniforms[ ` tiling$ { r } ` ]={value:i[r]??32}}),o&&(e.uniforms.terrainLightmap={value:o}),a&&(e.uniforms.detailTexture={value:a},e.uniforms.detailTiling={value:64},e.uniforms.detailFadeDistance={value:150},e.vertexShader=e.vertexShader.replace("#include <common>", ` # include < common >
varying vec3 vTerrainWorldPos ; ` ),e.vertexShader=e.vertexShader.replace("#include <worldpos_vertex>", ` # include < worldpos _vertex >
vTerrainWorldPos = ( modelMatrix * vec4 ( transformed , 1.0 ) ) . 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 ;
$ { n ? "uniform sampler2D visibilityMask;" : "" }
$ { o ? "uniform sampler2D terrainLightmap;" : "" }
uniform bool sunLightPointsDown ;
$ { a ? ` uniform sampler2D detailTexture;
uniform float detailTiling ;
uniform float detailFadeDistance ;
varying vec3 vTerrainWorldPos ; ` :""}
$ { eM }
// Global variable to store shadow factor from RE_Direct for use in output calculation
float terrainShadowFactor = 1.0 ;
` +e.fragmentShader,n){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 / 256.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 \xd7 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 / 512.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 \xd7 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 > ` )}({shader:e,baseTextures:s,alphaTextures:n,visibilityMask:t,tiling:eR,detailTexture:l?u:null,lightmap:a}),(0,eF.injectCustomFog)(e,eT.globalFogUniforms)},[s,n,t,u,l,a]),h=(0,f.useRef)(null);(0,f.useEffect)(()=>{let e=h.current;e&&(e.defines??={},e.defines.DEBUG_MODE=+!!o,e.needsUpdate=!0)},[o]);let m= ` $ { l ? "detail" : "nodetail" } - $ { a ? "lightmap" : "nolightmap" } ` ;return(0,d.jsx)("meshLambertMaterial",{ref:h,map:e,depthWrite:!0,side:p.FrontSide,defines:{DEBUG_MODE:+!!o},onBeforeCompile:c},m)}function eD({displacementMap:e,visibilityMask:t,textureNames:r,alphaTextures:n,detailTextureName:i,lightmap:a}){return(0,d.jsx)(f.Suspense,{fallback:(0,d.jsx)("meshLambertMaterial",{color:"rgb(0, 109, 56)",wireframe:!0}),children:(0,d.jsx)(ew,{displacementMap:e,visibilityMask:t,textureNames:r,alphaTextures:n,detailTextureName:i,lightmap:a})})}let eI=(0,f.memo)(function({tileX:e,tileZ:t,blockSize:r,basePosition:n,textureNames:i,geometry:a,displacementMap:o,visibilityMask:s,alphaTextures:l,detailTextureName:u,lightmap:c,visible:h=!0}){let m=(0,f.useMemo)(()=>{let i=r/2;return[n.x+e*r+i,0,n.z+t*r+i]},[e,t,r,n]);return(0,d.jsx)("mesh",{position:m,geometry:a,castShadow:!0,receiveShadow:!0,visible:h,children:(0,d.jsx)(eD,{displacementMap:o,visibilityMask:s,textureNames:i,alphaTextures:l,detailTextureName:u,lightmap:c})})});var eG=e.i(77482);function eL(e){return(0,eG.useRuntime)().getObjectByName(e)}function eO(e){let t=new Uint8Array(65536);for(let r of(t.fill(255),e)){let e=255&r,n=r>>8&255,i=r>>16,a=256*n;for(let r=0;r<i;r++){let n=a+e+r;n<t.length&&(t[n]=0)}}let r=new p.DataTexture(t,256,256,p.RedFormat,p.UnsignedByteType);return r.colorSpace=p.NoColorSpace,r.wrapS=r.wrapT=p.ClampToEdgeWrapping,r.magFilter=p.NearestFilter,r.minFilter=p.NearestFilter,r.needsUpdate=!0,r}let eP=(0,f.memo)(function({object:e}){let t=(0,ey.getProperty)(e,"terrainFile"),r=(0,ey.getInt)(e,"squareSize")??8,n=(0,ey.getProperty)(e,"detailTexture"),i=256*r,a=function(){let e=eL("Sky");if(!e)return 600;let t=(0,ey.getFloat)(e,"high_visibleDistance");return null!=t&&t>0?t:(0,ey.getFloat)(e,"visibleDistance")??600}(),o=(0,eB.useThree)(e=>e.camera),s=(0,f.useMemo)(()=>{let e=-(128*r);return{x:e,z:e}},[r]),l=(0,f.useMemo)(()=>{let t=(0,ey.getProperty)(e,"emptySquares");return t?t.split(" ").map(e=>parseInt(e,10)):[]},[e]),{data:u}=eA({queryKey:["terrain",t],queryFn:()=>(0,ev.loadTerrain)(t)},ea,void 0),c=(0,f.useMemo)(()=>{if(!u)return null;let e=function(e,t){let r=new p.BufferGeometry,n=new Float32Array(198147),i=new Float32Array(198147),a=new Float32Array(132098),o=new Uint32Array(393216),s=0,l=e/256;for(let t=0;t<=256;t++)for(let r=0;r<=256;r++){let o=257*t+r;n[3*o]=r*l-e/2,n[3*o+1]=e/2-t*l,n[3*o+2]=0,i[3*o]=0,i[3*o+1]=0,i[3*o+2]=1,a[2*o]=r/256,a[2*o+1]=1-t/256}for(let e=0;e<256;e++)for(let t=0;t<256;t++){let r=257*e+t,n=r+1,i=(e+1)*257+t,a=i+1;((t^e)&1)==0?(o[s++]=r,o[s++]=i,o[s++]=a,o[s++]=r,o[s++]=a,o[s++]=n):(o[s++]=r,o[s++]=i,o[s++]=n,o[s++]=n,o[s++]=i,o[s++]=a)}return r.setIndex(new p.BufferAttribute(o,1)),r.setAttribute("position",new p.Float32BufferAttribute(n,3)),r.setAttribute("normal",new p.Float32BufferAttribute(i,3)),r.setAttribute("uv",new p.Float32BufferAttribute(a,2)),r.rotateX(-Math.PI/2),r.rotateY(-Math.PI/2),r}(256*r,0);return!function(e,t,r){let n=e.attributes.position,i=e.attributes.uv,a=e.attributes.normal,o=n.array,s=i.array,l=a.array,u=n.count,c=(e,r)=>(e=Math.max(0,Math.min(255,e)),t[256*(r=Math.max(0,Math.min(255,r)))+e]/65535*2048),d=(e,r)=>{let n=Math.floor(e=Math.max(0,Math.min(255,e))),i=Math.floor(r=Math.max(0,Math.min(255,r))),a=Math.min(n+1,255),o=Math.min(i+1,255),s=e-n,l=r-i;return(t[256*i+n]/65535*2048*(1-s)+t[256*i+a]/65535*2048*s)*(1-l)+(t[256*o+n]/65535*2048*(1-s)+t[256*o+a]/65535*2048*s)*l};for(let e=0;e<u;e++){let t=s[2*e],n=s[2*e+1],i=c(255&Math.floor(256*t),255&Math.floor(256*n));o[3*e+1]=i;let a=255*t,u=255*n,f=d(a-1,u),h=d(a+1,u),m=(d(a,u+1)-d(a,u-1))/2,p=r,A=(h-f)/2,g=Math.sqrt(m*m+p*p+A*A);g>0?(m/=g,p/=g,A/=g):(m=0,p=1,A=0),l[3*e]=m,l[3*e+1]=p,l[3*e+2]=A}n.needsUpdate=!0,a.needsUpdate=!0}(e,u.
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 tJ({materialName:e,material:t,lightMap:r}){let n=(0,eS.useDebug)(),i=n?.debugMode??!1,a=(0,ev.textureToUrl)(e),o=(0,ex.useTexture)(a,e=>(0,eb.setupTexture)(e)),s=new Set(t?.userData?.flag_names??[]).has("SelfIlluminating"),l=new Set(t?.userData?.surface_flag_names??[]).has("SurfaceOutsideVisible"),u=(0,f.useCallback)(e=>{let t;(0,eF.injectCustomFog)(e,eT.globalFogUniforms),t=l??!1,e.uniforms.useSceneLighting={value:t},e.uniforms.interiorDebugColor={value:t?new p.Vector3(0,.4,1):new p.Vector3(1,.2,0)},e.fragmentShader=e.fragmentShader.replace("#include <common>", ` # include < common >
$ { tN }
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 \xd7 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 \xd7 texture_linear / PI
// Extract pure lighting: lighting = reflectedLight \xd7 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 \xd7 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 > ` )},[l]),c=(0,f.useRef)(null),h=(0,f.useRef)(null);(0,f.useEffect)(()=>{let e=c.current??h.current;e&&(e.defines??={},e.defines.DEBUG_MODE=+!!i,e.needsUpdate=!0)},[i]);let m={DEBUG_MODE:+!!i},A= ` $ { l } ` ;return s?(0,d.jsx)("meshBasicMaterial",{ref:c,map:o,toneMapped:!1,defines:m,onBeforeCompile:u},A):(0,d.jsx)("meshLambertMaterial",{ref:h,map:o,lightMap:r,toneMapped:!1,defines:m,onBeforeCompile:u},A)}function tK(e){if(!e)return null;let t=e.emissiveMap;return t&&(t.colorSpace=p.SRGBColorSpace),t??null}function tQ({node:e}){let t=(0,f.useMemo)(()=>e.material?Array.isArray(e.material)?e.material.map(e=>tK(e)):[tK(e.material)]:[],[e.material]);return(0,d.jsx)("mesh",{geometry:e.geometry,castShadow:!0,receiveShadow:!0,children:e.material?(0,d.jsx)(f.Suspense,{fallback:(0,d.jsx)("meshStandardMaterial",{color:"yellow",wireframe:!0}),children:Array.isArray(e.material)?e.material.map((e,r)=>(0,d.jsx)(tJ,{materialName:e.userData.resource_path,material:e,lightMap:t[r]},r)):(0,d.jsx)(tJ,{materialName:e.material.userData.resource_path,material:e.material,lightMap:t[0]})}):null})}let tV=(0,f.memo)(({object:e,interiorFile:t})=>{let{nodes:r}=tU((0,ev.interiorToUrl)(t)),n=(0,eS.useDebug)(),i=n?.debugMode??!1;return(0,d.jsxs)("group",{rotation:[0,-Math.PI/2,0],children:[Object.entries(r).filter(([,e])=>e.isMesh).map(([e,t])=>(0,d.jsx)(tQ,{node:t},e)),i?(0,d.jsxs)(tj.FloatingLabel,{children:[e._id,": ",t]}):null]})});function tq({color:e,label:t}){return(0,d.jsxs)("mesh",{children:[(0,d.jsx)("boxGeometry",{args:[10,10,10]}),(0,d.jsx)("meshStandardMaterial",{color:e,wireframe:!0}),t?(0,d.jsx)(tj.FloatingLabel,{color:e,children:t}):null]})}function tX({label:e}){let t=(0,eS.useDebug)();return t?.debugMode?(0,d.jsx)(tq,{color:"red",label:e}):null}let tW=(0,f.memo)(function({object:e}){let t=(0,ey.getProperty)(e,"interiorFile"),r=(0,f.useMemo)(()=>(0,ey.getPosition)(e),[e]),n=(0,f.useMemo)(()=>(0,ey.getScale)(e),[e]),i=(0,f.useMemo)(()=>(0,ey.getRotation)(e),[e]);return(0,d.jsx)("group",{position:r,quaternion:i,scale:n,children:(0,d.jsx)(eN,{fallback:(0,d.jsx)(tX,{label: ` $ { e . _id } : $ { t } ` }),children:(0,d.jsx)(f.Suspense,{fallback:(0,d.jsx)(tq,{color:"orange"}),children:(0,d.jsx)(tV,{object:e,interiorFile:t})})})})});function tY(e,{path:t}){let[r]=(0,tL.useLoader)(p.CubeTextureLoader,[e],e=>e.setPath(t));return r}tY.preload=(e,{path:t})=>tL.useLoader.preload(p.CubeTextureLoader,[e],e=>e.setPath(t));let tz=()=>{};function tZ(e){return e.wrapS=p.RepeatWrapping,e.wrapT=p.RepeatWrapping,e.minFilter=p.LinearFilter,e.magFilter=p.LinearFilter,e.colorSpace=p.NoColorSpace,e.needsUpdate=!0,e}let t $ = `
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 ;
}
` ,t0= `
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 \xd7 vertex color
// Vertex color is white with varying alpha, so:
// Final RGB = Texture RGB \xd7 1.0 = Texture RGB
// Final Alpha = Texture Alpha \xd7 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 t1({textureUrl:e,radius:t,heightPercent:r,speed:n,windDirection:i,layerIndex:a}){let{debugMode:o}=(0,eS.useDebug)(),{animationEnabled:s}=(0,eS.useSettings)(),l=(0,f.useRef)(null),u=(0,ex.useTexture)(e,tZ),c=(0,f.useMemo)(()=>{let e=r-.05;return function(e,t,r,n){var i;let a,o,s,l,u,c,d,f,h,m,A,g,v,C,B,y,b,x=new p.BufferGeometry,E=new Float32Array(75),M=new Float32Array(50),S=[.05,.05,.05,.05,.05,.05,r,r,r,.05,.05,r,t,r,.05,.05,r,r,r,.05,.05,.05,.05,.05,.05],F=2*e/4;for(let t=0;t<5;t++)for(let r=0;r<5;r++){let n=5*t+r,i=-e+r*F,a=e-t*F,o=e*S[n];E[3*n]=i,E[3*n+1]=o,E[3*n+2]=a,M[2*n]=r,M[2*n+1]=t}i=E,a=e=>({x:i[3*e],y:i[3*e+1],z:i[3*e+2]}),o=(e,t,r,n)=>{i[3*e]=t,i[3*e+1]=r,i[3*e+2]=n},s=a(1),l=a(3),u=a(5),c=a(6),d=a(8),f=a(9),h=a(15),m=a(16),A=a(18),g=a(19),v=a(21),C=a(23),B=u.x+(s.x-u.x)*.5,y=u.y+(s.y-u.y)*.5,b=u.z+(s.z-u.z)*.5,o(0,c.x+(B-c.x)*2,c.y+(y-c.y)*2,c.z+(b-c.z)*2),B=f.x+(l.x-f.x)*.5,y=f.y+(l.y-f.y)*.5,b=f.z+(l.z-f.z)*.5,o(4,d.x+(B-d.x)*2,d.y+(y-d.y)*2,d.z+(b-d.z)*2),B=v.x+(h.x-v.x)*.5,y=v.y+(h.y-v.y)*.5,b=v.z+(h.z-v.z)*.5,o(20,m.x+(B-m.x)*2,m.y+(y-m.y)*2,m.z+(b-m.z)*2),B=C.x+(g.x-C.x)*.5,y=C.y+(g.y-C.y)*.5,b=C.z+(g.z-C.z)*.5,o(24,A.x+(B-A.x)*2,A.y+(y-A.y)*2,A.z+(b-A.z)*2);let T=function(e,t){let r=new Float32Array(25);for(let n=0;n<25;n++){let i=e[3*n],a=e[3*n+2],o=1.3-Math.sqrt(i*i+a*a)/t;o<.4?o=0:o>.8&&(o=1),r[n]=o}return r}(E,e),R=[];for(let e=0;e<4;e++)for(let t=0;t<4;t++){let r=5*e+t,n=r+1,i=r+5,a=i+1;R.push(r,i,a),R.push(r,a,n)}return x.setIndex(R),x.setAttribute("position",new p.Float32BufferAttribute(E,3)),x.setAttribute("uv",new p.Float32BufferAttribute(M,2)),x.setAttribute("alpha",new p.Float32BufferAttribute(T,1)),x.computeBoundingSphere(),x}(t,r,e,0)},[t,r]);(0,f.useEffect)(()=>()=>{c.dispose()},[c]);let h=(0,f.useMemo)(()=>new p.ShaderMaterial({uniforms:{cloudTexture:{value:u},uvOffset:{value:new p.Vector2(0,0)},debugMode:{value:+!!o},layerIndex:{value:a}},vertexShader:t $ ,fragmentShader:t0,transparent:!0,depthWrite:!1,side:p.DoubleSide}),[u,o,a]);return(0,f.useEffect)(()=>()=>{h.dispose()},[h]),(0,eC.useFrame)(s?(e,t)=>{let r=1e3*t/32;l.current??=new p.Vector2(0,0),l.current.x+=i.x*n*r,l.current.y+=i.y*n*r,l.current.x-=Math.floor(l.current.x),l.current.y-=Math.floor(l.current.y),h.uniforms.uvOffset.value.copy(l.current)}:tz),(0,d.jsx)("mesh",{geometry:c,frustumCulled:!1,renderOrder:10,children:(0,d.jsx)("primitive",{object:h,attach:"material"})})}function t9({object:e}){var t;let{data:r}=eA({queryKey:["detailMapList",t=(0,ey.getProperty)(e,"materialList")],queryFn:()=>(0,ev.loadDetailMapList)(t),enabled:!!t},ea,void 0),n=.95*((0,ey.getFloat)(e,"visibleDistance")??500),i=(0,f.useMemo)(()=>[(0,ey.getFloat)(e,"cloudSpeed1")??1e-4,(0,ey.getFloat)(e,"cloudSpeed2")??2e-4,(0,ey.getFloat)(e,"cloudSpeed3")??3e-4],[e]),a=(0,f.useMemo)(()=>[(0,ey.getFloat)(e,"cloudHeightPer1")??.35,(0,ey.getFloat)(e,"cloudHeightPer2")??.25,(0,ey.getFloat)(e,"cloudHeightPer3")??.2],[e]),o=(0,f.useMemo)(()=>{let t=(0,ey.getProperty)(e,"windVelocity");if(t){let[e,r]=t.split(" ").map(e=>parseFloat(e));if(0!==e||0!==r)return new p.Vector2(r,-e).normalize()}return new p.Vector2(1,0)},[e]),s=(0,f.useMemo)(()=>{if(!r)return[];let e=[];for(let t=0;t<3;t++){let n=r[7+t];n&&e.push({texture:n,height:a[t],speed:i[t]})}return e},[r,i,a]),l=(0,f.useRef)(null);return((0,eC.useFrame)(({camera:e})=>{l.current&&l.current.position.copy(e.position)}),s&&0!==s.length)?(0,d.jsx)("group",{ref:l,children:s.map((e,t)=>{let r=(0,ev.textureToUrl)(e.texture);return(0,d.jsx)(f.Suspense,{fallback:null,children:(0,d.jsx)(t1,{textureUrl:r,radius:n,heightPercent:e.height,speed:e.speed,windDirection:o,layerIndex:t})},t)})}):null}let t2=!1;function t3(e){if(!e)return;let[t,r,n]=e.split(" ").map(e=>parseFloat(e));return[new p.Color().setRGB(t,r,n),new p.Color().setRGB(t,r,n).convertSRGBToLinear()]}function t8({skyBoxFiles:e,fogColor:t,fogState:r}){let{camera:n}=(0,eB.useThree)(),i=tY(e,{path:""}),a=!!t,o=(0,f.useMemo)(()=>n.projectionMatrixInverse,[n]),s=(0,f.useMemo)(()=>r?(0,eT.packFogVolumeData)(r.fogVolumes):new Float32Array(12),[r]),l=(0,f.useRef)(
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 t5({materialList:e,fogColor:t,fogState:r}){let{data:n}=eA({queryKey:["detailMapList",e],queryFn:()=>(0,ev.loadDetailMapList)(e)},ea,void 0),i=(0,f.useMemo)(()=>n?[(0,ev.textureToUrl)(n[1]),(0,ev.textureToUrl)(n[3]),(0,ev.textureToUrl)(n[4]),(0,ev.textureToUrl)(n[5]),(0,ev.textureToUrl)(n[0]),(0,ev.textureToUrl)(n[2])]:null,[n]);return i?(0,d.jsx)(t8,{skyBoxFiles:i,fogColor:t,fogState:r}):null}function t6({skyColor:e,fogColor:t,fogState:r}){let{camera:n}=(0,eB.useThree)(),i=!!t,a=(0,f.useMemo)(()=>n.projectionMatrixInverse,[n]),o=(0,f.useMemo)(()=>r?(0,eT.packFogVolumeData)(r.fogVolumes):new Float32Array(12),[r]),s=(0,f.useMemo)(()=>{if(!r)return .18;let e=.95*r.visibleDistance/Math.sqrt(3);return 60/Math.sqrt(e*e+3600)},[r]),l=(0,f.useRef)({skyColor:{value:e},fogColor:{value:t??new p.Color(0,0,0)},enableFog:{value:i},inverseProjectionMatrix:{value:a},cameraMatrixWorld:{value:n.matrixWorld},cameraHeight:eT.globalFogUniforms.cameraHeight,fogVolumeData:{value:o},horizonFogHeight:{value:s}});return(0,f.useEffect)(()=>{l.current.skyColor.value=e,l.current.fogColor.value=t??new p.Color(0,0,0),l.current.enableFog.value=i,l.current.fogVolumeData.value=o,l.current.horizonFogHeight.value=s},[e,t,i,o,s]),(0,d.jsxs)("mesh",{renderOrder:-1e3,frustumCulled:!1,children:[(0,d.jsxs)("bufferGeometry",{children:[(0,d.jsx)("bufferAttribute",{attach:"attributes-position",array:new Float32Array([-1,-1,0,3,-1,0,-1,3,0]),count:3,itemSize:3}),(0,d.jsx)("bufferAttribute",{attach:"attributes-uv",array:new Float32Array([0,0,2,0,0,2]),count:3,itemSize:2})]}),(0,d.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 t4(e,t){let{fogDistance:r,visibleDistance:n}=e;return[r,n]}function t7({fogState:e,enabled:t}){let{scene:r,camera:n}=(0,eB.useThree)(),i=(0,f.useRef)(null),a=(0,f.useMemo)(()=>(0,eT.packFogVolumeData)(e.fogVolumes),[e.fogVolumes]);return(0,f.useEffect)(()=>{t2||((0,eF.installCustomFogShader)(),t2=!0)},[]),(0,f.useEffect)(()=>{(0,eT.resetGlobalFogUniforms)();let[t,o]=t4(e,n.position.y),s=new p.Fog(e.fogColor,t,o);return r.fog=s,i.current=s,(0,eT.updateGlobalFogUniforms)(n.position.y,a),()=>{r.fog=null,i.current=null,(0,eT.resetGlobalFogUniforms)()}},[r,n,e,a]),(0,f.useEffect)(()=>{let r=i.current;if(r)if(t){let[t,i]=t4(e,n.position.y);r.near=t,r.far=i}else r.near=1e10,r.far=1e10},[t,e,n.position.y]),(0,eC.useFrame)(()=>{let r=i.current;if(!r)return;let o=n.position.y;if((0,eT.updateGlobalFogUniforms)(o,a,t),t){let[t,n]=t4(e,o);r.near=t,r.far=n,r.color.copy(e.fogColor)}}),null}let re=/borg|xorg|porg|dorg|plant|tree|bush|fern|vine|grass|leaf|flower|frond|palm|foliage/i;function rt(e){return re.test(e)}let rr=(0,f.createContext)(null);function rn(){let e=(0,f.useContext)(rr);if(!e)throw Error("useShapeInfo must be used within ShapeInfoProvider");return e}function ri({children:e,object:t,shapeName:r,type:n}){let i=(0,f.useMemo)(()=>rt(r),[r]),a=(0,f.useMemo)(()=>({object:t,shapeName:r,type:n,isOrganic:i}),[t,r,n,i]);return(0,d.jsx)(rr.Provider,{value:a,children:e})}var ra=e.i(51475);let ro=new Map;function rs(e){e.onBeforeCompile=t=>{(0,eF.injectCustomFog)(t,eT.globalFogUniforms),e instanceof p.MeshLambertMaterial&&(t.uniforms.shapeDirectionalFactor={value:1},t.uniforms.shapeAmbientFactor={value:1.5},t.fragmentShader=t.fragmentShader.replace("#include <common>", ` # include < common >
uniform float shapeDirectionalFactor ;
uniform float shapeAmbientFactor ;
` ),t.fragmentShader=t.fragmentShader.replace("#include <lights_fragment_end>", ` # include < lights _fragment _end >
// Apply shape-specific lighting multipliers
reflectedLight . directDiffuse *= shapeDirectionalFactor ;
reflectedLight . indirectDiffuse *= shapeAmbientFactor ;
2025-12-17 07:42:45 +00:00
` ))}}function rl(e,t,r,n){let i=r.has("Translucent"),a=r.has("Additive");if(r.has("SelfIlluminating")){let e=new p.MeshBasicMaterial({map:t,side:2,transparent:a,alphaTest:.5*!a,fog:!0,...a&&{blending:p.AdditiveBlending}});return rs(e),e}if(n||i){let e={map:t,transparent:!1,alphaTest:.5,reflectivity:0},r=new p.MeshLambertMaterial({...e,side:1,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),n=new p.MeshLambertMaterial({...e,side:0});return rs(r),rs(n),[r,n]}let o=new p.MeshLambertMaterial({map:t,side:2,reflectivity:0});return rs(o),o}let ru=(0,f.memo)(function({material:e,shapeName:t,geometry:r,backGeometry:n,castShadow:i=!1,receiveShadow:a=!1}){let o=e.userData.resource_path,s=new Set(e.userData.flag_names??[]),l=function(e){let{animationEnabled:t}=(0,eS.useSettings)(),{data:r}=eA({queryKey:["ifl",e],queryFn:()=>(0,ev.loadImageFrameList)(e),enabled:!0,suspense:!0,throwOnError:em,placeholderData:void 0},ea,void 0),n=(0,f.useMemo)(()=>r.map(t=>(0,ev.iflTextureToUrl)(t.name,e)),[r,e]),i=(0,ex.useTexture)(n),a=(0,f.useMemo)(()=>{var t;let n,a=ro.get(e);if(!a){let t,r,n,o,s,l,u,c,d;r=(t=i[0].image).width,n=t.height,s=Math.ceil(Math.sqrt(o=i.length)),l=Math.ceil(o/s),(u=document.createElement("canvas")).width=r*s,u.height=n*l,c=u.getContext("2d"),i.forEach((e,t)=>{let i=Math.floor(t/s);c.drawImage(e.image,t%s*r,i*n)}),(d=new p.CanvasTexture(u)).colorSpace=p.SRGBColorSpace,d.generateMipmaps=!1,d.minFilter=p.NearestFilter,d.magFilter=p.NearestFilter,d.wrapS=p.ClampToEdgeWrapping,d.wrapT=p.ClampToEdgeWrapping,d.repeat.set(1/s,1/l),a={texture:d,columns:s,rows:l,frameCount:o,frameStartTicks:[],totalTicks:0,lastFrame:-1},ro.set(e,a)}return n=0,(t=a).frameStartTicks=r.map(e=>{let t=n;return n+=e.frameCount,t}),t.totalTicks=n,a},[e,i,r]);return(0,ra.useTick)(e=>{let r=t?function(e,t){if(0===e.totalTicks)return 0;let r=t%e.totalTicks,{frameStartTicks:n}=e;for(let e=n.length-1;e>=0;e--)if(r>=n[e])return e;return 0}(a,e):0;!function(e,t){if(t===e.lastFrame)return;e.lastFrame=t;let r=t%e.columns,n=e.rows-1-Math.floor(t/e.columns);e.texture.offset.set(r/e.columns,n/e.rows)}(a,r)}),a.texture}( ` textures / $ { o } . ifl ` ),u=t&&rt(t),c=(0,f.useMemo)(()=>rl(e,l,s,u),[e,l,s,u]);return Array.isArray(c)?(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)("mesh",{geometry:n||r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:c[0],attach:"material"})}),(0,d.jsx)("mesh",{geometry:r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:c[1],attach:"material"})})]}):(0,d.jsx)("mesh",{geometry:r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:c,attach:"material"})})}),rc=(0,f.memo)(function({material:e,shapeName:t,geometry:r,backGeometry:n,castShadow:i=!1,receiveShadow:a=!1}){let o=e.userData.resource_path,s=new Set(e.userData.flag_names??[]),l=(0,f.useMemo)(()=>(o||console.warn( ` No resource _path was found on "${t}" - rendering fallback . ` ),o?(0,ev.textureToUrl)(o):ev.FALLBACK_TEXTURE_URL),[o,t]),u=t&&rt(t),c=s.has("Translucent"),h=(0,ex.useTexture)(l,e=>u||c?(0,eb.setupTexture)(e,{disableMipmaps:!0}):(0,eb.setupTexture)(e)),m=(0,f.useMemo)(()=>rl(e,h,s,u),[e,h,s,u]);return Array.isArray(m)?(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)("mesh",{geometry:n||r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:m[0],attach:"material"})}),(0,d.jsx)("mesh",{geometry:r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:m[1],attach:"material"})})]}):(0,d.jsx)("mesh",{geometry:r,castShadow:i,receiveShadow:a,children:(0,d.jsx)("primitive",{object:m,attach:"material"})})}),rd=(0,f.memo)(function({material:e,shapeName:t,geometry:r,backGeometry:n,castShadow:i=!1,receiveShadow:a=!1}){let o=new Set(e.userData.flag_names??[]).has("IflMaterial"),s=e.userData.resource_path;return o&&s?(0,d.jsx)(ru,{material:e,shapeName:t,geometry:r,backGeometry:n,castShadow:i,receiveShadow:a}):e.name?(0,d.jsx)(rc,{material:e,shapeName:t,geometry:r,backGeometry:n,castShadow:i,receiveShadow:a}):null});function rf({color:e,label:t}){return(0,d.jsxs)("mesh",{children:[(0,d.jsx)("boxGeometry",{args:[10,10,10]}),(