Reconstructing Procedural Terrain, Part IV: Rebuilding the Height-Generation Pipeline for Four Biomes

The same height formulas can still produce the wrong terrain when applied to a different biome. While testing these four requests, several major discrepancies came from data passed between stages: which version of the height field a query read, which order draws ran in, and when an uploaded texture became usable. This article follows the generation process to explain how those failures arose and were fixed.

Part I follows one tundra request through heightmap generation. Part II covers POI, Stamp, vegetation, and rock placement. Part III examines how those results enter UE’s runtime terrain, collision, and networking systems. This installment returns to the generator and the work completed from October 3 to 9: testing the same algorithms with four biome requests and moving more of support sorting, texture row conversion, and continuous height rendering into C++.

The Plain, Desert Dune, Ionic Crimson, and Scorched Moor cases now have complete comparisons for natural Stamp parent records, region interior points, and corner heights. Regenerating each case with its baseline’s explicit startup settings produces four 4096² float32 height fields that are bit-identical between C++/HLSL and the frozen Web baseline, after Location height-texture drawing and before Vista. Comparison with the reference target still leaves millimeter-scale residuals. Outer terrain and engine runtime behavior have separate acceptance requirements.

The largest errors in this work occurred between stages: an outdated mission boundary, post-platform heights used for a pre-platform query, CPU record order confused with GPU draw order, and geometry left unchanged when texture residency changed. The article starts with the four results, follows the request through configuration lookup and height generation, and then examines how these errors were found and fixed.

Four biomes, one comparison area

All four comparisons use the same central disk with a radius of 450 meters, a sample spacing of 0.5 meters, and 2,544,680 pixels per case. Each figure shows the reference height, independently generated height, and absolute error. The circle defines the statistical area; it is neither the map boundary nor the outer-blending radius.

Desert Dune: reference, independent generation, and error

Figure 1 | Data visualization: Desert Dune. The continuous error scale on the right ends at 2 millimeters. Maximum absolute error is approximately 0.5913 millimeters. The colors represent height residuals, not differences in surface materials or lighting.

Plain: reference, independent generation, and error

Figure 2 | Data visualization: Plain. Maximum absolute error in the same central area is approximately 0.5569 millimeters. No sampled pixel exceeds 1 millimeter.

Ionic Crimson: reference, independent generation, and error

Figure 3 | Data visualization: Ionic Crimson. Maximum absolute error is approximately 1.5011 millimeters. There are 49 pixels above 1 millimeter and none above 1 centimeter.

Scorched Moor: reference, independent generation, and error

Figure 4 | Data visualization: Scorched Moor. Maximum absolute error is approximately 1.3332 millimeters, with 37 pixels above 1 millimeter. The available reference covers only the central crop, so this result cannot establish full-map error.

Case Seed Mean absolute error Maximum absolute error Above 1 mm Above 1 cm
Plain 2763691705 0.0334 mm 0.5569 mm 0 0
Desert Dune 2429929577 0.0084 mm 0.5913 mm 0 0
Ionic Crimson 930813242 0.0129 mm 1.5011 mm 49 0
Scorched Moor 2431905037 0.0258 mm 1.3332 mm 37 0

The four Seeds identify the requests used here. Reproduction also requires the same mission conditions, static resources, configuration versions, and startup policy. A Seed alone does not determine the result. The biome names in the table correspond to the English configuration names.

These statistics use pre-encoding float32 height arrays without mean, median, or global height-offset compensation. When an error image is reduced, each 2×2 display block retains the maximum error so that isolated differences are not averaged away. A colored display block may therefore represent several original pixels; the counts in the table always refer to the original resolution.

These are four fixed requests, each using different configuration and execution branches. All four can now independently generate the corresponding result. Other Seeds, mission conditions, and special groups still need their own tests.

Results and reading guide

Results of this validation round

All four fixed requests regenerate through Location height-texture drawing.
With each case’s baseline startup and query settings preserved, the four pre-Vista 4096² float32 arrays are bit-identical between C++/HLSL and the frozen Web baseline.
Against the reference target, maximum absolute error in the common central area is approximately 0.56–1.50 millimeters.
Outer Vista terrain, the UE runtime, object assembly, and networking retain separate validation scopes.

These height comparisons answer different questions. The frozen Web baseline checks whether migration preserves the existing implementation. The reference target measures the residual of independent generation. The validation chapter lists all three comparison types; bitwise parity with the baseline does not replace target comparison.

The article proceeds from the demo to configuration, stage outputs, and failure diagnosis. For a particular topic, use the links below.

Reading goal Section
Identify the actual entry point and height versions Demo pipeline
Follow request lookup, two selections, and Location categories Requests and configuration
Inspect height parameters, source blending, types, and curves Region recipes and continuous height
Compare stage outputs and individual examples Stage outputs and local examples
Diagnose spacing, state versions, row order, sorting, and resources Seven stage-consistency cases
Check migration results and comparison scopes C++ migration, Validation
Review engine integration and conclusions UE validation, Conclusion

Captions identify each figure’s source. Runtime screenshots come from the actual demo. Data visualizations read archived height arrays or placement records. Configuration and mechanism diagrams explain data dependencies. Numerical teaching examples illustrate a calculation; they are not traces of an actual region in that Seed.

What the demo actually generates

Start with the complete demo page. The left panel keeps the request conditions, selected configuration, and stage controls visible. The right panel displays the selected terrain stage. Check the request, the selected recipe, and then the height changes between stages. This example uses Scorched Moor, Region 36, and profile 0. The configuration walkthrough, six-stage screenshots, and two local examples below follow this same job.

Complete demo page: request, configuration, stages, and height result

Figure 5 | Runtime screenshot: the complete demo. Seed, Planet, Scene, faction, difficulty, and mission inputs remain visible on the left, along with six stage controls. The right panel shows the result after Location asset-height drawing. This is the inner height field, without outer Vista terrain.

This is an independent four-biome demo. Web submits requests and displays results; upstream C++ generates placement, regions, and instances; a standalone D3D11 host executes the independently written HLSL height shaders. The UE runtime project discussed in Part III has its own entry point and validation status. Fixes in this demo have not automatically been integrated into UE.

The current backend reads a 200-byte request template, replaces only the specified Seed, preserves the other conditions, loads the static catalog, and runs upstream C++. Selecting the Plain or Scorched Moor case therefore also selects its initial mission, scene, and resource-filtering conditions. The recipe index displayed by the page is a later lookup and selection result, not an initial request field.

Instance generation, three height inputs, and pixel-drawing order

Figure 6 | Mechanism diagram: upstream generation and pixel drawing are separated according to their actual calls. Natural Stamp parents already exist upstream; static height assets are expanded later for drawing. The three height inputs are preserved separately rather than all being replaced with the latest height field.

The following stage names are used throughout. Hbase is the smoothed base pixel height; Hplatform is the output after forced flattening. A request with no platform writes may produce identical arrays, but queries and drawing still read their designated versions. Figure 6 separates instance generation from pixel writes because a generated parent participates in height drawing only at a later stage.

Stage Input and operation Output or use
Upstream scene generation Request, static configuration, placement, regions, and instances Height-bearing triangles, natural Stamp parents, and Location records
Rasterization Per-pixel interpolation of height-bearing triangles Raster height array
Smoothing Multiscale filtering of the raster field Hbase, used by the base-query pyramid and later processing
Forced flattening Query platform targets from Hbase, then draw platforms Hplatform, the drawing base for Location support
Location support Auxiliary queries retain Hbase; pixel writes read Hplatform Support-adjusted terrain and separate support parameters
Natural Stamp height drawing Expand static height assets of accepted parents Terrain with natural local shapes applied
Location height-texture drawing Draw static Location height assets onto the current terrain The final inner height field in this continuous sequence
Outer Vista Separate outer inputs and blending Outside the four pre-Vista bitwise regressions reported here

Upstream placement, regions, and instances

After initialization, Location candidate and position processing provides spatial inputs. Region processing then calculates ownership, height groups, types, and Zones. Locations and regions also undergo association, attachment, and later layout updates. Some operations are called again at different stages; completing an early Location pass does not mean that its data will never change again.

Location sources enter base-corner height assignment. Boundaries, types, and curves then contribute to subdivision heights, producing height-bearing interior points and corners. The natural Stamp loop uses region members, mission extent, obstacles, and query data to accept parents. The current entry point then assembles the height triangles used for rasterization. Location draw records and natural Stamp parents are saved separately and expanded into local units by the resource adapter.

In Figure 6, natural Stamp parents are upstream outputs, while support, natural Stamp, and Location height textures are later pixel operations. Parents are selected and saved first. Their static height assets are drawn only after the support terrain is ready. Instance records connect the two processes; pixel draw order must not be mistaken for instance-generation order.

Three height inputs that cannot be exchanged

