Ray tracing geometry over budget in Unreal Engine 5.8, and the fix that emptied every HISM on the street
Fifteen photographs at lunchtime
Second Chance has 546 terraced houses on 28 streets in Radworth, and by Thursday morning every one of them had its own brick, its own door colour and its own weathering. That pass had gone in the day before. It worked and it was not enough. Seen from the pavement the street still read as a row of the same house painted different colours, because variation in colour is not what makes a real terrace look like a real terrace. What makes it look real is the clutter bolted to the front of it.
So at lunchtime James went out with his phone and photographed fifteen frontages.
The photographs were not for tracing. They were for counting. The agent – Claude Code Opus 5, which does most of the driving here – went through them and worked out how often each fitting actually appears, and those percentages went straight into the builder as the odds of a house getting one. Gutter and fascia board: on essentially every frontage in the set, and completely absent from the game, which had left every downpipe in Radworth running up to nothing at the top. TV aerial: 48 per cent, about 24 of them across 12 of the 15 photographs, and there were none at all in the project. Satellite dish: 46 per cent. Alarm bell box: 18 per cent. Air brick: 24 per cent. Meter box: 16 per cent. A doorbell on one house in eight, a camera on one in sixteen.
That is the whole argument for building a game in a town you actually walk through, and it is a straight difference from GTA 6. Rockstar have people whose job is reference, and a budget for sending them places. He had a snap break and a phone. What he came back with was a set of numbers nobody could have guessed, and the giveaway is the aerials: had anyone been asked to estimate, they would have said aerials were the dead thing and dishes were the live one. On those fifteen frontages they were level. A street with dishes and no aerials reads as wrong to a British player without them being able to say why.
By half four the detail was going in level-wide. Every house got string courses, a gutter, a fascia board, a downpipe with a swan-neck hopper, a chimney stack with between one and four pots at deliberately unequal heights, and whichever of the small fittings its dice came up for. 7,444 pieces of geometry across the level, and not one of them a new actor.
Six hundred and thirty-one hosts, and why not 28
Seven thousand extra actors on a World Partition map is a bad trade, so the detail went in as instanced meshes instead. Which meant finding out how to build one from Python, and the first finding was that the obvious way does not exist: add_component_by_class and add_component are both absent from Actor in UE 5.8's Python API. Not deprecated, not renamed. Absent. The agent probed for them before writing a line of the builder, which is the one thing that went right early.
The way that works is to spawn from a Blueprint that already carries the component, then reach in and feed it transforms.
The second decision was less obvious and more interesting. Keying the hosts on mesh and material alone gives 28 of them, which is a nicer number. It is also wrong. Instances are added in world space, so 28 hosts sitting at the origin would each have bounds stretching across the entire map – World Partition could never stream one out, and the HLOD builder would have nothing local to cluster. Keying on street as well gives 631 hosts whose bounds are about one grid cell each, and that is the version that shipped. It is only a cheap trade because these hosts do not tick, and the thing this game is short of is game thread, not static actors.

Decorating a house nobody had looked at
Between those two, the day's worst hour.
Radworth Road has four medium houses on it as well as the terraces, and they had been left plain. The agent read the mesh's numbers out of the FBX, wrote a detail pass for them, spawned 57 actors and produced something clearly worse than nothing. Terracotta bay roofs and door canopies jutted off the wall as floating peach shelves with nothing underneath them. Window dressings sat flush and read as boarded-up openings. The hedge, which was a flat card, fell to pieces the moment the camera moved off its axis.
The cause was one assumption, made silently: that the medium house has projecting bay windows, because every terrace in the reference photographs does. It does not. Its ground floor is a flat wall with recessed openings, and everything built for a bay was hanging off thin air.
All 57 were deleted and the deletion saved. The script is still on disk with its clear-only switch set so it cannot spawn anything by accident.
The reusable part is the contrast with the terraces, where the agent had run a probe that flooded each material slot a different colour before touching anything – and that probe caught a misidentified slot before it shipped. On the medium houses it went from numbers straight to 57 spawned actors without ever looking at the geometry. The numbers were not wrong. The reading of what they described was.
The windows were built, placed and invisible
The window dressings – blinds, curtains, nets, a dark empty room – were the part meant to make one house feel lived-in and the next feel empty. They were built. They were correctly placed. They could not be seen at all, and James's verdict on the first capture was that it all still looked the same to him. He was right.
The agent blamed the glass, twice, and got as far as recommending a change to the shared glass material on the strength of it. That would have been a poor afternoon's work: the glass is translucent at 0.30 opacity with a pale blue tint and was never the problem. The actual cause is that nothing lights a building interior in this game, and the house material has no emissive parameter at all – only colour, roughness and a weathering strength. So every dressing resolved to the same near-black, and the window showed nothing but the sky reflected in it. The fix was a small dedicated material with colour wired to emissive, not a change to anything shared. Read the material before blaming it.
Which produced the opposite problem an hour later.

