尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A client-side, stylized 3D city walker: spawn into a pastel rendering of
Dresden built from Saxon open geodata and walk (or fly) through it. Buildings
come from LoD2 CityJSON, the ground from DGM1 elevation rasters,
surfaces (roads, water, meadow, …) from an ATKIS Basis-DLM land-cover
raster, and trees from DLM hedge/tree rows plus a DOM1-derived canopy. The
build turns it into an OGC 3D Tiles
tileset of glTF, which the browser streams with
3DTilesRendererJS and renders
with three.js (WebGPURenderer and TSL node
materials) — one route, no backend, no database, no accounts, nothing
persisted.檢視完整 README ↗A client-side, stylized 3D city walker: spawn into a pastel rendering of a
German city built from open geodata and walk (or fly) through it — Dresden
first, and any place whose Land publishes the data: Leipzig, Meißen, Grimma,
Hamburg, München, Berlin and Unna are configured. Buildings come from LoD2
CityGML/CityJSON, the ground from DGM1 elevation rasters, surfaces
(roads, water, meadow, …) from the ATKIS Basis-DLM (or OpenStreetMap where
the Land does not publish it openly), and trees from hedge/tree rows plus a
canopy derived from the surface model. The build turns it into an
OGC 3D Tiles tileset of glTF, which
the browser streams with
3DTilesRendererJS and renders
with three.js (WebGPURenderer and TSL node
materials) — a start page and one route per city (/dresden, /leipzig,
…), no backend, no database, no accounts; nothing persisted but what
the browser keeps for itself (the crash trail, the device's GPU safety
level — ADR 0046 —, a few preferences; the full list is on /datenschutz).
package.json packageManager)WebGPURenderer picks its WebGL2 backend; ?gpu=webgl2 forces it)pipeline/)bun install
bun dev # prepares public/data, then serves https://localhost:3000
The dev server speaks HTTPS only (WebGPU on a phone over the LAN needs a secure context); Next generates a self-signed certificate on the first run, so the browser asks once to accept it.
The data of seven cities is committed, so that is all: the start page at
/ lists Dresden, Grimma, Hamburg, Leipzig, Meißen, München and Unna,
and each viewer runs at its own route (/dresden, …). bun dev runs
scripts/prepare-sites.ts first, which runs scripts/prepare-data.ts for
every site whose data is ready; that bakes the site's data into a 3D Tiles
tileset in public/data/<site>/ — terrain meshes at two levels of detail
from the DGM GeoTIFFs, building meshes with a per-building attribute table
from the CityJSON — and publishes everything under content-hashed names with
a manifest.json (≈ 2 min cold for fifteen tiles, ≈ 1 s warm; the
bake cache lives in .cache/). The index of the built sites,
public/data/sites.json, is what the start page lists.
Add ?scene=lite (/dresden?scene=lite) to stream the spawn tile alone with a small shadow map —
that is what the headless e2e suite uses; it is not how the scene is meant to
look.
bun run fetch leipzig # downloads the site's data (once; minutes to
# an hour — statewide packages are GBs)
bun run bake leipzig # derives land cover, trees, lamps, … (minutes)
bun run site leipzig # where the site stands, tile by tile
bun dev # or: bun run build — now also /leipzig
The site is the first argument of every script (leipzig, meissen,
grimma, hamburg, muenchen, berlin, unna, dresden); a wrong or
missing one stops with the list. The build serves every site whose data is
ready, each under its own route, and the start page gains a card for it
(bun scripts/prepare-sites.ts leipzig unna prepares only those). Each site
is one file in
sites/ — its tiles (2 km cells, the first is the spawn),
curated viewpoints, and the data provider it draws on
(sites/providers.ts: CRS, licence and credit, which
products are open). bun run site --all lists every site and how much of it
is on disk. Adding a place in a covered Land is one file plus one line in
sites/index.ts; see portability.
Deploying more cities needs no configuration: one deployment serves
every site whose data/<id>/ is present where it builds, at /<id>. A host
that builds from git sees only what is committed — the seven cities above
(.gitignore un-ignores each by name); un-ignore a new site's folder once
you decide to ship it — it is roughly 20–90 MB per site, plain git, no LFS (ADR 0037).
Crash and performance reports are optional: set
NEXT_PUBLIC_SENTRY_DSN (a Sentry project's DSN, or any tracker that takes
Sentry envelopes) in the deploy's environment and rebuild. The viewer then
reports pages the phone killed (from the next load), the errors it catches,
each page's frame rate and memory and a session per page (crash-free rate
per release) — no SDK, no position, no cookie, through the site's own
origin (ADR 0043).
With SENTRY_AUTH_TOKEN (an organisation token), SENTRY_ORG and
SENTRY_PROJECT there as well, the build creates its release,
bridge@<commit>, with its commit and deploy. Without a DSN, nothing is
sent.
Impressum and privacy policy (/impressum, /datenschutz, German)
are MDX pages, app/(legal)/*/page.mdx (@next/mdx), and name this
deployment's operator, host (Vercel) and tracker (Sentry, EU region) —
a deployment of your own edits them. Visitors turn the reports off on
the privacy page (ADR 0045).
provider portals ── bun run fetch <site> ──→ data/_raw/<provider>/ (downloads, gitignored)
└─→ data/<site>/dgm, cityjson (build sources)
data/_raw + data/<site> ── bun run bake <site> ──→ data/<site>/dlm, dop (derived, small)
data/<site>/ ── scripts/prepare-sites.ts (bun dev/build) ──→ public/data/<site>/ (3D Tiles)
└─→ public/data/sites.json (the index)
The fetch step is one adapter per Land in pipeline/bake/providers/
(Saxony, NRW, Bavaria, Hamburg, Berlin): it mosaics and clips each
provider's grid to our 2 km tiles, writes the DGM compactly and converts the
LoD2 CityGML to CityJSON with its own converter (no Java, no extra tools).
The bulky downloads — surface model, orthophotos, the statewide Basis-DLM,
the OSM extract — are kept per provider, so sites in one Land share them;
a tile's DGM and LoD2 go straight into data/<site>/.
Tile id scheme: <UTM zone><easting km>_<northing km>_2_<provider> — Dresden
spawns on 33412_5656_2_sn, spanning 412000–414000 E / 5656000–5658000 N in
EPSG:25833 (the provider sets the CRS: EPSG:25833 for Saxony and Berlin,
25832 for Hamburg, Bavaria and NRW). The viewer streams every tile of the site around the camera —
detailed near, coarse far, unloaded when out of view — and walking, collision
and demolish work on every loaded tile.
Neither the DGM1 GeoTIFF nor the CityJSON is served. prepare-data.ts
turns the DGM into terrain meshes at two levels — the fine one an
error-bounded TIN of the native 1 m DGM (±0.15 m) with retaining walls
snapped to the measured step, the coarse one the DGM resampled to 512² with
the walls burned in as breaklines — and runs the CityJSON parser once at build
time; both are written as standard glTF (meshopt-compressed, quantised,
buildings with an EXT_mesh_features / EXT_structural_metadata table) into
a tileset (lib/city/tileset.ts). Every /data file
is published under a content-hashed name and cached as immutable; each
site's manifest.json maps the logical names and, with sites.json, is
what revalidates.
Provenance. Each provider's licence and credit line is in
sites/providers.ts and shows in the HUD footer. For
Dresden: the Saxon open-geodata portal of
GeoSN (Datenlizenz Deutschland – Namensnennung 2.0, "Quelle: GeoSN,
dl-de/by-2-0") plus OpenStreetMap for street lamps, retaining walls, station
platforms and bridge structure (ODbL, "© OpenStreetMap contributors"). Every
dataset — download route, strengths and weaknesses, the editions in use — is
described in the guide Where the data comes from
(deutsch).
Desktop:
| Input | Action |
|---|---|
| Drag | Look around |
W A S D | Move (Shift sprints) |
F | Toggle walk / fly (an animated take-off / landing) |
Space / Shift | Up / down (fly mode) |
| Scroll | Move forward / back |
Alt + scroll | Zoom (field of view) |
| Double-click the ground | Glide there |
| Click the minimap | Glide there |
R | Demolish the building under the crosshair |
| "Immersive mode" | Pointer lock (Esc exits) |
Touch: drag to look, joystick to walk, pinch to move forward / back, double-tap the ground to glide there, and the button in the corner opens the scene settings.
A React shell owns the HUD; three.js owns the canvas.
app/_components/city-walk.tsx mounts
createCityWalkApp (create-app.ts), which
builds the scene imperatively and returns a handle of setters — every HUD
slider calls one. Pure, three-free, unit-tested math lives in
lib/city/; the renderer, the TSL node materials and the
post pipeline live in app/_components/.
Coordinate frames matter here. Source data is ETRS89/UTM, Z-up
(EPSG:25833 for Saxony and Berlin, 25832 for Hamburg, Bavaria and NRW); a parent
world group is rotated −90° about X so data-Z (elevation) becomes scene-Y
(up), giving x = easting − cx, z = −(northing − cy), y = elevation, with
(cx, cy) the shared recenter offset (lib/city/recenter.ts,
lib/city/ground-clamp.ts). The sun goes suncalc →
ENU → world in lib/city/sun.ts — note suncalc 2 reports
degrees with a north-based azimuth (1.x used radians measured from south).
Two design decisions worth knowing: demolish filters the building's
triangles out of its tile's index buffer and rebuilds the tile's BVH — there
is no CityJSON in the browser to re-parse (city-layer.ts), and
cityjson-threejs-loader is patched via patches/ — read the
patch header before bumping it. The reasoning behind these and the other
load-bearing choices is recorded in docs/adr.
bun run verify # lint + typecheck + unit tests — run this before pushing
bun run build
bun run test:e2e # Playwright, against a production build
bun run fix # oxfmt + oxlint --fix
bun run shots # real-GPU screenshot plates from shots/*.json (headed)
bun run bake dresden # offline bakes (needs uv): raw downloads → data/
bun run test:pipeline # pytest + ruff for pipeline/
Unit tests are bun test files colocated with the code they cover (lib/,
app/_components/ and scripts/); the bakes have their own pytest suite in
pipeline/tests/. The e2e specs drive the viewer through the window.__poc
hook (poc-debug.ts), which dev builds expose
automatically and production builds only with NEXT_PUBLIC_POC_DEBUG=1;
changing that hook means updating e2e/.
The code is licensed under the Apache License 2.0. The geodata
are not: data/ and everything built from it stay under the licences of
their sources — the survey offices' open-data licences (dl-de/by-2-0,
dl-de/zero-2-0, CC BY 4.0), the ODbL for everything derived from
OpenStreetMap (share-alike: a database derived from it stays under the
ODbL), CC BY 4.0 for the timetable. NOTICE has the credits; the
guide lists every source with its licence
(Licences and credits).