@plasius/gpu-world-generator 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -81,6 +81,30 @@ console.log(bake.jobs.find((job) => job.key === "assetSerialize"));
81
81
  debug allocation tags for integration with `@plasius/gpu-performance` and
82
82
  `@plasius/gpu-debug`.
83
83
 
84
+ ## Render Representation Plans
85
+
86
+ `@plasius/gpu-world-generator` now also publishes explicit chunk
87
+ representation-tier plans so renderer and worker packages can coordinate near,
88
+ mid, far, and horizon outputs without guessing from distance alone.
89
+
90
+ ```js
91
+ import { createWorldGeneratorRepresentationPlan } from "@plasius/gpu-world-generator";
92
+
93
+ const plan = createWorldGeneratorRepresentationPlan({
94
+ chunkId: "hex-12-9",
95
+ profile: "streaming",
96
+ gameplayImportance: "critical",
97
+ });
98
+
99
+ console.log(plan.bands);
100
+ console.log(plan.representations.find((entry) => entry.output === "rtProxy"));
101
+ ```
102
+
103
+ Each plan exposes raster-facing and RT-facing outputs separately, plus refresh
104
+ cadence, shadow relevance, chunk-identity preservation, and scheduling metadata
105
+ that downstream renderer and worker packages can prioritize by band and
106
+ importance.
107
+
84
108
  ## Demo
85
109
  The WebGPU mixed-forest demo lives in `demo/`. Run it with:
86
110
 