Emissive is the one term that does not scale with scene lighting, so a dim viewport grab always flatters it. The numbers had been judged on exactly that. The first proper high-resolution capture at a decent quality preset showed pillarbox red and primary green, on boxes that filled the whole pane. James's note was that the windows were far too colourful for real life and needed proper glass with proper curtains behind. Three passes had been spent recolouring, which was treating the symptom: a dressing that fills the opening has stopped being a window and become a painted board – no reflection, no depth, nothing to see through.
The answer was shape, not colour. A blind is a band across the top. Curtains are a strip down each side. Nets cover the pane but are inset so a rim of glass frames them, and they are dim enough to read as fabric rather than hardboard. Emissive came down to under a tenth of where it started and the albedos were desaturated. Shape reads at any distance, at any time of day, under any lighting; brightness needed retuning three times and colour twice.

Two houses on that street are pinned by hand and excluded from the random roll. One is the player's own, which they look out of – the dice had given it dirty glass and the playtest verdict was that he could hardly see out. The other is the house opposite, where a resident sits at the bay and the player is meant to notice her. Clear glass alone did not do it: a clean window onto an unlit room is a clean window onto a black void. It needed a light in the room, hung at seated height near the window, because the first attempt lit the ceiling and left the chair in the dark.
A dish that points at a real satellite
Evening, and two reports off a playtest. The satellite dishes were squares, which they were – a scaled engine cube, 62 cm on a side. And the street name plates had their posts running up through the plate, with the street name printed on the top face of the sign as well as the front.
The dish was authored headless in Blender so it never touched a scene James had open: 456 triangles, 60 by 55 cm. The detail that matters is baked into the mesh. A UK domestic dish is offset-fed, which means the reflector stands nearly vertical even though the satellite is about 24 degrees up, because the feed horn is offset by roughly 22 degrees. A dish physically tilted back 24 degrees looks wrong to anyone who has ever looked at a roofline, even if they could not tell you why. So the plate is tilted inside its own mesh and the boresight is the mesh's local forward axis, and the builder emits it with exactly the rotation it always used.
That rotation is not decorative either. UK dishes point at Astra 28.2°E, and from the latitude and longitude of the town the reference photographs come from that works out at about 147 degrees true and 23.7 degrees of elevation. World north in this project is yaw 20, set by where the day-night system puts the noon sun, so everything celestial – sun, moon, dishes – uses the same convention. And because these are front-facade mounts, a house only gets one if its front wall can actually see the satellite. On a north-facing terrace the real dish is round the back or on the gable, where the player never goes, so the honest thing is to fit none rather than aim one into its own brickwork. 128 dishes went on across 22 streets. 279 houses correctly got nothing.
The name plate turned out to be a lesson in cube UVs. A cube's faces each map the full 0 to 1 range, so one atlas row painted the street name on all six faces. The fix is two material slots – front face on its own, every other face plain white – rather than the UV trick of parking the other faces on a white patch of the atlas, which works at full resolution and goes grey at distance as the mipmaps average the black border in.
One trap on the way: Blender 5.1's FBX scale-units option does apply the metre-to-centimetre conversion itself, contrary to the project's own export notes, which say to apply it by hand. Both together landed the first dish in Unreal at 60 metres across. Re-importing and reading the bounds back is what caught it, and it is the only reason that did not reach the street.
Four hundred and fourteen megabytes of triangles
Then the playtest HUD put up a warning: ray tracing geometry, requested memory over budget, 414.325 MiB against 400 MiB.
Every instance contributes its own entry to the ray tracing acceleration structure, and roughly 13,000 centimetre-scale boxes bolted to facades is what tipped it. None of them contribute anything to a reflection or to bounced light – nobody has ever seen a meter box in a puddle. The correct fix is to take the detail and sign hosts out of the ray tracing scene entirely. Raising the budget in a console variable would have removed the warning and none of the memory, which on an 8 GB card is the same as doing nothing.
So: set visible_in_ray_tracing to false on those components, re-run the builders, done.
The fix that deleted the street
The rebuild reported that it expected several thousand instances and had got none. Every line.
component.set_editor_property("visible_in_ray_tracing", False) on an instanced static mesh component empties it. Before or after the instances are added, it makes no difference – setting it on a populated component deletes the contents. No error, no warning, no log line. It left all 659 detail hosts and all 56 street-sign hosts spawned and empty, on disk as well as in memory: every gutter, downpipe, chimney pot, aerial, dish and nameplate in Radworth, gone.
Worse, the standalone repair script had the same call in it, and that one runs over hosts that are already populated. It was written to count and to opt out, and it would have deleted about 13,000 instances while reporting success.
The agent bisected it rather than guessing, which was the right instinct after a day of guessing. Mobility was innocent. Collision was innocent. The property setter was not, and the method with the same name – set_visible_in_ray_tracing(False) – keeps every instance and correctly turns ray tracing off. Same component, same value, same outcome intended; one of them takes the instances with it.
There are two tells worth writing down. The first is already in the builders' own output: a report full of expected N instances, got 0 means the component was emptied, not that the transforms were wrong. The second works from outside the editor entirely – an empty host package on disk is a uniform 6 KB, where a populated one varies from 7 KB to 98 KB with its instance count. Grouping the packages by size tells you whether a build actually landed without loading the level at all.
Two other things bit during the same rebuild, both of them the same shape. Destroying an actor does not delete its package, so a clear pass that reported 112 removed left 168 files on disk and the actors came back. And the function that lists every actor in the level is blind to World Partition actors that are not currently loaded, so the clear pass had politely skipped every host that happened to be streamed out. Both builders now load by label first and collect the packages of everything they destroy.
Rebuilt and re-counted out of the level, not off the builder's own claim: 659 hosts, 12,996 instances, 28 name plates and zero engine cubes left on the street.