Reader What the current demo reads Purpose Not interchangeable with
Natural Stamp candidate query Base-corner heights, downhill neighbors, outside distance, and spatial index Position height, slope, and extent conditions Post-support or final GPU height
Location support auxiliary query Smoothed base pixel height Hbase and its query pyramid Boundary sampling, height envelope, and auxiliary height Post-platform Hplatform
Support pixel drawing Post-platform Hplatform and calculated support parameters Modify the current terrain An old base field that omits platforms

A natural Stamp query first uses the spatial index to find a corner, reads its height, and applies the caller’s scale. If a valid downhill neighbor exists, the height difference is divided by horizontal distance and converted to a slope angle with atan. This queries the upstream corner field and adjacency. It does not sample the final height texture, and its slope is not equivalent to a rendered mesh-normal slope.

Location support uses a different path. The independently generated triangles are rasterized and smoothed into Hbase, published in the designated texture row order, and used to build an eight-level query pyramid. Query outputs include auxiliary height, an envelope, and accepted/rejected sample counts. Admission, targets, parent-child coordination, emission, and adjacency are calculated afterward. The query return value is not immediately the final support height.

Forced flattening also queries Hbase to calculate platform targets, then writes Hplatform. Location support queries still retain Hbase; support drawing reads Hplatform as its base. The program preserves separate file paths and hashes, and binds different inputs for queries and drawing. The Ionic Crimson fix described later restores Hbase where post-platform height had been connected by mistake.

Prepare drawing early, but preserve pixel-write order

Static Level/Unit data, textures, patch meshes, and residency plans can be prepared before pixel drawing. Parsing natural resources does not mean their heights have already been written. Once the end-to-end validator has prepared the draw package, the continuous C++ entry executes support, natural Stamps, and Locations in order, then produces the height array for comparison.

That entry point is not another complete Seed-to-scene generator. It receives support directories, platform heights, and draw records from its caller and preserves downstream draw order. The end-to-end validator separately ensures that these inputs came from the current upstream generation. Checking the draw output and checking the complete input provenance are distinct tasks.

The original-algorithm analysis has checked configuration reads, parent records, stage kernels, and heights in the common area. Inspection of the demo must additionally establish whether those rules are connected to its entry point, whether the tested request executed the relevant branch, and whether the output was compared with the target. A function existing in source is not enough to show that the request actually used it.

How a request selects Region and profile

Generation needs three kinds of input: a request supplies the conditions, static tables contain resources and rules, and execution state tracks branch selection and the random sequence. Keeping only the Seed loses the effects of the other inputs.

The Scorched Moor walkthrough uses Seed 2431905037, Planet index 73, Scene index 23, recipe 36, and profile 0. First follow where each field is read, then return to the same demo job’s results. A separate Plain figure set illustrates triangles, rasterization, and local writes; its captions identify that request.

Request and configuration overview

Figure 7 | Configuration diagram: the Scorched Moor request. Identifiers locate records, eligibility conditions filter candidates, and the Seed participates in recipe and profile selection. The lower row shows the later steps that read the selected configuration.

At the current entry point, the request is a 200-byte record. The diagram presents logical fields, not the complete byte layout. For example, the Scene identifier has already been decoded into an internal index. A field’s position in a diagram does not specify its serialized representation.

Independent generation here means that the generation branch does not read intermediate reference heights, instance lists, or parent transforms. Reference arrays enter only the comparison after generation. Intermediate files generated by this job may, of course, feed its later stages. Web submits and displays jobs; heights come from the C++ core, independently written HLSL, and standalone graphics host. The frozen Web baseline also contains outputs of this implementation. Its bitwise comparison with C++ is an implementation regression, not a second independent algorithm validating the original.

Figure 7 follows actual reads. Planet determines the candidate sets used to find recipes and supplies Location eligibility conditions. Scene limits profile categories, interprets mission IDs, and provides scene rules. Together they constrain recipe selection. The selected Region recipe then controls grouping, type selection, and resource processing. Corners, triangles, and local drawing eventually turn those choices into pixel heights.

Configuration, instances, and calculated outputs

Many requests can reuse one Region recipe. Different Seeds, missions, and layouts produce different region ownership and Location instances; those instances then affect source-height assignment and local texture writes. A recipe stores neither a fixed heightmap nor a ready-made building list.

Data This example Read by Result
Logical request Seed, Planet/Scene identifiers, nine missions, condition lists Initialization and mission matching Static records, mission kinds, execution conditions
Planet record Candidate-set references 23/23, type 9, B0/D4 lists Region entry and Location eligibility Candidate set, template and Location filters
Scene record Allowed categories [1,2], 32-slot mission table, rule reference Recipe eligibility and mission processing Eligible profile categories, mission kinds, derived mode
Candidate set Seven recipe references and outer weights Region selection Recipe 36
Recipe profiles Category, selection weight, tags, Location-category weights Profile selection and Location processing Profile 0 and its later constraints
Height and type parameters Four group weights, distances, noise, type table Region classification, corners, subdivision Height groups, types, Zone references, height-bearing points
Instances Location positions, resource references, natural Stamp parents and orientations Source calculations and asset expansion Source heights, draw transforms, local texture inputs
Stage height arrays Rasterization, smoothing, support, and local-write outputs Later queries, drawing, final comparison Queryable and encodable surface height

These outputs fall into three categories: lookup and selection identify records, eligibility checks filter candidates, and height calculations produce numerical values. Planet type 9, profile category 1, and Location category 1 serve compatibility, profile eligibility, and resource allocation respectively. They are different fields from height groups and pixel heights. The diagrams below show where each is used.

The implementation must distinguish recipe indices, profile indices, region IDs, instance IDs, and texture coordinates. Passing an unexplained “item 6” makes it easy for the next module to read row 6 of the wrong table.

Planet lookup and Location eligibility

Planet limits the Region candidate set and eligible Location resources; it does not yet calculate heights. Once lookup identifies the record, set references select recipes, while eligibility conditions filter Locations.

The Planet table contains 438 records. This request identifier resolves to zero-based index 73, the 74th record. This is an identifier match, not an arithmetic conversion of the request value. Seed is not used for the lookup.

Planet input, lookup, and outputs

Figure 8 | Configuration diagram: Planet lookup uses the identifier, then reads a candidate-set reference according to the active branch. Set IDs, type, and condition lists feed Region lookup and Location eligibility separately.

The record stores two candidate-set references, both 23 here. The independent caller explicitly uses branch 0; the original upstream rule that sets this state is not fully established. The four tested requests have reference pairs 23/23, 3/3, 21/21, and 24/24, so this branch difference does not change their candidate-set choice. This does not establish that the original four runs all had state 0.

The same record contributes to Location eligibility. Its B0 list is empty, D4 is [9,9], and type is 9. B0 and D4 retain their field labels because their read behavior is known but their gameplay names have not been verified.

A candidate with an empty B0 list passes this check. Otherwise its list must intersect Planet’s B0 list. An empty Planet list therefore does not make every candidate pass. The D4 check accepts a candidate value of 0, a value present in Planet’s list, or a Planet list containing 9. This example satisfies the last condition; faction, difficulty, and template checks still follow.

The request’s B0 control word, Planet’s B0 list, and the candidate Location’s B0 list are different fields. Similar names in analysis notes do not make their inputs interchangeable. Type 9 also participates in template compatibility, corresponding to bit 7 of the template mask. Passing Location eligibility does not make every template usable.

Scene mission kinds and allowed categories

Scene provides three kinds of configuration: allowed profile categories, mission kinds, and derived region rules. The following sections describe their uses before tracing the nine mission lookups.

The Scene table has 161 records. This request matches index 23. The upper row in Figure 9 shows inputs, loaded fields, and their uses; the lower row keeps the actual slots and kinds from nine matches. Scene classification, allowed profile categories, and mission kinds are separate fields.

Scene input, mission table, and outputs

Figure 9 | Configuration diagram: allowed categories filter profiles, mission kinds control the category-allocation branch, and the Zone mode derived from a rule reference affects later subregion processing.

Allowed profile categories are [1,2]. They filter profiles inside Region recipes; they do not mean two missions, nor a Location’s final category. Scene classification 1 contributes to the default mission-radius configuration. Radius-override reference 0 means no override, not a zero radius.

Scene also stores a rule reference that construction logic turns into Zone mode 3. The stored reference and the derived execution mode must remain distinct. Mode 3 is not simply a numeric field read directly from the Scene table. After Location placement, the relevant branch checks mode flags and subregion coverage, then writes type, Zone, and height-group information.

Nine requests against a thirty-two-slot mission table

The mission table has 32 slots of 24 bytes. Mission ID and mission kind have been confirmed at offsets 0 and 12 within each slot. This article does not assign meanings to the other 16 bytes.

Mission matching and subsequent Location processing

Figure 10 | Configuration diagram: mission matching and Location generation. Repeated IDs are processed in request order. Skipping category allocation does not skip subsequent resource selection and placement.

The request preserves nine mission IDs in their original order. The first two repeat, as do the next two. They match slots [0,0,1,1,4,6,5,9,13] and obtain kinds [0,0,0,0,3,3,3,3,2]. IDs are not deduplicated here; nine matches cannot be rewritten as seven independent missions.

