wip(assembler): M1 round 2 -- surface recipe closer to legacy; native still slower and visibly buggy
Luna, M1 round 2 of 4: round 1's mismatch was an underfit recipe (two synthetic profiles, missing large-scale surface terms). Now: biome IDs match 4,096/4,096, mean height offset -1.366 vox, density MAE 274.04 -> 17.23 vox, sign mismatches 55.2% -> 18.14%. Bound audit 298 boxes / 8,046 points, 0 misses, negative control caught. Hash c5e0371612e927af at W1/W8/W16. Surface query 8-14 ms / 4,096 points. Matched 3D capture pairs added. Problems (my review of the captures + her timings): - matched 120-tile views: full-detail readiness 5.227 s native vs 2.882 s legacy -- SLOWER; - surface capture shows a rectangular flat-topped raised block with vertical walls (tile/patch-level height bug, inferred); - cliff capture is broken (floating shard, debris trail), not a cliff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
No files matched your search
@@ -139,11 +139,117 @@ also not measured by audit mode. Earlier small-view D3D attempt timings (owner 0
|
|||||||
view had no geometry. The 23.085 s matched-workload gate remains open. The ~1.57 s aim remains
|
view had no geometry. The 23.085 s matched-workload gate remains open. The ~1.57 s aim remains
|
||||||
reasonable, but round 1 has no valid full-view evidence to support or reject it.
|
reasonable, but round 1 has no valid full-view evidence to support or reject it.
|
||||||
|
|
||||||
|
## Round 2 results
|
||||||
|
|
||||||
|
### Round 1 mismatch diagnosis
|
||||||
|
|
||||||
|
Round 1 was structurally underfit, not mis-scaled or shifted by a constant height datum. The sample
|
||||||
|
used two synthetic biome profiles (IDs 4097 and 4098) and a reduced height model. The owner export
|
||||||
|
uses four resolved surface profiles and relief/moisture climate ranges; the baseline also omitted
|
||||||
|
the mountain and relief response, height warp, output-space profile blend, coast/beach and cliff
|
||||||
|
shaping, and per-biome cap detail added in evaluator 2. Both sides use 25 cm voxels, and the current
|
||||||
|
4,096-column check finds only a -1.366-voxel mean height offset. The owner and Core biome IDs now match on all 4,096
|
||||||
|
diagnostic columns. These measurements point to missing structure and profile assignment rather
|
||||||
|
than a unit conversion, datum offset, detail-noise phase, or biome-ID selection error.
|
||||||
|
|
||||||
|
This round changed those terms as a group, so the before/after result does not isolate each term's
|
||||||
|
individual contribution. On the same 4,096 density-query points, mean absolute error fell from
|
||||||
|
274.0383 to 17.2262 voxels, maximum density error from 841.4257 to 392.4136 voxels, and sign
|
||||||
|
mismatches from 2,261/4,096 (55.2%) to 743/4,096 (18.14%). The separate 4,096-column height
|
||||||
|
diagnostic now reports MAE 13.060 voxels, maximum error 203.728 voxels, correlation 0.96496 and
|
||||||
|
linear-fit slope 0.95136. Residual peaks and sign errors remain visible; this is not parity.
|
||||||
|
|
||||||
|
### Evaluator, manifest and audit
|
||||||
|
|
||||||
|
Evaluator 2 keeps schema 1 and fixed signed 1/256-voxel units while adding deterministic
|
||||||
|
climate-based assignment across the owner's four profiles, output-space ground and cap blending,
|
||||||
|
per-profile relief/height warp/beach/cliff/cap settings, fixed-season water outputs, and stable-ID
|
||||||
|
bounded 3D overhang exceptions. Player carve/fill edits remain unrestricted; route constraints only
|
||||||
|
apply to automatic system generation or changes. This sample has one bounded exception
|
||||||
|
(`09812c26310b90c9`) and no route yet; route planning and capsule sweep remain round 3.
|
||||||
|
|
||||||
|
The evaluator-v2 review manifest is 4,514 bytes. Recipe hash is `dbe949967c0dfbce`, plan hash is
|
||||||
|
`165a00c4a77ebae0`, and manifest hash is `203d7043158069f6`. The 4,096-sample evaluator hash is
|
||||||
|
`c5e0371612e927af` at W1, W8 and W16. Bounds audit passed with 298 boxes and 8,046 points: zero
|
||||||
|
missed and zero unknown bounds; 16 biome-border boxes, 16 cliff boxes, 9 cap boxes, 248
|
||||||
|
negative-coordinate boxes, and one exception box with 27 exception samples. The deliberately
|
||||||
|
wrong one-sided bound was detected.
|
||||||
|
|
||||||
|
The final interval audit followed one failed audit with 102 missed bounds. Inspection found the cap
|
||||||
|
range was overwritten by a global plan range and that plateau/beach and cliff envelopes were
|
||||||
|
incomplete. Those envelopes were corrected before the final run. The current result is a sampled
|
||||||
|
audit of this evaluator and recipe, not a mathematical proof for arbitrary future inputs.
|
||||||
|
|
||||||
|
### Matched 3D captures
|
||||||
|
|
||||||
|
Each pair uses the same camera for owner and native Core, with full-detail tiles ready before
|
||||||
|
capture. Images are 640×400 JPEGs; each file is 9,089–17,961 bytes. The native surface, border and
|
||||||
|
coast pairs broadly agree in silhouette. The cliff pair shows a remaining large difference: the
|
||||||
|
owner has an extreme noisy overhang, while the prototype currently emits the bounded, smoother
|
||||||
|
shelf. Water height/fill is emitted as a field sample, but a water overlay mesh is not implemented,
|
||||||
|
so the coast captures do not show rendered water.
|
||||||
|
|
||||||
|
| Site and shared viewer camera (x, y, z; yaw, pitch) | Owner reference | Native Core |
|
||||||
|
|---|---|---|
|
||||||
|
| Surface `(0, -35, -487.943; 0, -22)` |  |  |
|
||||||
|
| Biome border `(128, -243, -492.912; 0, -22)` |  |  |
|
||||||
|
| Coast `(336, -67, -477.755; 0, -22)` |  |  |
|
||||||
|
| Cliff `(-168, 70, -445.712; 0, 34)` |  |  |
|
||||||
|
|
||||||
|
`Present` continued to return `DXGI_STATUS_OCCLUDED`, including after bringing the viewer forward.
|
||||||
|
The first surface back-buffer file was black and was excluded. The foreground retry's copied
|
||||||
|
back-buffer was nonblack and visibly contained generated geometry; the final pairs above use that
|
||||||
|
verified capture path. This validates the native mesh/viewer path for these captures, but the
|
||||||
|
presentation status itself remains unresolved.
|
||||||
|
|
||||||
|
### Query, cave compile and full-view timings
|
||||||
|
|
||||||
|
| Measurement | Result | Scope |
|
||||||
|
|---|---:|---|
|
||||||
|
| Surface point queries W1 | 13.569 ms | 4,096 evaluator points, point evaluation only |
|
||||||
|
| Surface point queries W8 | 8.085 ms | same points, same field hash |
|
||||||
|
| Surface point queries W16 | 12.396 ms | same points, same field hash |
|
||||||
|
| Legacy owner density queries | 61.974 ms | same 4,096 points; reference timing, not a performance gate |
|
||||||
|
| Core world compile and query bridge | 36.295 ms; 45/45 exact | Surface recipe; `World::densityAt`, no render mesh |
|
||||||
|
| Existing cave-only shape compile | 0.100 s | no-manifest `type_3_Maze.json` smoke, from round 1 |
|
||||||
|
| Owner full-detail readiness | mean 2.882 s | 120 near tiles (64 LOD0 + 56 LOD1), 16 workers, four capture sites |
|
||||||
|
| Native Core full-detail readiness | mean 5.227 s | same tile count, workers and sites; range 4.401–6.636 s |
|
||||||
|
|
||||||
|
| Capture site | Owner | Native Core |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Surface | 2.873 s | 4.401 s |
|
||||||
|
| Biome border | 2.904 s | 4.907 s |
|
||||||
|
| Coast | 2.877 s | 4.962 s |
|
||||||
|
| Cliff | 2.874 s | 6.636 s |
|
||||||
|
|
||||||
|
The cave compile and surface query figures are separate workloads. The surface viewer took longer:
|
||||||
|
the native 5.227 s mean is 81% above the owner's 2.882 s on these matched views. This 120-tile
|
||||||
|
clip-radius-1 workload is not the agreed 23.085 s matched-workload gate. All viewer runs used 16
|
||||||
|
workers; maximum observed full-detail time was 6.636 s at the upward-facing cliff camera.
|
||||||
|
|
||||||
|
### Round 2 run log
|
||||||
|
|
||||||
|
- The first evaluator build failed because the JSON parser version has no `IsBool`/`IsNull` methods.
|
||||||
|
The parser was changed to inspect the JSON value variant, and the next isolated build passed.
|
||||||
|
- The first expanded interval audit failed with 102 escaped samples. The envelope fixes described
|
||||||
|
above were applied and the final audit passed. No failing result was discarded from the report.
|
||||||
|
- The initial native D3D surface capture was black with an occluded presentation status. A
|
||||||
|
foreground recapture still reported the same status but its back-buffer contained visible mesh
|
||||||
|
geometry; only the verified foreground copy was used. All four final owner/native pairs are
|
||||||
|
downscaled and below 18 KB per image.
|
||||||
|
- Successful final standalone builds completed in under one minute with `/MP16`, Normal priority
|
||||||
|
and 16 CPUs. Existing `vswhere.exe` lookup output and C4838 narrowing warnings at
|
||||||
|
`VoxelStrateManager.cpp:3974-3975` remain nonfatal. No process exceeded the 15-minute limit or
|
||||||
|
8 GB cap; no `0xC0000142` retry occurred. The crash-folder check found only the pre-existing
|
||||||
|
`UECC-Windows-F1A3EFD5469F5DD62E68EBBACAC84376_0000` from 2026-09-17 and no new UECC folder.
|
||||||
|
- No owner project or BuildHost project was built. No `.uasset` was opened or changed. No new source
|
||||||
|
files were added.
|
||||||
|
|
||||||
### Remaining work by round
|
### Remaining work by round
|
||||||
|
|
||||||
- **Round 2:** Match the legacy reference more closely; add output-space biome blending, coast/beach,
|
- **Round 2:** Evaluator-v2 surface recipe, matched 3D comparisons and expanded bounds audit are
|
||||||
cap detail, cliffs and bounded 3D overhang features with stable IDs and certified influence
|
implemented. The measured residual mismatch, rendered-water gap, and owner/native cliff difference
|
||||||
bounds. Expand bound audits across borders, caps, negative coordinates and exceptions.
|
remain open for round 3 review; round 2 does not claim surface parity.
|
||||||
- **Round 3:** Add the finite-strate capsule route and support constraints, sweep it against the final
|
- **Round 3:** Add the finite-strate capsule route and support constraints, sweep it against the final
|
||||||
composed field, and reject or repair any failing automatic plan. Verify full-evaluator W1/W8/W16
|
composed field, and reject or repair any failing automatic plan. Verify full-evaluator W1/W8/W16
|
||||||
hashes. Measure collision readiness separately from query and mesh readiness; demonstrate a
|
hashes. Measure collision readiness separately from query and mesh readiness; demonstrate a
|
||||||
|
|||||||
@@ -79,22 +79,33 @@ Pass `analyticOnly = false` to retain the reference packed-page compiler. Packed
|
|||||||
parent-cell coordinates and an 8-voxel apron; each covers a 128-voxel cube or one larger coarse
|
parent-cell coordinates and an 8-voxel apron; each covers a 128-voxel cube or one larger coarse
|
||||||
tile. This path remains available for parity checks and workloads that need the packed lattice.
|
tile. This path remains available for parity checks and workloads that need the packed lattice.
|
||||||
|
|
||||||
## World assembler manifest v1 (prototype)
|
## World assembler manifest v1 (prototype; evaluator v1 and v2)
|
||||||
|
|
||||||
`ResolvedWorldSnapshot::FromJson` accepts an optional `world_assembly` object alongside the owner
|
`ResolvedWorldSnapshot::FromJson` accepts an optional `world_assembly` object alongside the owner
|
||||||
snapshot. The object is the shipped-plan payload shape: a fixed-scale recipe and finite-strate plan
|
snapshot. The object is the shipped-plan payload shape: a fixed-scale recipe and finite-strate plan
|
||||||
with hashes, no generated mesh or voxel samples. The reviewed sample payload is
|
with hashes, no generated mesh or voxel samples. The current evaluator-v2 review payload is
|
||||||
[`round1-manifest.json`](../Research/WorldAssemblerM1/round1-manifest.json). Round 4 still needs to
|
[`round2/world_assembly.json`](../Research/WorldAssemblerM1/round2/world_assembly.json); the
|
||||||
connect this payload to the season asset/hash path.
|
evaluator-v1 sample remains at [`round1-manifest.json`](../Research/WorldAssemblerM1/round1-manifest.json).
|
||||||
|
Round 4 still needs to connect this payload to the season asset/hash path.
|
||||||
|
|
||||||
Every coordinate and scalar uses signed integer units of 1/256 voxel (`units_per_voxel: 256`),
|
Every coordinate and scalar uses signed integer units of 1/256 voxel (`units_per_voxel: 256`),
|
||||||
independent of the plugin's 25 cm Core voxel and of tile size. Schema and evaluator versions are
|
independent of the plugin's 25 cm Core voxel and of tile size. The schema version is 1; evaluator
|
||||||
both 1. JSON integer fields are canonicalized in declared field order and hashed as 4- or 8-byte
|
versions 1 and 2 are accepted, with evaluator 2 used for new recipes. Evaluator 1 retains its
|
||||||
little-endian words with FNV-1a 64. Hashes are emitted as 16 lowercase hexadecimal characters;
|
original evaluation and hash semantics. JSON integer fields are canonicalized in declared field
|
||||||
JSON key order and whitespace do not affect them. The root, recipe, plan and each stratum carry a
|
order and hashed as 4- or 8-byte little-endian words with FNV-1a 64. Hashes are emitted as 16
|
||||||
content hash. The recipe stores its seed, warped biome-map settings and one to eight biome profiles;
|
lowercase hexadecimal characters; JSON key order and whitespace do not affect them. The root,
|
||||||
the plan stores finite-strate extents, cap settings, stable-ID features and route points. Bounds,
|
recipe, plan and each stratum carry a content hash. The recipe stores its seed, warped biome-map
|
||||||
feature count and route count are validated before a manifest is accepted.
|
settings and one to eight biome profiles; the plan stores finite-strate extents, cap settings,
|
||||||
|
stable-ID features, fixed-season water and route points. Bounds, feature count and route count are
|
||||||
|
validated before a manifest is accepted.
|
||||||
|
|
||||||
|
Evaluator 2 adds climate-based biome assignment, output-space blending of both ground and cap
|
||||||
|
profiles, per-biome relief and height warp, beach width, cliff limits, and optional per-biome cap
|
||||||
|
profiles. Feature types 1, 2 and 3 represent a cliff plane, a bounded 3D overhang box and an authored
|
||||||
|
cap feature. An overhang feature has a stable ID and a finite AABB clipped to its stratum; intervals
|
||||||
|
outside the box can use its certified influence bound, while intersecting intervals remain on the
|
||||||
|
evaluation path. Water surface and fill are separate sample outputs. The season water plane is fixed;
|
||||||
|
this block does not generate a water mesh.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string error;
|
std::string error;
|
||||||
@@ -105,18 +116,20 @@ const auto* loaded = snapshot.worldAssembly(); // null when the owner snapshot h
|
|||||||
|
|
||||||
`Heightfield2D` is the first field block. `point(xQ, yQ, zQ)` evaluates the biome map and blended
|
`Heightfield2D` is the first field block. `point(xQ, yQ, zQ)` evaluates the biome map and blended
|
||||||
height profiles at fixed-scale coordinates, returning the ground and cap heights, primary and
|
height profiles at fixed-scale coordinates, returning the ground and cap heights, primary and
|
||||||
neighbor biome IDs, Q16 blend weight, and signed density. Negative density is solid. Its block
|
neighbor biome IDs, Q16 blend weight and signed density. Evaluator 2 also returns the fixed water
|
||||||
contract reads `biome` and writes `height` plus `rock`; `water`, `waterSurface`, `route` and
|
surface and water-fill sample values. Negative density is solid. Its contract reads `biome` and
|
||||||
`surfaceTags` are declared channels for later blocks. The detail envelope reports maximum absolute
|
writes `height`, `rock`, `water` and `waterSurface`; route and `surfaceTags` remain declared channels
|
||||||
vertical displacement and minimum feature size. Influence is global in XY and limited to the
|
for later blocks. The detail envelope reports maximum absolute displacement and minimum feature
|
||||||
finite stratum's Z extent.
|
size. Influence is global in XY and limited to the finite stratum's Z extent, except for the
|
||||||
|
explicitly bounded evaluator-v2 feature boxes.
|
||||||
|
|
||||||
`interval(minXQ, minYQ, minZQ, maxXQ, maxYQ, maxZQ)` returns a conservative density range or
|
`interval(minXQ, minYQ, minZQ, maxXQ, maxYQ, maxZQ)` returns a conservative density range or
|
||||||
`known == false`; callers must keep evaluating when it is unknown. The current heightfield range
|
`known == false`; callers must keep evaluating when it is unknown. The current heightfield range
|
||||||
uses the maximum recipe-profile amplitude and cap undulation over the whole biome set. The public
|
uses conservative profile, beach, cliff, cap and bounded-feature envelopes. The public
|
||||||
`AuditHeightfieldBounds` samples points inside randomized boxes and includes a deliberately false
|
`AuditHeightfieldBounds` samples points in randomized boxes and targeted biome-border, cliff, cap,
|
||||||
one-sided range as a negative control. A pass means no sampled point escaped and the negative
|
negative-coordinate and overhang boxes, and includes a deliberately false one-sided range as a
|
||||||
control was rejected; it is an audit of this block implementation, not a proof for future blocks.
|
negative control. A pass means no sampled point escaped and the negative control was rejected; it
|
||||||
|
is an audit of this block implementation, not a proof for future blocks.
|
||||||
|
|
||||||
## Queries and tiles
|
## Queries and tiles
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ namespace VoxelForgeCore
|
|||||||
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
|
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
|
||||||
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
|
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
|
||||||
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 1;
|
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 1;
|
||||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 1;
|
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 2;
|
||||||
|
|
||||||
enum class FieldChannel : std::uint32_t
|
enum class FieldChannel : std::uint32_t
|
||||||
{
|
{
|
||||||
@@ -124,6 +124,29 @@ namespace VoxelForgeCore
|
|||||||
std::int64_t mountainWavelengthSubvoxels = 256 * 128;
|
std::int64_t mountainWavelengthSubvoxels = 256 * 128;
|
||||||
std::int64_t detailAmplitudeSubvoxels = 0;
|
std::int64_t detailAmplitudeSubvoxels = 0;
|
||||||
std::int64_t detailWavelengthSubvoxels = 256 * 16;
|
std::int64_t detailWavelengthSubvoxels = 256 * 16;
|
||||||
|
// Evaluator 2: climate box and the resolved surface-operation parameters.
|
||||||
|
// Evaluateur 2 : plage climatique et parametres resolus des operations de surface.
|
||||||
|
std::int64_t reliefMinimumQ16 = 0, reliefMaximumQ16 = 1ll << 16;
|
||||||
|
std::int64_t moistureMinimumQ16 = 0, moistureMaximumQ16 = 1ll << 16;
|
||||||
|
std::int64_t reliefStrengthQ16 = 0, reliefContrastQ16 = 1ll << 16;
|
||||||
|
std::int64_t reliefWavelengthSubvoxels = 256 * 1024;
|
||||||
|
std::int64_t heightWarpStrengthSubvoxels = 0;
|
||||||
|
std::int64_t heightWarpWavelengthSubvoxels = 256 * 128;
|
||||||
|
std::int64_t beachWidthSubvoxels = 0;
|
||||||
|
std::int64_t cliffStrengthQ16 = 0, cliffSlopeThresholdQ16 = 1ll << 15;
|
||||||
|
std::int64_t cliffSharpnessQ16 = 2ll << 16;
|
||||||
|
std::int64_t cliffSampleDistanceSubvoxels = 256 * 2;
|
||||||
|
std::int64_t cliffMaximumDisplacementSubvoxels = 0;
|
||||||
|
bool hasCapProfile = false;
|
||||||
|
std::int64_t capHeightSubvoxels = 0;
|
||||||
|
std::int64_t capWarpStrengthSubvoxels = 0;
|
||||||
|
std::int64_t capWarpWavelengthSubvoxels = 256 * 128;
|
||||||
|
std::int64_t capUndulationSubvoxels = 0;
|
||||||
|
std::int64_t capWavelengthSubvoxels = 256 * 512;
|
||||||
|
std::int64_t capDetailAmplitudeSubvoxels = 0;
|
||||||
|
std::int64_t capDetailWavelengthSubvoxels = 256 * 16;
|
||||||
|
std::int64_t capRidgeAmplitudeSubvoxels = 0;
|
||||||
|
std::int64_t capRidgeWavelengthSubvoxels = 256 * 64;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorldAssemblyRecipeV1
|
struct WorldAssemblyRecipeV1
|
||||||
@@ -136,6 +159,11 @@ namespace VoxelForgeCore
|
|||||||
std::int64_t biomeWarpSubvoxels = 256 * 250;
|
std::int64_t biomeWarpSubvoxels = 256 * 250;
|
||||||
std::int64_t biomeWarpWavelengthSubvoxels = 256 * 556;
|
std::int64_t biomeWarpWavelengthSubvoxels = 256 * 556;
|
||||||
std::int64_t biomeBorderBlendSubvoxels = 256 * 120;
|
std::int64_t biomeBorderBlendSubvoxels = 256 * 120;
|
||||||
|
// Climate fields used to assign deterministic biome sites.
|
||||||
|
// Champs climatiques utilises pour affecter les sites de biome de facon deterministe.
|
||||||
|
std::int64_t biomeClimateReliefWavelengthSubvoxels = 256 * 3333;
|
||||||
|
std::int64_t biomeClimateMoistureWavelengthSubvoxels = 256 * 2500;
|
||||||
|
std::int64_t biomeClimateReliefContrastQ16 = 2ll << 16;
|
||||||
std::vector<WorldAssemblyBiomeProfileV1> biomes;
|
std::vector<WorldAssemblyBiomeProfileV1> biomes;
|
||||||
std::uint64_t contentHash = 0;
|
std::uint64_t contentHash = 0;
|
||||||
};
|
};
|
||||||
@@ -167,6 +195,14 @@ namespace VoxelForgeCore
|
|||||||
std::int64_t capHeightSubvoxels = 0;
|
std::int64_t capHeightSubvoxels = 0;
|
||||||
std::int64_t capUndulationSubvoxels = 0;
|
std::int64_t capUndulationSubvoxels = 0;
|
||||||
std::int64_t capWavelengthSubvoxels = 256 * 512;
|
std::int64_t capWavelengthSubvoxels = 256 * 512;
|
||||||
|
// Optional evaluator-2 cap layers; all zero preserves the evaluator-1 payload hash.
|
||||||
|
// Couches de plafond optionnelles; zero conserve le hash historique des payloads v1.
|
||||||
|
std::int64_t capDetailAmplitudeSubvoxels = 0;
|
||||||
|
std::int64_t capDetailWavelengthSubvoxels = 256 * 16;
|
||||||
|
std::int64_t capRidgeAmplitudeSubvoxels = 0;
|
||||||
|
std::int64_t capRidgeWavelengthSubvoxels = 256 * 64;
|
||||||
|
std::int64_t waterSurfaceSubvoxels = 0;
|
||||||
|
bool waterEnabled = false;
|
||||||
std::vector<WorldAssemblyFeatureV1> features;
|
std::vector<WorldAssemblyFeatureV1> features;
|
||||||
std::vector<WorldAssemblyRoutePointV1> route;
|
std::vector<WorldAssemblyRoutePointV1> route;
|
||||||
std::uint64_t contentHash = 0;
|
std::uint64_t contentHash = 0;
|
||||||
@@ -204,6 +240,8 @@ namespace VoxelForgeCore
|
|||||||
std::uint32_t neighborBiomeId = 0;
|
std::uint32_t neighborBiomeId = 0;
|
||||||
std::uint32_t biomeWeightQ16 = 0;
|
std::uint32_t biomeWeightQ16 = 0;
|
||||||
std::int64_t densitySubvoxels = 0;
|
std::int64_t densitySubvoxels = 0;
|
||||||
|
std::int64_t waterSurfaceSubvoxels = 0;
|
||||||
|
std::int64_t waterFillSubvoxels = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FieldInterval
|
struct FieldInterval
|
||||||
@@ -254,6 +292,8 @@ namespace VoxelForgeCore
|
|||||||
FieldInfluenceBounds influenceBounds() const noexcept;
|
FieldInfluenceBounds influenceBounds() const noexcept;
|
||||||
FieldDetailEnvelope detailEnvelope() const noexcept;
|
FieldDetailEnvelope detailEnvelope() const noexcept;
|
||||||
FieldBlockContract contract() const noexcept;
|
FieldBlockContract contract() const noexcept;
|
||||||
|
const WorldAssemblyRecipeV1* recipe() const noexcept { return recipe_; }
|
||||||
|
const WorldAssemblyStratumPlanV1* plan() const noexcept { return plan_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const WorldAssemblyRecipeV1* recipe_ = nullptr;
|
const WorldAssemblyRecipeV1* recipe_ = nullptr;
|
||||||
@@ -266,6 +306,12 @@ namespace VoxelForgeCore
|
|||||||
std::uint64_t pointSamples = 0;
|
std::uint64_t pointSamples = 0;
|
||||||
std::uint64_t missedBounds = 0;
|
std::uint64_t missedBounds = 0;
|
||||||
std::uint64_t unknownBounds = 0;
|
std::uint64_t unknownBounds = 0;
|
||||||
|
std::uint64_t biomeBorderBoxes = 0;
|
||||||
|
std::uint64_t cliffBoxes = 0;
|
||||||
|
std::uint64_t capBoxes = 0;
|
||||||
|
std::uint64_t negativeCoordinateBoxes = 0;
|
||||||
|
std::uint64_t exceptionBoxes = 0;
|
||||||
|
std::uint64_t exceptionPointSamples = 0;
|
||||||
bool negativeControlDetected = false;
|
bool negativeControlDetected = false;
|
||||||
bool passed = false;
|
bool passed = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -638,3 +638,49 @@ the capsule-swept route. The approximately 1.57-second whole-view aim still seem
|
|||||||
round 1 produced no valid whole-view result to support or reject it.
|
round 1 produced no valid whole-view result to support or reject it.
|
||||||
|
|
||||||
— Luna
|
— Luna
|
||||||
|
|
||||||
|
## Luna, M1 round 2 results
|
||||||
|
|
||||||
|
Hi Claude,
|
||||||
|
|
||||||
|
I diagnosed the round 1 mismatch before extending the recipe. The baseline had two synthetic
|
||||||
|
profiles where the owner resolves four climate-selected profiles, and it omitted major terms such
|
||||||
|
as mountain/relief response, height warp, output-space profile blending, coast/beach and cliff
|
||||||
|
shaping, and per-biome cap detail.
|
||||||
|
Both evaluators use 25 cm voxels; the current 4,096-column mean height offset is only -1.366 voxels,
|
||||||
|
and owner biome IDs match 4,096/4,096 samples. Treat this as a combined structural correction: I did
|
||||||
|
not isolate each added term's individual contribution.
|
||||||
|
|
||||||
|
On the same 4,096 density points, MAE fell from 274.0383 to 17.2262 voxels, maximum error from
|
||||||
|
841.4257 to 392.4136 voxels, and sign mismatches from 55.2% to 18.14%. The remaining error is still
|
||||||
|
visible. Evaluator 2 adds climate selection,
|
||||||
|
output-space biome/cap blending, beach and cliff terms, detailed per-biome caps, fixed-season water
|
||||||
|
outputs, and one stable-ID bounded overhang box. Schema stays v1; the new recipe and four owner
|
||||||
|
profiles use evaluator 2. Route points remain empty until round 3. Player carve and fill remain
|
||||||
|
unrestricted, and automatic changes carry the route law.
|
||||||
|
|
||||||
|
The 4,096 sample hash is `c5e0371612e927af` at W1/W8/W16. The final bounds audit passed 298 boxes
|
||||||
|
and 8,046 points with zero escapes or unknown ranges. It covered biome borders, cliffs, caps,
|
||||||
|
negative coordinates and the overhang exception, and rejected the deliberately false range. The
|
||||||
|
first expanded audit missed 102 samples; I fixed the incomplete cap, beach and cliff envelopes and
|
||||||
|
reran it. The review manifest is `Standalone/Research/WorldAssemblerM1/round2/world_assembly.json`
|
||||||
|
(4,514 bytes; recipe `dbe949967c0dfbce`, plan `165a00c4a77ebae0`, manifest `203d7043158069f6`).
|
||||||
|
|
||||||
|
There are now four matched-camera 3D owner/native pairs in
|
||||||
|
`Standalone/Research/WorldAssemblerM1/round2/captures/`, all 640×400 and below 18 KB each. Surface,
|
||||||
|
border and coast broadly agree. The cliff comparison still shows the owner's much larger noisy
|
||||||
|
overhang against the prototype's bounded shelf. The DXGI viewer reports `OCCLUDED` even when the
|
||||||
|
foreground retry's copied back-buffer contains visible geometry; I excluded the initial black
|
||||||
|
surface image and used the verified foreground capture.
|
||||||
|
|
||||||
|
Surface point evaluation takes 13.569/8.085/12.396 ms at W1/W8/W16 for 4,096 points; owner density
|
||||||
|
queries took 61.974 ms for those points. The surface Core compile/query bridge took 36.295 ms for
|
||||||
|
45 exact queries without a render mesh. The existing
|
||||||
|
cave-only shape compile reference is 0.100 s. Full-detail rendering on matched 120-tile views took
|
||||||
|
2.882 s owner versus 5.227 s Core on average. This is not the 23.085 s matched-workload gate.
|
||||||
|
|
||||||
|
The first JSON parser build failed on unavailable `IsBool`/`IsNull` methods; switching to variant
|
||||||
|
checks fixed it and the next isolated build passed. No crash, `0xC0000142` retry, new UECC folder,
|
||||||
|
owner build or `.uasset` edit occurred. No source files were added, and I did not commit.
|
||||||
|
|
||||||
|
— Luna
|
||||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -0,0 +1 @@
|
|||||||
|
{"schema_version":1,"content_hash":"203d7043158069f6","recipe":{"schema_version":1,"evaluator_version":2,"units_per_voxel":256,"seed":0,"biome_cell_size_q":204800,"biome_warp_q":64000,"biome_warp_wavelength_q":142222,"biome_border_blend_q":30720,"climate_relief_wavelength_q":853333,"climate_moisture_wavelength_q":640000,"climate_relief_contrast_q16":131072,"content_hash":"dbe949967c0dfbce","biomes":[{"id":4097,"base_height_q":-516096,"continent_amplitude_q":128000,"continent_wavelength_q":256000,"mountain_amplitude_q":0,"mountain_wavelength_q":128000,"detail_amplitude_q":512,"detail_wavelength_q":6400,"relief_min_q16":0,"relief_max_q16":19661,"moisture_min_q16":0,"moisture_max_q16":19661,"relief_strength_q16":6554,"relief_contrast_q16":104858,"relief_wavelength_q":2560000,"height_warp_strength_q":12288,"height_warp_wavelength_q":32000,"beach_width_q":3072,"cliff_strength_q16":0,"cliff_slope_threshold_q16":32768,"cliff_sharpness_q16":131072,"cliff_sample_distance_q":512,"cliff_max_displacement_q":0,"cap_enabled":true,"cap_height_q":-149094,"cap_warp_strength_q":0,"cap_warp_wavelength_q":25600,"cap_undulation_q":256000,"cap_wavelength_q":1280000,"cap_detail_amplitude_q":2560,"cap_detail_wavelength_q":25600,"cap_ridge_amplitude_q":0,"cap_ridge_wavelength_q":12800},{"id":4098,"base_height_q":-484639,"continent_amplitude_q":128000,"continent_wavelength_q":256000,"mountain_amplitude_q":128000,"mountain_wavelength_q":512000,"detail_amplitude_q":512,"detail_wavelength_q":25600,"relief_min_q16":52429,"relief_max_q16":65536,"moisture_min_q16":52429,"moisture_max_q16":65536,"relief_strength_q16":45875,"relief_contrast_q16":104858,"relief_wavelength_q":170667,"height_warp_strength_q":12288,"height_warp_wavelength_q":25600,"beach_width_q":3072,"cliff_strength_q16":65536,"cliff_slope_threshold_q16":19661,"cliff_sharpness_q16":327680,"cliff_sample_distance_q":5120,"cliff_max_displacement_q":2570240,"cap_enabled":true,"cap_height_q":-149094,"cap_warp_strength_q":0,"cap_warp_wavelength_q":25600,"cap_undulation_q":230400,"cap_wavelength_q":1280000,"cap_detail_amplitude_q":2560,"cap_detail_wavelength_q":25600,"cap_ridge_amplitude_q":0,"cap_ridge_wavelength_q":12800},{"id":4099,"base_height_q":-516096,"continent_amplitude_q":20480,"continent_wavelength_q":85333,"mountain_amplitude_q":0,"mountain_wavelength_q":128000,"detail_amplitude_q":512,"detail_wavelength_q":6400,"relief_min_q16":6554,"relief_max_q16":26214,"moisture_min_q16":32768,"moisture_max_q16":52429,"relief_strength_q16":0,"relief_contrast_q16":16384,"relief_wavelength_q":170752,"height_warp_strength_q":16640,"height_warp_wavelength_q":32000,"beach_width_q":3072,"cliff_strength_q16":0,"cliff_slope_threshold_q16":32768,"cliff_sharpness_q16":131072,"cliff_sample_distance_q":512,"cliff_max_displacement_q":0,"cap_enabled":true,"cap_height_q":-149094,"cap_warp_strength_q":0,"cap_warp_wavelength_q":25600,"cap_undulation_q":256000,"cap_wavelength_q":1280000,"cap_detail_amplitude_q":2560,"cap_detail_wavelength_q":25600,"cap_ridge_amplitude_q":0,"cap_ridge_wavelength_q":12800},{"id":4100,"base_height_q":-385024,"continent_amplitude_q":20480,"continent_wavelength_q":256000,"mountain_amplitude_q":20480,"mountain_wavelength_q":512000,"detail_amplitude_q":512,"detail_wavelength_q":25600,"relief_min_q16":64881,"relief_max_q16":65536,"moisture_min_q16":52429,"moisture_max_q16":65536,"relief_strength_q16":45875,"relief_contrast_q16":104858,"relief_wavelength_q":170667,"height_warp_strength_q":12288,"height_warp_wavelength_q":25600,"beach_width_q":3072,"cliff_strength_q16":0,"cliff_slope_threshold_q16":32768,"cliff_sharpness_q16":131072,"cliff_sample_distance_q":512,"cliff_max_displacement_q":0,"cap_enabled":true,"cap_height_q":-149094,"cap_warp_strength_q":0,"cap_warp_wavelength_q":25600,"cap_undulation_q":230400,"cap_wavelength_q":1280000,"cap_detail_amplitude_q":2560,"cap_detail_wavelength_q":25600,"cap_ridge_amplitude_q":0,"cap_ridge_wavelength_q":12800}]},"plan":{"schema_version":1,"content_hash":"165a00c4a77ebae0","strates":[{"strate_index":1,"top_chunk_z":-16,"bottom_chunk_z":-79,"cap_height_q":-149094,"cap_undulation_q":0,"cap_wavelength_q":64000,"cap_detail_amplitude_q":1536,"cap_detail_wavelength_q":6400,"cap_ridge_amplitude_q":0,"cap_ridge_wavelength_q":12800,"water_surface_q":-505610,"water_enabled":true,"content_hash":"5f409a7a9653937c","features":[{"stable_id":"09812c26310b90c9","type":2,"min_q":[-181248,97280,-434134],"max_q":[-162816,117760,-429782],"parameters_q":[0,0,0,0]}],"route":[]}]}}
|
||||||
@@ -7710,12 +7710,26 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
std::int64_t WorldAssemblyFixed(double voxels)
|
std::int64_t WorldAssemblyFixed(double voxels)
|
||||||
{return static_cast<std::int64_t>(std::llround(voxels*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));}
|
{return static_cast<std::int64_t>(std::llround(voxels*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));}
|
||||||
|
|
||||||
|
std::int64_t WorldAssemblyQ16(double value)
|
||||||
|
{return static_cast<std::int64_t>(std::llround(value*65536.0));}
|
||||||
|
|
||||||
std::int64_t WorldAssemblyWavelength(double frequency,double fallbackVoxels)
|
std::int64_t WorldAssemblyWavelength(double frequency,double fallbackVoxels)
|
||||||
{
|
{
|
||||||
const double wavelength=std::isfinite(frequency)&&frequency>0.0?1.0/frequency:fallbackVoxels;
|
const double wavelength=std::isfinite(frequency)&&frequency>0.0?1.0/frequency:fallbackVoxels;
|
||||||
return std::max<std::int64_t>(1,WorldAssemblyFixed(wavelength));
|
return std::max<std::int64_t>(1,WorldAssemblyFixed(wavelength));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64 WorldAssemblyFeatureId(uint32 seed,int64 x,int64 y,uint32 kind)
|
||||||
|
{
|
||||||
|
uint64 hash=14695981039346656037ull;
|
||||||
|
const auto add=[&](uint64 value,int bytes)
|
||||||
|
{
|
||||||
|
for(int i=0;i<bytes;++i){hash^=static_cast<uint8>(value>>(8*i));hash*=1099511628211ull;}
|
||||||
|
};
|
||||||
|
add(seed,4);add(static_cast<uint64>(x),8);add(static_cast<uint64>(y),8);add(kind,4);
|
||||||
|
return hash?hash:1;
|
||||||
|
}
|
||||||
|
|
||||||
bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
||||||
std::string& error,const std::string& manifestOutputPath)
|
std::string& error,const std::string& manifestOutputPath)
|
||||||
{
|
{
|
||||||
@@ -7738,10 +7752,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
const FBiomeContext biomeContext=owner.Manager->GetBiomeContextForChunk(chunk);
|
const FBiomeContext biomeContext=owner.Manager->GetBiomeContextForChunk(chunk);
|
||||||
const std::int64_t bottomVoxel=static_cast<std::int64_t>(bottomChunkZ)*CHUNK_SIZE;
|
const std::int64_t bottomVoxel=static_cast<std::int64_t>(bottomChunkZ)*CHUNK_SIZE;
|
||||||
const std::int64_t heightVoxels=static_cast<std::int64_t>(topChunkZ-bottomChunkZ+1)*CHUNK_SIZE;
|
const std::int64_t heightVoxels=static_cast<std::int64_t>(topChunkZ-bottomChunkZ+1)*CHUNK_SIZE;
|
||||||
const double baseHeight=static_cast<double>(bottomVoxel)
|
|
||||||
+static_cast<double>(heightVoxels)*surface.BaseGroundRelative;
|
|
||||||
const double elevation=std::max(16.0,static_cast<double>(surface.ElevationRange));
|
|
||||||
const FBiomeMapParams& map=biomeContext.Map;
|
const FBiomeMapParams& map=biomeContext.Map;
|
||||||
|
const UVoxelStrateDefinition* definition=owner.Manager->GetStrateForChunk(chunk);
|
||||||
|
|
||||||
VoxelForgeCore::WorldAssemblyManifest manifest;
|
VoxelForgeCore::WorldAssemblyManifest manifest;
|
||||||
auto& recipe=manifest.recipe;
|
auto& recipe=manifest.recipe;
|
||||||
@@ -7750,34 +7762,140 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
recipe.biomeWarpSubvoxels=WorldAssemblyFixed(map.WarpStrength);
|
recipe.biomeWarpSubvoxels=WorldAssemblyFixed(map.WarpStrength);
|
||||||
recipe.biomeWarpWavelengthSubvoxels=WorldAssemblyWavelength(map.WarpFrequency,556.0);
|
recipe.biomeWarpWavelengthSubvoxels=WorldAssemblyWavelength(map.WarpFrequency,556.0);
|
||||||
recipe.biomeBorderBlendSubvoxels=WorldAssemblyFixed(map.BorderBlend);
|
recipe.biomeBorderBlendSubvoxels=WorldAssemblyFixed(map.BorderBlend);
|
||||||
VoxelForgeCore::WorldAssemblyBiomeProfileV1 lowland;
|
recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0);
|
||||||
lowland.stableId=0x1001u;
|
recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0);
|
||||||
lowland.baseHeightSubvoxels=WorldAssemblyFixed(baseHeight-elevation*0.15);
|
recipe.biomeClimateReliefContrastQ16=WorldAssemblyQ16(map.ReliefContrast);
|
||||||
lowland.continentAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.45);
|
const auto makeProfile=[&](uint32 stableId,const FSurfaceGenerationParams& params,
|
||||||
lowland.continentWavelengthSubvoxels=WorldAssemblyWavelength(surface.ContinentFrequency,800.0);
|
const FBiomeResolved* climate)
|
||||||
lowland.mountainAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.12*surface.MountainStrength);
|
{
|
||||||
lowland.mountainWavelengthSubvoxels=WorldAssemblyWavelength(surface.MountainFrequency,192.0);
|
VoxelForgeCore::WorldAssemblyBiomeProfileV1 profile;
|
||||||
lowland.detailAmplitudeSubvoxels=WorldAssemblyFixed(std::max(1.0,static_cast<double>(surface.SurfaceRoughness)));
|
profile.stableId=stableId;
|
||||||
lowland.detailWavelengthSubvoxels=WorldAssemblyWavelength(surface.DetailFrequency,24.0);
|
profile.baseHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(bottomVoxel)
|
||||||
VoxelForgeCore::WorldAssemblyBiomeProfileV1 upland=lowland;
|
+static_cast<double>(heightVoxels)*params.BaseGroundRelative);
|
||||||
upland.stableId=0x1002u;
|
profile.continentAmplitudeSubvoxels=WorldAssemblyFixed(params.ElevationRange);
|
||||||
upland.baseHeightSubvoxels=WorldAssemblyFixed(baseHeight+elevation*0.20);
|
profile.continentWavelengthSubvoxels=WorldAssemblyWavelength(params.ContinentFrequency,1000.0);
|
||||||
upland.continentAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.80);
|
profile.mountainAmplitudeSubvoxels=WorldAssemblyFixed(params.ElevationRange*params.MountainStrength);
|
||||||
upland.continentWavelengthSubvoxels=WorldAssemblyWavelength(surface.ContinentFrequency*0.72,1120.0);
|
profile.mountainWavelengthSubvoxels=WorldAssemblyWavelength(params.MountainFrequency,500.0);
|
||||||
upland.mountainAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.42*std::max(0.25,static_cast<double>(surface.MountainStrength)));
|
profile.detailAmplitudeSubvoxels=WorldAssemblyFixed(params.SurfaceRoughness);
|
||||||
upland.mountainWavelengthSubvoxels=WorldAssemblyWavelength(surface.MountainFrequency*0.75,256.0);
|
profile.detailWavelengthSubvoxels=WorldAssemblyWavelength(params.DetailFrequency,25.0);
|
||||||
upland.detailAmplitudeSubvoxels=WorldAssemblyFixed(std::max(2.0,static_cast<double>(surface.SurfaceRoughness)*1.5));
|
profile.reliefMinimumQ16=climate?WorldAssemblyQ16(climate->ReliefMin):0;
|
||||||
upland.detailWavelengthSubvoxels=WorldAssemblyWavelength(surface.DetailFrequency*0.8,30.0);
|
profile.reliefMaximumQ16=climate?WorldAssemblyQ16(climate->ReliefMax):65536;
|
||||||
recipe.biomes={lowland,upland};
|
profile.moistureMinimumQ16=climate?WorldAssemblyQ16(climate->MoistureMin):0;
|
||||||
|
profile.moistureMaximumQ16=climate?WorldAssemblyQ16(climate->MoistureMax):65536;
|
||||||
|
profile.reliefStrengthQ16=WorldAssemblyQ16(params.ReliefStrength);
|
||||||
|
profile.reliefContrastQ16=WorldAssemblyQ16(params.ReliefContrast);
|
||||||
|
profile.reliefWavelengthSubvoxels=WorldAssemblyWavelength(params.ReliefFrequency,667.0);
|
||||||
|
profile.heightWarpStrengthSubvoxels=WorldAssemblyFixed(params.HeightWarpStrength);
|
||||||
|
profile.heightWarpWavelengthSubvoxels=WorldAssemblyWavelength(params.HeightWarpFrequency,125.0);
|
||||||
|
profile.beachWidthSubvoxels=WorldAssemblyFixed(params.BeachWidth);
|
||||||
|
profile.cliffStrengthQ16=WorldAssemblyQ16(params.CliffStrength);
|
||||||
|
profile.cliffSlopeThresholdQ16=std::max<std::int64_t>(1,WorldAssemblyQ16(params.CliffSlopeThreshold));
|
||||||
|
profile.cliffSharpnessQ16=WorldAssemblyQ16(params.CliffSharpness);
|
||||||
|
profile.cliffSampleDistanceSubvoxels=WorldAssemblyFixed(std::max(0.5f,params.CliffSampleDist));
|
||||||
|
const double structuralAbsBound=static_cast<double>(params.ElevationRange)
|
||||||
|
+static_cast<double>(params.ElevationRange)*params.MountainStrength
|
||||||
|
+2.0*params.SurfaceRoughness;
|
||||||
|
const double cliffAbsBound=2.0*structuralAbsBound*params.CliffStrength*params.CliffSharpness;
|
||||||
|
profile.cliffMaximumDisplacementSubvoxels=WorldAssemblyFixed(params.CliffStrength>0.0f
|
||||||
|
?std::max(1.0,cliffAbsBound):0.0);
|
||||||
|
profile.hasCapProfile=true;
|
||||||
|
profile.capHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(bottomVoxel)
|
||||||
|
+static_cast<double>(heightVoxels)*params.CeilingRelative);
|
||||||
|
profile.capWarpStrengthSubvoxels=WorldAssemblyFixed(params.CeilingWarpStrength);
|
||||||
|
profile.capWarpWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingWarpFrequency,100.0);
|
||||||
|
profile.capUndulationSubvoxels=WorldAssemblyFixed(params.CeilingUndulation);
|
||||||
|
profile.capWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingUndulationFrequency,500.0);
|
||||||
|
profile.capDetailAmplitudeSubvoxels=WorldAssemblyFixed(params.CeilingRoughness);
|
||||||
|
profile.capDetailWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingRoughnessFrequency,25.0);
|
||||||
|
profile.capRidgeAmplitudeSubvoxels=WorldAssemblyFixed(params.CeilingRidgeStrength);
|
||||||
|
profile.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingRidgeFrequency,50.0);
|
||||||
|
return profile;
|
||||||
|
};
|
||||||
|
if(biomeContext.IsValid()&&definition)
|
||||||
|
{
|
||||||
|
for(const FBiomeResolved& resolved:biomeContext.Biomes)
|
||||||
|
{
|
||||||
|
const UVoxelBiomeDefinition* biome=definition->Biomes.IsValidIndex(resolved.Index)
|
||||||
|
?definition->Biomes[resolved.Index]:nullptr;
|
||||||
|
FSurfaceGenerationParams params=surface;
|
||||||
|
if(biome&&biome->bOverrideTerrain&&biome->GeneratorType==ECaveGeneratorType::SurfaceWorld)
|
||||||
|
{
|
||||||
|
params=biome->SurfaceParams;
|
||||||
|
params.StrateTopWorldZ=surface.StrateTopWorldZ;
|
||||||
|
params.StrateBottomWorldZ=surface.StrateBottomWorldZ;
|
||||||
|
params.BoundarySealThickness=surface.BoundarySealThickness;
|
||||||
|
params.BaseDensity=surface.BaseDensity;
|
||||||
|
params.WaterLevelRelative=surface.WaterLevelRelative;
|
||||||
|
}
|
||||||
|
recipe.biomes.push_back(makeProfile(0x1001u+static_cast<uint32>(resolved.Index),params,&resolved));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(recipe.biomes.empty())recipe.biomes.push_back(makeProfile(0x1001u,surface,nullptr));
|
||||||
|
|
||||||
VoxelForgeCore::WorldAssemblyStratumPlanV1 stratum;
|
VoxelForgeCore::WorldAssemblyStratumPlanV1 stratum;
|
||||||
stratum.strateIndex=owner.Manager->GetStrateIndex(
|
stratum.strateIndex=owner.Manager->GetStrateIndex(
|
||||||
static_cast<float>((static_cast<double>(surfaceChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
static_cast<float>((static_cast<double>(surfaceChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
||||||
stratum.topChunkZ=topChunkZ;
|
stratum.topChunkZ=topChunkZ;
|
||||||
stratum.bottomChunkZ=bottomChunkZ;
|
stratum.bottomChunkZ=bottomChunkZ;
|
||||||
stratum.capHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(topChunkZ+1)*CHUNK_SIZE-4.0);
|
const double strateBottom=static_cast<double>(bottomVoxel);
|
||||||
stratum.capUndulationSubvoxels=0;
|
const double strateHeight=static_cast<double>(heightVoxels);
|
||||||
stratum.capWavelengthSubvoxels=WorldAssemblyFixed(512.0);
|
stratum.capHeightSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.CeilingRelative);
|
||||||
|
stratum.capUndulationSubvoxels=WorldAssemblyFixed(surface.CeilingUndulation);
|
||||||
|
stratum.capWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingUndulationFrequency,250.0);
|
||||||
|
stratum.capDetailAmplitudeSubvoxels=WorldAssemblyFixed(surface.CeilingRoughness);
|
||||||
|
stratum.capDetailWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRoughnessFrequency,25.0);
|
||||||
|
stratum.capRidgeAmplitudeSubvoxels=WorldAssemblyFixed(surface.CeilingRidgeStrength);
|
||||||
|
stratum.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRidgeFrequency,50.0);
|
||||||
|
stratum.waterSurfaceSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.WaterLevelRelative);
|
||||||
|
stratum.waterEnabled=definition&&definition->bHasWater&&surface.WaterLevelRelative>0.0f;
|
||||||
|
|
||||||
|
// Select a deterministic steep edge in the resolved owner field for a bounded shelf.
|
||||||
|
// Selection d'une pente deterministe dans le champ resolu pour une saillie bornee.
|
||||||
|
constexpr int probeSide=49,probeStep=32;
|
||||||
|
std::array<float,probeSide*probeSide> heights{};
|
||||||
|
std::array<uint8,probeSide*probeSide> valid{};
|
||||||
|
for(int y=0;y<probeSide;++y)
|
||||||
|
for(int x=0;x<probeSide;++x)
|
||||||
|
{
|
||||||
|
const float px=static_cast<float>((x-probeSide/2)*probeStep);
|
||||||
|
const float py=static_cast<float>((y-probeSide/2)*probeStep);
|
||||||
|
float ceiling=0.0f;
|
||||||
|
const std::size_t index=static_cast<std::size_t>(y)*probeSide+x;
|
||||||
|
valid[index]=owner.Generator->GetSurfaceHeightAt(px,py,topChunkZ,heights[index],ceiling)?1u:0u;
|
||||||
|
}
|
||||||
|
double steepest=0.0;int cliffX=0,cliffY=0;bool xAxis=true;int downhill=1;
|
||||||
|
for(int y=1;y<probeSide-1;++y)
|
||||||
|
for(int x=1;x<probeSide-1;++x)
|
||||||
|
{
|
||||||
|
const std::size_t center=static_cast<std::size_t>(y)*probeSide+x;
|
||||||
|
if(!valid[center]||!valid[center-1]||!valid[center+1]||!valid[center-probeSide]||!valid[center+probeSide])continue;
|
||||||
|
const double dx=static_cast<double>(heights[center+1]-heights[center-1]);
|
||||||
|
const double dy=static_cast<double>(heights[center+probeSide]-heights[center-probeSide]);
|
||||||
|
const double slope=(std::abs(dx)+std::abs(dy))/(4.0*probeStep);
|
||||||
|
if(slope>steepest){steepest=slope;cliffX=(x-probeSide/2)*probeStep;cliffY=(y-probeSide/2)*probeStep;
|
||||||
|
xAxis=std::abs(dx)>=std::abs(dy);downhill=(xAxis?dx:dy)>=0.0?-1:1;}
|
||||||
|
}
|
||||||
|
if(steepest>0.05)
|
||||||
|
{
|
||||||
|
float ground=0.0f,ceiling=0.0f;
|
||||||
|
if(owner.Generator->GetSurfaceHeightAt(static_cast<float>(cliffX),static_cast<float>(cliffY),
|
||||||
|
topChunkZ,ground,ceiling))
|
||||||
|
{
|
||||||
|
VoxelForgeCore::WorldAssemblyFeatureV1 shelf;
|
||||||
|
shelf.stableId=WorldAssemblyFeatureId(recipe.seed,cliffX,cliffY,0x4f564552u);
|
||||||
|
shelf.type=2;
|
||||||
|
const int reach=72,width=36;
|
||||||
|
const int minX=xAxis?cliffX+(downhill<0?-reach:0)-4:cliffX-width;
|
||||||
|
const int maxX=xAxis?cliffX+(downhill>0?reach:0)+4:cliffX+width;
|
||||||
|
const int minY=xAxis?cliffY-width:cliffY+(downhill<0?-reach:0)-4;
|
||||||
|
const int maxY=xAxis?cliffY+width:cliffY+(downhill>0?reach:0)+4;
|
||||||
|
shelf.minimumSubvoxels={WorldAssemblyFixed(minX),WorldAssemblyFixed(minY),
|
||||||
|
WorldAssemblyFixed(std::max<double>(bottomVoxel,ground-12.0))};
|
||||||
|
shelf.maximumSubvoxels={WorldAssemblyFixed(maxX),WorldAssemblyFixed(maxY),
|
||||||
|
WorldAssemblyFixed(std::min<double>((topChunkZ+1)*CHUNK_SIZE,ground+5.0))};
|
||||||
|
stratum.features.push_back(shelf);
|
||||||
|
}
|
||||||
|
}
|
||||||
manifest.plan.strates.push_back(std::move(stratum));
|
manifest.plan.strates.push_back(std::move(stratum));
|
||||||
if(!manifest.finalize(error))return false;
|
if(!manifest.finalize(error))return false;
|
||||||
const std::string manifestText=manifest.ToJson();
|
const std::string manifestText=manifest.ToJson();
|
||||||
@@ -7802,12 +7920,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
if(!rootObject)
|
if(!rootObject)
|
||||||
{error="owner snapshot root is not a JSON object";return false;}
|
{error="owner snapshot root is not a JSON object";return false;}
|
||||||
(*rootObject)["world_assembly"]=std::move(manifestJson);
|
(*rootObject)["world_assembly"]=std::move(manifestJson);
|
||||||
std::printf("[WorldAssemblerPrototype] recipe_version=%u evaluator_version=%u strata=%zu biomes=%zu strate_index=%d chunk_z=%d..%d serialized_bytes=%zu recipe_hash=%016llx plan_hash=%016llx manifest_hash=%016llx source=owner_surface_params_and_biome_map route_points=0 features=0\n",
|
std::printf("[WorldAssemblerPrototype] recipe_version=%u evaluator_version=%u strata=%zu biomes=%zu strate_index=%d chunk_z=%d..%d serialized_bytes=%zu recipe_hash=%016llx plan_hash=%016llx manifest_hash=%016llx source=owner_surface_params_and_biome_map route_points=%zu features=%zu\n",
|
||||||
manifest.recipe.schemaVersion,manifest.recipe.evaluatorVersion,manifest.plan.strates.size(),
|
manifest.recipe.schemaVersion,manifest.recipe.evaluatorVersion,manifest.plan.strates.size(),
|
||||||
manifest.recipe.biomes.size(),manifest.plan.strates.front().strateIndex,topChunkZ,bottomChunkZ,
|
manifest.recipe.biomes.size(),manifest.plan.strates.front().strateIndex,topChunkZ,bottomChunkZ,
|
||||||
manifestText.size(),static_cast<unsigned long long>(manifest.recipe.contentHash),
|
manifestText.size(),static_cast<unsigned long long>(manifest.recipe.contentHash),
|
||||||
static_cast<unsigned long long>(manifest.plan.contentHash),
|
static_cast<unsigned long long>(manifest.plan.contentHash),
|
||||||
static_cast<unsigned long long>(manifest.contentHash));
|
static_cast<unsigned long long>(manifest.contentHash),manifest.plan.strates.front().route.size(),
|
||||||
|
manifest.plan.strates.front().features.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7831,11 +7950,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
static_cast<long long>(influence.maximumZSubvoxels),
|
static_cast<long long>(influence.maximumZSubvoxels),
|
||||||
static_cast<long long>(detail.maximumAbsoluteDisplacementSubvoxels),
|
static_cast<long long>(detail.maximumAbsoluteDisplacementSubvoxels),
|
||||||
static_cast<long long>(detail.minimumFeatureSizeSubvoxels),bounds.passed?"PASS":"FAIL");
|
static_cast<long long>(detail.minimumFeatureSizeSubvoxels),bounds.passed?"PASS":"FAIL");
|
||||||
std::printf("[WorldAssemblerBoundAudit] boxes=%llu point_samples=%llu missed_bounds=%llu unknown_bounds=%llu deliberately_wrong_bound_detected=%s status=%s\n",
|
std::printf("[WorldAssemblerBoundAudit] boxes=%llu point_samples=%llu missed_bounds=%llu unknown_bounds=%llu border_boxes=%llu cliff_boxes=%llu cap_boxes=%llu negative_coordinate_boxes=%llu exception_boxes=%llu exception_point_samples=%llu deliberately_wrong_bound_detected=%s status=%s\n",
|
||||||
static_cast<unsigned long long>(bounds.boxesTested),
|
static_cast<unsigned long long>(bounds.boxesTested),
|
||||||
static_cast<unsigned long long>(bounds.pointSamples),
|
static_cast<unsigned long long>(bounds.pointSamples),
|
||||||
static_cast<unsigned long long>(bounds.missedBounds),
|
static_cast<unsigned long long>(bounds.missedBounds),
|
||||||
static_cast<unsigned long long>(bounds.unknownBounds),
|
static_cast<unsigned long long>(bounds.unknownBounds),
|
||||||
|
static_cast<unsigned long long>(bounds.biomeBorderBoxes),
|
||||||
|
static_cast<unsigned long long>(bounds.cliffBoxes),
|
||||||
|
static_cast<unsigned long long>(bounds.capBoxes),
|
||||||
|
static_cast<unsigned long long>(bounds.negativeCoordinateBoxes),
|
||||||
|
static_cast<unsigned long long>(bounds.exceptionBoxes),
|
||||||
|
static_cast<unsigned long long>(bounds.exceptionPointSamples),
|
||||||
bounds.negativeControlDetected?"true":"false",bounds.passed?"PASS":"FAIL");
|
bounds.negativeControlDetected?"true":"false",bounds.passed?"PASS":"FAIL");
|
||||||
|
|
||||||
struct Query {std::int64_t x=0,y=0,z=0;};
|
struct Query {std::int64_t x=0,y=0,z=0;};
|
||||||
@@ -7889,6 +8014,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
add(static_cast<std::uint64_t>(sample.capHeightSubvoxels),8);
|
add(static_cast<std::uint64_t>(sample.capHeightSubvoxels),8);
|
||||||
add(sample.biomeId,4);add(sample.neighborBiomeId,4);add(sample.biomeWeightQ16,4);
|
add(sample.biomeId,4);add(sample.neighborBiomeId,4);add(sample.biomeWeightQ16,4);
|
||||||
add(static_cast<std::uint64_t>(sample.densitySubvoxels),8);
|
add(static_cast<std::uint64_t>(sample.densitySubvoxels),8);
|
||||||
|
add(static_cast<std::uint64_t>(sample.waterSurfaceSubvoxels),8);
|
||||||
|
add(static_cast<std::uint64_t>(sample.waterFillSubvoxels),8);
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
@@ -7908,6 +8035,139 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
std::printf("[WorldAssemblerQueryTiming] samples=%zu native_W1_ms=%.3f native_W8_ms=%.3f native_W16_ms=%.3f scope=point_evaluation_only\n",
|
std::printf("[WorldAssemblerQueryTiming] samples=%zu native_W1_ms=%.3f native_W8_ms=%.3f native_W16_ms=%.3f scope=point_evaluation_only\n",
|
||||||
queries.size(),time1,time8,time16);
|
queries.size(),time1,time8,time16);
|
||||||
|
|
||||||
|
double ownerHeightSum=0.0,nativeHeightSum=0.0;
|
||||||
|
double ownerHeightSquareSum=0.0,nativeHeightSquareSum=0.0,centeredCrossSum=0.0;
|
||||||
|
double heightAbsoluteSum=0.0,heightMaximumError=0.0;
|
||||||
|
std::vector<double> heightDeltas;
|
||||||
|
heightDeltas.reserve(4096);
|
||||||
|
std::uint64_t ownerBiomeSamples=0,ownerBiomeIdMismatches=0;
|
||||||
|
const FBiomeContext ownerBiomeContext=owner.Manager->GetBiomeContextForChunk(
|
||||||
|
FIntVector(0,0,plan.topChunkZ));
|
||||||
|
std::uint64_t heightSamples=0;
|
||||||
|
constexpr int heightProbeSide=64;
|
||||||
|
constexpr double heightProbeSpan=3200.0;
|
||||||
|
for(int y=0;y<heightProbeSide;++y)
|
||||||
|
for(int x=0;x<heightProbeSide;++x)
|
||||||
|
{
|
||||||
|
const double xVoxel=(static_cast<double>(x)/(heightProbeSide-1)-0.5)*heightProbeSpan;
|
||||||
|
const double yVoxel=(static_cast<double>(y)/(heightProbeSide-1)-0.5)*heightProbeSpan;
|
||||||
|
float ownerGround=0.0f,ownerCap=0.0f;
|
||||||
|
if(!owner.Generator->GetSurfaceHeightAt(static_cast<float>(xVoxel),
|
||||||
|
static_cast<float>(yVoxel),plan.topChunkZ,ownerGround,ownerCap))continue;
|
||||||
|
const auto native=block.point(
|
||||||
|
static_cast<std::int64_t>(std::llround(xVoxel*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),
|
||||||
|
static_cast<std::int64_t>(std::llround(yVoxel*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),0);
|
||||||
|
const double nativeGround=static_cast<double>(native.groundHeightSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||||
|
const double ownerGroundValue=ownerGround;
|
||||||
|
ownerHeightSum+=ownerGroundValue;nativeHeightSum+=nativeGround;
|
||||||
|
ownerHeightSquareSum+=ownerGroundValue*ownerGroundValue;
|
||||||
|
nativeHeightSquareSum+=nativeGround*nativeGround;
|
||||||
|
centeredCrossSum+=ownerGroundValue*nativeGround;
|
||||||
|
const double difference=std::abs(nativeGround-ownerGroundValue);
|
||||||
|
heightAbsoluteSum+=difference;heightMaximumError=std::max(heightMaximumError,difference);
|
||||||
|
heightDeltas.push_back(nativeGround-ownerGroundValue);
|
||||||
|
FVoxelBiomeQuery ownerBiome;
|
||||||
|
owner.Generator->QueryBiomeAt(static_cast<float>(xVoxel),static_cast<float>(yVoxel),
|
||||||
|
plan.topChunkZ,ownerBiome);
|
||||||
|
if(ownerBiome.bHasBiomes&&ownerBiomeContext.Biomes.IsValidIndex(ownerBiome.DominantContextIndex))
|
||||||
|
{
|
||||||
|
const std::uint32_t ownerBiomeId=0x1001u+static_cast<std::uint32_t>(
|
||||||
|
ownerBiomeContext.Biomes[ownerBiome.DominantContextIndex].Index);
|
||||||
|
++ownerBiomeSamples;
|
||||||
|
if(native.biomeId!=ownerBiomeId)++ownerBiomeIdMismatches;
|
||||||
|
}
|
||||||
|
++heightSamples;
|
||||||
|
}
|
||||||
|
if(heightSamples>0)
|
||||||
|
{
|
||||||
|
const double ownerMean=ownerHeightSum/heightSamples;
|
||||||
|
const double nativeMean=nativeHeightSum/heightSamples;
|
||||||
|
const double ownerVariance=std::max(0.0,ownerHeightSquareSum/heightSamples-ownerMean*ownerMean);
|
||||||
|
const double nativeVariance=std::max(0.0,nativeHeightSquareSum/heightSamples-nativeMean*nativeMean);
|
||||||
|
const double covariance=centeredCrossSum/heightSamples-ownerMean*nativeMean;
|
||||||
|
const double slope=ownerVariance>0.0?covariance/ownerVariance:0.0;
|
||||||
|
const double intercept=nativeMean-slope*ownerMean;
|
||||||
|
const double correlation=(ownerVariance>0.0&&nativeVariance>0.0)
|
||||||
|
?covariance/std::sqrt(ownerVariance*nativeVariance):0.0;
|
||||||
|
double offsetCorrectedAbsoluteSum=0.0;
|
||||||
|
for(const double delta:heightDeltas)offsetCorrectedAbsoluteSum+=std::abs(delta-(nativeMean-ownerMean));
|
||||||
|
std::printf("[WorldAssemblerSurfaceShapeDiagnostic] samples=%llu owner_voxel_cm=%.1f core_voxel_cm=%.1f mean_owner_height=%.3f mean_native_height=%.3f mean_offset=%.3f mae=%.3f mae_after_mean_offset_removal=%.3f max_error=%.3f regression_native_equals_slope_owner_plus_offset_slope=%.5f offset=%.3f correlation=%.5f owner_biome_samples=%llu owner_biome_id_mismatches=%llu\n",
|
||||||
|
static_cast<unsigned long long>(heightSamples),VOXEL_SIZE,25.0,
|
||||||
|
ownerMean,nativeMean,nativeMean-ownerMean,heightAbsoluteSum/heightSamples,
|
||||||
|
heightDeltas.empty()?0.0:offsetCorrectedAbsoluteSum/heightDeltas.size(),
|
||||||
|
heightMaximumError,slope,intercept,correlation,
|
||||||
|
static_cast<unsigned long long>(ownerBiomeSamples),
|
||||||
|
static_cast<unsigned long long>(ownerBiomeIdMismatches));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FWorldAssemblerCaptureSite
|
||||||
|
{double X=0.0,Y=0.0,OwnerGround=0.0,NativeGround=0.0,Score=0.0;bool Found=false;};
|
||||||
|
FWorldAssemblerCaptureSite borderSite,coastSite;
|
||||||
|
const auto waterVoxel=static_cast<double>(plan.waterSurfaceSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||||
|
for(int y=-1600;y<=1600;y+=64)
|
||||||
|
for(int x=-1600;x<=1600;x+=64)
|
||||||
|
{
|
||||||
|
float ownerGround=0.0f,ownerCap=0.0f;
|
||||||
|
if(!owner.Generator->GetSurfaceHeightAt(static_cast<float>(x),static_cast<float>(y),
|
||||||
|
plan.topChunkZ,ownerGround,ownerCap))continue;
|
||||||
|
FVoxelBiomeQuery ownerBiome;
|
||||||
|
owner.Generator->QueryBiomeAt(static_cast<float>(x),static_cast<float>(y),
|
||||||
|
plan.topChunkZ,ownerBiome);
|
||||||
|
const auto native=block.point(static_cast<std::int64_t>(x)
|
||||||
|
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,static_cast<std::int64_t>(y)
|
||||||
|
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0);
|
||||||
|
if(ownerBiome.bHasBiomes&&ownerBiome.NeighborWeight>borderSite.Score)
|
||||||
|
borderSite={static_cast<double>(x),static_cast<double>(y),ownerGround,
|
||||||
|
static_cast<double>(native.groundHeightSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||||
|
ownerBiome.NeighborWeight,true};
|
||||||
|
const double waterDistance=std::abs(static_cast<double>(ownerGround)-waterVoxel);
|
||||||
|
if(!coastSite.Found||waterDistance<coastSite.Score)
|
||||||
|
coastSite={static_cast<double>(x),static_cast<double>(y),ownerGround,
|
||||||
|
static_cast<double>(native.groundHeightSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||||
|
waterDistance,true};
|
||||||
|
}
|
||||||
|
const auto printCaptureSite=[&](const char* name,const FWorldAssemblerCaptureSite& site)
|
||||||
|
{
|
||||||
|
if(!site.Found)return;
|
||||||
|
const double cameraX=site.X/4.0,cameraY=(site.Y-140.0)/4.0;
|
||||||
|
const double cameraZ=(site.OwnerGround+64.0)/4.0;
|
||||||
|
std::printf("[WorldAssemblerCaptureSite] name=%s target_vox=(%.0f,%.0f,%.2f) owner_ground_vox=%.2f native_ground_vox=%.2f score=%.5f camera_m=(%.3f,%.3f,%.3f,0,-22)\n",
|
||||||
|
name,site.X,site.Y,site.OwnerGround,site.OwnerGround,site.NativeGround,site.Score,
|
||||||
|
cameraX,cameraY,cameraZ);
|
||||||
|
};
|
||||||
|
const auto originNativeColumn=block.point(0,0,0);
|
||||||
|
float originOwnerGround=0.0f,originOwnerCap=0.0f;
|
||||||
|
if(owner.Generator->GetSurfaceHeightAt(0.0f,0.0f,plan.topChunkZ,
|
||||||
|
originOwnerGround,originOwnerCap))
|
||||||
|
printCaptureSite("surface",{0.0,0.0,originOwnerGround,
|
||||||
|
static_cast<double>(originNativeColumn.groundHeightSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0.0,true});
|
||||||
|
printCaptureSite("biome_border",borderSite);
|
||||||
|
printCaptureSite("coast",coastSite);
|
||||||
|
if(!plan.features.empty()&&plan.features.front().type==2)
|
||||||
|
{
|
||||||
|
const auto& feature=plan.features.front();
|
||||||
|
const double fx=(feature.minimumSubvoxels[0]+feature.maximumSubvoxels[0])
|
||||||
|
/(2.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||||
|
const double fy=(feature.minimumSubvoxels[1]+feature.maximumSubvoxels[1])
|
||||||
|
/(2.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||||
|
float ownerGround=0.0f,ownerCap=0.0f;
|
||||||
|
if(owner.Generator->GetSurfaceHeightAt(static_cast<float>(fx),static_cast<float>(fy),
|
||||||
|
plan.topChunkZ,ownerGround,ownerCap))
|
||||||
|
{
|
||||||
|
const auto native=block.point(static_cast<std::int64_t>(std::llround(fx
|
||||||
|
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),static_cast<std::int64_t>(std::llround(fy
|
||||||
|
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),0);
|
||||||
|
printCaptureSite("cliff",{fx,fy,ownerGround,
|
||||||
|
static_cast<double>(native.groundHeightSubvoxels)
|
||||||
|
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0.0,true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double absoluteSum=0.0,maximumError=0.0;
|
double absoluteSum=0.0,maximumError=0.0;
|
||||||
std::uint64_t signMismatches=0;
|
std::uint64_t signMismatches=0;
|
||||||
const auto legacyStart=std::chrono::steady_clock::now();
|
const auto legacyStart=std::chrono::steady_clock::now();
|
||||||
@@ -8520,6 +8780,11 @@ int main(int Argc,char** Argv)
|
|||||||
{
|
{
|
||||||
const auto WindowShowStart=std::chrono::steady_clock::now();
|
const auto WindowShowStart=std::chrono::steady_clock::now();
|
||||||
ShowWindow(App.Window,SW_SHOW);
|
ShowWindow(App.Window,SW_SHOW);
|
||||||
|
if(!App.CapturePath.empty())
|
||||||
|
{
|
||||||
|
BringWindowToTop(App.Window);
|
||||||
|
SetForegroundWindow(App.Window);
|
||||||
|
}
|
||||||
std::printf("[ColdTimeline] stage=viewer_window_show duration_ms=%.3f\n",
|
std::printf("[ColdTimeline] stage=viewer_window_show duration_ms=%.3f\n",
|
||||||
std::chrono::duration<double,std::milli>(
|
std::chrono::duration<double,std::milli>(
|
||||||
std::chrono::steady_clock::now()-WindowShowStart).count());
|
std::chrono::steady_clock::now()-WindowShowStart).count());
|
||||||
|
|||||||