@aethervtt/shard-project 0.1.0 → 0.2.0

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/dist/docs.js CHANGED
@@ -747,6 +747,9 @@ whose path no longer exists is kept in the file and logged as \`prefab/stale-ove
747
747
  `,
748
748
  "add-sound.md": `# Add sound
749
749
 
750
+ For authored events, mixers, snapshots, acoustics, adaptive music and offline previews, read
751
+ [the audio guide](../audio.md).
752
+
750
753
  Add \`"audio"\` to \`plugins\` in \`shard.json\`. Drop \`.ogg\`, \`.mp3\`, \`.wav\`, or \`.flac\` files under
751
754
  \`assets/\` and run \`shard import\`: each is an \`AudioClip\` (MCP \`get_asset\` shows duration, channels,
752
755
  sample rate, codec). Files stay compressed. Clips under \`music/\` stream; set \`"mode": "stream"\` in
@@ -765,7 +768,7 @@ the \`.meta\` for other long files, and \`loopStart\`/\`loopEnd\` (seconds) for
765
768
 
766
769
  - Sources autoplay; set \`playing\` to start or stop one. A clip that doesn't loop clears
767
770
  \`playing\` at its end and sends \`audio/AudioFinished\`. Spatial sources follow their transform;
768
- the listener is the first \`audio/AudioListener\` (put it on the camera).
771
+ the fallback listener has the lowest entity ID; setAudioListener selects one explicitly.
769
772
  - \`rolloff\`: \`inverse\` (default, natural), \`linear\` (silent at \`maxDistance\`; past it the source
770
773
  goes virtual and holds no voice), \`exponential\`. \`panning\`: \`equal-power\` or \`hrtf\`.
771
774
  - One-shots from code: \`playSound(world, weapon.sound, { position: muzzle, bus: 'sfx' })\` from
@@ -1165,6 +1168,81 @@ orbit down to 0.4 m vertex spacing on the ground. Put the camera in the planet's
1165
1168
  \`describe_terrain\` shows \`detail.lodBias\` above 1 while it's coarsening.
1166
1169
  - Give it air: \`"render/Atmosphere": {}\` on the planet entity takes the planet's radius, draws
1167
1170
  the sky from the ground to orbit, and hazes distant terrain (tune-an-atmosphere.md).
1171
+ - Rocks, trees and grass: give biomes (or the planet) a scatter set (scatter-props-and-foliage.md).
1172
+ - A bounded landscape you shape (roads, valleys) rather than a whole planet: make-a-terrain.md.
1173
+ `,
1174
+ "make-a-terrain.md": `# Make a heightfield terrain (an open world)
1175
+
1176
+ A bounded landscape, 256 m to 64 km a side, shaped on purpose: a \`*.terrain.json\` file stacks
1177
+ height layers (noise, heightmap images, splines that flatten, raise or carve) and paint layers, and an
1178
+ entity with \`terrain/Terrain\` (it brings \`transform/Grid\`) draws, streams and collides with it. Add
1179
+ \`terrain\` to \`shard.json\`'s plugins (with \`render/forward\` and \`physics3d\`). The file's schema is
1180
+ \`.shard/schemas/terrain.schema.json\`; coordinates are metres from the terrain's corner.
1181
+
1182
+ \`\`\`json
1183
+ { "size": [4096, 4096], "spacing": 1, "heightRange": [-200, 800], "seed": 7,
1184
+ "splines": { "road": { "points": [[120, "ground", 300], [900, "ground", 420]], "width": 8, "falloff": 14 } },
1185
+ "height": [
1186
+ { "noise": { "path": "assets/noise/hills.noise.json" }, "scale": 260 },
1187
+ { "image": { "path": "assets/terrain/valley.height.png" }, "at": [2048, 1800], "size": [1200, 900],
1188
+ "rotation": 20, "range": [0, 140], "blend": "max", "falloff": 80 },
1189
+ { "spline": "road", "mode": "flatten" } ],
1190
+ "layers": [{ "name": "grass" }, { "name": "rock", "triplanar": true }, { "name": "gravel" }],
1191
+ "paint": [{ "layer": "grass" }, { "layer": "rock", "slope": [32, 90], "blend": 6 },
1192
+ { "layer": "gravel", "spline": "road", "blend": 2 }] }
1193
+ \`\`\`
1194
+
1195
+ - Sizes are whole numbers of roots: 64 \xD7 spacing \xD7 2^k a side, at most 64 roots (2048, 4096 or
1196
+ 16384 m at 1 m spacing). \`heightRange\` is what the bake stores in 16 bits: keep it close to the
1197
+ real range (a clipped height is \`terrain/out-of-range\`).
1198
+ - Heightmaps are 16-bit grayscale PNGs (name them \`*.height.png\`, or set \`"importer": "heightmap"\`
1199
+ in the \`.meta\`) or \`.r16\`/\`.r32\` raw files with \`width\` and \`height\` in the \`.meta\` settings.
1200
+ - \`shard import\` bakes stale terrains into \`.shard/cache/terrain\` (only the blocks an edit touches);
1201
+ \`shard terrain bake\` forces it, \`shard terrain stats\` shows size on disk and time per block.
1202
+ \`shard dev\` rebakes after you save the file.
1203
+ - Standing on it: characters, dynamic bodies, \`NavAgent\`s and \`terrain/TerrainAnchor\`s get
1204
+ heightfield colliders within \`colliderRadius\`, exactly the drawn ground. \`terrainHeightAt(world,
1205
+ terrain, x, z)\` is the ground under a point; \`await loadTerrainRegion(world, terrain, rect)\` first
1206
+ makes it exact away from anchors (spawn points). A plain \`nav/NavMesh\` paths on it.
1207
+ - Rocks and trees: \`terrain/Terrain.scatter\` takes a \`*.scatter.json\` (scatter-props-and-foliage.md).
1208
+ - Check it: MCP \`terrain_map\` (\`{ "mode": "layers" }\`, or \`"bake"\` to see which blocks an edit
1209
+ rebuilt), \`sample_terrain\` (\`{ "points": [[120, 300], [124, 300]] }\`: is the road flat across?),
1210
+ and \`describe_terrain\` (bake, pages, collider tiles). Overlays \`terrain-lod\`, \`terrain-pages\`
1211
+ and \`terrain-colliders\`.
1212
+ `,
1213
+ "scatter-props-and-foliage.md": `# Scatter rocks, trees and grass
1214
+
1215
+ Add \`scatter\` to \`shard.json\`'s plugins. A \`*.scatter.json\` (a \`scatter/ScatterSet\`) holds rules;
1216
+ put it on a biome (\`"scatter": { "path": "assets/scatter/forest.scatter.json" }\` in a \`*.biome.json\`:
1217
+ it applies where that biome dominates), on the planet (\`terrain/Planet.scatter\`: everywhere), or on
1218
+ a flat level's ground mesh (\`scatter/ScatterSurface\` next to its \`render/Mesh3d\`).
1219
+
1220
+ \`\`\`json
1221
+ { "rules": [
1222
+ { "name": "trees", "items": [{ "generator": "shard/Tree", "variants": 6 }],
1223
+ "density": 0.02, "spacing": 4, "masks": { "slope": [0, 25] }, "scale": [0.7, 1.3],
1224
+ "collider": "ball", "range": 400 },
1225
+ { "name": "grass", "kind": "foliage", "items": [{ "generator": "shard/GrassClump", "variants": 4 }],
1226
+ "density": 5, "avoid": ["trees"], "range": 50 } ] }
1227
+ \`\`\`
1228
+
1229
+ - Two kinds. \`prop\` rules spawn entities (\`scatter/Prop\`, with LODs and an optional collider) in
1230
+ chunks around cameras and \`TerrainAnchor\`s; \`foliage\` rules are GPU instances near cameras only,
1231
+ millions of them, no entities. Grass, flowers and small pebbles are foliage; anything you bump into is a prop.
1232
+ - Items are mesh generators (\`shard/Rock\`, \`shard/Tree\`, \`shard/Bush\`, \`shard/GrassClump\`,
1233
+ \`shard/Crystal\`, or a project's) with \`params\` and \`variants\` (4\u20138 shapes read as unique), or
1234
+ prefabs. Without a \`material\`, engine generators get \`scatter/Vegetation\` tinted for their kind.
1235
+ - \`density\` is per m\xB2 where the masks pass, \`spacing\` the least distance between items. Too dense
1236
+ for the spacing is \`scatter/density-too-high\` (it says the most that fits). Rule order is priority:
1237
+ \`avoid\` names earlier rules. \`align\` 0 stands items upright, 1 along the slope. \`sink\` buries
1238
+ them by a share of their height.
1239
+ - Placement is a global jittered lattice: the same seed puts the same tree in the same place on every
1240
+ visit and machine. A prop the game despawns stays gone (saved in \`scatter/Removed\`).
1241
+ - Check it: MCP \`preview_generator\` with \`{ "generator": "assets/scatter/forest.scatter.json" }\`
1242
+ shows the set on a 64 m patch from above and at eye level; \`describe_scatter\` gives per-rule
1243
+ counts; \`sample_scatter\` lists props near a point (\`{ "entity": "landing-pad", "radius": 10 }\`).
1244
+ Overlays \`scatter\` and \`foliage-chunks\` show placements in a screenshot.
1245
+ - \`scatter/Budget\` caps props spawned per frame; \`scatter/Wind\` sways foliage and leaves.
1168
1246
  `,