These kind values control execution. Kinds greater than 1 skip Location-category allocation, but resource selection and placement continue. Kind 0 enters the applicable allocation logic. Describing the skip as “no Location is generated” would omit later work and miscount random-number consumption.

The gameplay names for kinds 0, 3, and 2 remain unconfirmed. Their branch behavior can be explained without inventing mission names from their numbers, positions, or appearance. Mission kind, Scene’s allowed category, and Location category are three different domains despite all being integers.

Region recipe and profile are selected separately

Selection first chooses a Region recipe, then a profile inside it. Each selection initializes its own random state from the same Seed and takes the remainder against its own ticket total. They do not consume one continuous state sequence.

Planet selects candidate set 23, named region_moor_arid in an external mapping. It contains recipe indices [16,36,19,46,68,34,63] with weights [0,1,1,0,0,1,1]. Scene-category eligibility must be checked before these entries contribute tickets.

Recipe eligibility and the two selections

Figure 11 | Configuration and selection diagram: inputs and ticket counts that select Scorched Moor recipe 36 and profile 0.

Each recipe contains eight profiles. Compatibility normalization happens before selection: if their weight sum is zero, or legacy flags, category, and weight fail the current format conditions, all profile weights are cleared, profile 0 receives weight 1, and the legacy category is used. Eligibility reads this normalized projection rather than just a row of the original eight-profile table.

Recipe 36 has legacy flag 0, category 1, weight 1, and an eight-profile weight sum of 1.8, so no rewrite occurs. The current selector implements the compatibility projection required for eligibility, but the initialization entry has not validated complete profile-payload copying and rewriting after an old-format selection. It rejects a selected recipe requiring that rewrite. Filtering candidates therefore does not mean every legacy recipe can generate successfully. Recipe 36 avoids this unintegrated branch. Recipes 34 and 63 project to categories 3 and 5, outside Scene’s [1,2]. Entries 16, 46, and 68 have zero set weight despite category eligibility, so receive no ordinary selection tickets. Only 36 and 19 participate here.

Each weight is multiplied by 1000 in float32, then converted to an unsigned ticket count. The two recipes receive 1000 tickets each, totaling 2000. Random state is calculated as follows, preserving uint64 overflow in multiplication and addition:

state = uint64(seed) × 0x5851f42d4c957f2d + 0x14057b7ef767814f
target = state % totalTickets

The state is 0xb1509b2349729f98. Its remainder modulo 2000 is 520, selecting recipe 36. Traversal skips zero-ticket entries, accumulates positive tickets, and returns at the first cumulative value greater than or equal to target. Reproduction must preserve that boundary comparison rather than substitute another interval convention.

Profile selection then considers eligible profiles inside recipe 36. Profile 0 has category 1 and weight 1; profile 1 has category 2 and weight 0.8, giving 1000 and 800 tickets. Both selections initialize separately from the same Seed; they do not share a continuously advanced state. Profile selection therefore reuses the state above. Modulo 1800 gives 720, selecting profile 0.

Seventy-five recipes do not mean seventy-five biomes

Recipes and candidate-set references

Figure 12 | Static configuration relationships: recipes, candidate sets, and display-family names describe different levels of organization.

The static table holds 75 recipes, each 3296 bytes. Recipe 36 is the 37th record (zero-based index 36) and maps externally to region_moor_mountain. That name is not a string stored in the record.

The 34 candidate sets can share recipes. The current summary groups them under 13 family labels and references 46 distinct recipes, including zero-weight references. Another 29 recipes do not appear in this set of references. Unreferenced does not mean obsolete, and 75 records do not mean 75 planets or unrelated ecosystems.

At this point selection has established recipe 36 and profile 0, but has generated neither region heights nor Location resources. Set weights selected the recipe, and profile weights selected the profile. Tags, Location-category weights, and height-group weights control later steps. Figure 13 follows those profile reads.

How profile constrains Location resources

Profile tags and Location-category inputs

Figure 13 | Configuration diagram: the selected profile supplies tags and Location-category weights. Allocation also depends on mission kind, quotas, and per-slot availability; it is not an independent weighted draw for each mission.

A profile stores tag constraints and Location-category weights in addition to its own category and selection weight. Profile 0 has tag slots [0,0,0,0]. Parsing stops at the first 0 and produces a zero tag mask, not four tags named 0. All-match, any-match, and exclusion conditions are still evaluated separately. A zero mask does not make the entire profile unconditional.

Profile 0 assigns Location categories 1 through 6 weights [1,0,0,0,0,0]; profile 1 assigns [1,1,0,0,0,0]. This table participates in Location eligibility and category processing. It is separate from Scene’s allowed profile categories [1,2]. Mission kind determines whether allocation runs; the selected profile supplies the weights used there.

Compatibility, special groups, and override rules

Figure 14 | Conditional-rule diagram: compatibility, special groups, and overrides are separate paths. Recipe 36 does not trigger legacy rewriting. The other columns explain conditions, not special-rule hits observed in this request.

Figure 14 lists three additional rule paths. Compatibility projection can filter candidates, while complete rewriting of a selected legacy payload is not integrated. Special groups apply only to marked regions. An override match still needs full calling context before it can be applied. The request conditions and call records must establish whether these rules actually ran.

How Location-category weights affect allocation

These weights do not make each mission independently draw a category in proportion to them. The current target-category allocator also reads category quotas and each mission slot’s available categories. Resource candidates first pass faction, difficulty, template, tag, and list constraints. A slot marks a category available only when an eligible resource exists.

The allocator produces a category traversal order, skips categories whose weights are not positive, and attempts allocation according to quotas. A slot must have kind at most 1, no assigned target, and the current category available. If another unassigned slot supports only this category, allocation to a slot with multiple options is deferred. This preserves the constrained slot’s only viable choice.

For a teaching example, slot A supports only category 1, slot B supports 1 and 2, and each category has quota one. When category 1 is processed first, A is more constrained; its possibility is preserved, leaving B usable for category 2. This is a quota-and-availability constraint, not a change of B’s draw probability to 50%. In the actual profile 0, category 2 has zero weight, so these teaching conditions do not describe the request’s allocation result.

The same weights have different uses at different steps. The target-category allocator checks only whether a weight is positive; later resource selection may use its magnitude. Both read sites must be checked separately.

After target is assigned, the slot has only a category goal. Resource selection, position proposals, checks, and placement still follow. Successful allocation has not yet generated a Location; a mission that skips allocation may still select and place resources.

How a Region recipe becomes continuous height

Recipe 36 first selects initial height groups and adjusts them by distance to low groups. Locations and region boundaries provide sources that blend into corner heights. Zone curves associated with types, together with noise, then calculate subdivision heights. Figure 15 identifies the parameters read at each step. The walkthrough follows that order before returning to triangles and stage screenshots.

Where recipe 36 height parameters are read

Figure 15 | Mechanism diagram: grouping, types and Zones, base sources, and subdivision each retain their outputs. The resulting height-bearing geometry is then rasterized.

Initial grouping uses weights 0.4, 1.25, 1, 0, totaling 2.65. It normally uses a spatial-noise sample; a random value below threshold 0.1 switches to random sampling. This threshold does not add 10% height noise to every region.

For a teaching sample normalized to [0,1], value 0.5 multiplied by 2.65 gives 1.325. Cumulative weight is 0.4 after group 1 and 1.65 after group 2, so group 2 is selected. This uses real configuration values to explain the rule; it is not an actual region trace for this Seed.

Random-state advancement during initial grouping

Grouping first checks special rules. If a region’s special entry enables the forced flag, it writes group 1 without ordinary sampling. The ordinary branch first advances the stage’s random state to obtain draw. Only when draw is below 0.1 does it advance again and use the second sample as value. Otherwise it reads spatial noise at the region center and maps it to [0,1].

The spatial-noise branch therefore still consumes the first random value. The forced branch bypasses this sampling sequence. Calling the generator only when a random sample is needed shifts the sequence seen by later regions. Initial grouping and type selection also have separately initialized stage states; they cannot be joined into an arbitrary shared stream.

Initial grouping selects when cumulative weight is strictly greater than threshold. Region ticket selection used greater than or equal to target. These boundaries differ. The teaching sample 0.5 does not lie on a boundary, so both implementations would return the same group and that example alone would miss the error.

Adjust height groups by distance to low groups

Initial noise assigns groups 1 through 4, not terrain heights. The next step adjusts grouping according to distance from low groups. The resulting group then constrains type and curve selection.

Distance adjustment anchors on group 1, falling back to group 2 when none exists. That fallback belongs only to the distance-adjustment function. The current downstream Location/corner height implementation still requires group 1 and rejects inputs without a low-group anchor; the fallback does not establish support for an entire no-group-1 pipeline. Distance to the nearest anchor center is reduced by spacing and clamped to nonnegative. The smaller of the configured cap and the current maximum distance becomes cap. Only group 2, 3, and 4 weights participate, totaling 2.25; group 1’s 0.4 is excluded.

