Clamp, Not Wrap
The loader directory split into bsp/, map/, model/, pk3/, shader/ and texture/, and one missing texture setting that made every tiled surface wrong.
Six Folders, Forty-Five Files
src/engine/loader/ was reorganised into:
bsp/ BspLoader, BspMeshBuilder, BspTest
map/ MapParser, BrushGenerator, PatchGenerator
model/ ModelLoader, PrefabLoader, PrefabInstance
pk3/ PK3Loader, PK3Index, PK3TextureLoader
shader/ Q3ShaderParser, Q3ShaderRegistry, Q3ShaderTypes
texture/ TextureManager, SkyboxLoader, KTX1LoaderEach with a barrel index.ts. 45 files modified, 12 unused-variable warnings fixed, a PatchControlPoint export conflict resolved, and several previously internal helpers promoted to exports — q3ToBabylonVec3(), reverseTriangleWinding(), MAX_LIGHTMAPS, createCustomStages().
The Two Fixes That Actually Changed The Screen
Root cause: PK3TextureLoader was not setting wrap modes,
causing Babylon.js to default to CLAMP instead of WRAPWrap mode. Not setting it does not mean no tiling — it means tiling happens exactly once, at the edge of the texture, and then the pixel is smeared for the rest of the surface. Every tiled wall in every loaded map was wrong and nothing about the symptom pointed at the cause.
Normal maps. _norm, _normal and _bump companions are now auto-loaded and applied to material.bumpTexture.
Known Issues: none stated.