1169
1247
  "tune-an-atmosphere.md": `# Tune an alien sky
1170
1248
 
@@ -1264,6 +1342,27 @@ browser, and so do real frame times.
1264
1342
 
1265
1343
  With the MCP server attached to a running page: \`metrics_reset\`, exercise the scene, then
1266
1344
  \`metrics_record\` returns the same record the scripts write.
1345
+ `,
1346
+ "find-what-is-slow.md": `# Find what's slow, or what stutters
1347
+
1348
+ The profiler is always on. Systems, schedules, the frame, render-graph encodes (\`render/<node>\`),
1349
+ GPU passes (\`gpu:<node>\`), worker jobs and asset loads are spans.
1350
+
1351
+ - On average: \`describe_perf\` (or \`shard profile\`'s top spans) lists last, avg, p95 and max ms per
1352
+ span over 120 frames. \`{ "spans": ["render"] }\` keeps the spans a key covers.
1353
+ - Against a budget: \`describe_budgets\` names the machine, lists every budget with its latest p95
1354
+ (over budget first), and splits a scenario's frame into slices, each measured share next to
1355
+ its budget share. Where \`describe_perf\` says \`"overlapping": true\` (Apple and other tile GPUs),
1356
+ pass times don't add up: \`ablate_passes\` measures what each pass costs.
1357
+ - A stutter: \`capture_perf { "frames": 300 }\`, or a flight recorder that waits for one:
1358
+ \`{ "until": { "frameMs": 30 } }\`. Read \`worst\`: each slow frame lists \`over\`, the spans that ran
1359
+ longest above their median there. The first one is the lead.
1360
+ - Your own code: \`const SPAN = defineSpan('<project>/spawn')\` once, then in a system
1361
+ \`const t = profiler.begin(SPAN)\` \u2026 \`profiler.end(t)\` with \`world.resource(ProfilerResource)\`.
1362
+ - The line: \`shard profile --cpu-prof\` (or \`"sample": true\`) adds the hottest functions and
1363
+ writes a \`.cpuprofile\`. Traces go to \`.shard/captures/*.trace.json\`: open them in Perfetto.
1364
+ - In a browser, \`shard dev\`'s pages are cross-origin isolated, so timings are to 5 \xB5s. Turn on the
1365
+ \`perf\` overlay (\`debug_overlays\`) to watch frame time and the top spans live.
1267
1366
  `
