@drawcall/charta 0.1.7 → 0.1.9

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.
@@ -1,21 +1,12 @@
1
- import { Group } from 'three';
1
+ import { LoadingManager, Loader } from 'three';
2
2
  import { Interpreter } from '../interpreter.js';
3
- export type AssetLoaderOptions = {
4
- mock?: boolean;
5
- };
6
- export declare class AssetLoader extends Group {
7
- private interpreter;
8
- private options;
9
- private manager;
3
+ export declare class AssetLoader extends Loader<void, Interpreter> {
4
+ private readonly mock;
10
5
  private textureLoader;
11
6
  private gltfLoader;
12
- private _whenReady;
13
- private readonly pendingFinalizers;
14
- constructor(interpreter: Interpreter, options?: AssetLoaderOptions);
15
- updateMatrixWorld(force?: boolean): void;
7
+ constructor(manager?: LoadingManager, mock?: boolean);
8
+ load(interpreter: Interpreter, onLoad: (data: void) => void, onProgress?: (event: ProgressEvent) => void, onError?: (err: unknown) => void): void;
16
9
  private handleLoadTexture;
17
10
  private handleLoadModel;
18
- whenReady(): Promise<void>;
19
- dispose(): void;
20
11
  }
21
12
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/assets/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA6D,MAAM,OAAO,CAAA;AAExF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAM/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,qBAAa,WAAY,SAAQ,KAAK;IAOxB,OAAO,CAAC,WAAW;IAAe,OAAO,CAAC,OAAO;IAN7D,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwB;gBAEtC,WAAW,EAAE,WAAW,EAAU,OAAO,GAAE,kBAAuB;IAmCtF,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO;YAUnB,iBAAiB;YA8BjB,eAAe;IA6CvB,SAAS;IAIf,OAAO;CAQR"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/assets/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,cAAc,EAAqB,MAAM,EAAW,MAAM,OAAO,CAAA;AAEzG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAU/C,qBAAa,WAAY,SAAQ,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC;IAIlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAH3D,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,UAAU,CAAY;gBAElB,OAAO,CAAC,EAAE,cAAc,EAAmB,IAAI,GAAE,OAAe;IAM5E,IAAI,CACF,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAC5B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EAC3C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAC/B,IAAI;YAuBO,iBAAiB;YA8BjB,eAAe;CAuC9B"}
@@ -1,79 +1,67 @@
1
- import { Group, Texture, TextureLoader, LoadingManager, Object3D, Matrix4 } from 'three';
1
+ import { Texture, TextureLoader, Object3D, Loader, Vector3 } from 'three';
2
2
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
3
3
  import { ChartaError } from '../errors.js';
4
- import { ObjectFactory } from '../place/index.js';
5
4
  import { InstancedMeshGroup } from '../utils/instanced-mesh-group.js';
6
5
  import { object, string } from 'zod';
