t2-mapper/docs/_next/static/chunks/f8ab872058b0d11b.js

528 lines
322 KiB
JavaScript
Raw Normal View History

(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]}),(