1268
1367
  };
1269
1368
  function agentsBlock(manifest) {
@@ -1281,6 +1380,7 @@ shard import --json # import new and changed asset files
1281
1380
  shard mv <from> <to> # move an asset and rewrite references to it
1282
1381
  shard tiles read <map> # tilemap cells as rows; tiles edit <map> --cell x,y=name
1283
1382
  shard run --frames 600 # headless run
1383
+ shard profile --frames 600 # capture spans: the worst frames and what grew in them
1284
1384
  shard screenshot ${manifest.startScene} --out shot.png
1285
1385
  shard test --json # gameplay tests in tests/
1286
1386
  shard check --json # type-check scripts: file, line, column
@@ -1301,6 +1401,9 @@ shard mcp # MCP server for this project (see .mcp.json)
1301
1401
  - \`.agents/components.md\`: every component and field; \`.agents/assets.md\`: importers and
1302
1402
  project data types;
1303
1403
  \`.agents/errors.md\`: error codes
1404
+ - \`.agents/audio.md\`: authored sound, music, mixing and measured previews
1405
+ - \`.agents/effects.md\`: reusable effect timelines, target bindings and owned lifetimes
1406
+ - \`.agents/vfx.md\`: trails, beams, projected decals and receiver masks
1304
1407
  - \`.agents/skills/\`: recipes for common tasks
1305
1408
 
1306
1409
  Engine plugins available: ${Object.entries(BUILTIN_PLUGINS).map(([n, d]) => `\`${n}\` (${d})`).join("; ")}.
@@ -1322,8 +1425,143 @@ Project-specific notes for agents go here; \`shard docs\` keeps them.
1322
1425
  ${existing}`;
1323
1426
  return `${existing.slice(0, start)}${block}${existing.slice(end + GENERATED_END.length)}`;
1324
1427
  }
