# 3D-modellen — herkomst en licentie laatste wijzigingen: frost_wisp.glb: "Spaceship" (https://skfb.ly/6CpKU) by Darren McNerney 3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). azure_drone.glb: "Drone RK700" (https://skfb.ly/oyEKC) by kraytay is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). purple_cube.glb: "Galaxy 6 - Sci-Fi Spaceship" (https://skfb.ly/pKrOB) by ARTEL_3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). crystal_lancer.glb: "Spaceship O-100 MK2" (https://skfb.ly/p9UFG) by FilipZelinka is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). helicopter.glb: "HELICOPTER" (https://skfb.ly/oLAQA) by pranav27 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). belegeraar.glb: "Titan Droneman/Behemoth 1.0 (New Version)" (https://skfb.ly/pG7Lu) by Eveston - Elite Big Speakerman - Hugo is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). fighter.glb: "AJF-12 "Dvergr"" (https://skfb.ly/FMID) by Star Conflict is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). zwermmoeder.glb: Free Standard, geen naamsvermelding nodig. omdat ik het wel netjes vind om te doen, de naam van het model is UFO Flying Saucer Spaceship OVNI en de auteur is BlackHawk0512 op sketchfab Aanpassingen aan deze modellen (CC-BY vraagt om dit te vermelden): * **2026-09-12 — alles wat bij het opstarten laadt is opnieuw ingepakt.** Veertien modellen: texturen naar WebP (en waar dat kon naar 512 px), geometrie gecomprimeerd met `EXT_meshopt_compression` bovenop `KHR_mesh_quantization`. Samen 48,2 MB → 15,8 MB. Het AANTAL DRIEHOEKEN is van geen enkel model veranderd — er is niets vereenvoudigd of weggelaten, alleen anders opgeslagen. Hoe het gedaan is en hoe je het overdoet staat in `ops/README-modellen.md`. | | van | naar | | | van | naar | |---|---|---|---|---|---|---| | spaceship_corridor | 8,62 MB | 2,17 MB | | fighter | 2,97 MB | 1,20 MB | | london_tower | 8,08 MB | 2,68 MB | | zwermmoeder | 2,83 MB | 1,01 MB | | spaceship_fighter_4mb | 4,29 MB | 2,13 MB | | purple_cube | 2,62 MB | 0,42 MB | | belegeraar | 4,25 MB | 1,90 MB | | frost_wisp | 2,55 MB | 0,73 MB | | crystal_lancer | 4,07 MB | 0,81 MB | | azure_drone | 2,24 MB | 1,06 MB | | swarmer_opt | 3,11 MB | 0,88 MB | | mini_opt | 1,80 MB | 0,54 MB | | | | | | helicopter | 0,55 MB | 0,16 MB | | | | | | bob_lazar_ufo | 0,18 MB | 0,03 MB | * Alle vier zijn ingehaakt via `ENEMY_TYPES` in `assets/js3d/protocol.js`. De loader centreert ze, schaalt ze op speelmaat en draait de neus naar +Z; de gemeten draaiing staat per bestand in `MODEL_YAW`, en `azure_drone` en `purple_cube` staan op 0,6 in `MODEL_SCALE` omdat ze in bovenaanzicht ongeveer even breed als lang zijn. * `azure_drone.glb` is opnieuw ingepakt met gltf-transform: texturen naar 512 px, geometrie gequantiseerd en vereenvoudigd (`optimize --texture-size 512 --compress quantize`, daarna `simplify --ratio 0.35 --error 0.002`). Van 9,09 MB / 83.628 driehoeken naar 2,35 MB / 25.320. Het origineel stond ver boven het budget van 10-15k driehoeken, en dit type is ongeveer een zesde van alles wat er spawnt. * `helicopter.glb` is vereenvoudigd met gltf-transform (`weld`, daarna `simplify --ratio 0.08 --error 0.002` en `prune`). Van 8,48 MB / 290.856 driehoeken naar 576 kB / 26.488. Bijna al die driehoeken zaten in het landingsgestel: twee kuffen van elk 89.856 driehoeken plus een dwarsbalk van 74.112, samen 254.000 van de 291.000 — glad getesselleerde buizen aan een ding dat je als stip boven de stad ziet. Hij vliegt met z'n drieën rond boven Thousand Doors (`losangeles.js::buildHelis`); de hoofd- en staartrotor worden er op VORM uit gevist (bounding box, niet op naam — alle onderdelen heten `Object_N`) zodat ze los kunnen draaien. * De drie BOSSMODELLEN zijn opnieuw ingepakt met gltf-transform 3.10 (`optimize --texture-size ... --compress quantize --simplify false`, daarna `simplify` met een eigen ratio per model). De originelen staan niet in de repo: | Bestand | Van | Naar | Texturen | |---|---|---|---| | belegeraar.glb | 37,4 MB / 170.589 driehoeken | 4,2 MB / 32.985 | 54 × 256 px | | fighter.glb | 6,2 MB / 25.744 | 3,0 MB / 19.375 | 8 × 512 px | | zwermmoeder.glb | 19,6 MB / 344.103 | 2,8 MB / 55.384 | 4 × 512 px | De zwerm-moeder blijft hoog: het is een gladde schotel zonder detail om weg te gooien, en onder 55k gaat de rand zichtbaar hoekig staan. Dat mag hier, want een boss staat alleen in beeld. * `belegeraar.glb` is GERIGD (53 delen, 33 skeletons) en gaat daarom langs een eigen laadweg: `loadModel(..., { keepRig: true })` voegt hem NIET samen, want MergeMeshes gooit het skelet weg en dan is een skinned mesh een klomp (222 × 1526 × 437 units, gemeten). Zie `models.js::riggedTemplate`. * De overige drie zijn onbewerkt overgenomen. Deze map bevat twee soorten modellen: een CC0-assetkit die als vulling begon, en zelf aangeschafte/gedownloade modellen die de belangrijkste daarvan vervangen. ## Kenney Space Kit (2.0) — CC0 `hangar_roundA.glb`, `satelliteDish_large.glb`, `machine_generatorLarge.glb` en `turret_single.glb`. De acht `craft_*.glb` kwamen hier ook vandaan. Ze zijn op 2026-09-12 uit de repo gehaald omdat ze nergens meer werden aangeroepen — de vijanden die ze ooit voorstelden hebben allang hun eigen model (zie bovenaan dit bestand). Ze staan in `C:\wamp64\archief\cosmic-survivor\modellen-ongebruikt-2026-09-12`. - Bron: https://kenney.nl/assets/space-kit - Auteur: Kenney (www.kenney.nl) - Licentie: **CC0 1.0 Universal** (publiek domein) — https://creativecommons.org/publicdomain/zero/1.0/ - Vrij te gebruiken in persoonlijke, educatieve én commerciële projecten. Naamsvermelding is niet verplicht, maar wel netjes — vandaar dit bestand. ## Zelf toegevoegde modellen — LICENTIE NOG INVULLEN `spaceship_fighter_4mb.glb`, `bob_lazar_ufo.glb`, `swarmer_opt.glb`, `mini_opt.glb`, `spaceship_corridor.glb`, en de afgeleiden `london_tower.glb` (van `free_london_skyscraper.glb`) en `datacore_room.glb` (van `scifi_room.glb`). **De bronbestanden staan sinds 2026-09-12 niet meer in de repo.** `free_london_skyscraper.glb`, `scifi_room.glb`, `office_building.glb`, `server_room_high_poly.glb`, `office_tower.glb` en `server_room.glb` werden nergens meer aangeroepen — samen 125 MB die bij elke deploy meeging — en staan nu in `C:\wamp64\archief\cosmic-survivor\modellen-ongebruikt-2026-09-12`, plus in de git-geschiedenis. De recepten verderop in dit bestand blijven kloppen; je hebt alleen de originelen uit het archief nodig om ze opnieuw te draaien. De licentie van het origineel geldt onverkort voor de afgeleiden die we wél uitleveren, en daarom blijft de vermelding hieronder staan. De `_opt`-varianten zijn afgeleid van `swarmer.glb` en `mini.glb`; die originelen staan hier niet meer (zie de sectie hieronder), maar de licentie van het origineel geldt onverkort voor wat we ervan gemaakt hebben. > **Te doen:** vul per model de bron en de licentie in. Dit is geen formaliteit: > zodra het spel publiek staat, is "ik weet niet meer waar dit vandaan komt" een > probleem dat je niet achteraf oplost. Let vooral op modellen die alleen voor > persoonlijk gebruik gelicentieerd zijn. | Bestand | Bron | Licentie | |---|---|---| | spaceship_fighter_4mb.glb | ? | ? | "SpaceShip Fighter - Version 2 [MESHY 6]" (https://skfb.ly/pKIxJ) by Ethan Cox is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | bob_lazar_ufo.glb | ? | ? | "Bob Lazar Ufo" (https://skfb.ly/6URJp) by Batuhan13 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | swarmer.glb | ? | ? | free, no restrictions | mini.glb | ? | ? | "Skyward DDF Poco Ship" (https://skfb.ly/6DCMw) by Simon Jennings is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | spaceship_corridor.glb | ? | ? | "Spaceship Corridor Challenge" (https://skfb.ly/6VVOp) by June is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | free_london_skyscraper.glb | ? | ? | "[FREE] London Skyscraper" (https://skfb.ly/pKVG9) by 99.Miles is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | office_building.glb | ? | ? | "Office building" (https://skfb.ly/6SNSI) by Nikitos & 3130 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | server_room_high_poly.glb | ? | ? | "Server room (high poly)" (https://skfb.ly/ouMN9) by Scarecrow_original is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). | scifi_room.glb (gedownload als `sci-fi_room.glb`) | ? | ? | "Sci-fi Room" (https://skfb.ly/6SMv7) by mertaltintas is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). ## `london_tower.glb` — het baken Afgeleid van `free_london_skyscraper.glb` (One Blackfriars). Het origineel is 30,3 MB met 142.742 driehoeken, 60 primitives en vijftig textures van 1024² — samen ongeveer 280 MB videogeheugen voor één gebouw. Wat er nu staat: | | origineel | `london_tower.glb` | |---|---|---| | bestand | 30,3 MB | **8,1 MB** | | driehoeken | 142.742 | **88.904** | | primitives (draw calls) | 60 | **20** | | textures | 50 × 1024² PNG/JPEG | 50 × 512² WebP | Opnieuw genereren: ```bash cd assets/models3d npx @gltf-transform/cli@3 join free_london_skyscraper.glb _t1.glb npx @gltf-transform/cli@3 simplify _t1.glb _t2.glb --ratio 0.3 --error 0.004 npx @gltf-transform/cli@3 resize _t2.glb _t3.glb --width 512 --height 512 npx @gltf-transform/cli@3 webp _t3.glb london_tower.glb --quality 82 ``` `join` is hier de belangrijkste stap: die voegt meshes met hetzelfde materiaal samen en bracht de draw calls van 60 naar 20. Bewust géén `--ratio` lager dan 0.3: bij een gebouw zie je decimatie het eerst in de raamlijnen. **De verhoudingen van dit model staan in de server.** `CityField::LANDMARK_SHAPE` is de opgemeten wereldmaat (37,2 × 166,9 × 50,9) en daar wordt de botsingsdoos uit gerekend — breedte, diepte én hoogte, met één uniforme schaal. Vervang je het model, dan hoort die constante mee te veranderen; `city.js` meldt bij het laden in de console wat het echt gemeten heeft en waarschuwt als het meer dan 6% van de doos afwijkt. Doe je dat niet, dan bots je zichtbaar naast de gevel. Hier stond een procedurele mast bovenop, omdat het gebouw op eigen verhouding net niet tot het speelplafond kwam en een baken waar je overheen kunt vliegen geen baken is. Die is eruit: in plaats van er een paal op te zetten is de toren zelf ruimer geschaald (`CityField::LANDMARK_FOOT`), waardoor hij op eigen verhouding door het plafond prikt. De mast liep zichtbaar door het model heen. ## `office_tower.glb` en `server_room.glb` Hetzelfde verhaal als bij de toren: beide originelen zijn te zwaar om zo te gebruiken. | | office_building | **office_tower** | server_room_high_poly | **server_room** | |---|---|---|---|---| | bestand | 36,2 MB | **14,6 MB** | 34,0 MB | **5,4 MB** | | driehoeken | 643.445 | **358.035** | 1.086.773 | **247.192** | | primitives | 57 | **13** | 20 | 20 | ```bash cd assets/models3d # kantoor npx @gltf-transform/cli@3 join office_building.glb _o1.glb npx @gltf-transform/cli@3 weld _o1.glb _ow.glb npx @gltf-transform/cli@3 simplify _ow.glb _o2.glb --ratio 0.1 --error 0.02 npx @gltf-transform/cli@3 quantize _o2.glb _o3.glb npx @gltf-transform/cli@3 webp _o3.glb office_tower.glb --quality 85 # serverzaal npx @gltf-transform/cli@3 weld server_room_high_poly.glb _s1.glb npx @gltf-transform/cli@3 simplify _s1.glb _s2.glb --ratio 0.2 --error 0.02 npx @gltf-transform/cli@3 quantize _s2.glb _s3.glb npx @gltf-transform/cli@3 webp _s3.glb server_room.glb --quality 85 ``` Twee dingen om te weten: **`simplify` geeft bij het kantoor niet mee.** Het blijft op ~358k hangen, wat je ook aan `--ratio` of `--error` draait, ook na `weld`. Dat komt doordat het model uit veel losse stukjes bestaat (raamlijsten, railingen) waar bijna elke driehoek op een rand ligt en dus niet mag verdwijnen. Het is opgelost door `quantize` erachteraan te zetten: die haalt de bestandsgrootte alsnog omlaag (23 → 15 MB) zonder driehoeken weg te gooien. 358k voor één gebouw in de hub is te doen — daar wordt niet gevochten. **`quantize` maakt de accessor-maten onbruikbaar.** Na quantiseren lopen de posities van -32767 tot 32767 met een schaal op de node, dus `glbinfo` toont een bbox van 65534 units. De code meet de WERELD-bbox na het laden en trapt daar dus niet in — maar meet nooit iets aan een gequantiseerd bestand met een offline tool. ## `datacore_room.glb` — de datakern Afgeleid van `scifi_room.glb` ("Sci-fi Room"). Het origineel is met 77k driehoeken en 4,8 MB al bruikbaar; wat er nog aan gedaan is, is vooral het aantal draw calls. | | scifi_room | **datacore_room** | |---|---|---| | bestand | 4,79 MB | **2,69 MB** | | driehoeken | 77.362 | **77.362** | | primitives (draw calls) | 59 | **16** | | materialen | 11 | **8** | ```bash cd assets/models3d npx @gltf-transform/cli@3 join scifi_room.glb _d1.glb npx @gltf-transform/cli@3 dedup _d1.glb _d2.glb npx @gltf-transform/cli@3 quantize _d2.glb datacore_room.glb ``` Bewust GEEN `simplify`: 77k voor één zaal waar niets rondvliegt is prima, en de pilaren en het vloerpatroon zijn precies het soort geometrie waar een decimator zichtbaar in snijdt. Drie dingen om te weten als je hier een ander zaalmodel in hangt: **Er zitten drie personages in, en die staan uit.** Ze staan met hun drieën pal om de console (één er dwars door), ze zijn helder wit en dus het eerste waar je oog naartoe gaat, en er zit geen animatie in het bestand — drie stilstaande poppen in je accountscherm dus. `terminal.js` zet elke mesh met een skelet uit; één regel weghalen zet ze terug. **Culling blijft AAN in deze zaal.** Bij de gang staat backface culling uit, omdat je daar van binnenuit tegen enkelzijdige wanden aankijkt. Deze zaal is uit massieve vormen opgebouwd, en met culling uit kijk je door de holo-bol heen ook tegen de binnenkant van zijn achterste helft aan — dat rendert als een schaakbordpatroon van panelen dat er niet hoort te zijn. **De kamer wordt opgemeten, niet uitgerekend.** Dit model is een kamer van 249 units met een vloerdek van 778 eromheen. De camera-afstand uit de bounding box of uit de zaalhoogte halen zet hem dus buiten de pilarenkrans, met een zuil pal voor de lens. `terminal.js` meet daarom per onderdeel de horizontale maat op en laat één duidelijke uitschieter (het dek) buiten de meting; de camera-afstand is een fractie van wat er dan overblijft. Wat NIET werkt, en dat is het vermelden waard omdat het de logische eerste ingeving is: raycasts vanaf de as naar buiten om de wand te vinden. Deze kamer heeft geen doorlopende wand — acht pilaren met gaten ertussen — dus vliegen de meeste stralen er dwars tussendoor en raken alleen nog de console in het midden. Een straal kan geen wand vinden die er niet is. ## Waarom glTF/GLB glTF is een open standaard die door élke moderne 3D-engine gelezen wordt (Babylon.js, three.js, PlayCanvas, Godot, Unity, Unreal). Mochten we ooit van renderer wisselen, dan verhuizen deze modellen ongewijzigd mee — alleen de laadcode verandert. Dat is precies waarom de content hier bewust NIET in een engine-specifiek formaat staat. ## De `*_opt.glb`-varianten `swarmer_opt.glb` en `mini_opt.glb` zijn met [glTF-Transform](https://gltf-transform.dev) uit het origineel gemaakt: minder driehoeken (`simplify`) en textures op 512² (`resize`). **De originele `swarmer.glb` en `mini.glb` staan hier niet meer** — samen 17 MB voor bestanden die het spel niet gebruikt. Wil je opnieuw decimeren met andere instellingen, dan moet het origineel dus opnieuw gedownload worden; de bronnen staan in de licentietabel hierboven. Waarom dit nodig was: een spelerschip zie je één keer, een swarmer twintig keer tegelijk. `mini.glb` had 195.764 driehoeken; met acht in de lucht is dat al 1,6 miljoen driehoeken per frame voor één vijandsoort. | | origineel | `_opt` | |---|---|---| | swarmer | 72.677 driehoeken · 8,7 MB | 17.174 · 3,1 MB | | mini | 195.764 driehoeken · 8,9 MB | 35.662 · 1,8 MB | Opnieuw genereren (Node 20.11 kan alleen v3 van de CLI): ```bash cd assets/models3d npx @gltf-transform/cli@3 simplify mini.glb _t.glb --ratio 0.05 --error 0.03 npx @gltf-transform/cli@3 resize _t.glb mini_opt.glb --width 512 --height 512 ``` Beter dan decimeren is het model meteen laag-poly exporteren uit de bron (meshy.ai heeft daar een instelling voor): een decimator raadt welke driehoeken mogen verdwijnen, de exporteur weet het. ## Welk model hoort bij wat `ENEMY_TYPES` in `assets/js3d/protocol.js` (veld `model`) plus `PLAYER_MODEL` zijn de bron van waarheid. Ontbreekt een model of laadt het niet, dan valt de renderer terug op de procedurele vormen — het spel blijft dus speelbaar zonder deze bestanden. | Bestand | Gebruikt voor | |---|---| | spaceship_fighter_4mb.glb | Spelerschip | | swarmer_opt.glb | Swarmer | | bob_lazar_ufo.glb | UFO | | frost_wisp.glb | Frost Wisp | | azure_drone.glb | Azure Drone | | purple_cube.glb | Purple Cube | | crystal_lancer.glb | Crystal Lancer | | mini_opt.glb | Mini Bot | | hangar_roundA.glb, satelliteDish_large.glb, machine_generatorLarge.glb, turret_single.glb | Dakdetails in de stad | | london_tower.glb | Het baken van élk gebied; in NEXUS is dat ook de toren waar de hangar aanmeert | | belegeraar.glb | De boss van het type BELEGERAAR (`BOSS_KINDS.siege`) | | fighter.glb | De boss van het type JAGER (`BOSS_KINDS.hunter`) | | zwermmoeder.glb | De boss van het type ZWERM-MOEDER (`BOSS_KINDS.swarm`) | | helicopter.glb | De drie helikopters boven Thousand Doors | | datacore_room.glb | Het interieur van de datakern (`assets/js3d/terminal.js`) | | spaceship_corridor.glb | De gang: het inlog-/registratiescherm (`assets/js3d/corridor.js`) | | *(nog geen)* | Hangar-interieur — zie `HANGAR_MODEL` in `assets/js3d/shop.js`; nu procedureel | ## Nog te zoeken Dit is de boodschappenlijst. Alles hieronder werkt nu al met een terugval (procedurele vorm of een Kenney-schip), dus het spel is niet stuk zonder — het ziet er alleen minder uit dan het kan. **Bovengrens voor alles: ~10-15k driehoeken en textures van 1024².** Daarboven eerst decimeren (zie hierboven). Een schip zie je één keer, een vijand twintig keer tegelijk: bij vijanden is die grens dus geen richtlijn maar een grens. ### Vijanden, op volgorde van hoe vaak je ze tegenkomt Percentages zijn geteld over alle 220 golven in de 30 gebieden (3.978 vijanden). | Type | Aandeel | Nu | Radius | Waar het op moet lijken | |---|---|---|---|---| | Azure Drone | 16,5% | `craft_miner` | 24 | Log, gepanzerd, tráag — een werkbot met een kanon. Hoekig, industrieel, koud blauw. Mag er zwaar uitzien; hij is het meest voorkomende doel dat terugvuurt. | | Frost Wisp | 15,9% | `craft_racer` | 15 | Klein, spits, snél (300 u/s, sneller dan jij). Geen wapens — hij ramt. Denk: sliert, scherf, ijssplinter. IJl silhouet, geen dikke romp. | | Crystal Lancer | 9,1% | `craft_cargoB` | 30 | De grootste gewone vijand. Langwerpig met één duidelijke loop of stekel naar voren. Kristallijn, hoekig, scherp. Moet er dreigend uitzien op afstand. | | Purple Cube | 9,0% | `craft_cargoA` | 26 | Blokkig, massief, traag (95 u/s). Letterlijk een kubus mag — als het geometrisch en onnatuurlijk leest, is het goed. Geen vloeiende vormen. | | **Boss** | 60× in de wereld | **niets — procedureel** | **140** | Vult je scherm: bijna vijf keer een Crystal Lancer. Silhouet is hier belangrijker dan detail: het moet in één oogopslag "dit is de baas" zeggen. Ringen, armen, een duidelijk midden om op te schieten. Hier mag het polygoonbudget rúimer (30-40k), want er staat er nooit meer dan één. | Begin bij de Azure Drone en de Frost Wisp: samen 32% van alles wat je tegenkomt. De boss daarna — die is maar 1,5% van de aantallen, maar hij domineert het beeld als hij er is. ### Hangar-interieur De winkel is een eigen 3D-scene (`assets/js3d/shop.js`) en nu procedureel gebouwd: een ronde bay met acht pilaren, lichtstrips, een draaischijf en één raam op de sector. Dat is compleet, maar een echt model is beter. Waar je op moet zoeken — zoektermen: *hangar interior*, *spaceship hangar bay*, *sci-fi garage*, *docking bay*, *starship maintenance bay*. - **Een ruimte, geen gebouw.** Het moet van binnenuit te bekijken zijn (normalen naar binnen, of in elk geval een dak en muren die je niet doorkijkt). - **Rond of vierkant maakt niet uit, maar er moet een open midden zijn** van minstens 100 units doorsnede voor het schip op de draaischijf. Het schip is 62 units lang. - **Verticale herhaling** — pilaren, ribben, panelen. Dat is wat een ruimte schaal geeft; een gladde koepel voelt maatloos. - **Industrieel en werkend**, niet clean-showroom: kabels, leidingen, kratten, werklampen. Dit is de plek waar je schip gerepareerd wordt. - **Eigen licht mag ontbreken.** De scene zet zijn eigen lampen; een model met ingebakken lichtmappen werkt tegen. - Textures mogen hier ruimer (2048² voor één ruimte is prima), driehoeken tot ~60k, want er staat maar één hangar en er vliegt niets rond. Heb je er een: zet de bestandsnaam in `HANGAR_MODEL` in `assets/js3d/shop.js`. De procedurele bay blijft als terugval staan. ### Voor later: gebouwen en steden Losse gave gebouwen en complete stadsmodellen. Dit staat bewust op de lijst en niet in de code, omdat het één ding is dat we eerst moeten uitzoeken: de stad bestaat nu uit *thin instances* van ~30 driehoeken per gebouw, 80-150 gebouwen per gebied, en dat houdt de hele skyline op een handvol draw calls. `free_london_skyscraper.glb` liet het probleem zien: 30,3 MB, 142.742 driehoeken, 60 primitives. Als één landmark per gebied kán het — dat is precies wat er nu mee gebeurt, na decimatie naar `london_tower.glb` (zie hierboven). Als gewóón gebouw kan het niet: tien exemplaren is 890.000 driehoeken tegen de ~4.500 die de hele huidige skyline kost. Wat er dus uitgezocht moet worden voordat we hier modellen in gaan hangen: welke gebouwen als instance kunnen (één mesh, veel exemplaren), welke als uniek landmark, en of we losse gebouwen willen of complete stadsblokken die de huidige celverdeling van `CityField` vervangen. ## Twee valkuilen bij een nieuw model 1. **Oriëntatie en maat staan in `protocol.js`, niet in de laadcode.** `MODEL_YAW` draait de neus naar +Z, `MODEL_SCALE` corrigeert de maat. Meet de neus op (het smalle uiteinde van de romp) in plaats van te gokken. 2. **Een schijfvormig model wordt te groot getekend.** De loader schaalt de langste horizontale maat op `radius × 5.2`; bij een schotel is dat de dóórsnede, en dan staat er een schijf van 114 units om een hitbox van 44. Zet `MODEL_SCALE` op ~0.6 zodat de verhouding op ~1,55× uitkomt, gelijk aan het spelerschip.