@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,156 @@
1
+ /** A light channel (0069): `sight` (unset) follows the sight channel. */
2
+ export declare const LIGHT_CHANNELS: readonly ['sight', 'normal', 'none'];
3
+ export declare const Level: import("@aethervtt/shard-core").ComponentDef<{
4
+ readonly index: import("@aethervtt/shard-core").FieldType<number, "i8">;
5
+ readonly elevation: import("@aethervtt/shard-core").FieldType<number, "f32">;
6
+ readonly height: import("@aethervtt/shard-core").FieldType<number, "f32">;
7
+ readonly interiorAmbient: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
8
+ readonly interiorFill: import("@aethervtt/shard-core").FieldType<number, "f32">;
9
+ }>;
10
+ export declare const WALL_SHAPES: readonly ['straight', 'arc', 'bezier'];
11
+ export declare const Wall: import("@aethervtt/shard-core").ComponentDef<{
12
+ readonly level: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
13
+ readonly a: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
14
+ readonly b: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
15
+ readonly height: import("@aethervtt/shard-core").FieldType<number, "f32">;
16
+ readonly thickness: import("@aethervtt/shard-core").FieldType<number, "f32">;
17
+ readonly elevation: import("@aethervtt/shard-core").FieldType<number, "f32">;
18
+ readonly material: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Material"> | null, "object">;
19
+ readonly shape: import("@aethervtt/shard-core").FieldType<"arc" | "bezier" | "straight", "u8">;
20
+ readonly bow: import("@aethervtt/shard-core").FieldType<number, "f32">;
21
+ readonly c0: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
22
+ readonly c1: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
23
+ readonly light: import("@aethervtt/shard-core").FieldType<"none" | "normal" | "sight", "u8">;
24
+ }>;
25
+ export declare const OPENING_KINDS: readonly ['door', 'window'];
26
+ export declare const HINGES: readonly ['start', 'end'];
27
+ export declare const SWINGS: readonly ['left', 'right'];
28
+ export declare const DOOR_STATES: readonly ['closed', 'open', 'locked'];
29
+ export declare const CHANNELS: readonly ['normal', 'none'];
30
+ export declare const Opening: import("@aethervtt/shard-core").ComponentDef<{
31
+ readonly wall: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
32
+ readonly kind: import("@aethervtt/shard-core").FieldType<"door" | "window", "u8">;
33
+ readonly offset: import("@aethervtt/shard-core").FieldType<number, "f32">;
34
+ readonly width: import("@aethervtt/shard-core").FieldType<number, "f32">;
35
+ readonly height: import("@aethervtt/shard-core").FieldType<number, "f32">;
36
+ readonly sill: import("@aethervtt/shard-core").FieldType<number, "f32">;
37
+ readonly frameWidth: import("@aethervtt/shard-core").FieldType<number, "f32">;
38
+ readonly frameDepth: import("@aethervtt/shard-core").FieldType<number, "f32">;
39
+ readonly frameMaterial: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Material"> | null, "object">;
40
+ readonly hinge: import("@aethervtt/shard-core").FieldType<"end" | "start", "u8">;
41
+ readonly swing: import("@aethervtt/shard-core").FieldType<"left" | "right", "u8">;
42
+ readonly state: import("@aethervtt/shard-core").FieldType<"closed" | "locked" | "open", "u8">;
43
+ readonly sight: import("@aethervtt/shard-core").FieldType<"none" | "normal", "u8">;
44
+ readonly movement: import("@aethervtt/shard-core").FieldType<"none" | "normal", "u8">;
45
+ readonly light: import("@aethervtt/shard-core").FieldType<"none" | "normal" | "sight", "u8">;
46
+ }>;
47
+ export declare const Floor: import("@aethervtt/shard-core").ComponentDef<{
48
+ readonly level: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
49
+ readonly points: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2[], "object">;
50
+ readonly elevation: import("@aethervtt/shard-core").FieldType<number, "f32">;
51
+ readonly thickness: import("@aethervtt/shard-core").FieldType<number, "f32">;
52
+ readonly material: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Material"> | null, "object">;
53
+ }>;
54
+ export declare const Roof: import("@aethervtt/shard-core").ComponentDef<{
55
+ readonly level: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
56
+ readonly points: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2[], "object">;
57
+ readonly height: import("@aethervtt/shard-core").FieldType<number, "f32">;
58
+ readonly pitch: import("@aethervtt/shard-core").FieldType<number, "f32">;
59
+ readonly ridge: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
60
+ readonly thickness: import("@aethervtt/shard-core").FieldType<number, "f32">;
61
+ readonly material: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Material"> | null, "object">;
62
+ readonly shadowWhenHidden: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
63
+ readonly cutaway: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
64
+ }>;
65
+ export declare const CUTOUT_KINDS: readonly ['hole', 'hatch', 'skylight'];
66
+ export declare const Cutout: import("@aethervtt/shard-core").ComponentDef<{
67
+ readonly host: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
68
+ readonly points: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2[], "object">;
69
+ readonly kind: import("@aethervtt/shard-core").FieldType<"hatch" | "hole" | "skylight", "u8">;
70
+ readonly frameWidth: import("@aethervtt/shard-core").FieldType<number, "f32">;
71
+ readonly frameDepth: import("@aethervtt/shard-core").FieldType<number, "f32">;
72
+ readonly frameMaterial: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Material"> | null, "object">;
73
+ readonly hinge: import("@aethervtt/shard-core").FieldType<number, "u16">;
74
+ readonly state: import("@aethervtt/shard-core").FieldType<"closed" | "locked" | "open", "u8">;
75
+ }>;
76
+ export declare const StructureChunk: import("@aethervtt/shard-core").ComponentDef<{
77
+ readonly group: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
78
+ readonly x: import("@aethervtt/shard-core").FieldType<number, "i32">;
79
+ readonly z: import("@aethervtt/shard-core").FieldType<number, "i32">;
80
+ }>;
81
+ export declare const DoorLeaf: import("@aethervtt/shard-core").ComponentDef<{
82
+ readonly opening: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
83
+ readonly angle: import("@aethervtt/shard-core").FieldType<number, "f32">;
84
+ }>;
85
+ /** A chunk's contact shade mesh (0068): one per (group, chunk) with walls near floors. */
86
+ export declare const ContactMesh: import("@aethervtt/shard-core").TagDef;
87
+ /** Glass in a window: a pane that structure spawns and keeps. */
88
+ export declare const WindowPane: import("@aethervtt/shard-core").TagDef;
89
+ /** Contact shade (0068): noisy dark strips where walls meet floors and each other. */
90
+ export interface ContactSettings {
91
+ /** false despawns every contact mesh: no draws, no compile work. */
92
+ enabled: boolean;
93
+ /** Floor strips run from a wall's face out this far (m). */
94
+ floorReach: number;
95
+ /** Corner strips run from a joint along the face this far (m). */
96
+ cornerReach: number;
97
+ /** Alpha scale of the falloff. */
98
+ opacity: number;
99
+ /** Alpha never goes above this. */
100
+ maxAlpha: number;
101
+ /** What it darkens toward: linear, display-referred. */
102
+ color: [number, number, number];
103
+ /** How far the noise moves a strip's edge, as a share of its reach (0 to 1). */
104
+ wobble: number;
105
+ }
106
+ /** Contact shade's defaults: Aether's, in metres. */
107
+ export declare function defaultContact(): ContactSettings;
108
+ export declare const INTERIOR_QUALITIES: readonly ['low', 'medium', 'high'];
109
+ export type InteriorQuality = (typeof INTERIOR_QUALITIES)[number];
110
+ /** Interior lighting (0069), with interiorLightingPlugin. */
111
+ export interface InteriorSettings {
112
+ /** Sky visibility: ambient and image-based light scaled by how much sky reaches a point. */
113
+ sky: boolean;
114
+ /** Point and spot lights with blockedByWalls are occluded by their level's walls. */
115
+ blockLights: boolean;
116
+ /** low: 0.5 m field, 256 bins, 1 tap; medium: 0.25 m, 512, 3; high: 0.125 m, 1024, 5. */
117
+ quality: InteriorQuality;
118
+ /** How far sky light spills through an opening (m): the field's screening length. */
119
+ spillReach: number;
120
+ /** Rows for blocked lights; more are lit unblocked, with structure/too-many-blocked-lights. */
121
+ maxBlockedLights: number;
122
+ }
123
+ /** Interior lighting's defaults. */
124
+ export declare function defaultInterior(): InteriorSettings;
125
+ export interface StructureSettingsValue {
126
+ /** Chunk edge, world units. Changing it rebuilds every chunk. */
127
+ chunkSize: number;
128
+ /** How long a door takes to swing, ms. */
129
+ doorSwingMs: number;
130
+ /** Doors snap open and closed instead of swinging. */
131
+ reducedMotion: boolean;
132
+ /**
133
+ * How far a curved wall's chords may stray from the curve, metres (0066). planarBarriers must
134
+ * use the same value, so a server blocks what's drawn. Changing it rebuilds every chunk.
135
+ */
136
+ curveTolerance: number;
137
+ /**
138
+ * Contact shade (0068). A patch may hold only the fields it changes: the rest keep their values.
139
+ * Turning it off or on, or changing a reach, rebuilds only contact meshes; the other fields
140
+ * rebuild nothing.
141
+ */
142
+ contact: ContactSettings;
143
+ /**
144
+ * Walls open around cameras' reveal points (0070): wall pieces (frames included) draw in meshes
145
+ * of their own, apart from floors, tagged Cutaway, and so are door leaves and window panes.
146
+ * Changing it rebuilds every chunk with walls.
147
+ */
148
+ cutawayWalls: boolean;
149
+ /**
150
+ * Interior lighting (0069), with interiorLightingPlugin. A patch may hold only the fields it
151
+ * changes. Turning a part off frees its texture; quality or spill reach re-solves the field and
152
+ * rebuilds every row once.
153
+ */
154
+ interior: InteriorSettings;
155
+ }
156
+ export declare const StructureSettings: import("@aethervtt/shard-core").ResourceDef<StructureSettingsValue>;
@@ -0,0 +1,294 @@
1
+ import { defineComponent, defineResource, defineTag, t } from "@aethervtt/shard-core";
2
+ import { Transform } from "@aethervtt/shard-transform";
3
+ const LEVEL_FIELD = {
4
+ level: t.entity({
5
+ description: "The Level it stands on (0067). Empty is the ground level; elevation is relative to the level."
6
+ })
7
+ };
8
+ const LIGHT_CHANNELS = ["sight", "normal", "none"];
9
+ const lightField = (what) => t.enum(LIGHT_CHANNELS, {
10
+ description: `Whether ${what} blocks light (0069): sky light spilling in, and wall-blocked lights. sight (unset) follows its sight channel; set it for a curtain that blocks sight but not light, or the opposite.`
11
+ });
12
+ const Level = defineComponent(
13
+ "structure/Level",
14
+ {
15
+ index: t.i8({
16
+ min: -16,
17
+ max: 15,
18
+ description: "Stacking order: a cellar is -1, the ground level 0. Use it as the GroundLayer.level of what stands on the level."
19
+ }),
20
+ elevation: t.f32({
21
+ unit: "m",
22
+ description: "Where the level's floor stands (y). Pieces on it are placed relative to it."
23
+ }),
24
+ height: t.f32({ default: 3, min: 0, unit: "m", description: "Floor to ceiling." }),
25
+ interiorAmbient: t.vec3({
26
+ unit: "cd/m\xB2",
27
+ description: "Interior lighting (0069): ambient light where the sky doesn't reach, linear colour, added to interiorFill's share of the sky's. A cellar darker than a hall."
28
+ }),
29
+ interiorFill: t.f32({
30
+ default: 0.05,
31
+ min: 0,
32
+ max: 1,
33
+ description: "Interior lighting (0069): the share of the view's sky ambient (uniform or image-based) kept where the sky doesn't reach."
34
+ })
35
+ },
36
+ {
37
+ description: "A level (0067): its walls, floors and roofs compile into its own group of chunk meshes, children of this entity, so Visibility on it hides the level without a rebuild.",
38
+ requires: [Transform]
39
+ }
40
+ );
41
+ const WALL_SHAPES = ["straight", "arc", "bezier"];
42
+ const Wall = defineComponent(
43
+ "structure/Wall",
44
+ {
45
+ a: t.vec2({ description: "Start of the centreline, (x, z) in world units." }),
46
+ b: t.vec2({ description: "End of the centreline, (x, z) in world units." }),
47
+ height: t.f32({ default: 3, min: 0, unit: "m", description: "Height above elevation." }),
48
+ thickness: t.f32({ default: 0.2, min: 0, unit: "m", description: "Full thickness." }),
49
+ elevation: t.f32({ unit: "m", description: "Where the wall stands (y), above its level." }),
50
+ ...LEVEL_FIELD,
51
+ material: t.handle("Material", { description: "Surface material. Empty uses a plain grey." }),
52
+ shape: t.enum(WALL_SHAPES, {
53
+ description: "straight; arc (bowed by bow); bezier (a cubic through c0 and c1)."
54
+ }),
55
+ bow: t.f32({
56
+ unit: "m",
57
+ description: "Arc: how far the midpoint bows off the line from a to b; positive bows to the left of a \u2192 b. More than half the chord is a major arc."
58
+ }),
59
+ c0: t.vec2({ description: "B\xE9zier: the first control point, (x, z)." }),
60
+ c1: t.vec2({ description: "B\xE9zier: the second control point, (x, z)." }),
61
+ light: lightField("it")
62
+ },
63
+ {
64
+ description: "A wall: straight, an arc or a B\xE9zier (0066). Structure compile draws it into chunked, per-material meshes; the entity itself draws nothing."
65
+ }
66
+ );
67
+ const OPENING_KINDS = ["door", "window"];
68
+ const HINGES = ["start", "end"];
69
+ const SWINGS = ["left", "right"];
70
+ const DOOR_STATES = ["closed", "open", "locked"];
71
+ const CHANNELS = ["normal", "none"];
72
+ const Opening = defineComponent(
73
+ "structure/Opening",
74
+ {
75
+ wall: t.entity({ description: "The host wall entity." }),
76
+ kind: t.enum(OPENING_KINDS, {
77
+ description: "A door (with a leaf that swings) or a window (with glass)."
78
+ }),
79
+ offset: t.f32({
80
+ min: 0,
81
+ unit: "m",
82
+ description: "Distance from the wall's start to the opening."
83
+ }),
84
+ width: t.f32({ default: 1, min: 0, unit: "m", description: "Width along the wall." }),
85
+ height: t.f32({ default: 2.1, min: 0, unit: "m", description: "Height of the hole." }),
86
+ sill: t.f32({
87
+ min: 0,
88
+ unit: "m",
89
+ description: "Height of the hole's bottom above the wall's base."
90
+ }),
91
+ frameWidth: t.f32({
92
+ default: 0.08,
93
+ min: 0,
94
+ unit: "m",
95
+ description: "Width of the frame around the hole."
96
+ }),
97
+ frameDepth: t.f32({
98
+ default: 0.04,
99
+ min: 0,
100
+ unit: "m",
101
+ description: "How far the frame stands out from each face of the wall."
102
+ }),
103
+ frameMaterial: t.handle("Material", {
104
+ description: "Frame and door leaf material. Empty uses the built-in wood (structure:frame)."
105
+ }),
106
+ hinge: t.enum(HINGES, { description: "Doors: which end of the opening the leaf hangs from." }),
107
+ swing: t.enum(SWINGS, { description: "Doors: which side of the wall the leaf opens toward." }),
108
+ state: t.enum(DOOR_STATES, {
109
+ description: "Doors: a change swings the leaf and rebuilds no geometry."
110
+ }),
111
+ sight: t.enum(CHANNELS, {
112
+ description: "Whether it blocks sight when closed (planarBarriers)."
113
+ }),
114
+ movement: t.enum(CHANNELS, {
115
+ description: "Whether it blocks movement when closed (planarBarriers)."
116
+ }),
117
+ light: lightField("it, closed,")
118
+ },
119
+ { description: "A door or window cut into a wall." }
120
+ );
121
+ const Floor = defineComponent(
122
+ "structure/Floor",
123
+ {
124
+ points: t.list(t.vec2, {
125
+ description: "Outline, (x, z) in world units: a simple polygon of 3 to 256 points, either winding."
126
+ }),
127
+ elevation: t.f32({ unit: "m", description: "Height of the floor (y), above its level." }),
128
+ thickness: t.f32({
129
+ min: 0,
130
+ unit: "m",
131
+ description: "Slab thickness below the surface: 0 draws the top only; more adds the underside, the edges and the rims of its cutouts."
132
+ }),
133
+ material: t.handle("Material", { description: "Surface material. Empty uses a plain grey." }),
134
+ ...LEVEL_FIELD
135
+ },
136
+ {
137
+ description: "A flat floor polygon, triangulated once per edit (with its cutouts as holes) and clipped into chunks."
138
+ }
139
+ );
140
+ const Roof = defineComponent(
141
+ "structure/Roof",
142
+ {
143
+ points: t.list(t.vec2, {
144
+ description: "Footprint, (x, z): a simple polygon of 3 to 256 points, either winding."
145
+ }),
146
+ height: t.f32({
147
+ default: 3,
148
+ min: 0,
149
+ unit: "m",
150
+ description: "The eaves' height above its level's elevation."
151
+ }),
152
+ pitch: t.f32({
153
+ min: 0,
154
+ max: 80,
155
+ unit: "deg",
156
+ description: "0 is flat; more is one slope rising toward ridge from the lowest point."
157
+ }),
158
+ ridge: t.vec2({ default: [0, 1], description: "The direction the slope rises, (x, z)." }),
159
+ thickness: t.f32({ default: 0.2, min: 0, unit: "m", description: "Measured straight down." }),
160
+ material: t.handle("Material", { description: "Surface material. Empty uses a plain grey." }),
161
+ shadowWhenHidden: t.bool({
162
+ default: true,
163
+ description: "While hidden (Visibility), it still casts shadows, keeping the interior dark."
164
+ }),
165
+ cutaway: t.bool({
166
+ description: "Opens around cameras' reveal points (CutawayView, 0070) instead of hiding whole: its chunk meshes, hatches and skylights are Cutaway. It still casts whole."
167
+ }),
168
+ ...LEVEL_FIELD
169
+ },
170
+ {
171
+ description: "A roof (0067): its own group of chunk meshes, children of this entity, so it hides alone and nothing under it rebuilds.",
172
+ requires: [Transform]
173
+ }
174
+ );
175
+ const CUTOUT_KINDS = ["hole", "hatch", "skylight"];
176
+ const Cutout = defineComponent(
177
+ "structure/Cutout",
178
+ {
179
+ host: t.entity({ description: "The Floor or Roof it cuts." }),
180
+ points: t.list(t.vec2, {
181
+ description: "The hole, (x, z): a simple polygon inside its host, either winding."
182
+ }),
183
+ kind: t.enum(CUTOUT_KINDS, {
184
+ description: "hole (a gap), hatch (a leaf that swings up) or skylight (glass)."
185
+ }),
186
+ frameWidth: t.f32({
187
+ default: 0.06,
188
+ min: 0,
189
+ unit: "m",
190
+ description: "Width of the frame around the hole, on the host surface."
191
+ }),
192
+ frameDepth: t.f32({
193
+ default: 0.03,
194
+ min: 0,
195
+ unit: "m",
196
+ description: "How far the frame stands above the host surface."
197
+ }),
198
+ frameMaterial: t.handle("Material", {
199
+ description: "Frame and hatch leaf material. Empty uses the built-in wood (structure:frame)."
200
+ }),
201
+ hinge: t.u16({
202
+ description: "Hatches: the outline edge the leaf hangs from (edge i runs from point i to i + 1)."
203
+ }),
204
+ state: t.enum(DOOR_STATES, {
205
+ description: "Hatches: a change swings the leaf and rebuilds no geometry."
206
+ })
207
+ },
208
+ { description: "A hole, hatch or skylight cut into a floor or a roof (0067)." }
209
+ );
210
+ const StructureChunk = defineComponent(
211
+ "structure/Chunk",
212
+ {
213
+ group: t.entity({
214
+ readonly: true,
215
+ description: "The group it belongs to: a Level, a Roof, or the ground level."
216
+ }),
217
+ x: t.i32({ readonly: true, description: "Chunk column (x / chunkSize)." }),
218
+ z: t.i32({ readonly: true, description: "Chunk row (z / chunkSize)." })
219
+ },
220
+ {
221
+ description: "One group's geometry in one chunk and one material, rebuilt by structure compile.",
222
+ serialize: false,
223
+ save: false
224
+ }
225
+ );
226
+ const DoorLeaf = defineComponent(
227
+ "structure/DoorLeaf",
228
+ {
229
+ opening: t.entity({
230
+ readonly: true,
231
+ description: "The Opening (a door or window) or Cutout (a hatch or skylight) it belongs to."
232
+ }),
233
+ angle: t.f32({ readonly: true, description: "How far open, 0 (closed) to 1 (open)." })
234
+ },
235
+ {
236
+ description: "A door leaf (or a window pane) that structure spawns for an opening. Picks resolve through `opening`.",
237
+ serialize: false,
238
+ save: false
239
+ }
240
+ );
241
+ const ContactMesh = defineTag("structure/ContactMesh", { serialize: false, save: false });
242
+ const WindowPane = defineTag("structure/WindowPane", { serialize: false, save: false });
243
+ function defaultContact() {
244
+ return {
245
+ enabled: true,
246
+ floorReach: 0.7,
247
+ cornerReach: 0.43,
248
+ opacity: 0.2,
249
+ maxAlpha: 0.42,
250
+ color: [33e-4, 52e-4, 8e-3],
251
+ wobble: 0.2
252
+ };
253
+ }
254
+ const INTERIOR_QUALITIES = ["low", "medium", "high"];
255
+ function defaultInterior() {
256
+ return { sky: true, blockLights: true, quality: "medium", spillReach: 3, maxBlockedLights: 256 };
257
+ }
258
+ const StructureSettings = defineResource("structure/Settings", {
259
+ description: "Chunk size, door swing time, reduced motion, curve tolerance, contact shade, cutaway walls and interior lighting. Write with patchResource.",
260
+ init: () => ({
261
+ chunkSize: 8,
262
+ doorSwingMs: 250,
263
+ reducedMotion: false,
264
+ curveTolerance: 0.01,
265
+ contact: defaultContact(),
266
+ cutawayWalls: false,
267
+ interior: defaultInterior()
268
+ }),
269
+ hostWritable: true
270
+ });
271
+ export {
272
+ CHANNELS,
273
+ CUTOUT_KINDS,
274
+ ContactMesh,
275
+ Cutout,
276
+ DOOR_STATES,
277
+ DoorLeaf,
278
+ Floor,
279
+ HINGES,
280
+ INTERIOR_QUALITIES,
281
+ LIGHT_CHANNELS,
282
+ Level,
283
+ OPENING_KINDS,
284
+ Opening,
285
+ Roof,
286
+ SWINGS,
287
+ StructureChunk,
288
+ StructureSettings,
289
+ WALL_SHAPES,
290
+ Wall,
291
+ WindowPane,
292
+ defaultContact,
293
+ defaultInterior
294
+ };
@@ -0,0 +1,4 @@
1
+ /** The material key of the built-in contact shade. */
2
+ export declare const CONTACT_KEY = "structure:contact";
3
+ export declare const ContactShade: import("@aethervtt/shard-render").MaterialType;
4
+ export declare const CONTACT_SHADERS: Record<string, string>;
@@ -0,0 +1,76 @@
1
+ import { t } from "@aethervtt/shard-core";
2
+ import { defineMaterial } from "@aethervtt/shard-render";
3
+ import { CONTACT_CORE, CONTACT_CORE_BOOST } from "./contact.js";
4
+ const CONTACT_KEY = "structure:contact";
5
+ const ContactShade = defineMaterial("structure/ContactShade", {
6
+ extends: "none",
7
+ blend: "alpha",
8
+ pickable: false,
9
+ fields: {
10
+ opacity: t.f32({ default: 0.2, min: 0, max: 1, description: "Alpha scale of the falloff." }),
11
+ maxAlpha: t.f32({ default: 0.42, min: 0, max: 1, description: "Alpha never goes above this." }),
12
+ color: t.vec3({
13
+ default: [33e-4, 52e-4, 8e-3],
14
+ min: 0,
15
+ description: "What it darkens toward: linear, display-referred (exposure is undone)."
16
+ }),
17
+ wobble: t.f32({
18
+ default: 0.2,
19
+ min: 0,
20
+ max: 1,
21
+ description: "How far the noise moves the strip's edge, as a share of its reach."
22
+ })
23
+ },
24
+ shader: "structure::contact",
25
+ description: "Structure's contact shade (0068): dark strips where walls meet floors and each other. Built in; StructureSettings.contact sets its fields."
26
+ });
27
+ const CONTACT_SHADERS = {
28
+ "structure::contact": `
29
+ import shard::view::view;
30
+ import shard::pbr::types::VertexOutput;
31
+ import material::contact_shade::ContactShade;
32
+
33
+ fn contact_hash(x: u32) -> u32 {
34
+ var h = x * 747796405u + 2891336453u;
35
+ h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u;
36
+ return (h >> 22u) ^ h;
37
+ }
38
+
39
+ fn contact_cell(c: vec2i) -> f32 {
40
+ let h = contact_hash(bitcast<u32>(c.x) ^ contact_hash(bitcast<u32>(c.y)));
41
+ return f32(h >> 8u) * (1.0 / 16777216.0);
42
+ }
43
+
44
+ /** One octave of value noise in [0, 1]. */
45
+ fn contact_noise(p: vec2f) -> f32 {
46
+ let i = floor(p);
47
+ let f = p - i;
48
+ let u = f * f * (3.0 - 2.0 * f);
49
+ let c = vec2i(i);
50
+ return mix(
51
+ mix(contact_cell(c), contact_cell(c + vec2i(1, 0)), u.x),
52
+ mix(contact_cell(c + vec2i(0, 1)), contact_cell(c + vec2i(1, 1)), u.x),
53
+ u.y,
54
+ );
55
+ }
56
+
57
+ /** u is fade: 0 at the contact line, 1 at the reach. */
58
+ override fn shade(in: VertexOutput) -> vec4f {
59
+ let p = in.world_position;
60
+ // World space, so strips meet across chunks and walls; y folds in for the corner strips.
61
+ let n = contact_noise(vec2f(p.x + p.y * 0.61, p.z - p.y * 0.83) / 0.6);
62
+ let wobble = clamp(ContactShade.wobble, 0.0, 1.0);
63
+ let fade = in.uv.x;
64
+ let f = clamp(fade * (1.0 + wobble * (2.0 * n - 1.0)), 0.0, 1.0);
65
+ let falloff = 1.0 - smoothstep(0.0, 1.0, f);
66
+ let core = 1.0 - smoothstep(0.0, ${CONTACT_CORE.toFixed(2)}, f);
67
+ let edge = 1.0 - smoothstep(0.8, 1.0, fade);
68
+ let alpha = ContactShade.opacity * (falloff * (0.8 + 0.2 * falloff) + core * ${CONTACT_CORE_BOOST.toFixed(2)}) * (0.94 + 0.12 * n);
69
+ return vec4f(ContactShade.color / max(view.exposure, 1e-12), min(ContactShade.maxAlpha, alpha) * edge);
70
+ }`
71
+ };
72
+ export {
73
+ CONTACT_KEY,
74
+ CONTACT_SHADERS,
75
+ ContactShade
76
+ };
@@ -0,0 +1,65 @@
1
+ import { type MeshBuilder, type OpeningShape, type WallShape } from './geometry.js';
2
+ /** Floats per quad: its normal (x, y, z, 0), then four corners (x, y, z, fade). */
3
+ export declare const CONTACT_STRIDE = 20;
4
+ /** Floor strips are walked in pieces of at most this (m): each decides its sides on its own. */
5
+ export declare const CONTACT_STEP = 1.8;
6
+ /** How far strips stand off what they shade (m), above the floor or off the face. */
7
+ export declare const CONTACT_LIFT = 0.002;
8
+ /** Quads, grown as needed: `count` quads of CONTACT_STRIDE floats in `data`. */
9
+ export declare class ContactQuads {
10
+ data: Float64Array<ArrayBuffer>;
11
+ count: number;
12
+ reset(): void;
13
+ /** Room for one more quad; returns its offset. */
14
+ next(): number;
15
+ /** Whether `other` holds the same quads (an edit that changed nothing here). */
16
+ equals(other: ContactQuads): boolean;
17
+ copy(other: ContactQuads): void;
18
+ }
19
+ export interface ContactReach {
20
+ /** Floor strips: from the face out to here (m). */
21
+ floorReach: number;
22
+ /** Corner strips: from the joint along the face to here (m). */
23
+ cornerReach: number;
24
+ }
25
+ /** What a wall's contact geometry needs to know about its surroundings. */
26
+ export interface ContactEnv {
27
+ /**
28
+ * The top (y) of the floor on the wall's level containing (x, z), the one closest to `base` if
29
+ * several do; NaN if none does.
30
+ */
31
+ floorTop(x: number, z: number, base: number): number;
32
+ /**
33
+ * The joint at an end (0: the start, 1: the end), into `out`: the height the walls there share
34
+ * (y0, y1, before insets), and how far along the face the joined walls cover. False if no other
35
+ * wall meets that end.
36
+ */
37
+ joint(end: 0 | 1, out: Float64Array): boolean;
38
+ }
39
+ /**
40
+ * A wall's contact quads into `out` (reset first): floor strips on each side that faces a floor,
41
+ * skipping door openings (`openings` sorted by offset), and corner strips at joined ends.
42
+ */
43
+ export declare function contactQuads(w: WallShape, openings: readonly OpeningShape[], reach: ContactReach, env: ContactEnv, out: ContactQuads): void;
44
+ /** Adds the keys of every chunk a wall's contact quads have area (or length) in to `out`. */
45
+ export declare function contactChunks(quads: ContactQuads, size: number, out: Set<number>): void;
46
+ /**
47
+ * Emits the parts of contact quads inside one chunk square into `m`: positions, normals, and fade
48
+ * in u. Returns whether anything was emitted.
49
+ */
50
+ export declare function emitContact(quads: ContactQuads, minX: number, minZ: number, maxX: number, maxZ: number, m: MeshBuilder): boolean;
51
+ /** What the contact fragment reads (StructureSettings.contact's appearance). */
52
+ export interface ContactLook {
53
+ opacity: number;
54
+ maxAlpha: number;
55
+ wobble: number;
56
+ }
57
+ /** The share of the reach the contact core darkens. */
58
+ export declare const CONTACT_CORE = 0.1;
59
+ /** How much the core adds over the falloff. */
60
+ export declare const CONTACT_CORE_BOOST = 0.55;
61
+ /**
62
+ * The contact strip's alpha at `fade` (0 at the contact line, 1 at the reach) for noise value `n`
63
+ * in [0, 1]: the CPU mirror of `structure::contact`'s shade. Non-increasing in fade for any n.
64
+ */
65
+ export declare function contactAlpha(fade: number, n: number, look: ContactLook): number;