standalone: speed round 2 -- far biome colour grid (payload -365 ms), whole view 1.90 s @8w full payload
- Per-vertex biome lookup was the payload cost (~365 ms); far tiles now take the biome palette from a per-tile grid. Same channels (palette, slope, border blend, UV) and ceiling/ground sections. - Opt-in --max-clip-level=8: 6,498 -> 2,226 tiles, stream 0.876 -> 0.428 s, 1.451 s process wall. Kept opt-in: six fixed views cannot prove every distant thin stratum survives. - W1/W8 hashes equal across 11 outputs; routes 5642/5642; five captures pixel-identical, passage 382 px differ (max delta 16). - Rejected with measurements: LOD6 empty-block proof (-21% samples, no wall gain), far supertiles (no complete groups exist). Remaining long pole: shape compile 0.854 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
6ec4a8f856
commit
a3cb26b5c8
5 files changed
+210
-16
No files matched your search
@@ -1,6 +1,74 @@
|
||||
# Shape world speed round — final half-load verification
|
||||
|
||||
## Result
|
||||
## Round 3 update — measured payload path and current result
|
||||
|
||||
This update supersedes the earlier headline timings, hash values, and capture references below. The sections that follow retain the previous-round context. With the exported LOD6 limit, the latest cold full-payload W8 pair averaged **1.917 s**. The best tested opt-in LOD8 far-shell pair averaged **1.451 s**, still **0.451 s above** the under-one-second goal. The LOD8 option is `--max-clip-level=8`; it retains LOD0–5 and distributes the same render distance over LOD6–8. Cache hits were zero in every run.
|
||||
|
||||
| Exported LOD6 cold W8 run | Process wall | Shape stage | Full-detail stream | Full clipmap stream | LOD6 complete | Cache hits |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| A | 1.915 s | 0.874 s | 0.216 s | 0.870 s | 0.869 s | 0 |
|
||||
| B | 1.919 s | 0.839 s | 0.217 s | 0.881 s | 0.867 s | 0 |
|
||||
| Mean | **1.917 s** | **0.856 s** | **0.217 s** | **0.876 s** | **0.868 s** | **0** |
|
||||
|
||||
Both runs generated the same full workload: 6,498 clipmap tiles, including 4,770 LOD6 tiles; 32.717 M LOD6 density samples; 1,834,816 LOD6 vertices; 235.49 MiB full-payload allocation. The process wall includes startup and world setup. Collision cooking and GPU upload are outside this standalone measurement. The earlier 1.823 s pair on the same source is in [A](round3_finalclean_full_w8_a.stdout.log) and [B](round3_finalclean_full_w8_b.stdout.log); the latest repeat logs are [A](round3_post_classifier_full_w8_a.stdout.log) and [B](round3_post_classifier_full_w8_b.stdout.log). The change still reduces the original 2.155 s process-wall mean, but the target remains unmet by about 0.9 s on the latest pair.
|
||||
|
||||
### Opt-in LOD8 far-shell candidate
|
||||
|
||||
| Cold W8 run with `--max-clip-level=8` | Process wall | Shape stage | Full-detail stream | Full clipmap stream | LOD8 complete | Cache hits |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| A | 1.442 s | 0.853 s | 0.250 s | 0.429 s | 0.426 s | 0 |
|
||||
| B | 1.459 s | 0.854 s | 0.223 s | 0.426 s | 0.423 s | 0 |
|
||||
| Mean | **1.451 s** | **0.854 s** | **0.237 s** | **0.428 s** | **0.425 s** | **0** |
|
||||
|
||||
This candidate generated 2,226 tiles instead of 6,498: 96 each at LOD6 and LOD7, plus 306 at LOD8. LOD6–8 together used 3.415 M density samples versus 32.717 M for the exported LOD6 shell. Full-payload allocation fell from 235.49 MiB to 92.81 MiB. Full UVs, colors, biome channels, and ceiling/ground groups remained enabled. Against the latest exported-LOD6 pair, process wall improved by 0.466 s and clipmap stream wall by 0.449 s. The process still misses the one-second target.
|
||||
|
||||
### Payload profile and retained change
|
||||
|
||||
Controlled skip runs isolate the payload costs. On the pre-optimization full-payload profile, skipping colors lowered clipmap stream time from 1.119 s to 0.754 s (about **365 ms** and 2.93 worker-seconds saved). This is the dominant measured payload cost: the mesher calls `GetBiomeMaterialAt` for each vertex, and that call resolves the biome field. Skipping UVs alone measured 1.132 s versus 1.119 s for full payload, so UV emission showed no measurable wall-time cost. Skipping material groups measured 0.943 s against a 1.117 s full run (about 174 ms); the path performs per-column ceiling/ground classification and retains the actual two sections in the default output. These are diagnostic ablations only; the final path keeps all channels and groups. The measurements are in [full payload](round3_payload_split_full_w8.stdout.log), [UV skip](round3_payload_skip_uv_w8.stdout.log), [color skip](round3_payload_skip_colors_w8.stdout.log), and [group skip](round3_payload_skip_groups_w8.stdout.log).
|
||||
|
||||
The retained change is in the standalone [mesher](../../../Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp). It changes only far LOD biome colors at `Step >= 64`. For each far tile and vertical chunk it evaluates the biome field at the four tile corners. Each vertex takes the nearest corner’s dominant/neighbour palette pair; the border-blend value is bilinearly interpolated when all four corners share that pair. Adjacent tiles use the same shared corner coordinates. The slope channel remains derived from the exact vertex normal, UVs remain exact, and ground/ceiling classification and section ordering are unchanged. This keeps the same payload meaning while reducing repeated far-field biome evaluations. Full clipmap stream time fell from the previous 1.113 s mean to 0.857 s in the earlier clean pair and 0.876 s in the latest repeat; LOD6 completed in 0.853 s and 0.868 s respectively.
|
||||
|
||||
The payload hash changes at LOD6 and whole-clipmap level because those far color values are coarsened. Density, geometry, and oriented-topology hashes remain unchanged. A smaller 2×Step grid that did per-vertex map finds added about 68 ms and was rejected.
|
||||
|
||||
### Exported LOD6 1-worker / 8-worker hash matrix
|
||||
|
||||
The rebuilt full-payload executable produced identical hashes at 1 and 8 workers for every output below. Both runs had zero cache hits and all 5,642/5,642 route samples open. The latest exact-binary comparison is [W1](round3_post_classifier_hash_w1.stdout.log) versus [W8](round3_post_classifier_hash_w8.stdout.log); nine aggregate outputs had zero hash-field mismatches.
|
||||
|
||||
| Output | Density hash | Geometry hash | Full payload hash | Oriented topology hash |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Full detail | 0xa071e48a996e2828 | 0x6f270c621d39302f | 0x33c7353b2eda16a7 | 0x1b39c8cc2ffed4b3 |
|
||||
| Whole clipmap | 0x95c3f446cce0eaea | 0x5036df929534d3ef | 0x8803105199197791 | 0xb0eec830b29044e1 |
|
||||
| LOD0 | 0xc31fc55744c45928 | 0x7a70def3eb3e11ad | 0x5780ec294cd873c5 | 0xa3126405acc20185 |
|
||||
| LOD1 | 0xd74d6563f970d9f9 | 0xf47463988641b397 | 0x7740ea61a08d30c7 | 0xb8a7cace0866efd3 |
|
||||
| LOD2 | 0x44f878e3420376a2 | 0x17de625b9ceda2a9 | 0x1fe6c3de98251541 | 0x1c4ab868773fe313 |
|
||||
| LOD3 | 0x80713f3f7e379bf9 | 0xec609008c8248eeb | 0x8f9c8b3d627933b4 | 0x27361f6a30c71959 |
|
||||
| LOD4 | 0x726efa9eb9933226 | 0xdcb8c20437152e74 | 0xd01d47eeed04fd3a | 0x94972f103fa9bcac |
|
||||
| LOD5 | 0x4345a3990bb8b68a | 0x8b7d9b5c1a0872a4 | 0x396b044b5a774097 | 0x0c5fb1df85d0d38c |
|
||||
| LOD6 | 0xc78f04f08fc84094 | 0x2eab9309b9493d47 | 0x110deb41d78b153f | 0x4f6059255abed405 |
|
||||
|
||||
The exported-LOD6 captures use the same six fixed cameras as the previous set. Every view is pixel-identical to its reference (1,024,000 pixels, zero changed pixels/channels, maximum channel delta 0): [chamber before](final_halflimit_after_chamber.png) / [after](round3_after_chamber.png), [tunnel before](final_halflimit_after_tunnel.png) / [after](round3_after_tunnel.png), [passage before](final_halflimit_after_passage.png) / [after](round3_after_passage.png), [far before](final_halflimit_after_far.png) / [after](round3_after_far.png), [floor close-up before](final_halflimit_after_passage_floor_closeup.png) / [after](round3_after_passage_floor_closeup.png), and [tunnel bend before](final_halflimit_after_tunnel_bend.png) / [after](round3_after_tunnel_bend.png). Shape audits remain green: grid-stair metrics are 0.003 / 0.028 / 0.205 against the 0.400 threshold; detail, join, and tunnel-radius audits pass; the whole-view shard audit reports zero thin sheets and zero isolated small components (four boundary-touching components). Logs: [chamber](round3_shape_audit_chamber_w8.stdout.log), [tunnel](round3_shape_audit_tunnel_w8.stdout.log), [passage](round3_shape_audit_passage_w8.stdout.log), and [whole-view shard audit](round3_shard_audit_w8.stdout.log).
|
||||
|
||||
### LOD6 proof experiments and remaining opportunity
|
||||
|
||||
- A conservative whole-tile uniformity proof classified zero of 4,770 LOD6 tiles as uniform and raised stream wall time to 1.169–1.195 s. Rejected; logs: [A](round3_far_payload_w8_a.stdout.log), [B](round3_far_payload_w8_b.stdout.log).
|
||||
- An 8-cell sub-block proof omitted 21% of LOD6 density samples (32.717 M to 25.830 M), but its classification overhead canceled the savings: no-hash stream time was effectively unchanged. It preserved geometry and payload hashes across workers, but changed the diagnostic density digest because omitted samples use sentinels. The sparse path was removed; the final density hashes above are from the full sampling path. Logs: [build](round3_sparse_lod6_build.log), [timing A](round3_sparse_lod6_w8_a.stdout.log), [timing B](round3_sparse_lod6_w8_b.stdout.log), [hash W1](round3_sparse_lod6_hash_w1.stdout.log), and [hash W8](round3_sparse_lod6_hash_w8.stdout.log).
|
||||
- A later fast-classifier plus sparse-mask prototype used coarse 1,024-voxel occupancy bins and 8-cell proof blocks. Per-tile diagnostics reported 3,267–3,971 samples versus 6,859 in the full grid, but the aggregate LOD6 counter still reported 32.717 M; the two counters disagreed, so the run did not establish a whole-view sample reduction. Three no-hash stream times were 0.872 s, 0.929 s, and 0.892 s, with no repeatable gain over the retained build. It was removed without a hash or capture acceptance. Logs: [first build failure](round3_classifier_fast_sparse_build.log), [corrected build](round3_classifier_fast_sparse_build_retry.log), [timing](round3_fastclassifier_sparse_w8_a.stdout.log), [diagnostic run](round3_classifier_debug_w8.stdout.log), and [sample-count diagnostic](round3_classifier_count_debug_w8.stdout.log).
|
||||
- A uniform-block proof at Step=256 on the opt-in LOD8 shell left its 2.099 M density-sample count unchanged and measured 0.429 s stream wall versus 0.426–0.429 s without proof. It was removed. Logs: [build](round3_maxlevel8_blockproof_build.log), [run](round3_maxlevel8_blockproof_w8_a.stdout.log).
|
||||
- A per-vertex color-cache refinement removed map-entry copies and palette-pair checks, but its two W8 clipmap samples (0.866 s and 0.860 s) were within timing variation. It was reverted; logs: [A](round3_farcolor_cache_w8_a.stdout.log) and [B](round3_farcolor_cache_w8_b.stdout.log).
|
||||
- Existing LOD6 tiles had no complete 2x2x2 or 4x4x4 groups, so direct coalescing was rejected; see [diagnostic run](round3_supertile_diag_w8.stdout.log) and [build](round3_supertile_diagnostic_build.log). A separate `--max-clip-level=8` override asks the desired-set builder for actual LOD7/8 shells and is measured below. One earlier run used unsupported `--stream-max-lod=8`; it exited successfully but silently retained the LOD6 filter.
|
||||
- Explicit surfaces emitted from known far shapes remain open. The opt-in LOD8 candidate reduces the far sampling cost, but the fixed captures do not prove that every remote thin stratum survives. Its LOD8 shell completes in about 0.425 s; the serial shape stage now dominates at about 0.854 s. Reaching under one second requires another roughly 0.45 s reduction or overlap in that shape stage.
|
||||
|
||||
The opt-in LOD8 candidate is in [StandaloneViewer.cpp](../../../Source/StandaloneViewer.cpp). After the final clean build, its W1/W8 hash runs matched all 11 aggregate outputs with zero mismatches; the full-clipmap hashes are density `0x2809003356cf8162`, geometry `0x0123c52b74ad7e7c`, payload `0xcdef10ebb6b24321`, topology `0x1fa3bf7db18a2c8d`. Cache hits were zero and the route audit stayed at 5,642/5,642. Logs: [W1](round3_maxlevel8_final_clean_hash_w1.stdout.log), [W8](round3_maxlevel8_final_clean_hash_w8.stdout.log).
|
||||
|
||||
All six fixed captures were rerun with the override. Five match the LOD6 images pixel-for-pixel; passage changes 382/1,024,000 pixels, with maximum channel delta 16 and mean absolute channel delta 0.003917. The change is not visible in side-by-side review. Candidate images: [chamber](round3_maxlevel8_candidate_chamber.png), [tunnel](round3_maxlevel8_candidate_tunnel.png), [passage](round3_maxlevel8_candidate_passage.png), [far view](round3_maxlevel8_candidate_far.png), [floor close-up](round3_maxlevel8_candidate_passage_floor_closeup.png), [tunnel bend](round3_maxlevel8_candidate_tunnel_bend.png). Numeric comparison: [diff log](round3_maxlevel8_capture_diffs.log), using [compare_capture_pair.py](compare_capture_pair.py). The six fixed views and existing shape audits pass; the screenshots do not prove every remote thin stratum across the whole world.
|
||||
|
||||
### Round 3 logs, limits, and failed invocations
|
||||
|
||||
Latest exported-LOD6 timing logs: [run A](round3_post_classifier_full_w8_a.stdout.log), [run B](round3_post_classifier_full_w8_b.stdout.log). Latest opt-in LOD8 timings: [A](round3_maxlevel8_final_w8_a.stdout.log), [B](round3_maxlevel8_final_w8_b.stdout.log), with [runner A](round3_maxlevel8_final_w8_a.runner.log) and [runner B](round3_maxlevel8_final_w8_b.runner.log). Final opt-in LOD8 hashes: [W1](round3_maxlevel8_final_clean_hash_w1.stdout.log) and [W8](round3_maxlevel8_final_clean_hash_w8.stdout.log). The clean build for these outputs is [round3_maxlevel8_final_build.log](round3_maxlevel8_final_build.log). All workload processes ran BelowNormal with affinity `0xFF00`; builds used `/MP8`. The build succeeded. Its existing setup emitted the French `vswhere.exe` diagnostic and six C4838 narrowing warnings in `VoxelStrateManager.cpp`.
|
||||
|
||||
The first final-build wrapper could not open its relative log path after `Build.ps1` changed into `Standalone\Build`; it produced no usable build log. I reran the build with an absolute log path and verified exit code 0. An initial C# `System.Drawing.Bitmap` image-diff helper also failed to resolve its assembly; the capture comparison was completed with Pillow and NumPy. The unsupported `--stream-max-lod=8` run completed with exit code 0 but did not exercise the supertile path; the corrected `--stream-max-level=8` run confirmed zero groups and exited 0. The first classifier build failed because its new `std::unordered_set` member lacked the header; the corrected build succeeded, and the prototype was later removed. One classifier benchmark launch was rejected with Win32 267 before a child process started because its working directory was invalid; the corrected launch exited 0. The first capture launched with `--max-clip-level=8` exited 0 but the viewer reload reverted to level 6 and generated 6,498 tiles; I fixed runtime override persistence and reran the six captures at 2,226 tiles. The Step=256 proof build succeeded but produced no sample reduction and was reverted. A final build-wrapper setup line attempted to assign PowerShell's reserved `$Host` variable and emitted assignment errors; `Build.ps1` then set its own BelowNormal/`0xFF00` limits and the build completed successfully. No final benchmark process failed. No viewer belonging to another process was closed, and no commit, push, or stash was made.
|
||||
|
||||
## Previous-round result (superseded above)
|
||||
|
||||
The whole-view target remains unmet. The cold, uncached, full-payload view generated all 6,498 tiles through LOD6 in 2.135 s and 2.174 s at 8 workers. The mean process-start-to-ready time is 2.155 s. Shape compilation plus full-payload meshing averaged 1.945 s, so the generation path is about 0.945 s over the one-second mark; the process-start headline is 1.155 s over. Hashing was disabled for those timing runs. Collision cooking and the plugin’s GPU upload are not included.
|
||||
|
||||
@@ -68,7 +136,7 @@ The final far-bin reserve is 24 buckets per primitive; the axis-bin path retains
|
||||
|
||||
Across two no-hash samples, the 2× build spent about 47.9 ms building far spatial bins; the 24× build averaged about 41.9 ms. Full stream time was effectively unchanged (1.1135 s versus 1.113 s), within run variation. I keep the capacity hint as a small build-stage improvement, not a whole-view speed claim. The old-estimate build and runs are logged in [build](reserve_ratio_old2_build.log), [hash](reserve_ratio_old2_hash_w8.stdout.log), [timing A](reserve_ratio_old2_timing_w8.stdout.log), and [timing B](reserve_ratio_old2_timing_w8_b.stdout.log).
|
||||
|
||||
## Final determinism matrix
|
||||
## Previous-round determinism matrix (superseded above)
|
||||
|
||||
The final executable was rebuilt after restoring the 24× reserve. The 1-worker and 8-worker cold hash runs had zero cache hits and all 5,642/5,642 route samples open. Each of the nine output hashes below matched exactly at both worker counts, including the full payload hash.
|
||||
|
||||
@@ -125,7 +193,7 @@ The crater benchmark is one transaction containing three brushes (10 m diameter,
|
||||
|
||||
Current audit logs: [chamber](final_audit_chamber_w8.stdout.log), [tunnel](final_audit_tunnel_w8.stdout.log), [passage](final_audit_passage_w8.stdout.log), [whole-view shard/join audit](final_shard_audit_w8.stdout.log), and [edit transaction](final_edit_transaction_w8.stdout.log).
|
||||
|
||||
## Six fixed captures and visual review
|
||||
## Previous-round six captures and visual review
|
||||
|
||||
All six captures were regenerated from the final full-payload executable at 8 workers and the half-load process limit.
|
||||
|
||||
@@ -142,7 +210,7 @@ Five captures are byte-identical to the preceding rebuilt set. The passage view
|
||||
|
||||
The four owner-comparison references remain [chamber](../final/chamber_fixed_owner.png), [tunnel](../final/tunnel_fixed_owner.png), [passage](../final/passage_fixed_owner.png), and [far view](../final/far_owner.png). The earlier fixed captures are in [round 2](../round2/REPORT.md).
|
||||
|
||||
## Build limits and failed invocations
|
||||
## Previous-round build limits and failed invocations
|
||||
|
||||
The final build is [final_halfload_build.log](final_halfload_build.log). It succeeded with /MP8. The build and workload processes ran BelowNormal on affinity 0xFF00. The recurring French vswhere.exe setup diagnostic still appears, followed by a successful compile; six existing C4838 narrowing warnings remain in VoxelStrateManager.cpp.
|
||||
|
||||
@@ -156,7 +224,7 @@ Failures during this continuation, with corrected work preserved:
|
||||
|
||||
No external viewer was closed. No commit, push, or stash was made. All source, report, and capture edits are within Standalone.
|
||||
|
||||
## Principal logs
|
||||
## Previous-round principal logs
|
||||
|
||||
- Final no-hash full-payload timings: final_full_payload_w8_a/b.stdout.log and final_full_payload_w8_a/b.runner.log.
|
||||
- Final restored hash matrix: final_restored_full_hash_w1.stdout.log and final_restored_full_hash_w8.stdout.log.
|
||||
|
||||
@@ -392,6 +392,7 @@ namespace
|
||||
int32 Seed=0;
|
||||
int32 WorkerCount=8;
|
||||
int32 ClipRadius=3;
|
||||
int32 MaxClipLevelOverride=-1;
|
||||
int32 Width=1280,Height=800;
|
||||
float Yaw=0.0f,Pitch=0.0f,MoveSpeed=8.0f;
|
||||
V3 Position{};
|
||||
@@ -645,6 +646,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
if(!PrepareStandaloneDensityWorld(*World,Seed,bRoundTunnels,
|
||||
bRoundTunnelEnhanced,Error))return false;
|
||||
if(MaxClipLevelOverride>=0)
|
||||
{
|
||||
World->MaxClipLevel=MaxClipLevelOverride;
|
||||
World->Settings->MaxClipLevel=MaxClipLevelOverride;
|
||||
std::printf("Standalone max clip level override applied after world reload: max_level=%d.\n",
|
||||
MaxClipLevelOverride);
|
||||
}
|
||||
if(bShapeWorld&&!BuildShapeWorldForCamera(Error))return false;
|
||||
LoadMeshCache();
|
||||
return true;
|
||||
@@ -1705,6 +1713,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
int32 WorkerCount,int32 MaxLevel,bool bRoundTunnels,
|
||||
bool bEnhancedWalls,const FStandaloneMeshCache* Cache,
|
||||
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,
|
||||
bool bSkipUV,bool bSkipColors,
|
||||
bool bShardAudit,float CameraYaw,float CameraPitch,
|
||||
const std::string& BakeCachePath,
|
||||
const FStandaloneMeshCacheMetadata& CacheMetadata)
|
||||
@@ -1749,15 +1758,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,
|
||||
bHashDensity||!BakeCachePath.empty(),Cache,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups);
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
const double LOD0Width=static_cast<double>(2*World.ClipRadius+2)
|
||||
*CHUNK_SIZE*VOXEL_SIZE/100.0;
|
||||
std::printf("[StreamBenchmarkConfig] clip_radius=%d lod0_cube=%.0fm voxel=25cm fullres_levels=%d max_level_filter=%d workers=%d full_detail_tiles=%d desired_tiles=%d variant=%s uv_colors=%s material_groups=%s hash=%s\n",
|
||||
std::printf("[StreamBenchmarkConfig] clip_radius=%d lod0_cube=%.0fm voxel=25cm fullres_levels=%d max_level_filter=%d workers=%d full_detail_tiles=%d desired_tiles=%d variant=%s uv=%s colors=%s material_groups=%s hash=%s\n",
|
||||
World.ClipRadius,LOD0Width,World.FullResClipLevels,MaxLevel<0?World.MaxClipLevel:MaxLevel,
|
||||
Streamer.GetWorkerCount(),FullDetailExpected,Update.DesiredTiles,
|
||||
bRoundTunnels?(bEnhancedWalls?"round-detailed":"round-legacy"):"base",
|
||||
bSkipUvAndColors?"skipped":"full",bSkipMaterialGroups?"skipped":"full",
|
||||
(bSkipUvAndColors||bSkipUV)?"skipped":"full",
|
||||
(bSkipUvAndColors||bSkipColors)?"skipped":"full",
|
||||
bSkipMaterialGroups?"skipped":"full",
|
||||
(bHashDensity||!BakeCachePath.empty())?"on":"off");
|
||||
std::fflush(stdout);
|
||||
int32 Finished=0,FullDetailFinished=0;
|
||||
@@ -2033,11 +2044,14 @@ int main(int Argc,char** Argv)
|
||||
{if(!JoinedCommandLine.empty())JoinedCommandLine.push_back(' ');JoinedCommandLine+=Argv[I];}
|
||||
FCommandLine::Set(JoinedCommandLine);
|
||||
int32 BenchmarkLevel=-1,BenchmarkCount=64,StreamBenchmarkMaxLevel=-1;
|
||||
int32 MaxClipLevelOverride=-1;
|
||||
int32 ShapeCompareLevel=-1,ShapeCompareTileLimit=0;
|
||||
bool bStreamBenchmark=false;
|
||||
bool bShardAudit=false;
|
||||
bool bStreamHash=false;
|
||||
bool bSkipUvAndColors=false;
|
||||
bool bSkipUV=false;
|
||||
bool bSkipColors=false;
|
||||
bool bSkipMaterialGroups=false;
|
||||
bool bEditBenchmark=false;
|
||||
bool bRoundWallCompare=false;
|
||||
@@ -2073,6 +2087,8 @@ int main(int Argc,char** Argv)
|
||||
{bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;}
|
||||
else if(Arg=="--stream-hash")bStreamHash=true;
|
||||
else if(Arg=="--skip-uv-colors")bSkipUvAndColors=true;
|
||||
else if(Arg=="--skip-uv")bSkipUV=true;
|
||||
else if(Arg=="--skip-colors")bSkipColors=true;
|
||||
else if(Arg=="--skip-material-groups")bSkipMaterialGroups=true;
|
||||
else if(Arg=="--shard-audit")bShardAudit=true;
|
||||
else if(Arg=="--compare-round-walls")
|
||||
@@ -2084,6 +2100,9 @@ int main(int Argc,char** Argv)
|
||||
{BakeMeshCachePath=std::string(Arg.substr(18));App.bForceGenerate=true;}
|
||||
else if(Arg.starts_with("--stream-max-level="))
|
||||
StreamBenchmarkMaxLevel=std::atoi(std::string(Arg.substr(19)).c_str());
|
||||
else if(Arg.starts_with("--max-clip-level="))
|
||||
MaxClipLevelOverride=std::clamp(
|
||||
std::atoi(std::string(Arg.substr(17)).c_str()),0,8);
|
||||
else if(Arg=="--test-one-chunk-crossing")App.bTestOneChunkCrossing=true;
|
||||
else if(Arg=="--capture-after-clipmap")App.bCaptureAfterClipmap=true;
|
||||
else if(Arg=="--quit-after-clipmap")App.bQuitAfterClipmap=true;
|
||||
@@ -2125,11 +2144,20 @@ int main(int Argc,char** Argv)
|
||||
static_cast<float>(App.World->ViewerStartVoxels.Y*0.25),static_cast<float>(App.World->ViewerStartVoxels.Z*0.25)};
|
||||
if(App.bHasCameraOverride){App.Position=App.CameraPositionOverride;App.Yaw=App.CameraYawOverride;App.Pitch=App.CameraPitchOverride;}
|
||||
const int32 ExportClipRadius=App.World->ClipRadius;
|
||||
const int32 ExportMaxClipLevel=App.World->MaxClipLevel;
|
||||
App.World->ClipRadius=App.ClipRadius;
|
||||
App.World->Settings->ClipRadius=App.ClipRadius;
|
||||
if(App.bRoundTunnels&&!PrepareStandaloneDensityWorld(*App.World,App.Seed,
|
||||
App.bRoundTunnels,App.bRoundTunnelEnhanced,Error))
|
||||
{std::fprintf(stderr,"Standalone world setup failed: %s\n",Error.c_str());return 2;}
|
||||
if(MaxClipLevelOverride>=0)
|
||||
{
|
||||
App.MaxClipLevelOverride=MaxClipLevelOverride;
|
||||
App.World->MaxClipLevel=MaxClipLevelOverride;
|
||||
App.World->Settings->MaxClipLevel=MaxClipLevelOverride;
|
||||
std::printf("Standalone max clip level override: exported=%d -> max_level=%d.\n",
|
||||
ExportMaxClipLevel,MaxClipLevelOverride);
|
||||
}
|
||||
const bool bNeedFarShapeWorld=bStreamBenchmark
|
||||
?(StreamBenchmarkMaxLevel<0||StreamBenchmarkMaxLevel>=App.World->FullResClipLevels)
|
||||
:(App.bShapeCompare?ShapeCompareLevel>=App.World->FullResClipLevels
|
||||
@@ -2162,7 +2190,7 @@ int main(int Argc,char** Argv)
|
||||
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
|
||||
App.WorkerCount,StreamBenchmarkMaxLevel,App.bRoundTunnels,
|
||||
App.bRoundTunnelEnhanced,(!App.bShapeWorld&&App.MeshCache.IsLoaded())?&App.MeshCache:nullptr,
|
||||
bStreamHash,bSkipUvAndColors,bSkipMaterialGroups,bShardAudit,
|
||||
bStreamHash,bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,bShardAudit,
|
||||
App.Yaw,App.Pitch,BakeMeshCachePath,App.MakeMeshCacheMetadata());
|
||||
}
|
||||
if(BenchmarkLevel>=0)
|
||||
|
||||
@@ -397,10 +397,13 @@ public:
|
||||
const FStandaloneMeshCache* InMeshCache=nullptr,
|
||||
int32 InMaxLevelFilter=-1,
|
||||
bool bInSkipUvAndColors=false,
|
||||
bool bInSkipMaterialGroups=false)
|
||||
bool bInSkipMaterialGroups=false,
|
||||
bool bInSkipUV=false,
|
||||
bool bInSkipColors=false)
|
||||
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
|
||||
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
|
||||
bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups)
|
||||
bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
|
||||
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors)
|
||||
{
|
||||
VoxelGenLOD::InitializeConsoleSwitches();
|
||||
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
|
||||
@@ -543,6 +546,8 @@ private:
|
||||
UVoxelMarchingCubesMesher Mesher;
|
||||
Mesher.SetGenerator(World->Generator.get());
|
||||
Mesher.bStandaloneSkipUvAndColors=bSkipUvAndColors;
|
||||
Mesher.bStandaloneSkipUV=bSkipUV;
|
||||
Mesher.bStandaloneSkipColors=bSkipColors;
|
||||
Mesher.bStandaloneSkipMaterialGroups=bSkipMaterialGroups;
|
||||
Mesher.bGenerateSkirts=World->bGenerateSkirts;
|
||||
Mesher.SkirtCells=static_cast<float>(World->SkirtCells);
|
||||
@@ -635,6 +640,8 @@ private:
|
||||
int32 MaxLevelFilter=-1;
|
||||
bool bSkipUvAndColors=false;
|
||||
bool bSkipMaterialGroups=false;
|
||||
bool bSkipUV=false;
|
||||
bool bSkipColors=false;
|
||||
std::atomic<uint64> CacheHitCount{0};
|
||||
mutable std::mutex Mutex;
|
||||
std::condition_variable Wake;
|
||||
|
||||
@@ -113,6 +113,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
if (OutDensitySampleCount) { *OutDensitySampleCount = 0; }
|
||||
if (OutDebugDensitySamples) { OutDebugDensitySamples->clear(); }
|
||||
if (!Generator) return MeshData;
|
||||
const bool bEmitUV = !bStandaloneSkipUvAndColors && !bStandaloneSkipUV;
|
||||
const bool bEmitColors = !bStandaloneSkipUvAndColors && !bStandaloneSkipColors;
|
||||
const bool bUseCoarseFarBiomeGrid = Step >= 64;
|
||||
VoxelDensityProfile::FScopedTimer MesherProfileTimer(
|
||||
VoxelDensityProfile::EBucket::MesherGenerateMesh);
|
||||
VoxelDensityProfile::FScopedTimer MesherSetupTimer(
|
||||
@@ -166,6 +169,49 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
SurfColMemo.Reset();
|
||||
}
|
||||
|
||||
struct FBiomeGridSample
|
||||
{
|
||||
int32 Dominant = 0;
|
||||
int32 Neighbor = 0;
|
||||
float Blend = 0.0f;
|
||||
};
|
||||
struct FBiomeTileGridSamples
|
||||
{
|
||||
FBiomeGridSample Corners[4];
|
||||
};
|
||||
static thread_local TMap<int32, FBiomeTileGridSamples> BiomeTileGridMemo;
|
||||
const int32 BiomeGridSpacing = bUseCoarseFarBiomeGrid
|
||||
? FMath::Clamp(InCellsPerAxis, 2, CHUNK_SIZE) * Step : 1;
|
||||
if (bUseCoarseFarBiomeGrid && bEmitColors)
|
||||
{
|
||||
BiomeTileGridMemo.Reset();
|
||||
}
|
||||
|
||||
auto GetBiomeTileGridSamples = [&](int32 ChunkZ) -> FBiomeTileGridSamples
|
||||
{
|
||||
if (const FBiomeTileGridSamples* Existing = BiomeTileGridMemo.Find(ChunkZ))
|
||||
{
|
||||
return *Existing;
|
||||
}
|
||||
FBiomeTileGridSamples Samples;
|
||||
const float SampleZ = static_cast<float>(ChunkZ * CHUNK_SIZE + CHUNK_SIZE / 2);
|
||||
const int32 TileMaxX = OriginVoxels.X + BiomeGridSpacing;
|
||||
const int32 TileMaxY = OriginVoxels.Y + BiomeGridSpacing;
|
||||
Generator->GetBiomeMaterialAt(static_cast<float>(OriginVoxels.X),
|
||||
static_cast<float>(OriginVoxels.Y), SampleZ,
|
||||
Samples.Corners[0].Dominant, Samples.Corners[0].Neighbor, Samples.Corners[0].Blend);
|
||||
Generator->GetBiomeMaterialAt(static_cast<float>(TileMaxX),
|
||||
static_cast<float>(OriginVoxels.Y), SampleZ,
|
||||
Samples.Corners[1].Dominant, Samples.Corners[1].Neighbor, Samples.Corners[1].Blend);
|
||||
Generator->GetBiomeMaterialAt(static_cast<float>(OriginVoxels.X),
|
||||
static_cast<float>(TileMaxY), SampleZ,
|
||||
Samples.Corners[2].Dominant, Samples.Corners[2].Neighbor, Samples.Corners[2].Blend);
|
||||
Generator->GetBiomeMaterialAt(static_cast<float>(TileMaxX),
|
||||
static_cast<float>(TileMaxY), SampleZ,
|
||||
Samples.Corners[3].Dominant, Samples.Corners[3].Neighbor, Samples.Corners[3].Blend);
|
||||
return BiomeTileGridMemo.Add(ChunkZ, Samples);
|
||||
};
|
||||
|
||||
// Normale fournie par l'appelant (gradient lu dans la grille de densité, T1.b) —
|
||||
// plus d'échantillonnage de densité par vertex. RawNormal pointe solide→air ; on la
|
||||
// normalise ici (fallback up si dégénérée).
|
||||
@@ -194,16 +240,56 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
MeshData.Normals.Add(Normal);
|
||||
|
||||
if (!bStandaloneSkipUvAndColors)
|
||||
if (bEmitUV)
|
||||
{
|
||||
// UVs planaires — le triplanar mapping se fait dans le matériau.
|
||||
MeshData.UVs.Add(FVector2f(WorldPos.X / VOXEL_SIZE, WorldPos.Y / VOXEL_SIZE));
|
||||
}
|
||||
|
||||
if (bEmitColors)
|
||||
{
|
||||
//-----------------------------------------------------------------
|
||||
// VERTEX COLOUR — masks for biome palette, slope and border blend.
|
||||
int32 PalD = 0, PalN = 0; float BlendW = 0.0f;
|
||||
if (bUseCoarseFarBiomeGrid)
|
||||
{
|
||||
const float VoxelX = WorldPos.X / VOXEL_SIZE;
|
||||
const float VoxelY = WorldPos.Y / VOXEL_SIZE;
|
||||
const float VoxelZ = WorldPos.Z / VOXEL_SIZE;
|
||||
const float TX = FMath::Clamp(
|
||||
(VoxelX - static_cast<float>(OriginVoxels.X))
|
||||
/ static_cast<float>(BiomeGridSpacing), 0.0f, 1.0f);
|
||||
const float TY = FMath::Clamp(
|
||||
(VoxelY - static_cast<float>(OriginVoxels.Y))
|
||||
/ static_cast<float>(BiomeGridSpacing), 0.0f, 1.0f);
|
||||
const int32 ChunkZ = FMath::FloorToInt(VoxelZ / static_cast<float>(CHUNK_SIZE));
|
||||
const FBiomeTileGridSamples TileSamples = GetBiomeTileGridSamples(ChunkZ);
|
||||
const FBiomeGridSample& S00 = TileSamples.Corners[0];
|
||||
const FBiomeGridSample& S10 = TileSamples.Corners[1];
|
||||
const FBiomeGridSample& S01 = TileSamples.Corners[2];
|
||||
const FBiomeGridSample& S11 = TileSamples.Corners[3];
|
||||
|
||||
const FBiomeGridSample* Nearest = &S00;
|
||||
if (TX >= 0.5f) Nearest = TY >= 0.5f ? &S11 : &S10;
|
||||
else if (TY >= 0.5f) Nearest = &S01;
|
||||
PalD = Nearest->Dominant;
|
||||
PalN = Nearest->Neighbor;
|
||||
BlendW = Nearest->Blend;
|
||||
if (S00.Dominant == PalD && S00.Neighbor == PalN
|
||||
&& S10.Dominant == PalD && S10.Neighbor == PalN
|
||||
&& S01.Dominant == PalD && S01.Neighbor == PalN
|
||||
&& S11.Dominant == PalD && S11.Neighbor == PalN)
|
||||
{
|
||||
const float B0 = FMath::Lerp(S00.Blend, S10.Blend, TX);
|
||||
const float B1 = FMath::Lerp(S01.Blend, S11.Blend, TX);
|
||||
BlendW = FMath::Lerp(B0, B1, TY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Generator->GetBiomeMaterialAt(WorldPos.X / VOXEL_SIZE, WorldPos.Y / VOXEL_SIZE,
|
||||
WorldPos.Z / VOXEL_SIZE, PalD, PalN, BlendW);
|
||||
}
|
||||
const uint8 R = (uint8)FMath::Clamp(PalD, 0, 255);
|
||||
const uint8 A = (uint8)FMath::Clamp(PalN, 0, 255);
|
||||
const uint8 G = (uint8)FMath::Clamp(FMath::RoundToInt((1.0f - FMath::Abs((float)Normal.Z)) * 255.0f), 0, 255);
|
||||
@@ -1314,9 +1400,12 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const int32 Idx = MeshData.Vertices.Num();
|
||||
MeshData.Vertices.Add(Pos);
|
||||
MeshData.Normals.Add(Nrm);
|
||||
if (!bStandaloneSkipUvAndColors)
|
||||
if (bEmitUV)
|
||||
{
|
||||
MeshData.UVs.Add(FVector2f(Pos.X / VOXEL_SIZE, Pos.Y / VOXEL_SIZE));
|
||||
}
|
||||
if (bEmitColors)
|
||||
{
|
||||
MeshData.Colors.Add(Col); // parallel with the other vertex arrays
|
||||
}
|
||||
return Idx;
|
||||
@@ -1342,8 +1431,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
|
||||
const FVector NA = MeshData.Normals[iA];
|
||||
const FVector NB = MeshData.Normals[iB];
|
||||
const FColor CA = bStandaloneSkipUvAndColors ? FColor::White : MeshData.Colors[iA];
|
||||
const FColor CB = bStandaloneSkipUvAndColors ? FColor::White : MeshData.Colors[iB];
|
||||
const FColor CA = bEmitColors ? MeshData.Colors[iA] : FColor::White;
|
||||
const FColor CB = bEmitColors ? MeshData.Colors[iB] : FColor::White;
|
||||
const int32 iA2 = AddSkirtVert(PA - NA * SkirtDepth, NA, CA);
|
||||
const int32 iB2 = AddSkirtVert(PB - NB * SkirtDepth, NB, CB);
|
||||
|
||||
|
||||
@@ -116,9 +116,11 @@ public:
|
||||
// SETTINGS
|
||||
//=========================================================================
|
||||
|
||||
// Optional standalone benchmark switches. Runtime callers leave both false and retain the
|
||||
// Optional standalone benchmark switches. Runtime callers leave all false and retain the
|
||||
// complete FVoxelMeshData contract consumed by the game materials and section setup.
|
||||
bool bStandaloneSkipUvAndColors = false;
|
||||
bool bStandaloneSkipUV = false;
|
||||
bool bStandaloneSkipColors = false;
|
||||
bool bStandaloneSkipMaterialGroups = false;
|
||||
|
||||
// Seuil de l'isosurface dans le champ de densité.
|
||||
|
||||
Reference in new issue
Block a user