1428
+ const AUDIO_GUIDE = `# Authoring audio
1429
+
1430
+ Enable \`audio/events\` for layered events and parameters, \`audio/mixer\` for effects and snapshots,
1431
+ \`audio/acoustics\` for zones/obstruction, and \`audio/music\` for adaptive music. Their dependencies
1432
+ are enabled automatically. Base \`audio\` keeps AudioSource/playSound for simple clips.
1433
+
1434
+ Assets are ordinary JSON: \`.audio-event.json\`, \`.audio-parameters.json\`, \`.audio-collection.json\`,
1435
+ \`.audio-mixer.json\`, \`.audio-snapshot.json\` and \`.audio-music.json\`. Use their generated schemas
1436
+ under \`.shard/schemas\`. Import edits with \`shard import\`; references can use paths or stable GUIDs.
1437
+ Authored gains use dB and pitches use semitones. Legacy volume/gain use linear multipliers.
1438
+
1439
+ Prepare a collection with \`await prepareAudio(world, ref)\`, play its events with \`playAudioEvent\`,
1440
+ and release the returned lease when its scope ends. Active instances retain their own pins.
1441
+ Use local \`parameters\` for surface labels, RPM or intensity; shared parameter assets connect
1442
+ weather/environment values to events, music and mixer curves. Built-in distance/speed are readonly.
1443
+ Sequence/no-repeat selectors retain bounded session history. Layers have independent RNG streams.
1444
+
1445
+ Simulation event decisions follow timeScale and pause. Their samples normally follow audio time;
1446
+ \`pitchLinked\` opts into scaled playback. Realtime UI/ambience can continue without visual frames.
1447
+ Transient blocked events expire after 250 ms by default. \`wait\` preserves their phase; \`restart\`
1448
+ starts their timeline again after unlock. Choose an explicit AudioListener with setAudioListener;
1449
+ the fallback is the lowest live listener entity ID. There is one audio session per app/protocol target.
1450
+
1451
+ Mixer domain buses (music/sfx/ui/voice) are direct children of master. Put submixes below them.
1452
+ Each event layer can declare up to eight \`sends\` with \`target\`, \`gainDb\`, and an optional
1453
+ parameter/points curve. Declare those routes in its mixer bus first. Per-instance sends tap after
1454
+ spatialization and preserve the originating category's user gate. Event \`tags\` can drive ducking;
1455
+ only physically rendering voices trigger it by default. Duck rules also include descendant buses.
1456
+
1457
+ Parent inserts process the summed children once. Pre/post sends feed named returns, and user
1458
+ category mute gates that category's dry and wet output. Apply imported mixer edits with
1459
+ \`await configureAudioMixer(world, ref)\`: scalar edits preserve effect history; topology/IR
1460
+ changes crossfade within the overlap budget. Delay and convolution tails are finite;
1461
+ decoded PCM and generated DSP kernels have separate bounded memory budgets. Snapshots compose
1462
+ by priority and stable identity; they do not overwrite user volume settings.
1463
+ Player controls apply to master and ordinary direct-child categories; nested/return controls reject.
1464
+ Use \`setAudioUserBus(world, 'music', { volume: 0.5, muted: false })\` for player settings;
1465
+ \`setBus\` remains the independent gameplay fader. Effect target fields
1466
+ are \`effectId/parameter\` (for example \`water/frequency\`), and send fields are \`send/returnBus\`.
1467
+ Zones use spheres or world-axis boxes and at most four smoothly handed-off returns. Obstruction
1468
+ comes from an injected bounded query provider; headless tests can supply deterministic queries.
1469
+ Optional \`gainCurve\` and \`lowpassCurve\` map transmission to dB and Hz.
1470
+
1471
+ Music programs use decoded stems, fixed tempo/meter, source sample markers and a 960-PPQ grid.
1472
+ Rate conform is explicit and pitch changing, bounded by authored cents. Prepare then start a
1473
+ program, request beat/bar/marker transitions, and set intensity parameters. Current, incoming and
1474
+ crossfading sections own PCM; retired sections are released. Entire stem groups reserve voices.
1475
+ Pause/seek cancel future sources, and resume creates fresh scheduled sources at the saved phase.
1476
+ Cancelling an already audible destination pauses it and releases both sides of its crossfade.
1477
+ Replacing a stinger reserves overlap capacity first; failure preserves the existing stinger.
1478
+ Persist only intentional sustained events/music; save restoration diagnoses changed revisions.
1479
+
1480
+ Use \`audio.describe\` and \`audio.log\` (cursor plus truncation metadata), \`audio.events.inspect\`,
1481
+ \`audio.mix.inspect\`, \`audio.music.inspect\`, and \`audio.meters\`. Headless gains are modeled; meters
1482
+ must say unavailable there. Gameplay tests expose \`game.audio.describe/events/music/mix/log\`.
1483
+ CLI: \`shard audio call audio.parameter.set --attach --params '{"instance":1,"name":"rpm","value":0.8}'\`.
1484
+ Without --attach, the CLI uses a temporary headless target. MCP exposes matching audio_* tools.
1485
+
1486
+ \`shard audio preview plans/showcase.json --out captures/mix.wav --json\` runs Chromium's offline
1487
+ renderer through the same event/music/mixer implementations. The preview schema accepts clips,
1488
+ authored events/music, seed, controls, movement, duration, rate and tails. Controls evaluate at
1489
+ 60 Hz aligned upward to 128-sample render quanta; the trace includes requested/actual times.
1490
+ Music timestamps are scheduled natively. Output includes WAV, decisions, peak/RMS/clipping,
1491
+ backend and capabilities. Limits: 60 seconds including tails and 64 MiB each for input/PCM/output.
1492
+ No audio device, microphone or GPU is needed. Install Playwright Chromium in the CLI package.
1493
+ See \`examples/audio-lab\` for a complete authored showcase and original synthetic assets.
1494
+ `;
1495
+ const VFX_GUIDE = `# Trails, beams and projected decals
1496
+
1497
+ "render/forward" includes ribbonsPlugin and decalsPlugin. SDK hosts can use forwardCorePlugin
1498
+ plus just the optional plugins they need. Components are schema-authored; use render.describe
1499
+ for ribbons/decals records, points/triangles, age, allocation sizes and structured error codes.
1500
+
1501
+ Trail follows its Transform (including parent/bone attachments) and local offset. Old points
1502
+ stay in world space. capacity bounds the history, lifetime expires points, minDistance rejects
1503
+ duplicates, and breakDistance clears history after teleporting. playing=false drains the trail;
1504
+ paused=true freezes its clock. Beam uses local start/end, or end relative to its target entity;
1505
+ local points override endpoints. segments/noise/seed control deterministic endpoint-beam shape.
1506
+ Both accept width, ordered widthCurve [{at,value}], color/endColor, opacity, repeat, scroll,
1507
+ blend alpha/additive, layers and a Texture handle. Width curves use trail age or beam distance.
1508
+ Colors are display-referred unlit overlays, not PBR lighting inputs. Alpha compositing uses
1509
+ stable creation order; intersecting transparent ribbons are not sorted per segment.
1510
+
1511
+ Decal projects along local -Z, with local X/Y as UVs and size as the clipping box. Mark eligible
1512
+ rigid Mesh3d entities with DecalReceiver; its mask must overlap receiverMask. The projector clips
1513
+ real triangles and caches them until geometry/transforms/masks change. maxAngle rejects grazing
1514
+ and back-facing surfaces; offset avoids z fighting. lifetime=0 persists; fadeOut fades the end
1515
+ of a finite lifetime. paused freezes age, playing=false hides it. maxVertices and a 32768 input
1516
+ triangle budget bound projection; overflow gives a diagnostic and no partial mark. Skinned,
1517
+ morph/compute-deformed and transparent receivers are unsupported. No PBR normal/roughness decals.
1518
+
1519
+ All texture handles resolve through the host AssetServer catalog (GUIDs work). No file picker,
1520
+ URL loading or database dependency is introduced. Prefabs carry reusable VFX component settings;
1521
+ 0081 visual tracks own activation and cleanup. A beam visual track's endpoint binds another
1522
+ named sequence target. Use property tracks for opacity/width changes. Effects are presentation;
1523
+ the host remains responsible for damage, collision and persistent game state.
1524
+
1525
+ Preview: playground/vfx.html. Toggle each feature to isolate its pixels; replay a seed, pause,
1526
+ clear, and switch perspective/orthographic cameras. The host registers the original textures.
1527
+ `;
1528
+ const EFFECTS_GUIDE = `# Coordinate reusable effect sequences
1529
+
1530
+ Enable "effects" in shard.json and the plugins used by your tracks: "animation", "particles",
1531
+ "audio/events" and "render/forward" for visual tracks. The scene plugin handles owned prefab targets. Author *.effect-sequence.json
1532
+ against .shard/schemas/effect-sequence.schema.json. Hosts may supply the same JSON directly;
1533
+ all dependencies resolve through the host's AssetServer catalog, including GUID-only assets.
1534
+
1535
+ A sequence has duration, parameters, named targets and tracks. Targets with a prefab handle
1536
+ are spawned/owned; other targets are borrowed entity bindings. Property tracks interpolate
1537
+ float/vector/color component fields (from/to arrays, linear or smoothstep, optional parameter
1538
+ multiplier). Animation and particle tracks require owned targets. Audio tracks use authored
1539
+ AudioEvent assets. Visual tracks require owned targets and name render/Trail, render/Beam or
1540
+ render/Decal in component. They activate playing, pause their clocks, and stop at track end;
1541
+ trails drain their remaining history. Beam tracks may bind an endpoint by target name.
1542
+ Marker tracks record presentation milestones; gameplay applies damage itself.
1543
+
1544
+ Use prepareEffect(world, source), then playEffect(world, preparation, { bindings, parameters,
1545
+ seed, parent, ownerEntity }). The optional parent anchors owned targets in a local frame, so
1546
+ one prepared spell can play at different positions/orientations. Removing the parent cancels
1547
+ the effect unless another ownerEntity was supplied. Release the preparation when no more plays are needed: active instances
1548
+ retain it. cancelEffect restores borrowed fields, stops audio and removes owned prefab trees.
1549
+ pauseEffect pauses only that instance. Keep claimed fields free of other writers while playing.
1550
+ No runtime file picker, URL fetcher or persistence backend is assumed.
1551
+
1552
+ Protocol/MCP: effects.prepare, effects.play, effects.pause, effects.cancel, effects.release,
1553
+ effects.inspect (trace cursor, active instances and preparation references). The inspect trace
1554
+ reports late windows skipped after a large frame step. A missing target fails and cleans up the
1555
+ whole instance. Calls return structured effects/* errors. Dispose the app to release all plans.
1556
+
1557
+ Preview at playground/effects.html; its spell asset and host catalog adapter demonstrate the
1558
+ complete flow. Replay by cancelling and playing again; backward scrubbing is not implemented.
1559
+ `;
1325
1560
  function generateDocs(manifest, errors) {
1326
1561
  const files = {
1562
+ ".agents/audio.md": AUDIO_GUIDE,
1563
+ ".agents/effects.md": EFFECTS_GUIDE,
1564
+ ".agents/vfx.md": VFX_GUIDE,
1327
1565
  ".agents/components.md": renderComponentCatalog(),
1328
1566
  ".agents/errors.md": renderErrorCatalog(errors),
1329
1567
  ".agents/generators.md": renderGeneratorCatalog(),
package/dist/host.js CHANGED
@@ -1,7 +1,14 @@
1
1
  import { animationPlugin } from "@aethervtt/shard-animation";
2
- import { audioPlugin } from "@aethervtt/shard-audio";
2
+ import {
3
+ audioAcousticsPlugin,
4
+ audioEventsPlugin,
5
+ audioMixerPlugin,
6
+ audioMusicPlugin,
7
+ audioPlugin
8
+ } from "@aethervtt/shard-audio";
3
9
  import { controlsPlugin } from "@aethervtt/shard-controls";
4
10
  import { dicePlugin } from "@aethervtt/shard-dice";
11
+ import { effectsPlugin } from "@aethervtt/shard-effects";
5
12
  import { gltfPlugin } from "@aethervtt/shard-gltf";
6
13
  import { gesturesPlugin, inputPlugin } from "@aethervtt/shard-input";
7
14
  import { loadNavCache, Nav, navGridPlugin, navPlugin } from "@aethervtt/shard-nav";
@@ -13,6 +20,7 @@ import { forwardPlugin, renderPlugin, Shaders } from "@aethervtt/shard-render";
13
20
  import { materialNoisePlugin } from "@aethervtt/shard-render/noise";
14
21
  import { App, LogResource } from "@aethervtt/shard-runtime";
15
22
  import { savePlugin } from "@aethervtt/shard-save";
23
+ import { scatterPlugin } from "@aethervtt/shard-scatter";
16
24
  import { loadScene, ScenePlugin } from "@aethervtt/shard-scene";
17
25
  import { spritePlugin } from "@aethervtt/shard-sprite";
18
26
  import { terrainPlugin } from "@aethervtt/shard-terrain";
@@ -28,12 +36,15 @@ function buildApp(options) {
28
36
  names.add("gestures");
29
37
  names.add("render/forward");
30
38
  }
39
+ if (names.has("audio/acoustics")) names.add("audio/mixer");
40
+ if (names.has("audio/music") || names.has("audio/mixer")) names.add("audio/events");
41
+ if (names.has("audio/events")) names.add("audio");
31
42
  if (names.has("gestures")) names.add("input");
32
43
  if (names.has("render/forward")) {
33
44
  names.add("render");
34
45
  names.add("core/transform");
35
46
  }
36
- if (names.has("physics3d") || names.has("physics2d") || names.has("audio") || names.has("ui") || names.has("nav") || names.has("nav/grid") || names.has("terrain"))
47
+ if (names.has("physics3d") || names.has("physics2d") || names.has("audio") || names.has("ui") || names.has("nav") || names.has("nav/grid") || names.has("terrain") || names.has("scatter"))
37
48
  names.add("core/transform");
38
49
  const app = new App({ seed: manifest.seed });
39
50
  app.addPlugin(gltfPlugin);
@@ -63,12 +74,19 @@ function buildApp(options) {
63
74
  if (names.has("gestures")) app.addPlugin(gesturesPlugin);
64
75
  if (names.has("controls")) app.addPlugin(controlsPlugin);
65
76
  if (names.has("audio")) app.addPlugin(audioPlugin({ backend: options.audio }));
77
+ if (names.has("audio/events")) app.addPlugin(audioEventsPlugin());
78
+ if (names.has("audio/mixer")) app.addPlugin(audioMixerPlugin());
79
+ if (names.has("audio/acoustics")) app.addPlugin(audioAcousticsPlugin());
80
+ if (names.has("audio/music")) app.addPlugin(audioMusicPlugin());
66
81
  if (names.has("ui")) app.addPlugin(uiPlugin);
67
82
  if (names.has("nav")) app.addPlugin(navPlugin);
68
83
  else if (names.has("nav/grid")) app.addPlugin(navGridPlugin);
69
- if (names.has("terrain")) app.addPlugin(terrainPlugin({ workers: options.platform?.workers }));
84
+ if (names.has("terrain"))
85
+ app.addPlugin(terrainPlugin({ workers: options.platform?.workers, fs: options.platform?.fs }));
86
+ if (names.has("scatter")) app.addPlugin(scatterPlugin({ workers: options.platform?.workers }));
70
87
  app.addPlugin(noisePlugin);
71
88
  app.addPlugin(ScenePlugin);
89
+ if (names.has("effects")) app.addPlugin(effectsPlugin);
72
90
  app.addPlugin(
73
91
  procgenPlugin({
74
92
  fs: options.platform?.fs,
@@ -28,6 +28,7 @@ export type ManifestValue = Infer<typeof Manifest>;
28
28
  export declare function validateManifest(json: unknown): ShardError[];
29
29
  /** Built-in engine plugins a manifest can name, with what they bring. */
30
30
  export declare const BUILTIN_PLUGINS: {
31
+ readonly effects: 'Prepared effect sequences: coordinated properties, animation, particles, audio and markers; add the feature plugins used by tracks';
31
32
  readonly render: 'GPU device, render graph, views, shaders';
32
33
  readonly 'render/forward': 'cameras, meshes, standard material, lights (includes render and core/transform)';
33
34
  readonly sprite: 'sprites, atlases, frame animation, tilemaps (includes render/forward)';
@@ -40,10 +41,15 @@ export declare const BUILTIN_PLUGINS: {
40
41
  readonly gestures: 'taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)';
41
42
  readonly controls: 'OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)';
42
43
  readonly audio: 'AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)';
44
+ readonly 'audio/events': 'Authored layered events, parameters, collections and prepared voice lifetimes (includes audio)';
45
+ readonly 'audio/mixer': 'Hierarchical effects, sends and composable snapshots (includes audio/events)';
46
+ readonly 'audio/acoustics': 'Listener reverb zones and bounded injected obstruction queries (includes audio/mixer)';
47
+ readonly 'audio/music': 'Prepared stems, beat/bar/marker transitions and stingers (includes audio/events)';
43
48
  readonly ui: 'UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)';
44
49
  readonly nav: 'navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)';
45
50
  readonly 'nav/grid': 'navigation on NavGrids only, without loading the Recast WASM (includes core/transform)';
46
51
  readonly terrain: 'planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)';
52
+ readonly scatter: 'props and foliage from ScatterSet rules (*.scatter.json) on planets (Planet.scatter, Biome.scatter) and ScatterSurface meshes: props as entities around cameras and anchors, GPU foliage near cameras; scatter.* methods; the engine generators shard/Rock, Tree, Bush, GrassClump and Crystal make their meshes (includes core/transform; add terrain for planets, render/forward to draw)';
47
53
  readonly dice: 'the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)';
48
54
  readonly 'core/transform': 'Transform and hierarchy propagation';
49
55
  };
package/dist/manifest.js CHANGED
@@ -50,7 +50,7 @@ const Manifest = defineSchema(
50
50
  ),
51
51
  plugins: t.list(t.string, {
52
52
  default: ["render/forward", "input"],
53
- description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, gestures, controls, audio, ui, nav, nav/grid, dice, core/transform."
53
+ description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, gestures, controls, audio, ui, nav, nav/grid, terrain, scatter, dice, core/transform."
54
54
  })
55
55
  },
56
56
  { description: "shard.json: the project manifest." }
@@ -89,6 +89,7 @@ function validateManifest(json) {
89
89
  return errors;
90
90
  }
91
91
  const BUILTIN_PLUGINS = {
92
+ effects: "Prepared effect sequences: coordinated properties, animation, particles, audio and markers; add the feature plugins used by tracks",
92
93
  render: "GPU device, render graph, views, shaders",
93
94
  "render/forward": "cameras, meshes, standard material, lights (includes render and core/transform)",
94
95
  sprite: "sprites, atlases, frame animation, tilemaps (includes render/forward)",
@@ -101,10 +102,15 @@ const BUILTIN_PLUGINS = {
101
102
  gestures: "taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)",
102
103
  controls: "OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)",
103
104
  audio: "AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)",
105
+ "audio/events": "Authored layered events, parameters, collections and prepared voice lifetimes (includes audio)",
106
+ "audio/mixer": "Hierarchical effects, sends and composable snapshots (includes audio/events)",
107
+ "audio/acoustics": "Listener reverb zones and bounded injected obstruction queries (includes audio/mixer)",
108
+ "audio/music": "Prepared stems, beat/bar/marker transitions and stingers (includes audio/events)",
104
109
  ui: "UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)",
105
110
  nav: "navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)",
106
111
  "nav/grid": "navigation on NavGrids only, without loading the Recast WASM (includes core/transform)",
107
112
  terrain: "planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)",
113
+ scatter: "props and foliage from ScatterSet rules (*.scatter.json) on planets (Planet.scatter, Biome.scatter) and ScatterSurface meshes: props as entities around cameras and anchors, GPU foliage near cameras; scatter.* methods; the engine generators shard/Rock, Tree, Bush, GrassClump and Crystal make their meshes (includes core/transform; add terrain for planets, render/forward to draw)",
108
114
  dice: "the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)",
109
115
  "core/transform": "Transform and hierarchy propagation"
110
116
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aethervtt/shard-project",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -8,6 +8,10 @@
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
10
  "default": "./dist/index.js"
11
+ },
12
+ "./manifest": {
13
+ "types": "./dist/manifest.d.ts",
14
+ "default": "./dist/manifest.js"
11
15
  }
12
16
  },
13
17
  "files": [
@@ -16,33 +20,35 @@
16
20
  "README.md"
17
21
  ],
18
22
  "dependencies": {
19
- "@aethervtt/shard-animation": "0.1.0",
20
- "@aethervtt/shard-assets": "0.1.0",
21
- "@aethervtt/shard-audio": "0.1.0",
22
- "@aethervtt/shard-controls": "0.1.0",
23
- "@aethervtt/shard-core": "0.1.0",
24
- "@aethervtt/shard-dice": "0.1.0",
25
- "@aethervtt/shard-fog": "0.1.0",
26
- "@aethervtt/shard-gltf": "0.1.0",
27
- "@aethervtt/shard-gpu": "0.1.0",
28
- "@aethervtt/shard-input": "0.1.0",
29
- "@aethervtt/shard-nav": "0.1.0",
30
- "@aethervtt/shard-noise": "0.1.0",
31
- "@aethervtt/shard-particles": "0.1.0",
32
- "@aethervtt/shard-physics": "0.1.0",
33
- "@aethervtt/shard-platform": "0.1.0",
34
- "@aethervtt/shard-procgen": "0.1.0",
35
- "@aethervtt/shard-protocol": "0.1.0",
36
- "@aethervtt/shard-render": "0.1.0",
37
- "@aethervtt/shard-runtime": "0.1.0",
38
- "@aethervtt/shard-save": "0.1.0",
39
- "@aethervtt/shard-scene": "0.1.0",
40
- "@aethervtt/shard-sprite": "0.1.0",
41
- "@aethervtt/shard-terrain": "0.1.0",
42
- "@aethervtt/shard-text": "0.1.0",
43
- "@aethervtt/shard-transform": "0.1.0",
44
- "@aethervtt/shard-ui": "0.1.0",
45
- "@aethervtt/shard-verify": "0.1.0"
23
+ "@aethervtt/shard-animation": "0.2.0",
24
+ "@aethervtt/shard-assets": "0.2.0",
25
+ "@aethervtt/shard-audio": "0.2.0",
26
+ "@aethervtt/shard-controls": "0.2.0",
27
+ "@aethervtt/shard-core": "0.2.0",
28
+ "@aethervtt/shard-dice": "0.2.0",
29
+ "@aethervtt/shard-fog": "0.2.0",
30
+ "@aethervtt/shard-gltf": "0.2.0",
31
+ "@aethervtt/shard-gpu": "0.2.0",
32
+ "@aethervtt/shard-input": "0.2.0",
33
+ "@aethervtt/shard-nav": "0.2.0",
34
+ "@aethervtt/shard-noise": "0.2.0",
35
+ "@aethervtt/shard-particles": "0.2.0",
36
+ "@aethervtt/shard-physics": "0.2.0",
37
+ "@aethervtt/shard-platform": "0.2.0",
38
+ "@aethervtt/shard-procgen": "0.2.0",
39
+ "@aethervtt/shard-protocol": "0.2.0",
40
+ "@aethervtt/shard-render": "0.2.0",
41
+ "@aethervtt/shard-runtime": "0.2.0",
42
+ "@aethervtt/shard-save": "0.2.0",
43
+ "@aethervtt/shard-scatter": "0.2.0",
44
+ "@aethervtt/shard-scene": "0.2.0",
45
+ "@aethervtt/shard-sprite": "0.2.0",
46
+ "@aethervtt/shard-terrain": "0.2.0",
47
+ "@aethervtt/shard-text": "0.2.0",
48
+ "@aethervtt/shard-transform": "0.2.0",
49
+ "@aethervtt/shard-ui": "0.2.0",
50
+ "@aethervtt/shard-verify": "0.2.0",
51
+ "@aethervtt/shard-effects": "0.2.0"
46
52
  },
47
53
  "repository": {
48
54
  "type": "git",
@@ -54,6 +60,6 @@
54
60
  "provenance": true
55
61
  },
56
62
  "shard": {
57
- "buildSha": "8e14c9896f82a3554b156f1ffdd68f2cc3e7d78a"
63
+ "buildSha": "32c22a3944ed23f5d1baa90bf7cc9fae724f5fd8"
58
64
  }
59
65
  }