d = max(nearestAnchorDistance − spacing, 0)
cap = min(configuredCap, maximumDistance)
target = min(d / cap, 1) × (weight2 + weight3 + weight4)

This expression omits the implementation’s near-zero checks. With teaching conditions cap=425 and d=300, target is approximately 1.588. It exceeds group 2’s cumulative 1.25 and selects group 3. Using cap=425 illustrates a case where the configured upper bound is active; it does not assert that the actual map’s maximum distance must exceed 425.

Recipe 36 grouping, sampling, and distance parameters

Figure 16 | Configuration and teaching diagram: seven grouping and distance parameters from recipe 36. Sample 0.5 and distance 300 are teaching inputs, not traces of a specific region.

Initial grouping uses four weights, while distance adjustment uses only groups 2–4. Other parameters are read later by source and subdivision calculations. The complete fourteen-field table below supports implementation checks. Offsets are relative to the start of this 3296-byte record. English names describe confirmed uses, not recovered original symbols. The diagram groups reads; the table retains every value. Noise frequencies undergo internal scaling, and distance inputs depend on caller coordinates, so the inputs are not all labeled meters or hertz.

Offset Use and descriptive name Value Main readers
0x18 Group 1 weight heightGroup1Weight 0.4 Initial grouping, subdivision-noise threshold
0x14 Group 2 weight heightGroup2Weight 1.25 Initial grouping, low-group distance adjustment, noise threshold
0x10 Group 3 weight heightGroup3Weight 1 Same as above
0x0C Group 4 weight heightGroup4Weight 0 Same; contributes no positive weight here
0x1C Base frequency input baseNoiseFrequencyInput 5 Initial grouping, type selection, subdivision main noise
0x20 Random-sampling threshold randomInsteadOfNoiseThreshold 0.1 Sampling branches in grouping and type selection
0x24 Height amplitude input heightAmplitudeInput 75 Location sources and base-corner sources
0x28 Alternate frequency input alternateNoiseFrequencyInput 0.5 Second subdivision-noise layer
0x2C Mixed noise amplitude subdivisionMixedNoiseAmplitude 7.5 Subdivision mixed term
0x30 Base noise amplitude subdivisionBaseNoiseAmplitude 0 Independent main-noise term
0x34 Radial amplitude radialHeightAmplitude −500 Radial candidate and maximum comparison
0x38 Height transition width heightWidth 550 Source-distance normalization
0x3C Low-group distance cap lowGroupDistanceCap 425 Distance adjustment
0x40 Height margin heightMargin 4 Source-distance deduction
Recipe 36 source, subdivision-noise, and radial parameters

Figure 17 | Configuration and teaching diagram: the other seven height parameters, covering source contributions, subdivision noise, and radial candidates. N, v, alt, mask, h, and w are calculation variables; source blending has not yet been applied to the example contribution.

Source and corner calculations deduct margin=4 from boundary distance, normalize using width=550, and multiply by amplitude input 75 and the caller’s normalization factor. One contribution can be written as:

d = max(rawBoundaryDistance − 4, 0)
sourceContribution = min(d / 550, 1) × (75 / N)

N is supplied by the caller. At teaching distance d=275, the contribution is 37.5/N. Location sources and weights still need blending, so this is not a final terrain height of 37.5 meters. The value has completed only the distance-to-contribution step.

Subdivision noise has more than one amplitude. The main frequency input is 5 and the alternate is 0.5. After main noise is mapped to [0,1], height-group weights define a threshold that produces v:

k = (0.4 / 2.65) × 1.15
denominator = clamp(1 − k, 0.001, 1)
v = clamp((n − k) / denominator, 0, 1)
noiseContribution = (v × 0 + v × alt × 7.5) × (1 − mask)

Base amplitude is 0 here, but mixed amplitude is 7.5 and can still produce local detail. The base-map mask modulates that contribution before it is combined with the curve result. A zero base amplitude does not remove the second noise term.

The mask has its own generation path. The current entry rasterizes independently generated base-map triangles, retaining a 512² mask, 256²/128² maximum-value stages, and the final 128² R8 field. R8 samples divided by 255 feed subdivision. This is an intermediate layout output, not a reference-target height or the later smoothed 4096² height field. An older diagnostic interface allows explicit input replacement, but the four-request end-to-end tests here do not use that override.

Radial amplitude −500 is not subtracted uniformly from the map. The code forms candidate = w×(−500)+(1−w)×h, then returns max(h,candidate). Whether a point changes depends on its existing height and radial weight. The parameter’s sign alone does not determine whether output rises or falls.

How Location sources contribute to corner heights

The earlier sourceContribution explains only the distance-to-height mapping. Actual Location sources first query region ownership using Location XY. Excluded resource selections and Locations belonging to height group 1 receive zero source height. Other Locations find the nearest low-group center, then calculate minimum distance to that group’s polygon boundary. Margin is deducted before amplitude is applied. Height is not assigned directly from center-to-center distance.

A Location’s extent also affects weights. For corner P, distance to the Location center minus extent gives t. The inspected entry passes radius=512, gain=10, and nearDistance=96. These belong to this checked call, not universal constants for every ecosystem.

t = distance(P, locationCenter) − extent

baseWeight = 1000                         when t < 0
baseWeight = (1 − t / radius)^6           when 0 ≤ t < radius
baseWeight = 0                            when t ≥ radius

weight = baseWeight
when t < nearDistance:
    weight *= (1 − t / nearDistance) × gain

The sixth power quickly attenuates influence outside a Location. At teaching t=256 and radius=512, base weight is 0.5^6=1/64; at t=384 it is 0.25^6=1/4096. Inside the extent, a different strong-weight branch applies. The sixth-power expression must not be extended indefinitely inward.

Corners also keep their own source contributions and weights. A low-group corner is written directly as zero. Otherwise all valid Location sources and the corner’s own source are normalized together:

cornerHeight = (sum(locationWeight × locationSourceHeight)
                + ownWeight × ownSourceHeight)
               / (sum(locationWeight) + ownWeight)

For example, two teaching Location sources have heights 12 and 20 with weights 2 and 1. The corner’s own source is 8 with weight 1. The result is (2×12+1×20+1×8)/4=13. These are explanatory values, not a runtime trace of this job’s corner.

A zero source can still contribute to the denominator. The current implementation writes zero source height for some invalid selections but retains subsequent distance weighting. Deleting those entries entirely during migration reduces the denominator and amplifies the other sources. Checking only nonzero source positions and heights can miss this discrepancy.

Outputs are corner heights, Location source heights, and calculated distances. They determine neither a building’s final Z nor its later support target. The same Location first contributes through XY and resource relationships to base terrain, then queries the formed ground for support, and finally writes its local height asset.

The type table connects classification to curves

Types, Zones, curves, and rule inputs

Figure 18 | Configuration and teaching diagram: an ordinary region already assigned height group 2 is used to demonstrate filtering and selection. Candidate values come from recipe 36; sample 0.5 is illustrative. Zone lookup and merging subsequently read different data.

The recipe type table has 16 slots of 68 bytes. Six fields are explained so far; the remaining 44 bytes are preserved. A slot is a type candidate with a Zone, curve multiplier, weight, and group constraint, not an actual region.

Ordinary selection considers only group-compatible entries with positive weights. All 16 slots are retained below for implementation checks. Listing zero-weight and post-terminator entries does not include them in this request’s ordinary selection.

Slot Offset Type key Zone Curve multiplier Type weight Group condition Merge probability
0 0x238 18 1 1 0 1 1
1 0x27C 19 2 1 0 0 1
2 0x2C0 20 3 1 0 1 1
3 0x304 24 29 1 0 1 1
4 0x348 25 27 1 0 1 1
5 0x38C 26 28 1 0 1 1
6 0x3D0 8 66 1 0.5 1 1
7 0x414 7 65 1 0.2 2 1
8 0x458 11 63 1 0.15 2 1
9 0x49C 9 66 1 1 2 1
10 0x4E0 12 65 1 0.5 3 1
11 0x524 10 66 1 1 3 1
12 0x568 4 67 1 1 4 1
13 0x5AC 0 0 1 1 0 1
14 0x5F0 0 0 1 1 0 1
15 0x634 0 0 1 1 0 1

Slot 6, for example, stores type 8, Zone 66, multiplier 1, weight 0.5, required group 1, and merge probability 1. It affects a region only after eligibility and selection. Probability 1 removes probabilistic rejection, but same-type and edge conditions still apply. It does not merge every region matching the group.

Ordinary candidate parsing stops at type key 0, slot 13 here. Slots 14 and 15 remain stored but do not become two regions. Zero-weight entries do not participate in ordinary weighted selection. Special rules and override tables have their own conditions and priorities; a stored record alone does not establish that the map executed it.

A type-selection calculation for recipe 36

