@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.
@@ -0,0 +1,27 @@
1
+ import type { Entity } from '@aethervtt/shard-core';
2
+ import type { GpuContext } from '@aethervtt/shard-gpu';
3
+ import { OffscreenTarget } from '@aethervtt/shard-render';
4
+ import { App } from '@aethervtt/shard-runtime';
5
+ import { hostScene } from './fixtures.js';
6
+ export interface Rig {
7
+ app: App;
8
+ host: ReturnType<typeof hostScene>;
9
+ camera: Entity;
10
+ sun: Entity;
11
+ target: OffscreenTarget;
12
+ view: string;
13
+ frame(): void;
14
+ look(eye: [number, number, number], at: [number, number, number]): void;
15
+ dispose(): Promise<void>;
16
+ }
17
+ export declare function rig(gpu: GpuContext, options?: {
18
+ width?: number;
19
+ height?: number;
20
+ shadowUpdate?: 'always' | 'on-change';
21
+ shadows?: boolean;
22
+ orthoHeight?: number;
23
+ /** Adds surfacePlugin (0068), for SurfaceMaterial walls and floors. */
24
+ surface?: boolean;
25
+ /** Adds interiorLightingPlugin (0069). */
26
+ interior?: boolean;
27
+ }): Promise<Rig>;
@@ -0,0 +1,74 @@
1
+ import {
2
+ Camera3d,
3
+ DirectionalLight,
4
+ Exposure,
5
+ forwardPlugin,
6
+ OffscreenTarget,
7
+ RenderTargets,
8
+ renderPlugin,
9
+ Tonemapping
10
+ } from "@aethervtt/shard-render";
11
+ import { surfacePlugin } from "@aethervtt/shard-render/surface";
12
+ import { App } from "@aethervtt/shard-runtime";
13
+ import { lookAt, Transform, TransformPlugin } from "@aethervtt/shard-transform";
14
+ import { hostScene } from "./fixtures.js";
15
+ import { interiorLightingPlugin } from "./interior-plugin.js";
16
+ import { structurePlugin } from "./plugin.js";
17
+ async function rig(gpu, options = {}) {
18
+ const app = new App().addPlugin(
19
+ TransformPlugin,
20
+ renderPlugin({ gpu, windowView: false }),
21
+ forwardPlugin({ msaa: 1 }),
22
+ structurePlugin
23
+ );
24
+ if (options.surface) app.addPlugin(surfacePlugin);
25
+ if (options.interior) app.addPlugin(interiorLightingPlugin);
26
+ await app.init();
27
+ const world = app.world;
28
+ const target = new OffscreenTarget(gpu, {
29
+ label: "structure",
30
+ width: options.width ?? 96,
31
+ height: options.height ?? 64
32
+ });
33
+ const ref = world.resource(RenderTargets).add(target, "structure");
34
+ const camera = world.spawn(
35
+ [
36
+ Camera3d,
37
+ options.orthoHeight ? { target: ref, projection: "orthographic", orthoHeight: options.orthoHeight, far: 500 } : { target: ref, fovY: 50 }
38
+ ],
39
+ [Exposure, { ev100: 12 }],
40
+ [Tonemapping, { dither: false }],
41
+ Transform
42
+ );
43
+ const sun = world.spawn(
44
+ [
45
+ DirectionalLight,
46
+ {
47
+ illuminance: 2e4,
48
+ shadows: options.shadows ?? true,
49
+ shadowUpdate: options.shadowUpdate ?? "always"
50
+ }
51
+ ],
52
+ [Transform, { rotation: lookAt([-3, 8, 2], [0, 0, 0]) }]
53
+ );
54
+ const host = hostScene(world);
55
+ return {
56
+ app,
57
+ host,
58
+ camera,
59
+ sun,
60
+ target,
61
+ view: `camera:${camera}`,
62
+ frame: () => app.update(1 / 60),
63
+ look(eye, at) {
64
+ world.set(camera, Transform, { translation: eye, rotation: lookAt(eye, at) });
65
+ },
66
+ async dispose() {
67
+ await app.dispose();
68
+ target.destroy();
69
+ }
70
+ };
71
+ }
72
+ export {
73
+ rig
74
+ };
@@ -0,0 +1,9 @@
1
+ export { type CompileReport, compileStructure, DOOR_DEMAND, roofAt, Structure, StructureState, swingDoors, } from './compile.js';
2
+ export { CHANNELS, ContactMesh, type ContactSettings, CUTOUT_KINDS, Cutout, DOOR_STATES, DoorLeaf, defaultContact, defaultInterior, Floor, HINGES, INTERIOR_QUALITIES, type InteriorQuality, type InteriorSettings, Level, LIGHT_CHANNELS, OPENING_KINDS, Opening, Roof, StructureChunk, StructureSettings, type StructureSettingsValue, SWINGS, WALL_SHAPES, Wall, WindowPane, } from './components.js';
3
+ export { CONTACT_CORE, CONTACT_CORE_BOOST, CONTACT_LIFT, CONTACT_STEP, CONTACT_STRIDE, type ContactEnv, type ContactLook, ContactQuads, type ContactReach, contactAlpha, contactChunks, contactQuads, emitContact, } from './contact.js';
4
+ export { CONTACT_KEY, CONTACT_SHADERS, ContactShade } from './contact-shade.js';
5
+ export { arcOf, type Centerline, CURVE_TOLERANCE, type CurveInput, pointAt, quadraticToCubic, sampleWall, type WallCurve, } from './curve.js';
6
+ export { ClipScratch, chunkKey, chunkX, chunkZ, emitFloor, emitWall, type FloorShape, floorChunks, floorShape, insideRing, MeshBuilder, type OpeningShape, type Piece, ringInside, ringShape, ringsOverlap, type SlabFrame, type SlabOptions, slabY, type WallShape, wallChunks, wallLength, wallPieces, } from './geometry.js';
7
+ export { type Barrier, type Bounds, buildRow, type Cover, clearRect, type FieldGrid, FieldLayer, packRect, packTexel, RowScratch, rasterBarrier, rasterCover, relaxation, rowBias, SolveScratch, screening, solveRect, type TexelRect, texelsOf, unpackBin, unpackVisibility, } from './interior.js';
8
+ export { INTERIOR_QUALITY, Interior, type InteriorSolveReport, InteriorState, interiorLightingPlugin, interiorMethods, skyAt, updateInterior, } from './interior-plugin.js';
9
+ export { structureMethods, structurePlugin } from './plugin.js';
package/dist/index.js ADDED
@@ -0,0 +1,189 @@
1
+ import {
2
+ compileStructure,
3
+ DOOR_DEMAND,
4
+ roofAt,
5
+ Structure,
6
+ StructureState,
7
+ swingDoors
8
+ } from "./compile.js";
9
+ import {
10
+ CHANNELS,
11
+ ContactMesh,
12
+ CUTOUT_KINDS,
13
+ Cutout,
14
+ DOOR_STATES,
15
+ DoorLeaf,
16
+ defaultContact,
17
+ defaultInterior,
18
+ Floor,
19
+ HINGES,
20
+ INTERIOR_QUALITIES,
21
+ Level,
22
+ LIGHT_CHANNELS,
23
+ OPENING_KINDS,
24
+ Opening,
25
+ Roof,
26
+ StructureChunk,
27
+ StructureSettings,
28
+ SWINGS,
29
+ WALL_SHAPES,
30
+ Wall,
31
+ WindowPane
32
+ } from "./components.js";
33
+ import {
34
+ CONTACT_CORE,
35
+ CONTACT_CORE_BOOST,
36
+ CONTACT_LIFT,
37
+ CONTACT_STEP,
38
+ CONTACT_STRIDE,
39
+ ContactQuads,
40
+ contactAlpha,
41
+ contactChunks,
42
+ contactQuads,
43
+ emitContact
44
+ } from "./contact.js";
45
+ import { CONTACT_KEY, CONTACT_SHADERS, ContactShade } from "./contact-shade.js";
46
+ import {
47
+ arcOf,
48
+ CURVE_TOLERANCE,
49
+ pointAt,
50
+ quadraticToCubic,
51
+ sampleWall
52
+ } from "./curve.js";
53
+ import {
54
+ ClipScratch,
55
+ chunkKey,
56
+ chunkX,
57
+ chunkZ,
58
+ emitFloor,
59
+ emitWall,
60
+ floorChunks,
61
+ floorShape,
62
+ insideRing,
63
+ MeshBuilder,
64
+ ringInside,
65
+ ringShape,
66
+ ringsOverlap,
67
+ slabY,
68
+ wallChunks,
69
+ wallLength,
70
+ wallPieces
71
+ } from "./geometry.js";
72
+ import {
73
+ buildRow,
74
+ clearRect,
75
+ FieldLayer,
76
+ packRect,
77
+ packTexel,
78
+ RowScratch,
79
+ rasterBarrier,
80
+ rasterCover,
81
+ relaxation,
82
+ rowBias,
83
+ SolveScratch,
84
+ screening,
85
+ solveRect,
86
+ texelsOf,
87
+ unpackBin,
88
+ unpackVisibility
89
+ } from "./interior.js";
90
+ import {
91
+ INTERIOR_QUALITY,
92
+ Interior,
93
+ InteriorState,
94
+ interiorLightingPlugin,
95
+ interiorMethods,
96
+ skyAt,
97
+ updateInterior
98
+ } from "./interior-plugin.js";
99
+ import { structureMethods, structurePlugin } from "./plugin.js";
100
+ export {
101
+ CHANNELS,
102
+ CONTACT_CORE,
103
+ CONTACT_CORE_BOOST,
104
+ CONTACT_KEY,
105
+ CONTACT_LIFT,
106
+ CONTACT_SHADERS,
107
+ CONTACT_STEP,
108
+ CONTACT_STRIDE,
109
+ CURVE_TOLERANCE,
110
+ CUTOUT_KINDS,
111
+ ClipScratch,
112
+ ContactMesh,
113
+ ContactQuads,
114
+ ContactShade,
115
+ Cutout,
116
+ DOOR_DEMAND,
117
+ DOOR_STATES,
118
+ DoorLeaf,
119
+ FieldLayer,
120
+ Floor,
121
+ HINGES,
122
+ INTERIOR_QUALITIES,
123
+ INTERIOR_QUALITY,
124
+ Interior,
125
+ InteriorState,
126
+ LIGHT_CHANNELS,
127
+ Level,
128
+ MeshBuilder,
129
+ OPENING_KINDS,
130
+ Opening,
131
+ Roof,
132
+ RowScratch,
133
+ SWINGS,
134
+ SolveScratch,
135
+ Structure,
136
+ StructureChunk,
137
+ StructureSettings,
138
+ StructureState,
139
+ WALL_SHAPES,
140
+ Wall,
141
+ WindowPane,
142
+ arcOf,
143
+ buildRow,
144
+ chunkKey,
145
+ chunkX,
146
+ chunkZ,
147
+ clearRect,
148
+ compileStructure,
149
+ contactAlpha,
150
+ contactChunks,
151
+ contactQuads,
152
+ defaultContact,
153
+ defaultInterior,
154
+ emitContact,
155
+ emitFloor,
156
+ emitWall,
157
+ floorChunks,
158
+ floorShape,
159
+ insideRing,
160
+ interiorLightingPlugin,
161
+ interiorMethods,
162
+ packRect,
163
+ packTexel,
164
+ pointAt,
165
+ quadraticToCubic,
166
+ rasterBarrier,
167
+ rasterCover,
168
+ relaxation,
169
+ ringInside,
170
+ ringShape,
171
+ ringsOverlap,
172
+ roofAt,
173
+ rowBias,
174
+ sampleWall,
175
+ screening,
176
+ skyAt,
177
+ slabY,
178
+ solveRect,
179
+ structureMethods,
180
+ structurePlugin,
181
+ swingDoors,
182
+ texelsOf,
183
+ unpackBin,
184
+ unpackVisibility,
185
+ updateInterior,
186
+ wallChunks,
187
+ wallLength,
188
+ wallPieces
189
+ };
@@ -0,0 +1,206 @@
1
+ import { type Entity, type World } from '@aethervtt/shard-core';
2
+ import { type InteriorLighting } from '@aethervtt/shard-render';
3
+ import { type AppMethod } from '@aethervtt/shard-runtime';
4
+ import { type StructureState } from './compile.js';
5
+ import { type InteriorSettings } from './components.js';
6
+ import { type Barrier, type Bounds, type Cover, type FieldGrid, FieldLayer, RowScratch, SolveScratch, type TexelRect } from './interior.js';
7
+ /** Per quality: field texel (m), bins per light row, filter taps, coarse cells (texels in ~1 m). */
8
+ export declare const INTERIOR_QUALITY: {
9
+ readonly low: {
10
+ readonly texel: 0.5;
11
+ readonly bins: 256;
12
+ readonly taps: 1;
13
+ readonly cells: 2;
14
+ };
15
+ readonly medium: {
16
+ readonly texel: 0.25;
17
+ readonly bins: 512;
18
+ readonly taps: 3;
19
+ readonly cells: 4;
20
+ };
21
+ readonly high: {
22
+ readonly texel: 0.125;
23
+ readonly bins: 1024;
24
+ readonly taps: 5;
25
+ readonly cells: 8;
26
+ };
27
+ };
28
+ interface WallCache {
29
+ layer: number;
30
+ bounds: Bounds;
31
+ barriers: Barrier[];
32
+ signature: string;
33
+ /** Index cells it's in (layer-qualified keys). */
34
+ cells: number[];
35
+ }
36
+ interface SlabCache {
37
+ /** Layers it covers: a roof its own level's; a floor every lower level's. */
38
+ layers: number[];
39
+ bounds: Bounds;
40
+ cover: Cover;
41
+ signature: string;
42
+ }
43
+ interface LightCache {
44
+ row: number;
45
+ x: number;
46
+ y: number;
47
+ z: number;
48
+ range: number;
49
+ layer: number;
50
+ /** The frame it was last seen, and the frame its row was last built. */
51
+ frame: number;
52
+ rebuilt: number;
53
+ }
54
+ interface LevelEntry {
55
+ entity: Entity;
56
+ elevation: number;
57
+ height: number;
58
+ ambient: [number, number, number];
59
+ fill: number;
60
+ }
61
+ /** The last field solve (structure.describe → interior.lastSolve). */
62
+ export interface InteriorSolveReport {
63
+ /** Whole-field solve (bounds grew, quality or reach changed), or a region after an edit. */
64
+ full: boolean;
65
+ /** Regions re-solved: layer, and [minX, minZ, maxX, maxZ] in metres. */
66
+ regions: {
67
+ layer: number;
68
+ bounds: [number, number, number, number];
69
+ }[];
70
+ texels: number;
71
+ sweeps: number;
72
+ /** Texels uploaded. */
73
+ uploaded: number;
74
+ ms: number;
75
+ }
76
+ /** What the plugin keeps: the field, every piece's barriers and cover, and the blocked lights. */
77
+ export declare class InteriorState {
78
+ settings: InteriorSettings;
79
+ /** The `StructureSettings.interior` object last merged (a patch replaces it). */
80
+ seen: object | undefined;
81
+ /** Settings the field and rows were last built with. */
82
+ builtTexel: number;
83
+ builtReach: number;
84
+ builtBins: number;
85
+ curveTolerance: number;
86
+ grid: FieldGrid | undefined;
87
+ layers: FieldLayer[];
88
+ levels: LevelEntry[];
89
+ /** Layer of each Level entity (1 + its rank by elevation); the ground level is layer 0. */
90
+ readonly levelLayer: Map<number, number>;
91
+ readonly walls: Map<number, WallCache>;
92
+ readonly slabs: Map<number, SlabCache>;
93
+ /** Openings' and cutouts' hosts as last seen, so an edit that moves one dirties both. */
94
+ readonly openingWall: Map<number, number>;
95
+ readonly cutoutHost: Map<number, number>;
96
+ readonly cells: Map<number, Set<number>>;
97
+ readonly lights: Map<number, LightCache>;
98
+ /** Rows not given to a light, lowest last (`pop` hands out the lowest). */
99
+ freeRows: number[];
100
+ rowCount: number;
101
+ rowData: Uint32Array<ArrayBuffer>;
102
+ /** Per layer, regions to rasterize and solve again. */
103
+ dirty: TexelRect[][];
104
+ readonly dirtyRows: Set<number>;
105
+ /** The field (and every row) is built from scratch next. */
106
+ full: boolean;
107
+ /** Lights over maxBlockedLights were reported. */
108
+ warnedRows: boolean;
109
+ warnedLevels: boolean;
110
+ last: InteriorSolveReport | null;
111
+ lastRows: {
112
+ lights: number;
113
+ ms: number;
114
+ };
115
+ frame: number;
116
+ seenLights: number;
117
+ readonly removedWalls: Entity[];
118
+ readonly removedSlabs: Entity[];
119
+ readonly removedOpenings: Entity[];
120
+ readonly removedCutouts: Entity[];
121
+ /** Point and spot lights whose component went (despawns included): their rows free first. */
122
+ readonly removedLights: Entity[];
123
+ levelsDirty: boolean;
124
+ /** Field regions and rows to upload this frame (unless the texture is new, and gets it all). */
125
+ readonly uploads: {
126
+ layer: number;
127
+ rect: TexelRect;
128
+ }[];
129
+ readonly rowUploads: Set<number>;
130
+ readonly scratch: SolveScratch;
131
+ readonly rowScratch: RowScratch;
132
+ readonly candidates: Barrier[];
133
+ readonly stamp: Map<number, number>;
134
+ /** The layer pieces on `group` (a Level, or the ground level's group) draw into; -1 for none. */
135
+ layerOfGroup(structure: StructureState, group: Entity): number;
136
+ /** The table entry (and so layer) whose [floor, top) holds y: the shader's rule. */
137
+ layerAtY(y: number): number;
138
+ /** The field's value at a point: 1 outdoors, as the lighting stage reads it (without the normal offset). */
139
+ skyAt(x: number, y: number, z: number): number;
140
+ describe(lighting: InteriorLighting | undefined): {
141
+ settings: {
142
+ sky: boolean;
143
+ blockLights: boolean;
144
+ quality: import("./components.js").InteriorQuality;
145
+ spillReach: number;
146
+ maxBlockedLights: number;
147
+ };
148
+ sky: boolean;
149
+ blockLights: boolean;
150
+ field: {
151
+ extent: number[];
152
+ texel: number;
153
+ size: number[];
154
+ layers: number;
155
+ } | null;
156
+ levels: {
157
+ level: number;
158
+ layer: number;
159
+ elevation: number;
160
+ height: number;
161
+ }[];
162
+ lastSolve: InteriorSolveReport | null;
163
+ lastRows: {
164
+ lights: number;
165
+ ms: number;
166
+ };
167
+ blockedLights: {
168
+ light: Entity;
169
+ row: number;
170
+ lastRebuild: number;
171
+ }[];
172
+ rows: {
173
+ used: number;
174
+ capacity: number;
175
+ };
176
+ bytes: number;
177
+ };
178
+ }
179
+ export declare const Interior: import("@aethervtt/shard-core").ResourceDef<InteriorState>;
180
+ /**
181
+ * Keeps the field and the rows current: settings, levels, then edits (each a region of the field
182
+ * and the rows of lights in reach), then the lights themselves. Uploads only what changed; a still
183
+ * frame does no work and allocates nothing.
184
+ */
185
+ export declare const updateInterior: import("@aethervtt/shard-core").SystemDef<{
186
+ walls: import("@aethervtt/shard-core").Query;
187
+ floors: import("@aethervtt/shard-core").Query;
188
+ roofs: import("@aethervtt/shard-core").Query;
189
+ openings: import("@aethervtt/shard-core").Query;
190
+ cutouts: import("@aethervtt/shard-core").Query;
191
+ levels: import("@aethervtt/shard-core").Query;
192
+ points: import("@aethervtt/shard-core").Query;
193
+ spots: import("@aethervtt/shard-core").Query;
194
+ }>;
195
+ /** Sky visibility at a point (0069): 1 outdoors, else the field there. For tests and agents. */
196
+ export declare function skyAt(world: World, x: number, y: number, z: number): number;
197
+ export declare const interiorMethods: AppMethod[];
198
+ /**
199
+ * Interior lighting (0069): sky visibility from the plan scales ambient and image-based light
200
+ * (dark deep in a room, dark in a sealed one), and lights with blockedByWalls stop at their level's
201
+ * walls and closed doors. StructureSettings.interior sets the parts, quality and spill reach;
202
+ * Level.interiorAmbient and interiorFill light what the sky doesn't reach. Needs structurePlugin
203
+ * and render's interiorPlugin (in forwardPlugin).
204
+ */
205
+ export declare const interiorLightingPlugin: import("@aethervtt/shard-runtime").Plugin;
206
+ export {};