So, I should probably mentioned that I’ve used Webpack in my day job for the last seven years, and have even written a custom plugin for it. I’ve also used rollup, parcel and snowpack. In fact, my current side-project uses snowpack for the game editor, since the faster build times are important with a large code base. Although, sadly, I have not figured out how to get hot reloading working with three.js - that is, if I change some code in a React component, it hot reloads just fine, but if I edit a shader or a class that generates meshes, the game just hangs, so for now I have hot reloading disabled.
The problem with that approach is that you lose a lot of control over loading lifecycles. For one thing, three.js resources need to be properly disposed - they can’t just be garbage collected. This is because buffers and shaders allocate resources on the CPU, and if you want your app to run for more than 30 minutes you better not leak this stuff. So you need an explicit unload mechanism - I tend to use the lru-cache package for this, that lets me tweak and tune how long the resources live in memory.
The game world is organized in to a grid of cells, each being 16 x 16 meters - there are thousands of cells in a typical overland map. The resources for each cell are fetched via window.fetch, with an API path like
/api/scenery/<realm>/<cell-x>/<cell-y>. The server (written in node.js/Koa) also supports area queries, so you can load the entire surrounding terrain in a single HTTP request. Some of these resources are .glb files, but a lot of it is game data that is encoded in msgpack format - another task that the build pipeline needs to accomplish. So for example, the metadata for an actor is a msgpack structure that includes character behavior data, color remappings, and so on - as well as a reference to a .glb skin and armature, which are loaded separately since they are shared between many actors.
I haven’t gone too far into mesh vertex optimization yet, although I may reach that point. I’ve made the deliberate choice to do most of my coding on a 2015 MacBook Air, which gives me a framerate of about 20 fps - but when I run it on a more modern machine, I get a smooth 60fps. The reason for coding it on the older platform is to prevent me from getting too fancy with the graphical detail, I basically keep adding features until I can’t stand the frame rate, and then go optimize for a while until I feel comfortable again
I’m also experimenting with “reactive game scripting” - taking the basic ideas of systems like MobX or Recoil and applying them to character and scenery behavior rather than HTML widgets. (I have authored a number of obscure game scripting languages, the primary one being SAGA - Scripts for Animated Graphic Adventures, which is still supported by the Scrumm VM.)
Reactive scripting means that scripts are not written in an imperative language like Lua/Python/C#/Papyrus/etc. but are more like a spreadsheet formula, one that recalculates automatically when its dependencies change - so it’s relatively easy to attach an expression like “door.open = button1.active” and have the door open or close whenever the toggle button changes state - without all that tedious mucking about with subscribing and unsubscribing to event channels. I’m in the middle of constructing a general framework for this, which I call “reflex”.