For an ordinary region, selection reads its computed group and retains positive-weight types that match that group or a wildcard condition. Recipe 36’s candidates are listed below. Values 0.2 and 0.15 are readable decimal displays; execution uses the original float32 values.

Required group Type Zone Type weight Curve multiplier Merge probability
1 8 66 0.5 1 1
2 7 65 0.2 1 1
2 11 63 0.15 1 1
2 9 66 1 1 1
3 12 65 0.5 1 1
3 10 66 1 1 1
4 4 67 1 1 1

A group-2 region retains types 7, 11, and 9, totaling approximately 1.35. Group 1’s type 8 is excluded from that denominator. Teaching sample value=0.5 scales to approximately 0.675. Subtracting 0.2 and 0.15 still does not select either earlier entry; selection reaches type 9 and obtains Zone 66 and its parameters.

Group filtering must happen before weighted selection. Drawing from all types and then substituting a default for a group mismatch can change both distribution and random sequence. Special regions can obtain types directly from a special table; already-typed regions do not repeat ordinary selection.

The ordinary branch uses the noise-or-random sampling structure but its own stage state. Shared coordinates and main frequency correlate spatial noise; they do not mean that both stages used the same random sample.

After selection, same-type neighbors can undergo merge checks. The implementation reads edge flags and advances random state at the relevant check, even when merge probability is 1. Removing that call changes later state. Probability, blocking flags, and existing group extents jointly affect merging. Member ranges are saved per region and read by later boundary processing and natural Stamp traversal.

The current type selector rejects inputs requiring an unintegrated adjacency-exclusion condition. Successful ordinary-table selection does not establish that all configuration-specific adjacency rules are implemented.

Zone curves become subdivision heights

Zone is a reference for curve lookup, not a height. Zone 66 does not mean 66 meters. Its static curve is evaluated using the subdivision point’s distance to associated boundary samples.

The distance calculation takes squared distance to each associated sample, subtracts squared edge width, clamps to nonnegative, then takes the minimum and square root. The current kernel multiplies distance by 1/64 and clamps to [0,1] to find a curve segment and fraction. It does not pass nearest-region-center distance directly to the curve.

curveHeight = curve[i]
              + (curve[i+1] − curve[i]) × fraction × multiplier

The multiplier applies only to the interpolated increment, not the curve base. For teaching endpoints 10 and 18, fraction=0.25, and multiplier 0.5, the result is 10+(18−10)×0.25×0.5=11. Multiplying the whole interpolated result by 0.5 gives 6 instead. Recipe 36 uses multiplier 1 and cannot expose this mistake, so the teaching example deliberately uses 0.5 rather than claiming it is the recipe value.

Subdivision adds the earlier mask-controlled noise, compares the radial candidate, and outputs interior-point and corner heights. Triangles then interpolate those results. Recipe parameters become drawable height through these calculations; type IDs, Zone IDs, and group IDs are never written directly as pixel values.

Natural-resource weights depend on candidate position

Region type supplies a Zone reference, and Zone supplies StampGroups. Each group stores placement parameters, candidate entries, and overlap policy. The independent input preserves 96-byte group records and 104-byte candidate records. Their lengths are established, but some field meanings remain unknown. Traversal also uses merged-region members and scheduling quotas. Three groups in a Zone do not imply three instances.

A group organizes placement, a candidate describes a resource and eligibility, and an accepted parent stores this job’s resource reference, position, and orientation. Static Level/Unit data then expands that parent into height-draw parameters. A candidate may be accepted repeatedly, and one parent resource may expand into multiple units. Group, candidate, parent, and draw counts must remain separate.

Recipe and region choose resource sets, but a resource’s static weight need not be its effective selection weight. Natural Stamp selection reads slope, height, outside distance, and classification at the candidate position, adjusting weights through enabled conditions.

Each range has four values [a,b,c,d]: zero weight below a or above d, linear rise from a to b, a plateau from b to c, and linear decay from c to d. A range is read only when its gate is enabled. Its presence in a record does not make it active.

For a teaching static weight 2 and enabled range [0,10,20,30], input 5 gives weight 1, input 15 retains 2, and input 25 gives 1. If another enabled condition halves it, the current weight is multiplied by 0.5 rather than reset to the static weight.

Classification bounds can reject a candidate directly. After adjustment, the program accumulates effective weights and selects. The total must exceed candidate count multiplied by 2^-23; a simple greater-than-zero test is not equivalent. Weighting chooses the resource; later region, collision, and mission-boundary checks decide whether the instance is accepted.

This is why one set can select different resources at different positions: the position changes effective weights, and checks can trigger retries. Estimating instance counts requires tracing candidate generation, weighting, and retries, not just reading static weights.

The preceding sections explain how weights, distances, types, and Zones enter selection and height calculations. Runtime records establish what a particular region actually selected. Special overrides in their complete calling context are not fully validated. Next, placement and triangles show how those calculations appear in the height field.

From instance placement to continuous pixel height

The Plain presentation pages illustrate the transition from geometry to height arrays. This request uses Seed 2763691705, 39 Locations, and 1174 natural Stamp parents, separately from the Scorched Moor case. Figures 19–22 use archived independent C++/HLSL stage data; Figures 23 and 24 return to the same recipe-36 demo job. The generator was not rerun while preparing this article.

Plain Location layout and natural Stamp parents

Figure 19 | Placement data: actual Plain parent layout, separate from the preceding Scorched Moor configuration example.

Locations and natural Stamps first establish parent positions and orientations. Static Level, Unit, and local-transform data expands them into draw inputs. The layout identifies parent-resource positions and directions. Textures, meshes, and vegetation inside each parent still require expansion and separate world-transform checks.

Plain triangles, rasterization, and smoothing

Figure 20 | Stage data visualization: independently generated stage data. The triangle count belongs to the displayed crop.

This page follows height-bearing triangles through rasterization and smoothing. Its crop contains 39,837 triangles, not the full map’s total. Per-pixel interpolation converts geometry into an array, and smoothing changes the surface. Keeping the triangles, raster field, and smoothed field makes it possible to locate the first discrepancy in height assignment, coverage, or filtering.

Stage views use the same 900-meter square, height colors, orientation, and lighting, with a color range of approximately −42.018 to 68.225 meters. These top-down relief images are generated from height data, not captured from the Web 3D view. They show shape changes; colored patches cannot replace statistics over the original float32 pixels.

Plain base height, flattening, and Location support

Figure 21 | Stage data visualization: drawing outputs and query-source versions are identified separately.

Forced flattening and Location support read earlier heights, but the base height used for queries must remain separate from the current GPU field being modified. Platform writes do not make every later query switch to their output. The Ionic Crimson case below shows the error caused by that substitution.

Plain natural Stamp and Location height drawing

Figure 22 | Stage data visualization: three actual fields share the same crop, scale, and lighting.

The final two local-write stages use natural Stamp and Location static height assets. The figure keeps post-support, post-natural-Stamp, and post-Location-texture terrain side by side so that additions can be located. They use different instance sources and resource relationships. Similar final heights do not replace checking instance-acceptance order and draw parameters.

The Plain figures span placement through local asset writes. The following recipe-36 screenshots show all six pixel stages in one job, before examining failures in subdivision, random state, and data binding.

Six demo stages for recipe 36

Return to the Scorched Moor request: Seed 2431905037 selects Region 36 and profile 0, producing 66 Location records and 1501 natural Stamp parents. Location records include placeholders, so 66 is not a building count. Parent count is not a hill, tree, or rock count either. Both pages below come from this job with the same camera, view area, color scale, lighting, and vertical scale.

Recipe 36 rasterization, smoothing, and platform screenshots

Figure 23 | Runtime screenshots: rasterization, smoothing, and platforms from left to right. This request has no platform writes; the complete height arrays of the second and third stages are identical.

The left view interpolates height-bearing triangles into the first height array, still showing local triangular contours. The center smooths that array into Hbase, retaining broad relief while making local transitions more continuous. The right is Hplatform. Because this request writes no platforms, Hplatform equals Hbase. Reaching a stage does not require it to change the ground.

The same request after support, natural Stamps, and Location textures

Figure 24 | Runtime screenshots: Location support, natural Stamp drawing, and Location asset-height drawing, using the request and display conditions of Figure 23.

The left view modifies Hplatform using support targets and transitions. Target calculation still queries Hbase from Figure 23; their roles remain separate even though the arrays happen to be equal here. The center expands accepted natural Stamp parents into static height draws, adding dense local relief. The right adds Location asset contours, producing this job’s last inner height stage.

These pages distinguish three kinds of output: recipe and layout produce height-bearing geometry; rasterization and smoothing form a continuous base; support and static asset draws modify it locally. For a mismatch, compare adjacent arrays to find the first changed stage, then inspect its parameters, resources, and execution order. The local examples use these same six arrays at a smaller scale.

The screenshots show inner height without Vista, surface materials, vegetation, or rocks obscuring changes. Web display averages each 8×8 block of the 4096² array into 512² before building the preview mesh. Display resolution and original height precision are separate. The local data figures below read the complete stage arrays.