The HLOD is now stale again. It still bakes the old cube dishes and the old cube name plates into the distant view, which means walking down the street shows one thing and looking at it from the top of the hill shows another. That is a job for a morning with the editor closed.
The fault
Ray tracing geometry requested memory over budget after adding instanced facade detail
Playtest HUD, on an 8 GB card:
RAY TRACING GEOMETRY - REQUESTED MEMORY OVER BUDGET 414.325 MiB / 400 MiB
Cause: roughly 13,000 new instanced boxes and cylinders on house frontages. Every instance contributes its own entry to the ray tracing acceleration structure, whether or not it is visible in any reflection.
Raising r.RayTracing.Geometry.* budget cvars removes the warning and none of the memory. On a card this size that is worse than useless — it hides the thing that will crash later.
The fault
Every HISM instance disappears and the builder reports "expected N instances, got 0"
Symptom: the build reports !! <street>/<mat>: expected 412 instances, got 0 on every line; the hosts exist in the Outliner and are empty. The loss is written to disk, not just in memory.
Cause: component.set_editor_property("visible_in_ray_tracing", False) on an InstancedStaticMeshComponent or HierarchicalInstancedStaticMeshComponent empties the component. Before or after add_instance, no error, no warning. Bisected against a control:
| what was set | instances survive |
|---|---|
| nothing (control) | yes |
set_mobility(STATIC) | yes |
set_collision_enabled(NO_COLLISION) | yes |
set_editor_property("visible_in_ray_tracing", False) | no — component emptied |
set_visible_in_ray_tracing(False) | yes, and the flag reads back false |
The fix
Use the setter method, and guard the builder on its own instance count
component.set_visible_in_ray_tracing(False) keeps the instances and turns ray tracing off. The property form of the same name does not.
The guards now in place:
- both builders assert the instance count after every population step, and abort on a drop
- the standalone repair pass refuses to run if the count changes while it is running — it is
supposed to opt hosts out, never to write to them
- an empty host package on disk is a uniform 6 KB; a populated one is 7–98 KB. Grouping the
external actor packages by file size proves a build landed without opening the level
- the level census is refetched from the level, never taken from the builder's own report
The fault
A window dressing behind glass renders black, and blaming the glass costs an afternoon
Symptom: props built and placed correctly inside a building are invisible from outside. The window shows only the sky reflected in the pane.
Cause: nothing lights a building interior, and the shared house material has no emissive parameter — only colour, roughness and weathering. Every unlit prop resolves to near-black. The glass is innocent at 0.30 opacity with a pale blue tint. Read the material before changing it.
Two more from the same thread:
- Specular, not opacity, is what blocks a view through glass. At 0.12 opacity with full
specular the pane was still an opaque sheen. A multiplier on the rain function's specular output is what let the room show.
- Never tune an emissive against a dim or low-quality capture. Emissive is the one term
that does not scale with scene lighting, so a dark viewport grab always flatters it. Set the quality preset before judging, not after.
The fix
Put the variation in shape, not in brightness or colour
A dressing that fills the whole opening stops being a window and becomes a painted board. Blind = a band at the top. Curtains = a strip down each side. Nets = full pane but inset, so a rim of glass frames them. Unlit room = full pane, deliberately dark.
Shape reads at any distance and any time of day. Brightness needed three retunes and colour two. Scoring variation by "how many choices differ" is a lying metric — four pale rectangles under four different names are one result.
The kit
What was actually used
- Unreal Engine 5.8, Blueprint-only, on one 8 GB card.
- Blender 5.1, headless (
blender --background --python) to author the dish, so it never
touches a scene that is open on the desktop.
- Two editor MCP servers driving the editor from a script, which is how a change gets made
to 546 houses at once and how the bisect that found the ray tracing trap was run at all.
- Hierarchical instanced static meshes, keyed on street rather than on mesh, so World
Partition can still stream them and HLOD has something local to cluster.
- Fifteen photographs taken on a phone at lunchtime, used as a frequency survey rather than
as texture source.
Plain English
| Term | What it means |
|---|---|
| HISM | One object in the level that holds thousands of copies of the same model and draws them together. A street's worth of downpipes costs roughly what one downpipe costs. The hierarchical version also picks the right level of detail per copy. |
| instancing | Drawing many copies of the same object in one go instead of one at a time. The difference between a hundred bins costing one bin and costing a hundred. |
| World Partition | Unreal's system for very large maps. It chops the world into a grid and only loads the squares near the player. |
| HLOD | The same idea applied to whole groups of buildings at once, so a distant part of town is drawn as one cheap lump. |
| acceleration structure | The index the graphics card builds so it can work out what a traced ray hits without checking every triangle. It lives in video memory, every instance adds to it, and it has its own budget separate from textures. |
| bounds | The invisible box the engine considers an object to occupy. Streaming and culling decisions are made on the box, not on the model, so an object with a box the size of the map can never be streamed out. |
| material slot | A numbered surface on a model that takes its own material. One mesh can have several — brick on one, roof tiles on another — and the numbering is how a script says which is which. |
| emissive | The part of a material that gives off light — a screen, a bulb, a neon sign. |
| UV | The flat pattern that says which part of a 2D image lands on which part of a 3D model. Wrong UVs mean the texture arrives stretched or in the wrong place. |
| mipmap | Pre-shrunk copies of a texture used at distance, so far-away surfaces do not shimmer. |
| scalability settings | The engine's quality dials — shadows, textures, effects, anti-aliasing — each of which can be set independently. Turning one up can cost far more than turning the rest up together. |
| dirty WP package | One square of that World Partition grid holding unsaved changes. "Dirty" just means changed-but-not-saved, and unsaved squares are how a day's work quietly disappears. |
| actor | Anything placed in the game world — a lamp post, a person, a trigger volume, a camera. If it sits in the level, it is an actor. |
Things we still need to fix tomorrow:
- The HLOD is stale again. It still bakes cube dishes and cube name plates into the distant view.
- 27 of the 28 streets have not been looked at by a human. One capture of one of them has been
sent. The other 26 are unseen.
- The medium houses are still plain, and the next attempt has to start by looking at the
geometry rather than at the numbers describing it.
- Parked cars are in every single reference photograph and there are none in the game.
- The flower on a windowsill is a flat box and reads as a postage stamp behind the glass.
- The street has looked like this since Thursday night and there is still no video of it.