@adatechnology/game-shapes 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,394 @@
1
+ /** `[x, y, z]` in metres (or a direction). */
2
+ type Vec3 = readonly [number, number, number];
3
+ /** `[x, y]`. */
4
+ type Vec2 = readonly [number, number];
5
+ /** A column-major 4 x 4 matrix (the layout of three's `Matrix4.elements`). */
6
+ type Mat4 = readonly number[];
7
+ /** `0xRRGGBB` or `'#rrggbb'`. */
8
+ type Color = number | string;
9
+ /** Euler order, as three names it. */
10
+ type RotationOrder = 'XYZ' | 'YXZ' | 'ZXY' | 'ZYX' | 'YZX' | 'XZY';
11
+ /** An atlas cell: the primitive's uvs are remapped into it (`u0 + u x (u1 - u0)`). */
12
+ interface UvRect {
13
+ readonly u0: number;
14
+ readonly v0: number;
15
+ readonly u1: number;
16
+ readonly v1: number;
17
+ }
18
+ /**
19
+ * A local transform. Either `position` / `rotation` / `rotationOrder` / `scale` (composed as three's `Object3D` does: Euler, quaternion, matrix),
20
+ * or `heading` with `position` (a pure rotation about +y built as a rotation matrix, the turn of an item on the floor), or a whole `matrix`.
21
+ */
22
+ interface Transform {
23
+ readonly position?: Vec3;
24
+ readonly rotation?: Vec3;
25
+ readonly rotationOrder?: RotationOrder;
26
+ readonly scale?: number | Vec3;
27
+ readonly heading?: number;
28
+ readonly matrix?: Mat4;
29
+ }
30
+ /** One copy of an instanced node: where it goes (between the parent and the node) and, optionally, its own colour. */
31
+ interface InstanceSpec extends Transform {
32
+ readonly color?: Color;
33
+ }
34
+ /** Fields every node may carry. */
35
+ interface NodeCommon extends Transform {
36
+ /** Named range: the vertices and indices this node adds are listed per batch in `ranges`. */
37
+ readonly name?: string;
38
+ /** Starts a movable part (see the README): the node's transform is the part's pivot, relative to the root; its children are baked in the part's space. */
39
+ readonly part?: string;
40
+ /** Material id, inherited by the children. */
41
+ readonly material?: string;
42
+ /** Vertex colour, inherited by the children. */
43
+ readonly color?: Color;
44
+ /** Atlas cell of the uvs, inherited by the children. */
45
+ readonly uv?: UvRect;
46
+ /** Instancing hint: draw this node once per entry instead of merging it. */
47
+ readonly instances?: readonly InstanceSpec[];
48
+ /** Left out of the LOD levels whose `detail` is below this. */
49
+ readonly minDetail?: number;
50
+ /** Free strings carried to the node's `ranges`. */
51
+ readonly tags?: readonly string[];
52
+ }
53
+ interface GroupNode extends NodeCommon {
54
+ readonly kind: 'group';
55
+ readonly children?: readonly ShapeNode[];
56
+ }
57
+ interface BoxNode extends NodeCommon {
58
+ readonly kind: 'box';
59
+ /** `[width, height, depth]`, default `[1, 1, 1]`. */
60
+ readonly size?: Vec3;
61
+ /** `[widthSegments, heightSegments, depthSegments]`, default `[1, 1, 1]`. */
62
+ readonly segments?: readonly [number, number, number];
63
+ }
64
+ interface CylinderNode extends NodeCommon {
65
+ readonly kind: 'cylinder';
66
+ readonly radiusTop?: number;
67
+ readonly radiusBottom?: number;
68
+ readonly height?: number;
69
+ readonly radialSegments?: number;
70
+ readonly heightSegments?: number;
71
+ readonly openEnded?: boolean;
72
+ readonly thetaStart?: number;
73
+ readonly thetaLength?: number;
74
+ }
75
+ interface ConeNode extends NodeCommon {
76
+ readonly kind: 'cone';
77
+ readonly radius?: number;
78
+ readonly height?: number;
79
+ readonly radialSegments?: number;
80
+ readonly heightSegments?: number;
81
+ readonly openEnded?: boolean;
82
+ readonly thetaStart?: number;
83
+ readonly thetaLength?: number;
84
+ }
85
+ interface SphereNode extends NodeCommon {
86
+ readonly kind: 'sphere';
87
+ readonly radius?: number;
88
+ readonly widthSegments?: number;
89
+ readonly heightSegments?: number;
90
+ readonly phiStart?: number;
91
+ readonly phiLength?: number;
92
+ readonly thetaStart?: number;
93
+ readonly thetaLength?: number;
94
+ }
95
+ interface PlaneNode extends NodeCommon {
96
+ readonly kind: 'plane';
97
+ readonly width?: number;
98
+ readonly height?: number;
99
+ readonly widthSegments?: number;
100
+ readonly heightSegments?: number;
101
+ }
102
+ interface CircleNode extends NodeCommon {
103
+ readonly kind: 'circle';
104
+ readonly radius?: number;
105
+ readonly segments?: number;
106
+ readonly thetaStart?: number;
107
+ readonly thetaLength?: number;
108
+ }
109
+ interface RingNode extends NodeCommon {
110
+ readonly kind: 'ring';
111
+ readonly innerRadius?: number;
112
+ readonly outerRadius?: number;
113
+ readonly thetaSegments?: number;
114
+ readonly phiSegments?: number;
115
+ readonly thetaStart?: number;
116
+ readonly thetaLength?: number;
117
+ }
118
+ interface LatheNode extends NodeCommon {
119
+ readonly kind: 'lathe';
120
+ /** The profile, `[x, y]` with x the distance from the axis. */
121
+ readonly points: readonly Vec2[];
122
+ readonly segments?: number;
123
+ readonly phiStart?: number;
124
+ readonly phiLength?: number;
125
+ }
126
+ interface TorusNode extends NodeCommon {
127
+ readonly kind: 'torus';
128
+ readonly radius?: number;
129
+ readonly tube?: number;
130
+ readonly radialSegments?: number;
131
+ readonly tubularSegments?: number;
132
+ readonly arc?: number;
133
+ }
134
+ /** A straight-edged outline (curves are sampled by the caller), with optional holes. */
135
+ interface ShapeOutline {
136
+ readonly outer: readonly Vec2[];
137
+ readonly holes?: readonly (readonly Vec2[])[];
138
+ }
139
+ interface BevelSpec {
140
+ readonly thickness?: number;
141
+ readonly size?: number;
142
+ readonly offset?: number;
143
+ readonly segments?: number;
144
+ }
145
+ interface ExtrudeNode extends NodeCommon {
146
+ readonly kind: 'extrude';
147
+ readonly shape: ShapeOutline;
148
+ /** Default 1, as three. */
149
+ readonly depth?: number;
150
+ readonly steps?: number;
151
+ /** `false` for no bevel; default three's (`thickness 0.2`, `size thickness - 0.1`, `offset 0`, `segments 3`). */
152
+ readonly bevel?: false | BevelSpec;
153
+ }
154
+ /** A rectangle `min`..`max` in the box's xy plane (centred coordinates) cut through the whole depth. */
155
+ interface HoleSpec {
156
+ readonly min: Vec2;
157
+ readonly max: Vec2;
158
+ }
159
+ /** A box minus rectangular prisms that go all the way through it along z: walls with windows and doors. */
160
+ interface DifferenceNode extends NodeCommon {
161
+ readonly kind: 'difference';
162
+ readonly size: Vec3;
163
+ readonly subtract: readonly HoleSpec[];
164
+ }
165
+ /** Raw triangles. Normals are computed as three's `computeVertexNormals` when missing; uvs default to zero. */
166
+ interface MeshNode extends NodeCommon {
167
+ readonly kind: 'mesh';
168
+ readonly positions: readonly number[];
169
+ readonly normals?: readonly number[];
170
+ readonly uvs?: readonly number[];
171
+ readonly indices?: readonly number[];
172
+ }
173
+ /** An anchor (a light, a muzzle, a seat): reported in `points`, at `position` in its parent's space. */
174
+ interface PointNode extends NodeCommon {
175
+ readonly kind: 'point';
176
+ readonly data?: unknown;
177
+ }
178
+ /** A node made by a generator the host registered (`registerGenerator`). */
179
+ interface CustomNode extends NodeCommon {
180
+ readonly kind: 'custom';
181
+ readonly generator: string;
182
+ readonly params?: unknown;
183
+ }
184
+ type PrimitiveNode = BoxNode | CylinderNode | ConeNode | SphereNode | PlaneNode | CircleNode | RingNode | LatheNode | TorusNode | ExtrudeNode | DifferenceNode | MeshNode;
185
+ type ShapeNode = GroupNode | PrimitiveNode | PointNode | CustomNode;
186
+ type ShapeKind = ShapeNode['kind'];
187
+ /** Renderer-agnostic hints for a material id; `game-shapes-three` turns them into a material when the host gives none. */
188
+ interface MaterialHint {
189
+ readonly color?: Color;
190
+ readonly roughness?: number;
191
+ readonly metalness?: number;
192
+ readonly opacity?: number;
193
+ readonly transparent?: boolean;
194
+ readonly emissive?: Color;
195
+ readonly emissiveIntensity?: number;
196
+ readonly side?: 'front' | 'back' | 'double';
197
+ readonly flatShading?: boolean;
198
+ readonly vertexColors?: boolean;
199
+ readonly depthWrite?: boolean;
200
+ }
201
+ /** A level after the base one. */
202
+ interface LodLevel {
203
+ /** Camera distance from which this level is drawn. */
204
+ readonly distance: number;
205
+ /** Multiplies the segment counts of round primitives (0 < detail <= 1). */
206
+ readonly detail: number;
207
+ /** Leaf nodes whose bounding radius is below this are left out. */
208
+ readonly minSize?: number;
209
+ /** Segment counts never go below this (default 3). */
210
+ readonly minSegments?: number;
211
+ }
212
+ /** The document: a node tree and how to draw it. */
213
+ interface ShapeSchema {
214
+ readonly version: 1;
215
+ readonly name?: string;
216
+ readonly materials?: Readonly<Record<string, MaterialHint>>;
217
+ readonly root: ShapeNode;
218
+ readonly lod?: readonly LodLevel[];
219
+ readonly cullDistance?: number;
220
+ }
221
+ /** A group of the index of a primitive, as three's `BufferGeometry.groups`. */
222
+ interface GeometryGroup {
223
+ readonly start: number;
224
+ readonly count: number;
225
+ readonly materialIndex: number;
226
+ }
227
+ /** One primitive on its own, not baked: exactly the arrays the three generator of `type` builds. */
228
+ interface PrimitiveBuffers {
229
+ /** The three geometry this reproduces (`'BoxGeometry'`...), or `'BufferGeometry'`. */
230
+ readonly type: string;
231
+ /** The parameters, as three's `geometry.parameters` would hold them. */
232
+ readonly parameters: Readonly<Record<string, unknown>>;
233
+ readonly positions: Float32Array;
234
+ readonly normals: Float32Array;
235
+ readonly uvs: Float32Array;
236
+ /** Absent for non-indexed geometry (extrusions). */
237
+ readonly indices?: Uint16Array | Uint32Array;
238
+ readonly groups: readonly GeometryGroup[];
239
+ }
240
+ /** The vertices and indices a named node added to a batch. */
241
+ interface NamedRange {
242
+ readonly name: string;
243
+ readonly tags?: readonly string[];
244
+ readonly vertexStart: number;
245
+ readonly vertexCount: number;
246
+ readonly indexStart: number;
247
+ readonly indexCount: number;
248
+ }
249
+ interface Bounds {
250
+ readonly min: [number, number, number];
251
+ readonly max: [number, number, number];
252
+ }
253
+ /** Everything of one (part, material): baked, ready for one draw call. */
254
+ interface ShapeBatch {
255
+ readonly material: string;
256
+ /** `null` for the static body. */
257
+ readonly part: string | null;
258
+ readonly positions: Float32Array;
259
+ readonly normals: Float32Array;
260
+ readonly uvs: Float32Array;
261
+ readonly colors?: Float32Array;
262
+ readonly indices?: Uint16Array | Uint32Array;
263
+ readonly ranges: readonly NamedRange[];
264
+ readonly bounds: Bounds;
265
+ }
266
+ /** A node drawn many times: its geometry once (in the node's space) and one matrix per copy (in the part's space). */
267
+ interface InstancedBatch {
268
+ readonly material: string;
269
+ readonly part: string | null;
270
+ readonly name?: string;
271
+ readonly geometry: PrimitiveBuffers;
272
+ /** 16 floats per instance, column-major. */
273
+ readonly matrices: Float32Array;
274
+ /** rgb per instance, when the instances have colours. */
275
+ readonly colors?: Float32Array;
276
+ readonly count: number;
277
+ }
278
+ interface ShapeLevel {
279
+ readonly distance: number;
280
+ readonly detail: number;
281
+ readonly batches: readonly ShapeBatch[];
282
+ readonly instanced: readonly InstancedBatch[];
283
+ }
284
+ /** A movable part: its pivot transform, relative to the root, as given in the schema, and as a matrix. */
285
+ interface ShapePart extends Transform {
286
+ readonly id: string;
287
+ readonly matrix: Mat4;
288
+ }
289
+ interface ShapePoint {
290
+ readonly name?: string;
291
+ readonly part: string | null;
292
+ readonly position: [number, number, number];
293
+ readonly data?: unknown;
294
+ readonly tags?: readonly string[];
295
+ }
296
+ interface ShapeStats {
297
+ readonly vertices: number;
298
+ readonly triangles: number;
299
+ readonly batches: number;
300
+ readonly instances: number;
301
+ readonly primitives: number;
302
+ }
303
+ /** What `generate` returns. Every array owns its buffer (transferable). */
304
+ interface ShapeBuffers {
305
+ readonly key: string;
306
+ readonly name?: string;
307
+ readonly levels: readonly ShapeLevel[];
308
+ readonly parts: readonly ShapePart[];
309
+ readonly points: readonly ShapePoint[];
310
+ readonly materials?: Readonly<Record<string, MaterialHint>>;
311
+ readonly cullDistance?: number;
312
+ /** Of level 0. */
313
+ readonly stats: ShapeStats;
314
+ }
315
+ /** What a custom generator receives besides its params. */
316
+ interface GeneratorContext {
317
+ /** Seeded stream in [0, 1), its own per (seed, generator, node path). */
318
+ readonly random: () => number;
319
+ readonly seed: number | string;
320
+ readonly detail: number;
321
+ readonly path: string;
322
+ }
323
+ type ShapeGenerator = (params: any, context: GeneratorContext) => ShapeNode;
324
+ interface GenerateOptions {
325
+ /** Seed of the custom generators. Default 0. */
326
+ readonly seed?: number | string;
327
+ /** Vertex colours: `'linear'` (three's working space, default), `'srgb'` (the channels over 255) or `'none'`. */
328
+ readonly colors?: 'linear' | 'srgb' | 'none';
329
+ /** Index the batches (default true). */
330
+ readonly indexed?: boolean;
331
+ /** Material ids the host draws; any other id becomes `fallbackMaterial`. Default: every id is kept. */
332
+ readonly materials?: readonly string[];
333
+ readonly fallbackMaterial?: string;
334
+ /** Ids that share a draw: `{ metal: 'plain' }` puts metal into the plain batches (after `materials`). */
335
+ readonly materialAlias?: Readonly<Record<string, string>>;
336
+ /** Instance identical leaf primitives repeated at least `minCount` times. Off by default. */
337
+ readonly instancing?: {
338
+ readonly minCount: number;
339
+ };
340
+ /** Only these level indices (0 is the base). Default all. */
341
+ readonly levels?: readonly number[];
342
+ /** Generators besides the registered ones. */
343
+ readonly generators?: Readonly<Record<string, ShapeGenerator>>;
344
+ /** Look custom nodes up only in `generators`, never in the global registry (what a `createShapes` set does). */
345
+ readonly isolatedGenerators?: boolean;
346
+ /** Validate the schema first (default true). */
347
+ readonly validate?: boolean;
348
+ /**
349
+ * The result's `key`: computed by default (`shapeKey`, a hash of the whole schema text), taken as given when a string, or `''` with `false`
350
+ * (a schema built in code, used once and never cached: the hash would cost more than a small shape).
351
+ */
352
+ readonly key?: string | false;
353
+ }
354
+
355
+ /** A job the pool posts: `options` without `generators` (functions do not cross threads; register them in the worker script). */
356
+ interface ShapeJob {
357
+ readonly id: number;
358
+ readonly schema: ShapeSchema;
359
+ readonly options?: Omit<GenerateOptions, 'generators'>;
360
+ }
361
+ type ShapeReply = {
362
+ readonly id: number;
363
+ readonly ok: true;
364
+ readonly buffers: ShapeBuffers;
365
+ } | {
366
+ readonly id: number;
367
+ readonly ok: false;
368
+ readonly error: string;
369
+ };
370
+ /** What a worker scope looks like: `self` in a browser worker, a `worker_threads` `parentPort` wrapper in Node, or an in-thread fake. */
371
+ interface WorkerScopeLike {
372
+ onmessage: ((event: {
373
+ data: unknown;
374
+ }) => void) | null;
375
+ postMessage(message: unknown, transfer?: ArrayBuffer[]): void;
376
+ }
377
+ interface ShapeWorkerOptions {
378
+ /** Generators of the `custom` nodes, the same ones the main thread registers. */
379
+ readonly generators?: Readonly<Record<string, ShapeGenerator>>;
380
+ /** Transfer the arrays back (default true: zero copy, the worker keeps nothing). */
381
+ readonly transfer?: boolean;
382
+ }
383
+ /**
384
+ * The worker side of `createShapePool`: answers each job with `generate(schema, options)`, transferring every array.
385
+ *
386
+ * ```ts
387
+ * // shape-worker.ts
388
+ * import { installShapeWorker } from '@adatechnology/game-shapes/worker'
389
+ * installShapeWorker(self as never, { generators: myGenerators })
390
+ * ```
391
+ */
392
+ declare function installShapeWorker(scope: WorkerScopeLike, options?: ShapeWorkerOptions): void;
393
+
394
+ export { type ShapePart as A, type BevelSpec as B, type Color as C, type DifferenceNode as D, type ExtrudeNode as E, type ShapePoint as F, type GenerateOptions as G, type HoleSpec as H, type InstanceSpec as I, type ShapeReply as J, type ShapeStats as K, type LatheNode as L, type Mat4 as M, type NamedRange as N, type ShapeWorkerOptions as O, type PrimitiveNode as P, type SphereNode as Q, type RotationOrder as R, type ShapeSchema as S, type Transform as T, type TorusNode as U, type Vec3 as V, type UvRect as W, type Vec2 as X, type WorkerScopeLike as Y, installShapeWorker as Z, type ShapeBuffers as a, type ShapeNode as b, type PrimitiveBuffers as c, type ShapeOutline as d, type ShapeGenerator as e, type Bounds as f, type BoxNode as g, type CircleNode as h, type ConeNode as i, type CustomNode as j, type CylinderNode as k, type GeneratorContext as l, type GeometryGroup as m, type GroupNode as n, type InstancedBatch as o, type LodLevel as p, type MaterialHint as q, type MeshNode as r, type NodeCommon as s, type PlaneNode as t, type PointNode as u, type RingNode as v, type ShapeBatch as w, type ShapeJob as x, type ShapeKind as y, type ShapeLevel as z };
@@ -0,0 +1 @@
1
+ export { x as ShapeJob, J as ShapeReply, O as ShapeWorkerOptions, Y as WorkerScopeLike, Z as installShapeWorker } from './worker-BZ7plgyI.js';
package/dist/worker.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ installShapeWorker
3
+ } from "./chunk-IFXQIDSA.js";
4
+ export {
5
+ installShapeWorker
6
+ };
7
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "@adatechnology/game-shapes",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "description": "Renderer-agnostic 3D shape generator: a JSON shape schema (primitives, extrusions, lathes, cheap booleans, transforms, groups, named parts, materials by id, LOD rules, instancing hints) in, typed-array buffers per material out. Pure, deterministic by seed, worker friendly and cacheable; the generators are bit-exact ports of three.js r160.",
6
+ "keywords": [
7
+ "3d",
8
+ "geometry",
9
+ "procedural",
10
+ "low-poly",
11
+ "game",
12
+ "json",
13
+ "worker"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/Andersonfrfilho/adatechnology-game-kit.git",
18
+ "directory": "packages/game-shapes"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "type": "module",
24
+ "main": "dist/index.js",
25
+ "module": "dist/index.js",
26
+ "types": "dist/index.d.ts",
27
+ "unpkg": "dist/index.global.js",
28
+ "jsdelivr": "dist/index.global.js",
29
+ "sideEffects": false,
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./worker": {
36
+ "types": "./dist/worker.d.ts",
37
+ "import": "./dist/worker.js"
38
+ },
39
+ "./schema.json": "./schema/shape.schema.json",
40
+ "./global": "./dist/index.global.js"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "schema"
45
+ ],
46
+ "dependencies": {
47
+ "@adatechnology/game-contracts": "^0.22.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.10.7",
51
+ "@types/three": "^0.160.0",
52
+ "three": "^0.160.1",
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^5.7.3",
55
+ "vitest": "^3.2.4"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "typecheck": "tsc -p tsconfig.json --noEmit",
60
+ "test": "vitest run"
61
+ }
6
62
  }