@drawcall/charta 0.1.18 → 0.1.20
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/assets/loader.d.ts +12 -5
- package/dist/assets/loader.d.ts.map +1 -1
- package/dist/assets/loader.js +15 -9
- package/dist/grass/index.d.ts +2 -2
- package/dist/grass/index.d.ts.map +1 -1
- package/dist/grass/index.js +4 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/parser.d.ts +3 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +34 -24
- package/dist/pillars/index.d.ts +2 -2
- package/dist/pillars/index.d.ts.map +1 -1
- package/dist/pillars/index.js +52 -44
- package/dist/place/index.d.ts +2 -2
- package/dist/place/index.d.ts.map +1 -1
- package/dist/place/index.js +12 -12
- package/dist/tiles/index.d.ts +2 -2
- package/dist/tiles/index.d.ts.map +1 -1
- package/dist/tiles/index.js +8 -4
- package/dist/utils/bvh.d.ts +11 -0
- package/dist/utils/bvh.d.ts.map +1 -0
- package/dist/utils/bvh.js +20 -0
- package/dist/utils/instanced-mesh-group.d.ts.map +1 -1
- package/dist/utils/instanced-mesh-group.js +6 -0
- package/dist/walls/index.d.ts +3 -3
- package/dist/walls/index.d.ts.map +1 -1
- package/dist/walls/index.js +56 -44
- package/dist/water/index.d.ts +2 -2
- package/dist/water/index.d.ts.map +1 -1
- package/dist/water/index.js +29 -24
- package/package.json +2 -1
package/dist/assets/loader.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { LoadingManager, Loader } from 'three';
|
|
1
|
+
import { LoadingManager, Loader, Vector3 } from 'three';
|
|
2
2
|
import { Interpreter } from '../interpreter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Function type for measuring a GLB model's bounding box size.
|
|
5
|
+
* Takes a URL and returns a Vector3 with the model's dimensions (x, y, z).
|
|
6
|
+
*/
|
|
7
|
+
export type MeasureGlbFunction = (url: string) => Promise<Vector3>;
|
|
3
8
|
export type AssetLoaderOptions = {
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* Measure-only mode: uses the provided function to measure model sizes,
|
|
11
|
+
* then creates lightweight PrefabBatchBuilders with correct sizes.
|
|
12
|
+
* The models themselves are not kept in memory.
|
|
13
|
+
* Useful for server-side validation where model sizes are needed.
|
|
7
14
|
*/
|
|
8
|
-
|
|
15
|
+
measureModelsOnlyWith?: MeasureGlbFunction;
|
|
9
16
|
/**
|
|
10
|
-
* Skip texture loading entirely. When true, loadTexture calls
|
|
17
|
+
* Skip texture loading entirely. When true, loadTexture calls create placeholder textures.
|
|
11
18
|
* Default: false.
|
|
12
19
|
*/
|
|
13
20
|
skipTextureLoading?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/assets/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,cAAc,EAAqB,MAAM,
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/assets/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,cAAc,EAAqB,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAEzG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAM/C;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,kBAAkB,CAAA;IAC1C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAED,qBAAa,WAAY,SAAQ,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC;IACxD,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,CAAC,EAAE,cAAc,EAAE,OAAO,GAAE,kBAAuB;IAOtE,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;CAqD9B"}
|
package/dist/assets/loader.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Texture, TextureLoader, Object3D, Loader
|
|
1
|
+
import { Texture, TextureLoader, Object3D, Loader } from 'three';
|
|
2
2
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
3
3
|
import { ChartaError } from '../errors.js';
|
|
4
4
|
import { InstancedMeshGroup } from '../utils/instanced-mesh-group.js';
|
|
@@ -27,9 +27,7 @@ export class AssetLoader extends Loader {
|
|
|
27
27
|
basePath = args.path.length === 0 ? undefined : args.path;
|
|
28
28
|
}
|
|
29
29
|
else if (key === 'loadTexture') {
|
|
30
|
-
|
|
31
|
-
promises.push(this.handleLoadTexture(interpreter, args.name, basePath, args.path, location));
|
|
32
|
-
}
|
|
30
|
+
promises.push(this.handleLoadTexture(interpreter, args.name, basePath, args.path, location));
|
|
33
31
|
}
|
|
34
32
|
else if (key === 'loadModel') {
|
|
35
33
|
promises.push(this.handleLoadModel(interpreter, args.name, basePath, args.path, location));
|
|
@@ -40,7 +38,7 @@ export class AssetLoader extends Loader {
|
|
|
40
38
|
.catch((reason) => onError?.(reason));
|
|
41
39
|
}
|
|
42
40
|
async handleLoadTexture(interpreter, name, basePath, path, location) {
|
|
43
|
-
if (this.options.
|
|
41
|
+
if (this.options.skipTextureLoading) {
|
|
44
42
|
const texture = new Texture();
|
|
45
43
|
texture.name = name;
|
|
46
44
|
interpreter.setAsset(`${name}BaseColorTexture`, texture);
|
|
@@ -57,12 +55,20 @@ export class AssetLoader extends Loader {
|
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
async handleLoadModel(interpreter, name, basePath, path, location) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
const fullPath = new URL(path, basePath).href;
|
|
59
|
+
// Measure-only mode: use custom function to get size, don't load full model
|
|
60
|
+
if (this.options.measureModelsOnlyWith) {
|
|
61
|
+
try {
|
|
62
|
+
const size = await this.options.measureModelsOnlyWith(fullPath);
|
|
63
|
+
const batchBuilder = new PrefabBatchBuilder(size, () => new Object3D());
|
|
64
|
+
interpreter.setAsset(`${name}Prefab`, batchBuilder);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
interpreter.reportError(new ChartaError(`Failed to measure model ${name} at ${fullPath}: ${err instanceof Error ? err.message : String(err)}`, interpreter.getSource(), location));
|
|
68
|
+
}
|
|
63
69
|
return;
|
|
64
70
|
}
|
|
65
|
-
|
|
71
|
+
// Full mode: load model with Three.js (browser only)
|
|
66
72
|
try {
|
|
67
73
|
const gltf = await this.gltfLoader.loadAsync(fullPath);
|
|
68
74
|
const scene = gltf.scene;
|
package/dist/grass/index.d.ts
CHANGED
|
@@ -19,10 +19,10 @@ export type GrassOptions = GrassMaterialOptions & {
|
|
|
19
19
|
*/
|
|
20
20
|
variationStrength?: number;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Validate-only mode: validates grass() calls exist but skips expensive blade generation.
|
|
23
23
|
* Useful for server-side validation. Default: false.
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
validateOnly?: boolean;
|
|
26
26
|
};
|
|
27
27
|
export declare class GrassMesh extends InstancedMesh {
|
|
28
28
|
constructor(interpreter: Interpreter, material?: Material, opts?: GrassOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grass/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,aAAa,EACb,QAAQ,EAMT,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO9E,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grass/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,aAAa,EACb,QAAQ,EAMT,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO9E,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,qBAAa,SAAU,SAAQ,aAAa;gBAExC,WAAW,EAAE,WAAW,EACxB,QAAQ,GAAE,QAAkC,EAC5C,IAAI,GAAE,YAAiB;CAuP1B"}
|
package/dist/grass/index.js
CHANGED
|
@@ -6,8 +6,8 @@ const UpVector = new Vector3(0, 1, 0);
|
|
|
6
6
|
const colorHelper = new Color();
|
|
7
7
|
export class GrassMesh extends InstancedMesh {
|
|
8
8
|
constructor(interpreter, material = new MeshPhongMaterial(), opts = {}) {
|
|
9
|
-
//
|
|
10
|
-
if (opts.
|
|
9
|
+
// Validate-only mode: validate grass calls exist, skip geometry allocation entirely
|
|
10
|
+
if (opts.validateOnly) {
|
|
11
11
|
super(undefined, material, 0);
|
|
12
12
|
// Still validate all grass() calls to catch errors
|
|
13
13
|
const rows = interpreter.getRows();
|
|
@@ -182,5 +182,7 @@ export class GrassMesh extends InstancedMesh {
|
|
|
182
182
|
this.instanceMatrix.needsUpdate = true;
|
|
183
183
|
if (this.instanceColor)
|
|
184
184
|
this.instanceColor.needsUpdate = true;
|
|
185
|
+
// Disable raycasting entirely for grass (no-op)
|
|
186
|
+
this.raycast = () => { };
|
|
185
187
|
}
|
|
186
188
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,6 @@ export * from "./pillars/index.js";
|
|
|
10
10
|
export * from "./water/index.js";
|
|
11
11
|
export * from "./locations.js";
|
|
12
12
|
export * from "./utils/instanced-mesh-group.js";
|
|
13
|
+
export * from "./utils/bvh.js";
|
|
13
14
|
export * from "./assets/loader.js";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iCAAiC,CAAA;AAC/C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/parser.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChartaError } from "./errors.js";
|
|
1
2
|
export type KeyParam = [key: string, value: string];
|
|
2
3
|
export type Call = {
|
|
3
4
|
name: string;
|
|
@@ -5,5 +6,7 @@ export type Call = {
|
|
|
5
6
|
keyParams: KeyParam[];
|
|
6
7
|
offset: number;
|
|
7
8
|
};
|
|
9
|
+
export type ParseResult = [result: Call[][][], error: null] | [result: null, error: ChartaError];
|
|
10
|
+
export declare function safeParse(input: string): ParseResult;
|
|
8
11
|
export declare function parse(input: string): Call[][][];
|
|
9
12
|
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AACpD,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAIF,MAAM,MAAM,WAAW,GACnB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GACjC,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;AAYvC,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAqDpD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,CAI/C"}
|
package/dist/parser.js
CHANGED
|
@@ -2,7 +2,16 @@ import nearley from "nearley";
|
|
|
2
2
|
const { Parser, Grammar } = nearley;
|
|
3
3
|
import grammar from "./grammar.js";
|
|
4
4
|
import { ChartaError } from "./errors.js";
|
|
5
|
-
|
|
5
|
+
function getLocation(input, offset) {
|
|
6
|
+
const clampedOffset = Math.max(0, Math.min(offset, input.length));
|
|
7
|
+
const prefix = input.slice(0, clampedOffset);
|
|
8
|
+
const lines = prefix.split("\n");
|
|
9
|
+
return {
|
|
10
|
+
line: lines.length,
|
|
11
|
+
column: lines[lines.length - 1].length + 1,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function safeParse(input) {
|
|
6
15
|
const normalized = input.replace(/\r\n?/g, "\n");
|
|
7
16
|
const parser = new Parser(Grammar.fromCompiled(grammar));
|
|
8
17
|
let rows;
|
|
@@ -10,38 +19,39 @@ export function parse(input) {
|
|
|
10
19
|
rows = parser.feed(normalized).finish()[0];
|
|
11
20
|
}
|
|
12
21
|
catch (error) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
const offset = typeof error.offset === "number" ? error.offset : 0;
|
|
23
|
+
return [
|
|
24
|
+
null,
|
|
25
|
+
new ChartaError(error.message || "Parse error", normalized, getLocation(normalized, offset)),
|
|
26
|
+
];
|
|
17
27
|
}
|
|
18
28
|
if (!rows || rows.length === 0)
|
|
19
|
-
return [];
|
|
20
|
-
// Validate Meta Row (first row)
|
|
29
|
+
return [[], null];
|
|
30
|
+
// Validate Meta Row (first row must contain exactly one cell)
|
|
21
31
|
if (rows[0].length !== 1) {
|
|
22
|
-
|
|
32
|
+
return [
|
|
33
|
+
null,
|
|
34
|
+
new ChartaError(`First row must contain exactly one cell (meta row), found ${rows[0].length}`, normalized, getLocation(normalized, rows[0].offset)),
|
|
35
|
+
];
|
|
23
36
|
}
|
|
24
|
-
// Validate Grid Rows (
|
|
25
|
-
// All grid rows must have the same number of cells as the first grid row
|
|
37
|
+
// Validate Grid Rows (all must have same width as first grid row)
|
|
26
38
|
if (rows.length > 1) {
|
|
27
39
|
const gridRows = rows.slice(1);
|
|
28
40
|
const expectedWidth = gridRows[0].length;
|
|
29
|
-
const badRowIdx = gridRows.findIndex(r => r.length !== expectedWidth);
|
|
41
|
+
const badRowIdx = gridRows.findIndex((r) => r.length !== expectedWidth);
|
|
30
42
|
if (badRowIdx !== -1) {
|
|
31
|
-
|
|
43
|
+
const badRow = gridRows[badRowIdx];
|
|
44
|
+
return [
|
|
45
|
+
null,
|
|
46
|
+
new ChartaError(`Row ${badRowIdx + 2} has ${badRow.length} cells; expected ${expectedWidth}`, normalized, getLocation(normalized, badRow.offset)),
|
|
47
|
+
];
|
|
32
48
|
}
|
|
33
49
|
}
|
|
34
|
-
return rows;
|
|
50
|
+
return [rows, null];
|
|
35
51
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const prefix = input.slice(0, clampedOffset);
|
|
42
|
-
const lines = prefix.split('\n');
|
|
43
|
-
return {
|
|
44
|
-
line: lines.length,
|
|
45
|
-
column: lines[lines.length - 1].length + 1
|
|
46
|
-
};
|
|
52
|
+
export function parse(input) {
|
|
53
|
+
const [result, error] = safeParse(input);
|
|
54
|
+
if (error)
|
|
55
|
+
throw error;
|
|
56
|
+
return result;
|
|
47
57
|
}
|
package/dist/pillars/index.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { Interpreter } from '../interpreter.js';
|
|
|
3
3
|
export type PillarCorner = 'topleft' | 'topright' | 'bottomright' | 'bottomleft';
|
|
4
4
|
export type PillarMeshOptions = {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Validate-only mode: validates pillar calls but skips geometry creation.
|
|
7
7
|
* Useful for server-side validation. Default: false.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
validateOnly?: boolean;
|
|
10
10
|
};
|
|
11
11
|
export declare class PillarMesh extends InstancedMesh {
|
|
12
12
|
constructor(interpreter: Interpreter, material?: Material, options?: PillarMeshOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pillars/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,QAAQ,EAMT,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pillars/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,QAAQ,EAMT,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAQ/C,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,CAAA;AAYhF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,qBAAa,UAAW,SAAQ,aAAa;gBAC/B,WAAW,EAAE,WAAW,EAAE,QAAQ,GAAE,QAAkC,EAAE,OAAO,GAAE,iBAAsB;CA4LpH"}
|
package/dist/pillars/index.js
CHANGED
|
@@ -4,34 +4,22 @@ import { buildTextureArrayFromAssets } from '../utils/texture.js';
|
|
|
4
4
|
import { buildPillarMeshMaterial } from './material.js';
|
|
5
5
|
import { ChartaError } from '../errors.js';
|
|
6
6
|
import { pillarSchema } from '../schemas.js';
|
|
7
|
+
import { setupBVH, acceleratedRaycast } from '../utils/bvh.js';
|
|
7
8
|
export class PillarMesh extends InstancedMesh {
|
|
8
9
|
constructor(interpreter, material = new MeshBasicMaterial(), options = {}) {
|
|
9
10
|
const rows = interpreter.getRows();
|
|
10
11
|
const cols = interpreter.getCols();
|
|
11
12
|
const cellSize = interpreter.getCellSize();
|
|
12
|
-
//
|
|
13
|
-
if (options.mock) {
|
|
14
|
-
// Use undefined geometry with 0 instances - no memory allocation
|
|
15
|
-
super(undefined, material, 0);
|
|
16
|
-
for (let row = 0; row < rows; row++) {
|
|
17
|
-
for (let col = 0; col < cols; col++) {
|
|
18
|
-
const entries = interpreter.getCalls([row, col], { pillar: pillarSchema });
|
|
19
|
-
for (const [, parsed, , loc] of entries) {
|
|
20
|
-
// Validate texture reference
|
|
21
|
-
interpreter.getAsset(Texture, `${parsed.texture}BaseColorTexture`, loc);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
// Collect pillar instances with resolved heights
|
|
13
|
+
// Collect pillar instances only in non-validateOnly mode
|
|
28
14
|
const pillars = [];
|
|
29
|
-
// tilesGeometry is only required for height sampling; fetch lazily where needed
|
|
30
15
|
const usedTextures = [];
|
|
31
|
-
const getTextureId = (name) => {
|
|
32
|
-
const texture = interpreter.getAsset(Texture, `${name}BaseColorTexture
|
|
16
|
+
const getTextureId = (name, loc) => {
|
|
17
|
+
const texture = interpreter.getAsset(Texture, `${name}BaseColorTexture`, loc);
|
|
33
18
|
if (!texture)
|
|
34
19
|
return 0;
|
|
20
|
+
// Only track textures in non-validateOnly mode
|
|
21
|
+
if (options.validateOnly)
|
|
22
|
+
return 0;
|
|
35
23
|
let idx = usedTextures.indexOf(texture);
|
|
36
24
|
if (idx === -1) {
|
|
37
25
|
idx = usedTextures.length;
|
|
@@ -61,6 +49,7 @@ export class PillarMesh extends InstancedMesh {
|
|
|
61
49
|
}
|
|
62
50
|
}
|
|
63
51
|
}
|
|
52
|
+
// Validation loop runs in both modes
|
|
64
53
|
for (let row = 0; row < rows; row++) {
|
|
65
54
|
for (let col = 0; col < cols; col++) {
|
|
66
55
|
const entries = interpreter.getCalls([row, col], {
|
|
@@ -76,11 +65,13 @@ export class PillarMesh extends InstancedMesh {
|
|
|
76
65
|
const [cx, cz] = interpreter.getWorldCellCenter(row, col);
|
|
77
66
|
const offsetX = px - cx;
|
|
78
67
|
const offsetZ = pz - cz;
|
|
79
|
-
|
|
68
|
+
// Validate texture reference (runs in both modes)
|
|
69
|
+
const textureId = getTextureId(parsed.texture, loc);
|
|
80
70
|
// Derive default layers relative to call position (like walls)
|
|
81
71
|
const isLayer = (c) => c.name === 'ground' || c.name === 'ceiling';
|
|
82
72
|
let bottomY = parsed.bottomY;
|
|
83
73
|
let topY = parsed.topY;
|
|
74
|
+
// Validate layer existence (runs in both modes)
|
|
84
75
|
let bottomStackIndex;
|
|
85
76
|
if (bottomY == null) {
|
|
86
77
|
const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
|
|
@@ -89,10 +80,13 @@ export class PillarMesh extends InstancedMesh {
|
|
|
89
80
|
interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing bottomY and no preceding layer`, interpreter.getSource(), loc));
|
|
90
81
|
continue;
|
|
91
82
|
}
|
|
92
|
-
|
|
93
|
-
if (!
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
// Height sampling only in non-validateOnly mode
|
|
84
|
+
if (!options.validateOnly) {
|
|
85
|
+
const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
|
|
86
|
+
if (!tilesGeometry)
|
|
87
|
+
continue;
|
|
88
|
+
bottomY = tilesGeometry.getHeight(row, col, bottomStackIndex, offsetX, offsetZ);
|
|
89
|
+
}
|
|
96
90
|
}
|
|
97
91
|
let topStackIndex;
|
|
98
92
|
if (topY == null) {
|
|
@@ -101,31 +95,42 @@ export class PillarMesh extends InstancedMesh {
|
|
|
101
95
|
interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing topY and no subsequent layer`, interpreter.getSource(), loc));
|
|
102
96
|
continue;
|
|
103
97
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
// Height sampling only in non-validateOnly mode
|
|
99
|
+
if (!options.validateOnly) {
|
|
100
|
+
const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
|
|
101
|
+
topStackIndex = layersBefore;
|
|
102
|
+
const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
|
|
103
|
+
if (!tilesGeometry)
|
|
104
|
+
continue;
|
|
105
|
+
topY = tilesGeometry.getHeight(row, col, topStackIndex, offsetX, offsetZ);
|
|
106
|
+
}
|
|
110
107
|
}
|
|
111
|
-
//
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
bottomY
|
|
115
|
-
|
|
108
|
+
// Only collect pillar data in non-validateOnly mode
|
|
109
|
+
if (!options.validateOnly) {
|
|
110
|
+
// Support "negative pillars" by swapping
|
|
111
|
+
if (topY < bottomY) {
|
|
112
|
+
const tmp = bottomY;
|
|
113
|
+
bottomY = topY;
|
|
114
|
+
topY = tmp;
|
|
115
|
+
}
|
|
116
|
+
pillars.push({
|
|
117
|
+
x: px,
|
|
118
|
+
z: pz,
|
|
119
|
+
sizeX,
|
|
120
|
+
sizeZ,
|
|
121
|
+
bottomY: bottomY,
|
|
122
|
+
topY: topY,
|
|
123
|
+
textureId,
|
|
124
|
+
});
|
|
116
125
|
}
|
|
117
|
-
pillars.push({
|
|
118
|
-
x: px,
|
|
119
|
-
z: pz,
|
|
120
|
-
sizeX,
|
|
121
|
-
sizeZ,
|
|
122
|
-
bottomY: bottomY,
|
|
123
|
-
topY: topY,
|
|
124
|
-
textureId,
|
|
125
|
-
});
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
// ValidateOnly mode: skip geometry creation
|
|
130
|
+
if (options.validateOnly) {
|
|
131
|
+
super(undefined, material, 0);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
129
134
|
// Base cube with base at y=0 for easy scaling
|
|
130
135
|
const geometry = new BoxGeometry(1, 1, 1);
|
|
131
136
|
geometry.translate(0, 0.5, 0);
|
|
@@ -156,5 +161,8 @@ export class PillarMesh extends InstancedMesh {
|
|
|
156
161
|
const textureArray = buildTextureArrayFromAssets(usedTextures);
|
|
157
162
|
buildPillarMeshMaterial(this.material, textureArray);
|
|
158
163
|
}
|
|
164
|
+
// Setup BVH for accelerated raycasting
|
|
165
|
+
setupBVH(this.geometry);
|
|
166
|
+
this.raycast = acceleratedRaycast;
|
|
159
167
|
}
|
|
160
168
|
}
|
package/dist/place/index.d.ts
CHANGED
|
@@ -24,10 +24,10 @@ export declare class PrefabBatchBuilder {
|
|
|
24
24
|
}
|
|
25
25
|
export type PlaceGroupOptions = {
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Validate-only mode: validates scatter/place calls and assets but skips expensive transform
|
|
28
28
|
* computation and instance generation. Useful for server-side validation. Default: false.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
validateOnly?: boolean;
|
|
31
31
|
skipModelInstantiation?: boolean;
|
|
32
32
|
};
|
|
33
33
|
export declare class PlaceGroup extends Group {
|
|
@@ -1 +1 @@
|
|
|
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;AAO/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,SAAgB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAK;gBAG3B,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;CAGzB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,
|
|
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;AAO/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,SAAgB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAK;gBAG3B,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;CAGzB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC,CAAA;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,SAAgB,aAAa,kCAAwC;gBAEzD,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ,EAAE,IAAI,GAAE,iBAAsB;IAuPhG,OAAO;CAOR"}
|
package/dist/place/index.js
CHANGED
|
@@ -34,14 +34,14 @@ export class PlaceGroup extends Group {
|
|
|
34
34
|
batchBuilders = new Map();
|
|
35
35
|
constructor(interpreter, _materialFallback, opts = {}) {
|
|
36
36
|
super();
|
|
37
|
-
const {
|
|
37
|
+
const { validateOnly = false, skipModelInstantiation = false } = opts;
|
|
38
38
|
const rows = interpreter.getRows();
|
|
39
39
|
const cols = interpreter.getCols();
|
|
40
40
|
const cellSize = interpreter.getCellSize();
|
|
41
|
-
// Only get tilesGeometry if not in
|
|
42
|
-
const tilesGeometry =
|
|
43
|
-
// Return early if not
|
|
44
|
-
if (!
|
|
41
|
+
// Only get tilesGeometry if not in validateOnly mode (needed for height calculations)
|
|
42
|
+
const tilesGeometry = validateOnly ? null : interpreter.getAsset(TilesGeometry, 'tilesGeometry');
|
|
43
|
+
// Return early if not validateOnly and tilesGeometry is missing
|
|
44
|
+
if (!validateOnly && !tilesGeometry) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
const computeScale = (usualSize, sizeX, sizeZ) => {
|
|
@@ -109,8 +109,8 @@ export class PlaceGroup extends Group {
|
|
|
109
109
|
const batchBuilder = interpreter.getAsset(PrefabBatchBuilder, prefabName, loc);
|
|
110
110
|
if (!batchBuilder)
|
|
111
111
|
continue;
|
|
112
|
-
// In
|
|
113
|
-
if (
|
|
112
|
+
// In validateOnly mode, we only validate asset existence - skip expensive placement computation
|
|
113
|
+
if (validateOnly)
|
|
114
114
|
continue;
|
|
115
115
|
this.batchBuilders.set(parsed.model, batchBuilder);
|
|
116
116
|
const usualSize = new Vector3();
|
|
@@ -176,7 +176,7 @@ export class PlaceGroup extends Group {
|
|
|
176
176
|
const usualSize = batchBuilder.prefabSize;
|
|
177
177
|
const px = cx + (parsed.offsetX ?? 0);
|
|
178
178
|
const pz = cz + (parsed.offsetZ ?? 0);
|
|
179
|
-
// Compute scale for footprint validation (needed for both
|
|
179
|
+
// Compute scale for footprint validation (needed for both validateOnly and normal mode)
|
|
180
180
|
const scale = computeScale(usualSize, parsed.sizeX, parsed.sizeZ);
|
|
181
181
|
const halfX = (usualSize.x * scale.x) / 2;
|
|
182
182
|
const halfZ = (usualSize.z * scale.z) / 2;
|
|
@@ -186,8 +186,8 @@ export class PlaceGroup extends Group {
|
|
|
186
186
|
interpreter.reportError(new ChartaError(`location is inside place(${parsed.model}) footprint`, interpreter.getSource(), loc));
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
-
// In
|
|
190
|
-
if (
|
|
189
|
+
// In validateOnly mode, skip expensive transform computation and placement
|
|
190
|
+
if (validateOnly)
|
|
191
191
|
continue;
|
|
192
192
|
this.batchBuilders.set(parsed.model, batchBuilder);
|
|
193
193
|
const layerIndex = interpreter.countCalls([row, col], (c) => c.name === 'ground' || c.name === 'ceiling', 0, callIdx) - 1;
|
|
@@ -205,8 +205,8 @@ export class PlaceGroup extends Group {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
// Build step: skip if
|
|
209
|
-
if (!
|
|
208
|
+
// Build step: skip if validateOnly or skipBuild is enabled
|
|
209
|
+
if (!validateOnly && !skipModelInstantiation) {
|
|
210
210
|
for (const batchBuilder of this.batchBuilders.values()) {
|
|
211
211
|
this.add(batchBuilder.build());
|
|
212
212
|
}
|
package/dist/tiles/index.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ import { groundSchema, ceilingSchema } from '../schemas.js';
|
|
|
5
5
|
export { groundSchema, ceilingSchema };
|
|
6
6
|
export type TilesMeshOptions = {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Validate-only mode: validates ground/ceiling/wall calls but skips geometry creation.
|
|
9
9
|
* Useful for server-side validation. Default: false.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
validateOnly?: boolean;
|
|
12
12
|
skipTextureLoading?: boolean;
|
|
13
13
|
};
|
|
14
14
|
export declare class TilesMesh extends Mesh<TilesGeometry> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tiles/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,IAAI,EAOL,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAuB,MAAM,eAAe,CAAA;AAIlE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAc,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tiles/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,IAAI,EAOL,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAuB,MAAM,eAAe,CAAA;AAIlE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAc,MAAM,eAAe,CAAA;AAGvE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AAEtC,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AAyCD,qBAAa,SAAU,SAAQ,IAAI,CAAC,aAAa,CAAC;gBACpC,WAAW,EAAE,WAAW,EAAE,QAAQ,GAAE,QAAkC,EAAE,OAAO,GAAE,gBAAqB;CAqInH"}
|
package/dist/tiles/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { buildTilesMaterial } from './material.js';
|
|
|
4
4
|
import { ChartaError } from '../errors.js';
|
|
5
5
|
import { buildTextureArrayFromAssets } from '../utils/texture.js';
|
|
6
6
|
import { groundSchema, ceilingSchema, wallSchema } from '../schemas.js';
|
|
7
|
+
import { setupBVH, acceleratedRaycast } from '../utils/bvh.js';
|
|
7
8
|
export { groundSchema, ceilingSchema };
|
|
8
9
|
function resolveHeights(position, items, interpreter, reverse) {
|
|
9
10
|
items = [...items];
|
|
@@ -34,9 +35,9 @@ export class TilesMesh extends Mesh {
|
|
|
34
35
|
constructor(interpreter, material = new MeshBasicMaterial(), options = {}) {
|
|
35
36
|
const rows = interpreter.getRows();
|
|
36
37
|
const cols = interpreter.getCols();
|
|
37
|
-
const {
|
|
38
|
-
const createGeometry = !
|
|
39
|
-
const createMaterial = !
|
|
38
|
+
const { validateOnly = false, skipTextureLoading = false } = options;
|
|
39
|
+
const createGeometry = !validateOnly;
|
|
40
|
+
const createMaterial = !validateOnly && !skipTextureLoading;
|
|
40
41
|
// Texture index tracking (only used when creating materials)
|
|
41
42
|
const textureIndex = new Map();
|
|
42
43
|
const getTextureIndex = (name, loc) => {
|
|
@@ -111,7 +112,7 @@ export class TilesMesh extends Mesh {
|
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
else if (!skipTextureLoading) {
|
|
114
|
-
// Validate texture exists in
|
|
115
|
+
// Validate texture exists in validateOnly mode (unless mockTexture is set)
|
|
115
116
|
interpreter.getAsset(Texture, `${parsed.texture}BaseColorTexture`, loc);
|
|
116
117
|
}
|
|
117
118
|
}
|
|
@@ -133,5 +134,8 @@ export class TilesMesh extends Mesh {
|
|
|
133
134
|
this.material = buildTilesMaterial(material, buildTextureArrayFromAssets(texturesInOrder));
|
|
134
135
|
}
|
|
135
136
|
}
|
|
137
|
+
// Setup BVH for accelerated raycasting
|
|
138
|
+
setupBVH(this.geometry);
|
|
139
|
+
this.raycast = acceleratedRaycast;
|
|
136
140
|
}
|
|
137
141
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { acceleratedRaycast } from 'three-mesh-bvh';
|
|
2
|
+
import type { BufferGeometry } from 'three';
|
|
3
|
+
/**
|
|
4
|
+
* Sets up BVH (Bounding Volume Hierarchy) on a geometry for accelerated raycasting.
|
|
5
|
+
* Call this after geometry is created, then assign acceleratedRaycast to the mesh's raycast method.
|
|
6
|
+
*
|
|
7
|
+
* @param geometry - The BufferGeometry to compute BVH for. If undefined or empty, this is a no-op.
|
|
8
|
+
*/
|
|
9
|
+
export declare function setupBVH(geometry: BufferGeometry | undefined): void;
|
|
10
|
+
export { acceleratedRaycast };
|
|
11
|
+
//# sourceMappingURL=bvh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bvh.d.ts","sourceRoot":"","sources":["../../src/utils/bvh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAwC,MAAM,gBAAgB,CAAA;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAWnE;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh';
|
|
2
|
+
/**
|
|
3
|
+
* Sets up BVH (Bounding Volume Hierarchy) on a geometry for accelerated raycasting.
|
|
4
|
+
* Call this after geometry is created, then assign acceleratedRaycast to the mesh's raycast method.
|
|
5
|
+
*
|
|
6
|
+
* @param geometry - The BufferGeometry to compute BVH for. If undefined or empty, this is a no-op.
|
|
7
|
+
*/
|
|
8
|
+
export function setupBVH(geometry) {
|
|
9
|
+
if (!geometry)
|
|
10
|
+
return;
|
|
11
|
+
// Skip BVH for empty geometries (no index or no position attribute)
|
|
12
|
+
const index = geometry.getIndex();
|
|
13
|
+
const position = geometry.getAttribute('position');
|
|
14
|
+
if (!index || index.count === 0 || !position || position.count === 0)
|
|
15
|
+
return;
|
|
16
|
+
geometry.computeBoundsTree = computeBoundsTree;
|
|
17
|
+
geometry.disposeBoundsTree = disposeBoundsTree;
|
|
18
|
+
geometry.computeBoundsTree();
|
|
19
|
+
}
|
|
20
|
+
export { acceleratedRaycast };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instanced-mesh-group.d.ts","sourceRoot":"","sources":["../../src/utils/instanced-mesh-group.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAQ,QAAQ,EAAS,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"instanced-mesh-group.d.ts","sourceRoot":"","sources":["../../src/utils/instanced-mesh-group.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAQ,QAAQ,EAAS,MAAM,OAAO,CAAC;AAG7E,UAAU,kBAAkB;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,OAAO,EAAE,kBAAkB,EAAE,CAAM;gBAEvB,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;IAmDtE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAiB1C;;;OAGG;IACH,OAAO;CAOR"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Group, InstancedMesh, Matrix4 } from "three";
|
|
2
|
+
import { setupBVH, acceleratedRaycast } from "./bvh.js";
|
|
2
3
|
export class InstancedMeshGroup extends Group {
|
|
3
4
|
entries = [];
|
|
4
5
|
constructor(object, count, matrices) {
|
|
@@ -29,6 +30,11 @@ export class InstancedMeshGroup extends Group {
|
|
|
29
30
|
this.setMatrixAt(i, matrices[i]);
|
|
30
31
|
}
|
|
31
32
|
}
|
|
33
|
+
// Setup BVH for accelerated raycasting on all child InstancedMeshes
|
|
34
|
+
for (const entry of this.entries) {
|
|
35
|
+
setupBVH(entry.mesh.geometry);
|
|
36
|
+
entry.mesh.raycast = acceleratedRaycast;
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
39
|
setMatrixAt(index, matrix) {
|
|
34
40
|
const instanceMatrix = new Matrix4();
|
package/dist/walls/index.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare const WALL_CONFIG: {
|
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
49
|
export declare const WALL_CONNECTION_TOLERANCE_RATIO: number;
|
|
50
|
-
export declare function computeWallVerticalBounds(interpreter: Interpreter, tilesGeometry: TilesGeometry, row: number, col: number, wallIdx: number, parsed: {
|
|
50
|
+
export declare function computeWallVerticalBounds(interpreter: Interpreter, tilesGeometry: TilesGeometry | undefined, row: number, col: number, wallIdx: number, parsed: {
|
|
51
51
|
bottomY?: number;
|
|
52
52
|
topY?: number;
|
|
53
53
|
dir: string;
|
|
@@ -59,10 +59,10 @@ export declare function computeWallVerticalBounds(interpreter: Interpreter, tile
|
|
|
59
59
|
} | undefined;
|
|
60
60
|
export type WallMeshOptions = {
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Validate-only mode: validates wall/window/door calls but skips geometry creation.
|
|
63
63
|
* Useful for server-side validation. Default: false.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
validateOnly?: boolean;
|
|
66
66
|
};
|
|
67
67
|
export declare class WallMesh extends Mesh {
|
|
68
68
|
constructor(interpreter: Interpreter, material?: Material, options?: WallMeshOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/walls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAER,IAAI,EAGJ,YAAY,EAEZ,YAAY,EAOb,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD,OAAO,EAAe,aAAa,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAGL,UAAU,EACV,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/walls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAER,IAAI,EAGJ,YAAY,EAEZ,YAAY,EAOb,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD,OAAO,EAAe,aAAa,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAGL,UAAU,EACV,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,CAAA;AAK/C,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,MAAM,EAAE,YAAY,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,YAAY,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,UAAU,EAAE,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CAed,CAAA;AAEV,eAAO,MAAM,+BAA+B,QAAQ,CAAA;AAEpD,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,GAAG,SAAS,EACxC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACxD,MAAM,EAAE,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,EACtD,GAAG,EAAE,aAAa;YAsFO,YAAY;UACd,YAAY;;;cAwDpC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,qBAAa,QAAS,SAAQ,IAAI;gBACpB,WAAW,EAAE,WAAW,EAAE,QAAQ,GAAE,QAAkC,EAAE,OAAO,GAAE,eAAoB;CAkTlH;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAGrG"}
|
package/dist/walls/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { buildTextureArrayFromAssets } from '../utils/texture.js';
|
|
|
5
5
|
import { buildWallMeshMaterial } from './material.js';
|
|
6
6
|
import { ChartaError } from '../errors.js';
|
|
7
7
|
import { groundSchema, ceilingSchema, wallSchema, windowSchema, doorSchema, } from '../schemas.js';
|
|
8
|
+
import { setupBVH, acceleratedRaycast } from '../utils/bvh.js';
|
|
8
9
|
export { wallSchema, windowSchema, doorSchema };
|
|
9
10
|
const eulerHelper = new Euler();
|
|
10
11
|
const scaleHelper = new Vector3(1, 1, 1);
|
|
@@ -92,6 +93,15 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
|
|
|
92
93
|
return undefined;
|
|
93
94
|
if (topLayer === undefined && parsed.topY === undefined)
|
|
94
95
|
return undefined;
|
|
96
|
+
// In validateOnly mode (no tilesGeometry), skip height sampling - validation is complete
|
|
97
|
+
if (!tilesGeometry) {
|
|
98
|
+
return {
|
|
99
|
+
yStart: [0, 0, 0],
|
|
100
|
+
yEnd: [0, 0, 0],
|
|
101
|
+
worldWallX,
|
|
102
|
+
worldWallZ,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
95
105
|
const sampleOffsets = [-cellSize / 2, 0, cellSize / 2];
|
|
96
106
|
const yStart = [0, 0, 0];
|
|
97
107
|
const yEnd = [0, 0, 0];
|
|
@@ -145,40 +155,15 @@ export class WallMesh extends Mesh {
|
|
|
145
155
|
const rows = interpreter.getRows();
|
|
146
156
|
const cols = interpreter.getCols();
|
|
147
157
|
const cellSize = interpreter.getCellSize();
|
|
148
|
-
//
|
|
149
|
-
|
|
158
|
+
// Get tilesGeometry only in non-validateOnly mode (needed for height sampling)
|
|
159
|
+
const tilesGeometry = options.validateOnly ? undefined : interpreter.getAsset(TilesGeometry, 'tilesGeometry');
|
|
160
|
+
// In non-validateOnly mode, if no tilesGeometry exists, early return
|
|
161
|
+
if (!options.validateOnly && !tilesGeometry) {
|
|
150
162
|
super();
|
|
151
|
-
for (let row = 0; row < rows; row++) {
|
|
152
|
-
for (let col = 0; col < cols; col++) {
|
|
153
|
-
// Validate all wall/window/door calls
|
|
154
|
-
const entries = interpreter.getCalls([row, col], {
|
|
155
|
-
wall: wallSchema,
|
|
156
|
-
window: windowSchema,
|
|
157
|
-
door: doorSchema,
|
|
158
|
-
});
|
|
159
|
-
// Validate texture references and wall ordering
|
|
160
|
-
let hasWall = false;
|
|
161
|
-
for (const [name, parsed, , loc] of entries) {
|
|
162
|
-
if (name === 'wall') {
|
|
163
|
-
hasWall = true;
|
|
164
|
-
interpreter.getAsset(Texture, `${parsed.texture}BaseColorTexture`, loc);
|
|
165
|
-
}
|
|
166
|
-
else if (name === 'window' || name === 'door') {
|
|
167
|
-
if (!hasWall) {
|
|
168
|
-
interpreter.reportError(new ChartaError(`${name} without preceding wall`, interpreter.getSource(), loc));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
163
|
return;
|
|
175
164
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
super();
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const wallData = [];
|
|
165
|
+
// Allocate data structures only in non-validateOnly mode
|
|
166
|
+
const wallData = options.validateOnly ? [] : [];
|
|
182
167
|
const usedTextures = [];
|
|
183
168
|
const getWallTextureId = (texture) => {
|
|
184
169
|
let idx = usedTextures.indexOf(texture);
|
|
@@ -188,6 +173,7 @@ export class WallMesh extends Mesh {
|
|
|
188
173
|
}
|
|
189
174
|
return idx;
|
|
190
175
|
};
|
|
176
|
+
// Validation loop runs in both modes
|
|
191
177
|
for (let row = 0; row < rows; row++) {
|
|
192
178
|
for (let col = 0; col < cols; col++) {
|
|
193
179
|
const entries = interpreter.getCalls([row, col], {
|
|
@@ -196,8 +182,17 @@ export class WallMesh extends Mesh {
|
|
|
196
182
|
door: doorSchema,
|
|
197
183
|
});
|
|
198
184
|
let currentWall;
|
|
185
|
+
// Track wall validity separately for window/door validation in validateOnly mode
|
|
186
|
+
let hasValidWall = false;
|
|
199
187
|
for (const [name, winOrDoor, wallIdx, loc] of entries) {
|
|
200
188
|
if (name === 'window' || name === 'door') {
|
|
189
|
+
// In validateOnly mode, we only validate wall ordering (not geometry bounds)
|
|
190
|
+
if (options.validateOnly) {
|
|
191
|
+
if (!hasValidWall) {
|
|
192
|
+
interpreter.reportError(new ChartaError(`${name} without preceding wall`, interpreter.getSource(), loc));
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
201
196
|
if (!currentWall) {
|
|
202
197
|
interpreter.reportError(new ChartaError(`${name} without preceding wall`, interpreter.getSource(), loc));
|
|
203
198
|
continue;
|
|
@@ -260,31 +255,45 @@ export class WallMesh extends Mesh {
|
|
|
260
255
|
}
|
|
261
256
|
const wallParsed = winOrDoor; // Type assertion since it could be wall or window in loop
|
|
262
257
|
const config = WALL_CONFIG[wallParsed.dir];
|
|
258
|
+
// computeWallVerticalBounds validates layer existence and reports errors
|
|
259
|
+
// In validateOnly mode (tilesGeometry=undefined), it skips height sampling but still validates
|
|
263
260
|
const bounds = computeWallVerticalBounds(interpreter, tilesGeometry, row, col, wallIdx, wallParsed, config, loc);
|
|
264
261
|
if (!bounds) {
|
|
265
262
|
currentWall = undefined;
|
|
263
|
+
hasValidWall = false;
|
|
266
264
|
continue;
|
|
267
265
|
}
|
|
268
|
-
|
|
266
|
+
// Validate texture reference (runs in both modes)
|
|
267
|
+
const texture = interpreter.getAsset(Texture, `${wallParsed.texture}BaseColorTexture`, loc);
|
|
269
268
|
if (!texture) {
|
|
270
269
|
currentWall = undefined;
|
|
270
|
+
hasValidWall = false;
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
273
|
+
hasValidWall = true;
|
|
274
|
+
// Only build wall data in non-validateOnly mode
|
|
275
|
+
if (!options.validateOnly) {
|
|
276
|
+
const newWall = {
|
|
277
|
+
x: bounds.worldWallX,
|
|
278
|
+
z: bounds.worldWallZ,
|
|
279
|
+
rotationY: config.rotation,
|
|
280
|
+
xzSize: [cellSize, 0.1], // Walls are always cellSize width, 0.1 depth
|
|
281
|
+
yStart: bounds.yStart,
|
|
282
|
+
yEnd: bounds.yEnd,
|
|
283
|
+
textureId: getWallTextureId(texture),
|
|
284
|
+
windows: [],
|
|
285
|
+
};
|
|
286
|
+
wallData.push(newWall);
|
|
287
|
+
currentWall = newWall;
|
|
288
|
+
}
|
|
285
289
|
}
|
|
286
290
|
}
|
|
287
291
|
}
|
|
292
|
+
// ValidateOnly mode: skip geometry creation
|
|
293
|
+
if (options.validateOnly) {
|
|
294
|
+
super();
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
288
297
|
// Build per-wall geometries (baked) and merge
|
|
289
298
|
const bakedGeometries = [];
|
|
290
299
|
const tmpMatrix = new Matrix4();
|
|
@@ -377,6 +386,9 @@ export class WallMesh extends Mesh {
|
|
|
377
386
|
const textureArray = buildTextureArrayFromAssets(usedTextures);
|
|
378
387
|
buildWallMeshMaterial(this.material, textureArray);
|
|
379
388
|
}
|
|
389
|
+
// Setup BVH for accelerated raycasting
|
|
390
|
+
setupBVH(this.geometry);
|
|
391
|
+
this.raycast = acceleratedRaycast;
|
|
380
392
|
}
|
|
381
393
|
}
|
|
382
394
|
export function isLayerConnectedToWall(wallTopY, wallBotY, y, tileSize) {
|
package/dist/water/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { Mesh } from "three";
|
|
|
2
2
|
import { Interpreter } from "../interpreter.js";
|
|
3
3
|
export type WaterMeshOptions = {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Validate-only mode: validates water calls but skips geometry creation.
|
|
6
6
|
* Useful for server-side validation. Default: false.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
validateOnly?: boolean;
|
|
9
9
|
};
|
|
10
10
|
export declare class WaterMesh extends Mesh {
|
|
11
11
|
constructor(interpreter: Interpreter, options?: WaterMeshOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/water/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAY,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/water/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAY,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAC;AAEF,qBAAa,SAAU,SAAQ,IAAI;gBACrB,WAAW,EAAE,WAAW,EAAE,OAAO,GAAE,gBAAqB;IAwDpE,OAAO,IAAI,IAAI;CAMhB;AAED,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA"}
|
package/dist/water/index.js
CHANGED
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
import { Mesh, Material } from "three";
|
|
2
2
|
import { TilesGeometry } from "../tiles/geometry.js";
|
|
3
3
|
import { waterSchema } from "../schemas.js";
|
|
4
|
+
import { setupBVH, acceleratedRaycast } from "../utils/bvh.js";
|
|
4
5
|
export class WaterMesh extends Mesh {
|
|
5
6
|
constructor(interpreter, options = {}) {
|
|
6
7
|
const rows = interpreter.getRows();
|
|
7
8
|
const cols = interpreter.getCols();
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
interpreter.getCalls([row, col], { water: waterSchema });
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return;
|
|
9
|
+
// Build tiles only in non-validateOnly mode (expensive allocation)
|
|
10
|
+
let tiles;
|
|
11
|
+
if (!options.validateOnly) {
|
|
12
|
+
tiles = new Array(rows)
|
|
13
|
+
.fill(undefined)
|
|
14
|
+
.map(() => new Array(cols).fill(undefined).map(() => []));
|
|
18
15
|
}
|
|
19
|
-
//
|
|
20
|
-
const tiles = new Array(rows)
|
|
21
|
-
.fill(undefined)
|
|
22
|
-
.map(() => new Array(cols).fill(undefined).map(() => []));
|
|
16
|
+
// Validation loop runs in both modes
|
|
23
17
|
for (let row = 0; row < rows; row++) {
|
|
24
18
|
for (let col = 0; col < cols; col++) {
|
|
25
19
|
const entries = interpreter.getCalls([row, col], { water: waterSchema });
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
stack
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
// Only build tile stack in non-validateOnly mode
|
|
21
|
+
if (!options.validateOnly) {
|
|
22
|
+
const stack = [];
|
|
23
|
+
for (const [, parsed] of entries) {
|
|
24
|
+
stack.push({
|
|
25
|
+
type: "water",
|
|
26
|
+
y: parsed.y,
|
|
27
|
+
textureId: 0,
|
|
28
|
+
row,
|
|
29
|
+
col,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
tiles[row][col] = stack;
|
|
35
33
|
}
|
|
36
|
-
tiles[row][col] = stack;
|
|
37
34
|
}
|
|
38
35
|
}
|
|
36
|
+
// ValidateOnly mode: skip geometry creation
|
|
37
|
+
if (options.validateOnly) {
|
|
38
|
+
super();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
39
41
|
const cellSize = interpreter.getCellSize();
|
|
40
42
|
const mapSizeX = cols * cellSize;
|
|
41
43
|
const mapSizeZ = rows * cellSize;
|
|
@@ -44,6 +46,9 @@ export class WaterMesh extends Mesh {
|
|
|
44
46
|
super(geometry);
|
|
45
47
|
this.renderOrder = 1;
|
|
46
48
|
this.frustumCulled = true;
|
|
49
|
+
// Setup BVH for accelerated raycasting
|
|
50
|
+
setupBVH(this.geometry);
|
|
51
|
+
this.raycast = acceleratedRaycast;
|
|
47
52
|
}
|
|
48
53
|
dispose() {
|
|
49
54
|
this.geometry.dispose();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drawcall/charta",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"author": "Bela Bohlender",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://drawcall.ai",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"fft.js": "^4.0.4",
|
|
26
26
|
"imurmurhash": "^0.1.4",
|
|
27
27
|
"seedrandom": "^3.0.5",
|
|
28
|
+
"three-mesh-bvh": "^0.8.0",
|
|
28
29
|
"zod": "^4.1.12"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|