Greyfen environment and equipment¶
The editable source is art/blender/vyrgarde_environment_v1.blend. Its Greyfen environment scene places the shared collections in the actual zone layout; Asset library holds origin-aligned reusable assets. The browser exports are client/public/models/environment/environment.glb and manifest.json.
The library contains 40 assets: buildings, market/well, woodland and rocks, camp structures, shrine/gate, and sword, mace, bow, staff and arrow. It uses one shared surface atlas, three textures and 75,435 exported triangles across the whole library (6,585,176 bytes). Each static prefab is one draw per spatial instance bucket. The shrine core, gate rubble, staff crystal and bow limbs remain separate because they move or change state.
Rebuild¶
Run from the repository root, with Blender 4.5:
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup --python-exit-code 1 --python tools/blender/create_environment_models.py
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup --python-exit-code 1 --python tools/blender/refine_camp_landmarks.py
npm run validate:environment
The first script prepares the world overview camera and lighting. Render the Greyfen environment scene in Blender to inspect the source. prepare_environment_scene.py can also produce art/blender/previews/blender-village-overview.png. Reload the browser after replacing a GLB; the library is cached for the life of a page.
Rendering and animation¶
environmentAssets.ts loads once, shares geometry/textures, and preserves procedural scenery if the library cannot load. Repeated scenery is split into 40 m buckets for frustum culling. Grass uses small tapered blades in 28 m buckets, GPU wind and a distance fade. Chimney/campfire smoke has a fixed GPU particle budget. Only the nearest four local point lights are enabled. The sun shadow follows the player within a smaller area to retain detail at the existing 1024 map size.
rigBatching.ts converts the existing rigid Blender character joints into one skinned mesh per detail level. Vertices retain their original joint ownership; animation timing and interpolation are unchanged. Geometry is shared between instances, while skeletons and flash materials remain independent. The original near/far geometry and animation throttling still apply. A deformation regression compares batched vertices with their original articulated meshes through rotation, root movement, scale and multiple poses.
Weapons keep their original grip sockets and authoritative attack timing. The new bow flexes its two limbs, attaches the string to their moving tips and settles after release. Sword/mace motion uses a bounded trail buffer. The shared 600-particle combat pool now respects particle size and lifetime opacity and renders soft radial particles instead of squares.
NPCs and interaction¶
The five service NPCs stand clear of building footprints and use the game's -Z forward convention. Their authored yaw remains editable. Four named ambient residents are generated for the starter village in shared/src/npcs.ts; an explicit NPC spawn with the same ID overrides a generated resident.
Optional npc_spawn properties:
Routes are validated against all static colliders, including the closing segment and the route from the spawn point. Blocked custom routes fall back to stationary behavior; blocked ambient routes are shortened or omitted. NPC spawn markers do not leave static collision obstacles behind. Movement is authoritative and appears in normal snapshots; NPCs pause and turn toward nearby players.
Quest icons come from each player's real quest definitions and progress: gold ! for available, ? for ready to turn in, muted dot for active. Locked prerequisites and completed quests produce no offer icon. Interaction favors the NPC in front of the player, avoiding accidental conversations with a closer resident behind them. Dialogue releases the mouse for quest cards and supports G, a close button, Escape and distance-based dismissal.
Verification¶
npm run typecheck
npm run build
npm run test:characters
npm run test:environment
npm run validate:characters
npm run validate:environment
The protocol checks accept WS_URL, allowing isolated test saves:
WS_URL=ws://127.0.0.1:8091 node tools/questtest.mjs
WS_URL=ws://127.0.0.1:8091 node tools/pvebot.mjs
WS_URL=ws://127.0.0.1:8091 node tools/smoketest.mjs
Screenshots and verification reports are under art/blender/previews. Frame-rate measurements are local browser observations, not a guarantee for other hardware. The existing Vite shared-chunk size advisory remains; the production build completes successfully.