A natural Stamp parent against local stage heights

In the same recipe-36 Scorched Moor job, parent #496 references resource 17720cadbae59a9e, at (1257.093, 1109.809) meters with orientation approximately 5.35195 rad. Its acceptance record also keeps root 49, group 0, member 254, and grid 10; the parent region-category field is 66. These trace the candidate’s region traversal, not the number of draw shapes in its resource. The arrow follows local +X using the parent matrix’s angular convention.

Natural Stamp 496 position, orientation, and local stage heights

Figure 25 | Local data visualization: a 207×102-meter crop read directly from this job’s 4096² stage heights. Orange marks the parent support AABB; white marks local +X. The difference compares the whole natural-drawing stage, not an isolated draw of #496.

Figure 25 enlarges one area before and after natural drawing in Figure 24. Upper left is support terrain; upper right is after all natural instances. Lower left retains the subsequent Location-texture result. Lower right subtracts pre-natural height from post-natural height: orange indicates raising and blue lowering. The parent record supplies resource, position, and orientation; expanded static assets provide shape; the difference shows what the stage changed.

The parent was not inferred from the final hill’s appearance. The box comes directly from the support AABB in its 304-byte record. It is used for placement checks, not the exact height-texture contour. Other parents also contribute within the view, so neither all relief nor the difference peak inside the box can be attributed solely to #496. Isolating one instance would require per-draw outputs.

Location auxiliary queries, support targets, and local results

Location #1 in this job is at (901.870, 1070.721) meters and references resource 384cd0efc44cee6b. The static resource supplies three support circles; the support output supplies their transformed world positions and radii. All four views use those recorded extents, the same crop, and the same XY scale.

Location 1 support circles, auxiliary height, and stage results

Figure 26 | Local data visualization: a 276×136-meter crop. Orange outlines the three support circles; the white cross is the parent position. Support-stage differences include all supports affecting the crop.

The circumference query retains 196 samples and rejects 116, reporting an envelope of 24.8096–32.5587 meters. Auxiliary height is approximately 29.1874 meters. After family coordination and target processing, the support target is approximately 27.2404 meters. These are separate stage values, not one generic Location height and not a building’s final Z.

Upper left and upper right read post-platform and post-support arrays, showing adjustments near the circles and their transitions. Lower right subtracts the same positions to locate raising and lowering. Lower left includes later natural Stamp and Location asset draws. Auxiliary height and target determine adjustment parameters; the difference locates actual modifications; the final stage includes the later static contours.

Figures 25 and 26 read this job’s pre-encoding float arrays with a common 0–83-meter scale and lighting direction. Their difference plots use individually stated symmetric ranges. Coordinates originate at the lower-left of the 2048-meter domain. Native texture rows are converted in Y exactly once. These are data visualizations; the six-stage overview contains browser screenshots. Neither modifies source heights. Reusing the completed job does not imply that reference validation was rerun.

Stage-consistency problems exposed by multiple biomes

The requests exercise different branches. Ionic Crimson’s platform writes expose a query-version error, Desert Dune exposes row-order and residency binding errors, and Scorched Moor exposes support-layer ordering. Each case below lists the before/after values, reproduction conditions, and comparisons actually performed.

Case Biome or test object Relationship checked
Subdivision spacing Ionic Crimson Configuration scaling for different sampling branches
Mission-extent version Ionic Crimson Initial extent versus post-Location-layout extent
Query-height version Ionic Crimson Hbase queries versus Hplatform drawing base
2D row order Desert Dune CPU publication and texture-query conventions
Record and execution order Scorched Moor CPU self IDs versus GPU layer sorting
Residency binding Desert Dune Mip chain, dimension constants, leaf geometry
Asynchronous query publication Four-biome offline device and state-machine tests GPU completion, row copies, descriptor publication, ownership

Each section follows the failure, its cause, and the repair. The last checks offline GPU completion and the query-publication state machine. In-game concurrency is not fully validated.

Preserve branch-specific subdivision spacing

Base height requires interior points, corners, and connectivity. Incorrect subdivision spacing changes counts, topology, and region ownership, then propagates into heights and placement. Editing the final texture cannot restore an upstream spatial structure that has already changed.

Ionic Crimson first exposed the regular-grid branch. That branch divides configured spacing by 2; the other divides by 4. The independent entry had uniformly divided by 4, producing too many samples and changing point IDs and topology.

Using descriptive variables:

effectiveSpacing = configuredSpacing / (regularGrid ? 2 : 4)

This rule belongs to the recovered subdivision stage, not all map sampling. Each caller must be checked for prior conversion to avoid dividing twice.

Regular-grid counts use float32 dimensions and spacing:

nx = ceil(float32(width  / spacing))
ny = ceil(float32(height / spacing))

x = (i − floor(nx / 2)) × spacing + spacing / 2 + centerX
y = (j − floor(ny / 2)) × spacing + spacing / 2 + centerY

The kernel writes row-major, incrementing X in the inner loop. Half-counts use integer division. Odd and even counts therefore need separate position checks. This grid branch consumes no random numbers; random sampling has its own state advancement.

A teaching domain centered at zero, 80 meters wide, with effective spacing 20 meters produces nx=4. X coordinates are −30, −10, 10, and 30 meters, with the first point half a spacing inside the boundary. Omitting a spacing conversion changes both count and positions. Scaling coordinates afterward cannot restore the original candidate count or IDs.

After repair, Ionic Crimson generates 11,413 interior points and 22,541 corners, totaling 33,954 heights. Complete subdivision comparison also checks regions, edges, candidates, ownership, and spatial buckets: 395 regions, 35,133 edges, and their connections pass. Heights alone are insufficient; incorrect connections can yield locally similar values while changing later searches.

The grid kernel additionally uses 108 synthetic inputs and 25,251 points for boundary cases. Those tests validate the kernel for those inputs, separately from Ionic Crimson’s full subdivision check. Their counts must not be combined into a number of validated maps.

Stamp counts still differed after subdivision was fixed, indicating another source of error. Passing subdivision did not eliminate the need to check the Stamp loop separately.

Read mission extent after Location layout

The complete natural Stamp loop includes candidate generation, resource selection, position and orientation, collision checks, mission-extent checks, retries, and acceptance. Continuation after a rejection also affects random state. A similar final instance count is not enough to establish loop parity.

A candidate can pass collision checks yet fail the mission-extent test. Conversely, an allowed position can fail because of existing instances or boundaries. Diagnosis must keep these different rejection sources separate.

Ionic Crimson once generated 1,006 parents against 1,012 reference records. The first missing candidate had the same position as the reference. It passed collision checks four times but was rejected by mission extent on every attempt, directing investigation toward that input.

The entry used the initialization extent ratio, approximately 0.38. After Location layout, upstream processing expands the extent around placed Locations. The independently calculated later value was approximately 0.3930511. Natural placement should read that value, but the entry still passed the initial one. Fixing the handoff restored the missing six records and the subsequent random sequence.

No six instances were appended to match the target count. The stage parameter was corrected and generation restarted from the initial request. Control flow regenerated positions, resources, acceptance order, and random state. Appending instances at the end would leave later records on the wrong sequence.

Final comparison uses complete 304-byte natural Stamp parent records. Only two address-pointer slots are normalized to zero to remove process-address differences. Coordinates, angles, resource keys, region ownership, and all other fields remain unchanged. Each request is also generated twice to check repeatability separately from reference comparison.

Case Natural Stamp parents Interior points Corners Comparison
Plain 1,174 11,928 23,606 Complete normalized records match; subdivision-height bit patterns match
Desert Dune 1,184 11,854 23,459 Same
Ionic Crimson 1,012 11,413 22,541 Same
Scorched Moor 1,501 11,798 23,391 Same

These count parents, not visible hills, trees, or rocks. One parent resource may contain multiple local height shapes and objects, and multiple parents can overlap in the final terrain.

Support queries and drawing use different height versions

Location support queries the terrain before calculating auxiliary height, parent-child coordination, and expansion. Reading the wrong height version changes the target even when other parameters match. Ionic Crimson’s platforms exposed this failure.

There are 23 forced-flattening regions. Platform targets are calculated by connected groups: adjacent regions with the same configuration form a group, and eligible outer edges contribute queries. Each boundary midpoint is extended outward by 32 meters, then queried with a 32-meter radius. The group target follows:

target = min(0.5 × low + 0.5 × high, low + limit)

low and high come from a valid height envelope; limit comes from region configuration. If no valid envelope exists, the recovered branch sets both values to zero. That fallback must accompany the formula.

For teaching low=6 meters, high=10 meters, and limit=1 meter, the midpoint is 8 meters but low + limit is 7, so the target is 7 meters. Limit caps raising relative to the low side, rather than multiplying final height by a uniform strength. These explanatory numbers are separate from the measured targets below.

The 23 regions form one connected group. Positions of 13 boundary queries and all 23 target values pass comparison; the target is approximately 7.0749998 meters. Platform drawing then changes 802,760 GPU pixels. These validate target calculation and platform drawing separately, not the input subsequently read by Location support.

