@aethervtt/shard-structure 0.0.0-stage → 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/planar.js ADDED
@@ -0,0 +1,105 @@
1
+ import { CURVE_TOLERANCE, pointAt, sampleWall } from "./curve.js";
2
+ function lightChannel(segment) {
3
+ return segment.light ?? segment.sight;
4
+ }
5
+ function setLight(light) {
6
+ return light === "normal" || light === "none" ? light : void 0;
7
+ }
8
+ function planarBarriers(walls, openings, options = {}) {
9
+ const tolerance = options.tolerance ?? CURVE_TOLERANCE;
10
+ const byWall = /* @__PURE__ */ new Map();
11
+ for (const opening of openings) {
12
+ const list = byWall.get(opening.wall);
13
+ if (list) list.push(opening);
14
+ else byWall.set(opening.wall, [opening]);
15
+ }
16
+ const out = [];
17
+ const only = options.level;
18
+ for (const wall of walls) {
19
+ const level = wall.level ?? null;
20
+ if (only !== void 0 && level !== only) continue;
21
+ const tagged = only !== void 0 || wall.level !== void 0;
22
+ const curved = wall.shape === "arc" || wall.shape === "bezier";
23
+ const line = curved ? sampleWall(wall, tolerance) : void 0;
24
+ const dx = wall.b[0] - wall.a[0];
25
+ const dy = wall.b[1] - wall.a[1];
26
+ const length = line ? line.length : Math.sqrt(dx * dx + dy * dy);
27
+ if (length < 1e-6) continue;
28
+ const wallLight = setLight(wall.light);
29
+ const cut = (from, to, id, sight, movement, light, openingId) => {
30
+ const pieces = line ? curvedSpan(line, from, to, id, wall.id, sight, movement) : [span(wall, length, from, to, id, sight, movement)];
31
+ for (const piece of pieces) {
32
+ if (openingId !== void 0) piece.openingId = openingId;
33
+ if (light !== void 0) piece.light = light;
34
+ if (tagged) piece.level = level;
35
+ out.push(piece);
36
+ }
37
+ };
38
+ const list = (byWall.get(wall.id) ?? []).slice();
39
+ list.sort((p, q) => p.offset - q.offset || (p.id < q.id ? -1 : p.id > q.id ? 1 : 0));
40
+ let cursor = 0;
41
+ for (const opening of list) {
42
+ if (opening.offset > cursor)
43
+ cut(cursor, opening.offset, `struct:${wall.id}:${cursor}`, "normal", "normal", wallLight);
44
+ const open = opening.kind === "door" && opening.state === "open";
45
+ const light = setLight(opening.light);
46
+ cut(
47
+ opening.offset,
48
+ opening.offset + opening.width,
49
+ `struct-opening:${opening.id}`,
50
+ open ? "none" : opening.sight,
51
+ open ? "none" : opening.movement,
52
+ open && light !== void 0 ? "none" : light,
53
+ opening.id
54
+ );
55
+ cursor = opening.offset + opening.width;
56
+ }
57
+ if (cursor < length)
58
+ cut(cursor, length, `struct:${wall.id}:${cursor}`, "normal", "normal", wallLight);
59
+ }
60
+ return out;
61
+ }
62
+ function curvedSpan(line, from, to, id, wall, sight, movement) {
63
+ const at = new Float64Array(4);
64
+ const points = [];
65
+ const push = (s) => {
66
+ pointAt(line, s, at);
67
+ points.push([at[0], at[1]]);
68
+ };
69
+ push(from);
70
+ for (let i = 1; i < line.count - 1; i++) {
71
+ const s = line.s[i];
72
+ if (s > from + 1e-9 && s < to - 1e-9) push(s);
73
+ }
74
+ push(to);
75
+ const out = [];
76
+ for (let k = 0; k + 1 < points.length; k++) {
77
+ out.push({
78
+ id: k === 0 ? id : `${id}~${k}`,
79
+ sourceWallId: wall,
80
+ a: points[k],
81
+ b: points[k + 1],
82
+ sight,
83
+ movement
84
+ });
85
+ }
86
+ return out;
87
+ }
88
+ function span(wall, length, from, to, id, sight, movement) {
89
+ const ax = wall.a[0];
90
+ const ay = wall.a[1];
91
+ const dx = (wall.b[0] - ax) / length;
92
+ const dy = (wall.b[1] - ay) / length;
93
+ return {
94
+ id,
95
+ sourceWallId: wall.id,
96
+ a: [ax + dx * from, ay + dy * from],
97
+ b: [ax + dx * to, ay + dy * to],
98
+ sight,
99
+ movement
100
+ };
101
+ }
102
+ export {
103
+ lightChannel,
104
+ planarBarriers
105
+ };
@@ -0,0 +1,10 @@
1
+ import { type AppMethod } from '@aethervtt/shard-runtime';
2
+ export declare const structureMethods: AppMethod[];
3
+ /**
4
+ * Walls, openings and floors (0055): compiled into chunked, per-material meshes that rebuild only
5
+ * where an edit lands, door leaves that swing without rebuilding anything, and window panes. Levels,
6
+ * roofs and cutouts (0067): each level and roof is a group whose meshes are its children, so hiding
7
+ * one is a Visibility write. Contact shade (0068): noisy dark strips where walls meet floors and
8
+ * each other, one blended mesh per (group, chunk). Needs `forwardPlugin` (render/forward).
9
+ */
10
+ export declare const structurePlugin: import("@aethervtt/shard-runtime").Plugin;
package/dist/plugin.js ADDED
@@ -0,0 +1,119 @@
1
+ import { Derived, defineSchema, PostUpdate } from "@aethervtt/shard-core";
2
+ import { box } from "@aethervtt/shard-mesh";
3
+ import { MaterialAsset, Materials, Meshes, registerShaders, Shaders } from "@aethervtt/shard-render";
4
+ import { definePlugin } from "@aethervtt/shard-runtime";
5
+ import { Transform, TransformSystems } from "@aethervtt/shard-transform";
6
+ import {
7
+ compileStructure,
8
+ FRAME_KEY,
9
+ observeRemovals,
10
+ Structure,
11
+ StructureState,
12
+ swingDoors
13
+ } from "./compile.js";
14
+ import {
15
+ ContactMesh,
16
+ Cutout,
17
+ DoorLeaf,
18
+ Floor,
19
+ Level,
20
+ Opening,
21
+ Roof,
22
+ StructureChunk,
23
+ StructureSettings,
24
+ Wall,
25
+ WindowPane
26
+ } from "./components.js";
27
+ import { CONTACT_KEY, CONTACT_SHADERS, ContactShade } from "./contact-shade.js";
28
+ const structureMethods = [
29
+ {
30
+ name: "structure.describe",
31
+ description: "Structure: wall, opening, floor, roof, cutout, level and chunk counts, the groups (ground, levels, roofs: their chunks, meshes and whether hidden), the chunk grid, pieces per chunk, and the last compile (its dirty chunks and time).",
32
+ params: defineSchema("structure/DescribeParams", {}),
33
+ handler: ({ world }) => world.resource(Structure).describe()
34
+ }
35
+ ];
36
+ function leafBox() {
37
+ const mesh = box();
38
+ const data = mesh.data();
39
+ const p = data.positions;
40
+ for (let i = 0; i < p.length; i += 3) {
41
+ p[i] = p[i] + 0.5;
42
+ p[i + 1] = p[i + 1] + 0.5;
43
+ }
44
+ mesh.update(data);
45
+ return mesh;
46
+ }
47
+ const structurePlugin = definePlugin({
48
+ name: "structure",
49
+ dependencies: ["render/forward", "core/transform"],
50
+ provides: [
51
+ Wall,
52
+ Opening,
53
+ Floor,
54
+ Level,
55
+ Roof,
56
+ Cutout,
57
+ StructureChunk,
58
+ DoorLeaf,
59
+ WindowPane,
60
+ ContactMesh,
61
+ ContactShade,
62
+ StructureSettings
63
+ ],
64
+ build(app) {
65
+ const world = app.world;
66
+ world.initResource(StructureSettings);
67
+ const state = new StructureState(world);
68
+ world.insertResource(Structure, state);
69
+ state.ground = world.spawn(Transform, Derived);
70
+ state.groupSlot(state.ground);
71
+ observeRemovals(world, state);
72
+ app.addSystems(PostUpdate, swingDoors.after(compileStructure).before(TransformSystems));
73
+ app.addSystems(PostUpdate, compileStructure.before(TransformSystems));
74
+ app.addMethod(...structureMethods);
75
+ },
76
+ ready(app) {
77
+ const world = app.world;
78
+ const state = world.resource(Structure);
79
+ const materials = world.resource(Materials);
80
+ state.defaultMaterial = materials.add(
81
+ new MaterialAsset({ baseColor: [0.62, 0.6, 0.57, 1], roughness: 0.85 }),
82
+ "structure:default"
83
+ );
84
+ state.materials.set(
85
+ FRAME_KEY,
86
+ materials.add(
87
+ new MaterialAsset({ baseColor: [0.29, 0.18, 0.1, 1], roughness: 0.6 }),
88
+ FRAME_KEY
89
+ )
90
+ );
91
+ state.glassMaterial = materials.add(
92
+ new MaterialAsset({
93
+ baseColor: [0.62, 0.76, 0.86, 0.28],
94
+ roughness: 0.05,
95
+ alphaMode: "alpha"
96
+ }),
97
+ "structure:glass"
98
+ );
99
+ state.leafMesh = world.resource(Meshes).add(leafBox(), "structure:leaf");
100
+ registerShaders(world.resource(Shaders), CONTACT_SHADERS);
101
+ const contact = state.contact;
102
+ state.contactMaterial = materials.add(
103
+ new MaterialAsset(
104
+ {
105
+ opacity: contact.opacity,
106
+ maxAlpha: contact.maxAlpha,
107
+ color: [...contact.color],
108
+ wobble: contact.wobble
109
+ },
110
+ ContactShade
111
+ ),
112
+ CONTACT_KEY
113
+ );
114
+ }
115
+ });
116
+ export {
117
+ structureMethods,
118
+ structurePlugin
119
+ };
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@aethervtt/shard-structure",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./planar": {
13
+ "types": "./dist/planar.d.ts",
14
+ "default": "./dist/planar.js"
15
+ },
16
+ "./fixtures": {
17
+ "types": "./dist/fixtures.d.ts",
18
+ "default": "./dist/fixtures.js"
19
+ },
20
+ "./bench": {
21
+ "types": "./dist/bench.d.ts",
22
+ "default": "./dist/bench.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "LICENSE",
28
+ "README.md"
29
+ ],
30
+ "dependencies": {
31
+ "@aethervtt/shard-core": "0.2.0",
32
+ "@aethervtt/shard-mesh": "0.2.0",
33
+ "@aethervtt/shard-mirror": "0.2.0",
34
+ "@aethervtt/shard-render": "0.2.0",
35
+ "@aethervtt/shard-runtime": "0.2.0",
36
+ "@aethervtt/shard-texture": "0.2.0",
37
+ "@aethervtt/shard-transform": "0.2.0"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/aethervtt/shard.git",
42
+ "directory": "packages/structure"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "provenance": true
47
+ },
48
+ "shard": {
49
+ "buildSha": "32c22a3944ed23f5d1baa90bf7cc9fae724f5fd8"
50
+ }
51
+ }