@drawcall/charta 0.1.18 → 0.1.19
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 +2 -2
- 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 +48 -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 +4 -4
- package/dist/walls/index.d.ts +3 -3
- package/dist/walls/index.d.ts.map +1 -1
- package/dist/walls/index.js +52 -44
- package/dist/water/index.d.ts +2 -2
- package/dist/water/index.d.ts.map +1 -1
- package/dist/water/index.js +25 -24
- package/package.json +1 -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;CAoP1B"}
|
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();
|
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;AAO/C,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,CAAA;AAYhF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,
|
|
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;AAO/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;CAwLpH"}
|
package/dist/pillars/index.js
CHANGED
|
@@ -9,29 +9,16 @@ export class PillarMesh extends InstancedMesh {
|
|
|
9
9
|
const rows = interpreter.getRows();
|
|
10
10
|
const cols = interpreter.getCols();
|
|
11
11
|
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
|
|
12
|
+
// Collect pillar instances only in non-validateOnly mode
|
|
28
13
|
const pillars = [];
|
|
29
|
-
// tilesGeometry is only required for height sampling; fetch lazily where needed
|
|
30
14
|
const usedTextures = [];
|
|
31
|
-
const getTextureId = (name) => {
|
|
32
|
-
const texture = interpreter.getAsset(Texture, `${name}BaseColorTexture
|
|
15
|
+
const getTextureId = (name, loc) => {
|
|
16
|
+
const texture = interpreter.getAsset(Texture, `${name}BaseColorTexture`, loc);
|
|
33
17
|
if (!texture)
|
|
34
18
|
return 0;
|
|
19
|
+
// Only track textures in non-validateOnly mode
|
|
20
|
+
if (options.validateOnly)
|
|
21
|
+
return 0;
|
|
35
22
|
let idx = usedTextures.indexOf(texture);
|
|
36
23
|
if (idx === -1) {
|
|
37
24
|
idx = usedTextures.length;
|
|
@@ -61,6 +48,7 @@ export class PillarMesh extends InstancedMesh {
|
|
|
61
48
|
}
|
|
62
49
|
}
|
|
63
50
|
}
|
|
51
|
+
// Validation loop runs in both modes
|
|
64
52
|
for (let row = 0; row < rows; row++) {
|
|
65
53
|
for (let col = 0; col < cols; col++) {
|
|
66
54
|
const entries = interpreter.getCalls([row, col], {
|
|
@@ -76,11 +64,13 @@ export class PillarMesh extends InstancedMesh {
|
|
|
76
64
|
const [cx, cz] = interpreter.getWorldCellCenter(row, col);
|
|
77
65
|
const offsetX = px - cx;
|
|
78
66
|
const offsetZ = pz - cz;
|
|
79
|
-
|
|
67
|
+
// Validate texture reference (runs in both modes)
|
|
68
|
+
const textureId = getTextureId(parsed.texture, loc);
|
|
80
69
|
// Derive default layers relative to call position (like walls)
|
|
81
70
|
const isLayer = (c) => c.name === 'ground' || c.name === 'ceiling';
|
|
82
71
|
let bottomY = parsed.bottomY;
|
|
83
72
|
let topY = parsed.topY;
|
|
73
|
+
// Validate layer existence (runs in both modes)
|
|
84
74
|
let bottomStackIndex;
|
|
85
75
|
if (bottomY == null) {
|
|
86
76
|
const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
|
|
@@ -89,10 +79,13 @@ export class PillarMesh extends InstancedMesh {
|
|
|
89
79
|
interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing bottomY and no preceding layer`, interpreter.getSource(), loc));
|
|
90
80
|
continue;
|
|
91
81
|
}
|
|
92
|
-
|
|
93
|
-
if (!
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
// Height sampling only in non-validateOnly mode
|
|
83
|
+
if (!options.validateOnly) {
|
|
84
|
+
const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
|
|
85
|
+
if (!tilesGeometry)
|
|
86
|
+
continue;
|
|
87
|
+
bottomY = tilesGeometry.getHeight(row, col, bottomStackIndex, offsetX, offsetZ);
|
|
88
|
+
}
|
|
96
89
|
}
|
|
97
90
|
let topStackIndex;
|
|
98
91
|
if (topY == null) {
|
|
@@ -101,31 +94,42 @@ export class PillarMesh extends InstancedMesh {
|
|
|
101
94
|
interpreter.reportError(new ChartaError(`pillar at ${row}/${col}: missing topY and no subsequent layer`, interpreter.getSource(), loc));
|
|
102
95
|
continue;
|
|
103
96
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
// Height sampling only in non-validateOnly mode
|
|
98
|
+
if (!options.validateOnly) {
|
|
99
|
+
const layersBefore = interpreter.countCalls([row, col], isLayer, 0, idx);
|
|
100
|
+
topStackIndex = layersBefore;
|
|
101
|
+
const tilesGeometry = interpreter.getAsset(TilesGeometry, 'tilesGeometry', loc);
|
|
102
|
+
if (!tilesGeometry)
|
|
103
|
+
continue;
|
|
104
|
+
topY = tilesGeometry.getHeight(row, col, topStackIndex, offsetX, offsetZ);
|
|
105
|
+
}
|
|
110
106
|
}
|
|
111
|
-
//
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
bottomY
|
|
115
|
-
|
|
107
|
+
// Only collect pillar data in non-validateOnly mode
|
|
108
|
+
if (!options.validateOnly) {
|
|
109
|
+
// Support "negative pillars" by swapping
|
|
110
|
+
if (topY < bottomY) {
|
|
111
|
+
const tmp = bottomY;
|
|
112
|
+
bottomY = topY;
|
|
113
|
+
topY = tmp;
|
|
114
|
+
}
|
|
115
|
+
pillars.push({
|
|
116
|
+
x: px,
|
|
117
|
+
z: pz,
|
|
118
|
+
sizeX,
|
|
119
|
+
sizeZ,
|
|
120
|
+
bottomY: bottomY,
|
|
121
|
+
topY: topY,
|
|
122
|
+
textureId,
|
|
123
|
+
});
|
|
116
124
|
}
|
|
117
|
-
pillars.push({
|
|
118
|
-
x: px,
|
|
119
|
-
z: pz,
|
|
120
|
-
sizeX,
|
|
121
|
-
sizeZ,
|
|
122
|
-
bottomY: bottomY,
|
|
123
|
-
topY: topY,
|
|
124
|
-
textureId,
|
|
125
|
-
});
|
|
126
125
|
}
|
|
127
126
|
}
|
|
128
127
|
}
|
|
128
|
+
// ValidateOnly mode: skip geometry creation
|
|
129
|
+
if (options.validateOnly) {
|
|
130
|
+
super(undefined, material, 0);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
129
133
|
// Base cube with base at y=0 for easy scaling
|
|
130
134
|
const geometry = new BoxGeometry(1, 1, 1);
|
|
131
135
|
geometry.translate(0, 0.5, 0);
|
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;AAEvE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AAEtC,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,
|
|
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;AAEvE,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;CAiInH"}
|
package/dist/tiles/index.js
CHANGED
|
@@ -34,9 +34,9 @@ export class TilesMesh extends Mesh {
|
|
|
34
34
|
constructor(interpreter, material = new MeshBasicMaterial(), options = {}) {
|
|
35
35
|
const rows = interpreter.getRows();
|
|
36
36
|
const cols = interpreter.getCols();
|
|
37
|
-
const {
|
|
38
|
-
const createGeometry = !
|
|
39
|
-
const createMaterial = !
|
|
37
|
+
const { validateOnly = false, skipTextureLoading = false } = options;
|
|
38
|
+
const createGeometry = !validateOnly;
|
|
39
|
+
const createMaterial = !validateOnly && !skipTextureLoading;
|
|
40
40
|
// Texture index tracking (only used when creating materials)
|
|
41
41
|
const textureIndex = new Map();
|
|
42
42
|
const getTextureIndex = (name, loc) => {
|
|
@@ -111,7 +111,7 @@ export class TilesMesh extends Mesh {
|
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
else if (!skipTextureLoading) {
|
|
114
|
-
// Validate texture exists in
|
|
114
|
+
// Validate texture exists in validateOnly mode (unless mockTexture is set)
|
|
115
115
|
interpreter.getAsset(Texture, `${parsed.texture}BaseColorTexture`, loc);
|
|
116
116
|
}
|
|
117
117
|
}
|
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;AAEtB,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,
|
|
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;AAEtB,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;CA8SlH;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
|
@@ -92,6 +92,15 @@ export function computeWallVerticalBounds(interpreter, tilesGeometry, row, col,
|
|
|
92
92
|
return undefined;
|
|
93
93
|
if (topLayer === undefined && parsed.topY === undefined)
|
|
94
94
|
return undefined;
|
|
95
|
+
// In validateOnly mode (no tilesGeometry), skip height sampling - validation is complete
|
|
96
|
+
if (!tilesGeometry) {
|
|
97
|
+
return {
|
|
98
|
+
yStart: [0, 0, 0],
|
|
99
|
+
yEnd: [0, 0, 0],
|
|
100
|
+
worldWallX,
|
|
101
|
+
worldWallZ,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
95
104
|
const sampleOffsets = [-cellSize / 2, 0, cellSize / 2];
|
|
96
105
|
const yStart = [0, 0, 0];
|
|
97
106
|
const yEnd = [0, 0, 0];
|
|
@@ -145,40 +154,15 @@ export class WallMesh extends Mesh {
|
|
|
145
154
|
const rows = interpreter.getRows();
|
|
146
155
|
const cols = interpreter.getCols();
|
|
147
156
|
const cellSize = interpreter.getCellSize();
|
|
148
|
-
//
|
|
149
|
-
|
|
157
|
+
// Get tilesGeometry only in non-validateOnly mode (needed for height sampling)
|
|
158
|
+
const tilesGeometry = options.validateOnly ? undefined : interpreter.getAsset(TilesGeometry, 'tilesGeometry');
|
|
159
|
+
// In non-validateOnly mode, if no tilesGeometry exists, early return
|
|
160
|
+
if (!options.validateOnly && !tilesGeometry) {
|
|
150
161
|
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
162
|
return;
|
|
175
163
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
super();
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const wallData = [];
|
|
164
|
+
// Allocate data structures only in non-validateOnly mode
|
|
165
|
+
const wallData = options.validateOnly ? [] : [];
|
|
182
166
|
const usedTextures = [];
|
|
183
167
|
const getWallTextureId = (texture) => {
|
|
184
168
|
let idx = usedTextures.indexOf(texture);
|
|
@@ -188,6 +172,7 @@ export class WallMesh extends Mesh {
|
|
|
188
172
|
}
|
|
189
173
|
return idx;
|
|
190
174
|
};
|
|
175
|
+
// Validation loop runs in both modes
|
|
191
176
|
for (let row = 0; row < rows; row++) {
|
|
192
177
|
for (let col = 0; col < cols; col++) {
|
|
193
178
|
const entries = interpreter.getCalls([row, col], {
|
|
@@ -196,8 +181,17 @@ export class WallMesh extends Mesh {
|
|
|
196
181
|
door: doorSchema,
|
|
197
182
|
});
|
|
198
183
|
let currentWall;
|
|
184
|
+
// Track wall validity separately for window/door validation in validateOnly mode
|
|
185
|
+
let hasValidWall = false;
|
|
199
186
|
for (const [name, winOrDoor, wallIdx, loc] of entries) {
|
|
200
187
|
if (name === 'window' || name === 'door') {
|
|
188
|
+
// In validateOnly mode, we only validate wall ordering (not geometry bounds)
|
|
189
|
+
if (options.validateOnly) {
|
|
190
|
+
if (!hasValidWall) {
|
|
191
|
+
interpreter.reportError(new ChartaError(`${name} without preceding wall`, interpreter.getSource(), loc));
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
201
195
|
if (!currentWall) {
|
|
202
196
|
interpreter.reportError(new ChartaError(`${name} without preceding wall`, interpreter.getSource(), loc));
|
|
203
197
|
continue;
|
|
@@ -260,31 +254,45 @@ export class WallMesh extends Mesh {
|
|
|
260
254
|
}
|
|
261
255
|
const wallParsed = winOrDoor; // Type assertion since it could be wall or window in loop
|
|
262
256
|
const config = WALL_CONFIG[wallParsed.dir];
|
|
257
|
+
// computeWallVerticalBounds validates layer existence and reports errors
|
|
258
|
+
// In validateOnly mode (tilesGeometry=undefined), it skips height sampling but still validates
|
|
263
259
|
const bounds = computeWallVerticalBounds(interpreter, tilesGeometry, row, col, wallIdx, wallParsed, config, loc);
|
|
264
260
|
if (!bounds) {
|
|
265
261
|
currentWall = undefined;
|
|
262
|
+
hasValidWall = false;
|
|
266
263
|
continue;
|
|
267
264
|
}
|
|
268
|
-
|
|
265
|
+
// Validate texture reference (runs in both modes)
|
|
266
|
+
const texture = interpreter.getAsset(Texture, `${wallParsed.texture}BaseColorTexture`, loc);
|
|
269
267
|
if (!texture) {
|
|
270
268
|
currentWall = undefined;
|
|
269
|
+
hasValidWall = false;
|
|
271
270
|
continue;
|
|
272
271
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
272
|
+
hasValidWall = true;
|
|
273
|
+
// Only build wall data in non-validateOnly mode
|
|
274
|
+
if (!options.validateOnly) {
|
|
275
|
+
const newWall = {
|
|
276
|
+
x: bounds.worldWallX,
|
|
277
|
+
z: bounds.worldWallZ,
|
|
278
|
+
rotationY: config.rotation,
|
|
279
|
+
xzSize: [cellSize, 0.1], // Walls are always cellSize width, 0.1 depth
|
|
280
|
+
yStart: bounds.yStart,
|
|
281
|
+
yEnd: bounds.yEnd,
|
|
282
|
+
textureId: getWallTextureId(texture),
|
|
283
|
+
windows: [],
|
|
284
|
+
};
|
|
285
|
+
wallData.push(newWall);
|
|
286
|
+
currentWall = newWall;
|
|
287
|
+
}
|
|
285
288
|
}
|
|
286
289
|
}
|
|
287
290
|
}
|
|
291
|
+
// ValidateOnly mode: skip geometry creation
|
|
292
|
+
if (options.validateOnly) {
|
|
293
|
+
super();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
288
296
|
// Build per-wall geometries (baked) and merge
|
|
289
297
|
const bakedGeometries = [];
|
|
290
298
|
const tmpMatrix = new Matrix4();
|
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;AAIhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,
|
|
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;AAIhD,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;IAoDpE,OAAO,IAAI,IAAI;CAMhB;AAED,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA"}
|
package/dist/water/index.js
CHANGED
|
@@ -5,37 +5,38 @@ export class WaterMesh extends Mesh {
|
|
|
5
5
|
constructor(interpreter, options = {}) {
|
|
6
6
|
const rows = interpreter.getRows();
|
|
7
7
|
const cols = interpreter.getCols();
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
interpreter.getCalls([row, col], { water: waterSchema });
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return;
|
|
8
|
+
// Build tiles only in non-validateOnly mode (expensive allocation)
|
|
9
|
+
let tiles;
|
|
10
|
+
if (!options.validateOnly) {
|
|
11
|
+
tiles = new Array(rows)
|
|
12
|
+
.fill(undefined)
|
|
13
|
+
.map(() => new Array(cols).fill(undefined).map(() => []));
|
|
18
14
|
}
|
|
19
|
-
//
|
|
20
|
-
const tiles = new Array(rows)
|
|
21
|
-
.fill(undefined)
|
|
22
|
-
.map(() => new Array(cols).fill(undefined).map(() => []));
|
|
15
|
+
// Validation loop runs in both modes
|
|
23
16
|
for (let row = 0; row < rows; row++) {
|
|
24
17
|
for (let col = 0; col < cols; col++) {
|
|
25
18
|
const entries = interpreter.getCalls([row, col], { water: waterSchema });
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
stack
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
// Only build tile stack in non-validateOnly mode
|
|
20
|
+
if (!options.validateOnly) {
|
|
21
|
+
const stack = [];
|
|
22
|
+
for (const [, parsed] of entries) {
|
|
23
|
+
stack.push({
|
|
24
|
+
type: "water",
|
|
25
|
+
y: parsed.y,
|
|
26
|
+
textureId: 0,
|
|
27
|
+
row,
|
|
28
|
+
col,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
tiles[row][col] = stack;
|
|
35
32
|
}
|
|
36
|
-
tiles[row][col] = stack;
|
|
37
33
|
}
|
|
38
34
|
}
|
|
35
|
+
// ValidateOnly mode: skip geometry creation
|
|
36
|
+
if (options.validateOnly) {
|
|
37
|
+
super();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
39
40
|
const cellSize = interpreter.getCellSize();
|
|
40
41
|
const mapSizeX = cols * cellSize;
|
|
41
42
|
const mapSizeZ = rows * cellSize;
|