The entry rebuilt its CPU query pyramid from platform output. The reference path instead retained the pre-platform base-query descriptor. Location auxiliary heights continue to use that descriptor even though current GPU terrain already includes platforms.

Both Hbase and Hplatform are valid outputs, but the implementation treated them as one overwriteable height. Correct query formulas cannot prevent changes in auxiliary targets when the wrong field is read. Separate variables make the two inputs explicit:

baseHeight    = Smooth(Rasterize(baseTriangles))
queryLevels   = BuildQueryPyramid(baseHeight)
platformHeight = DrawPlatforms(baseHeight, platformTargets)
supportTarget = QuerySupport(queryLevels, location)
supportHeight = DrawSupport(platformHeight, supportTarget)

This pseudocode omits resource creation and asynchronous waits. Its key distinction is that queryLevels retains base height while platformHeight contains current drawing output. Overwriting one generic height at every step hides the incorrect substitution.

In the current sorting regression, maximum error inside Ionic Crimson’s central 450 meters is approximately 1.5011 millimeters. In the earlier query-version repair, maximum error fell from approximately 0.4278 meters to 1.5030 millimeters, while pixels above 1 centimeter fell from 310,128 to zero. The two millimeter values belong to different downstream versions. The earlier 42.8-centimeter peak has a specific stage cause and cannot be explained as ordinary floating-point error.

The other three cases have no platform writes, so their pre/post-flattening base arrays are identical. That is why the same connection error did not produce four obvious failures. Adding biomes activates different branches and reveals input mistakes hidden when two arrays happen to match.

Support target remains an auxiliary height-processing value. It affects terrain and family coordination, not a building’s final Z. Building assembly, surface sampling, and collision have separate runtime steps.

Convert CPU rows only when publishing the texture

Desert Dune once had a mean inner-area discrepancy of approximately 26.534 meters. Two handoffs were wrong: CPU-to-texture publication omitted its row conversion, and later texture-to-query-pyramid processing introduced an extra flip. Both handled 2D arrays but could not independently choose a flip from visual appearance.

The field has a 2D interpretation and a linear storage order. CPU calculation enumerates rows toward world +Y; this pipeline’s texture convention stores them toward −Y. Both arrays can have valid dimensions and plausible heights while direct exchange maps queries to the wrong terrain positions.

This is the convention of the current pipeline, not a universal requirement of every graphics API. Each interface must specify which side row zero represents, where conversion occurs, and which order query indexing uses.

Publication converts:

texture[row, column] = cpu[N − 1 − row, column]

Float32 samples move without arithmetic or changes to their bit patterns. The C++ interface rejects zero dimensions, mismatched sample counts, and nonfinite values before copying rows.

CPU-to-texture row conversion

Figure 27 | Teaching diagram: publication of a four-row array. Column order and sample bit patterns remain unchanged. A later flip would restore the wrong orientation.

The repair centralizes publication in one C++ function and labels output as texture row order. Later stages follow that definition instead of deciding from the displayed image. Tests use asymmetric arrays because vertically symmetric terrain can conceal reversal, and check truncated inputs and nonfinite-height rejection.

A coordinate error can affect a broad area without making the picture obviously invalid. A generally circular terrain can retain plausible slopes and lowlands after a flip. Numerical diagnosis must track one world position through every array’s row and column, rather than compare only thumbnails.

Keep record IDs separate from GPU execution order

Scorched Moor previously drew in CPU insertion order, omitting layer sorting. Support targets, adjacency indices, and many buffers were already correct, yet final maximum error remained approximately 3.42 centimeters. Inspecting only parameter tables could misidentify the residual as a precision issue.

Support data contains circles, rectangles, adjacency spans, and self IDs in vertices. CPU records are organized by Location to reference these tables consistently. GPU draws also need height-layer ordering. The two orders serve different tasks.

CPU records currently sort by increasing Location ID, placing circles before rectangles within each Location and preserving input order within a shape type. Circle and rectangle parameter tables remain separate. Each draw’s self is a CPU row ID used to find adjacency and parameters.

For this support pass, GPU execution sorts rectangles at layer 900 before circles at 901, stably within each layer. The execution list contains CPU row IDs, not IDs in either shape-parameter table. Changing execution order must preserve parameter-index relationships.

Record IDs and execution permutation

Figure 28 | Teaching diagram: mixed circle/rectangle records. CPU row IDs are retained while GPU reads a separate execution permutation. Layers 900/901 belong to this support path, not general material priorities.

For two Locations, CPU records can be A-circle, A-rectangle, B-circle, B-rectangle. GPU draws both rectangles first, then both circles, still using the original CPU row IDs for each parameter lookup. Record organization and draw-layer order can therefore coexist.

In this interface, CPU rows identify records and GPU permutation selects execution order. Reordering only execution preserves IDs and references. Rebuilding CPU rows also requires remapping self and adjacency spans. These IDs are valid within the current buffers and interface, not permanent identifiers across jobs. Instance batching and material sorting likewise need to distinguish records from execution order.

After restoring a separate GPU permutation, support, natural Stamp, and Location drawing were rerun. Central maximum error fell from 34.1530 to 1.3332 millimeters. Pixels above 1 centimeter fell from 4,826 to zero; those above 1 millimeter fell from 14,493 to 37. Parameter buffers did not change; execution order did.

Because blending depends on order, the complete downstream sequence must rerun. An improvement at one point immediately after support is not a final-error measurement: later textures may overwrite or modify it again.

C++ migration outputs two permutations: source shapes to CPU rows, and GPU execution to those rows. It also rebuilds adjacency spans and updates vertex self IDs. Omitting any one can leave correct ordering with incorrect references.

The four cases have 77, 88, 74, and 87 support draws. New C++ buffers and execution permutations match archived inputs byte for byte. Redrawn support pixels are bit-identical to archived support output. This regression establishes preserved sorting behavior, not a new full-map comparison against the original target.

Resident mips, dimension constants, and geometry must agree

Current Desert Dune maximum error in the central common area is approximately 0.5913 millimeters. The following diagnosis explains the residency repair and retains an older full-map result including Vista. Their comparison areas remain separate.

After row-order repair, a peak of approximately 11.543 meters remained. Base height, platforms, and support all gave approximately 49.189884 meters there. Natural draw 3 changed it to approximately 60.733341 meters; Location and Vista did not change it afterward. The failure could therefore be isolated to a specific draw rather than a global height baseline.

A height texture’s mip level is often treated as a resolution choice. Here residency also affects resource dimensions, shader constants, and local leaf geometry. Switching only the byte start while retaining old dimensions and geometry changes height coverage.

The same resource is 608×608 at full resolution and 304×304 at mip1. Controlled tests select mip0, mip1, and mip2 while updating the texture chain, dimension constants, and leaf root/center together. With mip1, the point retains base height and its 3×3 neighborhood reproduces the reference’s uncovered center and surrounding contributions. Mip2 produces another result.

Residency level and synchronized bindings

Figure 29 | Mechanism diagram: residency determines the pixel chain, dimension constants, and leaf geometry together. The 608²/304² dimensions belong to this Desert Dune diagnostic resource. The diagram explains binding dependencies rather than displaying source pixels.

No target pixel was manually corrected. The binding policy changed and the request was regenerated. In that older Desert Dune version including Vista, 4096² maximum error fell to approximately 0.5913 millimeters, while the old peak’s error fell to approximately 0.003815 millimeters. This full-map record belongs to that version; the latest sorting version’s outer terrain still needs another composition and comparison.

This validation round uses explicit startup max_mip=1. It does not uniformly cut every resource to mip1. Fixed-loading and streamed resources follow their own descriptors, from which the generator builds the valid resident chain. Their initial levels and directory states cannot be replaced by one global cropping operation.

Later resource state agrees with this policy and supports the binding diagnosis. It does not establish the historical startup value or prove that asynchronous upgrades finished before every draw. The setting remains an explicit reproducibility input, not a universal original-game default.

Static directories, upgrade requests, CPU state changes, and GPU upload completion are separate stages. A CPU field set to the target mip does not make the new texture usable. Upgrading requires uploading the new prefix and copying the old chain into shifted levels. Testing those operation parameters does not establish real concurrent timing.

Publish query data only after GPU readback completes

Support queries read multilevel heights, with asynchronous readback between GPU calculation and CPU use. Submitted work and existing texture/request objects do not make pixels immediately readable. Figure 30 follows completion, mapping the staging texture, copying rows, and publishing a query descriptor. CPU queries can use the data only after publication.

The October 7 work checks request creation, queuing, GPU calculation, readback, and descriptor publication. On an independent D3D11 device, actual outputs for eight query levels in each of four biomes pass comparison. CPU row copies, completion polling, and publication are checked separately.

Asynchronous query readback and publication

Figure 30 | Mechanism diagram: query-level lifetime. A pending handle is retained until readiness. Only a successful completion query allows Map, RowPitch-aware copying, and stable CPU publication. Offline device tests are separate from in-game concurrency validation.