7
- export class AssetLoader extends Group {
8
- interpreter;
9
- options;
10
- manager;
6
+ import { measureObject, PrefabBatchBuilder } from '../place/index.js';
7
+ // Schemas
8
+ const basePathSchema = object({ path: string() });
9
+ const loadTextureSchema = object({ name: string(), path: string() });
10
+ const loadModelSchema = object({ name: string(), path: string() });
11
+ export class AssetLoader extends Loader {
12
+ mock;
11
13
  textureLoader;
12
14
  gltfLoader;
13
- _whenReady;
14
- pendingFinalizers = [];
15
- constructor(interpreter, options = {}) {
16
- super();
17
- this.interpreter = interpreter;
18
- this.options = options;
19
- this.manager = new LoadingManager();
20
- this.textureLoader = new TextureLoader(this.manager);
21
- this.gltfLoader = new GLTFLoader(this.manager);
22
- const promises = [];
23
- // Schemas
24
- const basePathSchema = object({ path: string() });
25
- const loadTextureSchema = object({ name: string(), path: string() });
26
- const loadModelSchema = object({ name: string(), path: string() });
27
- // Parse meta row calls
15
+ constructor(manager, mock = false) {
16
+ super(manager);
17
+ this.mock = mock;
18
+ this.textureLoader = new TextureLoader(manager);
19
+ this.gltfLoader = new GLTFLoader(manager);
20
+ }
21
+ load(interpreter, onLoad, onProgress, onError) {
28
22
  const calls = interpreter.getCalls(undefined, {
29
23
  basePath: basePathSchema,
30
24
  loadTexture: loadTextureSchema,
31
25
  loadModel: loadModelSchema,
32
26
  });
27
+ const promises = [];
33
28
  let basePath;
34
29
  for (const [key, args, , location] of calls) {
35
30
  if (key === 'basePath') {
36
31
  basePath = args.path.length === 0 ? undefined : args.path;
37
32
  }
38
33
  else if (key === 'loadTexture') {
39
- promises.push(this.handleLoadTexture(args.name, basePath, args.path, location));
34
+ promises.push(this.handleLoadTexture(interpreter, args.name, basePath, args.path, location));
40
35
  }
41
36
  else if (key === 'loadModel') {
42
- promises.push(this.handleLoadModel(args.name, basePath, args.path, location));
43
- }
44
- }
45
- this._whenReady = Promise.all(promises).then(() => { });
46
- }
47
- updateMatrixWorld(force) {
48
- if (this.pendingFinalizers.length > 0) {
49
- for (const finalize of this.pendingFinalizers) {
50
- finalize();
37
+ promises.push(this.handleLoadModel(interpreter, args.name, basePath, args.path, location));
51
38
  }
52
- this.pendingFinalizers.length = 0;
53
39
  }
54
- super.updateMatrixWorld(force);
40
+ Promise.all(promises)
41
+ .then(() => onLoad())
42
+ .catch((reason) => onError?.(reason));
55
43
  }
56
- async handleLoadTexture(name, basePath, path, location) {
57
- if (this.options.mock) {
44
+ async handleLoadTexture(interpreter, name, basePath, path, location) {
45
+ if (this.mock) {
58
46
  const texture = new Texture();
59
47
  texture.name = name;
60
- this.interpreter.setAsset(`${name}BaseColorTexture`, texture);
48
+ interpreter.setAsset(`${name}BaseColorTexture`, texture);
61
49
  return;
62
50
  }
63
51
  const fullPath = new URL(path, basePath).href;
64
52
  try {
65
53
  const texture = await this.textureLoader.loadAsync(fullPath);
66
54
  texture.name = name;
67
- this.interpreter.setAsset(`${name}BaseColorTexture`, texture);
55
+ interpreter.setAsset(`${name}BaseColorTexture`, texture);
68
56
  }
69
57
  catch (err) {
70
- this.interpreter.reportError(new ChartaError(`Failed to load texture "${name}" at "${fullPath}". Is the url correct?`, this.interpreter.getSource(), location));
58
+ interpreter.reportError(new ChartaError(`Failed to load texture "${name}" at "${fullPath}". Is the url correct?`, interpreter.getSource(), location));
71
59
  }
72
60
  }
73
- async handleLoadModel(name, basePath, path, location) {
74
- if (this.options.mock) {
75
- const factory = new ObjectFactory(new Object3D(), () => { });
76
- this.interpreter.setAsset(`${name}Factory`, factory);
61
+ async handleLoadModel(interpreter, name, basePath, path, location) {
62
+ if (this.mock) {
63
+ const batchBuilder = new PrefabBatchBuilder(new Vector3(), () => new Object3D());
64
+ interpreter.setAsset(`${name}Prefab`, batchBuilder);
77
65
  return;
78
66
  }
79
67
  const fullPath = new URL(path, basePath).href;
@@ -84,30 +72,11 @@ export class AssetLoader extends Group {
84
72
  object.castShadow = true;
85
73
  object.receiveShadow = true;
86
74
  });
87
- const matrices = [];
88
- const factory = new ObjectFactory(scene, ({ position, rotation, scale }) => {
89
- matrices.push(new Matrix4().compose(position, rotation, scale));
90
- });
91
- this.interpreter.setAsset(`${name}Factory`, factory);
92
- this.pendingFinalizers.push(() => {
93
- if (matrices.length > 0) {
94
- this.add(new InstancedMeshGroup(scene, matrices.length, matrices));
95
- }
96
- });
75
+ const batchBuilder = new PrefabBatchBuilder(measureObject(scene), (matrices) => new InstancedMeshGroup(scene, matrices.length, matrices));
76
+ interpreter.setAsset(`${name}Prefab`, batchBuilder);
97
77
  }
98
78
  catch (err) {
99
- this.interpreter.reportError(new ChartaError(`Failed to load model ${name} at ${fullPath}: ${err instanceof Error ? err.message : String(err)}`, this.interpreter.getSource(), location));
79
+ interpreter.reportError(new ChartaError(`Failed to load model ${name} at ${fullPath}: ${err instanceof Error ? err.message : String(err)}`, interpreter.getSource(), location));
100
80
  }
101
81
  }
102
- async whenReady() {
103
- return this._whenReady;
104
- }
105
- dispose() {
106
- this.traverse((child) => {
107
- if (child instanceof InstancedMeshGroup) {
108
- child.dispose();
109
- }
110
- });
111
- this.clear();
112
- }
113
82
  }
@@ -1,4 +1,4 @@
1
- import { Group, Material, Object3D, Quaternion, Vector3 } from 'three';
1
+ import { Group, Material, Matrix4, Object3D, Quaternion, Vector3 } from 'three';
2
2
  import { Interpreter } from '../interpreter.js';
3
3
  export type MeasureFunction = (target: Vector3) => void;
4
4
  export type InstantiateFunction = (args: {
@@ -13,25 +13,18 @@ export type InstantiateFunction = (args: {
13
13
  };
14
14
  name: string;
15
15
  }) => void;
16
- export declare class ObjectFactory {
17
- private readonly _measure;
18
- private readonly _instantiate;
19
- constructor(_measure: MeasureFunction | Object3D, _instantiate: InstantiateFunction);
20
- measure(target: Vector3): void;
21
- instantiate(args: {
22
- position: Vector3;
23
- rotation: Quaternion;
24
- scale: Vector3;
25
- interpreter: Interpreter;
26
- cell: {
27
- row: number;
28
- col: number;
29
- center: [number, number];
30
- };
31
- name: string;
32
- }): void;
16
+ export declare function measureObject(object: Object3D): Vector3;
17
+ export declare class PrefabBatchBuilder {
18
+ readonly prefabSize: Vector3;
19
+ private readonly buildFromMatrices;
20
+ private matrices;
21
+ constructor(prefabSize: Vector3, buildFromMatrices: (matrices: Array<Matrix4>) => Object3D);
22
+ add(matrix: Matrix4): this;
23
+ build(): Object3D;
33
24
  }
34
25
  export declare class PlaceGroup extends Group {
26
+ private batchBuilders;
35
27
  constructor(interpreter: Interpreter, _materialFallback?: Material);
28
+ dispose(): void;
36
29
  }
37
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/place/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAW,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAQ,MAAM,OAAO,CAAA;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAmC/C,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;AAEvD,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IAC5D,IAAI,EAAE,MAAM,CAAA;CACb,KAAK,IAAI,CAAA;AAIV,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBADZ,QAAQ,EAAE,eAAe,GAAG,QAAQ,EACpC,YAAY,EAAE,mBAAmB;IAGpD,OAAO,CAAC,MAAM,EAAE,OAAO;IAWvB,WAAW,CAAC,IAAI,EAAE;QAChB,QAAQ,EAAE,OAAO,CAAA;QACjB,QAAQ,EAAE,UAAU,CAAA;QACpB,KAAK,EAAE,OAAO,CAAA;QACd,WAAW,EAAE,WAAW,CAAA;QACxB,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;SAAE,CAAA;QAC5D,IAAI,EAAE,MAAM,CAAA;KACb;CAGF;AAED,qBAAa,UAAW,SAAQ,KAAK;gBACvB,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ;CA0NnE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/place/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAQ,MAAM,OAAO,CAAA;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAmC/C,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;AAEvD,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,UAAU,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IAC5D,IAAI,EAAE,MAAM,CAAA;CACb,KAAK,IAAI,CAAA;AAKV,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAOvD;AAED,qBAAa,kBAAkB;aAIX,UAAU,EAAE,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAJpC,OAAO,CAAC,QAAQ,CAAqB;gBAGnB,UAAU,EAAE,OAAO,EAClB,iBAAiB,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,QAAQ;IAG5E,GAAG,CAAC,MAAM,EAAE,OAAO;IAKZ,KAAK,IAAI,QAAQ;CAKzB;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,OAAO,CAAC,aAAa,CAAgC;gBAEzC,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ;IAqNlE,OAAO;CAOR"}
@@ -1,4 +1,4 @@
1
- import { Group, Matrix4, Object3D, Quaternion, Vector3, Box3 } from 'three';
1
+ import { Group, Matrix4, Quaternion, Vector3, Box3 } from 'three';
2
2
  import { TilesGeometry } from '../tiles/geometry.js';
3
3
  import { coerce, object, string, enum as zodEnum } from 'zod';
4
4
  import { createRng, hashNumbersToUint32, hashStringToUint32 } from '../utils/random.js';
@@ -30,28 +30,35 @@ const placeSchema = object({
30
30
  sizeZ: coerce.number().optional(),
31
31
  });
32
32
  const boxHelper = new Box3();
33
- export class ObjectFactory {
34
- _measure;
35
- _instantiate;
36
- constructor(_measure, _instantiate) {
37
- this._measure = _measure;
38
- this._instantiate = _instantiate;
33
+ const matrixHelper = new Matrix4();
34
+ export function measureObject(object) {
35
+ const parent = object.parent;
36
+ object.parent = null;
37
+ const target = new Vector3();
38
+ boxHelper.setFromObject(object).getSize(target);
39
+ object.parent = parent;
40
+ return target;
41
+ }
42
+ export class PrefabBatchBuilder {
43
+ prefabSize;
44
+ buildFromMatrices;
45
+ matrices = [];
46
+ constructor(prefabSize, buildFromMatrices) {
47
+ this.prefabSize = prefabSize;
48
+ this.buildFromMatrices = buildFromMatrices;
39
49
  }
40
- measure(target) {
41
- if (this._measure instanceof Object3D) {
42
- const parent = this._measure.parent;
43
- this._measure.parent = null;
44
- boxHelper.setFromObject(this._measure).getSize(target);
45
- this._measure.parent = parent;
46
- return;
47
- }
48
- this._measure(target);
50
+ add(matrix) {
51
+ this.matrices.push(matrix.clone());
52
+ return this;
49
53
  }
50
- instantiate(args) {
51
- this._instantiate(args);
54
+ build() {
55
+ const batch = this.buildFromMatrices(this.matrices);
56
+ this.matrices = [];
57
+ return batch;
52
58
  }
53
59
  }
54
60
  export class PlaceGroup extends Group {
61
+ batchBuilders = new Set();
55
62
  constructor(interpreter, _materialFallback) {
56
63
  super();
57
64
  const rows = interpreter.getRows();
@@ -120,12 +127,13 @@ export class PlaceGroup extends Group {
120
127
  // scatter: randomized distribution, density is count for now
121
128
  for (const [, parsed, callIdx, loc] of scatterEntries) {
122
129
  const layerIndex = interpreter.countCalls([z, x], (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
123
- const factoryName = `${parsed.model}Factory`;
124
- const factory = interpreter.getAsset(ObjectFactory, factoryName, loc);
125
- if (!factory)
130
+ const prefabName = `${parsed.model}Prefab`;
131
+ const batchBuilder = interpreter.getAsset(PrefabBatchBuilder, prefabName, loc);
132
+ if (!batchBuilder)
126
133
  continue;
134
+ this.batchBuilders.add(batchBuilder);
127
135
  const usualSize = new Vector3();
128
- factory.measure(usualSize);
136
+ usualSize.copy(batchBuilder.prefabSize);
129
137
  const base = hashStringToUint32(parsed.model);
130
138
  const seed = hashNumbersToUint32(base, z, x);
131
139
  const rng = createRng(seed);
@@ -168,14 +176,7 @@ export class PlaceGroup extends Group {
168
176
  : minSizeZ ?? maxSizeZ;
169
177
  const scale = computeScale(usualSize, sX, sZ);
170
178
  scl.multiply(scale);
171
- factory.instantiate({
172
- position: pos,
173
- rotation: quat,
174
- scale: scl,
175
- interpreter,
176
- cell: { row: z, col: x, center: [cx, cz] },
177
- name: parsed.model,
178
- });
179
+ batchBuilder.add(matrixHelper.compose(pos, quat, scl));
179
180
  }
180
181
  }
181
182
  // place: exact in-cell placement
@@ -193,24 +194,28 @@ export class PlaceGroup extends Group {
193
194
  fixedY: parsed.bottomY,
194
195
  layerIndex,
195
196
  });
196
- const factoryName = `${parsed.model}Factory`;
197
- const factory = interpreter.getAsset(ObjectFactory, factoryName, loc);
198
- if (!factory)
197
+ const prefabName = `${parsed.model}Prefab`;
198
+ const batchBuilder = interpreter.getAsset(PrefabBatchBuilder, prefabName, loc);
199
+ if (!batchBuilder)
199
200
  continue;
201
+ this.batchBuilders.add(batchBuilder);
200
202
  const usualSize = new Vector3();
201
- factory.measure(usualSize);
203
+ usualSize.copy(batchBuilder.prefabSize);
202
204
  const scale = computeScale(usualSize, parsed.sizeX, parsed.sizeZ);
203
205
  scl.multiply(scale);
204
- factory.instantiate({
205
- position: pos,
206
- rotation: quat,
207
- scale: scl,
208
- interpreter,
209
- cell: { row: z, col: x, center: [cx, cz] },
210
- name: parsed.model,
211
- });
206
+ batchBuilder.add(matrixHelper.compose(pos, quat, scl));
212
207
  }
213
208
  }
214
209
  }
210
+ for (const batchBuilder of this.batchBuilders) {
211
+ this.add(batchBuilder.build());
212
+ }
213
+ }
214
+ dispose() {
215
+ for (const child of this.children) {
216
+ if ('dispose' in child && typeof child.dispose === 'function') {
217
+ child.dispose();
218
+ }
219
+ }
215
220
  }
216
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawcall/charta",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "author": "Bela Bohlender",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://drawcall.ai",