package/dist/index.cjs CHANGED
@@ -57,6 +57,7 @@ __export(index_exports, {
57
57
  createFractalPrepassRunner: () => createFractalPrepassRunner,
58
58
  createMeshBuilder: () => createMeshBuilder,
59
59
  createPerfMonitor: () => createPerfMonitor,
60
+ createWorldGeneratorRepresentationPlan: () => createWorldGeneratorRepresentationPlan,
60
61
  defaultFieldParams: () => defaultFieldParams,
61
62
  defaultFractalMandelSettings: () => defaultFractalMandelSettings,
62
63
  defaultWorldGeneratorWorkerProfile: () => defaultWorldGeneratorWorkerProfile,
@@ -94,6 +95,8 @@ __export(index_exports, {
94
95
  unpackTerrain: () => unpackTerrain,
95
96
  validateTileAssetPayload: () => validateTileAssetPayload,
96
97
  worldGeneratorDebugOwner: () => worldGeneratorDebugOwner,
98
+ worldGeneratorRepresentationBands: () => worldGeneratorRepresentationBands,
99
+ worldGeneratorRepresentationOutputs: () => worldGeneratorRepresentationOutputs,
97
100
  worldGeneratorWorkerManifests: () => worldGeneratorWorkerManifests,
98
101
  worldGeneratorWorkerProfileNames: () => worldGeneratorWorkerProfileNames,
99
102
  worldGeneratorWorkerProfiles: () => worldGeneratorWorkerProfiles,
@@ -1404,7 +1407,6 @@ function serializeTileAssetBinary(asset) {
1404
1407
  view.setUint32(cursor, materialStride >>> 0, true);
1405
1408
  cursor += 4;
1406
1409
  view.setUint32(cursor, featureStride >>> 0, true);
1407
- cursor += 4;
1408
1410
  let writeOffset = HEADER_BYTES;
1409
1411
  new Float32Array(buffer, writeOffset, heightCount).set(asset.height);
1410
1412
  writeOffset += heightBytes;
@@ -1469,7 +1471,6 @@ function parseTileAssetBinary(input) {
1469
1471
  const materialStride = view.getUint32(cursor, true);
1470
1472
  cursor += 4;
1471
1473
  const featureStride = view.getUint32(cursor, true);
1472
- cursor += 4;
1473
1474
  const heightBytes = heightCount * 4;
1474
1475
  const fieldBytes = fieldCount * 4;
1475
1476
  const materialBytes = materialCount;
@@ -1890,6 +1891,64 @@ function createMeshBuilder(sizeOrOptions = 1) {
1890
1891
  var worldGeneratorDebugOwner = "world-generator";
1891
1892
  var worldGeneratorWorkerQueueClass = "voxel";
1892
1893
  var defaultWorldGeneratorWorkerProfile = "streaming";
1894
+ var worldGeneratorRepresentationBands = Object.freeze([
1895
+ "near",
1896
+ "mid",
1897
+ "far",
1898
+ "horizon"
1899
+ ]);
1900
+ var worldGeneratorRepresentationOutputs = Object.freeze([
1901
+ "liveGeometry",
1902
+ "simplifiedGeometry",
1903
+ "rtProxy",
1904
+ "mergedProxy",
1905
+ "horizonShell"
1906
+ ]);
1907
+ var worldGeneratorRepresentationBandPriorityHints = Object.freeze({
1908
+ near: 400,
1909
+ mid: 300,
1910
+ far: 200,
1911
+ horizon: 100
1912
+ });
1913
+ function assertWorldGeneratorIdentifier(name, value) {
1914
+ if (typeof value !== "string" || value.trim().length === 0) {
1915
+ throw new Error(`${name} must be a non-empty string.`);
1916
+ }
1917
+ return value.trim();
1918
+ }
1919
+ function normalizeWorldGeneratorImportance(name, value) {
1920
+ if (value === "medium" || value === "high" || value === "critical") {
1921
+ return value;
1922
+ }
1923
+ throw new Error(`${name} must be one of: medium, high, critical.`);
1924
+ }
1925
+ function buildWorldGeneratorRepresentationDescriptor(options) {
1926
+ return Object.freeze({
1927
+ id: `${options.chunkId}.${options.band}.${options.output}`,
1928
+ chunkId: options.chunkId,
1929
+ profile: options.profile,
1930
+ band: options.band,
1931
+ output: options.output,
1932
+ rasterMode: options.rasterMode,
1933
+ rtParticipation: options.rtParticipation,
1934
+ shadowRelevance: options.shadowRelevance,
1935
+ refreshCadence: Object.freeze({
1936
+ kind: options.refreshCadence.kind,
1937
+ divisor: options.refreshCadence.divisor
1938
+ }),
1939
+ preservesChunkIdentity: options.preservesChunkIdentity,
1940
+ sourceChunkIds: Object.freeze([options.chunkId]),
1941
+ sourceJobKeys: Object.freeze([...options.sourceJobKeys]),
1942
+ suggestedAllocationIds: Object.freeze([...options.suggestedAllocationIds]),
1943
+ scheduling: Object.freeze({
1944
+ owner: "renderer",
1945
+ queueClass: worldGeneratorWorkerQueueClass,
1946
+ priorityHint: worldGeneratorRepresentationBandPriorityHints[options.band],
1947
+ gameplayImportance: options.gameplayImportance,
1948
+ representationBand: options.band
1949
+ })
1950
+ });
1951
+ }
1893
1952
  function buildBudgetLevels(jobType, queueClass, levels) {
1894
1953
  return Object.freeze(
1895
1954
  levels.map(
@@ -2470,6 +2529,108 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
2470
2529
  }
2471
2530
  return manifest;
2472
2531
  }
2532
+ function createWorldGeneratorRepresentationPlan(options) {
2533
+ const profile = options.profile ?? defaultWorldGeneratorWorkerProfile;
2534
+ const spec = worldGeneratorWorkerProfileSpecs[profile];
2535
+ if (!spec) {
2536
+ const available = worldGeneratorWorkerProfileNames.join(", ");
2537
+ throw new Error(
2538
+ `Unknown world-generator worker profile "${profile}". Available: ${available}.`
2539
+ );
2540
+ }
2541
+ const chunkId = assertWorldGeneratorIdentifier("chunkId", options.chunkId);
2542
+ const gameplayImportance = normalizeWorldGeneratorImportance(
2543
+ "gameplayImportance",
2544
+ options.gameplayImportance ?? "high"
2545
+ );
2546
+ const highValueAllocations = spec.suggestedAllocationIds;
2547
+ const farFieldAllocations = spec.suggestedAllocationIds.filter(
2548
+ (allocationId) => allocationId.includes("mesh") || allocationId.includes("asset") || allocationId.includes("tile")
2549
+ );
2550
+ const bakeSourceJobKeys = profile === "bake" ? ["meshBuild", "tileBake", "assetSerialize"] : ["meshBuild", "tileBake"];
2551
+ const representations = Object.freeze([
2552
+ buildWorldGeneratorRepresentationDescriptor({
2553
+ profile,
2554
+ chunkId,
2555
+ band: "near",
2556
+ output: "liveGeometry",
2557
+ rasterMode: "full-live",
2558
+ rtParticipation: "full",
2559
+ shadowRelevance: "ray-traced-primary",
2560
+ refreshCadence: { kind: "per-frame", divisor: 1 },
2561
+ preservesChunkIdentity: true,
2562
+ sourceJobKeys: ["meshBuild"],
2563
+ gameplayImportance: gameplayImportance === "medium" ? "high" : gameplayImportance,
2564
+ suggestedAllocationIds: highValueAllocations
2565
+ }),
2566
+ buildWorldGeneratorRepresentationDescriptor({
2567
+ profile,
2568
+ chunkId,
2569
+ band: "mid",
2570
+ output: "simplifiedGeometry",
2571
+ rasterMode: "simplified-live",
2572
+ rtParticipation: "selective",
2573
+ shadowRelevance: "selective-raster",
2574
+ refreshCadence: { kind: "interval", divisor: 2 },
2575
+ preservesChunkIdentity: true,
2576
+ sourceJobKeys: ["meshBuild"],
2577
+ gameplayImportance,
2578
+ suggestedAllocationIds: highValueAllocations
2579
+ }),
2580
+ buildWorldGeneratorRepresentationDescriptor({
2581
+ profile,
2582
+ chunkId,
2583
+ band: "mid",
2584
+ output: "rtProxy",
2585
+ rasterMode: "not-rendered",
2586
+ rtParticipation: "proxy",
2587
+ shadowRelevance: "selective-raster",
2588
+ refreshCadence: { kind: "interval", divisor: 2 },
2589
+ preservesChunkIdentity: true,
2590
+ sourceJobKeys: ["meshBuild", "tileBake"],
2591
+ gameplayImportance,
2592
+ suggestedAllocationIds: highValueAllocations
2593
+ }),
2594
+ buildWorldGeneratorRepresentationDescriptor({
2595
+ profile,
2596
+ chunkId,
2597
+ band: "far",
2598
+ output: "mergedProxy",
2599
+ rasterMode: "proxy",
2600
+ rtParticipation: "proxy",
2601
+ shadowRelevance: "proxy-caster",
2602
+ refreshCadence: { kind: "interval", divisor: 8 },
2603
+ preservesChunkIdentity: true,
2604
+ sourceJobKeys: bakeSourceJobKeys,
2605
+ gameplayImportance: "medium",
2606
+ suggestedAllocationIds: farFieldAllocations.length > 0 ? farFieldAllocations : highValueAllocations
2607
+ }),
2608
+ buildWorldGeneratorRepresentationDescriptor({
2609
+ profile,
2610
+ chunkId,
2611
+ band: "horizon",
2612
+ output: "horizonShell",
2613
+ rasterMode: "horizon-shell",
2614
+ rtParticipation: "disabled",
2615
+ shadowRelevance: "baked-impression",
2616
+ refreshCadence: { kind: "interval", divisor: 60 },
2617
+ preservesChunkIdentity: true,
2618
+ sourceJobKeys: profile === "bake" ? ["tileBake", "assetSerialize"] : ["tileBake"],
2619
+ gameplayImportance: "medium",
2620
+ suggestedAllocationIds: farFieldAllocations.length > 0 ? farFieldAllocations : highValueAllocations
2621
+ })
2622
+ ]);
2623
+ return Object.freeze({
2624
+ schemaVersion: 1,
2625
+ owner: worldGeneratorDebugOwner,
2626
+ profile,
2627
+ chunkId,
2628
+ representations,
2629
+ bands: Object.freeze(
2630
+ [...new Set(representations.map((representation) => representation.band))]
2631
+ )
2632
+ });
2633
+ }
2473
2634
  // Annotate the CommonJS export names for ESM import in node:
2474
2635
  0 && (module.exports = {
2475
2636
  DEFAULT_TILE_SIZE_WORLD,
@@ -2498,6 +2659,7 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
2498
2659
  createFractalPrepassRunner,
2499
2660
  createMeshBuilder,
2500
2661
  createPerfMonitor,
2662
+ createWorldGeneratorRepresentationPlan,
2501
2663
  defaultFieldParams,
2502
2664
  defaultFractalMandelSettings,
2503
2665
  defaultWorldGeneratorWorkerProfile,
@@ -2535,6 +2697,8 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
2535
2697
  unpackTerrain,
2536
2698
  validateTileAssetPayload,
2537
2699
  worldGeneratorDebugOwner,
2700
+ worldGeneratorRepresentationBands,
2701
+ worldGeneratorRepresentationOutputs,
2538
2702
  worldGeneratorWorkerManifests,
2539
2703
  worldGeneratorWorkerProfileNames,
2540
2704
  worldGeneratorWorkerProfiles,