Readback textures have a row pitch. RowPitch may exceed valid pixel bytes, so copying must read row by row rather than treat mapped storage as tightly packed. For this single-channel float32 format, valid bytes per row are width × 4.

After completion, the staging texture is mapped, copied into CPU storage row by row, unmapped, and its associated device objects released. Publication writes the valid descriptor into the query table and clears that level’s pending handle. In the current eight-level state machine, the poll that handles the last batch still returns “pending entries encountered this round.” Only the next empty poll sends completion notification.

That return rule affects how the caller drives the state machine. Ending as soon as no handles remain after processing changes notification timing. Blocking until the data exists can help an offline numerical test, but does not replace checking the original publication process.

Ownership is also split. The request record can be recycled after publication while pixel storage remains available to CPU queries. Releasing both as one object may seem to work until later allocation reuses the memory. Local tests overwrite recycled records with different bytes to confirm that published queries no longer depend on them. Pixel cleanup is checked separately.

Current evidence covers actual offline GPU completion queries and the C++ publication state machine. It does not cover in-game resource registration, original worker-pool concurrency, or the complete ownership lifecycle. The offline device establishes these outputs and call ordering, not every runtime scheduling condition.

Preserving stage definitions in the C++ migration

This work moves row publication, support-buffer reordering, GPU execution permutations, and a continuous height-drawing entry into C++. The entry calls this implementation’s support, natural Stamp, and Location programs in order. Actual height pixels remain computed by the independently written HLSL.

Python still handles some static-resource parsing, serialization, and upstream orchestration. Major algorithm modules and downstream drawing have C++ implementations and a continuous entry, but Python remains in orchestration and the GPU still computes height pixels.

Component Current responsibility Information that must survive migration
C++ scene core Requests, placement, regions, subdivision, corner heights, natural Stamp parents Configuration branches, record order, random state
Static-resource adapter Level/Unit, texture directories, local transforms Resource versions, dimensions, transform hierarchy
C++ support module Queries, targets, adjacency, record and execution permutations Query-height version, CPU self IDs, GPU layer order
C++/HLSL height drawing Rasterization, smoothing, support, natural and Location writes Row order, mip binding, samplers, stage outputs
Web tools Job submission, reports, preview Input/output identity; no separate height result

The continuous rendering entry point accepts stage data but does not check whether it came from the current request. The end-to-end validator traces its relationship to the request and static library. Running the renderer successfully with supplied support heights or instance files proves only that it processes those inputs.

Validation uses a new output directory and rejects existing results and success markers. A failed stage stops immediately, without later outputs. This prevents an earlier successful heightmap from being mistaken for output of a new failed job. Each retained file can then be traced to a successful stage in the current job.

Only after the four regenerations finish does the validator read frozen Web arrays. The first attempt used GPU queries uniformly and left small discrepancies in Plain and Desert Dune. Inspection established that their frozen baselines used CPU float32 queries, while Ionic Crimson and Scorched Moor used GPU queries. Preserving each original configuration produces matching bit patterns and hashes for all four 4096² arrays.

Switching query backends can change the output. They can be close under these settings while floating-point execution still changes low bits. Migration checks must first reproduce the selected baseline’s settings. A later backend unification needs its own error criterion.

Bitwise parity and millimeter residuals compare different things

These experiments include module comparison, implementation regression, and target comparison. Equal array dimensions do not make their conclusions equivalent. Each comparison must identify its inputs and outputs.

Comparison Inputs and subjects Result Does not establish
Same-input kernel comparison Independent stage inputs, custom and reference kernels in one host Tested stage pixels can be bit-identical All upstream parameters or historical device states match
C++ versus frozen Web Same request, static library, explicit startup/query settings; pre-Vista 4096² float32 Zero mismatches and equal hashes for all four cases A full map identical to the reference target
Independent generation versus reference Common central area after generation finishes Maximum error approximately 0.56–1.50 mm All Seeds, outer terrain, collision, or networking pass

Error is calculated as:

error(P) = abs(generated(P) − reference(P))
MAE      = sum(error(P)) / sampleCount
maxError = max(error(P))
over1mm  = count(error(P) > 0.001)

The means here are MAE, not RMS. R16 agreement in Parts I and II, central-area RMS, and this round’s MAE come from different experiments. They cannot be joined into a curve claiming a particular precision improvement.

Outer Vista blending mechanism

Figure 31 | Mechanism diagram: outer blending, not a full-map acceptance result for all four biomes.

Outside the central common area, validation progress differs. Plain and Ionic Crimson have comparisons within radius 620 meters. Scorched Moor has only the central reference crop. An earlier Desert Dune version including Vista has a full-map record, but the latest sorting version has not repeated full-map Vista regression. The uniform four-case C++/Web bitwise comparison ends after Location height writes and before Vista.

The remaining millimeter residuals need further diagnosis. Maximum-error points in two cases were replayed draw by draw to identify the natural Stamps and Locations modifying them. Custom and reference kernels agree on the same independently generated inputs. This narrows the search, but does not identify the historical parameter or numerical step responsible. The available evidence does not identify GPU precision as the cause of all remaining differences.

The report retains finalScene=false because the complete scene entry, all outer terrain, and runtime resource timing have not passed one unified validation. Parent-record, support-sorting, and tested-pixel results remain valid, while the complete scene is still unvalidated.

Validation still needed for UE integration

Earlier installments discuss UE height sessions, terrain meshes, and network digests. These new C++/HLSL results come from a standalone Windows D3D11 validator, and do not establish equivalent behavior inside UE RHI. Engine work must connect the new stage definitions and sorting rules, then check device resources, packaging, and runtime behavior.

The integration must carry the confirmed dependencies: preserve base-query heights separately from platform output, keep CPU self IDs separate from GPU execution, convert row order once, and update resident mips with dimension constants and leaf geometry. Migrating only calculation functions while retaining old input bindings can reintroduce the same failures.

A dedicated server cannot directly adopt the client’s graphics path. Continuing with CPU heights requires per-stage numerical comparison against this GPU baseline. Reading offline outputs requires an explicit generation and version contract. This is an engine decision, not something resolved by four pixel comparisons.

Runtime trees, rocks, and POIs also require separate checks. Passing natural Stamp parents does not revalidate all child mesh, vegetation, and rock world matrices or collision. Passing Location height textures does not establish final building positions or navigation. Part II’s object checks retain their original dates and scope; this article adds no full-scene object-count claim.

The four-request baseline can serve as engine-integration regression input. Compare stage arrays and resource bindings first, then full outer terrain, object transforms, collision, and networking. A discrepancy can then be traced to a known input and stage instead of guessed from the final screenshot.

Conclusion

Four fixed requests now generate their results through the same configuration, base-height, and local-drawing workflow. Additional biomes also activate previously untested branches. Without platform writes, Hbase and Hplatform match and an incorrect query binding may remain invisible. Ionic Crimson’s platform writes made that error observable.

These fixes address the data passed between stages. Spacing changed point sets and topology; stale mission extent rejected valid candidates; a wrong query version changed support targets. Row order, drawing order, mip binding, and GPU completion each affected downstream writes. Migration must preserve input versions, indices, and execution conditions alongside formulas.

With each case’s baseline settings, all four pre-Vista 4096² float32 outputs are bit-identical between C++/HLSL and frozen Web, establishing that migration preserved existing results. Reference-target comparisons still have millimeter residuals in the common central area. The first checks whether migration preserved the output; the second measures the difference from the reference target. The complete scene needs separate acceptance.

Next, the established stage definitions must enter UE, followed by outer Vista, object transforms, collision, and networking checks. Residual points can still be investigated, but that work cannot replace runtime tests. Archived requests, stage arrays, and resource-binding conditions provide the baseline for tracing integration differences.

AI collaboration retrospective

This article uses the week’s stage notes, complete-record comparisons, four raw C++/Web height regressions, and C++ source for row order, support sorting, and query publication. Comparison images reuse existing height-array results. Configuration diagrams reorganize presentation SVGs, while full parameter and type tables remain in the text. Stage figures reuse archived outputs; row-order, sorting, mip, and readback diagrams explain migration details. Preparing the article did not rerun a complete map or add engine performance measurements.

Historical records required particular care because later results had superseded some findings. Early descriptions of base-height-only output, Ionic Crimson’s 1,006 Stamps, and Scorched Moor’s 3.42-centimeter residual were superseded by later results. Reading only a document’s opening or adding together success claims from separate reports would misstate the current implementation.

Claims of bitwise equality also need explicit comparison subjects. The four pre-Vista C++/Web 4096² float32 arrays match, while reference-target comparison still leaves residuals. Comparison subjects, settings, area, and format must remain explicit for later reproduction. The article therefore keeps both the actual input read at each stage and the experiment supporting that conclusion.

Leave a Comment

Discover more from AI Native Game Development

Subscribe now to keep reading and get access to the full archive.

Continue reading