research(intervals): blends, dual contouring, surface relief -- look better, not there; new stages single-threaded
Bounded smooth blends at junctions (3.1% of cells), QEF dual contouring on the exact crossings and analytic normals, band-limited surface relief (bound 0.60 m, observed 0.40-0.44 m, R toggles it), sparser strata. Exact signs vs the blended reference at every node, thin features kept, same hash across worker counts. Mesh has 0 boundary edges but 3-7 overused edge groups: not collision-ready. Near CPU total, 6 workers: 241 -> 836 ms. The new stages all run on ONE thread: blend index 186 ms, blend evaluation 201 ms, dual contouring 322 ms, relief 61 ms. Look: slab stack gone, joins rounder; chambers read as broad crumpled facets, junction seams still flaky. Viewer built as interval_viewer2.exe (the owner had the first one open). 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
8d29c09512
commit
a443eb8546
8 files changed
+1118
-38
No files matched your search
@@ -114,3 +114,68 @@ All final runs exited 0. No Unreal process was launched, no `Source/` file was m
|
|||||||
## Next step
|
## Next step
|
||||||
|
|
||||||
Extend the CPU corpus until it emits the intended ~60 MB / 2.4 million triangle workload, with page meshing parallelized deterministically. In parallel, replace the far sheet proxy with actual selected parent room/tunnel/strata shapes, radial horizon clipping, and a shared near/far transition. Re-run the same validation and gates; then decide whether the Unreal publication/collision test is justified.
|
Extend the CPU corpus until it emits the intended ~60 MB / 2.4 million triangle workload, with page meshing parallelized deterministically. In parallel, replace the far sheet proxy with actual selected parent room/tunnel/strata shapes, radial horizon clipping, and a shared near/far transition. Re-run the same validation and gates; then decide whether the Unreal publication/collision test is justified.
|
||||||
|
|
||||||
|
## Round 2: bounded blends, QEF mesh, and surface relief
|
||||||
|
|
||||||
|
Date: 2026-10-02. The existing section above records the first-round measurements; this section records the changed field and mesh.
|
||||||
|
|
||||||
|
### What changed
|
||||||
|
|
||||||
|
- The corpus now has **363 shapes**: 18 rooms, 147 anisotropic tunnel segments with changing cross-sections, 40 pillars, 36 stalactite/stalagmite forms, 42 shelves, 24 floating masses, 12 craters, 40 crowded-junction edits, three sparse rock bands, and the isolated audit seam. The 12 repeated thin strata were removed. The three remaining bands have different orientations and are placed at a nearby air lattice node so the interval mesh sees their thin crossings.
|
||||||
|
- Smooth min/max is applied only in bounded overlap regions. At 70 cm blend width, the mask covers a median **3.08% of cell centers** (3.07–3.19% across five seeds) and **1.94% of lattice nodes** (1.94–1.98%). Everywhere else the original exact ordered interval field is used. The banded features keep their hard field so their crossings remain exact.
|
||||||
|
- The near mesh now uses per-cell QEF dual contouring with cached analytic Hermite crossings/normals, face-connectivity component splits, a deterministic ambiguous-face sign, and deterministic page ownership. Page-edge ownership is checked across all 64 pages. The 12 cm audit seam, which no lattice point can see, gets a closed 336-triangle analytic patch.
|
||||||
|
- A deterministic four-band value-noise displacement runs after meshing, with wavelengths **6.4, 3.2, 1.6, and 1.2 m**. The shortest band spans 4.8 voxel cells. The hard displacement bound is **0.60 m**; the five measured seeds reached 0.404–0.439 m. Vertex normals are rebuilt after displacement. No extra volume samples are taken for relief.
|
||||||
|
- `interval_viewer2.exe` renders the blended dual-contour mesh and keeps a separate displaced vertex buffer. Press **R** to switch displacement off/on. The world-space rock color mottling and D3D11 back-buffer PNG capture are part of this standalone viewer. Capture mode closes its own window after writing the screenshots.
|
||||||
|
|
||||||
|
### Timing comparison
|
||||||
|
|
||||||
|
The first-round values below are the medians from the original five-seed report. Round 2 reports the median/p95/worst of five fresh sequential processes per worker count; p95 is linearly interpolated at the 95th percentile. The blend-index row is the AABB overlap raster plus surface-pair filtering. Near total includes the listed stages; far-field time is separate.
|
||||||
|
|
||||||
|
| Stage (ms) | Before medians, 1 / 6 / 8 workers | After, 1 worker med / p95 / worst | After, 6 workers med / p95 / worst | After, 8 workers med / p95 / worst |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| Corpus/hierarchy, excluding blend-zone index | 0.5 / 0.5 / 0.5 | 0.96 / 1.03 / 1.04 | 0.96 / 0.97 / 0.97 | 0.96 / 1.08 / 1.10 |
|
||||||
|
| Blend-zone index and mask | — / — / — | 186.68 / 203.10 / 205.61 | 186.07 / 203.27 / 205.51 | 186.97 / 202.43 / 204.94 |
|
||||||
|
| Interval solve, compose, and sign words | 154.2 / 48.2 / 43.2 | 154.88 / 156.72 / 157.12 | 48.89 / 50.47 / 50.65 | 46.10 / 46.97 / 47.16 |
|
||||||
|
| Blend-zone pointwise evaluation and crossings | — / — / — | 203.85 / 225.44 / 229.87 | 200.79 / 205.81 / 205.98 | 201.49 / 208.96 / 209.65 |
|
||||||
|
| Active-cell masks | 2.1 / 2.1 / 2.1 | 2.62 / 3.82 / 3.86 | 2.85 / 4.05 / 4.28 | 3.58 / 4.29 / 4.29 |
|
||||||
|
| Near meshing: old contour emitter / QEF dual contour | 188.4 / 191.2 / 192.8 | 315.62 / 324.11 / 325.76 | 322.14 / 344.69 / 349.24 | 330.38 / 331.55 / 331.57 |
|
||||||
|
| Surface displacement and normal rebuild | — / — / — | 61.43 / 62.29 / 62.45 | 61.04 / 61.80 / 61.91 | 61.26 / 63.41 / 63.69 |
|
||||||
|
| **Near CPU total** | **349.3 / 241.1 / 240.2** | **931.93 / 957.68 / 959.08** | **836.06 / 848.28 / 849.57** | **824.27 / 854.37 / 861.33** |
|
||||||
|
| Far-field proxy | 8.6 / 8.6 / 8.6 | 8.59 / 8.61 / 8.62 | 8.57 / 9.51 / 9.58 | 8.58 / 8.60 / 8.61 |
|
||||||
|
| **Near plus far** | **357.9 / 249.7 / 248.8** | **940.54 / 966.28 / 967.68** | **844.62 / 857.37 / 858.80** | **832.85 / 862.93 / 869.89** |
|
||||||
|
|
||||||
|
That is a **2.67× / 3.47× / 3.43×** near-total cost against round 1 at 1/6/8 workers. Most of the new cost is visible in the table: roughly 187–190 ms to build the overlap mask, 201–204 ms to evaluate blended nodes/rebuild crossings, 316–330 ms for dual contouring, and about 61 ms for relief plus normal rebuild. The 6/8-worker interval speedup remains, but the zone work and mesher are single-threaded here.
|
||||||
|
|
||||||
|
The median output across the five seeds is **265,508 near vertices**, **531,164 triangles**, and **12,747,440 near-buffer bytes**. Median working sets were 69.6 / 70.0 / 70.4 MB at 1 / 6 / 8 workers. The proposed 60 MB mesh workload is still not exercised.
|
||||||
|
|
||||||
|
### Correctness, topology, and repeatability
|
||||||
|
|
||||||
|
- All 15 final processes (seeds 1–5 at 1, 6, and 8 workers) exited 0. Each compared the interval/blend signs against the new pointwise blended-field reference at every lattice node: **zero mismatches** and zero blend-boundary sign disagreements. All page ownership checks passed, with zero missing analytic crossings, zero cell-contour faults, and zero open cell contours.
|
||||||
|
- The three lattice-snapped bands are present in the structural mesh. The 12 cm audit seam has no lattice sample and is retained by its closed 336-triangle patch. The thin-surface omission check still rejects a deliberately omitted patch.
|
||||||
|
- The edge-manifold diagnostic reports **zero boundary edges** on all five seeds, but **3–7 overused edge groups** remain. The JSON `non_manifold_edges_zero` field is therefore false. These are localized dual-contour singularities around ambiguous/multiple-crossing cells; they are visible in the diagnostics and were not waived as a zero count. The page-ownership/sign/thin-feature validation result is reported separately from this remaining topology count.
|
||||||
|
- The QEF clamp counter is 141,729–155,046 across seeds. It increments per clamped coordinate axis, not per unique vertex. There are also 1,654–2,035 retained near-zero-area triangles; keeping them preserves edge connectivity after QEF clamping. These facts, plus the remaining overused edges, mean the mesher still needs topology work before treating the output as production collision geometry.
|
||||||
|
- For each seed, raw mesh, displaced mesh, and combined hashes match at all three worker counts. A fresh repeated seed-1/one-worker process also matched: mesh `20d4d80f439c17f6`, displaced mesh `0592d64cb4f269ca`, combined `fe846e12af9fef67`. Per-seed combined hashes: `fe846e12af9fef67`, `8f0994fe2295a34e`, `ee13abc933f14a3f`, `129dcc0c9bac3b3e`, `66361d9daff2b1a6`.
|
||||||
|
|
||||||
|
### Screenshots and visual judgement
|
||||||
|
|
||||||
|
These PNGs were captured from the viewer’s own D3D11 swap-chain back buffer, at 1280×800:
|
||||||
|
|
||||||
|
| View | Capture |
|
||||||
|
|---|---|
|
||||||
|
| Inside the first chamber, relief on | [`chamber.png`](views/chamber.png) |
|
||||||
|
| Same camera, relief off | [`chamber_relief_off.png`](views/chamber_relief_off.png) |
|
||||||
|
| Open point in the crowded junction | [`junction.png`](views/junction.png) |
|
||||||
|
| Close to the chamber wall | [`wall.png`](views/wall.png) |
|
||||||
|
|
||||||
|
Compared with the reject image, the repeated thin slab stack is gone, the color has rock-like variation, and many primitive junctions read as rounded transitions. The relief-on chamber is visibly less smooth than the relief-off view. The result is still well short of the reference cave: the close-wall view shows an obvious rounded ellipsoid, the chamber has broad flat-looking facets, and the material lacks the target’s rich rock texture, crystals, and water. I would describe this as a measurable improvement over the rejection, not a finished natural-rock look.
|
||||||
|
|
||||||
|
### Failed and retried Round 2 attempts
|
||||||
|
|
||||||
|
- Two early compile invocations passed the `vcvars64.bat` path with broken shell quoting, so `cl.exe` never started. An intermediate C++ build also failed because `near` was used as an identifier; it was renamed. The first D3D11 viewer link failed with unresolved `CoInitializeEx`/`CoUninitialize`/`CoCreateInstance`; linking `ole32.lib` fixed it. A later corpus-snap build failed once because `eval_quad` was used before declaration; a forward declaration fixed it. The corrected builds succeeded. MSVC continued to print that `vswhere.exe` was unavailable from `vcvars64.bat`, but it still launched `cl.exe`.
|
||||||
|
- `round2_probe3` through `round2_probe12` were intermediate dual-contour runs that exited on validation failures while the Hermite cache, component topology, degenerate triangles, and thin patches were being corrected. At probe 12, missing crossings had reached zero, but the mesh still reported about 3.7k non-manifold edges. Skipping the zero-area triangles produced thousands of boundary edges; a one-QEF-vertex-per-cell experiment made the overused-edge count worse (798), so both approaches were reverted. The final five-seed check has zero boundary edges and 3–7 overused groups.
|
||||||
|
- The first two D3D capture runs failed at WIC pixel-format selection (`0x88982f80`, unsupported RGBA); they left zero-byte PNGs. The capture path now swaps the D3D11 RGBA readback into WIC BGRA and writes all four non-empty images above. The capture-mode viewer closed itself; no previously open viewer was touched.
|
||||||
|
- MSVC continued to print that `vswhere.exe` was unavailable from `vcvars64.bat`, but all final builds completed. All 15 final benchmark runs and the repeated determinism run exited 0. No Unreal build or launch was done, no plugin `Source/` file changed, and nothing was committed, pushed, or stashed.
|
||||||
|
|
||||||
|
### Round 2 conclusion
|
||||||
|
|
||||||
|
Smooth joins and broad relief improve the rejected world, with only about 3.1% of cell centers in blend zones. The price is substantial: the near build rises from 241 ms to 836 ms at six workers and from 240 ms to 824 ms at eight workers. QEF clamping and the remaining 3–7 overused edges also keep this from being production-ready geometry. The viewer and measurements are ready for owner review; the screenshots show honestly that more corpus shaping/material work is needed to approach the reference.
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -5,15 +5,19 @@
|
|||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
#include <dxgi.h>
|
#include <dxgi.h>
|
||||||
|
#include <wincodec.h>
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#pragma comment(lib, "d3d11.lib")
|
#pragma comment(lib, "d3d11.lib")
|
||||||
#pragma comment(lib, "dxgi.lib")
|
#pragma comment(lib, "dxgi.lib")
|
||||||
#pragma comment(lib, "d3dcompiler.lib")
|
#pragma comment(lib, "d3dcompiler.lib")
|
||||||
#pragma comment(lib, "shell32.lib")
|
#pragma comment(lib, "shell32.lib")
|
||||||
#pragma comment(lib, "user32.lib")
|
#pragma comment(lib, "user32.lib")
|
||||||
|
#pragma comment(lib, "windowscodecs.lib")
|
||||||
|
#pragma comment(lib, "ole32.lib")
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct V3 { float x,y,z; };
|
struct V3 { float x,y,z; };
|
||||||
@@ -33,19 +37,22 @@ struct CB { M4 viewProj; float camera[3],pad; float light[3],fogStart; float fog
|
|||||||
struct App {
|
struct App {
|
||||||
HWND hwnd{}; ID3D11Device* dev{}; ID3D11DeviceContext* ctx{}; IDXGISwapChain* swap{}; ID3D11RenderTargetView* rtv{}; ID3D11DepthStencilView* dsv{};
|
HWND hwnd{}; ID3D11Device* dev{}; ID3D11DeviceContext* ctx{}; IDXGISwapChain* swap{}; ID3D11RenderTargetView* rtv{}; ID3D11DepthStencilView* dsv{};
|
||||||
ID3D11VertexShader* vs{}; ID3D11PixelShader* ps{}; ID3D11InputLayout* layout{}; ID3D11Buffer* cb{}; ID3D11RasterizerState* solid{}; ID3D11RasterizerState* wire{};
|
ID3D11VertexShader* vs{}; ID3D11PixelShader* ps{}; ID3D11InputLayout* layout{}; ID3D11Buffer* cb{}; ID3D11RasterizerState* solid{}; ID3D11RasterizerState* wire{};
|
||||||
ID3D11Buffer* nearVB{}; ID3D11Buffer* nearIB{}; ID3D11Buffer* farVB{}; ID3D11Buffer* farIB{}; UINT nearIndices{},farIndices{};
|
ID3D11Buffer* nearVB{}; ID3D11Buffer* nearReliefVB{}; ID3D11Buffer* nearIB{}; ID3D11Buffer* farVB{}; ID3D11Buffer* farIB{}; UINT nearIndices{},farIndices{};
|
||||||
uint64_t seed=1;int workers=6;double generationMs{};bool showNear=true,showFar=true,wireframe=false,captured=false;float yaw=0,pitch=0,speed=8;V3 pos{};POINT last{};int width=1280,height=800;Clock::time_point prev=Clock::now();double frameMs=0;bool keys[256]{};
|
uint64_t seed=1;int workers=6;double generationMs{};bool showNear=true,showFar=true,wireframe=false,captured=false,showDisplacement=true,captureFailed=false;float yaw=0,pitch=0,speed=8;V3 pos{};POINT last{};int width=1280,height=800;Clock::time_point prev=Clock::now();double frameMs=0;bool keys[256]{};std::filesystem::path captureTarget;
|
||||||
void release_mesh(){if(nearVB)nearVB->Release();if(nearIB)nearIB->Release();if(farVB)farVB->Release();if(farIB)farIB->Release();nearVB=nearIB=farVB=farIB=nullptr;nearIndices=farIndices=0;}
|
void release_mesh(){if(nearVB)nearVB->Release();if(nearReliefVB)nearReliefVB->Release();if(nearIB)nearIB->Release();if(farVB)farVB->Release();if(farIB)farIB->Release();nearVB=nearReliefVB=nearIB=farVB=farIB=nullptr;nearIndices=farIndices=0;}
|
||||||
~App(){release_mesh();if(solid)solid->Release();if(wire)wire->Release();if(cb)cb->Release();if(layout)layout->Release();if(vs)vs->Release();if(ps)ps->Release();if(rtv)rtv->Release();if(dsv)dsv->Release();if(swap)swap->Release();if(ctx)ctx->Release();if(dev)dev->Release();}
|
~App(){release_mesh();if(solid)solid->Release();if(wire)wire->Release();if(cb)cb->Release();if(layout)layout->Release();if(vs)vs->Release();if(ps)ps->Release();if(rtv)rtv->Release();if(dsv)dsv->Release();if(swap)swap->Release();if(ctx)ctx->Release();if(dev)dev->Release();}
|
||||||
bool make_buffer(ID3D11Buffer** out,const void* data,size_t bytes,UINT bind){D3D11_BUFFER_DESC d{};d.ByteWidth=static_cast<UINT>(bytes);d.Usage=D3D11_USAGE_DEFAULT;d.BindFlags=bind;D3D11_SUBRESOURCE_DATA s{};s.pSysMem=data;return SUCCEEDED(dev->CreateBuffer(&d,&s,out));}
|
bool make_buffer(ID3D11Buffer** out,const void* data,size_t bytes,UINT bind){D3D11_BUFFER_DESC d{};d.ByteWidth=static_cast<UINT>(bytes);d.Usage=D3D11_USAGE_DEFAULT;d.BindFlags=bind;D3D11_SUBRESOURCE_DATA s{};s.pSysMem=data;return SUCCEEDED(dev->CreateBuffer(&d,&s,out));}
|
||||||
bool generate(){
|
bool generate(){
|
||||||
auto begin=Clock::now(); Corpus corpus;build_corpus(corpus,seed);AxisData axes[3];AxisStats stats[3];bool overflow=false;
|
auto begin=Clock::now(); Corpus corpus;build_corpus(corpus,seed);AxisData axes[3];AxisStats stats[3];bool overflow=false;
|
||||||
for(int a=0;a<3;++a)axes[a]=solve_axis(corpus,a,workers,stats[a],overflow);
|
for(int a=0;a<3;++a)axes[a]=solve_axis(corpus,a,workers,stats[a],overflow);
|
||||||
std::vector<uint64_t> active;active_cell_masks(axes[2],active);MeshOutput nearMesh=emit_near_mesh(corpus,axes,axes[2],active,workers);FarOutput farMesh=build_far_world(seed);
|
BlendStats blendStats{};apply_blend_zones(corpus,axes,stats,blendStats);
|
||||||
|
std::vector<uint64_t> active;active_cell_masks(axes[2],active);MeshOutput nearMesh=emit_dual_contour_mesh(corpus,axes,axes[2],active);FarOutput farMesh=build_far_world(seed);
|
||||||
|
std::vector<Vertex> reliefVertices;double maxDisplacement=0;apply_surface_displacement(nearMesh,reliefVertices,maxDisplacement);
|
||||||
generationMs=ms(begin,Clock::now());release_mesh();
|
generationMs=ms(begin,Clock::now());release_mesh();
|
||||||
std::vector<GPUVertex> nv;nv.reserve(nearMesh.vertices.size());for(const auto& v:nearMesh.vertices)nv.push_back({{v.v.x,v.v.y,v.v.z},{v.v.nx,v.v.ny,v.v.nz}});
|
std::vector<GPUVertex> nv;nv.reserve(nearMesh.vertices.size());for(const auto& v:nearMesh.vertices)nv.push_back({{v.v.x,v.v.y,v.v.z},{v.v.nx,v.v.ny,v.v.nz}});
|
||||||
|
std::vector<GPUVertex> rv;rv.reserve(reliefVertices.size());for(const auto& v:reliefVertices)rv.push_back({{v.x,v.y,v.z},{v.nx,v.ny,v.nz}});
|
||||||
std::vector<GPUVertex> fv;fv.reserve(farMesh.vertices.size());for(const auto& v:farMesh.vertices)fv.push_back({{v.x,v.y,v.z},{v.nx,v.ny,v.nz}});
|
std::vector<GPUVertex> fv;fv.reserve(farMesh.vertices.size());for(const auto& v:farMesh.vertices)fv.push_back({{v.x,v.y,v.z},{v.nx,v.ny,v.nz}});
|
||||||
if(!nv.empty()&&(!make_buffer(&nearVB,nv.data(),nv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&nearIB,nearMesh.indices.data(),nearMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
|
if(!nv.empty()&&(!make_buffer(&nearVB,nv.data(),nv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&nearReliefVB,rv.data(),rv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&nearIB,nearMesh.indices.data(),nearMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
|
||||||
if(!fv.empty()&&(!make_buffer(&farVB,fv.data(),fv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&farIB,farMesh.indices.data(),farMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
|
if(!fv.empty()&&(!make_buffer(&farVB,fv.data(),fv.size()*sizeof(GPUVertex),D3D11_BIND_VERTEX_BUFFER)||!make_buffer(&farIB,farMesh.indices.data(),farMesh.indices.size()*sizeof(uint32_t),D3D11_BIND_INDEX_BUFFER)))return false;
|
||||||
nearIndices=static_cast<UINT>(nearMesh.indices.size());farIndices=static_cast<UINT>(farMesh.indices.size());
|
nearIndices=static_cast<UINT>(nearMesh.indices.size());farIndices=static_cast<UINT>(farMesh.indices.size());
|
||||||
// The first generated room is centred near local (-13, -13, -8.5) metres.
|
// The first generated room is centred near local (-13, -13, -8.5) metres.
|
||||||
@@ -61,7 +68,7 @@ struct App {
|
|||||||
flags|=D3D11_CREATE_DEVICE_DEBUG;
|
flags|=D3D11_CREATE_DEVICE_DEBUG;
|
||||||
#endif
|
#endif
|
||||||
D3D_FEATURE_LEVEL fl{};if(FAILED(D3D11CreateDeviceAndSwapChain(nullptr,D3D_DRIVER_TYPE_HARDWARE,nullptr,flags,nullptr,0,D3D11_SDK_VERSION,&sd,&swap,&dev,&fl,&ctx)))return false;
|
D3D_FEATURE_LEVEL fl{};if(FAILED(D3D11CreateDeviceAndSwapChain(nullptr,D3D_DRIVER_TYPE_HARDWARE,nullptr,flags,nullptr,0,D3D11_SDK_VERSION,&sd,&swap,&dev,&fl,&ctx)))return false;
|
||||||
const char* shader="cbuffer C:register(b0){float4x4 VP;float3 Camera;float Pad;float3 LightDir;float FogStart;float FogEnd;float Ambient;float2 Pad2;} struct I{float3 p:POSITION;float3 n:NORMAL;};struct O{float4 p:SV_POSITION;float3 world:TEXCOORD0;float3 n:NORMAL;};O VS(I i){O o;o.p=mul(float4(i.p,1),VP);o.world=i.p;o.n=i.n;return o;}float4 PS(O i):SV_TARGET{float3 N=normalize(i.n);float3 L=normalize(-LightDir);float diffuse=saturate(dot(N,L))*0.72;float3 toCam=Camera-i.world;float dist=length(toCam);float head=saturate(dot(N,normalize(toCam)))*0.85/(1+dist*0.018);float light=Ambient+diffuse+head;float3 rock=float3(0.55,0.57,0.60)*light;float fog=saturate((dist-FogStart)/(FogEnd-FogStart));return float4(lerp(rock,float3(0.075,0.095,0.12),fog),1);} ";
|
const char* shader="cbuffer C:register(b0){float4x4 VP;float3 Camera;float Pad;float3 LightDir;float FogStart;float FogEnd;float Ambient;float2 Pad2;} struct I{float3 p:POSITION;float3 n:NORMAL;};struct O{float4 p:SV_POSITION;float3 world:TEXCOORD0;float3 n:NORMAL;};O VS(I i){O o;o.p=mul(float4(i.p,1),VP);o.world=i.p;o.n=i.n;return o;}float hash3(float3 p){return frac(sin(dot(p,float3(127.1,311.7,74.7)))*43758.5453);}float noise3(float3 p){float3 i=floor(p),f=frac(p);f=f*f*(3-2*f);float a=lerp(hash3(i),hash3(i+float3(1,0,0)),f.x);float b=lerp(hash3(i+float3(0,1,0)),hash3(i+float3(1,1,0)),f.x);float c=lerp(hash3(i+float3(0,0,1)),hash3(i+float3(1,0,1)),f.x);float d=lerp(hash3(i+float3(0,1,1)),hash3(i+float3(1,1,1)),f.x);return lerp(lerp(a,b,f.y),lerp(c,d,f.y),f.z);}float4 PS(O i):SV_TARGET{float3 N=normalize(i.n);float3 L=normalize(-LightDir);float diffuse=saturate(dot(N,L))*0.72;float3 toCam=Camera-i.world;float dist=length(toCam);float head=saturate(dot(N,normalize(toCam)))*0.85/(1+dist*0.018);float light=Ambient+diffuse+head;float mottling=0.68*noise3(i.world*0.28)+0.32*noise3(i.world*1.0);float shade=0.45+mottling;float3 rock=float3(0.46,0.39,0.30)*shade*light;float fog=saturate((dist-FogStart)/(FogEnd-FogStart));return float4(lerp(rock,float3(0.075,0.095,0.12),fog),1);} ";
|
||||||
ID3DBlob *vb=nullptr,*pb=nullptr,*err=nullptr;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"VS","vs_4_0",0,0,&vb,&err)))return false;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"PS","ps_4_0",0,0,&pb,&err))){if(vb)vb->Release();return false;}
|
ID3DBlob *vb=nullptr,*pb=nullptr,*err=nullptr;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"VS","vs_4_0",0,0,&vb,&err)))return false;if(FAILED(D3DCompile(shader,strlen(shader),nullptr,nullptr,nullptr,"PS","ps_4_0",0,0,&pb,&err))){if(vb)vb->Release();return false;}
|
||||||
HRESULT hr=dev->CreateVertexShader(vb->GetBufferPointer(),vb->GetBufferSize(),nullptr,&vs);if(SUCCEEDED(hr))hr=dev->CreatePixelShader(pb->GetBufferPointer(),pb->GetBufferSize(),nullptr,&ps);
|
HRESULT hr=dev->CreateVertexShader(vb->GetBufferPointer(),vb->GetBufferSize(),nullptr,&vs);if(SUCCEEDED(hr))hr=dev->CreatePixelShader(pb->GetBufferPointer(),pb->GetBufferSize(),nullptr,&ps);
|
||||||
D3D11_INPUT_ELEMENT_DESC il[]={{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}};if(SUCCEEDED(hr))hr=dev->CreateInputLayout(il,2,vb->GetBufferPointer(),vb->GetBufferSize(),&layout);vb->Release();pb->Release();if(FAILED(hr))return false;
|
D3D11_INPUT_ELEMENT_DESC il[]={{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}};if(SUCCEEDED(hr))hr=dev->CreateInputLayout(il,2,vb->GetBufferPointer(),vb->GetBufferSize(),&layout);vb->Release();pb->Release();if(FAILED(hr))return false;
|
||||||
@@ -70,22 +77,62 @@ struct App {
|
|||||||
return resize(width,height);
|
return resize(width,height);
|
||||||
}
|
}
|
||||||
bool resize(int w,int h){if(!dev||w<1||h<1)return true;width=w;height=h;if(rtv){rtv->Release();rtv=nullptr;}if(dsv){dsv->Release();dsv=nullptr;}if(FAILED(swap->ResizeBuffers(0,w,h,DXGI_FORMAT_UNKNOWN,0)))return false;ID3D11Texture2D* back{};if(FAILED(swap->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&back))))return false;HRESULT hr=dev->CreateRenderTargetView(back,nullptr,&rtv);back->Release();if(FAILED(hr))return false;D3D11_TEXTURE2D_DESC dd{};dd.Width=w;dd.Height=h;dd.MipLevels=1;dd.ArraySize=1;dd.Format=DXGI_FORMAT_D24_UNORM_S8_UINT;dd.SampleDesc.Count=1;dd.BindFlags=D3D11_BIND_DEPTH_STENCIL;ID3D11Texture2D* depth{};if(FAILED(dev->CreateTexture2D(&dd,nullptr,&depth)))return false;hr=dev->CreateDepthStencilView(depth,nullptr,&dsv);depth->Release();return SUCCEEDED(hr);}
|
bool resize(int w,int h){if(!dev||w<1||h<1)return true;width=w;height=h;if(rtv){rtv->Release();rtv=nullptr;}if(dsv){dsv->Release();dsv=nullptr;}if(FAILED(swap->ResizeBuffers(0,w,h,DXGI_FORMAT_UNKNOWN,0)))return false;ID3D11Texture2D* back{};if(FAILED(swap->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&back))))return false;HRESULT hr=dev->CreateRenderTargetView(back,nullptr,&rtv);back->Release();if(FAILED(hr))return false;D3D11_TEXTURE2D_DESC dd{};dd.Width=w;dd.Height=h;dd.MipLevels=1;dd.ArraySize=1;dd.Format=DXGI_FORMAT_D24_UNORM_S8_UINT;dd.SampleDesc.Count=1;dd.BindFlags=D3D11_BIND_DEPTH_STENCIL;ID3D11Texture2D* depth{};if(FAILED(dev->CreateTexture2D(&dd,nullptr,&depth)))return false;hr=dev->CreateDepthStencilView(depth,nullptr,&dsv);depth->Release();return SUCCEEDED(hr);}
|
||||||
|
bool save_backbuffer_png(const std::filesystem::path& path){
|
||||||
|
ID3D11Texture2D* back{};ID3D11Texture2D* staging{};IWICImagingFactory* factory{};IWICStream* stream{};IWICBitmapEncoder* encoder{};IWICBitmapFrameEncode* frame{};IPropertyBag2* props{};D3D11_TEXTURE2D_DESC desc{};D3D11_MAPPED_SUBRESOURCE mapped{};bool ok=false;HRESULT hr=S_OK;const char* stage="get swapchain buffer";
|
||||||
|
if(FAILED(hr=swap->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&back))))goto done;
|
||||||
|
back->GetDesc(&desc);desc.Usage=D3D11_USAGE_STAGING;desc.BindFlags=0;desc.CPUAccessFlags=D3D11_CPU_ACCESS_READ;desc.MiscFlags=0;
|
||||||
|
stage="create staging texture";if(FAILED(hr=dev->CreateTexture2D(&desc,nullptr,&staging)))goto done;
|
||||||
|
ctx->CopyResource(staging,back);
|
||||||
|
stage="map staging texture";if(FAILED(hr=ctx->Map(staging,0,D3D11_MAP_READ,0,&mapped)))goto done;
|
||||||
|
{
|
||||||
|
const UINT stride=desc.Width*4;std::vector<BYTE> pixels(static_cast<size_t>(stride)*desc.Height);
|
||||||
|
for(UINT y=0;y<desc.Height;++y){const BYTE* src=static_cast<const BYTE*>(mapped.pData)+static_cast<size_t>(y)*mapped.RowPitch;BYTE* dst=pixels.data()+static_cast<size_t>(y)*stride;for(UINT x=0;x<desc.Width;++x){dst[x*4]=src[x*4+2];dst[x*4+1]=src[x*4+1];dst[x*4+2]=src[x*4];dst[x*4+3]=src[x*4+3];}}
|
||||||
|
ctx->Unmap(staging,0);
|
||||||
|
stage="create WIC factory";if(FAILED(hr=CoCreateInstance(CLSID_WICImagingFactory,nullptr,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&factory))))goto done;
|
||||||
|
stage="create WIC stream";if(FAILED(hr=factory->CreateStream(&stream)))goto done;
|
||||||
|
stage="open PNG path";if(FAILED(hr=stream->InitializeFromFilename(path.c_str(),GENERIC_WRITE)))goto done;
|
||||||
|
stage="create PNG encoder";if(FAILED(hr=factory->CreateEncoder(GUID_ContainerFormatPng,nullptr,&encoder)))goto done;
|
||||||
|
stage="initialize PNG encoder";if(FAILED(hr=encoder->Initialize(stream,WICBitmapEncoderNoCache)))goto done;
|
||||||
|
stage="create PNG frame";if(FAILED(hr=encoder->CreateNewFrame(&frame,&props)))goto done;
|
||||||
|
stage="initialize PNG frame";if(FAILED(hr=frame->Initialize(props)))goto done;
|
||||||
|
stage="set PNG size";if(FAILED(hr=frame->SetSize(desc.Width,desc.Height)))goto done;
|
||||||
|
{WICPixelFormatGUID format=GUID_WICPixelFormat32bppBGRA;stage="set PNG pixel format";hr=frame->SetPixelFormat(&format);if(FAILED(hr)||format!=GUID_WICPixelFormat32bppBGRA){if(SUCCEEDED(hr))hr=WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;goto done;}}
|
||||||
|
stage="write PNG pixels";if(FAILED(hr=frame->WritePixels(desc.Height,stride,static_cast<UINT>(pixels.size()),pixels.data())))goto done;
|
||||||
|
stage="commit PNG";if(FAILED(hr=frame->Commit())||FAILED(hr=encoder->Commit()))goto done;ok=true;
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
if(!ok)std::fprintf(stderr,"Capture failed at %s (HRESULT 0x%08lx)\n",stage,static_cast<unsigned long>(hr));
|
||||||
|
if(staging)staging->Release();if(back)back->Release();if(frame)frame->Release();if(props)props->Release();if(encoder)encoder->Release();if(stream)stream->Release();if(factory)factory->Release();return ok;
|
||||||
|
}
|
||||||
V3 forward(){return norm({std::sin(yaw)*std::cos(pitch),std::cos(yaw)*std::cos(pitch),std::sin(pitch)});}
|
V3 forward(){return norm({std::sin(yaw)*std::cos(pitch),std::cos(yaw)*std::cos(pitch),std::sin(pitch)});}
|
||||||
void title(){char b[256];snprintf(b,sizeof(b),"Interval spike | %.2f ms/frame | %u tris | (%.1f, %.1f, %.1f) m | gen %.1f ms | seed %llu",frameMs,((showNear?nearIndices:0)+(showFar?farIndices:0))/3,pos.x,pos.y,pos.z,generationMs,(unsigned long long)seed);SetWindowTextA(hwnd,b);}
|
void title(){char b[256];snprintf(b,sizeof(b),"Interval spike | %.2f ms/frame | %u tris | relief %s | (%.1f, %.1f, %.1f) m | gen %.1f ms | seed %llu",frameMs,((showNear?nearIndices:0)+(showFar?farIndices:0))/3,showDisplacement?"ON":"OFF",pos.x,pos.y,pos.z,generationMs,(unsigned long long)seed);SetWindowTextA(hwnd,b);}
|
||||||
void draw(){auto now=Clock::now();float dt=std::clamp(std::chrono::duration<float>(now-prev).count(),0.0f,.1f);prev=now;frameMs=dt*1000;
|
void draw(){auto now=Clock::now();float dt=std::clamp(std::chrono::duration<float>(now-prev).count(),0.0f,.1f);prev=now;frameMs=dt*1000;
|
||||||
V3 f=forward(),r=norm(cross(f,{0,0,1})),u=norm(cross(r,f));float step=speed*dt*(keys[VK_SHIFT]?4.0f:1.0f);if(keys['W'])pos=add(pos,mul(f,step));if(keys['S'])pos=sub(pos,mul(f,step));if(keys['A'])pos=sub(pos,mul(r,step));if(keys['D'])pos=add(pos,mul(r,step));if(keys[VK_SPACE]||keys['E'])pos.z+=step;if(keys[VK_CONTROL]||keys['Q'])pos.z-=step;
|
V3 f=forward(),r=norm(cross(f,{0,0,1})),u=norm(cross(r,f));float step=speed*dt*(keys[VK_SHIFT]?4.0f:1.0f);if(keys['W'])pos=add(pos,mul(f,step));if(keys['S'])pos=sub(pos,mul(f,step));if(keys['A'])pos=sub(pos,mul(r,step));if(keys['D'])pos=add(pos,mul(r,step));if(keys[VK_SPACE]||keys['E'])pos.z+=step;if(keys[VK_CONTROL]||keys['Q'])pos.z-=step;
|
||||||
float clear[]={.075f,.095f,.12f,1};ctx->OMSetRenderTargets(1,&rtv,dsv);ctx->ClearRenderTargetView(rtv,clear);ctx->ClearDepthStencilView(dsv,D3D11_CLEAR_DEPTH,1,0);D3D11_VIEWPORT vp{0,0,(float)width,(float)height,0,1};ctx->RSSetViewports(1,&vp);ctx->RSSetState(wireframe?wire:solid);ctx->IASetInputLayout(layout);ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
float clear[]={.075f,.095f,.12f,1};ctx->OMSetRenderTargets(1,&rtv,dsv);ctx->ClearRenderTargetView(rtv,clear);ctx->ClearDepthStencilView(dsv,D3D11_CLEAR_DEPTH,1,0);D3D11_VIEWPORT vp{0,0,(float)width,(float)height,0,1};ctx->RSSetViewports(1,&vp);ctx->RSSetState(wireframe?wire:solid);ctx->IASetInputLayout(layout);ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
CB c{};M4 v=view_matrix(pos,f,r,u);M4 p=perspective(1.0472f,(float)width/height,.05f,20000); // compose view and projection for row-vector shader
|
CB c{};M4 v=view_matrix(pos,f,r,u);M4 p=perspective(1.0472f,(float)width/height,.05f,20000); // compose view and projection for row-vector shader
|
||||||
M4 out{};for(int i=0;i<4;++i)for(int j=0;j<4;++j)for(int k=0;k<4;++k)out.m[i][j]+=v.m[i][k]*p.m[k][j];c.viewProj=transpose(out);c.camera[0]=pos.x;c.camera[1]=pos.y;c.camera[2]=pos.z;c.light[0]=-.45f;c.light[1]=-.8f;c.light[2]=-.35f;c.fogStart=80;c.fogEnd=1200;c.ambient=.28f;ctx->UpdateSubresource(cb,0,nullptr,&c,0,0);ctx->VSSetConstantBuffers(0,1,&cb);ctx->PSSetConstantBuffers(0,1,&cb);ctx->VSSetShader(vs,nullptr,0);ctx->PSSetShader(ps,nullptr,0);
|
M4 out{};for(int i=0;i<4;++i)for(int j=0;j<4;++j)for(int k=0;k<4;++k)out.m[i][j]+=v.m[i][k]*p.m[k][j];c.viewProj=transpose(out);c.camera[0]=pos.x;c.camera[1]=pos.y;c.camera[2]=pos.z;c.light[0]=-.45f;c.light[1]=-.8f;c.light[2]=-.35f;c.fogStart=80;c.fogEnd=1200;c.ambient=.28f;ctx->UpdateSubresource(cb,0,nullptr,&c,0,0);ctx->VSSetConstantBuffers(0,1,&cb);ctx->PSSetConstantBuffers(0,1,&cb);ctx->VSSetShader(vs,nullptr,0);ctx->PSSetShader(ps,nullptr,0);
|
||||||
auto mesh=[&](ID3D11Buffer* vb,ID3D11Buffer* ib,UINT count){if(!vb||!ib||!count)return;UINT stride=sizeof(GPUVertex),offset=0;ctx->IASetVertexBuffers(0,1,&vb,&stride,&offset);ctx->IASetIndexBuffer(ib,DXGI_FORMAT_R32_UINT,0);ctx->DrawIndexed(count,0,0);};if(showNear)mesh(nearVB,nearIB,nearIndices);if(showFar)mesh(farVB,farIB,farIndices);swap->Present(1,0);title();}
|
auto mesh=[&](ID3D11Buffer* vb,ID3D11Buffer* ib,UINT count){if(!vb||!ib||!count)return;UINT stride=sizeof(GPUVertex),offset=0;ctx->IASetVertexBuffers(0,1,&vb,&stride,&offset);ctx->IASetIndexBuffer(ib,DXGI_FORMAT_R32_UINT,0);ctx->DrawIndexed(count,0,0);};if(showNear)mesh(showDisplacement?nearReliefVB:nearVB,nearIB,nearIndices);if(showFar)mesh(farVB,farIB,farIndices);if(!captureTarget.empty()){if(save_backbuffer_png(captureTarget))std::printf("Captured %ls\n",captureTarget.c_str());else{std::fprintf(stderr,"Capture failed: %ls\n",captureTarget.c_str());captureFailed=true;}captureTarget.clear();}swap->Present(1,0);title();}
|
||||||
void capture(bool on){captured=on;if(on){SetCapture(hwnd);ShowCursor(FALSE);GetCursorPos(&last);POINT c{width/2,height/2};ClientToScreen(hwnd,&c);SetCursorPos(c.x,c.y);}else{ReleaseCapture();ShowCursor(TRUE);}}
|
void capture(bool on){captured=on;if(on){SetCapture(hwnd);ShowCursor(FALSE);GetCursorPos(&last);POINT c{width/2,height/2};ClientToScreen(hwnd,&c);SetCursorPos(c.x,c.y);}else{ReleaseCapture();ShowCursor(TRUE);}}
|
||||||
};
|
};
|
||||||
App* g{};
|
App* g{};
|
||||||
LRESULT CALLBACK wndproc(HWND h,UINT m,WPARAM w,LPARAM l){if(!g)return DefWindowProc(h,m,w,l);switch(m){case WM_SIZE:if(w!=SIZE_MINIMIZED)g->resize(LOWORD(l),HIWORD(l));return 0;case WM_KEYDOWN:if(w<256)g->keys[w]=true;if(w==VK_ESCAPE){if(g->captured)g->capture(false);else DestroyWindow(h);}if(w=='F')g->wireframe=!g->wireframe;if(w=='N'||w=='P'){g->seed+=(w=='N'?1:static_cast<uint64_t>(-1));if(!g->generate())MessageBoxA(h,"Could not create mesh buffers.","Viewer error",MB_ICONERROR);}if(w=='M')g->showNear=!g->showNear;if(w=='B')g->showFar=!g->showFar;return 0;case WM_KEYUP:if(w<256)g->keys[w]=false;return 0;case WM_RBUTTONDOWN:g->capture(true);return 0;case WM_RBUTTONUP:g->capture(false);return 0;case WM_MOUSEMOVE:if(g->captured){POINT c{g->width/2,g->height/2};ClientToScreen(h,&c);POINT p{GET_X_LPARAM(l),GET_Y_LPARAM(l)};POINT sc=p;ClientToScreen(h,&sc);int dx=sc.x-c.x,dy=sc.y-c.y;if(dx||dy){g->yaw+=dx*.0035f;g->pitch=std::clamp(g->pitch-dy*.0035f,-1.55f,1.55f);SetCursorPos(c.x,c.y);}}return 0;case WM_MOUSEWHEEL:g->speed=std::clamp(g->speed*(GET_WHEEL_DELTA_WPARAM(w)>0?1.25f:.8f),.25f,1000.0f);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(h,m,w,l);}
|
LRESULT CALLBACK wndproc(HWND h,UINT m,WPARAM w,LPARAM l){if(!g)return DefWindowProc(h,m,w,l);switch(m){case WM_SIZE:if(w!=SIZE_MINIMIZED)g->resize(LOWORD(l),HIWORD(l));return 0;case WM_KEYDOWN:if(w<256)g->keys[w]=true;if(w==VK_ESCAPE){if(g->captured)g->capture(false);else DestroyWindow(h);}if(w=='F')g->wireframe=!g->wireframe;if(w=='R')g->showDisplacement=!g->showDisplacement;if(w=='N'||w=='P'){g->seed+=(w=='N'?1:static_cast<uint64_t>(-1));if(!g->generate())MessageBoxA(h,"Could not create mesh buffers.","Viewer error",MB_ICONERROR);}if(w=='M')g->showNear=!g->showNear;if(w=='B')g->showFar=!g->showFar;return 0;case WM_KEYUP:if(w<256)g->keys[w]=false;return 0;case WM_RBUTTONDOWN:g->capture(true);return 0;case WM_RBUTTONUP:g->capture(false);return 0;case WM_MOUSEMOVE:if(g->captured){POINT c{g->width/2,g->height/2};ClientToScreen(h,&c);POINT p{GET_X_LPARAM(l),GET_Y_LPARAM(l)};POINT sc=p;ClientToScreen(h,&sc);int dx=sc.x-c.x,dy=sc.y-c.y;if(dx||dy){g->yaw+=dx*.0035f;g->pitch=std::clamp(g->pitch-dy*.0035f,-1.55f,1.55f);SetCursorPos(c.x,c.y);}}return 0;case WM_MOUSEWHEEL:g->speed=std::clamp(g->speed*(GET_WHEEL_DELTA_WPARAM(w)>0?1.25f:.8f),.25f,1000.0f);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(h,m,w,l);}
|
||||||
}
|
}
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE instance,HINSTANCE,LPSTR,int){
|
int WINAPI WinMain(HINSTANCE instance,HINSTANCE,LPSTR,int){
|
||||||
int argc=0;LPWSTR* argv=CommandLineToArgvW(GetCommandLineW(),&argc);uint64_t seed=1;int workers=6,positional=0;if(argv){for(int i=1;i<argc;++i){if(positional==0){seed=_wcstoui64(argv[i],nullptr,10);++positional;}else if(positional==1){workers=std::clamp(_wtoi(argv[i]),1,8);++positional;}}LocalFree(argv);}
|
int argc=0;LPWSTR* argv=CommandLineToArgvW(GetCommandLineW(),&argc);uint64_t seed=1;int workers=6,positional=0;std::filesystem::path captureDir;bool captureMode=false;
|
||||||
AllocConsole();FILE* stream{};freopen_s(&stream,"CONOUT$","w",stdout);std::printf("Controls: hold right mouse button to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel speed; F wireframe; M near; B far; N/P next/previous seed; Esc quit.\n");
|
if(argv)for(int i=1;i<argc;++i){if(std::wcscmp(argv[i],L"--capture-dir")==0&&i+1<argc){captureDir=argv[++i];captureMode=true;}else if(std::wcscmp(argv[i],L"--seed")==0&&i+1<argc)seed=_wcstoui64(argv[++i],nullptr,10);else if(std::wcscmp(argv[i],L"--workers")==0&&i+1<argc)workers=std::clamp(_wtoi(argv[++i]),1,8);else if(positional==0){seed=_wcstoui64(argv[i],nullptr,10);++positional;}else if(positional==1){workers=std::clamp(_wtoi(argv[i]),1,8);++positional;}}
|
||||||
App app;g=&app;app.seed=seed;app.workers=workers;WNDCLASSA wc{};wc.lpfnWndProc=wndproc;wc.hInstance=instance;wc.lpszClassName="IntervalSpikeViewer";wc.hCursor=LoadCursor(nullptr,IDC_ARROW);RegisterClassA(&wc);RECT rc{0,0,app.width,app.height};AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);app.hwnd=CreateWindowA(wc.lpszClassName,"Interval spike viewer",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,nullptr,nullptr,instance,nullptr);if(!app.hwnd||!app.init_device()){MessageBoxA(nullptr,"Direct3D 11 initialization failed.","Viewer error",MB_ICONERROR);return 1;}ShowWindow(app.hwnd,SW_SHOW);if(!app.generate()){MessageBoxA(app.hwnd,"World generation or GPU buffer creation failed.","Viewer error",MB_ICONERROR);return 2;}MSG msg{};while(msg.message!=WM_QUIT){if(PeekMessage(&msg,nullptr,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else app.draw();}return 0;
|
if(FAILED(CoInitializeEx(nullptr,COINIT_MULTITHREADED))){if(argv)LocalFree(argv);return 1;}
|
||||||
|
AllocConsole();FILE* stream{};freopen_s(&stream,"CONOUT$","w",stdout);std::printf("Controls: hold right mouse button to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel speed; F wireframe; R relief toggle; M near; B far; N/P next/previous seed; Esc quit.\n");
|
||||||
|
App app;g=&app;app.seed=seed;app.workers=workers;WNDCLASSA wc{};wc.lpfnWndProc=wndproc;wc.hInstance=instance;wc.lpszClassName="IntervalSpikeViewer2";wc.hCursor=LoadCursor(nullptr,IDC_ARROW);RegisterClassA(&wc);RECT rc{0,0,app.width,app.height};AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE);app.hwnd=CreateWindowA(wc.lpszClassName,"Interval spike viewer 2",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,nullptr,nullptr,instance,nullptr);if(!app.hwnd||!app.init_device()){MessageBoxA(nullptr,"Direct3D 11 initialization failed.","Viewer error",MB_ICONERROR);CoUninitialize();if(argv)LocalFree(argv);return 1;}ShowWindow(app.hwnd,SW_SHOW);UpdateWindow(app.hwnd);if(!app.generate()){MessageBoxA(app.hwnd,"World generation or GPU buffer creation failed.","Viewer error",MB_ICONERROR);CoUninitialize();if(argv)LocalFree(argv);return 2;}
|
||||||
|
if(captureMode){
|
||||||
|
std::error_code ec;std::filesystem::create_directories(captureDir,ec);if(ec){std::fprintf(stderr,"Could not create capture directory: %ls\n",captureDir.c_str());CoUninitialize();if(argv)LocalFree(argv);return 3;}
|
||||||
|
auto capture=[&](const wchar_t* name){app.captureTarget=captureDir/name;app.draw();};
|
||||||
|
capture(L"chamber.png");
|
||||||
|
app.showDisplacement=false;capture(L"chamber_relief_off.png");app.showDisplacement=true;
|
||||||
|
app.pos={kWorldOriginCm[0]*.01f-2.5f,kWorldOriginCm[1]*.01f-.75f,kWorldOriginCm[2]*.01f-3.5f};app.yaw=0;app.pitch=0;capture(L"junction.png");
|
||||||
|
Rng rng(seed ^ 0x0A57A20261002ull);int lx=-1300+rng.range(-170,170),ly=-1300+rng.range(-170,170),lz=-850+rng.range(-120,120);
|
||||||
|
app.pos={kWorldOriginCm[0]*.01f+(lx+500)*.01f,kWorldOriginCm[1]*.01f+ly*.01f,kWorldOriginCm[2]*.01f+lz*.01f};app.yaw=1.5707963f;app.pitch=0;capture(L"wall.png");
|
||||||
|
const bool ok=!app.captureFailed;DestroyWindow(app.hwnd);g=nullptr;CoUninitialize();if(argv)LocalFree(argv);return ok?0:4;
|
||||||
|
}
|
||||||
|
if(argv)LocalFree(argv);MSG msg{};while(msg.message!=WM_QUIT){if(PeekMessage(&msg,nullptr,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else app.draw();}CoUninitialize();return 0;
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 418 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 406 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 340 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 429 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 498 KiB |
Reference in new issue
Block a user