* Speed up property model viewer loading
- Render the viewer on demand (load completion, camera change, resize)
instead of every animation frame
- Share the UGC brick geometry cache and parsed Materials.xml across all
UGC models on the converter
- Serve model and ldddb files with strong ETag/Last-Modified validators
and conditional requests so repeat visits get 304s
- Gzip model and ldddb responses (COMPRESS_MODEL_RESPONSES), optionally
storing .gz copies on disk (CACHE_COMPRESSED_MODELS)
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Add configurable redraw behaviour while property models load
PROPERTY_VIEWER_LOAD_REDRAW ('none' or 'interval') and
PROPERTY_VIEWER_REDRAW_INTERVAL_MS control whether the property viewer
redraws as individual models finish loading. A final draw always happens
once all loads complete or fail, and a loading indicator is shown
meanwhile. Also wrap long lines added by this branch for flake8.
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Show model loading progress in the property viewer
Replace the static 'Loading models...' badge in the upper-left corner with
a progress indicator, e.g. 'Loading models: 42% (89/203)', counting loaded
and failed models out of the total. When everything is done it shows
'Loaded 100% (203/203)' (with '(N failed)' if any), turns green (orange on
failures) and fades to a faint badge after a few seconds.
The indicator is a DOM element updated at most once per animation frame,
so it does not trigger canvas redraws in either redraw mode.
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Make the loading progress badge more opaque and readable
Use a ~93% opaque background for all states (dark while loading, darker
green when done, orange on failures), bold white text with a subtle text
shadow, border and drop shadow, and only fade to 70% opacity after
completion so it stays legible over colourful models.
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Batch UGC models and load the viewer's brick data asynchronously
Brick-built (UGC) models used to be loaded with synchronous XHRs on the
main thread: one request per model's LXFML plus one per brick primitive
file, strictly one after another, with the page frozen meanwhile. On a
remote server every request costs a network round trip, so a property
with 654 UGC models took about two minutes to load.
- New route properties.get_ugc_models/<property id> (login required, like
get_model) returns all UGC LXFML of a property as one JSON object
({content id: lxfml}) from a single DB query. It is gzip-encoded per
COMPRESS_MODEL_RESPONSES and has an ETag over the whole set, so repeat
loads get a 304. The per-model get_model route keeps working.
- The viewer lists the brick database, fetches the LXFML (batched on
view_models, per model with bounded concurrency otherwise) and then each
needed brick primitive exactly once with at most 8 requests in flight,
all with async fetch. Meshes are then built in short slices so the page
stays responsive. The progress badge counts brick-built models as they
are built and shows the fetch stage (e.g. 'bricks 120/385').
- Loading finishes once both the loading manager (pre-built models,
ground texture) and the brick-built models are done; redraw modes are
unchanged ('none' draws once at the end, 'interval' at most every N ms).
- get_model for a UGC model only decompresses the LXFML when it has to
send a body (a matching If-None-Match skips it).
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Show the prebuilt material stage on the loading badge
While prebuilt models load, the badge now shows how many MTL files have
arrived (e.g. 'materials 120/203'), so progress moves from the start on
prebuilt-heavy properties instead of sitting at 0% until the first OBJ
loads.
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
* Show a rendering state on the badge before the final render
The final full render can block the main thread for a second or more on
large properties, which made the badge look frozen. Once all loads are
done the badge now shows 'Rendering N parts…' with a CSS spinner (a
transform animation the compositor keeps running), waits two animation
frames so it is painted, then renders and shows the Loaded state.
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
---------
Co-authored-by: bob-g-code <346622190+bob-g-code@users.noreply.github.com>
Co-authored-by: HailStorm32 <demetriv.s.7@gmail.com>
prebuilt() only split render_asset on an escaped double backslash and then on
'/', so paths using single backslashes (mesh\ftcommunityitems\x.nif) kept
their directories in the file name. The LXFML lookup then failed and
get_model returned 500. Extract the base name by splitting on any run of
backslashes or forward slashes and lowercasing it. Also return 404 instead
of an unusable string when a LOT has no render asset.
Co-authored-by: bob-g-code <346622190+bob-g-code@users.noreply.github.com>
With several gunicorn workers and an empty cache, every worker ran the
brickdb unpack at once and workers crashed with FileExistsError on
app/cache/brickprimitives. Serialize it with a file lock, extract to a temp
dir and move into place, and mark completion with a marker file.
Co-authored-by: bob-g-code <bob-g-code@users.noreply.github.com>
UGC LXFML brick transforms are relative to the model's own origin, but the
LDD viewer added the bricks straight to the scene and never applied the
model's properties_contents position/rotation, so the models ended up around
(0,0,0), far below the property ground plane. Properties with only
brick-built models showed just the green ground.
Build each LXFML into its own THREE.Group placed with the saved
position/quaternion (like pre-built models), load each file into a fresh
Scene, and target the single-model camera at the placed model.
Co-authored-by: bob-g-code <346622190+bob-g-code@users.noreply.github.com>
Deleting an account crashed with IntegrityError (ugc.character_id cannot be
null) and left the account half-deleted. Remove all dependent rows explicitly
with bulk deletes in a single transaction, and roll back on failure.
Co-authored-by: bob-g-code <bob-g-code@users.noreply.github.com>